From 3d9449a111194530856126f3dc89543d83fa0440 Mon Sep 17 00:00:00 2001 From: simon Date: Thu, 17 Feb 2005 18:34:24 +0000 Subject: [PATCH] Revamp interface to verify_ssh_host_key() and askalg(). Each of them 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 | 52 +++++------ macosx/README.OSX | 7 -- macosx/osxclass.h | 4 + macosx/osxdlg.m | 143 ++++++++++++++++++++++++------ macosx/osxwin.m | 20 +++++ putty.h | 23 ++++- ssh.c | 260 ++++++++++++++++++++++++++++++++++++++++++++++-------- unix/gtkdlg.c | 23 +++-- unix/uxcons.c | 23 ++--- windows/wincons.c | 23 ++--- windows/windlg.c | 20 +++-- 11 files changed, 457 insertions(+), 141 deletions(-) diff --git a/mac/mac.c b/mac/mac.c index f663deea..2fb7d44c 100644 --- 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) diff --git a/macosx/README.OSX b/macosx/README.OSX index 9dded051..8d989a63 100644 --- a/macosx/README.OSX +++ b/macosx/README.OSX @@ -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 diff --git a/macosx/osxclass.h b/macosx/osxclass.h index 5f009a91..76a8fc7c 100644 --- a/macosx/osxclass.h +++ b/macosx/osxclass.h @@ -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 /* diff --git a/macosx/osxdlg.m b/macosx/osxdlg.m index 5bc13a43..0ea327f5 100644 --- a/macosx/osxdlg.m +++ b/macosx/osxdlg.m @@ -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) diff --git a/macosx/osxwin.m b/macosx/osxwin.m index a54f771f..8166f913 100644 --- a/macosx/osxwin.m +++ b/macosx/osxwin.m @@ -207,6 +207,8 @@ { NSRect rect = { {0,0}, {0,0} }; + alert_ctx = NULL; + cfg = aCfg; /* structure copy */ init_ucs(&ucsdata, cfg.line_codepage, cfg.utf8_override, @@ -307,6 +309,7 @@ * 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 0b49dfa8..46708f10 100644 --- 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 e2e80772..e41e9a9e 100644 --- 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(); diff --git a/unix/gtkdlg.c b/unix/gtkdlg.c index 2a293804..2ef18313 100644 --- a/unix/gtkdlg.c +++ b/unix/gtkdlg.c @@ -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; } } diff --git a/unix/uxcons.c b/unix/uxcons.c index 7f05d1fa..ef2866cc 100644 --- a/unix/uxcons.c +++ b/unix/uxcons.c @@ -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; } } diff --git a/windows/wincons.c b/windows/wincons.c index 3417720a..07913586 100644 --- a/windows/wincons.c +++ b/windows/wincons.c @@ -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; } } diff --git a/windows/windlg.c b/windows/windlg.c index 89038714..742a761b 100644 --- a/windows/windlg.c +++ b/windows/windlg.c @@ -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; } /* -- 2.11.0