Rationalised host key storage. Also started code reorg: persistent-state
[sgt/putty] / windlg.c
index e98db05..71f46ba 100644 (file)
--- a/windlg.c
+++ b/windlg.c
@@ -1,13 +1,20 @@
 #include <windows.h>
 #include <commctrl.h>
 #include <commdlg.h>
+#ifndef AUTO_WINSOCK
+#ifdef WINSOCK_TWO
+#include <winsock2.h>
+#else
 #include <winsock.h>
+#endif
+#endif
 #include <stdio.h>
 #include <stdlib.h>
 
 #include "ssh.h"
 #include "putty.h"
 #include "win_res.h"
+#include "storage.h"
 
 #define NPANELS 8
 #define MAIN_NPANELS 8
@@ -149,6 +156,7 @@ static void save_settings (char *section, int do_host) {
     }
     wpps (sesskey, "UserName", cfg.username);
     wppi (sesskey, "NoPTY", cfg.nopty);
+    wppi (sesskey, "AgentFwd", cfg.agentfwd);
     wpps (sesskey, "RemoteCmd", cfg.remote_cmd);
     wpps (sesskey, "Cipher", cfg.cipher == CIPHER_BLOWFISH ? "blowfish" :
                              cfg.cipher == CIPHER_DES ? "des" : "3des");
@@ -285,6 +293,7 @@ static void load_settings (char *section, int do_host) {
     }
     gpps (sesskey, "UserName", "", cfg.username, sizeof(cfg.username));
     gppi (sesskey, "NoPTY", 0, &cfg.nopty);
