Having now compiled the last few days' changes with MSVC, it's turned
[u/mdw/putty] / pageant.c
index 370c099..6f94262 100644 (file)
--- a/pageant.c
+++ b/pageant.c
@@ -9,6 +9,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <ctype.h>
+#include <tchar.h>
 
 #include "ssh.h"
 #include "tree234.h"
@@ -41,6 +42,7 @@ static HWND hwnd;
 static HWND keylist;
 static HWND aboutbox;
 static HMENU systray_menu;
+static int already_running;
 
 static tree234 *rsakeys, *ssh2keys;
 
@@ -54,6 +56,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);
+
+/*
  * We need this to link with the RSA code, because rsaencrypt()
  * pads its data with random bytes. Since we only use rsadecrypt()
  * and the signing functions, which are deterministic, this should
@@ -222,11 +230,11 @@ static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,
 static void keylist_update(void) {
     struct RSAKey *rkey;
     struct ssh2_userkey *skey;
-    enum234 e;
+    int i;
 
     if (keylist) {
         SendDlgItemMessage(keylist, 100, LB_RESETCONTENT, 0, 0);
-        for (rkey = first234(rsakeys, &e); rkey; rkey = next234(&e)) {
+        for (i = 0; NULL != (rkey = index234(rsakeys, i)); i++) {
             char listentry[512], *p;
             /*
              * Replace two spaces in the fingerprint with tabs, for
@@ -240,7 +248,7 @@ static void keylist_update(void) {
             SendDlgItemMessage (keylist, 100, LB_ADDSTRING,
                                 0, (LPARAM)listentry);
         }
-        for (skey = first234(ssh2keys, &e); skey; skey = next234(&e)) {
+        for (i = 0; NULL != (skey = index234(ssh2keys, i)); i++) {
             char listentry[512], *p;
            int len;
             /*
@@ -329,12 +337,89 @@ static void add_keyfile(char *filename) {
         return;
     }
     if (ver == 1) {
-       if (add234(rsakeys, rkey) != rkey)
-           sfree(rkey);               /* already present, don't waste RAM */
+       if (already_running) {
+           unsigned char *request, *response;
+           int reqlen, clen, resplen;
+
+           clen = strlen(rkey->comment);
+
+           reqlen = 4 + 1 +           /* length, message type */
+               4 +                    /* bit count */
+               ssh1_bignum_length(rkey->modulus) +
+               ssh1_bignum_length(rkey->exponent) +
+               ssh1_bignum_length(rkey->private_exponent) +
+               ssh1_bignum_length(rkey->iqmp) +
+               ssh1_bignum_length(rkey->p) +
+               ssh1_bignum_length(rkey->q) +
+               4 + clen               /* comment */
+               ;
+
+           request = smalloc(reqlen);
+
+           request[4] = SSH1_AGENTC_ADD_RSA_IDENTITY;
+           reqlen = 5;
+           PUT_32BIT(request+reqlen, bignum_bitcount(rkey->modulus));
+           reqlen += 4;
+           reqlen += ssh1_write_bignum(request+reqlen, rkey->modulus);
+           reqlen += ssh1_write_bignum(request+reqlen, rkey->exponent);
+           reqlen += ssh1_write_bignum(request+reqlen, rkey->private_exponent);
+           reqlen += ssh1_write_bignum(request+reqlen, rkey->iqmp);
+           reqlen += ssh1_write_bignum(request+reqlen, rkey->p);
+           reqlen += ssh1_write_bignum(request+reqlen, rkey->q);
+           PUT_32BIT(request+reqlen, clen);
+           memcpy(request+reqlen+4, rkey->comment, clen);
+           reqlen += 4+clen;
+           PUT_32BIT(request, reqlen-4);
+
+           agent_query(request, reqlen, &response, &resplen);
+           if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
+               MessageBox(NULL, "The already running Pageant "
+                          "refused to add the key.", APPNAME,
+                          MB_OK | MB_ICONERROR);
+       } else {
+           if (add234(rsakeys, rkey) != rkey)
+               sfree(rkey);           /* already present, don't waste RAM */
+       }
     } else {
-       if (add234(ssh2keys, skey) != skey) {
-           skey->alg->freekey(skey->data);
-           sfree(skey);               /* already present, don't waste RAM */
+       if (already_running) {
+           unsigned char *request, *response;
+           int reqlen, alglen, clen, keybloblen, resplen;
+           alglen = strlen(skey->alg->name);
+           clen = strlen(skey->comment);
+
+           keybloblen = skey->alg->openssh_fmtkey(skey->data, NULL, 0);
+
+           reqlen = 4 + 1 +           /* length, message type */
+               4 + alglen +           /* algorithm name */
+               keybloblen +           /* key data */
+               4 + clen               /* comment */
+               ;
+
+           request = smalloc(reqlen);
+
+           request[4] = SSH2_AGENTC_ADD_IDENTITY;
+           reqlen = 5;
+           PUT_32BIT(request+reqlen, alglen);
+           reqlen += 4;
+           memcpy(request+reqlen, skey->alg->name, alglen);
+           reqlen += alglen;
+           reqlen += skey->alg->openssh_fmtkey(skey->data,
+                                               request+reqlen, keybloblen);
+           PUT_32BIT(request+reqlen, clen);
+           memcpy(request+reqlen+4, skey->comment, clen);
+           PUT_32BIT(request, reqlen-4);
+           reqlen += clen+4;
+
+           agent_query(request, reqlen, &response, &resplen);
+           if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
+               MessageBox(NULL, "The already running Pageant"
+                          "refused to add the key.", APPNAME,
+                          MB_OK | MB_ICONERROR);
+       } else {
+           if (add234(ssh2keys, skey) != skey) {
+               skey->alg->freekey(skey->data);
+               sfree(skey);           /* already present, don't waste RAM */
+           }
        }
     }
 }
