Revamp interface to verify_ssh_host_key() and askalg(). Each of them
authorsimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Thu, 17 Feb 2005 18:34:24 +0000 (18:34 +0000)
committersimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Thu, 17 Feb 2005 18:34:24 +0000 (18:34 +0000)
now returns an integer: 0 means cancel the SSH connection and 1
means continue with it. Additionally, they can return -1, which
means `front end has set an asynchronous alert box in motion, please
wait to be called back with the result', and each one is passed a
callback function pointer and context for this purpose.

I have not yet done the same to askappend() yet, because it will
take a certain amount of reorganisation of logging.c.

Importantly, this checkin means the host key dialog box now works on
OS X.

git-svn-id: svn://svn.tartarus.org/sgt/putty@5330 cda61777-01e9-0310-a592-d414129be87e

mac/mac.c
macosx/README.OSX
macosx/osxclass.h
macosx/osxdlg.m
macosx/osxwin.m
putty.h
ssh.c
unix/gtkdlg.c
unix/uxcons.c
windows/wincons.c
windows/windlg.c

index f663dee..2fb7d44 100644 (file)
--- a/mac/mac.c
+++ b/mac/mac.c
@@ -691,8 +691,9 @@ int agent_query(void *in, int inlen, void **out, int *outlen,
 
 /* Temporary null routines for testing. */
 
-void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
-                        char *keystr, char *fingerprint)
+int verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
+                        char *keystr, char *fingerprint,
+                        void (*callback)(void *ctx, int result), void *ctx)
 {
     Str255 pappname;
     Str255 pfingerprint;
@@ -705,64 +706,55 @@ void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
     c2pstrcpy(pfingerprint, fingerprint);
 
     /*
-     * This function is horribly wrong.  For one thing, the alert
-     * shouldn't be modal, it should be movable modal, or a sheet in
-     * Aqua.  Also, PuTTY might be in the background, in which case we
-     * should use the Notification Manager to wake up the user.  In
-     * any case, we shouldn't hold up processing of other connections'
-     * data just because this one's waiting for the user. Also see the
-     * note below about closing the connection.  All in all, a bit of
-     * a mess really.
+     * The alert shouldn't be modal, it should be movable modal, or
+     * a sheet in Aqua.  Also, PuTTY might be in the background, in
+     * which case we should use the Notification Manager to wake up
+     * the user.  In any case, we shouldn't hold up processing of
+     * other connections' data just because this one's waiting for
+     * the user.
      */
 
     /* Verify the key against the cache */
 
     ret = verify_host_key(host, port, keytype, keystr);
 
-    if (ret == 0)                     /* success - key matched OK */
-       return;
-    if (ret == 2) {                   /* key was different */
+    if (ret == 0) {                   /* success - key matched OK */
+       return 1;
+    } else if (ret == 2) {            /* key was different */
        ParamText(pappname, pkeytype, pfingerprint, NULL);
        alertret=CautionAlert(wWrong, NULL);
        if (alertret == 8) {
            /* Cancel */
-           goto cancel;
+            return 0;
        } else if (alertret == 9) {
            /* Connect Just Once */
+            return 1;
        } else {
            /* Update Key */
            store_host_key(host, port, keytype, keystr);
+            return 1;
        }
-    }
-    if (ret == 1) {                     /* key was absent */
+    } else /* ret == 1 */ {           /* key was absent */
        ParamText(pkeytype, pfingerprint, pappname, NULL);
        alertret=CautionAlert(wAbsent, NULL);
        if (alertret == 7) {
            /* Cancel */
-           goto cancel;
+            return 0;
        } else if (alertret == 8) {
            /* Connect Just Once */
+            return 1;
        } else {
            /* Update Key */
            store_host_key(host, port, keytype, keystr);
+            return 1;
        }
     }
-
-    return;
-
-  cancel:
-    /*
-     * User chose "Cancel".  Unfortunately, if I tear the
-     * connection down here, Bad Things happen when I return.  I
-     * think this function should actually return something
-     * telling the SSH code to abandon the connection.
-     */        
-    return;
 }
 
-void askalg(void *frontend, const char *algtype, const char *algname)
+int askalg(void *frontend, const char *algtype, const char *algname,
+          void (*callback)(void *ctx, int result), void *ctx)
 {
-
+    return 0;
 }
 
 void old_keyfile_warning(void)
index 9dded05..8d989a6 100644 (file)
@@ -16,13 +16,6 @@ version of the port decides to look somewhere completely different
 for the data and therefore loses them all. If that happens, don't
 say you weren't warned!
 
-Even more importantly, the alert box that confirms host keys is not
-yet implemented, and the application will bomb out and exit if it
-should be needed. This means you cannot make an SSH connection to a
-new host using the GUI PuTTY in this port: you must first run
-`plink' (which should be exactly identical to the version in the
-Unix port) and tell it to confirm the host key.
-
 Other ways in which the port is currently unfinished include:
 
  - terminal display is horribly slow
