X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/4320baf72ee1b9890be0ca6315161d4f4d026cfd..d4963650de01d908eea5792e2a8dd0f72ea9d582:/ssh.c diff --git a/ssh.c b/ssh.c index 417a9320..5493191e 100644 --- a/ssh.c +++ b/ssh.c @@ -130,21 +130,21 @@ static const char *const ssh2_disconnect_reasons[] = { NULL, - "SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT", - "SSH_DISCONNECT_PROTOCOL_ERROR", - "SSH_DISCONNECT_KEY_EXCHANGE_FAILED", - "SSH_DISCONNECT_HOST_AUTHENTICATION_FAILED", - "SSH_DISCONNECT_MAC_ERROR", - "SSH_DISCONNECT_COMPRESSION_ERROR", - "SSH_DISCONNECT_SERVICE_NOT_AVAILABLE", - "SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED", - "SSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE", - "SSH_DISCONNECT_CONNECTION_LOST", - "SSH_DISCONNECT_BY_APPLICATION", - "SSH_DISCONNECT_TOO_MANY_CONNECTIONS", - "SSH_DISCONNECT_AUTH_CANCELLED_BY_USER", - "SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE", - "SSH_DISCONNECT_ILLEGAL_USER_NAME", + "host not allowed to connect", + "protocol error", + "key exchange failed", + "host authentication failed", + "MAC error", + "compression error", + "service not available", + "protocol version not supported", + "host key not verifiable", + "connection lost", + "by application", + "too many connections", + "auth cancelled by user", + "no more auth methods available", + "illegal user name", }; #define SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED 1 /* 0x1 */ @@ -462,10 +462,10 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, const static struct ssh_signkey *hostkey_algs[] = { &ssh_rsa, &ssh_dss }; const static struct ssh_mac *macs[] = { - &ssh_sha1, &ssh_md5 + &ssh_hmac_sha1, &ssh_hmac_sha1_96, &ssh_hmac_md5 }; const static struct ssh_mac *buggymacs[] = { - &ssh_sha1_buggy, &ssh_md5 + &ssh_hmac_sha1_buggy, &ssh_hmac_sha1_96_buggy, &ssh_hmac_md5 }; static void *ssh_comp_none_init(void) @@ -680,7 +680,7 @@ struct ssh_tag { /* the above field _must_ be first in the structure */ char *v_c, *v_s; - SHA_State exhash; + void *exhash; Socket s; @@ -705,7 +705,8 @@ struct ssh_tag { void *cs_comp_ctx, *sc_comp_ctx; const struct ssh_kex *kex; const struct ssh_signkey *hostkey; - unsigned char v2_session_id[20]; + unsigned char v2_session_id[32]; + int v2_session_id_len; void *kex_ctx; char *savedhost; @@ -1562,21 +1563,21 @@ static int ssh_versioncmp(char *a, char *b) /* * Utility routines for putting an SSH-protocol `string' and - * `uint32' into a SHA state. + * `uint32' into a hash state. */ -static void sha_string(SHA_State * s, void *str, int len) +static void hash_string(const struct ssh_hash *h, void *s, void *str, int len) { unsigned char lenblk[4]; PUT_32BIT(lenblk, len); - SHA_Bytes(s, lenblk, 4); - SHA_Bytes(s, str, len); + h->bytes(s, lenblk, 4); + h->bytes(s, str, len); } -static void sha_uint32(SHA_State * s, unsigned i) +static void hash_uint32(const struct ssh_hash *h, void *s, unsigned i) { unsigned char intblk[4]; PUT_32BIT(intblk, i); - SHA_Bytes(s, intblk, 4); + h->bytes(s, intblk, 4); } /* @@ -1586,7 +1587,7 @@ static void ssh_pkt_ensure(struct Packet *pkt, int length) { if (pkt->maxlen < length) { unsigned char *body = pkt->body; - int offset = body ? pkt->data - body : 0; + int offset = body ? body - pkt->data : 0; pkt->maxlen = length + 256; pkt->data = sresize(pkt->data, pkt->maxlen + APIEXTRA, unsigned char); if (body) pkt->body = pkt->data + offset; @@ -1696,10 +1697,10 @@ static struct Packet *ssh1_pkt_init(int pkt_type) static struct Packet *ssh2_pkt_init(int pkt_type) { struct Packet *pkt = ssh_new_packet(); - pkt->length = 5; + pkt->length = 5; /* space for packet length + padding length */ pkt->forcepad = 0; ssh_pkt_addbyte(pkt, (unsigned char) pkt_type); - pkt->body = pkt->data + pkt->length; + pkt->body = pkt->data + pkt->length; /* after packet type */ return pkt; } @@ -1972,12 +1973,12 @@ void bndebug(char *string, Bignum b) } #endif -static void sha_mpint(SHA_State * s, Bignum b) +static void hash_mpint(const struct ssh_hash *h, void *s, Bignum b) { unsigned char *p; int len; p = ssh2_mpint_fmt(b, &len); - sha_string(s, p, len); + hash_string(h, s, p, len); sfree(p); } @@ -3255,7 +3256,8 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, /* Load the public half of ssh->cfg.keyfile so we notice if it's in Pageant */ if (!filename_is_null(ssh->cfg.keyfile)) { if (!rsakey_pubblob(&ssh->cfg.keyfile, - &s->publickey_blob, &s->publickey_bloblen, NULL)) + &s->publickey_blob, &s->publickey_bloblen, + NULL, NULL)) s->publickey_blob = NULL; } else s->publickey_blob = NULL; @@ -4947,26 +4949,26 @@ static int first_in_commasep_string(char *needle, char *haystack, int haylen) /* * SSH-2 key creation method. */ -static void ssh2_mkkey(Ssh ssh, Bignum K, unsigned char *H, - unsigned char *sessid, char chr, +static void ssh2_mkkey(Ssh ssh, Bignum K, unsigned char *H, char chr, unsigned char *keyspace) { - SHA_State s; - /* First 20 bytes. */ - SHA_Init(&s); + const struct ssh_hash *h = ssh->kex->hash; + void *s; + /* First hlen bytes. */ + s = h->init(); if (!(ssh->remote_bugs & BUG_SSH2_DERIVEKEY)) - sha_mpint(&s, K); - SHA_Bytes(&s, H, 20); - SHA_Bytes(&s, &chr, 1); - SHA_Bytes(&s, sessid, 20); - SHA_Final(&s, keyspace); - /* Next 20 bytes. */ - SHA_Init(&s); + hash_mpint(h, s, K); + h->bytes(s, H, h->hlen); + h->bytes(s, &chr, 1); + h->bytes(s, ssh->v2_session_id, ssh->v2_session_id_len); + h->final(s, keyspace); + /* Next hlen bytes. */ + s = h->init(); if (!(ssh->remote_bugs & BUG_SSH2_DERIVEKEY)) - sha_mpint(&s, K); - SHA_Bytes(&s, H, 20); - SHA_Bytes(&s, keyspace, 20); - SHA_Final(&s, keyspace + 20); + hash_mpint(h, s, K); + h->bytes(s, H, h->hlen); + h->bytes(s, keyspace, h->hlen); + h->final(s, keyspace + h->hlen); } /* @@ -4979,6 +4981,8 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, struct do_ssh2_transport_state { int nbits, pbits, warn_kex, warn_cscipher, warn_sccipher; Bignum p, g, e, f, K; + void *our_kexinit; + int our_kexinitlen; int kex_init_value, kex_reply_value; const struct ssh_mac **maclist; int nmacs; @@ -4991,9 +4995,9 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, char *hostkeydata, *sigdata, *keystr, *fingerprint; int hostkeylen, siglen; void *hkey; /* actual host key */ - unsigned char exchange_hash[20]; + unsigned char exchange_hash[32]; int n_preferred_kex; - const struct ssh_kex *preferred_kex[KEX_MAX]; + const struct ssh_kexes *preferred_kex[KEX_MAX]; int n_preferred_ciphers; const struct ssh2_ciphers *preferred_ciphers[CIPHER_MAX]; const struct ssh_compress *preferred_comp; @@ -5116,12 +5120,14 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, ssh2_pkt_addstring_start(s->pktout); commalist_started = 0; for (i = 0; i < s->n_preferred_kex; i++) { - const struct ssh_kex *k = s->preferred_kex[i]; + const struct ssh_kexes *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; + for (j = 0; j < k->nkexes; j++) { + if (commalist_started) + ssh2_pkt_addstring_str(s->pktout, ","); + ssh2_pkt_addstring_str(s->pktout, k->list[j]->name); + commalist_started = 1; + } } /* List server host key algorithms. */ ssh2_pkt_addstring_start(s->pktout); @@ -5202,17 +5208,14 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, ssh2_pkt_adduint32(s->pktout, 0); } - SHA_Init(&ssh->exhash); - sha_string(&ssh->exhash, ssh->v_c, strlen(ssh->v_c)); - sha_string(&ssh->exhash, ssh->v_s, strlen(ssh->v_s)); - sha_string(&ssh->exhash, s->pktout->data + 5, s->pktout->length - 5); + s->our_kexinitlen = s->pktout->length - 5; + s->our_kexinit = snewn(s->our_kexinitlen, unsigned char); + memcpy(s->our_kexinit, s->pktout->data + 5, s->our_kexinitlen); ssh2_pkt_send_noqueue(ssh, s->pktout); if (!pktin) crWaitUntil(pktin); - if (pktin->length > 5) - sha_string(&ssh->exhash, pktin->data + 5, pktin->length - 5); /* * Now examine the other side's KEXINIT to see what we're up @@ -5241,13 +5244,17 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, preferred = NULL; for (i = 0; i < s->n_preferred_kex; i++) { - const struct ssh_kex *k = s->preferred_kex[i]; + const struct ssh_kexes *k = s->preferred_kex[i]; if (!k) { s->warn_kex = TRUE; } else { - if (!preferred) preferred = k->name; - if (in_commasep_string(k->name, str, len)) - ssh->kex = k; + for (j = 0; j < k->nkexes; j++) { + if (!preferred) preferred = k->list[j]->name; + if (in_commasep_string(k->list[j]->name, str, len)) { + ssh->kex = k->list[j]; + break; + } + } } if (ssh->kex) break; @@ -5426,6 +5433,16 @@ static int 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 */ crWaitUntil(pktin); /* Ignore packet */ } @@ -5442,10 +5459,10 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, scbits = s->sccipher_tobe->keylen; s->nbits = (csbits > scbits ? csbits : scbits); } - /* The keys only have 160-bit entropy, since they're based on - * a SHA-1 hash. So cap the key size at 160 bits. */ - if (s->nbits > 160) - s->nbits = 160; + /* The keys only have hlen-bit entropy, since they're based on + * a hash. So cap the key size at hlen bits. */ + if (s->nbits > ssh->kex->hash->hlen * 8) + s->nbits = ssh->kex->hash->hlen * 8; /* * If we're doing Diffie-Hellman group exchange, start by @@ -5517,29 +5534,31 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, * involve user interaction. */ set_busy_status(ssh->frontend, BUSY_NOT); - sha_string(&ssh->exhash, s->hostkeydata, s->hostkeylen); - if (ssh->kex == &ssh_diffiehellman_gex) { - sha_uint32(&ssh->exhash, s->pbits); - sha_mpint(&ssh->exhash, s->p); - sha_mpint(&ssh->exhash, s->g); + hash_string(ssh->kex->hash, ssh->exhash, s->hostkeydata, s->hostkeylen); + if (!ssh->kex->pdata) { + hash_uint32(ssh->kex->hash, ssh->exhash, s->pbits); + hash_mpint(ssh->kex->hash, ssh->exhash, s->p); + hash_mpint(ssh->kex->hash, ssh->exhash, s->g); } - sha_mpint(&ssh->exhash, s->e); - sha_mpint(&ssh->exhash, s->f); - sha_mpint(&ssh->exhash, s->K); - SHA_Final(&ssh->exhash, s->exchange_hash); + hash_mpint(ssh->kex->hash, ssh->exhash, s->e); + hash_mpint(ssh->kex->hash, ssh->exhash, s->f); + hash_mpint(ssh->kex->hash, ssh->exhash, s->K); + assert(ssh->kex->hash->hlen <= sizeof(s->exchange_hash)); + ssh->kex->hash->final(ssh->exhash, s->exchange_hash); dh_cleanup(ssh->kex_ctx); ssh->kex_ctx = NULL; #if 0 debug(("Exchange hash is:\n")); - dmemdump(s->exchange_hash, 20); + dmemdump(s->exchange_hash, ssh->kex->hash->hlen); #endif s->hkey = ssh->hostkey->newkey(s->hostkeydata, s->hostkeylen); if (!s->hkey || !ssh->hostkey->verifysig(s->hkey, s->sigdata, s->siglen, - (char *)s->exchange_hash, 20)) { + (char *)s->exchange_hash, + ssh->kex->hash->hlen)) { bombout(("Server's host key did not match the signature supplied")); crStop(0); } @@ -5587,8 +5606,11 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, * authentication. */ if (!s->got_session_id) { + assert(sizeof(s->exchange_hash) <= sizeof(ssh->v2_session_id)); memcpy(ssh->v2_session_id, s->exchange_hash, sizeof(s->exchange_hash)); + ssh->v2_session_id_len = ssh->kex->hash->hlen; + assert(ssh->v2_session_id_len <= sizeof(ssh->v2_session_id)); s->got_session_id = TRUE; } @@ -5624,11 +5646,11 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, */ { unsigned char keyspace[40]; - ssh2_mkkey(ssh,s->K,s->exchange_hash,ssh->v2_session_id,'C',keyspace); + ssh2_mkkey(ssh,s->K,s->exchange_hash,'C',keyspace); ssh->cscipher->setkey(ssh->cs_cipher_ctx, keyspace); - ssh2_mkkey(ssh,s->K,s->exchange_hash,ssh->v2_session_id,'A',keyspace); + ssh2_mkkey(ssh,s->K,s->exchange_hash,'A',keyspace); ssh->cscipher->setiv(ssh->cs_cipher_ctx, keyspace); - ssh2_mkkey(ssh,s->K,s->exchange_hash,ssh->v2_session_id,'E',keyspace); + ssh2_mkkey(ssh,s->K,s->exchange_hash,'E',keyspace); ssh->csmac->setkey(ssh->cs_mac_ctx, keyspace); } @@ -5682,11 +5704,11 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, */ { unsigned char keyspace[40]; - ssh2_mkkey(ssh,s->K,s->exchange_hash,ssh->v2_session_id,'D',keyspace); + ssh2_mkkey(ssh,s->K,s->exchange_hash,'D',keyspace); ssh->sccipher->setkey(ssh->sc_cipher_ctx, keyspace); - ssh2_mkkey(ssh,s->K,s->exchange_hash,ssh->v2_session_id,'B',keyspace); + ssh2_mkkey(ssh,s->K,s->exchange_hash,'B',keyspace); ssh->sccipher->setiv(ssh->sc_cipher_ctx, keyspace); - ssh2_mkkey(ssh,s->K,s->exchange_hash,ssh->v2_session_id,'F',keyspace); + ssh2_mkkey(ssh,s->K,s->exchange_hash,'F',keyspace); ssh->scmac->setkey(ssh->sc_mac_ctx, keyspace); } logeventf(ssh, "Initialised %.200s server->client encryption", @@ -5702,7 +5724,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, */ freebn(s->f); freebn(s->K); - if (ssh->kex == &ssh_diffiehellman_gex) { + if (!ssh->kex->pdata) { freebn(s->g); freebn(s->p); } @@ -6619,7 +6641,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, if (keytype == SSH_KEYTYPE_SSH2) { s->publickey_blob = ssh2_userkey_loadpub(&ssh->cfg.keyfile, NULL, - &s->publickey_bloblen, NULL); + &s->publickey_bloblen, NULL, NULL); } else { char *msgbuf; logeventf(ssh, "Unable to use this key file (%s)", @@ -6869,7 +6891,8 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, ssh2_pkt_addstring_start(s->pktout); ssh2_pkt_addstring_data(s->pktout, s->pkblob, s->pklen); - s->siglen = s->pktout->length - 5 + 4 + 20; + s->siglen = s->pktout->length - 5 + 4 + + ssh->v2_session_id_len; if (ssh->remote_bugs & BUG_SSH2_PK_SESSIONID) s->siglen -= 4; s->len = 1; /* message type */ @@ -6888,11 +6911,12 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, s->q += 4; /* Now the data to be signed... */ if (!(ssh->remote_bugs & BUG_SSH2_PK_SESSIONID)) { - PUT_32BIT(s->q, 20); + PUT_32BIT(s->q, ssh->v2_session_id_len); s->q += 4; } - memcpy(s->q, ssh->v2_session_id, 20); - s->q += 20; + memcpy(s->q, ssh->v2_session_id, + ssh->v2_session_id_len); + s->q += ssh->v2_session_id_len; memcpy(s->q, s->pktout->data + 5, s->pktout->length - 5); s->q += s->pktout->length - 5; @@ -6959,7 +6983,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, (unsigned char *)ssh2_userkey_loadpub(&ssh->cfg.keyfile, &algorithm, &pub_blob_len, - NULL); + NULL, NULL); if (pub_blob) { s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); ssh2_pkt_addstring(s->pktout, s->username); @@ -7188,16 +7212,19 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * followed by everything so far placed in the * outgoing packet. */ - sigdata_len = s->pktout->length - 5 + 4 + 20; + sigdata_len = s->pktout->length - 5 + 4 + + ssh->v2_session_id_len; if (ssh->remote_bugs & BUG_SSH2_PK_SESSIONID) sigdata_len -= 4; sigdata = snewn(sigdata_len, unsigned char); p = 0; if (!(ssh->remote_bugs & BUG_SSH2_PK_SESSIONID)) { - PUT_32BIT(sigdata+p, 20); + PUT_32BIT(sigdata+p, ssh->v2_session_id_len); p += 4; } - memcpy(sigdata+p, ssh->v2_session_id, 20); p += 20; + memcpy(sigdata+p, ssh->v2_session_id, + ssh->v2_session_id_len); + p += ssh->v2_session_id_len; memcpy(sigdata+p, s->pktout->data + 5, s->pktout->length - 5); p += s->pktout->length - 5;