Merged SSH1 robustness changes from 0.55 release branch on to trunk.
[u/mdw/putty] / pageant.c
index 4cbc8ae..2ebf3ab 100644 (file)
--- a/pageant.c
+++ b/pageant.c
@@ -2,20 +2,22 @@
  * Pageant: the PuTTY Authentication Agent.
  */
 
-#include <windows.h>
-#ifndef NO_SECURITY
-#include <aclapi.h>
-#endif
 #include <stdio.h>
 #include <stdlib.h>
 #include <ctype.h>
 #include <assert.h>
 #include <tchar.h>
 
+#include "putty.h"
 #include "ssh.h"
 #include "misc.h"
 #include "tree234.h"
-#include "winstuff.h"
+
+#include <shellapi.h>
+
+#ifndef NO_SECURITY
+#include <aclapi.h>
+#endif
 
 #define IDI_MAINICON 200
 #define IDI_TRAYICON 201
@@ -49,7 +51,7 @@ static HMENU systray_menu, session_menu;
 static int already_running;
 static int requested_help;
 
-static char *help_path;
+char *help_path;
 static char *putty_path;
 
 #define IDM_PUTTY         0x0060
@@ -59,6 +61,23 @@ static char *putty_path;
 #define PUTTY_DEFAULT     "Default%20Settings"
 static int initial_menuitems_count;
 