index 5f009a9..76a8fc7 100644 (file)
@@ -40,6 +40,8 @@ extern AppController *controller;
     void *ldisc;
     Backend *back;
     void *backhandle;
+    void (*alert_callback)(void *, int);
+    void *alert_ctx;
 }
 - (id)initWithConfig:(Config)cfg;
 - (void)drawStartFinish:(BOOL)start;
@@ -48,6 +50,8 @@ extern AppController *controller;
 - (void)doText:(wchar_t *)text len:(int)len x:(int)x y:(int)y
     attr:(unsigned long)attr lattr:(int)lattr;
 - (int)fromBackend:(const char *)data len:(int)len isStderr:(int)is_stderr;
+- (void)startAlert:(NSAlert *)alert
+    withCallback:(void (*)(void *, int))callback andCtx:(void *)ctx;
 @end
 
 /*
index 5bc13a4..0ea327f 100644 (file)
@@ -311,16 +311,104 @@ int askappend(void *frontend, Filename filename)
     return 0;                         /* FIXME */
 }
 
-void askalg(void *frontend, const char *algtype, const char *algname)
+struct algstate {
+    void (*callback)(void *ctx, int result);
+    void *ctx;
+};
+
+static void askalg_callback(void *ctx, int result)
+{
+    struct algstate *state = (struct algstate *)ctx;
+
+    state->callback(state->ctx, result == 0);
+    sfree(state);
+}
+
+int askalg(void *frontend, const char *algtype, const char *algname,
+          void (*callback)(void *ctx, int result), void *ctx)
+{
+    static const char msg[] =
+       "The first %s supported by the server is "
+       "%s, which is below the configured warning threshold.\n"
+       "Continue with connection?";
+
+    char *text;
+    SessionWindow *win = (SessionWindow *)frontend;
+    struct algstate *state;
+    NSAlert *alert;
+
+    text = dupprintf(msg, algtype, algname);
+
+    state = snew(struct algstate);
+    state->callback = callback;
+    state->ctx = ctx;
+
+    alert = [NSAlert alloc];
+    [alert setInformativeText:[NSString stringWithCString:text]];
+    [alert addButtonWithTitle:@"Yes"];
+    [alert addButtonWithTitle:@"No"];
+    [win startAlert:alert withCallback:askalg_callback andCtx:state];
+
+    return -1;
+}
+
+struct hostkeystate {
+    char *host, *keytype, *keystr;
+    int port;
+    void (*callback)(void *ctx, int result);
+    void *ctx;
+};
+
+static void verify_ssh_host_key_callback(void *ctx, int result)
 {
-    fatalbox("Cipher algorithm dialog box not supported yet");
-    return;                           /* FIXME */
+    struct hostkeystate *state = (struct hostkeystate *)ctx;
+
+    if (result == NSAlertThirdButtonReturn)   /* `Accept' */
+       store_host_key(state->host, state->port,
+                      state->keytype, state->keystr);
+    state->callback(state->ctx, result != NSAlertFirstButtonReturn);
+    sfree(state->host);
+    sfree(state->keytype);
+    sfree(state->keystr);
+    sfree(state);
 }
 
