Add utility program for fetching URL with installed client certificate 0.4
authorJ. Ali Harlow <ali@juiblex.co.uk>
Thu, 10 Nov 2011 11:19:34 +0000 (11:19 +0000)
committerJ. Ali Harlow <ali@juiblex.co.uk>
Thu, 10 Nov 2011 11:19:34 +0000 (11:19 +0000)
app-manager/Makefile.am
app-manager/fetch.c [new file with mode: 0644]

index 0902882..bcee882 100644 (file)
@@ -1,9 +1,11 @@
 AM_CFLAGS=$(GUI_CFLAGS) -g -DPLOVER_DATADIR=\""$(pkgdatadir)"\" -I$(top_srcdir)
 LDADD=../plover/libplover.la ../plover-gtk/libplover-gtk.la $(GUI_LIBS)
 
-bin_PROGRAMS=app-manager
+bin_PROGRAMS=app-manager fetch
 app_manager_SOURCES=app-manager.c app-manager.h packagelist.c applications.c \
        localmedia.c localmedia.h
+fetch_SOURCES=fetch.c
+fetch_LDADD=$(LDADD) -lcrypt32
 if HAVE_WINDRES
 app_manager_SOURCES+=resources.rc app-manager.exe.manifest
 endif
diff --git a/app-manager/fetch.c b/app-manager/fetch.c
new file mode 100644 (file)
index 0000000..8980899
--- /dev/null
@@ -0,0 +1,411 @@
+/*
+ * A program to explore http support on MS-Windows.
+ */
+
+#include "config.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <glib.h>
+#include <razor.h>
+#if HAVE_WINHTTP_H
+#include <windows.h>
+#include <winhttp.h>
+#include <wincrypt.h>
+
+#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 */