X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/putty/blobdiff_plain/d38d6a1f7c84cff0c448437676339b3b2ffb091f..d57f70afa40c24426e5f936c86f7640801d43f7a:/ssh.c diff --git a/ssh.c b/ssh.c index 835a8579..63f200be 100644 --- a/ssh.c +++ b/ssh.c @@ -104,7 +104,7 @@ * Packet type contexts, so that ssh2_pkt_type can correctly decode * the ambiguous type numbers back into the correct type strings. */ -#define SSH2_PKTCTX_DHGROUP1 0x0001 +#define SSH2_PKTCTX_DHGROUP 0x0001 #define SSH2_PKTCTX_DHGEX 0x0002 #define SSH2_PKTCTX_PUBLICKEY 0x0010 #define SSH2_PKTCTX_PASSWORD 0x0020 @@ -162,7 +162,7 @@ static const char *const ssh2_disconnect_reasons[] = { #define BUG_CHOKES_ON_RSA 8 #define BUG_SSH2_RSA_PADDING 16 #define BUG_SSH2_DERIVEKEY 32 -#define BUG_SSH2_DH_GEX 64 +/* 64 was BUG_SSH2_DH_GEX, now spare */ #define BUG_SSH2_PK_SESSIONID 128 #define translate(x) if (type == x) return #x @@ -222,8 +222,8 @@ static char *ssh2_pkt_type(int pkt_ctx, int type) translate(SSH2_MSG_SERVICE_ACCEPT); translate(SSH2_MSG_KEXINIT); translate(SSH2_MSG_NEWKEYS); - translatec(SSH2_MSG_KEXDH_INIT, SSH2_PKTCTX_DHGROUP1); - translatec(SSH2_MSG_KEXDH_REPLY, SSH2_PKTCTX_DHGROUP1); + translatec(SSH2_MSG_KEXDH_INIT, SSH2_PKTCTX_DHGROUP); + translatec(SSH2_MSG_KEXDH_REPLY, SSH2_PKTCTX_DHGROUP); translatec(SSH2_MSG_KEX_DH_GEX_REQUEST, SSH2_PKTCTX_DHGEX); translatec(SSH2_MSG_KEX_DH_GEX_GROUP, SSH2_PKTCTX_DHGEX); translatec(SSH2_MSG_KEX_DH_GEX_INIT, SSH2_PKTCTX_DHGEX); @@ -360,11 +360,6 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, #define SSH_MAX_BACKLOG 32768 #define OUR_V2_WINSIZE 16384 -const static struct ssh_kex *kex_algs[] = { - &ssh_diffiehellman_gex, - &ssh_diffiehellman -}; - const static struct ssh_signkey *hostkey_algs[] = { &ssh_rsa, &ssh_dss }; static void *nullmac_make_context(void) @@ -713,13 +708,11 @@ struct ssh_tag { * size-based rekeys. */ unsigned long incoming_data_size, outgoing_data_size, deferred_data_size; + unsigned long max_data_size; int kex_in_progress; long next_rekey; }; -#define MAX_DATA_BEFORE_REKEY (0x40000000UL) -#define REKEY_TIMEOUT (3600 * TICKSPERSEC) - #define logevent(s) logevent(ssh->frontend, s) /* logevent, only printf-formatted. */ @@ -1658,7 +1651,8 @@ static void ssh2_pkt_send_noqueue(Ssh ssh, struct Packet *pkt) ssh->outgoing_data_size += pkt->encrypted_len; if (!ssh->kex_in_progress && - ssh->outgoing_data_size > MAX_DATA_BEFORE_REKEY) + ssh->max_data_size != 0 && + ssh->outgoing_data_size > ssh->max_data_size) do_ssh2_transport(ssh, "Initiating key re-exchange " "(too much data sent)", -1, NULL); @@ -1748,7 +1742,8 @@ static void ssh_pkt_defersend(Ssh ssh) ssh->outgoing_data_size += ssh->deferred_data_size; if (!ssh->kex_in_progress && - ssh->outgoing_data_size > MAX_DATA_BEFORE_REKEY) + ssh->max_data_size != 0 && + ssh->outgoing_data_size > ssh->max_data_size) do_ssh2_transport(ssh, "Initiating key re-exchange " "(too much data sent)", -1, NULL); ssh->deferred_data_size = 0; @@ -2057,14 +2052,6 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring) ssh->remote_bugs |= BUG_SSH2_PK_SESSIONID; logevent("We believe remote version has SSH2 public-key-session-ID bug"); } - - if (ssh->cfg.sshbug_dhgex2 == FORCE_ON) { - /* - * User specified the SSH2 DH GEX bug. - */ - ssh->remote_bugs |= BUG_SSH2_DH_GEX; - logevent("We believe remote version has SSH2 DH group exchange bug"); - } } /* @@ -2149,7 +2136,7 @@ static int do_ssh_init(Ssh ssh, unsigned char c) ssh->rdpkt2_state.incoming_sequence = 0; s->vstring[s->vslen] = 0; - s->vstring[strcspn(s->vstring, "\r\n")] = '\0';/* remove EOL chars */ + s->vstring[strcspn(s->vstring, "\015\012")] = '\0';/* remove EOL chars */ { char *vlog; vlog = snewn(20 + s->vslen, char); @@ -2184,13 +2171,13 @@ static int do_ssh_init(Ssh ssh, unsigned char c) /* * Construct a v2 version string. */ - verstring = dupprintf("SSH-2.0-%s\n", sshver); + verstring = dupprintf("SSH-2.0-%s\015\012", sshver); ssh->version = 2; } else { /* * Construct a v1 version string. */ - verstring = dupprintf("SSH-%s-%s\n", + verstring = dupprintf("SSH-%s-%s\012", (ssh_versioncmp(s->version, "1.5") <= 0 ? s->version : "1.5"), sshver); @@ -2204,8 +2191,10 @@ static int do_ssh_init(Ssh ssh, unsigned char c) * Hash our version string and their version string. */ SHA_Init(&ssh->exhashbase); - sha_string(&ssh->exhashbase, verstring, strlen(verstring)-1); - sha_string(&ssh->exhashbase, s->vstring, strcspn(s->vstring, "\r\n")); + sha_string(&ssh->exhashbase, verstring, + strcspn(verstring, "\015\012")); + sha_string(&ssh->exhashbase, s->vstring, + strcspn(s->vstring, "\015\012")); /* * Initialise SSHv2 protocol. @@ -2222,7 +2211,7 @@ static int do_ssh_init(Ssh ssh, unsigned char c) ssh->s_rdpkt = ssh1_rdpkt; } logeventf(ssh, "We claim version: %.*s", - strlen(verstring)-1, verstring); + strcspn(verstring, "\015\012"), verstring); sk_write(ssh->s, verstring, strlen(verstring)); sfree(verstring); } @@ -2758,7 +2747,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, /* Warn about chosen cipher if necessary. */ if (warn) - askcipher(ssh->frontend, cipher_string, 0); + askalg(ssh->frontend, "cipher", cipher_string); } switch (s->cipher_type) { @@ -4318,6 +4307,8 @@ static int do_ssh2_transport(Ssh ssh, unsigned char *in, int inlen, int hostkeylen, siglen; void *hkey; /* actual host key */ unsigned char exchange_hash[20]; + int n_preferred_kex; + const struct ssh_kex *preferred_kex[KEX_MAX]; int n_preferred_ciphers; const struct ssh2_ciphers *preferred_ciphers[CIPHER_MAX]; const struct ssh_compress *preferred_comp; @@ -4337,6 +4328,37 @@ static int do_ssh2_transport(Ssh ssh, unsigned char *in, int inlen, { int i; /* + * Set up the preferred key exchange. (NULL => warn below here) + */ + s->n_preferred_kex = 0; + for (i = 0; i < KEX_MAX; i++) { + switch (ssh->cfg.ssh_kexlist[i]) { + case KEX_DHGEX: + s->preferred_kex[s->n_preferred_kex++] = + &ssh_diffiehellman_gex; + break; + case KEX_DHGROUP14: + s->preferred_kex[s->n_preferred_kex++] = + &ssh_diffiehellman_group14; + break; + case KEX_DHGROUP1: + s->preferred_kex[s->n_preferred_kex++] = + &ssh_diffiehellman_group1; + break; + case CIPHER_WARN: + /* Flag for later. Don't bother if it's the last in + * the list. */ + if (i < KEX_MAX - 1) { + s->preferred_kex[s->n_preferred_kex++] = NULL; + } + break; + } + } + } + + { + int i; + /* * Set up the preferred ciphers. (NULL => warn below here) */ s->n_preferred_ciphers = 0; @@ -4385,7 +4407,7 @@ static int do_ssh2_transport(Ssh ssh, unsigned char *in, int inlen, begin_key_exchange: { - int i, j, cipherstr_started; + int i, j, commalist_started; /* * Enable queueing of outgoing auth- or connection-layer @@ -4406,13 +4428,14 @@ static int do_ssh2_transport(Ssh ssh, unsigned char *in, int inlen, ssh2_pkt_addbyte(s->pktout, (unsigned char) random_byte()); /* List key exchange algorithms. */ ssh2_pkt_addstring_start(s->pktout); - for (i = 0; i < lenof(kex_algs); i++) { - if (kex_algs[i] == &ssh_diffiehellman_gex && - (ssh->remote_bugs & BUG_SSH2_DH_GEX)) - continue; - ssh2_pkt_addstring_str(s->pktout, kex_algs[i]->name); - if (i < lenof(kex_algs) - 1) + commalist_started = 0; + for (i = 0; i < s->n_preferred_kex; i++) { + const struct ssh_kex *k = s->preferred_kex[i]; + if (!k) continue; /* warning flag */ + if (commalist_started) ssh2_pkt_addstring_str(s->pktout, ","); + ssh2_pkt_addstring_str(s->pktout, s->preferred_kex[i]->name); + commalist_started = 1; } /* List server host key algorithms. */ ssh2_pkt_addstring_start(s->pktout); @@ -4423,28 +4446,28 @@ static int do_ssh2_transport(Ssh ssh, unsigned char *in, int inlen, } /* List client->server encryption algorithms. */ ssh2_pkt_addstring_start(s->pktout); - cipherstr_started = 0; + commalist_started = 0; for (i = 0; i < s->n_preferred_ciphers; i++) { const struct ssh2_ciphers *c = s->preferred_ciphers[i]; if (!c) continue; /* warning flag */ for (j = 0; j < c->nciphers; j++) { - if (cipherstr_started) + if (commalist_started) ssh2_pkt_addstring_str(s->pktout, ","); ssh2_pkt_addstring_str(s->pktout, c->list[j]->name); - cipherstr_started = 1; + commalist_started = 1; } } /* List server->client encryption algorithms. */ ssh2_pkt_addstring_start(s->pktout); - cipherstr_started = 0; + commalist_started = 0; for (i = 0; i < s->n_preferred_ciphers; i++) { const struct ssh2_ciphers *c = s->preferred_ciphers[i]; if (!c) continue; /* warning flag */ for (j = 0; j < c->nciphers; j++) { - if (cipherstr_started) + if (commalist_started) ssh2_pkt_addstring_str(s->pktout, ","); ssh2_pkt_addstring_str(s->pktout, c->list[j]->name); - cipherstr_started = 1; + commalist_started = 1; } } /* List client->server MAC algorithms. */ @@ -4525,15 +4548,26 @@ static int do_ssh2_transport(Ssh ssh, unsigned char *in, int inlen, s->sccomp_tobe = NULL; pktin->savedpos += 16; /* skip garbage cookie */ ssh_pkt_getstring(pktin, &str, &len); /* key exchange algorithms */ - for (i = 0; i < lenof(kex_algs); i++) { - if (kex_algs[i] == &ssh_diffiehellman_gex && - (ssh->remote_bugs & BUG_SSH2_DH_GEX)) - continue; - if (in_commasep_string(kex_algs[i]->name, str, len)) { - ssh->kex = kex_algs[i]; + s->warn = 0; + for (i = 0; i < s->n_preferred_kex; i++) { + const struct ssh_kex *k = s->preferred_kex[i]; + if (!k) { + s->warn = 1; + } else if (in_commasep_string(k->name, str, len)) { + ssh->kex = k; + } + if (ssh->kex) { + if (s->warn) + askalg(ssh->frontend, "key-exchange algorithm", + ssh->kex->name); break; } } + if (!ssh->kex) { + bombout(("Couldn't agree a key exchange algorithm (available: %s)", + str ? str : "(null)")); + crStop(0); + } ssh_pkt_getstring(pktin, &str, &len); /* host key algorithms */ for (i = 0; i < lenof(hostkey_algs); i++) { if (in_commasep_string(hostkey_algs[i]->name, str, len)) { @@ -4557,7 +4591,8 @@ static int do_ssh2_transport(Ssh ssh, unsigned char *in, int inlen, } if (s->cscipher_tobe) { if (s->warn) - askcipher(ssh->frontend, s->cscipher_tobe->name, 1); + askalg(ssh->frontend, "client-to-server cipher", + s->cscipher_tobe->name); break; } } @@ -4583,7 +4618,8 @@ static int do_ssh2_transport(Ssh ssh, unsigned char *in, int inlen, } if (s->sccipher_tobe) { if (s->warn) - askcipher(ssh->frontend, s->sccipher_tobe->name, 2); + askalg(ssh->frontend, "server-to-client cipher", + s->sccipher_tobe->name); break; } } @@ -4648,7 +4684,7 @@ static int do_ssh2_transport(Ssh ssh, unsigned char *in, int inlen, * If we're doing Diffie-Hellman group exchange, start by * requesting a group. */ - if (ssh->kex == &ssh_diffiehellman_gex) { + if (!ssh->kex->pdata) { logevent("Doing Diffie-Hellman group exchange"); ssh->pkt_ctx |= SSH2_PKTCTX_DHGEX; /* @@ -4671,14 +4707,16 @@ static int do_ssh2_transport(Ssh ssh, unsigned char *in, int inlen, bombout(("unable to read mp-ints from incoming group packet")); crStop(0); } - ssh->kex_ctx = dh_setup_group(s->p, s->g); + ssh->kex_ctx = dh_setup_gex(s->p, s->g); s->kex_init_value = SSH2_MSG_KEX_DH_GEX_INIT; s->kex_reply_value = SSH2_MSG_KEX_DH_GEX_REPLY; } else { - ssh->pkt_ctx |= SSH2_PKTCTX_DHGROUP1; - ssh->kex_ctx = dh_setup_group1(); + ssh->pkt_ctx |= SSH2_PKTCTX_DHGROUP; + ssh->kex_ctx = dh_setup_group(ssh->kex); s->kex_init_value = SSH2_MSG_KEXDH_INIT; s->kex_reply_value = SSH2_MSG_KEXDH_REPLY; + logeventf(ssh, "Using Diffie-Hellman with standard group \"%s\"", + ssh->kex->groupname); } logevent("Doing Diffie-Hellman key exchange"); @@ -4877,8 +4915,10 @@ static int do_ssh2_transport(Ssh ssh, unsigned char *in, int inlen, * Key exchange is over. Schedule a timer for our next rekey. */ ssh->kex_in_progress = FALSE; - ssh->next_rekey = schedule_timer(REKEY_TIMEOUT, ssh2_timer, ssh); - + if (ssh->cfg.ssh_rekey_time != 0) + ssh->next_rekey = schedule_timer(ssh->cfg.ssh_rekey_time*60*TICKSPERSEC, + ssh2_timer, ssh); + /* * If this is the first key exchange phase, we must pass the * SSH2_MSG_NEWKEYS packet to the next layer, not because it @@ -7049,7 +7089,8 @@ static void ssh2_protocol(Ssh ssh, unsigned char *in, int inlen, if (pktin) { ssh->incoming_data_size += pktin->encrypted_len; if (!ssh->kex_in_progress && - ssh->incoming_data_size > MAX_DATA_BEFORE_REKEY) + ssh->max_data_size != 0 && + ssh->incoming_data_size > ssh->max_data_size) do_ssh2_transport(ssh, "Initiating key re-exchange " "(too much data received)", -1, NULL); } @@ -7171,6 +7212,7 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle, ssh->incoming_data_size = ssh->outgoing_data_size = ssh->deferred_data_size = 0L; + ssh->max_data_size = parse_blocksize(ssh->cfg.ssh_rekey_data); ssh->kex_in_progress = FALSE; p = connect_to_host(ssh, host, port, realhost, nodelay, keepalive); @@ -7233,12 +7275,14 @@ static void ssh_free(void *handle) sfree(c); } freetree234(ssh->channels); + ssh->channels = NULL; } if (ssh->rportfwds) { while ((pf = delpos234(ssh->rportfwds, 0)) != NULL) sfree(pf); freetree234(ssh->rportfwds); + ssh->rportfwds = NULL; } sfree(ssh->deferred_send_data); if (ssh->x11auth) @@ -7254,9 +7298,9 @@ static void ssh_free(void *handle) if (ssh->s) ssh_do_close(ssh); expire_timer_context(ssh); - sfree(ssh); if (ssh->pinger) pinger_free(ssh->pinger); + sfree(ssh); random_unref(); } @@ -7372,14 +7416,17 @@ static void ssh_size(void *handle, int width, int height) */ static const struct telnet_special *ssh_get_specials(void *handle) { - static const struct telnet_special ignore_special[] = { + static const struct telnet_special ssh1_ignore_special[] = { + {"IGNORE message", TS_NOP} + }; + static const struct telnet_special ssh2_transport_specials[] = { {"IGNORE message", TS_NOP}, {"Repeat key exchange", TS_REKEY}, }; static const struct telnet_special ssh2_session_specials[] = { {NULL, TS_SEP}, {"Break", TS_BRK}, - /* These are the signal names defined by draft-ietf-secsh-connect-19. + /* These are the signal names defined by draft-ietf-secsh-connect-23. * They include all the ISO C signals, but are a subset of the POSIX * required signals. */ {"SIGINT (Interrupt)", TS_SIGINT}, @@ -7397,7 +7444,8 @@ static const struct telnet_special *ssh_get_specials(void *handle) static const struct telnet_special specials_end[] = { {NULL, TS_EXITMENU} }; - static struct telnet_special ssh_specials[lenof(ignore_special) + + /* XXX review this length for any changes: */ + static struct telnet_special ssh_specials[lenof(ssh2_transport_specials) + lenof(ssh2_session_specials) + lenof(specials_end)]; Ssh ssh = (Ssh) handle; @@ -7414,9 +7462,9 @@ static const struct telnet_special *ssh_get_specials(void *handle) * won't cope with it, since we wouldn't bother sending it if * asked anyway. */ if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE)) - ADD_SPECIALS(ignore_special); + ADD_SPECIALS(ssh1_ignore_special); } else if (ssh->version == 2) { - ADD_SPECIALS(ignore_special); + ADD_SPECIALS(ssh2_transport_specials); if (ssh->mainchan) ADD_SPECIALS(ssh2_session_specials); } /* else we're not ready yet */