-void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
-                        char *keystr, char *fingerprint)
+int verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
+                        char *keystr, char *fingerprint,
+                        void (*callback)(void *ctx, int result), void *ctx)
 {
+    static const char absenttxt[] =
+       "The server's host key is not cached. You have no guarantee "
+       "that the server is the computer you think it is.\n"
+       "The server's %s key fingerprint is:\n"
+       "%s\n"
+       "If you trust this host, press \"Accept\" to add the key to "
+       "PuTTY's cache and carry on connecting.\n"
+       "If you want to carry on connecting just once, without "
+       "adding the key to the cache, press \"Connect Once\".\n"
+       "If you do not trust this host, press \"Cancel\" to abandon the "
+       "connection.";
+    static const char wrongtxt[] =
+       "WARNING - POTENTIAL SECURITY BREACH!\n"
+       "The server's host key does not match the one PuTTY has "
+       "cached. This means that either the server administrator "
+       "has changed the host key, or you have actually connected "
+       "to another computer pretending to be the server.\n"
+       "The new %s key fingerprint is:\n"
+       "%s\n"
+       "If you were expecting this change and trust the new key, "
+       "press \"Accept\" to update PuTTY's cache and continue connecting.\n"
+       "If you want to carry on connecting but without updating "
+       "the cache, press \"Connect Once\".\n"
+       "If you want to abandon the connection completely, press "
+       "\"Cancel\" to cancel. Pressing \"Cancel\" is the ONLY guaranteed "
+       "safe choice.";
+
     int ret;
+    char *text;
+    SessionWindow *win = (SessionWindow *)frontend;
+    struct hostkeystate *state;
+    NSAlert *alert;
 
     /*
      * Verify the key.
@@ -328,30 +416,27 @@ void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
     ret = verify_host_key(host, port, keytype, keystr);
 
     if (ret == 0)
-       return;
-
-    /*
-     * FIXME FIXME FIXME. I currently lack any sensible means of
-     * asking the user for a verification non-application-modally,
-     * _or_ any means of closing just this connection if the answer
-     * is no (the Unix and Windows ports just exit() in this
-     * situation since they're one-connection-per-process).
-     * 
-     * What I need to do is to make this function optionally-
-     * asynchronous, much like the interface to agent_query(). It
-     * can either run modally and return a result directly, _or_ it
-     * can kick off a non-modal dialog, return a `please wait'
-     * status, and the dialog can call the backend back when the
-     * result comes in. Also, in either case, the aye/nay result
-     * wants to be passed to the backend so that it can tear down
-     * the connection if the answer was nay.
-     * 
-     * For the moment, I simply bomb out if we have an unrecognised
-     * host key. This makes this port safe but not very useful: you
-     * can only use it at all if you already have a host key cache
-     * set up by running the Unix port.
-     */
-    fatalbox("Host key dialog box not supported yet");
+       return 1;
+
+    text = dupprintf((ret == 2 ? wrongtxt : absenttxt), keytype, fingerprint);
+
+    state = snew(struct hostkeystate);
+    state->callback = callback;
+    state->ctx = ctx;
+    state->host = dupstr(host);
+    state->port = port;
+    state->keytype = dupstr(keytype);
+    state->keystr = dupstr(keystr);
+
+    alert = [[NSAlert alloc] init];
+    [alert setInformativeText:[NSString stringWithCString:text]];
+    [alert addButtonWithTitle:@"Cancel"];
+    [alert addButtonWithTitle:@"Connect Once"];
+    [alert addButtonWithTitle:@"Accept"];
+    [win startAlert:alert withCallback:verify_ssh_host_key_callback
+     andCtx:state];
+
+    return -1;
 }
 
 void old_keyfile_warning(void)
index a54f771..8166f91 100644 (file)
 {
     NSRect rect = { {0,0}, {0,0} };
 
+    alert_ctx = NULL;
+
     cfg = aCfg;                               /* structure copy */
 
     init_ucs(&ucsdata, cfg.line_codepage, cfg.utf8_override,
      * terminal, the backend, the ldisc, the logctx, you name it.
      * Do so.
      */
+    sfree(alert_ctx);
     [super dealloc];
 }
 
@@ -778,6 +781,23 @@ printf("n=%d c=U+%04x cm=U+%04x m=%08x\n", n, c, cm, m);
     return term_data(term, is_stderr, data, len);
 }
 
+- (void)startAlert:(NSAlert *)alert
+    withCallback:(void (*)(void *, int))callback andCtx:(void *)ctx
+{
+    alert_callback = callback;
+    alert_ctx = ctx;                /* NB this is assumed to need freeing! */
+    [alert beginSheetModalForWindow:self modalDelegate:self
+     didEndSelector:@selector(alertSheetDidEnd:returnCode:contextInfo:)
+     contextInfo:NULL];
+}
+
+- (void)alertSheetDidEnd:(NSAlert *)alert returnCode:(int)returnCode
+    contextInfo:(void *)contextInfo
+{
+    alert_callback(alert_ctx, returnCode);   /* transfers ownership of ctx */
+    alert_ctx = NULL;
+}
+
 @end
 
 int from_backend(void *frontend, int is_stderr, const char *data, int len)
diff --git a/putty.h b/putty.h
index 0b49dfa..46708f1 100644 (file)
--- a/putty.h
+++ b/putty.h
@@ -896,9 +896,26 @@ int wc_unescape(char *output, const char *wildcard);
  * Exports from windlg.c
  */
 void logevent(void *frontend, const char *);
-void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
-                        char *keystr, char *fingerprint);
-void askalg(void *frontend, const char *algtype, const char *algname);
+/*
+ * verify_ssh_host_key() can return one of three values:
+ * 
+ *  - +1 means `key was OK' (either already known or the user just
+ *    approved it) `so continue with the connection'
+ * 
+ *  - 0 means `key was not OK, abandon the connection'
+ * 
+ *  - -1 means `I've initiated enquiries, please wait to be called
+ *    back via the provided function with a result that's either 0
+ *    or +1'.
+ */
+int verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
+                        char *keystr, char *fingerprint,
+                        void (*callback)(void *ctx, int result), void *ctx);
+/*
+ * askalg has the same set of return values as verify_ssh_host_key.
+ */
+int askalg(void *frontend, const char *algtype, const char *algname,
+          void (*callback)(void *ctx, int result), void *ctx);
 int askappend(void *frontend, Filename filename);
 
 /*
diff --git a/ssh.c b/ssh.c
index e2e8077..e41e9a9 100644 (file)
--- a/ssh.c
+++ b/ssh.c
@@ -726,10 +726,23 @@ struct ssh_tag {
     Config cfg;
 
     /*
-     * Used to transfer data back from async agent callbacks.
+     * Used to transfer data back from async callbacks.
      */
     void *agent_response;
     int agent_response_len;
