1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-03-13 02:23:50 -05:00

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.

[originally from svn r5330]
This commit is contained in:
Simon Tatham 2005-02-17 18:34:24 +00:00
parent 92ccb964a2
commit 8574822b9b
11 changed files with 456 additions and 140 deletions

View File

@ -691,8 +691,9 @@ int agent_query(void *in, int inlen, void **out, int *outlen,
/* Temporary null routines for testing. */ /* Temporary null routines for testing. */
void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype, int verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
char *keystr, char *fingerprint) char *keystr, char *fingerprint,
void (*callback)(void *ctx, int result), void *ctx)
{ {
Str255 pappname; Str255 pappname;
Str255 pfingerprint; Str255 pfingerprint;
@ -705,64 +706,55 @@ void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
c2pstrcpy(pfingerprint, fingerprint); c2pstrcpy(pfingerprint, fingerprint);
/* /*
* This function is horribly wrong. For one thing, the alert * The alert shouldn't be modal, it should be movable modal, or
* shouldn't be modal, it should be movable modal, or a sheet in * a sheet in Aqua. Also, PuTTY might be in the background, in
* Aqua. Also, PuTTY might be in the background, in which case we * which case we should use the Notification Manager to wake up
* should use the Notification Manager to wake up the user. In * the user. In any case, we shouldn't hold up processing of
* any case, we shouldn't hold up processing of other connections' * other connections' data just because this one's waiting for
* data just because this one's waiting for the user. Also see the * the user.
* note below about closing the connection. All in all, a bit of
* a mess really.
*/ */
/* Verify the key against the cache */ /* Verify the key against the cache */
ret = verify_host_key(host, port, keytype, keystr); ret = verify_host_key(host, port, keytype, keystr);
if (ret == 0) /* success - key matched OK */ if (ret == 0) { /* success - key matched OK */
return; return 1;
if (ret == 2) { /* key was different */ } else if (ret == 2) { /* key was different */
ParamText(pappname, pkeytype, pfingerprint, NULL); ParamText(pappname, pkeytype, pfingerprint, NULL);
alertret=CautionAlert(wWrong, NULL); alertret=CautionAlert(wWrong, NULL);
if (alertret == 8) { if (alertret == 8) {
/* Cancel */ /* Cancel */
goto cancel; return 0;
} else if (alertret == 9) { } else if (alertret == 9) {
/* Connect Just Once */ /* Connect Just Once */
return 1;
} else { } else {
/* Update Key */ /* Update Key */
store_host_key(host, port, keytype, keystr); store_host_key(host, port, keytype, keystr);
return 1;
} }
} } else /* ret == 1 */ { /* key was absent */
if (ret == 1) { /* key was absent */
ParamText(pkeytype, pfingerprint, pappname, NULL); ParamText(pkeytype, pfingerprint, pappname, NULL);
alertret=CautionAlert(wAbsent, NULL); alertret=CautionAlert(wAbsent, NULL);
if (alertret == 7) { if (alertret == 7) {
/* Cancel */ /* Cancel */
goto cancel; return 0;
} else if (alertret == 8) { } else if (alertret == 8) {
/* Connect Just Once */ /* Connect Just Once */
return 1;
} else { } else {
/* Update Key */ /* Update Key */
store_host_key(host, port, keytype, keystr); 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) void old_keyfile_warning(void)

View 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 for the data and therefore loses them all. If that happens, don't
say you weren't warned! 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: Other ways in which the port is currently unfinished include:
- terminal display is horribly slow - terminal display is horribly slow

View File

@ -40,6 +40,8 @@ extern AppController *controller;
void *ldisc; void *ldisc;
Backend *back; Backend *back;
void *backhandle; void *backhandle;
void (*alert_callback)(void *, int);
void *alert_ctx;
} }
- (id)initWithConfig:(Config)cfg; - (id)initWithConfig:(Config)cfg;
- (void)drawStartFinish:(BOOL)start; - (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 - (void)doText:(wchar_t *)text len:(int)len x:(int)x y:(int)y
attr:(unsigned long)attr lattr:(int)lattr; attr:(unsigned long)attr lattr:(int)lattr;
- (int)fromBackend:(const char *)data len:(int)len isStderr:(int)is_stderr; - (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 @end
/* /*

View File

@ -311,16 +311,104 @@ int askappend(void *frontend, Filename filename)
return 0; /* FIXME */ 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)
{ {
fatalbox("Cipher algorithm dialog box not supported yet"); struct algstate *state = (struct algstate *)ctx;
return; /* FIXME */
state->callback(state->ctx, result == 0);
sfree(state);
} }
void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype, int askalg(void *frontend, const char *algtype, const char *algname,
char *keystr, char *fingerprint) 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)
{
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);
}
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; int ret;
char *text;
SessionWindow *win = (SessionWindow *)frontend;
struct hostkeystate *state;
NSAlert *alert;
/* /*
* Verify the key. * 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); ret = verify_host_key(host, port, keytype, keystr);
if (ret == 0) if (ret == 0)
return; return 1;
/* text = dupprintf((ret == 2 ? wrongtxt : absenttxt), keytype, fingerprint);
* FIXME FIXME FIXME. I currently lack any sensible means of
* asking the user for a verification non-application-modally, state = snew(struct hostkeystate);
* _or_ any means of closing just this connection if the answer state->callback = callback;
* is no (the Unix and Windows ports just exit() in this state->ctx = ctx;
* situation since they're one-connection-per-process). state->host = dupstr(host);
* state->port = port;
* What I need to do is to make this function optionally- state->keytype = dupstr(keytype);
* asynchronous, much like the interface to agent_query(). It state->keystr = dupstr(keystr);
* can either run modally and return a result directly, _or_ it
* can kick off a non-modal dialog, return a `please wait' alert = [[NSAlert alloc] init];
* status, and the dialog can call the backend back when the [alert setInformativeText:[NSString stringWithCString:text]];
* result comes in. Also, in either case, the aye/nay result [alert addButtonWithTitle:@"Cancel"];
* wants to be passed to the backend so that it can tear down [alert addButtonWithTitle:@"Connect Once"];
* the connection if the answer was nay. [alert addButtonWithTitle:@"Accept"];
* [win startAlert:alert withCallback:verify_ssh_host_key_callback
* For the moment, I simply bomb out if we have an unrecognised andCtx:state];
* 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 return -1;
* set up by running the Unix port.
*/
fatalbox("Host key dialog box not supported yet");
} }
void old_keyfile_warning(void) void old_keyfile_warning(void)

