Implement OpenSSH-compatible RSA key fingerprints and use them throughout
[u/mdw/putty] / pageant.c
index f8d7ad2..a9af585 100644 (file)
--- a/pageant.c
+++ b/pageant.c
@@ -44,6 +44,13 @@ HMENU systray_menu;
 
 tree234 *rsakeys;
 
+int has_security;
+typedef DWORD (WINAPI *gsi_fn_t)
+    (HANDLE, SE_OBJECT_TYPE, SECURITY_INFORMATION,
+                                 PSID *, PSID *, PACL *, PACL *,
+                                 PSECURITY_DESCRIPTOR *);
+gsi_fn_t getsecurityinfo;
+
 /*
  * We need this to link with the RSA code, because rsaencrypt()
  * pads its data with random bytes. Since we only use rsadecrypt(),
@@ -79,16 +86,25 @@ void logevent(char *msg) {
 
 #define PASSPHRASE_MAXLEN 512
 
+struct PassphraseProcStruct {
+    char *passphrase;
+    char *comment;
+};
+
 /*
  * Dialog-box function for the passphrase box.
  */
 static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,
                                    WPARAM wParam, LPARAM lParam) {
     static char *passphrase;
+    struct PassphraseProcStruct *p;
 
     switch (msg) {
       case WM_INITDIALOG:
-        passphrase = (char *)lParam;
+        p = (struct PassphraseProcStruct *)lParam;
+        passphrase = p->passphrase;
+        if (p->comment)
+            SetDlgItemText(hwnd, 101, p->comment);
         *passphrase = 0;
         return 0;
       case WM_COMMAND:
@@ -118,6 +134,31 @@ static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,
 }
 
 /*
+ * Update the visible key list.
+ */
+void keylist_update(void) {
+    struct RSAKey *key;
+    enum234 e;
+
+    if (keylist) {
+        SendDlgItemMessage(keylist, 100, LB_RESETCONTENT, 0, 0);
+        for (key = first234(rsakeys, &e); key; key = next234(&e)) {
+            char listentry[512], *p;
+            /*
+             * Replace two spaces in the fingerprint with tabs, for
+             * nice alignment in the box.
+             */
+            rsa_fingerprint(listentry, sizeof(listentry), key);
+            p = strchr(listentry, ' '); if (p) *p = '\t';
+            p = strchr(listentry, ' '); if (p) *p = '\t';
+            SendDlgItemMessage (keylist, 100, LB_ADDSTRING,
+                                0, (LPARAM)listentry);
+        }
+        SendDlgItemMessage (keylist, 100, LB_SETCURSEL, (WPARAM) -1, 0);
+    }
+}
+
+/*
  * This function loads a key from a file and adds it.
  */
 void add_keyfile(char *filename) {
@@ -126,18 +167,22 @@ void add_keyfile(char *filename) {
     int needs_pass;
     int ret;
     int attempts;
+    char *comment;
+    struct PassphraseProcStruct pps;
 
-    /* FIXME: we can acquire comment here and use it in dialog */
-    needs_pass = rsakey_encrypted(filename, NULL);
+    needs_pass = rsakey_encrypted(filename, &comment);
     attempts = 0;
     key = malloc(sizeof(*key));
+    pps.passphrase = passphrase;
+    pps.comment = comment;
     do {
         if (needs_pass) {
             int dlgret;
             dlgret = DialogBoxParam(instance, MAKEINTRESOURCE(210),
                                     NULL, PassphraseProc,
-                                    (LPARAM)passphrase);
+                                    (LPARAM)&pps);
             if (!dlgret) {
+                if (comment) free(comment);
                 free(key);
                 return;                /* operation cancelled */
             }
@@ -146,6 +191,7 @@ void add_keyfile(char *filename) {
         ret = loadrsakey(filename, key, passphrase);
         attempts++;
     } while (ret == -1);
+    if (comment) free(comment);
     if (ret == 0) {
         MessageBox(NULL, "Couldn't load public key.", APPNAME,
                    MB_OK | MB_ICONERROR);
@@ -262,12 +308,36 @@ void answer_msg(void *msg) {
             memcpy(ret+5, response_md5, 16);
         }
         break;
-#if 0 /* FIXME: implement these */
       case SSH_AGENTC_ADD_RSA_IDENTITY:
         /*
          * Add to the list and return SSH_AGENT_SUCCESS, or
          * SSH_AGENT_FAILURE if the key was malformed.
          */
+        {
+            struct RSAKey *key;
+            char *comment;
+            key = malloc(sizeof(struct RSAKey));
+            memset(key, 0, sizeof(key));
+            p += makekey(p, key, NULL, 1);
+            p += makeprivate(p, key);
+            p += ssh1_read_bignum(p, NULL);    /* p^-1 mod q */
+            p += ssh1_read_bignum(p, NULL);    /* p */
+            p += ssh1_read_bignum(p, NULL);    /* q */
+            comment = malloc(GET_32BIT(p));
+            if (comment) {
+                memcpy(comment, p+4, GET_32BIT(p));
+                key->comment = comment;
+            }
+            PUT_32BIT(ret, 1);
+            ret[4] = SSH_AGENT_FAILURE;
+            if (add234(rsakeys, key) == key) {
+                keylist_update();
+                ret[4] = SSH_AGENT_SUCCESS;
+            } else {
+                freersakey(key);
+                free(key);
+            }
+        }
         break;
       case SSH_AGENTC_REMOVE_RSA_IDENTITY:
         /*
@@ -275,8 +345,23 @@ void answer_msg(void *msg) {
          * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
          * start with.
          */
+        {
+            struct RSAKey reqkey, *key;
+
+            p += makekey(p, &reqkey, NULL, 0);
+            key = find234(rsakeys, &reqkey, NULL);
+            freebn(reqkey.exponent);
+            freebn(reqkey.modulus);
+            PUT_32BIT(ret, 1);
+            ret[4] = SSH_AGENT_FAILURE;
+            if (key) {
+                del234(rsakeys, key);
+                keylist_update();
+                freersakey(key);
+                ret[4] = SSH_AGENT_SUCCESS;
+            }
+        }
         break;
-#endif
       default:
         failure:
         /*
@@ -337,10 +422,13 @@ static int CALLBACK KeyListProc(HWND hwnd, UINT msg,
 
     switch (msg) {
       case WM_INITDIALOG:
-        for (key = first234(rsakeys, &e); key; key = next234(&e)) {
-            SendDlgItemMessage (hwnd, 100, LB_ADDSTRING,
-                                0, (LPARAM) key->comment);
-        }
+        keylist = hwnd;
+       {
+           static int tabs[2] = {25, 175};
+           SendDlgItemMessage (hwnd, 100, LB_SETTABSTOPS, 2,
+                               (LPARAM) tabs);
+       }
+        keylist_update();
         return 0;
       case WM_COMMAND:
        switch (LOWORD(wParam)) {
@@ -370,13 +458,8 @@ static int CALLBACK KeyListProc(HWND hwnd, UINT msg,
                 of.Flags = 0;
                 if (GetOpenFileName(&of)) {
                     add_keyfile(filename);
+                    keylist_update();
                 }
-                SendDlgItemMessage(hwnd, 100, LB_RESETCONTENT, 0, 0);
-                for (key = first234(rsakeys, &e); key; key = next234(&e)) {
-                    SendDlgItemMessage (hwnd, 100, LB_ADDSTRING,
-                                        0, (LPARAM) key->comment);
-                }
-               SendDlgItemMessage (hwnd, 100, LB_SETCURSEL, (WPARAM) -1, 0);
             }
             return 0;
           case 102:                    /* remove key */
@@ -392,12 +475,7 @@ static int CALLBACK KeyListProc(HWND hwnd, UINT msg,
                         break;
                 del234(rsakeys, key);
                 freersakey(key); free(key);
-                SendDlgItemMessage(hwnd, 100, LB_RESETCONTENT, 0, 0);
-                for (key = first234(rsakeys, &e); key; key = next234(&e)) {
-                    SendDlgItemMessage (hwnd, 100, LB_ADDSTRING,
-                                        0, (LPARAM) key->comment);
-                }
-               SendDlgItemMessage (hwnd, 100, LB_SETCURSEL, (WPARAM) -1, 0);
+                keylist_update();
             }
             return 0;
        }
@@ -487,43 +565,49 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
 #endif
             if (filemap != NULL && filemap != INVALID_HANDLE_VALUE) {
                 int rc;
-                if ((proc = OpenProcess(MAXIMUM_ALLOWED, FALSE,
-                                        GetCurrentProcessId())) == NULL) {
+                if (has_security) {
+                    if ((proc = OpenProcess(MAXIMUM_ALLOWED, FALSE,
+                                            GetCurrentProcessId())) == NULL) {
 #ifdef DEBUG_IPC
-                    debug(("couldn't get handle for process\r\n"));
+                        debug(("couldn't get handle for process\r\n"));
 #endif
-                    return 0;
-                }
-                if (GetSecurityInfo(proc, SE_KERNEL_OBJECT,
-                                    OWNER_SECURITY_INFORMATION,
-                                    &procowner, NULL, NULL, NULL,
-                                    &psd2) != ERROR_SUCCESS) {
+                        return 0;
+                    }
+                    if (getsecurityinfo(proc, SE_KERNEL_OBJECT,
+                                        OWNER_SECURITY_INFORMATION,
+                                        &procowner, NULL, NULL, NULL,
+                                        &psd2) != ERROR_SUCCESS) {
 #ifdef DEBUG_IPC
-                    debug(("couldn't get owner info for process\r\n"));
+                        debug(("couldn't get owner info for process\r\n"));
 #endif
+                        CloseHandle(proc);
+                        return 0;          /* unable to get security info */
+                    }
                     CloseHandle(proc);
-                    return 0;          /* unable to get security info */
-                }
-                CloseHandle(proc);
-                if ((rc = GetSecurityInfo(filemap, SE_KERNEL_OBJECT,
-                                          OWNER_SECURITY_INFORMATION,
-                                          &mapowner, NULL, NULL, NULL,
-                                          &psd1) != ERROR_SUCCESS)) {
+                    if ((rc = getsecurityinfo(filemap, SE_KERNEL_OBJECT,
+                                              OWNER_SECURITY_INFORMATION,
+                                              &mapowner, NULL, NULL, NULL,
+                                              &psd1) != ERROR_SUCCESS)) {
 #ifdef DEBUG_IPC
-                    debug(("couldn't get owner info for filemap: %d\r\n", rc));
+                        debug(("couldn't get owner info for filemap: %d\r\n", rc));
 #endif
-                    return 0;
-                }
+                        return 0;
+                    }
+#ifdef DEBUG_IPC
+                    debug(("got security stuff\r\n"));
+#endif
+                    if (!EqualSid(mapowner, procowner))
+                        return 0;          /* security ID mismatch! */
 #ifdef DEBUG_IPC
-                debug(("got security stuff\r\n"));
+                    debug(("security stuff matched\r\n"));
 #endif
-                if (!EqualSid(mapowner, procowner))
-                    return 0;          /* security ID mismatch! */
+                    LocalFree(psd1);
+                    LocalFree(psd2);
+                } else {
 #ifdef DEBUG_IPC
-                debug(("security stuff matched\r\n"));
+                    debug(("security APIs not present\r\n"));
 #endif
-                LocalFree(psd1);
-                LocalFree(psd2);
+                }
                 p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);
 #ifdef DEBUG_IPC
                 debug(("p is %p\r\n", p));
@@ -544,6 +628,35 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
     WNDCLASS wndclass;
     MSG msg;
+    OSVERSIONINFO osi;
+    HMODULE advapi;
+
+    /*
+     * 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) {
+        has_security = TRUE;
+    } else
+        has_security = FALSE;
+
+    if (has_security) {
+        /*
+         * Attempt to ge the security API we need.
+         */
+        advapi = LoadLibrary("ADVAPI32.DLL");
+        getsecurityinfo = (gsi_fn_t)GetProcAddress(advapi, "GetSecurityInfo");
+        if (!getsecurityinfo) {
+            MessageBox(NULL,
+                       "Unable to access security APIs. Pageant will\n"
+                       "not run, in case it causes a security breach.",
+                       "Pageant Fatal Error", MB_ICONERROR | MB_OK);
+            return 1;
+        }
+    } else
+        advapi = NULL;
 
     /*
      * First bomb out totally if we are already running.
@@ -551,6 +664,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
     if (FindWindow("Pageant", "Pageant")) {
         MessageBox(NULL, "Pageant is already running", "Pageant Error",
                    MB_ICONERROR | MB_OK);
+        if (advapi) FreeLibrary(advapi);
         return 0;
     }
 
@@ -604,8 +718,8 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
             DestroyIcon(hicon); 
 
         systray_menu = CreatePopupMenu();
-        AppendMenu (systray_menu, MF_ENABLED, IDM_VIEWKEYS, "View Keys");
-        AppendMenu (systray_menu, MF_ENABLED, IDM_CLOSE, "Terminate");
+        AppendMenu (systray_menu, MF_ENABLED, IDM_VIEWKEYS, "&View Keys");
+        AppendMenu (systray_menu, MF_ENABLED, IDM_CLOSE, "E&xit");
     }
 
     ShowWindow (hwnd, SW_HIDE);
@@ -665,5 +779,6 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
         DestroyMenu(systray_menu);
     }
 
+    if (advapi) FreeLibrary(advapi);
     exit(msg.wParam);
 }