+    int user_response;
+
+    /*
+     * The SSH connection can be set as `frozen', meaning we are
+     * not currently accepting incoming data from the network. This
+     * is slightly more serious than setting the _socket_ as
+     * frozen, because we may already have had data passed to us
+     * from the network which we need to delay processing until
+     * after the freeze is lifted, so we also need a bufchain to
+     * store that data.
+     */
+    int frozen;
+    bufchain queued_incoming_data;
 
     /*
      * Dispatch table for packet types that we may have to deal
@@ -2331,6 +2344,49 @@ static int do_ssh_init(Ssh ssh, unsigned char c)
     crFinish(0);
 }
 
+static void ssh_process_incoming_data(Ssh ssh,
+                                     unsigned char **data, int *datalen)
+{
+    struct Packet *pktin = ssh->s_rdpkt(ssh, data, datalen);
+    if (pktin) {
+       ssh->protocol(ssh, NULL, 0, pktin);
+       ssh_free_packet(pktin);
+    }
+}
+
+static void ssh_queue_incoming_data(Ssh ssh,
+                                   unsigned char **data, int *datalen)
+{
+    bufchain_add(&ssh->queued_incoming_data, *data, *datalen);
+    *data += *datalen;
+    *datalen = 0;
+}
+
+static void ssh_process_queued_incoming_data(Ssh ssh)
+{
+    void *vdata;
+    unsigned char *data;
+    int len, origlen;
+
+    while (!ssh->frozen && bufchain_size(&ssh->queued_incoming_data)) {
+       bufchain_prefix(&ssh->queued_incoming_data, &vdata, &len);
+       data = vdata;
+       origlen = len;
+
+       while (!ssh->frozen && len > 0)
+           ssh_process_incoming_data(ssh, &data, &len);
+
+       if (origlen > len)
+           bufchain_consume(&ssh->queued_incoming_data, origlen - len);
+    }
+}
+
+static void ssh_set_frozen(Ssh ssh, int frozen)
+{
+    sk_set_frozen(ssh->s, frozen);
+    ssh->frozen = frozen;
+}
+
 static void ssh_gotdata(Ssh ssh, unsigned char *data, int datalen)
 {
     crBegin(ssh->ssh_gotdata_crstate);
@@ -2360,13 +2416,19 @@ static void ssh_gotdata(Ssh ssh, unsigned char *data, int datalen)
      */
     if (datalen == 0)
        crReturnV;
+
+    /*
+     * Process queued data if there is any.
+     */
+    ssh_process_queued_incoming_data(ssh);
+
     while (1) {
        while (datalen > 0) {
-           struct Packet *pktin = ssh->s_rdpkt(ssh, &data, &datalen);
-           if (pktin) {
-               ssh->protocol(ssh, NULL, 0, pktin);
-               ssh_free_packet(pktin);
-           }
+           if (ssh->frozen)
+               ssh_queue_incoming_data(ssh, &data, &datalen);
+
+           ssh_process_incoming_data(ssh, &data, &datalen);
+
            if (ssh->state == SSH_STATE_CLOSED)
                return;
        }
@@ -2554,9 +2616,9 @@ static void ssh1_throttle(Ssh ssh, int adjust)
     ssh->v1_throttle_count += adjust;
     assert(ssh->v1_throttle_count >= 0);
     if (ssh->v1_throttle_count && !old_count) {
-       sk_set_frozen(ssh->s, 1);
+       ssh_set_frozen(ssh, 1);
     } else if (!ssh->v1_throttle_count && old_count) {
-       sk_set_frozen(ssh->s, 0);
+       ssh_set_frozen(ssh, 0);
     }
 }
 
@@ -2680,6 +2742,24 @@ static void ssh_agent_callback(void *sshv, void *reply, int replylen)
        do_ssh2_authconn(ssh, NULL, -1, NULL);
 }
 
+static void ssh_dialog_callback(void *sshv, int ret)
+{
+    Ssh ssh = (Ssh) sshv;
+
+    ssh->user_response = ret;
+
+    if (ssh->version == 1)
+       do_ssh1_login(ssh, NULL, -1, NULL);
+    else
+       do_ssh2_transport(ssh, NULL, -1, NULL);
+
+    /*
+     * This may have unfrozen the SSH connection, so do a
+     * queued-data run.
+     */
+    ssh_process_queued_incoming_data(ssh);
+}
+
 static void ssh_agentf_callback(void *cv, void *reply, int replylen)
 {
     struct ssh_channel *c = (struct ssh_channel *)cv;
@@ -2741,6 +2821,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
        Bignum challenge;
        char *commentp;
        int commentlen;
+        int dlgret;
     };
     crState(do_ssh1_login_state);
 
