]> asedeno.scripts.mit.edu Git - PuTTY.git/commitdiff
Special host key warning when a better key exists.
authorSimon Tatham <anakin@pobox.com>
Sun, 27 Mar 2016 17:08:49 +0000 (18:08 +0100)
committerSimon Tatham <anakin@pobox.com>
Sun, 27 Mar 2016 17:20:37 +0000 (18:20 +0100)
If you're connecting to a new server and it _only_ provides host key
types you've configured to be below the warning threshold, it's OK to
give the standard askalg() message. But if you've newly demoted a host
key type and now reconnect to some server for which that type was the
best key you had cached, the askalg() wording isn't really appropriate
(it's not that the key we've settled on is the first type _supported
by the server_, it's that it's the first type _cached by us_), and
also it's potentially helpful to list the better algorithms so that
the user can pick one to cross-certify.

putty.h
ssh.c
unix/gtkdlg.c
unix/uxcons.c
windows/wincons.c
windows/windlg.c

diff --git a/putty.h b/putty.h
index 5f46b066f2ac6e48fcf9ec19f899ae55001d9106..c02e00f1ba181f4dcd6b8db62acedded12412ed1 100644 (file)
--- a/putty.h
+++ b/putty.h
@@ -1232,10 +1232,17 @@ int verify_ssh_host_key(void *frontend, char *host, int port,
  */
 int have_ssh_host_key(const char *host, int port, const char *keytype);
 /*
- * askalg has the same set of return values as verify_ssh_host_key.
+ * askalg and askhk have the same set of return values as
+ * verify_ssh_host_key.
+ *
+ * (askhk is used in the case where we're using a host key below the
+ * warning threshold because that's all we have cached, but at least
+ * one acceptable algorithm is available that we don't have cached.)
  */
 int askalg(void *frontend, const char *algtype, const char *algname,
           void (*callback)(void *ctx, int result), void *ctx);
+int askhk(void *frontend, const char *algname, const char *betteralgs,
+          void (*callback)(void *ctx, int result), void *ctx);
 /*
  * askappend can return four values:
  * 
diff --git a/ssh.c b/ssh.c
index a94ebd4110f058c42a6da885b9ca792415f860c5..146853dd439fb6d56440c3d5d576fcbfaa929eea 100644 (file)
--- a/ssh.c
+++ b/ssh.c
@@ -6813,10 +6813,53 @@ static void do_ssh2_transport(Ssh ssh, const void *vin, int inlen,
        }
 
        if (s->warn_hk) {
+            int j, k;
+            char *betteralgs;
+
            ssh_set_frozen(ssh, 1);
-           s->dlgret = askalg(ssh->frontend, "host key type",
-                              ssh->hostkey->name,
-                              ssh_dialog_callback, ssh);
+
+            /*
+             * Change warning box wording depending on why we chose a
+             * warning-level host key algorithm. If it's because
+             * that's all we have *cached*, use the askhk mechanism,
+             * and list the host keys we could usefully cross-certify.
+             * Otherwise, use askalg for the standard wording.
+             */
+            betteralgs = NULL;
+            for (j = 0; j < ssh->n_uncert_hostkeys; j++) {
+                const struct ssh_signkey_with_user_pref_id *hktype =
+                    &hostkey_algs[ssh->uncert_hostkeys[j]];
+                int better = FALSE;
+                for (k = 0; k < HK_MAX; k++) {
+                    int id = conf_get_int_int(ssh->conf, CONF_ssh_hklist, k);
+                    if (id == HK_WARN) {
+                        break;
+                    } else if (id == hktype->id) {
+                        better = TRUE;
+                        break;
+                    }
+                }
+                if (better) {
+                    if (betteralgs) {
+                        char *old_ba = betteralgs;
+                        betteralgs = dupcat(betteralgs, ",",
+                                            hktype->alg->name,
+                                            (const char *)NULL);
+                        sfree(old_ba);
+                    } else {
+                        betteralgs = dupstr(hktype->alg->name);
+                    }
+                }
+            }
+            if (betteralgs) {
+                s->dlgret = askhk(ssh->frontend, ssh->hostkey->name,
+                                  betteralgs, ssh_dialog_callback, ssh);
+                sfree(betteralgs);
+            } else {
+                s->dlgret = askalg(ssh->frontend, "host key type",
+                                   ssh->hostkey->name,
+                                   ssh_dialog_callback, ssh);
+            }
            if (s->dlgret < 0) {
                do {
                    crReturnV;
index 7058c6e18e02628e37dbbe31c8606c63c5dbf774..34773137b231ef25848e9482657afc431343a19a 100644 (file)
@@ -3562,6 +3562,37 @@ int askalg(void *frontend, const char *algtype, const char *algname,
     }
 }
 
+int askhk(void *frontend, const char *algname, const char *betteralgs,
+          void (*callback)(void *ctx, int result), void *ctx)
+{
+    static const char msg[] =
+       "The first host key type we have stored for this server\n"
+       "is %s, which is below the configured warning threshold.\n"
+       "The server also provides the following types of host key\n"
+        "above the threshold, which we do not have stored:\n"
+        "%s\n"
+       "Continue with connection?";
+    char *text;
+    int ret;
+
+    text = dupprintf(msg, algname, betteralgs);
+    ret = messagebox(GTK_WIDGET(get_window(frontend)),
+                    "PuTTY Security Alert", text,
+                    string_width("is ecdsa-nistp521, which is"
+                                  " below the configured warning threshold."),
+                     FALSE,
+                    "Yes", 'y', 0, 1,
+                    "No", 'n', 0, 0,
+                    NULL);
+    sfree(text);
+
+    if (ret) {
+       return 1;
+    } else {
+       return 0;
+    }
+}
+
 void old_keyfile_warning(void)
 {
     /*
index abad00db0ef7a065460fd04a1496e9562874b422..b9fd67fe927423c7587be969ff545247218ff1fd 100644 (file)
@@ -264,6 +264,59 @@ int askalg(void *frontend, const char *algtype, const char *algname,
     }
 }
 
+int askhk(void *frontend, const char *algname, const char *betteralgs,
+          void (*callback)(void *ctx, int result), void *ctx)
+{
+    static const char msg[] =
+       "The first host key type we have stored for this server\n"
+       "is %s, which is below the configured warning threshold.\n"
+       "The server also provides the following types of host key\n"
+        "above the threshold, which we do not have stored:\n"
+        "%s\n"
+       "Continue with connection? (y/n) ";
+    static const char msg_batch[] =
+       "The first host key type we have stored for this server\n"
+       "is %s, which is below the configured warning threshold.\n"
+       "The server also provides the following types of host key\n"
+        "above the threshold, which we do not have stored:\n"
+        "%s\n"
+       "Connection abandoned.\n";
+    static const char abandoned[] = "Connection abandoned.\n";
+
+    char line[32];
+    struct termios cf;
+
+    premsg(&cf);
+    if (console_batch_mode) {
+       fprintf(stderr, msg_batch, algname, betteralgs);
+       return 0;
+    }
+
+    fprintf(stderr, msg, algname, betteralgs);
+    fflush(stderr);
+
+    {
+       struct termios oldmode, newmode;
+       tcgetattr(0, &oldmode);
+       newmode = oldmode;
+       newmode.c_lflag |= ECHO | ISIG | ICANON;
+       tcsetattr(0, TCSANOW, &newmode);
+       line[0] = '\0';
+       if (block_and_read(0, line, sizeof(line) - 1) <= 0)
+           /* handled below */;
+       tcsetattr(0, TCSANOW, &oldmode);
+    }
+
+    if (line[0] == 'y' || line[0] == 'Y') {
+       postmsg(&cf);
+       return 1;
+    } else {
+       fprintf(stderr, abandoned);
+       postmsg(&cf);
+       return 0;
+    }
+}
+
 /*
  * Ask whether to wipe a session log file before writing to it.
  * Returns 2 for wipe, 1 for append, 0 for cancel (don't log).
index 0b60e1849712a879477490f8803350a65517408a..d7b95704174fb11a14a5e1e934d1d076bcf4e7cf 100644 (file)
@@ -197,6 +197,53 @@ int askalg(void *frontend, const char *algtype, const char *algname,
     }
 }
 
+int askhk(void *frontend, const char *algname, const char *betteralgs,
+          void (*callback)(void *ctx, int result), void *ctx)
+{
+    HANDLE hin;
+    DWORD savemode, i;
+
+    static const char msg[] =
+       "The first host key type we have stored for this server\n"
+       "is %s, which is below the configured warning threshold.\n"
+       "The server also provides the following types of host key\n"
+        "above the threshold, which we do not have stored:\n"
+        "%s\n"
+       "Continue with connection? (y/n) ";
+    static const char msg_batch[] =
+       "The first host key type we have stored for this server\n"
+       "is %s, which is below the configured warning threshold.\n"
+       "The server also provides the following types of host key\n"
+        "above the threshold, which we do not have stored:\n"
+        "%s\n"
+       "Connection abandoned.\n";
+    static const char abandoned[] = "Connection abandoned.\n";
+
+    char line[32];
+
+    if (console_batch_mode) {
+       fprintf(stderr, msg_batch, algname, betteralgs);
+       return 0;
+    }
+
+    fprintf(stderr, msg, algname, betteralgs);
+    fflush(stderr);
+
+    hin = GetStdHandle(STD_INPUT_HANDLE);
+    GetConsoleMode(hin, &savemode);
+    SetConsoleMode(hin, (savemode | ENABLE_ECHO_INPUT |
+                        ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT));
+    ReadFile(hin, line, sizeof(line) - 1, &i, NULL);
+    SetConsoleMode(hin, savemode);
+
+    if (line[0] == 'y' || line[0] == 'Y') {
+       return 1;
+    } else {
+       fprintf(stderr, abandoned);
+       return 0;
+    }
+}
+
 /*
  * Ask whether to wipe a session log file before writing to it.
  * Returns 2 for wipe, 1 for append, 0 for cancel (don't log).
index 8248ce835974fd8ffdc3142da7d118ea0f8f0913..7da9441d43f20396aa8da1ed03775ef745dad740 100644 (file)
@@ -895,6 +895,33 @@ int askalg(void *frontend, const char *algtype, const char *algname,
        return 0;
 }
 
+int askhk(void *frontend, const char *algname, const char *betteralgs,
+          void (*callback)(void *ctx, int result), void *ctx)
+{
+    static const char mbtitle[] = "%s Security Alert";
+    static const char msg[] =
+       "The first host key type we have stored for this server\n"
+       "is %s, which is below the configured warning threshold.\n"
+       "The server also provides the following types of host key\n"
+        "above the threshold, which we do not have stored:\n"
+        "%s\n"
+       "Do you want to continue with this connection?\n";
+    char *message, *title;
+    int mbret;
+
+    message = dupprintf(msg, algname, betteralgs);
+    title = dupprintf(mbtitle, appname);
+    mbret = MessageBox(NULL, message, title,
+                      MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2);
+    socket_reselect_all();
+    sfree(message);
+    sfree(title);
+    if (mbret == IDYES)
+       return 1;
+    else
+       return 0;
+}
+
 /*
  * Ask whether to wipe a session log file before writing to it.
  * Returns 2 for wipe, 1 for append, 0 for cancel (don't log).