View File

@ -207,6 +207,8 @@
{ {
NSRect rect = { {0,0}, {0,0} }; NSRect rect = { {0,0}, {0,0} };
alert_ctx = NULL;
cfg = aCfg; /* structure copy */ cfg = aCfg; /* structure copy */
init_ucs(&ucsdata, cfg.line_codepage, cfg.utf8_override, init_ucs(&ucsdata, cfg.line_codepage, cfg.utf8_override,
@ -307,6 +309,7 @@
* terminal, the backend, the ldisc, the logctx, you name it. * terminal, the backend, the ldisc, the logctx, you name it.
* Do so. * Do so.
*/ */
sfree(alert_ctx);
[super dealloc]; [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); 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 @end
int from_backend(void *frontend, int is_stderr, const char *data, int len) int from_backend(void *frontend, int is_stderr, const char *data, int len)

23
putty.h
View File

@ -896,9 +896,26 @@ int wc_unescape(char *output, const char *wildcard);
* Exports from windlg.c * Exports from windlg.c
*/ */
void logevent(void *frontend, const char *); void logevent(void *frontend, const char *);
void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype, /*
char *keystr, char *fingerprint); * verify_ssh_host_key() can return one of three values:
void askalg(void *frontend, const char *algtype, const char *algname); *
* - +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); int askappend(void *frontend, Filename filename);
/* /*

260
ssh.c
View File

@ -726,10 +726,23 @@ struct ssh_tag {
Config cfg; Config cfg;
/* /*
* Used to transfer data back from async agent callbacks. * Used to transfer data back from async callbacks.
*/ */
void *agent_response; void *agent_response;
int agent_response_len; 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 * 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); 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) static void ssh_gotdata(Ssh ssh, unsigned char *data, int datalen)
{ {
crBegin(ssh->ssh_gotdata_crstate); crBegin(ssh->ssh_gotdata_crstate);
@ -2360,13 +2416,19 @@ static void ssh_gotdata(Ssh ssh, unsigned char *data, int datalen)
*/ */
if (datalen == 0) if (datalen == 0)
crReturnV; crReturnV;
/*
* Process queued data if there is any.
*/
ssh_process_queued_incoming_data(ssh);
while (1) { while (1) {
while (datalen > 0) { while (datalen > 0) {
struct Packet *pktin = ssh->s_rdpkt(ssh, &data, &datalen); if (ssh->frozen)
if (pktin) { ssh_queue_incoming_data(ssh, &data, &datalen);
ssh->protocol(ssh, NULL, 0, pktin);
ssh_free_packet(pktin); ssh_process_incoming_data(ssh, &data, &datalen);
}
if (ssh->state == SSH_STATE_CLOSED) if (ssh->state == SSH_STATE_CLOSED)
return; return;
} }
@ -2554,9 +2616,9 @@ static void ssh1_throttle(Ssh ssh, int adjust)
ssh->v1_throttle_count += adjust; ssh->v1_throttle_count += adjust;
assert(ssh->v1_throttle_count >= 0); assert(ssh->v1_throttle_count >= 0);
if (ssh->v1_throttle_count && !old_count) { 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) { } 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); 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) static void ssh_agentf_callback(void *cv, void *reply, int replylen)
{ {
struct ssh_channel *c = (struct ssh_channel *)cv; 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; Bignum challenge;
char *commentp; char *commentp;
int commentlen; int commentlen;
int dlgret;
}; };
crState(do_ssh1_login_state); 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"); fatalbox("Out of memory");
rsastr_fmt(keystr, &hostkey); rsastr_fmt(keystr, &hostkey);
rsa_fingerprint(fingerprint, sizeof(fingerprint), &hostkey); rsa_fingerprint(fingerprint, sizeof(fingerprint), &hostkey);
verify_ssh_host_key(ssh->frontend,
ssh->savedhost, ssh->savedport, "rsa", keystr, ssh_set_frozen(ssh, 1);
fingerprint); s->dlgret = verify_ssh_host_key(ssh->frontend,
ssh->savedhost, ssh->savedport,
"rsa", keystr, fingerprint,
ssh_dialog_callback, ssh);
sfree(keystr); 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++) { 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. */ /* Warn about chosen cipher if necessary. */
if (warn) { if (warn) {
sk_set_frozen(ssh->s, 1); ssh_set_frozen(ssh, 1);
askalg(ssh->frontend, "cipher", cipher_string); s->dlgret = askalg(ssh->frontend, "cipher", cipher_string,
sk_set_frozen(ssh->s, 0); 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; const struct ssh_compress *preferred_comp;
int got_session_id, activated_authconn; int got_session_id, activated_authconn;
struct Packet *pktout; struct Packet *pktout;
int dlgret;
int guessok;
}; };
crState(do_ssh2_transport_state); crState(do_ssh2_transport_state);
@ -4945,7 +5064,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
*/ */
{ {
char *str; char *str;
int i, j, len, guessok; int i, j, len;
if (pktin->type != SSH2_MSG_KEXINIT) { if (pktin->type != SSH2_MSG_KEXINIT) {
bombout(("expected key exchange packet from server")); 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 (ssh->kex) {
if (s->warn) { if (s->warn) {
sk_set_frozen(ssh->s, 1); ssh_set_frozen(ssh, 1);
askalg(ssh->frontend, "key-exchange algorithm", s->dlgret = askalg(ssh->frontend, "key-exchange algorithm",
ssh->kex->name); ssh->kex->name,
sk_set_frozen(ssh->s, 0); 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; 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 * the first algorithm in our list, even if it's still the algorithm
* we end up using. * we end up using.
*/ */
guessok = s->guessok =
first_in_commasep_string(s->preferred_kex[0]->name, str, len); first_in_commasep_string(s->preferred_kex[0]->name, str, len);
ssh_pkt_getstring(pktin, &str, &len); /* host key algorithms */ ssh_pkt_getstring(pktin, &str, &len); /* host key algorithms */
for (i = 0; i < lenof(hostkey_algs); i++) { for (i = 0; i < lenof(hostkey_algs); i++) {
@ -4998,7 +5133,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
break; break;
} }
} }
guessok = guessok && s->guessok = s->guessok &&
first_in_commasep_string(hostkey_algs[0]->name, str, len); first_in_commasep_string(hostkey_algs[0]->name, str, len);
ssh_pkt_getstring(pktin, &str, &len); /* client->server cipher */ ssh_pkt_getstring(pktin, &str, &len); /* client->server cipher */
s->warn = 0; s->warn = 0;
@ -5016,10 +5151,27 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
} }
if (s->cscipher_tobe) { if (s->cscipher_tobe) {
if (s->warn) { if (s->warn) {
sk_set_frozen(ssh->s, 1); ssh_set_frozen(ssh, 1);
askalg(ssh->frontend, "client-to-server cipher", s->dlgret = askalg(ssh->frontend,
s->cscipher_tobe->name); "client-to-server cipher",
sk_set_frozen(ssh->s, 0); 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; break;
} }
@ -5046,10 +5198,27 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
} }
if (s->sccipher_tobe) { if (s->sccipher_tobe) {
if (s->warn) { if (s->warn) {
sk_set_frozen(ssh->s, 1); ssh_set_frozen(ssh, 1);
askalg(ssh->frontend, "server-to-client cipher", s->dlgret = askalg(ssh->frontend,
s->sccipher_tobe->name); "server-to-client cipher",
sk_set_frozen(ssh->s, 0); 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; 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); /* client->server language */
ssh_pkt_getstring(pktin, &str, &len); /* server->client 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 */ 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->keystr = ssh->hostkey->fmtkey(s->hkey);
s->fingerprint = ssh->hostkey->fingerprint(s->hkey); s->fingerprint = ssh->hostkey->fingerprint(s->hkey);
sk_set_frozen(ssh->s, 1); ssh_set_frozen(ssh, 1);
verify_ssh_host_key(ssh->frontend, s->dlgret = verify_ssh_host_key(ssh->frontend,
ssh->savedhost, ssh->savedport, ssh->hostkey->keytype, ssh->savedhost, ssh->savedport,
s->keystr, s->fingerprint); ssh->hostkey->keytype, s->keystr,
sk_set_frozen(ssh->s, 0); 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 */ if (!s->got_session_id) { /* don't bother logging this in rekeys */
logevent("Host key fingerprint is:"); logevent("Host key fingerprint is:");
logevent(s->fingerprint); logevent(s->fingerprint);
@ -7477,6 +7664,8 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle,
ssh->queueing = FALSE; ssh->queueing = FALSE;
ssh->qhead = ssh->qtail = NULL; ssh->qhead = ssh->qtail = NULL;
ssh->deferred_rekey_reason = NULL; ssh->deferred_rekey_reason = NULL;
bufchain_init(&ssh->queued_incoming_data);
ssh->frozen = FALSE;
*backend_handle = ssh; *backend_handle = ssh;
@ -7603,6 +7792,7 @@ static void ssh_free(void *handle)
expire_timer_context(ssh); expire_timer_context(ssh);
if (ssh->pinger) if (ssh->pinger)
pinger_free(ssh->pinger); pinger_free(ssh->pinger);
bufchain_clear(&ssh->queued_incoming_data);
sfree(ssh); sfree(ssh);
random_unref(); random_unref();

View File

@ -2294,8 +2294,9 @@ int reallyclose(void *frontend)
return ret; return ret;
} }
void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype, int verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
char *keystr, char *fingerprint) char *keystr, char *fingerprint,
void (*callback)(void *ctx, int result), void *ctx)
{ {
static const char absenttxt[] = static const char absenttxt[] =
"The server's host key is not cached. You have no guarantee " "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); ret = verify_host_key(host, port, keytype, keystr);
if (ret == 0) /* success - key matched OK */ if (ret == 0) /* success - key matched OK */
return; return 1;
text = dupprintf((ret == 2 ? wrongtxt : absenttxt), keytype, fingerprint); 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); sfree(text);
if (ret == 0) if (ret == 0)
cleanup_exit(0); return 0; /* do not continue with connection */
else if (ret == 2) else {
if (ret == 2)
store_host_key(host, port, keytype, keystr); store_host_key(host, port, keytype, keystr);
return 1; /* continue with connection */
}
} }
/* /*
* Ask whether the selected algorithm is acceptable (since it was * Ask whether the selected algorithm is acceptable (since it was
* below the configured 'warn' threshold). * 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[] = static const char msg[] =
"The first %s supported by the server is " "The first %s supported by the server is "
@ -2375,9 +2380,9 @@ void askalg(void *frontend, const char *algtype, const char *algname)
sfree(text); sfree(text);
if (ret) { if (ret) {
return; return 1;
} else { } else {
cleanup_exit(0); return 0;
} }
} }

View File

@ -47,8 +47,9 @@ void timer_change_notify(long next)
{ {
} }
void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype, int verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
char *keystr, char *fingerprint) char *keystr, char *fingerprint,
void (*callback)(void *ctx, int result), void *ctx)
{ {
int ret; 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); ret = verify_host_key(host, port, keytype, keystr);
if (ret == 0) /* success - key matched OK */ if (ret == 0) /* success - key matched OK */
return; return 1;
if (ret == 2) { /* key was different */ if (ret == 2) { /* key was different */
if (console_batch_mode) { if (console_batch_mode) {
fprintf(stderr, wrongmsg_batch, keytype, fingerprint); fprintf(stderr, wrongmsg_batch, keytype, fingerprint);
cleanup_exit(1); return 0;
} }
fprintf(stderr, wrongmsg, keytype, fingerprint); fprintf(stderr, wrongmsg, keytype, fingerprint);
fflush(stderr); 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 (ret == 1) { /* key was absent */
if (console_batch_mode) { if (console_batch_mode) {
fprintf(stderr, absentmsg_batch, keytype, fingerprint); fprintf(stderr, absentmsg_batch, keytype, fingerprint);
cleanup_exit(1); return 0;
} }
fprintf(stderr, absentmsg, keytype, fingerprint); fprintf(stderr, absentmsg, keytype, fingerprint);
fflush(stderr); 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] != '\0' && line[0] != '\r' && line[0] != '\n') {
if (line[0] == 'y' || line[0] == 'Y') if (line[0] == 'y' || line[0] == 'Y')
store_host_key(host, port, keytype, keystr); store_host_key(host, port, keytype, keystr);
return 1;
} else { } else {
fprintf(stderr, abandoned); 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 * Ask whether the selected algorithm is acceptable (since it was
* below the configured 'warn' threshold). * 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[] = static const char msg[] =
"The first %s supported by the server is\n" "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) { if (console_batch_mode) {
fprintf(stderr, msg_batch, algtype, algname); fprintf(stderr, msg_batch, algtype, algname);
cleanup_exit(1); return 0;
} }
fprintf(stderr, msg, algtype, algname); 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') { if (line[0] == 'y' || line[0] == 'Y') {
return; return 1;
} else { } else {
fprintf(stderr, abandoned); fprintf(stderr, abandoned);
cleanup_exit(0); return 0;
} }
} }

View File

@ -45,8 +45,9 @@ void timer_change_notify(long next)
{ {
} }
void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype, int verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
char *keystr, char *fingerprint) char *keystr, char *fingerprint,
void (*callback)(void *ctx, int result), void *ctx)
{ {
int ret; int ret;
HANDLE hin; 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); ret = verify_host_key(host, port, keytype, keystr);
if (ret == 0) /* success - key matched OK */ if (ret == 0) /* success - key matched OK */
return; return 1;
if (ret == 2) { /* key was different */ if (ret == 2) { /* key was different */
if (console_batch_mode) { if (console_batch_mode) {
fprintf(stderr, wrongmsg_batch, keytype, fingerprint); fprintf(stderr, wrongmsg_batch, keytype, fingerprint);
cleanup_exit(1); return 0;
} }
fprintf(stderr, wrongmsg, keytype, fingerprint); fprintf(stderr, wrongmsg, keytype, fingerprint);
fflush(stderr); 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 (ret == 1) { /* key was absent */
if (console_batch_mode) { if (console_batch_mode) {
fprintf(stderr, absentmsg_batch, keytype, fingerprint); fprintf(stderr, absentmsg_batch, keytype, fingerprint);
cleanup_exit(1); return 0;
} }
fprintf(stderr, absentmsg, keytype, fingerprint); fprintf(stderr, absentmsg, keytype, fingerprint);
fflush(stderr); 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] != '\0' && line[0] != '\r' && line[0] != '\n') {
if (line[0] == 'y' || line[0] == 'Y') if (line[0] == 'y' || line[0] == 'Y')
store_host_key(host, port, keytype, keystr); store_host_key(host, port, keytype, keystr);
return 1;
} else { } else {
fprintf(stderr, abandoned); 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 * Ask whether the selected algorithm is acceptable (since it was
* below the configured 'warn' threshold). * 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; HANDLE hin;
DWORD savemode, i; DWORD savemode, i;
@ -173,7 +176,7 @@ void askalg(void *frontend, const char *algtype, const char *algname)
if (console_batch_mode) { if (console_batch_mode) {
fprintf(stderr, msg_batch, algtype, algname); fprintf(stderr, msg_batch, algtype, algname);
cleanup_exit(1); return 0;
} }
fprintf(stderr, msg, algtype, algname); fprintf(stderr, msg, algtype, algname);
@ -187,10 +190,10 @@ void askalg(void *frontend, const char *algtype, const char *algname)
SetConsoleMode(hin, savemode); SetConsoleMode(hin, savemode);
if (line[0] == 'y' || line[0] == 'Y') { if (line[0] == 'y' || line[0] == 'Y') {
return; return 1;
} else { } else {
fprintf(stderr, abandoned); fprintf(stderr, abandoned);
cleanup_exit(0); return 0;
} }
} }

View 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, int verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
char *keystr, char *fingerprint) char *keystr, char *fingerprint,
void (*callback)(void *ctx, int result), void *ctx)
{ {
int ret; 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); ret = verify_host_key(host, port, keytype, keystr);
if (ret == 0) /* success - key matched OK */ if (ret == 0) /* success - key matched OK */
return; return 1;
if (ret == 2) { /* key was different */ if (ret == 2) { /* key was different */
int mbret; int mbret;
mbox.lpszText = dupprintf(wrongmsg, appname, keytype, fingerprint, 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) if (mbret == IDYES)
store_host_key(host, port, keytype, keystr); store_host_key(host, port, keytype, keystr);
if (mbret == IDCANCEL) if (mbret == IDCANCEL)
cleanup_exit(0); return 0;
return 1;
} }
if (ret == 1) { /* key was absent */ if (ret == 1) { /* key was absent */
int mbret; int mbret;
@ -812,7 +814,8 @@ void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
if (mbret == IDYES) if (mbret == IDYES)
store_host_key(host, port, keytype, keystr); store_host_key(host, port, keytype, keystr);
if (mbret == IDCANCEL) 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 * Ask whether the selected algorithm is acceptable (since it was
* below the configured 'warn' threshold). * 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 mbtitle[] = "%s Security Alert";
static const char msg[] = static const char msg[] =
@ -838,9 +842,9 @@ void askalg(void *frontend, const char *algtype, const char *algname)
sfree(message); sfree(message);
sfree(title); sfree(title);
if (mbret == IDYES) if (mbret == IDYES)
return; return 1;
else else
cleanup_exit(0); return 0;
} }
/* /*