@@ -2828,10 +2909,30 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
            fatalbox("Out of memory");
        rsastr_fmt(keystr, &hostkey);
        rsa_fingerprint(fingerprint, sizeof(fingerprint), &hostkey);
-       verify_ssh_host_key(ssh->frontend,
-                           ssh->savedhost, ssh->savedport, "rsa", keystr,
-                           fingerprint);
+
+        ssh_set_frozen(ssh, 1);
+       s->dlgret = verify_ssh_host_key(ssh->frontend,
+                                        ssh->savedhost, ssh->savedport,
+                                        "rsa", keystr, fingerprint,
+                                        ssh_dialog_callback, ssh);
        sfree(keystr);
+        if (s->dlgret < 0) {
+            do {
+                crReturn(0);
+                if (pktin) {
+                    bombout(("Unexpected data from server while waiting"
+                             " for user host key response"));
+                    crStop(0);
+                }
+            } while (pktin || inlen > 0);
+            s->dlgret = ssh->user_response;
+        }
+        ssh_set_frozen(ssh, 0);
+
+        if (s->dlgret == 0) {
+            ssh->close_expected = TRUE;
+            ssh_closing((Plug)ssh, NULL, 0, 0);
+        }
     }
 
     for (i = 0; i < 32; i++) {
@@ -2893,9 +2994,25 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
 
        /* Warn about chosen cipher if necessary. */
        if (warn) {
-            sk_set_frozen(ssh->s, 1);
-           askalg(ssh->frontend, "cipher", cipher_string);
-            sk_set_frozen(ssh->s, 0);
+            ssh_set_frozen(ssh, 1);
+           s->dlgret = askalg(ssh->frontend, "cipher", cipher_string,
+                              ssh_dialog_callback, ssh);
+           if (s->dlgret < 0) {
+               do {
+                   crReturn(0);
+                   if (pktin) {
+                       bombout(("Unexpected data from server while waiting"
+                                " for user response"));
+                       crStop(0);
+                   }
+               } while (pktin || inlen > 0);
+               s->dlgret = ssh->user_response;
+           }
+            ssh_set_frozen(ssh, 0);
+           if (s->dlgret == 0) {
+               ssh->close_expected = TRUE;
+               ssh_closing((Plug)ssh, NULL, 0, 0);
+           }
         }
     }
 
@@ -4732,6 +4849,8 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
        const struct ssh_compress *preferred_comp;
        int got_session_id, activated_authconn;
        struct Packet *pktout;
+        int dlgret;
+       int guessok;
     };
     crState(do_ssh2_transport_state);
 
@@ -4945,7 +5064,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
      */
     {
        char *str;
-       int i, j, len, guessok;
+       int i, j, len;
 
        if (pktin->type != SSH2_MSG_KEXINIT) {
            bombout(("expected key exchange packet from server"));
@@ -4971,10 +5090,26 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
            }
            if (ssh->kex) {
                if (s->warn) {
-                    sk_set_frozen(ssh->s, 1);
-                   askalg(ssh->frontend, "key-exchange algorithm",
-                          ssh->kex->name);
-                    sk_set_frozen(ssh->s, 0);
+                   ssh_set_frozen(ssh, 1);
+                   s->dlgret = askalg(ssh->frontend, "key-exchange algorithm",
+                                      ssh->kex->name,
+                                      ssh_dialog_callback, ssh);
+                   if (s->dlgret < 0) {
+                       do {
+                           crReturn(0);
+                           if (pktin) {
+                               bombout(("Unexpected data from server while"
+                                        " waiting for user response"));
+                               crStop(0);
+                           }
+                       } while (pktin || inlen > 0);
+                       s->dlgret = ssh->user_response;
+                   }
+                   ssh_set_frozen(ssh, 0);
+                   if (s->dlgret == 0) {
+                       ssh->close_expected = TRUE;
+                       ssh_closing((Plug)ssh, NULL, 0, 0);
+                   }
                 }
                break;
            }
@@ -4989,7 +5124,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
         * the first algorithm in our list, even if it's still the algorithm
         * we end up using.
         */
-       guessok =
+       s->guessok =
            first_in_commasep_string(s->preferred_kex[0]->name, str, len);
        ssh_pkt_getstring(pktin, &str, &len);    /* host key algorithms */
        for (i = 0; i < lenof(hostkey_algs); i++) {
@@ -4998,7 +5133,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
                break;
            }
        }