+/*
+ * Print a modal (Really Bad) message box and perform a fatal exit.
+ */
+void modalfatalbox(char *fmt, ...)
+{
+    va_list ap;
+    char *buf;
+
+    va_start(ap, fmt);
+    buf = dupvprintf(fmt, ap);
+    va_end(ap);
+    MessageBox(main_hwnd, buf, "Pageant Fatal Error",
+              MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
+    sfree(buf);
+    exit(1);
+}
+
 /* Un-munge session names out of the registry. */
 static void unmungestr(char *in, char *out, int outlen)
 {
@@ -96,18 +115,12 @@ static gsi_fn_t getsecurityinfo;
 #endif
 
 /*
- * Exports from pageantc.c
- */
-void agent_query(void *in, int inlen, void **out, int *outlen);
-int agent_exists(void);
-
-/*
  * Forward references
  */
 static void *make_keylist1(int *length);
 static void *make_keylist2(int *length);
-static void *get_keylist1(void);
-static void *get_keylist2(void);
+static void *get_keylist1(int *length);
+static void *get_keylist2(int *length);
 
 /*
  * We need this to link with the RSA code, because rsaencrypt()
@@ -137,14 +150,6 @@ struct blob {
 };
 static int cmpkeys_ssh2_asymm(void *av, void *bv);
 
-/*
- * This function is needed to link with the DES code. We need not
- * have it do anything at all.
- */
-void logevent(char *msg)
-{
-}
-
 #define GET_32BIT(cp) \
     (((unsigned long)(unsigned char)(cp)[0] << 24) | \
     ((unsigned long)(unsigned char)(cp)[1] << 16) | \
@@ -221,7 +226,7 @@ static int CALLBACK AboutProc(HWND hwnd, UINT msg,
            return 0;
          case 101:
            EnableWindow(hwnd, 0);
-           DialogBox(instance, MAKEINTRESOURCE(214), NULL, LicenceProc);
+           DialogBox(instance, MAKEINTRESOURCE(214), hwnd, LicenceProc);
            EnableWindow(hwnd, 1);
            SetActiveWindow(hwnd);
            return 0;
@@ -381,7 +386,7 @@ static void keylist_update(void)
 /*
  * This function loads a key from a file and adds it.
  */
-static void add_keyfile(char *filename)
+static void add_keyfile(Filename filename)
 {
     char passphrase[PASSPHRASE_MAXLEN];
     struct RSAKey *rkey = NULL;
@@ -394,7 +399,7 @@ static void add_keyfile(char *filename)
     int type;
     int original_pass;
        
-    type = key_type(filename);
+    type = key_type(&filename);
     if (type != SSH_KEYTYPE_SSH1 && type != SSH_KEYTYPE_SSH2) {
        char msg[256];
        sprintf(msg, "Couldn't load this key (%s)", key_type_to_str(type));
@@ -409,35 +414,41 @@ static void add_keyfile(char *filename)
     {
        void *blob;
        unsigned char *keylist, *p;
-       int i, nkeys, bloblen;
+       int i, nkeys, bloblen, keylistlen;
 
        if (type == SSH_KEYTYPE_SSH1) {
-           if (!rsakey_pubblob(filename, &blob, &bloblen)) {
+           if (!rsakey_pubblob(&filename, &blob, &bloblen, NULL)) {
                MessageBox(NULL, "Couldn't load private key.", APPNAME,
                           MB_OK | MB_ICONERROR);
                return;
            }
-           keylist = get_keylist1();
+           keylist = get_keylist1(&keylistlen);
        } else {
            unsigned char *blob2;
-           blob = ssh2_userkey_loadpub(filename, NULL, &bloblen);
+           blob = ssh2_userkey_loadpub(&filename, NULL, &bloblen, NULL);
            if (!blob) {
                MessageBox(NULL, "Couldn't load private key.", APPNAME,
                           MB_OK | MB_ICONERROR);
                return;
            }
            /* For our purposes we want the blob prefixed with its length */
-           blob2 = smalloc(bloblen+4);
+           blob2 = snewn(bloblen+4, unsigned char);
            PUT_32BIT(blob2, bloblen);
            memcpy(blob2 + 4, blob, bloblen);
            sfree(blob);
            blob = blob2;
 
-           keylist = get_keylist2();
+           keylist = get_keylist2(&keylistlen);
        }
        if (keylist) {
+           if (keylistlen < 4) {
+               MessageBox(NULL, "Received broken key list?!", APPNAME,
+                          MB_OK | MB_ICONERROR);
+               return;
+           }
            nkeys = GET_32BIT(keylist);
            p = keylist + 4;
+           keylistlen -= 4;
 
            for (i = 0; i < nkeys; i++) {
                if (!memcmp(blob, p, bloblen)) {
@@ -447,12 +458,48 @@ static void add_keyfile(char *filename)
                    return;
                }
                /* Now skip over public blob */
-               if (type == SSH_KEYTYPE_SSH1)
-                   p += rsa_public_blob_len(p);
-               else
-                   p += 4 + GET_32BIT(p);
+               if (type == SSH_KEYTYPE_SSH1) {
+                   int n = rsa_public_blob_len(p, keylistlen);
+                   if (n < 0) {
+                       MessageBox(NULL, "Received broken key list?!", APPNAME,
+                                  MB_OK | MB_ICONERROR);
+                       return;
+                   }
+                   p += n;
+                   keylistlen -= n;
+               } else {
+                   int n;
+                   if (keylistlen < 4) {
+                       MessageBox(NULL, "Received broken key list?!", APPNAME,
+                                  MB_OK | MB_ICONERROR);
+                       return;
+                   }
+                   n = 4 + GET_32BIT(p);
+                   if (keylistlen < n) {
+                       MessageBox(NULL, "Received broken key list?!", APPNAME,
+                                  MB_OK | MB_ICONERROR);
+                       return;
+                   }
+                   p += n;
+                   keylistlen -= n;
+               }
                /* Now skip over comment field */
-               p += 4 + GET_32BIT(p);
+               {
+                   int n;
+                   if (keylistlen < 4) {
+                       MessageBox(NULL, "Received broken key list?!", APPNAME,
+                                  MB_OK | MB_ICONERROR);
+                       return;
+                   }
+                   n = 4 + GET_32BIT(p);
+                   if (keylistlen < n) {
+                       MessageBox(NULL, "Received broken key list?!", APPNAME,
+                                  MB_OK | MB_ICONERROR);
+                       return;
+                   }
+                   p += n;
+                   keylistlen -= n;
+               }
            }
 
            sfree(keylist);
@@ -462,12 +509,12 @@ static void add_keyfile(char *filename)
     }
 
     if (type == SSH_KEYTYPE_SSH1)
-       needs_pass = rsakey_encrypted(filename, &comment);
+       needs_pass = rsakey_encrypted(&filename, &comment);
     else
-       needs_pass = ssh2_userkey_encrypted(filename, &comment);
+       needs_pass = ssh2_userkey_encrypted(&filename, &comment);
     attempts = 0;
     if (type == SSH_KEYTYPE_SSH1)
-       rkey = smalloc(sizeof(*rkey));
+       rkey = snew(struct RSAKey);
     pps.passphrase = passphrase;
     pps.comment = comment;
     original_pass = 0;
@@ -494,9 +541,9 @@ static void add_keyfile(char *filename)
        } else
            *passphrase = '\0';
        if (type == SSH_KEYTYPE_SSH1)
-           ret = loadrsakey(filename, rkey, passphrase);
+           ret = loadrsakey(&filename, rkey, passphrase, NULL);
        else {
-           skey = ssh2_load_userkey(filename, passphrase);
+           skey = ssh2_load_userkey(&filename, passphrase, NULL);
            if (skey == SSH2_WRONG_PASSPHRASE)
                ret = -1;
            else if (!skey)
@@ -526,7 +573,7 @@ static void add_keyfile(char *filename)
        if (already_running) {
            unsigned char *request, *response;
            void *vresponse;
-           int reqlen, clen, resplen;
+           int reqlen, clen, resplen, ret;
 
            clen = strlen(rkey->comment);
 
@@ -540,7 +587,7 @@ static void add_keyfile(char *filename)
                ssh1_bignum_length(rkey->q) + 4 + clen  /* comment */
                ;
 
-           request = smalloc(reqlen);
+           request = snewn(reqlen, unsigned char);
 
            request[4] = SSH1_AGENTC_ADD_RSA_IDENTITY;
            reqlen = 5;
@@ -559,7 +606,9 @@ static void add_keyfile(char *filename)
            reqlen += 4 + clen;
            PUT_32BIT(request, reqlen - 4);
 
-           agent_query(request, reqlen, &vresponse, &resplen);
+           ret = agent_query(request, reqlen, &vresponse, &resplen,
+                             NULL, NULL);
+           assert(ret == 1);
            response = vresponse;
            if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
                MessageBox(NULL, "The already running Pageant "
@@ -576,7 +625,7 @@ static void add_keyfile(char *filename)
        if (already_running) {
            unsigned char *request, *response;
            void *vresponse;
-           int reqlen, alglen, clen, keybloblen, resplen;
+           int reqlen, alglen, clen, keybloblen, resplen, ret;
            alglen = strlen(skey->alg->name);
            clen = strlen(skey->comment);
 
@@ -588,7 +637,7 @@ static void add_keyfile(char *filename)
                4 + clen               /* comment */
                ;
 
-           request = smalloc(reqlen);
+           request = snewn(reqlen, unsigned char);
 
            request[4] = SSH2_AGENTC_ADD_IDENTITY;
            reqlen = 5;
@@ -601,10 +650,12 @@ static void add_keyfile(char *filename)
                                                keybloblen);
            PUT_32BIT(request + reqlen, clen);
            memcpy(request + reqlen + 4, skey->comment, clen);
-           PUT_32BIT(request, reqlen - 4);
            reqlen += clen + 4;
+           PUT_32BIT(request, reqlen - 4);
 
-           agent_query(request, reqlen, &vresponse, &resplen);
+           ret = agent_query(request, reqlen, &vresponse, &resplen,
+                             NULL, NULL);
+           assert(ret == 1);
            response = vresponse;
            if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
                MessageBox(NULL, "The already running Pageant "
@@ -647,7 +698,7 @@ static void *make_keylist1(int *length)
     }
 
     /* Allocate the buffer. */
-    p = ret = smalloc(len);
+    p = ret = snewn(len, unsigned char);
     if (length) *length = len;
 
     PUT_32BIT(p, nkeys);
@@ -692,7 +743,7 @@ static void *make_keylist2(int *length)
     }
 
     /* Allocate the buffer. */
-    p = ret = smalloc(len);
+    p = ret = snewn(len, unsigned char);
     if (length) *length = len;
 
     /*
@@ -722,27 +773,31 @@ static void *make_keylist2(int *length)
  * calling make_keylist1 (if that's us) or sending a message to the
  * primary Pageant (if it's not).
  */
-static void *get_keylist1(void)
+static void *get_keylist1(int *length)
 {
     void *ret;
 
     if (already_running) {
        unsigned char request[5], *response;
        void *vresponse;
-       int resplen;
+       int resplen, retval;
        request[4] = SSH1_AGENTC_REQUEST_RSA_IDENTITIES;
        PUT_32BIT(request, 4);
 
-       agent_query(request, 5, &vresponse, &resplen);
+       retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL);
+       assert(retval == 1);
        response = vresponse;
        if (resplen < 5 || response[4] != SSH1_AGENT_RSA_IDENTITIES_ANSWER)
            return NULL;
 
-       ret = smalloc(resplen-5);
+       ret = snewn(resplen-5, unsigned char);
        memcpy(ret, response+5, resplen-5);
        sfree(response);
+
+       if (length)
+           *length = resplen-5;
     } else {
-       ret = make_keylist1(NULL);
+       ret = make_keylist1(length);
     }
     return ret;
 }
@@ -752,28 +807,32 @@ static void *get_keylist1(void)
  * calling make_keylist2 (if that's us) or sending a message to the
  * primary Pageant (if it's not).
  */
-static void *get_keylist2(void)
+static void *get_keylist2(int *length)
 {
     void *ret;
 
     if (already_running) {
        unsigned char request[5], *response;
        void *vresponse;
-       int resplen;
+       int resplen, retval;
 
        request[4] = SSH2_AGENTC_REQUEST_IDENTITIES;
        PUT_32BIT(request, 4);
 
-       agent_query(request, 5, &vresponse, &resplen);
+       retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL);
+       assert(retval == 1);
        response = vresponse;
        if (resplen < 5 || response[4] != SSH2_AGENT_IDENTITIES_ANSWER)
            return NULL;
 
-       ret = smalloc(resplen-5);
+       ret = snewn(resplen-5, unsigned char);
        memcpy(ret, response+5, resplen-5);
        sfree(response);
+
+       if (length)
+           *length = resplen-5;
     } else {
-       ret = make_keylist2(NULL);
+       ret = make_keylist2(length);
     }
     return ret;
 }
@@ -785,11 +844,19 @@ static void answer_msg(void *msg)
 {
     unsigned char *p = msg;
     unsigned char *ret = msg;
+    unsigned char *msgend;
     int type;
 
     /*
+     * Get the message length.
+     */
+    msgend = p + 4 + GET_32BIT(p);
+
+    /*
      * Get the message type.
      */
+    if (msgend < p+5)
+       goto failure;
     type = p[4];
 
     p += 5;
@@ -846,12 +913,28 @@ static void answer_msg(void *msg)
            int i, len;
 
            p += 4;
-           p += ssh1_read_bignum(p, &reqkey.exponent);
-           p += ssh1_read_bignum(p, &reqkey.modulus);
-           p += ssh1_read_bignum(p, &challenge);
+           i = ssh1_read_bignum(p, msgend - p, &reqkey.exponent);
+           if (i < 0)
+               goto failure;
+           p += i;
+           i = ssh1_read_bignum(p, msgend - p, &reqkey.modulus);
+           if (i < 0)
+               goto failure;
+           p += i;
+           i = ssh1_read_bignum(p, msgend - p, &challenge);
+           if (i < 0)
+               goto failure;
+           p += i;
+           if (msgend < p+16) {
+               freebn(reqkey.exponent);
+               freebn(reqkey.modulus);
+               freebn(challenge);
+               goto failure;
+           }
            memcpy(response_source + 32, p, 16);
            p += 16;
-           if (GET_32BIT(p) != 1 ||
+           if (msgend < p+4 ||
+               GET_32BIT(p) != 1 ||
                (key = find234(rsakeys, &reqkey, NULL)) == NULL) {
                freebn(reqkey.exponent);
                freebn(reqkey.modulus);
@@ -893,12 +976,20 @@ static void answer_msg(void *msg)
            unsigned char *data, *signature;
            int datalen, siglen, len;
 
+           if (msgend < p+4)
+               goto failure;
            b.len = GET_32BIT(p);
            p += 4;
+           if (msgend < p+b.len)
+               goto failure;
            b.blob = p;
            p += b.len;
+           if (msgend < p+4)
+               goto failure;
            datalen = GET_32BIT(p);
            p += 4;
+           if (msgend < p+datalen)
+               goto failure;
            data = p;
            key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
            if (!key)
@@ -920,16 +1011,65 @@ static void answer_msg(void *msg)
        {
            struct RSAKey *key;
            char *comment;
-            int commentlen;
-           key = smalloc(sizeof(struct RSAKey));
+            int n, commentlen;
+
+           key = snew(struct RSAKey);
            memset(key, 0, sizeof(struct RSAKey));
-           p += makekey(p, key, NULL, 1);
-           p += makeprivate(p, key);
-           p += ssh1_read_bignum(p, &key->iqmp);       /* p^-1 mod q */
-           p += ssh1_read_bignum(p, &key->p);  /* p */
-           p += ssh1_read_bignum(p, &key->q);  /* q */
+
+           n = makekey(p, msgend - p, key, NULL, 1);
+           if (n < 0) {
+               freersakey(key);
+               sfree(key);
+               goto failure;
+           }
+           p += n;
+
+           n = makeprivate(p, msgend - p, key);
+           if (n < 0) {
+               freersakey(key);
+               sfree(key);
+               goto failure;
+           }
+           p += n;
+
+           n = ssh1_read_bignum(p, msgend - p, &key->iqmp);  /* p^-1 mod q */
+           if (n < 0) {
+               freersakey(key);
+               sfree(key);
+               goto failure;
+           }
+           p += n;
+
+           n = ssh1_read_bignum(p, msgend - p, &key->p);  /* p */
+           if (n < 0) {
+               freersakey(key);
+               sfree(key);
+               goto failure;
+           }
+           p += n;
+
+           n = ssh1_read_bignum(p, msgend - p, &key->q);  /* q */
+           if (n < 0) {
+               freersakey(key);
+               sfree(key);
+               goto failure;
+           }
+           p += n;
+
+           if (msgend < p+4) {
+               freersakey(key);
+               sfree(key);
+               goto failure;
+           }
             commentlen = GET_32BIT(p);
-           comment = smalloc(commentlen+1);
+
+           if (msgend < p+commentlen) {
+               freersakey(key);
+               sfree(key);
+               goto failure;
+           }
+
+           comment = snewn(commentlen+1, char);
            if (comment) {
                memcpy(comment, p + 4, commentlen);
                 comment[commentlen] = '\0';
@@ -957,12 +1097,17 @@ static void answer_msg(void *msg)
            int alglen, commlen;
            int bloblen;
 
-           key = smalloc(sizeof(struct ssh2_userkey));
 
+           if (msgend < p+4)
+               goto failure;
            alglen = GET_32BIT(p);
            p += 4;
+           if (msgend < p+alglen)
+               goto failure;
            alg = p;
            p += alglen;
+
+           key = snew(struct ssh2_userkey);
            /* Add further algorithm names here. */
            if (alglen == 7 && !memcmp(alg, "ssh-rsa", 7))
                key->alg = &ssh_rsa;
@@ -973,19 +1118,33 @@ static void answer_msg(void *msg)
                goto failure;
            }
 
-           bloblen =
-               GET_32BIT((unsigned char *) msg) - (p -
-                                                   (unsigned char *) msg -
-                                                   4);
+           bloblen = msgend - p;
            key->data = key->alg->openssh_createkey(&p, &bloblen);
            if (!key->data) {
                sfree(key);
                goto failure;
            }
+
+           /*
+            * p has been advanced by openssh_createkey, but
+            * certainly not _beyond_ the end of the buffer.
+            */
+           assert(p <= msgend);
+
+           if (msgend < p+4) {
+               key->alg->freekey(key->data);
+               sfree(key);
+               goto failure;
+           }
            commlen = GET_32BIT(p);
            p += 4;
 
-           comment = smalloc(commlen + 1);
+           if (msgend < p+commlen) {
+               key->alg->freekey(key->data);
+               sfree(key);
+               goto failure;
+           }
+           comment = snewn(commlen + 1, char);
            if (comment) {
                memcpy(comment, p, commlen);
                comment[commlen] = '\0';
@@ -1012,8 +1171,12 @@ static void answer_msg(void *msg)
         */
        {
            struct RSAKey reqkey, *key;
+           int n;
+
+           n = makekey(p, msgend - p, &reqkey, NULL, 0);
+           if (n < 0)
+               goto failure;
 
-           p += makekey(p, &reqkey, NULL, 0);
            key = find234(rsakeys, &reqkey, NULL);
            freebn(reqkey.exponent);
            freebn(reqkey.modulus);
@@ -1038,10 +1201,16 @@ static void answer_msg(void *msg)
            struct ssh2_userkey *key;
            struct blob b;
 
+           if (msgend < p+4)
+               goto failure;
            b.len = GET_32BIT(p);
            p += 4;
+
+           if (msgend < p+b.len)
+               goto failure;
            b.blob = p;
            p += b.len;
+
            key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
            if (!key)
                goto failure;
@@ -1230,7 +1399,7 @@ static void prompt_add_keyfile(void)
 {
     OPENFILENAME of;
     char filename[FILENAME_MAX];
-    char *filelist = smalloc(8192);
+    char *filelist = snewn(8192, char);
     char *filewalker;
     int n, dirlen;
        
@@ -1241,7 +1410,8 @@ static void prompt_add_keyfile(void)
     of.lStructSize = sizeof(of);
 #endif
     of.hwndOwner = main_hwnd;
-    of.lpstrFilter = "PuTTY Private Key Files\0*.PPK\0AllFiles\0*\0\0\0";
+    of.lpstrFilter = "PuTTY Private Key Files (*.ppk)\0*.ppk\0"
+       "All Files (*.*)\0*\0\0\0";
     of.lpstrCustomFilter = NULL;
     of.nFilterIndex = 1;
     of.lpstrFile = filelist;
@@ -1254,7 +1424,7 @@ static void prompt_add_keyfile(void)
     if (GetOpenFileName(&of)) {
        if(strlen(filelist) > of.nFileOffset)
            /* Only one filename returned? */
-           add_keyfile(filelist);
+           add_keyfile(filename_from_str(filelist));
        else {
            /* we are returned a bunch of strings, end to
             * end. first string is the directory, the
@@ -1282,7 +1452,7 @@ static void prompt_add_keyfile(void)
                memcpy(filename + dirlen, filewalker, n);
                filewalker += n;
 
-               add_keyfile(filename);
+               add_keyfile(filename_from_str(filename));
            }
        }
 
@@ -1376,7 +1546,7 @@ static int CALLBACK KeyListProc(HWND hwnd, UINT msg,
                }
 
                /* get item indices in an array */
-               selectedArray = smalloc(numSelected * sizeof(int));
+               selectedArray = snewn(numSelected, int);
                SendDlgItemMessage(hwnd, 100, LB_GETSELITEMS,
                                numSelected, (WPARAM)selectedArray);
                
@@ -1596,14 +1766,17 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                keylist = CreateDialog(instance, MAKEINTRESOURCE(211),
                                       NULL, KeyListProc);
                ShowWindow(keylist, SW_SHOWNORMAL);
-               /* 
-                * Sometimes the window comes up minimised / hidden
-                * for no obvious reason. Prevent this.
-                */
-               SetForegroundWindow(keylist);
-               SetWindowPos(keylist, HWND_TOP, 0, 0, 0, 0,
-                            SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
            }
+           /* 
+            * Sometimes the window comes up minimised / hidden for
+            * no obvious reason. Prevent this. This also brings it
+            * to the front if it's already present (the user
+            * selected View Keys because they wanted to _see_ the
+            * thing).
+            */
+           SetForegroundWindow(keylist);
+           SetWindowPos(keylist, HWND_TOP, 0, 0, 0, 0,
+                        SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
            break;
          case IDM_ADDKEY:
            if (passphrase_box) {
@@ -1770,20 +1943,32 @@ void spawn_cmd(char *cmdline, char * args, int show)
 {
     if (ShellExecute(NULL, _T("open"), cmdline,
                     args, NULL, show) <= (HINSTANCE) 32) {
-       TCHAR sMsg[140];
-       sprintf(sMsg, _T("Failed to run \"%.100s\", Error: %d"), cmdline,
-               (int)GetLastError());
-       MessageBox(NULL, sMsg, APPNAME, MB_OK | MB_ICONEXCLAMATION);
+       char *msg;
+       msg = dupprintf("Failed to run \"%.100s\", Error: %d", cmdline,
+                       (int)GetLastError());
+       MessageBox(NULL, msg, APPNAME, MB_OK | MB_ICONEXCLAMATION);
+       sfree(msg);
     }
 }
 
+/*
+ * This is a can't-happen stub, since Pageant never makes
+ * asynchronous agent requests.
+ */
+void agent_schedule_callback(void (*callback)(void *, void *, int),
+                            void *callback_ctx, void *data, int len)
+{
+    assert(!"We shouldn't get here");
+}
+
 void cleanup_exit(int code) { exit(code); }
 
+int flags = FLAG_SYNCAGENT;
+
 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
 {
     WNDCLASS wndclass;
     MSG msg;
-    OSVERSIONINFO osi;
     HMODULE advapi;
     char *command = NULL;
     int added_keys = 0;
@@ -1794,9 +1979,11 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
      * Determine whether we're an NT system (should have security
      * APIs) or a non-NT system (don't do security).
      */
-    memset(&osi, 0, sizeof(OSVERSIONINFO));
-    osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
-    if (GetVersionEx(&osi) && osi.dwPlatformId == VER_PLATFORM_WIN32_NT) {
+    if (!init_winver())
+    {
+       modalfatalbox("Windows refuses to report a version");
+    }
+    if (osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT) {
        has_security = TRUE;
     } else
        has_security = FALSE;
@@ -1954,7 +2141,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
                command = "";
            break;
        } else {
-           add_keyfile(argv[i]);
+           add_keyfile(filename_from_str(argv[i]));
            added_keys = TRUE;
        }
     }
@@ -1997,8 +2184,11 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
      * Main message loop.
      */
     while (GetMessage(&msg, NULL, 0, 0) == 1) {
-       TranslateMessage(&msg);
-       DispatchMessage(&msg);
+       if (!(IsWindow(keylist) && IsDialogMessage(keylist, &msg)) &&
+           !(IsWindow(aboutbox) && IsDialogMessage(aboutbox, &msg))) {
+           TranslateMessage(&msg);
+           DispatchMessage(&msg);
+       }
     }
 
     /* Clean up the system tray icon */