X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/b185170a126d52767496ca94e6df0ba68e7b8902..d318ef8e04c5093c0a027a42ae0da3f6df79d368:/ssh.c diff --git a/ssh.c b/ssh.c index a842c4d0..a7f6f882 100644 --- a/ssh.c +++ b/ssh.c @@ -20,7 +20,7 @@ fprintf(stderr, "%s\n", s); } #define bombout(msg) ( ssh_state = SSH_STATE_CLOSED, \ - (s ? sk_close(s), s = NULL : (void)0), \ + (s ? sk_close(s), s = NULL : 0), \ connection_fatal msg ) #define SSH1_MSG_DISCONNECT 1 /* 0x1 */ @@ -87,6 +87,10 @@ #define SSH2_MSG_NEWKEYS 21 /* 0x15 */ #define SSH2_MSG_KEXDH_INIT 30 /* 0x1e */ #define SSH2_MSG_KEXDH_REPLY 31 /* 0x1f */ +#define SSH2_MSG_KEX_DH_GEX_REQUEST 30 /* 0x1e */ +#define SSH2_MSG_KEX_DH_GEX_GROUP 31 /* 0x1f */ +#define SSH2_MSG_KEX_DH_GEX_INIT 32 /* 0x20 */ +#define SSH2_MSG_KEX_DH_GEX_REPLY 33 /* 0x21 */ #define SSH2_MSG_USERAUTH_REQUEST 50 /* 0x32 */ #define SSH2_MSG_USERAUTH_FAILURE 51 /* 0x33 */ #define SSH2_MSG_USERAUTH_SUCCESS 52 /* 0x34 */ @@ -180,7 +184,12 @@ extern void x11_invent_auth(char *, int, char *, int); const static struct ssh_cipher *ciphers[] = { &ssh_blowfish_ssh2, &ssh_3des_ssh2 }; extern const struct ssh_kex ssh_diffiehellman; -const static struct ssh_kex *kex_algs[] = { &ssh_diffiehellman }; +extern const struct ssh_kex ssh_diffiehellman_gex; +const static struct ssh_kex *kex_algs[] = { +#ifdef DO_DIFFIE_HELLMAN_GEX + &ssh_diffiehellman_gex, +#endif + &ssh_diffiehellman }; extern const struct ssh_signkey ssh_dss; const static struct ssh_signkey *hostkey_algs[] = { &ssh_dss }; @@ -806,8 +815,8 @@ static int ssh_versioncmp(char *a, char *b) { /* - * Utility routine for putting an SSH-protocol `string' into a SHA - * state. + * Utility routines for putting an SSH-protocol `string' and + * `uint32' into a SHA state. */ #include static void sha_string(SHA_State *s, void *str, int len) { @@ -817,6 +826,12 @@ static void sha_string(SHA_State *s, void *str, int len) { SHA_Bytes(s, str, len); } +static void sha_uint32(SHA_State *s, unsigned i) { + unsigned char intblk[4]; + PUT_32BIT(intblk, i); + SHA_Bytes(s, intblk, 4); +} + /* * SSH2 packet construction functions. */ @@ -869,20 +884,18 @@ static void ssh2_pkt_addstring(char *data) { } static char *ssh2_mpint_fmt(Bignum b, int *len) { unsigned char *p; - int i, n = b[0]; - p = smalloc(n * 2 + 1); + int i, n = (ssh1_bignum_bitcount(b)+7)/8; + p = smalloc(n + 1); if (!p) fatalbox("out of memory"); p[0] = 0; - for (i = 0; i < n; i++) { - p[i*2+1] = (b[n-i] >> 8) & 0xFF; - p[i*2+2] = (b[n-i] ) & 0xFF; - } + for (i = 1; i <= n; i++) + p[i] = bignum_byte(b, n-i); i = 0; - while (p[i] == 0 && (p[i+1] & 0x80) == 0) + while (i <= n && p[i] == 0 && (p[i+1] & 0x80) == 0) i++; - memmove(p, p+i, n*2+1-i); - *len = n*2+1-i; + memmove(p, p+i, n+1-i); + *len = n+1-i; return p; } static void ssh2_pkt_addmp(Bignum b) { @@ -1041,7 +1054,7 @@ static void ssh2_pkt_getstring(char **p, int *length) { } static Bignum ssh2_pkt_getmp(void) { char *p; - int i, j, length; + int length; Bignum b; ssh2_pkt_getstring(&p, &length); @@ -1051,15 +1064,7 @@ static Bignum ssh2_pkt_getmp(void) { bombout(("internal error: Can't handle negative mpints")); return NULL; } - b = newbn((length+1)/2); - for (i = 0; i < length; i++) { - j = length - 1 - i; - if (j & 1) - b[j/2+1] |= ((unsigned char)p[i]) << 8; - else - b[j/2+1] |= ((unsigned char)p[i]); - } - while (b[0] > 1 && b[b[0]] == 0) b[0]--; + b = bignum_from_bytes(p, length); return b; } @@ -1753,9 +1758,8 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt) response = rsadecrypt(challenge, &pubkey); freebn(pubkey.private_exponent); /* burn the evidence */ - for (i = 0; i < 32; i += 2) { - buffer[i] = response[16-i/2] >> 8; - buffer[i+1] = response[16-i/2] & 0xFF; + for (i = 0; i < 32; i++) { + buffer[i] = bignum_byte(response, 31-i); } MD5Init(&md5c); @@ -2168,9 +2172,10 @@ static void ssh2_mkkey(Bignum K, char *H, char *sessid, char chr, char *keyspace */ static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt) { - static int i, len; + static int i, len, nbits; static char *str; - static Bignum e, f, K; + static Bignum p, g, e, f, K; + static int kex_init_value, kex_reply_value; static const struct ssh_mac **maclist; static int nmacs; static const struct ssh_cipher *cscipher_tobe = NULL; @@ -2377,25 +2382,60 @@ static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt) } /* - * Currently we only support Diffie-Hellman and DSS, so let's - * bomb out if those aren't selected. + * If we're doing Diffie-Hellman group exchange, start by + * requesting a group. */ - if (kex != &ssh_diffiehellman || hostkey != &ssh_dss) { - bombout(("internal fault: chaos in SSH 2 transport layer")); - crReturn(0); + if (kex == &ssh_diffiehellman_gex) { + int csbits, scbits; + + logevent("Doing Diffie-Hellman group exchange"); + /* + * Work out number of bits. We start with the maximum key + * length of either cipher... + */ + csbits = cscipher_tobe->keylen; + scbits = sccipher_tobe->keylen; + 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 (nbits > 160) nbits = 160; + /* + * ... and then work out how big a DH group we will need to + * allow that much data. + */ + nbits = 512 << ((nbits-1) / 64); + ssh2_pkt_init(SSH2_MSG_KEX_DH_GEX_REQUEST); + ssh2_pkt_adduint32(nbits); + ssh2_pkt_send(); + + crWaitUntil(ispkt); + if (pktin.type != SSH2_MSG_KEX_DH_GEX_GROUP) { + bombout(("expected key exchange group packet from server")); + crReturn(0); + } + p = ssh2_pkt_getmp(); + g = ssh2_pkt_getmp(); + dh_setup_group(p, g); + kex_init_value = SSH2_MSG_KEX_DH_GEX_INIT; + kex_reply_value = SSH2_MSG_KEX_DH_GEX_REPLY; + } else { + dh_setup_group1(); + kex_init_value = SSH2_MSG_KEXDH_INIT; + kex_reply_value = SSH2_MSG_KEXDH_REPLY; } + logevent("Doing Diffie-Hellman key exchange"); /* - * Now we begin the fun. Generate and send e for Diffie-Hellman. + * Now generate and send e for Diffie-Hellman. */ e = dh_create_e(); - ssh2_pkt_init(SSH2_MSG_KEXDH_INIT); + ssh2_pkt_init(kex_init_value); ssh2_pkt_addmp(e); ssh2_pkt_send(); crWaitUntil(ispkt); - if (pktin.type != SSH2_MSG_KEXDH_REPLY) { - bombout(("expected key exchange packet from server")); + if (pktin.type != kex_reply_value) { + bombout(("expected key exchange reply packet from server")); crReturn(0); } ssh2_pkt_getstring(&hostkeydata, &hostkeylen); @@ -2405,11 +2445,18 @@ static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt) K = dh_find_K(f); sha_string(&exhash, hostkeydata, hostkeylen); + if (kex == &ssh_diffiehellman_gex) { + sha_uint32(&exhash, nbits); + sha_mpint(&exhash, p); + sha_mpint(&exhash, g); + } sha_mpint(&exhash, e); sha_mpint(&exhash, f); sha_mpint(&exhash, K); SHA_Final(&exhash, exchange_hash); + dh_cleanup(); + #if 0 debug(("Exchange hash is:\r\n")); for (i = 0; i < 20; i++) @@ -2440,7 +2487,7 @@ static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt) fingerprint = hostkey->fingerprint(hkey); verify_ssh_host_key(savedhost, savedport, hostkey->keytype, keystr, fingerprint); - if (first_kex) { /* don't bother logging this in rekeys */ + if (first_kex) { /* don't bother logging this in rekeys */ logevent("Host key fingerprint is:"); logevent(fingerprint); } @@ -2858,7 +2905,11 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt) */ ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST); ssh2_pkt_adduint32(mainchan->remoteid); /* recipient channel */ - if (*cfg.remote_cmd) { + if (cfg.ssh_subsys) { + ssh2_pkt_addstring("subsystem"); + ssh2_pkt_addbool(1); /* want reply */ + ssh2_pkt_addstring(cfg.remote_cmd); + } else if (*cfg.remote_cmd) { ssh2_pkt_addstring("exec"); ssh2_pkt_addbool(1); /* want reply */ ssh2_pkt_addstring(cfg.remote_cmd);