app-manager/fetch.c
author J. Ali Harlow <ali@juiblex.co.uk>
Fri Mar 08 12:05:41 2019 +0000 (2019-03-08)
changeset 86 a8e48c62ec03
parent 14 29d8bb64056c
permissions -rw-r--r--
Fix historic issues exposed by the testsuite
     1 /*
     2  * A program to explore http support on MS-Windows.
     3  */
     4 
     5 #include "config.h"
     6 #include <stdlib.h>
     7 #include <stdio.h>
     8 #include <glib.h>
     9 #include <razor.h>
    10 #if HAVE_WINHTTP_H
    11 #include <windows.h>
    12 #include <winhttp.h>
    13 #include <wincrypt.h>
    14 
    15 #ifndef PKCS12_NO_PERSIST_KEY
    16 #define PKCS12_NO_PERSIST_KEY 0x00008000
    17 #endif
    18 
    19 static gboolean debug=FALSE;
    20 
    21 static GOptionEntry entries[] = 
    22 {
    23     { "debug",'d',0,G_OPTION_ARG_NONE,&debug,"Output debugging",NULL },
    24     { NULL }
    25 };
    26 
    27 static struct WinHttpFuncs
    28 {
    29     WINBOOL (WINAPI *AddRequestHeaders)(HINTERNET,LPCWSTR,DWORD,DWORD);
    30     WINBOOL (WINAPI *DetectAutoProxyConfigUrl)(DWORD,LPWSTR*);
    31     WINBOOL (WINAPI *CheckPlatform)(void);
    32     WINBOOL (WINAPI *CloseHandle)(HINTERNET);
    33     HINTERNET (WINAPI *Connect)(HINTERNET,LPCWSTR,INTERNET_PORT,DWORD);
    34     WINBOOL (WINAPI *CrackUrl)(LPCWSTR,DWORD,DWORD,LPURL_COMPONENTS);
    35     WINBOOL (WINAPI *CreateUrl)(LPURL_COMPONENTS,DWORD,LPWSTR,LPDWORD);
    36     WINBOOL (WINAPI *GetDefaultProxyConfiguration)(WINHTTP_PROXY_INFO*);
    37     WINBOOL (WINAPI *GetIEProxyConfigForCurrentUser)(WINHTTP_CURRENT_USER_IE_PROXY_CONFIG*);
    38     WINBOOL (WINAPI *GetProxyForUrl)(HINTERNET,LPCWSTR,WINHTTP_AUTOPROXY_OPTIONS*,WINHTTP_PROXY_INFO*);
    39     HINTERNET (WINAPI *Open)(LPCWSTR,DWORD,LPCWSTR,LPCWSTR,DWORD);
    40     HINTERNET (WINAPI *OpenRequest)(HINTERNET,LPCWSTR,LPCWSTR,LPCWSTR,LPCWSTR,LPCWSTR*,DWORD);
    41     WINBOOL (WINAPI *QueryAuthParams)(HINTERNET,DWORD,LPVOID*);
    42     WINBOOL (WINAPI *QueryAuthSchemes)(HINTERNET,LPDWORD,LPDWORD,LPDWORD);
    43     WINBOOL (WINAPI *QueryDataAvailable)(HINTERNET,LPDWORD);
    44     WINBOOL (WINAPI *QueryHeaders)(HINTERNET,DWORD,LPCWSTR,LPVOID,LPDWORD,LPDWORD);
    45     WINBOOL (WINAPI *QueryOption)(HINTERNET,DWORD,LPVOID,LPDWORD);
    46     WINBOOL (WINAPI *ReadData)(HINTERNET,LPVOID,DWORD,LPDWORD);
    47     WINBOOL (WINAPI *ReceiveResponse)(HINTERNET,LPVOID);
    48     WINBOOL (WINAPI *SendRequest)(HINTERNET,LPCWSTR,DWORD,LPVOID,DWORD,DWORD,DWORD_PTR);
    49     WINBOOL (WINAPI *SetDefaultProxyConfiguration)(WINHTTP_PROXY_INFO*);
    50     WINBOOL (WINAPI *SetCredentials)(HINTERNET,DWORD,DWORD,LPCWSTR,LPCWSTR,LPVOID);
    51     WINBOOL (WINAPI *SetOption)(HINTERNET,DWORD,LPVOID,DWORD);
    52     WINHTTP_STATUS_CALLBACK (WINAPI *SetStatusCallback)(HINTERNET,WINHTTP_STATUS_CALLBACK,DWORD,DWORD_PTR);
    53     WINBOOL (WINAPI *SetTimeouts)(HINTERNET,int,int,int,int);
    54     WINBOOL (WINAPI *TimeFromSystemTime)(CONST SYSTEMTIME *,LPWSTR);
    55     WINBOOL (WINAPI *TimeToSystemTime)(LPCWSTR,SYSTEMTIME*);
    56     WINBOOL (WINAPI *WriteData)(HINTERNET,LPCVOID,DWORD,LPDWORD);
    57 } WinHttp;
    58 
    59 static int plover_init_winhttp(void)
    60 {
    61     HMODULE module;
    62     if (WinHttp.Open)
    63 	return 0;
    64     module=LoadLibraryA("winhttp.dll");
    65     if (!module)
    66 	return -1;
    67     WinHttp.AddRequestHeaders=
    68       (WINBOOL (WINAPI *)(HINTERNET,LPCWSTR,DWORD,DWORD))
    69       GetProcAddress(module,"WinHttpAddRequestHeaders");
    70     WinHttp.DetectAutoProxyConfigUrl=
    71       (WINBOOL (WINAPI *)(DWORD,LPWSTR*))
    72       GetProcAddress(module,"WinHttpDetectAutoProxyConfigUrl");
    73     WinHttp.CheckPlatform=(WINBOOL (WINAPI *)(void))
    74       GetProcAddress(module,"WinHttpCheckPlatform");
    75     WinHttp.CloseHandle=(WINBOOL (WINAPI *)(HINTERNET))
    76       GetProcAddress(module,"WinHttpCloseHandle");
    77     WinHttp.Connect=
    78       (HINTERNET (WINAPI *)(HINTERNET,LPCWSTR,INTERNET_PORT,DWORD))
    79       GetProcAddress(module,"WinHttpConnect");
    80     WinHttp.CrackUrl=(WINBOOL (WINAPI *)(LPCWSTR,DWORD,DWORD,LPURL_COMPONENTS))
    81       GetProcAddress(module,"WinHttpCrackUrl");
    82     WinHttp.CreateUrl=
    83       (WINBOOL (WINAPI *)(LPURL_COMPONENTS,DWORD,LPWSTR,LPDWORD))
    84       GetProcAddress(module,"WinHttpCreateUrl");
    85     WinHttp.GetDefaultProxyConfiguration=
    86       (WINBOOL (WINAPI *)(WINHTTP_PROXY_INFO*))
    87       GetProcAddress(module,"WinHttpGetDefaultProxyConfiguration");
    88     WinHttp.GetIEProxyConfigForCurrentUser=
    89       (WINBOOL (WINAPI *)(WINHTTP_CURRENT_USER_IE_PROXY_CONFIG*))
    90       GetProcAddress(module,"WinHttpGetIEProxyConfigForCurrentUser");
    91     WinHttp.GetProxyForUrl=
    92       (WINBOOL (WINAPI *)(HINTERNET,LPCWSTR,WINHTTP_AUTOPROXY_OPTIONS*,WINHTTP_PROXY_INFO*))
    93       GetProcAddress(module,"WinHttpGetProxyForUrl");
    94     WinHttp.Open=(HINTERNET (WINAPI *)(LPCWSTR,DWORD,LPCWSTR,LPCWSTR,DWORD))
    95       GetProcAddress(module,"WinHttpOpen");
    96     WinHttp.OpenRequest=
    97       (HINTERNET (WINAPI *)(HINTERNET,LPCWSTR,LPCWSTR,LPCWSTR,LPCWSTR,LPCWSTR*,DWORD))
    98       GetProcAddress(module,"WinHttpOpenRequest");
    99     WinHttp.QueryAuthParams=(WINBOOL (WINAPI *)(HINTERNET,DWORD,LPVOID*))
   100       GetProcAddress(module,"WinHttpQueryAuthParams");
   101     WinHttp.QueryAuthSchemes=
   102       (WINBOOL (WINAPI *)(HINTERNET,LPDWORD,LPDWORD,LPDWORD))
   103       GetProcAddress(module,"WinHttpQueryAuthSchemes");
   104     WinHttp.QueryDataAvailable=(WINBOOL (WINAPI *)(HINTERNET,LPDWORD))
   105       GetProcAddress(module,"WinHttpQueryDataAvailable");
   106     WinHttp.QueryHeaders=
   107       (WINBOOL (WINAPI *)(HINTERNET,DWORD,LPCWSTR,LPVOID,LPDWORD,LPDWORD))
   108       GetProcAddress(module,"WinHttpQueryHeaders");
   109     WinHttp.QueryOption=(WINBOOL (WINAPI *)(HINTERNET,DWORD,LPVOID,LPDWORD))
   110       GetProcAddress(module,"WinHttpQueryOption");
   111     WinHttp.ReadData=(WINBOOL (WINAPI *)(HINTERNET,LPVOID,DWORD,LPDWORD))
   112       GetProcAddress(module,"WinHttpReadData");
   113     WinHttp.ReceiveResponse=(WINBOOL (WINAPI *)(HINTERNET,LPVOID))
   114       GetProcAddress(module,"WinHttpReceiveResponse");
   115     WinHttp.SendRequest=
   116       (WINBOOL (WINAPI *)(HINTERNET,LPCWSTR,DWORD,LPVOID,DWORD,DWORD,DWORD_PTR))
   117       GetProcAddress(module,"WinHttpSendRequest");
   118     WinHttp.SetDefaultProxyConfiguration=
   119       (WINBOOL (WINAPI *)(WINHTTP_PROXY_INFO*))
   120       GetProcAddress(module,"WinHttpSetDefaultProxyConfiguration");
   121     WinHttp.SetCredentials=
   122       (WINBOOL (WINAPI *)(HINTERNET,DWORD,DWORD,LPCWSTR,LPCWSTR,LPVOID))
   123       GetProcAddress(module,"WinHttpSetCredentials");
   124     WinHttp.SetOption=(WINBOOL (WINAPI *)(HINTERNET,DWORD,LPVOID,DWORD))
   125       GetProcAddress(module,"WinHttpSetOption");
   126     WinHttp.SetStatusCallback=
   127       (WINHTTP_STATUS_CALLBACK (WINAPI *)(HINTERNET,WINHTTP_STATUS_CALLBACK,DWORD,DWORD_PTR))
   128       GetProcAddress(module,"WinHttpSetStatusCallback");
   129     WinHttp.SetTimeouts=(WINBOOL (WINAPI *)(HINTERNET,int,int,int,int))
   130       GetProcAddress(module,"WinHttpSetTimeouts");
   131     WinHttp.TimeFromSystemTime=(WINBOOL (WINAPI *)(CONST SYSTEMTIME *,LPWSTR))
   132       GetProcAddress(module,"WinHttpTimeFromSystemTime");
   133     WinHttp.TimeToSystemTime=(WINBOOL (WINAPI *)(LPCWSTR,SYSTEMTIME*))
   134       GetProcAddress(module,"WinHttpTimeToSystemTime");
   135     WinHttp.WriteData=(WINBOOL (WINAPI *)(HINTERNET,LPCVOID,DWORD,LPDWORD))
   136       GetProcAddress(module,"WinHttpWriteData");
   137     if (!WinHttp.CrackUrl || !WinHttp.Open || !WinHttp.Connect ||
   138         !WinHttp.OpenRequest || !WinHttp.SendRequest ||
   139 	!WinHttp.ReceiveResponse || !WinHttp.QueryDataAvailable ||
   140 	!WinHttp.ReadData || !WinHttp.CloseHandle)
   141     {
   142 	FreeLibrary(module);
   143 	WinHttp.Open=NULL;
   144 	return -1;
   145     }
   146     return 0;
   147 }
   148 
   149 /*
   150  * This will find ${PREFIX}/${file} if it exists, but it may also
   151  * find ${PREFIX}/.../${file} if ${PREFIX}/${file} does not exist.
   152  * This seems unlikely and won't do any harm should it occur.
   153  */
   154 
   155 gchar *find_prefixed_file(const char *file)
   156 {
   157     int i,len;
   158     const char *name;
   159     char *install_root;
   160     struct razor_set *set;
   161     struct razor_error *error=NULL;
   162     struct razor_package *package;
   163     struct razor_package_iterator *pi;
   164     struct razor_file_iterator *fi;
   165     gchar *retval=NULL;
   166     len=strlen(file);
   167     install_root=getenv("RAZOR_ROOT");
   168     if (!install_root)
   169 	install_root="";
   170     set=razor_root_open_read_only(install_root,&error);
   171     if (set)
   172     {
   173 	pi=razor_package_iterator_create(set);
   174 	while (razor_package_iterator_next(pi,&package,RAZOR_DETAIL_LAST))
   175 	{
   176 	    fi=razor_file_iterator_create(set,package,0);
   177 	    while (!retval && razor_file_iterator_next(fi,&name))
   178 	    {
   179 		i=strlen(name)-len;
   180 		if (i>0 && name[i-1]=='/' && !strcmp(name+i,file))
   181 		{
   182 		    if (!retval || strlen(retval)>strlen(name))
   183 		    {
   184 			g_free(retval);
   185 			retval=g_strdup(name);
   186 		    }
   187 		}
   188 	    }
   189 	    razor_file_iterator_destroy(fi);
   190 	}
   191 	razor_package_iterator_destroy(pi);
   192 	razor_set_unref(set);
   193     }
   194     if (error)
   195 	razor_error_free(error);
   196     return retval;
   197 }
   198 
   199 static HCERTSTORE plover_p12_import(const char *file,const wchar_t *password)
   200 {
   201     GError *error=NULL;
   202     gchar *p12,*path,*s;
   203     gsize len;
   204     CRYPT_DATA_BLOB pfx;
   205     HCERTSTORE store;
   206     s=g_build_path("/","etc","pki",file,NULL);
   207     path=find_prefixed_file(s);
   208     if (!path)
   209     {
   210 	g_printerr("%s: Not installed\n",s);
   211 	g_free(s);
   212 	return NULL;
   213     }
   214     g_free(s);
   215     if (!g_file_get_contents(path,&p12,&len,&error))
   216     {
   217 	g_printerr("%s: %s\n",path,error->message);
   218 	g_free(path);
   219 	g_error_free(error);
   220 	return NULL;
   221     }
   222     g_free(path);
   223     pfx.pbData=(BYTE *)p12;
   224     pfx.cbData=len;
   225     store=PFXImportCertStore(&pfx,password,PKCS12_NO_PERSIST_KEY);
   226     if (!store)
   227 	g_printerr("PFXImportCertStore failed. Err: %lu\n",GetLastError());
   228     g_free(p12);
   229     return store;
   230 }
   231 
   232 static const CERT_CONTEXT *plover_get_client_certificate(HCERTSTORE store)
   233 {
   234     const CERT_CONTEXT *iter,*cert;
   235     cert=NULL;
   236     iter=CertEnumCertificatesInStore(store,NULL);
   237     while(iter)
   238     {
   239 	if (!cert)
   240 	    cert=CertDuplicateCertificateContext(iter);
   241 	iter=CertEnumCertificatesInStore(store,iter);
   242     }
   243     return cert;
   244 }
   245 
   246 void CALLBACK plover_status_callback(HINTERNET handle,DWORD_PTR context,
   247   DWORD status,LPVOID status_information,DWORD status_information_length)
   248 {
   249     g_printerr("CB status 0x%lX",status);
   250     if (status==WINHTTP_CALLBACK_STATUS_SECURE_FAILURE)
   251 	g_printerr(", flag 0x%lX",*(DWORD *)status_information);
   252     g_printerr("\n");
   253 }
   254 
   255 int fetch(HINTERNET session,HCERTSTORE store,const char *url)
   256 {
   257     URL_COMPONENTS components={0};
   258     HINTERNET connection,request;
   259     DWORD len,nb,options;
   260     char *buffer;
   261     wchar_t *ws;
   262     glong ws_len;
   263     gchar *hostname,*path;
   264     const CERT_CONTEXT *client_cert;
   265     components.dwStructSize=sizeof(components);
   266     components.dwSchemeLength=(DWORD)-1;
   267     components.dwHostNameLength=(DWORD)-1;
   268     components.dwUserNameLength=(DWORD)-1;
   269     components.dwPasswordLength=(DWORD)-1;
   270     components.dwUrlPathLength=(DWORD)-1;
   271     ws=g_utf8_to_utf16(url,-1,NULL,&ws_len,NULL);
   272     if (!ws || !WinHttp.CrackUrl(ws,ws_len,0,&components))
   273     {
   274 	g_printerr("%s: Invalid or unsupported URL\n",url);
   275 	return -1;
   276     }
   277     /* ICU_REJECT_USERPWD is not supported under Windows XP */
   278     if (components.dwUserNameLength || components.dwPasswordLength)
   279     {
   280 	g_printerr("%s: Credentials not supported\n",url);
   281 	return -1;
   282     }
   283     hostname=g_utf16_to_utf8(components.lpszHostName,
   284       components.dwHostNameLength,NULL,NULL,NULL);
   285     path=g_utf16_to_utf8(components.lpszUrlPath,components.dwUrlPathLength,NULL,
   286       NULL,NULL);
   287     g_free(ws);
   288     ws=g_utf8_to_utf16(hostname,-1,NULL,NULL,NULL);
   289     connection=WinHttp.Connect(session,ws,
   290       components.nPort?components.nPort:INTERNET_DEFAULT_PORT,0);
   291     g_free(ws);
   292     if (connection)
   293     {
   294 	ws=g_utf8_to_utf16(path,-1,NULL,NULL,NULL);
   295 	request=WinHttp.OpenRequest(connection,L"GET",ws,NULL,
   296 	  WINHTTP_NO_REFERER,WINHTTP_DEFAULT_ACCEPT_TYPES,
   297 	  components.nScheme==INTERNET_SCHEME_HTTPS?WINHTTP_FLAG_SECURE:0);
   298 	g_free(ws);
   299 	if (request)
   300 	{
   301 	    if (debug)
   302 		(void)WinHttp.SetStatusCallback(request,plover_status_callback,
   303 		  WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS,0);
   304 	    if (components.nScheme==INTERNET_SCHEME_HTTPS && WinHttp.SetOption)
   305 	    {
   306 		options=SECURITY_FLAG_IGNORE_UNKNOWN_CA;
   307 		(void)WinHttp.SetOption(request,WINHTTP_OPTION_SECURITY_FLAGS,
   308 		  &options,sizeof(options));
   309 		client_cert=plover_get_client_certificate(store);
   310 		if (client_cert)
   311 		{
   312 		    if (!WinHttp.SetOption(request,
   313 		      WINHTTP_OPTION_CLIENT_CERT_CONTEXT,(void *)client_cert,
   314 		      sizeof(*client_cert)))
   315 			g_printerr("Failed to set client certificate (%lu)\n",
   316 			  GetLastError());
   317 		}
   318 		else
   319 		    g_printerr("No client certificate found\n");
   320 	    }
   321 	    if (!WinHttp.SendRequest(request,WINHTTP_NO_ADDITIONAL_HEADERS,
   322 	      0,WINHTTP_NO_REQUEST_DATA,0,0,0))
   323 		g_printerr("Failed to send request (%lu)\n",GetLastError());
   324 	    else if (!WinHttp.ReceiveResponse(request,NULL))
   325 		g_printerr("Failed to receive response (%lu)\n",GetLastError());
   326 	    else
   327 	    {
   328 		do
   329 		{
   330 		    len=0;
   331 		    if (!WinHttp.QueryDataAvailable(request,&len))
   332 			g_printerr(
   333 			  "Error %lu in WinHttpQueryDataAvailable.\n",
   334 			  (unsigned long)GetLastError());
   335 		    buffer=calloc(len+1,1);
   336 		    if (!buffer)
   337 			break;
   338 		    else
   339 		    {
   340 			if (!WinHttp.ReadData(request,(void *)buffer,len,&nb))
   341 			    g_printerr("Error %lu in WinHttpReadData.\n",
   342 			      (unsigned long)GetLastError());
   343 			else
   344 			    printf("%s\n",buffer);
   345 			free(buffer);
   346 		    }
   347 		} while (len>0);
   348 	    }
   349 	    WinHttp.CloseHandle(request);
   350 	}
   351 	else
   352 	    g_printerr("Failed to open request for %s (%lu)\n",path,
   353 	      GetLastError());
   354 	WinHttp.CloseHandle(connection);
   355     }
   356     else
   357 	g_printerr("Failed to open connection to %s\n",hostname);
   358     g_free(hostname);
   359     g_free(path);
   360     return 0;
   361 }
   362 
   363 int main(int argc,char **argv)
   364 {
   365     GError *error=NULL;
   366     GOptionContext *context;
   367     HCERTSTORE store;
   368     HINTERNET session;
   369     wchar_t *ws;
   370     context=g_option_context_new("URL - fetch a URL");
   371     g_option_context_add_main_entries(context,entries,NULL);
   372     if (!g_option_context_parse(context,&argc,&argv,&error))
   373     {
   374 	g_printerr("Option parsing failed: %s\n",error->message);
   375 	exit(1);
   376     }
   377     if (argc!=2)
   378     {
   379 	g_printerr("%s\n",g_option_context_get_help(context,TRUE,NULL));
   380 	exit(1);
   381     }
   382     if (plover_init_winhttp())
   383     {
   384 	g_printerr("HTTP is not supported on this machine\n");
   385 	exit(1);
   386     }
   387     store=plover_p12_import("system.p12",L"xyzzy-ylem");
   388     if (!store)
   389 	exit(1);
   390     ws=g_utf8_to_utf16(PACKAGE_NAME "/" PACKAGE_VERSION,-1,NULL,NULL,NULL);
   391     session=WinHttp.Open(ws,WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
   392       WINHTTP_NO_PROXY_NAME,WINHTTP_NO_PROXY_BYPASS,0);
   393     free(ws);
   394     if (!session)
   395     {
   396 	g_printerr("Failed to open WinHttp session\n");
   397 	exit(1);
   398     }
   399     if (fetch(session,store,argv[1]))
   400 	exit(1);
   401     WinHttp.CloseHandle(session);
   402     CertCloseStore(store,CERT_CLOSE_STORE_FORCE_FLAG);
   403     exit(0);
   404 }
   405 #else	/* !HAVE_WINHTTP_H */
   406 main()
   407 {
   408     g_printerr("HTTP is not supported on this machine\n");
   409     exit(1);
   410 }
   411 #endif	/* HAVE_WINHTTP_H */