+    gppi (sesskey, "AgentFwd", 0, &cfg.agentfwd);
     gpps (sesskey, "RemoteCmd", "", cfg.remote_cmd, sizeof(cfg.remote_cmd));
     {
        char cipher[10];
@@ -423,6 +432,46 @@ static int CALLBACK LogProc (HWND hwnd, UINT msg,
            logbox = NULL;
            DestroyWindow (hwnd);
            return 0;
+          case IDN_COPY:
+           if (HIWORD(wParam) == BN_CLICKED ||
+               HIWORD(wParam) == BN_DOUBLECLICKED) {
+                int selcount;
+                int *selitems;
+                selcount = SendDlgItemMessage(hwnd, IDN_LIST,
+                                              LB_GETSELCOUNT, 0, 0);
+                selitems = malloc(selcount * sizeof(int));
+                if (selitems) {
+                    int count = SendDlgItemMessage(hwnd, IDN_LIST,
+                                                   LB_GETSELITEMS,
+                                                   selcount, (LPARAM)selitems);
+                    int i;
+                    int size;
+                    char *clipdata;
+                    static unsigned char sel_nl[] = SEL_NL;
+
+                    size = 0;
+                    for (i = 0; i < count; i++)
+                        size += strlen(events[selitems[i]]) + sizeof(sel_nl);
+
+                    clipdata = malloc(size);
+                    if (clipdata) {
+                        char *p = clipdata;
+                        for (i = 0; i < count; i++) {
+                            char *q = events[selitems[i]];
+                            int qlen = strlen(q);
+                            memcpy(p, q, qlen);
+                            p += qlen;
+                            memcpy(p, sel_nl, sizeof(sel_nl));
+                            p += sizeof(sel_nl);
+                        }
+                        write_clip(clipdata, size);
+                        term_deselect();
+                        free(clipdata);
+                    }
+                    free(selitems);
+                }
+            }
+            return 0;
        }
        return 0;
       case WM_CLOSE:
@@ -476,6 +525,7 @@ static int CALLBACK AboutProc (HWND hwnd, UINT msg,
            DialogBox (hinst, MAKEINTRESOURCE(IDD_LICENCEBOX),
                       NULL, LicenceProc);
            EnableWindow(hwnd, 1);
+            SetActiveWindow(hwnd);
            return 0;
        }
        return 0;
@@ -506,6 +556,8 @@ static int GeneralPanelProc (HWND hwnd, UINT msg,
     return 0;
 }
 
+static char savedsession[2048];
+
 static int CALLBACK ConnectionProc (HWND hwnd, UINT msg,
                                    WPARAM wParam, LPARAM lParam) {
     int i;
@@ -513,6 +565,7 @@ static int CALLBACK ConnectionProc (HWND hwnd, UINT msg,
     switch (msg) {
       case WM_INITDIALOG:
        SetDlgItemText (hwnd, IDC0_HOST, cfg.host);
+       SetDlgItemText (hwnd, IDC0_SESSEDIT, savedsession);
        SetDlgItemInt (hwnd, IDC0_PORT, cfg.port, FALSE);
        for (i = 0; i < nsessions; i++)
            SendDlgItemMessage (hwnd, IDC0_SESSLIST, LB_ADDSTRING,
@@ -569,9 +622,13 @@ static int CALLBACK ConnectionProc (HWND hwnd, UINT msg,
                cfg.warn_on_close = IsDlgButtonChecked (hwnd, IDC0_CLOSEWARN);
            break;
          case IDC0_SESSEDIT:
-           if (HIWORD(wParam) == EN_CHANGE)
+           if (HIWORD(wParam) == EN_CHANGE) {
                SendDlgItemMessage (hwnd, IDC0_SESSLIST, LB_SETCURSEL,
                                    (WPARAM) -1, 0);
+                GetDlgItemText (hwnd, IDC0_SESSEDIT,
+                                savedsession, sizeof(savedsession)-1);
+                savedsession[sizeof(savedsession)-1] = '\0';
+            }
            break;
          case IDC0_SESSSAVE:
            if (HIWORD(wParam) == BN_CLICKED ||
@@ -1009,6 +1066,7 @@ static int CALLBACK SshProc (HWND hwnd, UINT msg,
        SetDlgItemText (hwnd, IDC3_TTEDIT, cfg.termtype);
        SetDlgItemText (hwnd, IDC3_LOGEDIT, cfg.username);
        CheckDlgButton (hwnd, IDC3_NOPTY, cfg.nopty);
+       CheckDlgButton (hwnd, IDC3_AGENTFWD, cfg.agentfwd);
        CheckRadioButton (hwnd, IDC3_CIPHER3DES, IDC3_CIPHERDES,
                          cfg.cipher == CIPHER_BLOWFISH ? IDC3_CIPHERBLOWF :
                          cfg.cipher == CIPHER_DES ? IDC3_CIPHERDES :
@@ -1035,6 +1093,11 @@ static int CALLBACK SshProc (HWND hwnd, UINT msg,
                HIWORD(wParam) == BN_DOUBLECLICKED)
                cfg.nopty = IsDlgButtonChecked (hwnd, IDC3_NOPTY);
            break;
+         case IDC3_AGENTFWD:
+           if (HIWORD(wParam) == BN_CLICKED ||
+               HIWORD(wParam) == BN_DOUBLECLICKED)
+               cfg.agentfwd = IsDlgButtonChecked (hwnd, IDC3_AGENTFWD);
+           break;
          case IDC3_CIPHER3DES:
          case IDC3_CIPHERBLOWF:
          case IDC3_CIPHERDES:
@@ -1452,6 +1515,7 @@ static int CALLBACK MainDlgProc (HWND hwnd, UINT msg,
        DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX),
                  GetParent(hwnd), AboutProc);
        EnableWindow(hwnd, 1);
+        SetActiveWindow(hwnd);
     }
     return GenericMainDlgProc (hwnd, msg, wParam, lParam,
                               MAIN_NPANELS, mainp, &page);
@@ -1520,6 +1584,7 @@ int do_config (void) {
     int ret;
 
     get_sesslist(TRUE);
+    savedsession[0] = '\0';
     ret = DialogBox (hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL, MainDlgProc);
     get_sesslist(FALSE);
 
@@ -1555,9 +1620,13 @@ void logevent (char *string) {
     events[nevents] = smalloc(1+strlen(string));
     strcpy (events[nevents], string);
     nevents++;
-    if (logbox)
+    if (logbox) {
+        int count;
        SendDlgItemMessage (logbox, IDN_LIST, LB_ADDSTRING,
                            0, (LPARAM)string);
+       count = SendDlgItemMessage (logbox, IDN_LIST, LB_GETCOUNT, 0, 0);
+       SendDlgItemMessage (logbox, IDN_LIST, LB_SETTOPINDEX, count-1, 0);
+    }
 }
 
 void showeventlog (HWND hwnd) {
@@ -1576,67 +1645,70 @@ void showabout (HWND hwnd) {
     }
 }
 
-void verify_ssh_host_key(char *host, char *keystr) {
-    char *otherstr, *mungedhost;
-    int len;
-    HKEY rkey;
+void verify_ssh_host_key(char *host, char *keytype,
+                         char *keystr, char *fingerprint) {
+    int ret;
+
+    static const char absentmsg[] =
+        "The server's host key is not cached in the registry. You\n"
+        "have no guarantee that the server is the computer you\n"
+        "think it is.\n"
+        "The server's key fingerprint is:\n"
+        "%s\n"
+        "If you trust this host, hit Yes to add the key to\n"
+        "PuTTY's cache and carry on connecting.\n"
+        "If you do not trust this host, hit No to abandon the\n"
+        "connection.\n";
+
+    static const char wrongmsg[] =
+        "WARNING - POTENTIAL SECURITY BREACH!\n"
+        "\n"
+        "The server's host key does not match the one PuTTY has\n"
+        "cached in the registry. This means that either the\n"
+        "server administrator has changed the host key, or you\n"
+        "have actually connected to another computer pretending\n"
+        "to be the server.\n"
+        "The new key fingerprint is:\n"
+        "%s\n"
+        "If you were expecting this change and trust the new key,\n"
+        "hit Yes to update PuTTY's cache and continue connecting.\n"
+        "If you want to carry on connecting but without updating\n"
+        "the cache, hit No.\n"
+        "If you want to abandon the connection completely, hit\n"
+        "Cancel. Hitting Cancel is the ONLY guaranteed safe\n"
+        "choice.\n";
+
+    static const char mbtitle[] = "PuTTY Security Alert";
 
-    len = 1 + strlen(keystr);
+    
+    char message[160+                  /* sensible fingerprint max size */
+                 (sizeof(absentmsg) > sizeof(wrongmsg) ?
+                  sizeof(absentmsg) : sizeof(wrongmsg))];
 
     /*
-     * Now read a saved key in from the registry and see what it
-     * says.
+     * Verify the key against the registry.
      */
-    otherstr = smalloc(len);
-    mungedhost = smalloc(3*strlen(host)+1);
-    if (!otherstr || !mungedhost)
-       fatalbox("Out of memory");
-
-    mungestr(host, mungedhost);
-
-    if (RegCreateKey(HKEY_CURRENT_USER, PUTTY_REG_POS "\\SshHostKeys",
-                    &rkey) != ERROR_SUCCESS) {
-       if (MessageBox(NULL, "PuTTY was unable to open the host key cache\n"
-                      "in the registry. There is thus no way to tell\n"
-                      "if the remote host is what you think it is.\n"
-                      "Connect anyway?", "PuTTY Problem",
-                      MB_ICONWARNING | MB_YESNO) == IDNO)
-           exit(0);
-    } else {
-       DWORD readlen = len;
-       DWORD type;
-       int ret;
-
-       ret = RegQueryValueEx(rkey, mungedhost, NULL,
-                             &type, otherstr, &readlen);
-
-       if (ret == ERROR_MORE_DATA ||
-           (ret == ERROR_SUCCESS && type == REG_SZ &&
-            strcmp(otherstr, keystr))) {
-           if (MessageBox(NULL,
-                          "This host's host key is different from the\n"
-                          "one cached in the registry! Someone may be\n"
-                          "impersonating this host for malicious reasons;\n"
-                          "alternatively, the host key may have changed\n"
-                          "due to sloppy system administration.\n"
-                          "Replace key in registry and connect?",
-                          "PuTTY: Security Warning",
-                          MB_ICONWARNING | MB_YESNO) == IDNO)
-               exit(0);
-           RegSetValueEx(rkey, mungedhost, 0, REG_SZ, keystr,
-                         strlen(keystr)+1);
-       } else if (ret != ERROR_SUCCESS || type != REG_SZ) {
-           if (MessageBox(NULL,
-                          "This host's host key is not cached in the\n"
-                          "registry. Do you want to add it to the cache\n"
-                          "and carry on connecting?",
-                          "PuTTY: New Host",
-                          MB_ICONWARNING | MB_YESNO) == IDNO)
-               exit(0);
-           RegSetValueEx(rkey, mungedhost, 0, REG_SZ, keystr,
-                         strlen(keystr)+1);
-       }
-
-       RegCloseKey(rkey);
+    ret = verify_host_key(host, keytype, keystr);
+
+    if (ret == 0)                      /* success - key matched OK */
+        return;
+    if (ret == 2) {                    /* key was different */
+        int mbret;
+        sprintf(message, wrongmsg, fingerprint);
+        mbret = MessageBox(NULL, message, mbtitle,
+                           MB_ICONWARNING | MB_YESNOCANCEL);
+        if (mbret == IDYES)
+            store_host_key(host, keytype, keystr);
+        if (mbret == IDCANCEL)
+            exit(0);
+    }
+    if (ret == 1) {                    /* key was absent */
+        int mbret;
+        sprintf(message, absentmsg, fingerprint);
+        mbret = MessageBox(NULL, message, mbtitle,
+                           MB_ICONWARNING | MB_YESNO);
+        if (mbret == IDNO)
+            exit(0);
+        store_host_key(host, keytype, keystr);
     }
 }