@@ -359,15 +444,15 @@ static void answer_msg(void *msg) {
          * Reply with SSH1_AGENT_RSA_IDENTITIES_ANSWER.
          */
         {
-            enum234 e;
             struct RSAKey *key;
             int len, nkeys;
+           int i;
 
             /*
              * Count up the number and length of keys we hold.
              */
             len = nkeys = 0;
-            for (key = first234(rsakeys, &e); key; key = next234(&e)) {
+            for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
                 nkeys++;
                 len += 4;              /* length field */
                 len += ssh1_bignum_length(key->exponent);
@@ -386,8 +471,8 @@ static void answer_msg(void *msg) {
             ret[4] = SSH1_AGENT_RSA_IDENTITIES_ANSWER;
             PUT_32BIT(ret+5, nkeys);
             p = ret + 5 + 4;
-            for (key = first234(rsakeys, &e); key; key = next234(&e)) {
-                PUT_32BIT(p, ssh1_bignum_bitcount(key->modulus));
+            for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
+                PUT_32BIT(p, bignum_bitcount(key->modulus));
                 p += 4;
                 p += ssh1_write_bignum(p, key->exponent);
                 p += ssh1_write_bignum(p, key->modulus);
@@ -402,17 +487,17 @@ static void answer_msg(void *msg) {
          * Reply with SSH2_AGENT_IDENTITIES_ANSWER.
          */
         {
-            enum234 e;
             struct ssh2_userkey *key;
             int len, nkeys;
            unsigned char *blob;
            int bloblen;
+           int i;
 
             /*
              * Count up the number and length of keys we hold.
              */
             len = nkeys = 0;
-            for (key = first234(ssh2keys, &e); key; key = next234(&e)) {
+            for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
                 nkeys++;
                 len += 4;              /* length field */
                blob = key->alg->public_blob(key->data, &bloblen);
@@ -432,7 +517,7 @@ static void answer_msg(void *msg) {
             ret[4] = SSH2_AGENT_IDENTITIES_ANSWER;
             PUT_32BIT(ret+5, nkeys);
             p = ret + 5 + 4;
-            for (key = first234(ssh2keys, &e); key; key = next234(&e)) {
+            for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
                blob = key->alg->public_blob(key->data, &bloblen);
                 PUT_32BIT(p, bloblen);
                 p += 4;
@@ -664,9 +749,8 @@ static void answer_msg(void *msg) {
          */
         {
            struct RSAKey *rkey;
-           enum234 e;
 
-            while ( (rkey = first234(rsakeys, &e)) != NULL ) {
+            while ( (rkey = index234(rsakeys, 0)) != NULL ) {
                 del234(rsakeys, rkey);
                freersakey(rkey);
                sfree(rkey);
@@ -683,9 +767,8 @@ static void answer_msg(void *msg) {
          */
         {
             struct ssh2_userkey *skey;
-           enum234 e;
 
-            while ( (skey = first234(ssh2keys, &e)) != NULL ) {
+            while ( (skey = index234(ssh2keys, 0)) != NULL ) {
                 del234(ssh2keys, skey);
                skey->alg->freekey(skey->data);
                sfree(skey);
@@ -721,8 +804,8 @@ static int cmpkeys_rsa(void *av, void *bv) {
     /*
      * Compare by length of moduli.
      */
-    alen = ssh1_bignum_bitcount(am);
-    blen = ssh1_bignum_bitcount(bm);
+    alen = bignum_bitcount(am);
+    blen = bignum_bitcount(bm);
     if (alen > blen) return +1; else if (alen < blen) return -1;
     /*
      * Now compare by moduli themselves.
@@ -846,7 +929,6 @@ static void prompt_add_keyfile(void) {
  */
 static int CALLBACK KeyListProc(HWND hwnd, UINT msg,
                                 WPARAM wParam, LPARAM lParam) {
-    enum234 e;
     struct RSAKey *rkey;
     struct ssh2_userkey *skey;
 
@@ -891,11 +973,12 @@ static int CALLBACK KeyListProc(HWND hwnd, UINT msg,
            if (HIWORD(wParam) == BN_CLICKED ||
                HIWORD(wParam) == BN_DOUBLECLICKED) {
                int n = SendDlgItemMessage (hwnd, 100, LB_GETCURSEL, 0, 0);
+               int i;
                if (n == LB_ERR) {
                    MessageBeep(0);
                    break;
                }
-                for (rkey = first234(rsakeys, &e); rkey; rkey = next234(&e))
+                for (i = 0; NULL != (rkey = index234(rsakeys, i)); i++)
                     if (n-- == 0)
                         break;
                if (rkey) {
@@ -903,7 +986,7 @@ static int CALLBACK KeyListProc(HWND hwnd, UINT msg,
                    freersakey(rkey);
                    sfree(rkey);
                } else {
-                   for (skey = first234(ssh2keys, &e); skey; skey = next234(&e))
+                   for (i = 0; NULL != (skey = index234(ssh2keys, i)); i++)
                        if (n-- == 0)
                            break;
                    if (skey) {
@@ -1081,11 +1164,26 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
     return DefWindowProc (hwnd, message, wParam, lParam);
 }
 
+/*
+ * Fork and Exec the command in cmdline. [DBW]
+ */
+void spawn_cmd(char *cmdline, int show) {
+    if (ShellExecute(NULL, _T("open"), cmdline,
+                    NULL, NULL, show) <= (HINSTANCE) 32) {
+        TCHAR sMsg[140];
+        sprintf(sMsg, _T("Failed to run \"%.100s\", Error: %d"), cmdline,
+               GetLastError());
+        MessageBox(NULL, sMsg, APPNAME, MB_OK | MB_ICONEXCLAMATION);
+    }
+}
+
 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;
 
     /*
      * Determine whether we're an NT system (should have security
@@ -1101,7 +1199,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
     if (has_security) {
 #ifndef NO_SECURITY
         /*
-         * Attempt to ge the security API we need.
+         * Attempt to get the security API we need.
          */
         advapi = LoadLibrary("ADVAPI32.DLL");
         getsecurityinfo = (gsi_fn_t)GetProcAddress(advapi, "GetSecurityInfo");
@@ -1122,87 +1220,90 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
     } else
         advapi = NULL;
 
-    /*
-     * First bomb out totally if we are already running.
-     */
-    if (FindWindow("Pageant", "Pageant")) {
-        MessageBox(NULL, "Pageant is already running", "Pageant Error",
-                   MB_ICONERROR | MB_OK);
-        if (advapi) FreeLibrary(advapi);
-        return 0;
-    }
-
     instance = inst;
 
-    if (!prev) {
-       wndclass.style         = 0;
-       wndclass.lpfnWndProc   = WndProc;
-       wndclass.cbClsExtra    = 0;
-       wndclass.cbWndExtra    = 0;
-       wndclass.hInstance     = inst;
-       wndclass.hIcon         = LoadIcon (inst,
-                                          MAKEINTRESOURCE(IDI_MAINICON));
-       wndclass.hCursor       = LoadCursor (NULL, IDC_IBEAM);
-       wndclass.hbrBackground = GetStockObject (BLACK_BRUSH);
-       wndclass.lpszMenuName  = NULL;
-       wndclass.lpszClassName = APPNAME;
-
-       RegisterClass (&wndclass);
-    }
+    /*
+     * Find out if Pageant is already running.
+     */
+    already_running = FALSE;
+    if (agent_exists())
+       already_running = TRUE;
+    else {
+
+       if (!prev) {
+           wndclass.style         = 0;
+           wndclass.lpfnWndProc   = WndProc;
+           wndclass.cbClsExtra    = 0;
+           wndclass.cbWndExtra    = 0;
+           wndclass.hInstance     = inst;
+           wndclass.hIcon         = LoadIcon (inst,
+                                              MAKEINTRESOURCE(IDI_MAINICON));
+           wndclass.hCursor       = LoadCursor (NULL, IDC_IBEAM);
+           wndclass.hbrBackground = GetStockObject (BLACK_BRUSH);
+           wndclass.lpszMenuName  = NULL;
+           wndclass.lpszClassName = APPNAME;
+
+           RegisterClass (&wndclass);
+       }
 
-    hwnd = keylist = NULL;
+       hwnd = keylist = NULL;
 
-    hwnd = CreateWindow (APPNAME, APPNAME,
-                         WS_OVERLAPPEDWINDOW | WS_VSCROLL,
-                         CW_USEDEFAULT, CW_USEDEFAULT,
-                         100, 100, NULL, NULL, inst, NULL);
+       hwnd = CreateWindow (APPNAME, APPNAME,
+                            WS_OVERLAPPEDWINDOW | WS_VSCROLL,
+                            CW_USEDEFAULT, CW_USEDEFAULT,
+                            100, 100, NULL, NULL, inst, NULL);
 
-    /* Set up a system tray icon */
-    {
-        BOOL res;
-        NOTIFYICONDATA tnid;
-        HICON hicon;
+       /* Set up a system tray icon */
+       {
+           BOOL res;
+           NOTIFYICONDATA tnid;
+           HICON hicon;
 
 #ifdef NIM_SETVERSION
-        tnid.uVersion = 0;
-        res = Shell_NotifyIcon(NIM_SETVERSION, &tnid);
+           tnid.uVersion = 0;
+           res = Shell_NotifyIcon(NIM_SETVERSION, &tnid);
 #endif
 
-        tnid.cbSize = sizeof(NOTIFYICONDATA); 
-        tnid.hWnd = hwnd; 
-        tnid.uID = 1;                  /* unique within this systray use */
-        tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP; 
-        tnid.uCallbackMessage = WM_SYSTRAY;
-        tnid.hIcon = hicon = LoadIcon (instance, MAKEINTRESOURCE(201));
-        strcpy(tnid.szTip, "Pageant (PuTTY authentication agent)");
-
-        res = Shell_NotifyIcon(NIM_ADD, &tnid); 
-
-        if (hicon) 
-            DestroyIcon(hicon); 
-
-        systray_menu = CreatePopupMenu();
-        /* accelerators used: vkxa */
-        AppendMenu (systray_menu, MF_ENABLED, IDM_VIEWKEYS, "&View Keys");
-        AppendMenu (systray_menu, MF_ENABLED, IDM_ADDKEY, "Add &Key");
-        AppendMenu (systray_menu, MF_ENABLED, IDM_ABOUT, "&About");
-        AppendMenu (systray_menu, MF_ENABLED, IDM_CLOSE, "E&xit");
-    }
+           tnid.cbSize = sizeof(NOTIFYICONDATA);
+           tnid.hWnd = hwnd;
+           tnid.uID = 1;                  /* unique within this systray use */
+           tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
+           tnid.uCallbackMessage = WM_SYSTRAY;
+           tnid.hIcon = hicon = LoadIcon (instance, MAKEINTRESOURCE(201));
+           strcpy(tnid.szTip, "Pageant (PuTTY authentication agent)");
+
+           res = Shell_NotifyIcon(NIM_ADD, &tnid);
+
+           if (hicon)
+               DestroyIcon(hicon);
+
+           systray_menu = CreatePopupMenu();
+           /* accelerators used: vkxa */
+           AppendMenu (systray_menu, MF_ENABLED, IDM_VIEWKEYS, "&View Keys");
+           AppendMenu (systray_menu, MF_ENABLED, IDM_ADDKEY, "Add &Key");
+           AppendMenu (systray_menu, MF_ENABLED, IDM_ABOUT, "&About");
+           AppendMenu (systray_menu, MF_ENABLED, IDM_CLOSE, "E&xit");
+       }
 
-    ShowWindow (hwnd, SW_HIDE);
+       ShowWindow (hwnd, SW_HIDE);
 
-    /*
-     * Initialise storage for RSA keys.
-     */
-    rsakeys = newtree234(cmpkeys_rsa);
-    ssh2keys = newtree234(cmpkeys_ssh2);
+       /*
+        * Initialise storage for RSA keys.
+        */
+       rsakeys = newtree234(cmpkeys_rsa);
+       ssh2keys = newtree234(cmpkeys_ssh2);
+
+    }
 
     /*
      * Process the command line and add keys as listed on it.
+     * If we already determined that we need to spawn a program from above we
+     * need to ignore the first two arguments. [DBW]
      */
     {
         char *p;
         int inquotes = 0;
+        int ignorearg = 0;
         p = cmdline;
         while (*p) {
             while (*p && isspace(*p)) p++;
@@ -1221,11 +1322,39 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
                     if (*p) p++;
                     *pp++ = '\0';
                 }
-                add_keyfile(q);
+               if (!strcmp(q, "-c")) {
+                   /*
+                    * If we see `-c', then the rest of the
+                    * command line should be treated as a
+                    * command to be spawned.
+                    */
+                   while (*p && isspace(*p)) p++;
+                   command = p;
+                   break;
+               } else {
+                   add_keyfile(q);
+                   added_keys = TRUE;
+               }
             }
         }
     }
 
+    if (command) spawn_cmd (command, show);
+
+    /*
+     * If Pageant was already running, we leave now. If we haven't
+     * even taken any auxiliary action (spawned a command or added
+     * keys), complain.
+     */
+    if (already_running) {
+       if (!command && !added_keys) {
+           MessageBox(NULL, "Pageant is already running", "Pageant Error",
+                      MB_ICONERROR | MB_OK);
+       }
+        if (advapi) FreeLibrary(advapi);
+        return 0;
+    }
+
     /*
      * Main message loop.
      */