-       guessok = guessok &&
+       s->guessok = s->guessok &&
            first_in_commasep_string(hostkey_algs[0]->name, str, len);
        ssh_pkt_getstring(pktin, &str, &len);    /* client->server cipher */
        s->warn = 0;
@@ -5016,10 +5151,27 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
            }
            if (s->cscipher_tobe) {
                if (s->warn) {
-                    sk_set_frozen(ssh->s, 1);
-                   askalg(ssh->frontend, "client-to-server cipher",
-                          s->cscipher_tobe->name);
-                    sk_set_frozen(ssh->s, 0);
+                   ssh_set_frozen(ssh, 1);
+                   s->dlgret = askalg(ssh->frontend,
+                                      "client-to-server cipher",
+                                      s->cscipher_tobe->name,
+                                      ssh_dialog_callback, ssh);
+                   if (s->dlgret < 0) {
+                       do {
+                           crReturn(0);
+                           if (pktin) {
+                               bombout(("Unexpected data from server while"
+                                        " waiting for user response"));
+                               crStop(0);
+                           }
+                       } while (pktin || inlen > 0);
+                       s->dlgret = ssh->user_response;
+                   }
+                   ssh_set_frozen(ssh, 0);
+                   if (s->dlgret == 0) {
+                       ssh->close_expected = TRUE;
+                       ssh_closing((Plug)ssh, NULL, 0, 0);
+                   }
                 }
                break;
            }
@@ -5046,10 +5198,27 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
            }
            if (s->sccipher_tobe) {
                if (s->warn) {
-                    sk_set_frozen(ssh->s, 1);
-                   askalg(ssh->frontend, "server-to-client cipher",
-                          s->sccipher_tobe->name);
-                    sk_set_frozen(ssh->s, 0);
+                   ssh_set_frozen(ssh, 1);
+                   s->dlgret = askalg(ssh->frontend,
+                                      "server-to-client cipher",
+                                      s->sccipher_tobe->name,
+                                      ssh_dialog_callback, ssh);
+                   if (s->dlgret < 0) {
+                       do {
+                           crReturn(0);
+                           if (pktin) {
+                               bombout(("Unexpected data from server while"
+                                        " waiting for user response"));
+                               crStop(0);
+                           }
+                       } while (pktin || inlen > 0);
+                       s->dlgret = ssh->user_response;
+                   }
+                   ssh_set_frozen(ssh, 0);
+                   if (s->dlgret == 0) {
+                       ssh->close_expected = TRUE;
+                       ssh_closing((Plug)ssh, NULL, 0, 0);
+                   }
                 }
                break;
            }
@@ -5094,7 +5263,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
        }
        ssh_pkt_getstring(pktin, &str, &len);  /* client->server language */
        ssh_pkt_getstring(pktin, &str, &len);  /* server->client language */
-       if (ssh2_pkt_getbool(pktin) && !guessok) /* first_kex_packet_follows */
+       if (ssh2_pkt_getbool(pktin) && !s->guessok) /* first_kex_packet_follows */
            crWaitUntil(pktin);                /* Ignore packet */
     }
 
@@ -5218,11 +5387,29 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
      */
     s->keystr = ssh->hostkey->fmtkey(s->hkey);
     s->fingerprint = ssh->hostkey->fingerprint(s->hkey);
-    sk_set_frozen(ssh->s, 1);
-    verify_ssh_host_key(ssh->frontend,
-                       ssh->savedhost, ssh->savedport, ssh->hostkey->keytype,
-                       s->keystr, s->fingerprint);
-    sk_set_frozen(ssh->s, 0);
+    ssh_set_frozen(ssh, 1);
+    s->dlgret = verify_ssh_host_key(ssh->frontend,
+                                    ssh->savedhost, ssh->savedport,
+                                    ssh->hostkey->keytype, s->keystr,
+                                   s->fingerprint,
+                                    ssh_dialog_callback, ssh);
+    if (s->dlgret < 0) {
+        do {
+            crReturn(0);
+            if (pktin) {
+                bombout(("Unexpected data from server while waiting"
+                         " for user host key response"));
+                    crStop(0);
+            }
+        } while (pktin || inlen > 0);
+        s->dlgret = ssh->user_response;
+    }
+    ssh_set_frozen(ssh, 0);
+    if (s->dlgret == 0) {
+        ssh->close_expected = TRUE;
+        ssh_closing((Plug)ssh, NULL, 0, 0);
+        crStop(0);
+    }
     if (!s->got_session_id) {     /* don't bother logging this in rekeys */
        logevent("Host key fingerprint is:");
        logevent(s->fingerprint);
@@ -7477,6 +7664,8 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle,
     ssh->queueing = FALSE;
     ssh->qhead = ssh->qtail = NULL;
     ssh->deferred_rekey_reason = NULL;
+    bufchain_init(&ssh->queued_incoming_data);
+    ssh->frozen = FALSE;
 
     *backend_handle = ssh;
 
@@ -7603,6 +7792,7 @@ static void ssh_free(void *handle)
     expire_timer_context(ssh);
     if (ssh->pinger)
        pinger_free(ssh->pinger);
+    bufchain_clear(&ssh->queued_incoming_data);
     sfree(ssh);
 
     random_unref();
index 2a29380..2ef1831 100644 (file)
@@ -2294,8 +2294,9 @@ int reallyclose(void *frontend)
     return ret;
 }
 
