X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/620f0cafc4a77a6cb8e8b7356dd866881b401bcd..ff89646a8828ce862d53b96a19ee6469dabce71a:/ssh.c diff --git a/ssh.c b/ssh.c index 8e5dce7a..f48d1426 100644 --- a/ssh.c +++ b/ssh.c @@ -17,7 +17,7 @@ #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 +118,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 +365,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; @@ -510,6 +569,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); } @@ -649,6 +722,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); } @@ -928,7 +1023,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"); @@ -1510,6 +1605,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 +1789,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 +2270,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 +2290,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 +2304,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 +2312,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; @@ -2247,7 +2332,8 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt) { if (c) { int closetype; closetype = (pktin.type == SSH1_MSG_CHANNEL_CLOSE ? 1 : 2); - send_packet(pktin.type, PKT_INT, c->remoteid, PKT_END); + if (!(c->closes & closetype)) + send_packet(pktin.type, PKT_INT, c->remoteid, PKT_END); if ((c->closes == 0) && (c->type == CHAN_X11)) { logevent("X11 connection closed"); assert(c->u.x11.s != NULL); @@ -3016,6 +3102,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; @@ -3077,7 +3164,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. */ @@ -3446,8 +3533,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); @@ -3471,7 +3559,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"); @@ -3750,7 +3837,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) @@ -3776,8 +3862,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); @@ -3834,17 +3919,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(); @@ -3870,12 +3945,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); } }