X-Git-Url: https://asedeno.scripts.mit.edu/gitweb/?a=blobdiff_plain;f=ssh.c;h=f0a70c9acafca85de4054ff3f142326abf3a3b56;hb=1472aa2a265dfbfe7225729862ea21b5fb2da22f;hp=db31d389763a9326585ce5e7998be703b17b42d1;hpb=ea301bdd9b892a5e70692f82f5c0b98bd585e775;p=PuTTY.git diff --git a/ssh.c b/ssh.c index db31d389..f0a70c9a 100644 --- a/ssh.c +++ b/ssh.c @@ -460,14 +460,14 @@ struct Packet; static struct Packet *ssh1_pkt_init(int pkt_type); static struct Packet *ssh2_pkt_init(int pkt_type); static void ssh_pkt_ensure(struct Packet *, int length); -static void ssh_pkt_adddata(struct Packet *, void *data, int len); +static void ssh_pkt_adddata(struct Packet *, const void *data, int len); static void ssh_pkt_addbyte(struct Packet *, unsigned char value); static void ssh2_pkt_addbool(struct Packet *, unsigned char value); static void ssh_pkt_adduint32(struct Packet *, unsigned long value); static void ssh_pkt_addstring_start(struct Packet *); -static void ssh_pkt_addstring_str(struct Packet *, char *data); -static void ssh_pkt_addstring_data(struct Packet *, char *data, int len); -static void ssh_pkt_addstring(struct Packet *, char *data); +static void ssh_pkt_addstring_str(struct Packet *, const char *data); +static void ssh_pkt_addstring_data(struct Packet *, const char *data, int len); +static void ssh_pkt_addstring(struct Packet *, const char *data); static unsigned char *ssh2_mpint_fmt(Bignum b, int *len); static void ssh1_pkt_addmp(struct Packet *, Bignum b); static void ssh2_pkt_addmp(struct Packet *, Bignum b); @@ -832,6 +832,7 @@ struct ssh_tag { void *cs_comp_ctx, *sc_comp_ctx; const struct ssh_kex *kex; const struct ssh_signkey *hostkey; + char *hostkey_str; /* string representation, for easy checking in rekeys */ unsigned char v2_session_id[SSH2_KEX_MAX_HASH_LEN]; int v2_session_id_len; void *kex_ctx; @@ -1829,7 +1830,7 @@ static void ssh_pkt_ensure(struct Packet *pkt, int length) if (body) pkt->body = pkt->data + offset; } } -static void ssh_pkt_adddata(struct Packet *pkt, void *data, int len) +static void ssh_pkt_adddata(struct Packet *pkt, const void *data, int len) { if (pkt->logmode != PKTLOG_EMIT) { pkt->nblanks++; @@ -1863,17 +1864,18 @@ static void ssh_pkt_addstring_start(struct Packet *pkt) ssh_pkt_adduint32(pkt, 0); pkt->savedpos = pkt->length; } -static void ssh_pkt_addstring_str(struct Packet *pkt, char *data) +static void ssh_pkt_addstring_str(struct Packet *pkt, const char *data) { ssh_pkt_adddata(pkt, data, strlen(data)); PUT_32BIT(pkt->data + pkt->savedpos - 4, pkt->length - pkt->savedpos); } -static void ssh_pkt_addstring_data(struct Packet *pkt, char *data, int len) +static void ssh_pkt_addstring_data(struct Packet *pkt, const char *data, + int len) { ssh_pkt_adddata(pkt, data, len); PUT_32BIT(pkt->data + pkt->savedpos - 4, pkt->length - pkt->savedpos); } -static void ssh_pkt_addstring(struct Packet *pkt, char *data) +static void ssh_pkt_addstring(struct Packet *pkt, const char *data) { ssh_pkt_addstring_start(pkt); ssh_pkt_addstring_str(pkt, data); @@ -5666,12 +5668,28 @@ static void do_ssh2_transport(Ssh ssh, void *vin, int inlen, } } /* List server host key algorithms. */ - ssh2_pkt_addstring_start(s->pktout); - for (i = 0; i < lenof(hostkey_algs); i++) { - ssh2_pkt_addstring_str(s->pktout, hostkey_algs[i]->name); - if (i < lenof(hostkey_algs) - 1) - ssh2_pkt_addstring_str(s->pktout, ","); - } + if (!s->got_session_id) { + /* + * In the first key exchange, we list all the algorithms + * we're prepared to cope with. + */ + ssh2_pkt_addstring_start(s->pktout); + for (i = 0; i < lenof(hostkey_algs); i++) { + ssh2_pkt_addstring_str(s->pktout, hostkey_algs[i]->name); + if (i < lenof(hostkey_algs) - 1) + ssh2_pkt_addstring_str(s->pktout, ","); + } + } else { + /* + * In subsequent key exchanges, we list only the kex + * algorithm that was selected in the first key exchange, + * so that we keep getting the same host key and hence + * don't have to interrupt the user's session to ask for + * reverification. + */ + assert(ssh->kex); + ssh2_pkt_addstring(s->pktout, ssh->hostkey->name); + } /* List encryption algorithms (client->server then server->client). */ for (k = 0; k < 2; k++) { ssh2_pkt_addstring_start(s->pktout); @@ -5909,6 +5927,16 @@ static void do_ssh2_transport(Ssh ssh, void *vin, int inlen, ssh_pkt_getstring(pktin, &str, &len); /* server->client language */ s->ignorepkt = ssh2_pkt_getbool(pktin) && !s->guessok; + ssh->exhash = ssh->kex->hash->init(); + hash_string(ssh->kex->hash, ssh->exhash, ssh->v_c, strlen(ssh->v_c)); + hash_string(ssh->kex->hash, ssh->exhash, ssh->v_s, strlen(ssh->v_s)); + hash_string(ssh->kex->hash, ssh->exhash, + s->our_kexinit, s->our_kexinitlen); + sfree(s->our_kexinit); + if (pktin->length > 5) + hash_string(ssh->kex->hash, ssh->exhash, + pktin->data + 5, pktin->length - 5); + if (s->warn_kex) { ssh_set_frozen(ssh, 1); s->dlgret = askalg(ssh->frontend, "key-exchange algorithm", @@ -5983,16 +6011,6 @@ static void do_ssh2_transport(Ssh ssh, void *vin, int inlen, } } - ssh->exhash = ssh->kex->hash->init(); - hash_string(ssh->kex->hash, ssh->exhash, ssh->v_c, strlen(ssh->v_c)); - hash_string(ssh->kex->hash, ssh->exhash, ssh->v_s, strlen(ssh->v_s)); - hash_string(ssh->kex->hash, ssh->exhash, - s->our_kexinit, s->our_kexinitlen); - sfree(s->our_kexinit); - if (pktin->length > 5) - hash_string(ssh->kex->hash, ssh->exhash, - pktin->data + 5, pktin->length - 5); - if (s->ignorepkt) /* first_kex_packet_follows */ crWaitUntilV(pktin); /* Ignore packet */ } @@ -6223,41 +6241,57 @@ static void do_ssh2_transport(Ssh ssh, void *vin, int inlen, crStopV; } - /* - * Authenticate remote host: verify host key. (We've already - * checked the signature of the exchange hash.) - */ s->keystr = ssh->hostkey->fmtkey(s->hkey); - s->fingerprint = ssh->hostkey->fingerprint(s->hkey); - 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 { - crReturnV; - if (pktin) { - bombout(("Unexpected data from server while waiting" - " for user host key response")); + if (!s->got_session_id) { + /* + * Authenticate remote host: verify host key. (We've already + * checked the signature of the exchange hash.) + */ + s->fingerprint = ssh->hostkey->fingerprint(s->hkey); + 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 { + crReturnV; + if (pktin) { + bombout(("Unexpected data from server while waiting" + " for user host key response")); crStopV; - } - } while (pktin || inlen > 0); - s->dlgret = ssh->user_response; - } - ssh_set_frozen(ssh, 0); - if (s->dlgret == 0) { - ssh_disconnect(ssh, "User aborted at host key verification", NULL, - 0, TRUE); - crStopV; - } - if (!s->got_session_id) { /* don't bother logging this in rekeys */ - logevent("Host key fingerprint is:"); - logevent(s->fingerprint); + } + } while (pktin || inlen > 0); + s->dlgret = ssh->user_response; + } + ssh_set_frozen(ssh, 0); + if (s->dlgret == 0) { + ssh_disconnect(ssh, "User aborted at host key verification", NULL, + 0, TRUE); + crStopV; + } + logevent("Host key fingerprint is:"); + logevent(s->fingerprint); + sfree(s->fingerprint); + /* + * Save this host key, to check against the one presented in + * subsequent rekeys. + */ + ssh->hostkey_str = s->keystr; + } else { + /* + * In a rekey, we never present an interactive host key + * verification request to the user. Instead, we simply + * enforce that the key we're seeing this time is identical to + * the one we saw before. + */ + if (strcmp(ssh->hostkey_str, s->keystr)) { + bombout(("Host key was different in repeat key exchange")); + crStopV; + } + sfree(s->keystr); } - sfree(s->fingerprint); - sfree(s->keystr); ssh->hostkey->freekey(s->hkey); /* @@ -7093,6 +7127,15 @@ static void ssh2_msg_channel_close(Ssh ssh, struct Packet *pktin) break; } + /* + * Abandon any buffered data we still wanted to send to this + * channel. Receiving a CHANNEL_CLOSE is an indication that + * the server really wants to get on and _destroy_ this + * channel, and it isn't going to send us any further + * WINDOW_ADJUSTs to permit us to send pending stuff. + */ + bufchain_clear(&c->v.v2.outbuffer); + /* * Send outgoing EOF. */ @@ -7445,6 +7488,7 @@ static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin) else { c->type = CHAN_AGENT; /* identify channel type */ c->u.a.lensofar = 0; + c->u.a.message = NULL; c->u.a.outstanding_requests = 0; } } else { @@ -7543,7 +7587,9 @@ static void ssh2_setup_x11(struct ssh_channel *c, struct Packet *pktin, ssh2_pkt_adduint32(pktout, ssh->x11disp->screennum); ssh2_pkt_send(ssh, pktout); - crWaitUntilV(pktin); + /* Wait to be called back with either a response packet, or NULL + * meaning clean up and free our data */ + crReturnV; if (pktin) { if (pktin->type == SSH2_MSG_CHANNEL_SUCCESS) { @@ -7573,7 +7619,9 @@ static void ssh2_setup_agent(struct ssh_channel *c, struct Packet *pktin, ssh2_setup_agent, s); ssh2_pkt_send(ssh, pktout); - crWaitUntilV(pktin); + /* Wait to be called back with either a response packet, or NULL + * meaning clean up and free our data */ + crReturnV; if (pktin) { if (pktin->type == SSH2_MSG_CHANNEL_SUCCESS) { @@ -7620,7 +7668,9 @@ static void ssh2_setup_pty(struct ssh_channel *c, struct Packet *pktin, ssh2_pkt_send(ssh, pktout); ssh->state = SSH_STATE_INTERMED; - crWaitUntilV(pktin); + /* Wait to be called back with either a response packet, or NULL + * meaning clean up and free our data */ + crReturnV; if (pktin) { if (pktin->type == SSH2_MSG_CHANNEL_SUCCESS) { @@ -7678,7 +7728,9 @@ static void ssh2_setup_env(struct ssh_channel *c, struct Packet *pktin, s->env_left = s->num_env; while (s->env_left > 0) { - crWaitUntilV(pktin); + /* Wait to be called back with either a response packet, + * or NULL meaning clean up and free our data */ + crReturnV; if (!pktin) goto out; if (pktin->type == SSH2_MSG_CHANNEL_SUCCESS) s->env_ok++; @@ -9625,6 +9677,7 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle, ssh->kex = NULL; ssh->kex_ctx = NULL; ssh->hostkey = NULL; + ssh->hostkey_str = NULL; ssh->exitcode = -1; ssh->close_expected = FALSE; ssh->clean_exit = FALSE; @@ -9802,6 +9855,7 @@ static void ssh_free(void *handle) sfree(ssh->v_c); sfree(ssh->v_s); sfree(ssh->fullhostname); + sfree(ssh->hostkey_str); if (ssh->crcda_ctx) { crcda_free_context(ssh->crcda_ctx); ssh->crcda_ctx = NULL;