-void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
-                        char *keystr, char *fingerprint)
+int verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
+                        char *keystr, char *fingerprint,
+                        void (*callback)(void *ctx, int result), void *ctx)
 {
     static const char absenttxt[] =
        "The server's host key is not cached. You have no guarantee "
@@ -2332,7 +2333,7 @@ void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
     ret = verify_host_key(host, port, keytype, keystr);
 
     if (ret == 0)                     /* success - key matched OK */
-       return;
+       return 1;
 
     text = dupprintf((ret == 2 ? wrongtxt : absenttxt), keytype, fingerprint);
 
@@ -2347,16 +2348,20 @@ void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
     sfree(text);
 
     if (ret == 0)
-       cleanup_exit(0);
-    else if (ret == 2)
-       store_host_key(host, port, keytype, keystr);
+        return 0;                      /* do not continue with connection */
+    else {
+        if (ret == 2)
+            store_host_key(host, port, keytype, keystr);
+        return 1;                      /* continue with connection */
+    }
 }
 
 /*
  * Ask whether the selected algorithm is acceptable (since it was
  * below the configured 'warn' threshold).
  */
-void askalg(void *frontend, const char *algtype, const char *algname)
+int askalg(void *frontend, const char *algtype, const char *algname,
+          void (*callback)(void *ctx, int result), void *ctx)
 {
     static const char msg[] =
        "The first %s supported by the server is "
@@ -2375,9 +2380,9 @@ void askalg(void *frontend, const char *algtype, const char *algname)
     sfree(text);
 
     if (ret) {
-       return;
+       return 1;
     } else {
-       cleanup_exit(0);
+       return 0;
     }
 }
 
index 7f05d1f..ef2866c 100644 (file)
@@ -47,8 +47,9 @@ void timer_change_notify(long next)
 {
 }
 
