/* * A program to explore http support on MS-Windows. */ #include "config.h" #include #include #include #include #if HAVE_WINHTTP_H #include #include #include #ifndef PKCS12_NO_PERSIST_KEY #define PKCS12_NO_PERSIST_KEY 0x00008000 #endif static gboolean debug=FALSE; static GOptionEntry entries[] = { { "debug",'d',0,G_OPTION_ARG_NONE,&debug,"Output debugging",NULL }, { NULL } }; static struct WinHttpFuncs { WINBOOL (WINAPI *AddRequestHeaders)(HINTERNET,LPCWSTR,DWORD,DWORD); WINBOOL (WINAPI *DetectAutoProxyConfigUrl)(DWORD,LPWSTR*); WINBOOL (WINAPI *CheckPlatform)(void); WINBOOL (WINAPI *CloseHandle)(HINTERNET); HINTERNET (WINAPI *Connect)(HINTERNET,LPCWSTR,INTERNET_PORT,DWORD); WINBOOL (WINAPI *CrackUrl)(LPCWSTR,DWORD,DWORD,LPURL_COMPONENTS); WINBOOL (WINAPI *CreateUrl)(LPURL_COMPONENTS,DWORD,LPWSTR,LPDWORD); WINBOOL (WINAPI *GetDefaultProxyConfiguration)(WINHTTP_PROXY_INFO*); WINBOOL (WINAPI *GetIEProxyConfigForCurrentUser)(WINHTTP_CURRENT_USER_IE_PROXY_CONFIG*); WINBOOL (WINAPI *GetProxyForUrl)(HINTERNET,LPCWSTR,WINHTTP_AUTOPROXY_OPTIONS*,WINHTTP_PROXY_INFO*); HINTERNET (WINAPI *Open)(LPCWSTR,DWORD,LPCWSTR,LPCWSTR,DWORD); HINTERNET (WINAPI *OpenRequest)(HINTERNET,LPCWSTR,LPCWSTR,LPCWSTR,LPCWSTR,LPCWSTR*,DWORD); WINBOOL (WINAPI *QueryAuthParams)(HINTERNET,DWORD,LPVOID*); WINBOOL (WINAPI *QueryAuthSchemes)(HINTERNET,LPDWORD,LPDWORD,LPDWORD); WINBOOL (WINAPI *QueryDataAvailable)(HINTERNET,LPDWORD); WINBOOL (WINAPI *QueryHeaders)(HINTERNET,DWORD,LPCWSTR,LPVOID,LPDWORD,LPDWORD); WINBOOL (WINAPI *QueryOption)(HINTERNET,DWORD,LPVOID,LPDWORD); WINBOOL (WINAPI *ReadData)(HINTERNET,LPVOID,DWORD,LPDWORD); WINBOOL (WINAPI *ReceiveResponse)(HINTERNET,LPVOID); WINBOOL (WINAPI *SendRequest)(HINTERNET,LPCWSTR,DWORD,LPVOID,DWORD,DWORD,DWORD_PTR); WINBOOL (WINAPI *SetDefaultProxyConfiguration)(WINHTTP_PROXY_INFO*); WINBOOL (WINAPI *SetCredentials)(HINTERNET,DWORD,DWORD,LPCWSTR,LPCWSTR,LPVOID); WINBOOL (WINAPI *SetOption)(HINTERNET,DWORD,LPVOID,DWORD); WINHTTP_STATUS_CALLBACK (WINAPI *SetStatusCallback)(HINTERNET,WINHTTP_STATUS_CALLBACK,DWORD,DWORD_PTR); WINBOOL (WINAPI *SetTimeouts)(HINTERNET,int,int,int,int); WINBOOL (WINAPI *TimeFromSystemTime)(CONST SYSTEMTIME *,LPWSTR); WINBOOL (WINAPI *TimeToSystemTime)(LPCWSTR,SYSTEMTIME*); WINBOOL (WINAPI *WriteData)(HINTERNET,LPCVOID,DWORD,LPDWORD); } WinHttp; static int plover_init_winhttp(void) { HMODULE module; if (WinHttp.Open) return 0; module=LoadLibraryA("winhttp.dll"); if (!module) return -1; WinHttp.AddRequestHeaders= (WINBOOL (WINAPI *)(HINTERNET,LPCWSTR,DWORD,DWORD)) GetProcAddress(module,"WinHttpAddRequestHeaders"); WinHttp.DetectAutoProxyConfigUrl= (WINBOOL (WINAPI *)(DWORD,LPWSTR*)) GetProcAddress(module,"WinHttpDetectAutoProxyConfigUrl"); WinHttp.CheckPlatform=(WINBOOL (WINAPI *)(void)) GetProcAddress(module,"WinHttpCheckPlatform"); WinHttp.CloseHandle=(WINBOOL (WINAPI *)(HINTERNET)) GetProcAddress(module,"WinHttpCloseHandle"); WinHttp.Connect= (HINTERNET (WINAPI *)(HINTERNET,LPCWSTR,INTERNET_PORT,DWORD)) GetProcAddress(module,"WinHttpConnect"); WinHttp.CrackUrl=(WINBOOL (WINAPI *)(LPCWSTR,DWORD,DWORD,LPURL_COMPONENTS)) GetProcAddress(module,"WinHttpCrackUrl"); WinHttp.CreateUrl= (WINBOOL (WINAPI *)(LPURL_COMPONENTS,DWORD,LPWSTR,LPDWORD)) GetProcAddress(module,"WinHttpCreateUrl"); WinHttp.GetDefaultProxyConfiguration= (WINBOOL (WINAPI *)(WINHTTP_PROXY_INFO*)) GetProcAddress(module,"WinHttpGetDefaultProxyConfiguration"); WinHttp.GetIEProxyConfigForCurrentUser= (WINBOOL (WINAPI *)(WINHTTP_CURRENT_USER_IE_PROXY_CONFIG*)) GetProcAddress(module,"WinHttpGetIEProxyConfigForCurrentUser"); WinHttp.GetProxyForUrl= (WINBOOL (WINAPI *)(HINTERNET,LPCWSTR,WINHTTP_AUTOPROXY_OPTIONS*,WINHTTP_PROXY_INFO*)) GetProcAddress(module,"WinHttpGetProxyForUrl"); WinHttp.Open=(HINTERNET (WINAPI *)(LPCWSTR,DWORD,LPCWSTR,LPCWSTR,DWORD)) GetProcAddress(module,"WinHttpOpen"); WinHttp.OpenRequest= (HINTERNET (WINAPI *)(HINTERNET,LPCWSTR,LPCWSTR,LPCWSTR,LPCWSTR,LPCWSTR*,DWORD)) GetProcAddress(module,"WinHttpOpenRequest"); WinHttp.QueryAuthParams=(WINBOOL (WINAPI *)(HINTERNET,DWORD,LPVOID*)) GetProcAddress(module,"WinHttpQueryAuthParams"); WinHttp.QueryAuthSchemes= (WINBOOL (WINAPI *)(HINTERNET,LPDWORD,LPDWORD,LPDWORD)) GetProcAddress(module,"WinHttpQueryAuthSchemes"); WinHttp.QueryDataAvailable=(WINBOOL (WINAPI *)(HINTERNET,LPDWORD)) GetProcAddress(module,"WinHttpQueryDataAvailable"); WinHttp.QueryHeaders= (WINBOOL (WINAPI *)(HINTERNET,DWORD,LPCWSTR,LPVOID,LPDWORD,LPDWORD)) GetProcAddress(module,"WinHttpQueryHeaders"); WinHttp.QueryOption=(WINBOOL (WINAPI *)(HINTERNET,DWORD,LPVOID,LPDWORD)) GetProcAddress(module,"WinHttpQueryOption"); WinHttp.ReadData=(WINBOOL (WINAPI *)(HINTERNET,LPVOID,DWORD,LPDWORD)) GetProcAddress(module,"WinHttpReadData"); WinHttp.ReceiveResponse=(WINBOOL (WINAPI *)(HINTERNET,LPVOID)) GetProcAddress(module,"WinHttpReceiveResponse"); WinHttp.SendRequest= (WINBOOL (WINAPI *)(HINTERNET,LPCWSTR,DWORD,LPVOID,DWORD,DWORD,DWORD_PTR)) GetProcAddress(module,"WinHttpSendRequest"); WinHttp.SetDefaultProxyConfiguration= (WINBOOL (WINAPI *)(WINHTTP_PROXY_INFO*)) GetProcAddress(module,"WinHttpSetDefaultProxyConfiguration"); WinHttp.SetCredentials= (WINBOOL (WINAPI *)(HINTERNET,DWORD,DWORD,LPCWSTR,LPCWSTR,LPVOID)) GetProcAddress(module,"WinHttpSetCredentials"); WinHttp.SetOption=(WINBOOL (WINAPI *)(HINTERNET,DWORD,LPVOID,DWORD)) GetProcAddress(module,"WinHttpSetOption"); WinHttp.SetStatusCallback= (WINHTTP_STATUS_CALLBACK (WINAPI *)(HINTERNET,WINHTTP_STATUS_CALLBACK,DWORD,DWORD_PTR)) GetProcAddress(module,"WinHttpSetStatusCallback"); WinHttp.SetTimeouts=(WINBOOL (WINAPI *)(HINTERNET,int,int,int,int)) GetProcAddress(module,"WinHttpSetTimeouts"); WinHttp.TimeFromSystemTime=(WINBOOL (WINAPI *)(CONST SYSTEMTIME *,LPWSTR)) GetProcAddress(module,"WinHttpTimeFromSystemTime"); WinHttp.TimeToSystemTime=(WINBOOL (WINAPI *)(LPCWSTR,SYSTEMTIME*)) GetProcAddress(module,"WinHttpTimeToSystemTime"); WinHttp.WriteData=(WINBOOL (WINAPI *)(HINTERNET,LPCVOID,DWORD,LPDWORD)) GetProcAddress(module,"WinHttpWriteData"); if (!WinHttp.CrackUrl || !WinHttp.Open || !WinHttp.Connect || !WinHttp.OpenRequest || !WinHttp.SendRequest || !WinHttp.ReceiveResponse || !WinHttp.QueryDataAvailable || !WinHttp.ReadData || !WinHttp.CloseHandle) { FreeLibrary(module); WinHttp.Open=NULL; return -1; } return 0; } /* * This will find ${PREFIX}/${file} if it exists, but it may also * find ${PREFIX}/.../${file} if ${PREFIX}/${file} does not exist. * This seems unlikely and won't do any harm should it occur. */ gchar *find_prefixed_file(const char *file) { int i,len; const char *name; char *install_root; struct razor_set *set; struct razor_atomic *atomic; struct razor_package *package; struct razor_package_iterator *pi; struct razor_file_iterator *fi; gchar *retval=NULL; len=strlen(file); install_root=getenv("RAZOR_ROOT"); if (!install_root) install_root=""; atomic=razor_atomic_open("Query packages"); set=razor_root_open_read_only(install_root,atomic); if (set) { pi=razor_package_iterator_create(set); while (razor_package_iterator_next(pi,&package,RAZOR_DETAIL_LAST)) { fi=razor_file_iterator_create(set,package,0); while (!retval && razor_file_iterator_next(fi,&name)) { i=strlen(name)-len; if (i>0 && name[i-1]=='/' && !strcmp(name+i,file)) { if (!retval || strlen(retval)>strlen(name)) { g_free(retval); retval=g_strdup(name); } } } razor_file_iterator_destroy(fi); } razor_package_iterator_destroy(pi); razor_set_unref(set); } razor_atomic_destroy(atomic); return retval; } static HCERTSTORE plover_p12_import(const char *file,const wchar_t *password) { GError *error=NULL; gchar *p12,*path,*s; gsize len; CRYPT_DATA_BLOB pfx; HCERTSTORE store; s=g_build_path("/","etc","pki",file,NULL); path=find_prefixed_file(s); if (!path) { g_printerr("%s: Not installed\n",s); g_free(s); return NULL; } g_free(s); if (!g_file_get_contents(path,&p12,&len,&error)) { g_printerr("%s: %s\n",path,error->message); g_free(path); g_error_free(error); return NULL; } g_free(path); pfx.pbData=(BYTE *)p12; pfx.cbData=len; store=PFXImportCertStore(&pfx,password,PKCS12_NO_PERSIST_KEY); if (!store) g_printerr("PFXImportCertStore failed. Err: %lu\n",GetLastError()); g_free(p12); return store; } static const CERT_CONTEXT *plover_get_client_certificate(HCERTSTORE store) { const CERT_CONTEXT *iter,*cert; cert=NULL; iter=CertEnumCertificatesInStore(store,NULL); while(iter) { if (!cert) cert=CertDuplicateCertificateContext(iter); iter=CertEnumCertificatesInStore(store,iter); } return cert; } void CALLBACK plover_status_callback(HINTERNET handle,DWORD_PTR context, DWORD status,LPVOID status_information,DWORD status_information_length) { g_printerr("CB status 0x%lX",status); if (status==WINHTTP_CALLBACK_STATUS_SECURE_FAILURE) g_printerr(", flag 0x%lX",*(DWORD *)status_information); g_printerr("\n"); } int fetch(HINTERNET session,HCERTSTORE store,const char *url) { URL_COMPONENTS components={0}; HINTERNET connection,request; DWORD len,nb,options; char *buffer; wchar_t *ws; glong ws_len; gchar *hostname,*path; const CERT_CONTEXT *client_cert; components.dwStructSize=sizeof(components); components.dwSchemeLength=(DWORD)-1; components.dwHostNameLength=(DWORD)-1; components.dwUserNameLength=(DWORD)-1; components.dwPasswordLength=(DWORD)-1; components.dwUrlPathLength=(DWORD)-1; ws=g_utf8_to_utf16(url,-1,NULL,&ws_len,NULL); if (!ws || !WinHttp.CrackUrl(ws,ws_len,0,&components)) { g_printerr("%s: Invalid or unsupported URL\n",url); return -1; } /* ICU_REJECT_USERPWD is not supported under Windows XP */ if (components.dwUserNameLength || components.dwPasswordLength) { g_printerr("%s: Credentials not supported\n",url); return -1; } hostname=g_utf16_to_utf8(components.lpszHostName, components.dwHostNameLength,NULL,NULL,NULL); path=g_utf16_to_utf8(components.lpszUrlPath,components.dwUrlPathLength,NULL, NULL,NULL); g_free(ws); ws=g_utf8_to_utf16(hostname,-1,NULL,NULL,NULL); connection=WinHttp.Connect(session,ws, components.nPort?components.nPort:INTERNET_DEFAULT_PORT,0); g_free(ws); if (connection) { ws=g_utf8_to_utf16(path,-1,NULL,NULL,NULL); request=WinHttp.OpenRequest(connection,L"GET",ws,NULL, WINHTTP_NO_REFERER,WINHTTP_DEFAULT_ACCEPT_TYPES, components.nScheme==INTERNET_SCHEME_HTTPS?WINHTTP_FLAG_SECURE:0); g_free(ws); if (request) { if (debug) (void)WinHttp.SetStatusCallback(request,plover_status_callback, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS,0); if (components.nScheme==INTERNET_SCHEME_HTTPS && WinHttp.SetOption) { options=SECURITY_FLAG_IGNORE_UNKNOWN_CA; (void)WinHttp.SetOption(request,WINHTTP_OPTION_SECURITY_FLAGS, &options,sizeof(options)); client_cert=plover_get_client_certificate(store); if (client_cert) { if (!WinHttp.SetOption(request, WINHTTP_OPTION_CLIENT_CERT_CONTEXT,(void *)client_cert, sizeof(*client_cert))) g_printerr("Failed to set client certificate (%lu)\n", GetLastError()); } else g_printerr("No client certificate found\n"); } if (!WinHttp.SendRequest(request,WINHTTP_NO_ADDITIONAL_HEADERS, 0,WINHTTP_NO_REQUEST_DATA,0,0,0)) g_printerr("Failed to send request (%lu)\n",GetLastError()); else if (!WinHttp.ReceiveResponse(request,NULL)) g_printerr("Failed to receive response (%lu)\n",GetLastError()); else { do { len=0; if (!WinHttp.QueryDataAvailable(request,&len)) g_printerr( "Error %lu in WinHttpQueryDataAvailable.\n", (unsigned long)GetLastError()); buffer=calloc(len+1,1); if (!buffer) break; else { if (!WinHttp.ReadData(request,(void *)buffer,len,&nb)) g_printerr("Error %lu in WinHttpReadData.\n", (unsigned long)GetLastError()); else printf("%s\n",buffer); free(buffer); } } while (len>0); } WinHttp.CloseHandle(request); } else g_printerr("Failed to open request for %s (%lu)\n",path, GetLastError()); WinHttp.CloseHandle(connection); } else g_printerr("Failed to open connection to %s\n",hostname); g_free(hostname); g_free(path); return 0; } int main(int argc,char **argv) { GError *error=NULL; GOptionContext *context; HCERTSTORE store; HINTERNET session; wchar_t *ws; context=g_option_context_new("URL - fetch a URL"); g_option_context_add_main_entries(context,entries,NULL); if (!g_option_context_parse(context,&argc,&argv,&error)) { g_printerr("Option parsing failed: %s\n",error->message); exit(1); } if (argc!=2) { g_printerr("%s\n",g_option_context_get_help(context,TRUE,NULL)); exit(1); } if (plover_init_winhttp()) { g_printerr("HTTP is not supported on this machine\n"); exit(1); } store=plover_p12_import("system.p12",L"xyzzy-ylem"); if (!store) exit(1); ws=g_utf8_to_utf16(PACKAGE_NAME "/" PACKAGE_VERSION,-1,NULL,NULL,NULL); session=WinHttp.Open(ws,WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME,WINHTTP_NO_PROXY_BYPASS,0); free(ws); if (!session) { g_printerr("Failed to open WinHttp session\n"); exit(1); } if (fetch(session,store,argv[1])) exit(1); WinHttp.CloseHandle(session); CertCloseStore(store,CERT_CLOSE_STORE_FORCE_FLAG); exit(0); } #else /* !HAVE_WINHTTP_H */ main() { g_printerr("HTTP is not supported on this machine\n"); exit(1); } #endif /* HAVE_WINHTTP_H */