X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/824e9f94230b6979e64a4e4fb8e4f535e641a844..37d868ecb7ff6ba8aad4b8325c016c0a045f6bb3:/ssh.c diff --git a/ssh.c b/ssh.c index 8d310e1b..b3be8e46 100644 --- a/ssh.c +++ b/ssh.c @@ -15,9 +15,12 @@ #define TRUE 1 #endif +/* uncomment this for packet level debugging */ +/* #define DUMP_PACKETS */ + #define logevent(s) { logevent(s); \ if ((flags & FLAG_STDERR) && (flags & FLAG_VERBOSE)) \ - fprintf(stderr, "%s\n", s); } + { fprintf(stderr, "%s\n", s); fflush(stderr); } } #define bombout(msg) ( ssh_state = SSH_STATE_CLOSED, \ (s ? sk_close(s), s = NULL : 0), \ @@ -118,6 +121,29 @@ #define SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE 9 /* 0x9 */ #define SSH2_DISCONNECT_CONNECTION_LOST 10 /* 0xa */ #define SSH2_DISCONNECT_BY_APPLICATION 11 /* 0xb */ +#define SSH2_DISCONNECT_TOO_MANY_CONNECTIONS 12 /* 0xc */ +#define SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER 13 /* 0xd */ +#define SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE 14 /* 0xe */ +#define SSH2_DISCONNECT_ILLEGAL_USER_NAME 15 /* 0xf */ + +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", +}; #define SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED 1 /* 0x1 */ #define SSH2_OPEN_CONNECT_FAILED 2 /* 0x2 */ @@ -342,6 +368,42 @@ static int ssh_channelfind(void *av, void *bv) { return 0; } +static int alloc_channel_id(void) { + const unsigned CHANNEL_NUMBER_OFFSET = 256; + unsigned low, high, mid; + int tsize; + struct ssh_channel *c; + + /* + * First-fit allocation of channel numbers: always pick the + * lowest unused one. To do this, binary-search using the + * counted B-tree to find the largest channel ID which is in a + * contiguous sequence from the beginning. (Precisely + * everything in that sequence must have ID equal to its tree + * index plus CHANNEL_NUMBER_OFFSET.) + */ + tsize = count234(ssh_channels); + + low = -1; high = tsize; + while (high - low > 1) { + mid = (high + low) / 2; + c = index234(ssh_channels, mid); + if (c->localid == mid + CHANNEL_NUMBER_OFFSET) + low = mid; /* this one is fine */ + else + high = mid; /* this one is past it */ + } + /* + * Now low points to either -1, or the tree index of the + * largest ID in the initial sequence. + */ + { + unsigned i = low + 1 + CHANNEL_NUMBER_OFFSET; + assert(NULL == find234(ssh_channels, &i, ssh_channelfind)); + } + return low + 1 + CHANNEL_NUMBER_OFFSET; +} + static void c_write (char *buf, int len) { if ((flags & FLAG_STDERR)) { int i; @@ -428,11 +490,9 @@ next_packet: if (cipher) cipher->decrypt(pktin.data, st->biglen); -#if 0 - debug(("Got packet len=%d pad=%d\r\n", st->len, st->pad)); - for (st->i = 0; st->i < st->biglen; st->i++) - debug((" %02x", (unsigned char)pktin.data[st->i])); - debug(("\r\n")); +#ifdef DUMP_PACKETS + debug(("Got packet len=%d pad=%d\n", st->len, st->pad)); + dmemdump(pktin.data, st->biglen); #endif st->realcrc = crc32(pktin.data, st->biglen-4); @@ -447,14 +507,9 @@ next_packet: if (ssh1_compressing) { unsigned char *decompblk; int decomplen; -#if 0 - { - int i; - debug(("Packet payload pre-decompression:\n")); - for (i = -1; i < pktin.length; i++) - debug((" %02x", (unsigned char)pktin.body[i])); - debug(("\r\n")); - } +#ifdef DUMP_PACKETS + debug(("Packet payload pre-decompression:\n")); + dmemdump(pktin.body-1, pktin.length+1); #endif zlib_decompress_block(pktin.body-1, pktin.length+1, &decompblk, &decomplen); @@ -470,14 +525,9 @@ next_packet: memcpy(pktin.body-1, decompblk, decomplen); sfree(decompblk); pktin.length = decomplen-1; -#if 0 - { - int i; - debug(("Packet payload post-decompression:\n")); - for (i = -1; i < pktin.length; i++) - debug((" %02x", (unsigned char)pktin.body[i])); - debug(("\r\n")); - } +#ifdef DUMP_PACKETS + debug(("Packet payload post-decompression:\n")); + dmemdump(pktin.body-1, pktin.length+1); #endif } @@ -510,6 +560,20 @@ next_packet: goto next_packet; } + if (pktin.type == SSH1_MSG_DISCONNECT) { + /* log reason code in disconnect message */ + char buf[256]; + unsigned msglen = GET_32BIT(pktin.body); + unsigned nowlen; + strcpy(buf, "Remote sent disconnect: "); + nowlen = strlen(buf); + if (msglen > sizeof(buf)-nowlen-1) + msglen = sizeof(buf)-nowlen-1; + memcpy(buf+nowlen, pktin.body+4, msglen); + buf[nowlen+msglen] = '\0'; + logevent(buf); + } + crFinish(0); } @@ -599,11 +663,9 @@ next_packet: sccipher->decrypt(pktin.data + st->cipherblk, st->packetlen - st->cipherblk); -#if 0 - debug(("Got packet len=%d pad=%d\r\n", st->len, st->pad)); - for (st->i = 0; st->i < st->packetlen; st->i++) - debug((" %02x", (unsigned char)pktin.data[st->i])); - debug(("\r\n")); +#ifdef DUMP_PACKETS + debug(("Got packet len=%d pad=%d\n", st->len, st->pad)); + dmemdump(pktin.data, st->packetlen); #endif /* @@ -632,11 +694,9 @@ next_packet: } pktin.length = 5 + newlen; memcpy(pktin.data+5, newpayload, newlen); -#if 0 - debug(("Post-decompression payload:\r\n")); - for (st->i = 0; st->i < newlen; st->i++) - debug((" %02x", (unsigned char)pktin.data[5+st->i])); - debug(("\r\n")); +#ifdef DUMP_PACKETS + debug(("Post-decompression payload:\n")); + dmemdump(pktin.data+5, newlen); #endif sfree(newpayload); @@ -649,6 +709,28 @@ next_packet: if (pktin.type == SSH2_MSG_IGNORE || pktin.type == SSH2_MSG_DEBUG) goto next_packet; /* FIXME: print DEBUG message */ + if (pktin.type == SSH2_MSG_DISCONNECT) { + /* log reason code in disconnect message */ + char buf[256]; + int reason = GET_32BIT(pktin.data+6); + unsigned msglen = GET_32BIT(pktin.data+10); + unsigned nowlen; + if (reason > 0 && reason < lenof(ssh2_disconnect_reasons)) { + sprintf(buf, "Received disconnect message (%s)", + ssh2_disconnect_reasons[reason]); + } else { + sprintf(buf, "Received disconnect message (unknown type %d)", reason); + } + logevent(buf); + strcpy(buf, "Disconnection message text: "); + nowlen = strlen(buf); + if (msglen > sizeof(buf)-nowlen-1) + msglen = sizeof(buf)-nowlen-1; + memcpy(buf+nowlen, pktin.data+14, msglen); + buf[nowlen+msglen] = '\0'; + logevent(buf); + } + crFinish(0); } @@ -688,11 +770,9 @@ static int s_wrpkt_prepare(void) { pktout.body[-1] = pktout.type; -#if 0 +#ifdef DUMP_PACKETS debug(("Packet payload pre-compression:\n")); - for (i = -1; i < pktout.length; i++) - debug((" %02x", (unsigned char)pktout.body[i])); - debug(("\r\n")); + dmemdump(pktout.body-1, pktout.length+1); #endif if (ssh1_compressing) { @@ -703,11 +783,9 @@ static int s_wrpkt_prepare(void) { ssh1_pktout_size(complen-1); memcpy(pktout.body-1, compblk, complen); sfree(compblk); -#if 0 +#ifdef DUMP_PACKETS debug(("Packet payload post-compression:\n")); - for (i = -1; i < pktout.length; i++) - debug((" %02x", (unsigned char)pktout.body[i])); - debug(("\r\n")); + dmemdump(pktout.body-1, pktout.length+1); #endif } @@ -721,11 +799,9 @@ static int s_wrpkt_prepare(void) { PUT_32BIT(pktout.data+biglen, crc); PUT_32BIT(pktout.data, len); -#if 0 - debug(("Sending packet len=%d\r\n", biglen+4)); - for (i = 0; i < biglen+4; i++) - debug((" %02x", (unsigned char)pktout.data[i])); - debug(("\r\n")); +#ifdef DUMP_PACKETS + debug(("Sending packet len=%d\n", biglen+4)); + dmemdump(pktout.data, biglen+4); #endif if (cipher) cipher->encrypt(pktout.data+4, biglen); @@ -928,7 +1004,7 @@ static void ssh2_pkt_addstring(char *data) { } static char *ssh2_mpint_fmt(Bignum b, int *len) { unsigned char *p; - int i, n = (ssh1_bignum_bitcount(b)+7)/8; + int i, n = (bignum_bitcount(b)+7)/8; p = smalloc(n + 1); if (!p) fatalbox("out of memory"); @@ -963,11 +1039,9 @@ static int ssh2_pkt_construct(void) { /* * Compress packet payload. */ -#if 0 - debug(("Pre-compression payload:\r\n")); - for (i = 5; i < pktout.length; i++) - debug((" %02x", (unsigned char)pktout.data[i])); - debug(("\r\n")); +#ifdef DUMP_PACKETS + debug(("Pre-compression payload:\n")); + dmemdump(pktout.data+5, pktout.length-5); #endif { unsigned char *newpayload; @@ -999,11 +1073,9 @@ static int ssh2_pkt_construct(void) { outgoing_sequence); outgoing_sequence++; /* whether or not we MACed */ -#if 0 - debug(("Sending packet len=%d\r\n", pktout.length+padding)); - for (i = 0; i < pktout.length+padding; i++) - debug((" %02x", (unsigned char)pktout.data[i])); - debug(("\r\n")); +#ifdef DUMP_PACKETS + debug(("Sending packet len=%d\n", pktout.length+padding)); + dmemdump(pktout.data, pktout.length+padding); #endif if (cscipher) @@ -1061,7 +1133,7 @@ void bndebug(char *string, Bignum b) { debug(("%s", string)); for (i = 0; i < len; i++) debug((" %02x", p[i])); - debug(("\r\n")); + debug(("\n")); sfree(p); } #endif @@ -1510,6 +1582,11 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt) if ((supported_ciphers_mask & (1 << cipher_type)) == 0) { c_write_str("Selected cipher not supported, falling back to 3DES\r\n"); cipher_type = SSH_CIPHER_3DES; + if ((supported_ciphers_mask & (1 << cipher_type)) == 0) { + bombout(("Server violates SSH 1 protocol by " + "not supporting 3DES encryption")); + crReturn(0); + } } switch (cipher_type) { case SSH_CIPHER_3DES: logevent("Using 3DES encryption"); break; @@ -1689,7 +1766,7 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt) PUT_32BIT(agentreq, len); q = agentreq + 4; *q++ = SSH1_AGENTC_RSA_CHALLENGE; - PUT_32BIT(q, ssh1_bignum_bitcount(key.modulus)); + PUT_32BIT(q, bignum_bitcount(key.modulus)); q += 4; q += ssh1_write_bignum(q, key.exponent); q += ssh1_write_bignum(q, key.modulus); @@ -2170,9 +2247,7 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt) { } else if (pktin.type == SSH1_SMSG_X11_OPEN) { /* Remote side is trying to open a channel to talk to our * X-Server. Give them back a local channel number. */ - unsigned i; - struct ssh_channel *c, *d; - enum234 e; + struct ssh_channel *c; logevent("Received X11 connect request"); /* Refuse if X11 forwarding is disabled. */ @@ -2192,13 +2267,8 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt) { PKT_END); } else { logevent("opening X11 forward connection succeeded"); - for (i=1, d = first234(ssh_channels, &e); d; d = next234(&e)) { - if (d->localid > i) - break; /* found a free number */ - i = d->localid + 1; - } c->remoteid = GET_32BIT(pktin.body); - c->localid = i; + c->localid = alloc_channel_id(); c->closes = 0; c->type = CHAN_X11; /* identify channel type */ add234(ssh_channels, c); @@ -2211,9 +2281,7 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt) { } else if (pktin.type == SSH1_SMSG_AGENT_OPEN) { /* Remote side is trying to open a channel to talk to our * agent. Give them back a local channel number. */ - unsigned i; struct ssh_channel *c; - enum234 e; /* Refuse if agent forwarding is disabled. */ if (!ssh_agentfwd_enabled) { @@ -2221,15 +2289,9 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt) { PKT_INT, GET_32BIT(pktin.body), PKT_END); } else { - i = 1; - for (c = first234(ssh_channels, &e); c; c = next234(&e)) { - if (c->localid > i) - break; /* found a free number */ - i = c->localid + 1; - } c = smalloc(sizeof(struct ssh_channel)); c->remoteid = GET_32BIT(pktin.body); - c->localid = i; + c->localid = alloc_channel_id(); c->closes = 0; c->type = CHAN_AGENT; /* identify channel type */ c->u.a.lensofar = 0; @@ -2697,10 +2759,8 @@ static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt) dh_cleanup(); #if 0 - debug(("Exchange hash is:\r\n")); - for (i = 0; i < 20; i++) - debug((" %02x", exchange_hash[i])); - debug(("\r\n")); + debug(("Exchange hash is:\n")); + dmemdump(exchange_hash, 20); #endif hkey = hostkey->newkey(hostkeydata, hostkeylen); @@ -3017,6 +3077,7 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt) if (pktin.type != SSH2_MSG_USERAUTH_FAILURE) { bombout(("Strange packet received during authentication: type %d", pktin.type)); + crReturnV; } gotit = FALSE; @@ -3078,7 +3139,7 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt) method = 0; - if (!method && can_pubkey && agent_exists && !tried_agent) { + if (!method && can_pubkey && agent_exists() && !tried_agent) { /* * Attempt public-key authentication using Pageant. */ @@ -3447,8 +3508,9 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt) /* * So now create a channel with a session in it. */ + ssh_channels = newtree234(ssh_channelcmp); mainchan = smalloc(sizeof(struct ssh_channel)); - mainchan->localid = 100; /* as good as any */ + mainchan->localid = alloc_channel_id(); ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN); ssh2_pkt_addstring("session"); ssh2_pkt_adduint32(mainchan->localid); @@ -3472,7 +3534,6 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt) mainchan->v2.remmaxpkt = ssh2_pkt_getuint32(); mainchan->v2.outbuffer = NULL; mainchan->v2.outbuflen = mainchan->v2.outbufsize = 0; - ssh_channels = newtree234(ssh_channelcmp); add234(ssh_channels, mainchan); logevent("Opened channel for session"); @@ -3751,7 +3812,6 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt) } else if (pktin.type == SSH2_MSG_CHANNEL_CLOSE) { unsigned i = ssh2_pkt_getuint32(); struct ssh_channel *c; - enum234 e; c = find234(ssh_channels, &i, ssh_channelfind); if (!c) @@ -3777,8 +3837,7 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt) /* * See if that was the last channel left open. */ - c = first234(ssh_channels, &e); - if (!c) { + if (count234(ssh_channels) == 0) { logevent("All channels closed. Disconnecting"); ssh2_pkt_init(SSH2_MSG_DISCONNECT); ssh2_pkt_adduint32(SSH2_DISCONNECT_BY_APPLICATION); @@ -3835,17 +3894,7 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt) ssh2_pkt_send(); sfree(c); } else { - struct ssh_channel *d; - unsigned i; - enum234 e; - - for (i=1, d = first234(ssh_channels, &e); d; - d = next234(&e)) { - if (d->localid > i) - break; /* found a free number */ - i = d->localid + 1; - } - c->localid = i; + c->localid = alloc_channel_id(); c->closes = 0; c->v2.remwindow = ssh2_pkt_getuint32(); c->v2.remmaxpkt = ssh2_pkt_getuint32(); @@ -3871,12 +3920,12 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt) try_send = TRUE; } if (try_send) { - enum234 e; + int i; struct ssh_channel *c; /* * Try to send data on all channels if we can. */ - for (c = first234(ssh_channels, &e); c; c = next234(&e)) + for (i = 0; NULL != (c = index234(ssh_channels, i)); i++) ssh2_try_send(c); } }