-void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
-                        char *keystr, char *fingerprint)
+int verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
+                        char *keystr, char *fingerprint,
+                        void (*callback)(void *ctx, int result), void *ctx)
 {
     int ret;
 
@@ -107,12 +108,12 @@ void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
     ret = verify_host_key(host, port, keytype, keystr);
 
     if (ret == 0)                     /* success - key matched OK */
-       return;
+       return 1;
 
     if (ret == 2) {                   /* key was different */
        if (console_batch_mode) {
            fprintf(stderr, wrongmsg_batch, keytype, fingerprint);
-           cleanup_exit(1);
+           return 0;
        }
        fprintf(stderr, wrongmsg, keytype, fingerprint);
        fflush(stderr);
@@ -120,7 +121,7 @@ void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
     if (ret == 1) {                   /* key was absent */
        if (console_batch_mode) {
            fprintf(stderr, absentmsg_batch, keytype, fingerprint);
-           cleanup_exit(1);
+           return 0;
        }
        fprintf(stderr, absentmsg, keytype, fingerprint);
        fflush(stderr);
@@ -140,9 +141,10 @@ void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
     if (line[0] != '\0' && line[0] != '\r' && line[0] != '\n') {
        if (line[0] == 'y' || line[0] == 'Y')
            store_host_key(host, port, keytype, keystr);
+        return 1;
     } else {
        fprintf(stderr, abandoned);
-       cleanup_exit(0);
+        return 0;
     }
 }
 
@@ -150,7 +152,8 @@ void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
  * Ask whether the selected algorithm is acceptable (since it was
  * below the configured 'warn' threshold).
  */
-void askalg(void *frontend, const char *algtype, const char *algname)
+int askalg(void *frontend, const char *algtype, const char *algname,
+          void (*callback)(void *ctx, int result), void *ctx)
 {
     static const char msg[] =
        "The first %s supported by the server is\n"
@@ -166,7 +169,7 @@ void askalg(void *frontend, const char *algtype, const char *algname)
 
     if (console_batch_mode) {
        fprintf(stderr, msg_batch, algtype, algname);
-       cleanup_exit(1);
+       return 0;
     }
 
     fprintf(stderr, msg, algtype, algname);
@@ -184,10 +187,10 @@ void askalg(void *frontend, const char *algtype, const char *algname)
     }
 
     if (line[0] == 'y' || line[0] == 'Y') {
-       return;
+       return 1;
     } else {
        fprintf(stderr, abandoned);
-       cleanup_exit(0);
+       return 0;
     }
 }
 
index 3417720..0791358 100644 (file)
@@ -45,8 +45,9 @@ void timer_change_notify(long next)
 {
 }
 
-void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
-                        char *keystr, char *fingerprint)
+int verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
+                        char *keystr, char *fingerprint,
+                        void (*callback)(void *ctx, int result), void *ctx)
 {
     int ret;
     HANDLE hin;
@@ -111,12 +112,12 @@ void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
     ret = verify_host_key(host, port, keytype, keystr);
 
     if (ret == 0)                     /* success - key matched OK */
-       return;
+       return 1;
 
     if (ret == 2) {                   /* key was different */
        if (console_batch_mode) {
            fprintf(stderr, wrongmsg_batch, keytype, fingerprint);
-           cleanup_exit(1);
+            return 0;
        }
        fprintf(stderr, wrongmsg, keytype, fingerprint);
        fflush(stderr);
@@ -124,7 +125,7 @@ void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
     if (ret == 1) {                   /* key was absent */
        if (console_batch_mode) {
            fprintf(stderr, absentmsg_batch, keytype, fingerprint);
-           cleanup_exit(1);
+            return 0;
        }
        fprintf(stderr, absentmsg, keytype, fingerprint);
        fflush(stderr);
@@ -140,9 +141,10 @@ void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
     if (line[0] != '\0' && line[0] != '\r' && line[0] != '\n') {
        if (line[0] == 'y' || line[0] == 'Y')
            store_host_key(host, port, keytype, keystr);
+        return 1;
     } else {
        fprintf(stderr, abandoned);
-       cleanup_exit(0);
+        return 0;
     }
 }
 
@@ -154,7 +156,8 @@ void update_specials_menu(void *frontend)
  * Ask whether the selected algorithm is acceptable (since it was
  * below the configured 'warn' threshold).
  */
-void askalg(void *frontend, const char *algtype, const char *algname)
+int askalg(void *frontend, const char *algtype, const char *algname,
+          void (*callback)(void *ctx, int result), void *ctx)
 {
     HANDLE hin;
     DWORD savemode, i;
@@ -173,7 +176,7 @@ void askalg(void *frontend, const char *algtype, const char *algname)
 
     if (console_batch_mode) {
        fprintf(stderr, msg_batch, algtype, algname);
-       cleanup_exit(1);
+       return 0;
     }
 
     fprintf(stderr, msg, algtype, algname);
@@ -187,10 +190,10 @@ void askalg(void *frontend, const char *algtype, const char *algname)
     SetConsoleMode(hin, savemode);
 
     if (line[0] == 'y' || line[0] == 'Y') {
-       return;
+       return 1;
     } else {
        fprintf(stderr, abandoned);
-       cleanup_exit(0);
+       return 0;
     }
 }
 
index 8903871..742a761 100644 (file)
@@ -723,8 +723,9 @@ static VOID CALLBACK verify_ssh_host_key_help(LPHELPINFO lpHelpInfo)
     }
 }
 
-void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
-                        char *keystr, char *fingerprint)
+int verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
+                        char *keystr, char *fingerprint,
+                        void (*callback)(void *ctx, int result), void *ctx)
 {
     int ret;
 
@@ -782,7 +783,7 @@ void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
     ret = verify_host_key(host, port, keytype, keystr);
 
     if (ret == 0)                     /* success - key matched OK */
-       return;
+       return 1;
     if (ret == 2) {                   /* key was different */
        int mbret;
        mbox.lpszText = dupprintf(wrongmsg, appname, keytype, fingerprint,
@@ -797,7 +798,8 @@ void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
        if (mbret == IDYES)
            store_host_key(host, port, keytype, keystr);
        if (mbret == IDCANCEL)
-           cleanup_exit(0);
+           return 0;
+        return 1;
     }
     if (ret == 1) {                   /* key was absent */
        int mbret;
@@ -812,7 +814,8 @@ void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
        if (mbret == IDYES)
            store_host_key(host, port, keytype, keystr);
        if (mbret == IDCANCEL)
-           cleanup_exit(0);
+           return 0;
+        return 1;
     }
 }
 
@@ -820,7 +823,8 @@ void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
  * Ask whether the selected algorithm is acceptable (since it was
  * below the configured 'warn' threshold).
  */
-void askalg(void *frontend, const char *algtype, const char *algname)
+int askalg(void *frontend, const char *algtype, const char *algname,
+          void (*callback)(void *ctx, int result), void *ctx)
 {
     static const char mbtitle[] = "%s Security Alert";
     static const char msg[] =
@@ -838,9 +842,9 @@ void askalg(void *frontend, const char *algtype, const char *algname)
     sfree(message);
     sfree(title);
     if (mbret == IDYES)
-       return;
+       return 1;
     else
-       cleanup_exit(0);
+       return 0;
 }
 
 /*