X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/putty/blobdiff_plain/2425184bb0dd4cf3924f717c0b07c2ca6e7da4e6..e94b1ec49475425f8fa8002b405ae8c5a3e3b31b:/ssh.c diff --git a/ssh.c b/ssh.c index 7f3748af..a9ecaa3d 100644 --- a/ssh.c +++ b/ssh.c @@ -256,18 +256,6 @@ static char *ssh2_pkt_type(int pkt_ctx, int type) #undef translate #undef translatec -#define GET_32BIT(cp) \ - (((unsigned long)(unsigned char)(cp)[0] << 24) | \ - ((unsigned long)(unsigned char)(cp)[1] << 16) | \ - ((unsigned long)(unsigned char)(cp)[2] << 8) | \ - ((unsigned long)(unsigned char)(cp)[3])) - -#define PUT_32BIT(cp, value) { \ - (cp)[0] = (unsigned char)((value) >> 24); \ - (cp)[1] = (unsigned char)((value) >> 16); \ - (cp)[2] = (unsigned char)((value) >> 8); \ - (cp)[3] = (unsigned char)(value); } - /* Enumeration values for fields in SSH-1 packets */ enum { PKT_END, PKT_INT, PKT_CHAR, PKT_DATA, PKT_STR, PKT_BIGNUM, @@ -319,14 +307,19 @@ enum { typedef struct ssh_tag *Ssh; struct Packet; +static struct Packet *ssh1_pkt_init(int pkt_type); static struct Packet *ssh2_pkt_init(int pkt_type); +static void ssh_pkt_ensure(struct Packet *, int length); +static void ssh_pkt_adddata(struct Packet *, void *data, int len); +static void ssh_pkt_addbyte(struct Packet *, unsigned char value); static void ssh2_pkt_addbool(struct Packet *, unsigned char value); -static void ssh2_pkt_adduint32(struct Packet *, unsigned long value); -static void ssh2_pkt_addstring_start(struct Packet *); -static void ssh2_pkt_addstring_str(struct Packet *, char *data); -static void ssh2_pkt_addstring_data(struct Packet *, char *data, int len); -static void ssh2_pkt_addstring(struct Packet *, char *data); +static void ssh_pkt_adduint32(struct Packet *, unsigned long value); +static void ssh_pkt_addstring_start(struct Packet *); +static void ssh_pkt_addstring_str(struct Packet *, char *data); +static void ssh_pkt_addstring_data(struct Packet *, char *data, int len); +static void ssh_pkt_addstring(struct Packet *, char *data); static unsigned char *ssh2_mpint_fmt(Bignum b, int *len); +static void ssh1_pkt_addmp(struct Packet *, Bignum b); static void ssh2_pkt_addmp(struct Packet *, Bignum b); static int ssh2_pkt_construct(Ssh, struct Packet *); static void ssh2_pkt_send(Ssh, struct Packet *); @@ -342,18 +335,18 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * * - SSH1_BUFFER_LIMIT is the amount of backlog that must build up * on a local data stream before we throttle the whole SSH - * connection (in SSH1 only). Throttling the whole connection is + * connection (in SSH-1 only). Throttling the whole connection is * pretty drastic so we set this high in the hope it won't * happen very often. * * - SSH_MAX_BACKLOG is the amount of backlog that must build up * on the SSH connection itself before we defensively throttle * _all_ local data streams. This is pretty drastic too (though - * thankfully unlikely in SSH2 since the window mechanism should + * thankfully unlikely in SSH-2 since the window mechanism should * ensure that the server never has any need to throttle its end * of the connection), so we set this high as well. * - * - OUR_V2_WINSIZE is the maximum window size we present on SSH2 + * - OUR_V2_WINSIZE is the maximum window size we present on SSH-2 * channels. */ @@ -364,34 +357,11 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, const static struct ssh_signkey *hostkey_algs[] = { &ssh_rsa, &ssh_dss }; -static void *nullmac_make_context(void) -{ - return NULL; -} -static void nullmac_free_context(void *handle) -{ -} -static void nullmac_key(void *handle, unsigned char *key) -{ -} -static void nullmac_generate(void *handle, unsigned char *blk, int len, - unsigned long seq) -{ -} -static int nullmac_verify(void *handle, unsigned char *blk, int len, - unsigned long seq) -{ - return 1; -} -const static struct ssh_mac ssh_mac_none = { - nullmac_make_context, nullmac_free_context, nullmac_key, - nullmac_generate, nullmac_verify, "none", 0 -}; const static struct ssh_mac *macs[] = { - &ssh_sha1, &ssh_md5, &ssh_mac_none + &ssh_sha1, &ssh_md5 }; const static struct ssh_mac *buggymacs[] = { - &ssh_sha1_buggy, &ssh_md5, &ssh_mac_none + &ssh_sha1_buggy, &ssh_md5 }; static void *ssh_comp_none_init(void) @@ -439,7 +409,7 @@ struct ssh_channel { /* True if we opened this channel but server hasn't confirmed. */ int halfopen; /* - * In SSH1, this value contains four bits: + * In SSH-1, this value contains four bits: * * 1 We have sent SSH1_MSG_CHANNEL_CLOSE. * 2 We have sent SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION. @@ -475,11 +445,11 @@ struct ssh_channel { }; /* - * 2-3-4 tree storing remote->local port forwardings. SSH 1 and SSH - * 2 use this structure in different ways, reflecting SSH 2's + * 2-3-4 tree storing remote->local port forwardings. SSH-1 and SSH-2 + * use this structure in different ways, reflecting SSH-2's * altogether saner approach to port forwarding. * - * In SSH 1, you arrange a remote forwarding by sending the server + * In SSH-1, you arrange a remote forwarding by sending the server * the remote port number, and the local destination host:port. * When a connection comes in, the server sends you back that * host:port pair, and you connect to it. This is a ready-made @@ -491,15 +461,15 @@ struct ssh_channel { * host:port pairs we _are_ trying to forward to, and reject a * connection request from the server if it's not in the list. * - * In SSH 2, each side of the connection minds its own business and + * In SSH-2, each side of the connection minds its own business and * doesn't send unnecessary information to the other. You arrange a * remote forwarding by sending the server just the remote port * number. When a connection comes in, the server tells you which * of its ports was connected to; and _you_ have to remember what * local host:port pair went with that port number. * - * Hence, in SSH 1 this structure is indexed by destination - * host:port pair, whereas in SSH 2 it is indexed by source port. + * Hence, in SSH-1 this structure is indexed by destination + * host:port pair, whereas in SSH-2 it is indexed by source port. */ struct ssh_portfwd; /* forward declaration */ @@ -534,15 +504,15 @@ struct ssh_portfwd { sfree((pf)->sserv), sfree((pf)->dserv)) : (void)0 ), sfree(pf) ) struct Packet { - long length; - long forcepad; /* Force padding to at least this length */ - int type; - unsigned long sequence; - unsigned char *data; - unsigned char *body; - long savedpos; - long maxlen; - long encrypted_len; /* for SSH2 total-size counting */ + long length; /* length of `data' actually used */ + long forcepad; /* SSH-2: force padding to at least this length */ + int type; /* only used for incoming packets */ + unsigned long sequence; /* SSH-2 incoming sequence number */ + unsigned char *data; /* allocated storage */ + unsigned char *body; /* offset of payload within `data' */ + long savedpos; /* temporary index into `data' (for strings) */ + long maxlen; /* amount of storage allocated for `data' */ + long encrypted_len; /* for SSH-2 total-size counting */ /* * State associated with packet logging @@ -710,7 +680,7 @@ struct ssh_tag { struct rdpkt1_state_tag rdpkt1_state; struct rdpkt2_state_tag rdpkt2_state; - /* ssh1 and ssh2 use this for different things, but both use it */ + /* SSH-1 and SSH-2 use this for different things, but both use it */ int protocol_initial_phase_done; void (*protocol) (Ssh ssh, void *vin, int inlen, @@ -982,7 +952,7 @@ static struct Packet *ssh_new_packet(void) { struct Packet *pkt = snew(struct Packet); - pkt->data = NULL; + pkt->body = pkt->data = NULL; pkt->maxlen = 0; pkt->logmode = PKTLOG_EMIT; pkt->nblanks = 0; @@ -1282,36 +1252,9 @@ static struct Packet *ssh2_rdpkt(Ssh ssh, unsigned char **data, int *datalen) crFinish(st->pktin); } -static void ssh1_pktout_size(struct Packet *pkt, int len) +static int s_wrpkt_prepare(Ssh ssh, struct Packet *pkt, int *offset_p) { - int pad, biglen; - - len += 5; /* type and CRC */ - pad = 8 - (len % 8); - biglen = len + pad; - - pkt->length = len - 5; - if (pkt->maxlen < biglen) { - pkt->maxlen = biglen; - pkt->data = sresize(pkt->data, biglen + 4 + APIEXTRA, unsigned char); - } - pkt->body = pkt->data + 4 + pad + 1; -} - -static struct Packet *s_wrpkt_start(int type, int len) -{ - struct Packet *pkt = ssh_new_packet(); - ssh1_pktout_size(pkt, len); - pkt->type = type; - /* Initialise log omission state */ - pkt->nblanks = 0; - pkt->blanks = NULL; - return pkt; -} - -static int s_wrpkt_prepare(Ssh ssh, struct Packet *pkt) -{ - int pad, biglen, i; + int pad, biglen, i, pktoffs; unsigned long crc; #ifdef __SC__ /* @@ -1324,12 +1267,10 @@ static int s_wrpkt_prepare(Ssh ssh, struct Packet *pkt) #endif int len; - pkt->body[-1] = pkt->type; - if (ssh->logctx) - log_packet(ssh->logctx, PKT_OUTGOING, pkt->type, - ssh1_pkt_type(pkt->type), - pkt->body, pkt->length, + log_packet(ssh->logctx, PKT_OUTGOING, pkt->data[12], + ssh1_pkt_type(pkt->data[12]), + pkt->body, pkt->length - (pkt->body - pkt->data), pkt->nblanks, pkt->blanks); sfree(pkt->blanks); pkt->blanks = NULL; pkt->nblanks = 0; @@ -1338,132 +1279,99 @@ static int s_wrpkt_prepare(Ssh ssh, struct Packet *pkt) unsigned char *compblk; int complen; zlib_compress_block(ssh->cs_comp_ctx, - pkt->body - 1, pkt->length + 1, + pkt->data + 12, pkt->length - 12, &compblk, &complen); - ssh1_pktout_size(pkt, complen - 1); - memcpy(pkt->body - 1, compblk, complen); + memcpy(pkt->data + 12, compblk, complen); sfree(compblk); + pkt->length = complen + 12; } - len = pkt->length + 5; /* type and CRC */ + ssh_pkt_ensure(pkt, pkt->length + 4); /* space for CRC */ + pkt->length += 4; + len = pkt->length - 4 - 8; /* len(type+data+CRC) */ pad = 8 - (len % 8); - biglen = len + pad; + pktoffs = 8 - pad; + biglen = len + pad; /* len(padding+type+data+CRC) */ - for (i = 0; i < pad; i++) - pkt->data[i + 4] = random_byte(); - crc = crc32_compute(pkt->data + 4, biglen - 4); - PUT_32BIT(pkt->data + biglen, crc); - PUT_32BIT(pkt->data, len); + for (i = pktoffs; i < 4+8; i++) + pkt->data[i] = random_byte(); + crc = crc32_compute(pkt->data + pktoffs + 4, biglen - 4); /* all ex len */ + PUT_32BIT(pkt->data + pktoffs + 4 + biglen - 4, crc); + PUT_32BIT(pkt->data + pktoffs, len); if (ssh->cipher) - ssh->cipher->encrypt(ssh->v1_cipher_ctx, pkt->data + 4, biglen); + ssh->cipher->encrypt(ssh->v1_cipher_ctx, + pkt->data + pktoffs + 4, biglen); - return biglen + 4; + if (offset_p) *offset_p = pktoffs; + return biglen + 4; /* len(length+padding+type+data+CRC) */ } static void s_wrpkt(Ssh ssh, struct Packet *pkt) { - int len, backlog; - len = s_wrpkt_prepare(ssh, pkt); - backlog = sk_write(ssh->s, (char *)pkt->data, len); + int len, backlog, offset; + len = s_wrpkt_prepare(ssh, pkt, &offset); + backlog = sk_write(ssh->s, (char *)pkt->data + offset, len); if (backlog > SSH_MAX_BACKLOG) ssh_throttle_all(ssh, 1, backlog); + ssh_free_packet(pkt); } static void s_wrpkt_defer(Ssh ssh, struct Packet *pkt) { - int len; - len = s_wrpkt_prepare(ssh, pkt); + int len, offset; + len = s_wrpkt_prepare(ssh, pkt, &offset); if (ssh->deferred_len + len > ssh->deferred_size) { ssh->deferred_size = ssh->deferred_len + len + 128; ssh->deferred_send_data = sresize(ssh->deferred_send_data, ssh->deferred_size, unsigned char); } - memcpy(ssh->deferred_send_data + ssh->deferred_len, pkt->data, len); + memcpy(ssh->deferred_send_data + ssh->deferred_len, + pkt->data + offset, len); ssh->deferred_len += len; + ssh_free_packet(pkt); } /* - * Construct a packet with the specified contents. + * Construct a SSH-1 packet with the specified contents. + * (This all-at-once interface used to be the only one, but now SSH-1 + * packets can also be constructed incrementally.) */ -static struct Packet *construct_packet(Ssh ssh, int pkttype, - va_list ap1, va_list ap2) +static struct Packet *construct_packet(Ssh ssh, int pkttype, va_list ap) { - unsigned char *p, *argp, argchar; - unsigned long argint; - int pktlen, argtype, arglen; + int argtype; Bignum bn; struct Packet *pkt; - pktlen = 0; - while ((argtype = va_arg(ap1, int)) != PKT_END) { - switch (argtype) { - case PKT_INT: - (void) va_arg(ap1, int); - pktlen += 4; - break; - case PKT_CHAR: - (void) va_arg(ap1, int); - pktlen++; - break; - case PKT_DATA: - (void) va_arg(ap1, unsigned char *); - arglen = va_arg(ap1, int); - pktlen += arglen; - break; - case PKT_STR: - argp = va_arg(ap1, unsigned char *); - arglen = strlen((char *)argp); - pktlen += 4 + arglen; - break; - case PKT_BIGNUM: - bn = va_arg(ap1, Bignum); - pktlen += ssh1_bignum_length(bn); - break; - case PKTT_PASSWORD: - case PKTT_DATA: - case PKTT_OTHER: - /* ignore this pass */ - break; - default: - assert(0); - } - } - - pkt = s_wrpkt_start(pkttype, pktlen); - p = pkt->body; + pkt = ssh1_pkt_init(pkttype); - while ((argtype = va_arg(ap2, int)) != PKT_END) { - int offset = p - pkt->body, len = 0; + while ((argtype = va_arg(ap, int)) != PKT_END) { + unsigned char *argp, argchar; + unsigned long argint; + int arglen; switch (argtype) { /* Actual fields in the packet */ case PKT_INT: - argint = va_arg(ap2, int); - PUT_32BIT(p, argint); - len = 4; + argint = va_arg(ap, int); + ssh_pkt_adduint32(pkt, argint); break; case PKT_CHAR: - argchar = (unsigned char) va_arg(ap2, int); - *p = argchar; - len = 1; + argchar = (unsigned char) va_arg(ap, int); + ssh_pkt_addbyte(pkt, argchar); break; case PKT_DATA: - argp = va_arg(ap2, unsigned char *); - arglen = va_arg(ap2, int); - memcpy(p, argp, arglen); - len = arglen; + argp = va_arg(ap, unsigned char *); + arglen = va_arg(ap, int); + ssh_pkt_adddata(pkt, argp, arglen); break; case PKT_STR: - argp = va_arg(ap2, unsigned char *); - arglen = strlen((char *)argp); - PUT_32BIT(p, arglen); - memcpy(p + 4, argp, arglen); - len = arglen + 4; + argp = va_arg(ap, unsigned char *); + ssh_pkt_addstring(pkt, argp); break; case PKT_BIGNUM: - bn = va_arg(ap2, Bignum); - len = ssh1_write_bignum(p, bn); + bn = va_arg(ap, Bignum); + ssh1_pkt_addmp(pkt, bn); break; /* Tokens for modifications to packet logging */ case PKTT_PASSWORD: @@ -1476,16 +1384,6 @@ static struct Packet *construct_packet(Ssh ssh, int pkttype, end_log_omission(ssh, pkt); break; } - p += len; - /* Deal with logfile omission, if required. */ - if (len && (pkt->logmode != PKTLOG_EMIT)) { - pkt->nblanks++; - pkt->blanks = sresize(pkt->blanks, pkt->nblanks, - struct logblank_t); - pkt->blanks[pkt->nblanks-1].offset = offset; - pkt->blanks[pkt->nblanks-1].len = len; - pkt->blanks[pkt->nblanks-1].type = pkt->logmode; - } } return pkt; @@ -1494,27 +1392,21 @@ static struct Packet *construct_packet(Ssh ssh, int pkttype, static void send_packet(Ssh ssh, int pkttype, ...) { struct Packet *pkt; - va_list ap1, ap2; - va_start(ap1, pkttype); - va_start(ap2, pkttype); - pkt = construct_packet(ssh, pkttype, ap1, ap2); - va_end(ap2); - va_end(ap1); + va_list ap; + va_start(ap, pkttype); + pkt = construct_packet(ssh, pkttype, ap); + va_end(ap); s_wrpkt(ssh, pkt); - ssh_free_packet(pkt); } static void defer_packet(Ssh ssh, int pkttype, ...) { struct Packet *pkt; - va_list ap1, ap2; - va_start(ap1, pkttype); - va_start(ap2, pkttype); - pkt = construct_packet(ssh, pkttype, ap1, ap2); - va_end(ap2); - va_end(ap1); + va_list ap; + va_start(ap, pkttype); + pkt = construct_packet(ssh, pkttype, ap); + va_end(ap); s_wrpkt_defer(ssh, pkt); - ssh_free_packet(pkt); } static int ssh_versioncmp(char *a, char *b) @@ -1541,7 +1433,6 @@ static int ssh_versioncmp(char *a, char *b) * 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) { unsigned char lenblk[4]; @@ -1558,69 +1449,74 @@ static void sha_uint32(SHA_State * s, unsigned i) } /* - * SSH2 packet construction functions. + * Packet construction functions. Mostly shared between SSH-1 and SSH-2. */ -static void ssh2_pkt_ensure(struct Packet *pkt, int length) +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; pkt->maxlen = length + 256; pkt->data = sresize(pkt->data, pkt->maxlen + APIEXTRA, unsigned char); + if (body) pkt->body = pkt->data + offset; } } -static void ssh2_pkt_adddata(struct Packet *pkt, void *data, int len) +static void ssh_pkt_adddata(struct Packet *pkt, void *data, int len) { if (pkt->logmode != PKTLOG_EMIT) { pkt->nblanks++; pkt->blanks = sresize(pkt->blanks, pkt->nblanks, struct logblank_t); - pkt->blanks[pkt->nblanks-1].offset = pkt->length - 6; + assert(pkt->body); + pkt->blanks[pkt->nblanks-1].offset = pkt->length - + (pkt->body - pkt->data); pkt->blanks[pkt->nblanks-1].len = len; pkt->blanks[pkt->nblanks-1].type = pkt->logmode; } pkt->length += len; - ssh2_pkt_ensure(pkt, pkt->length); + ssh_pkt_ensure(pkt, pkt->length); memcpy(pkt->data + pkt->length - len, data, len); } -static void ssh2_pkt_addbyte(struct Packet *pkt, unsigned char byte) -{ - ssh2_pkt_adddata(pkt, &byte, 1); -} -static struct Packet *ssh2_pkt_init(int pkt_type) +static void ssh_pkt_addbyte(struct Packet *pkt, unsigned char byte) { - struct Packet *pkt = ssh_new_packet(); - pkt->length = 5; - pkt->forcepad = 0; - ssh2_pkt_addbyte(pkt, (unsigned char) pkt_type); - return pkt; + ssh_pkt_adddata(pkt, &byte, 1); } static void ssh2_pkt_addbool(struct Packet *pkt, unsigned char value) { - ssh2_pkt_adddata(pkt, &value, 1); + ssh_pkt_adddata(pkt, &value, 1); } -static void ssh2_pkt_adduint32(struct Packet *pkt, unsigned long value) +static void ssh_pkt_adduint32(struct Packet *pkt, unsigned long value) { unsigned char x[4]; PUT_32BIT(x, value); - ssh2_pkt_adddata(pkt, x, 4); + ssh_pkt_adddata(pkt, x, 4); } -static void ssh2_pkt_addstring_start(struct Packet *pkt) +static void ssh_pkt_addstring_start(struct Packet *pkt) { - ssh2_pkt_adduint32(pkt, 0); + ssh_pkt_adduint32(pkt, 0); pkt->savedpos = pkt->length; } -static void ssh2_pkt_addstring_str(struct Packet *pkt, char *data) +static void ssh_pkt_addstring_str(struct Packet *pkt, char *data) { - ssh2_pkt_adddata(pkt, data, strlen(data)); + ssh_pkt_adddata(pkt, data, strlen(data)); PUT_32BIT(pkt->data + pkt->savedpos - 4, pkt->length - pkt->savedpos); } -static void ssh2_pkt_addstring_data(struct Packet *pkt, char *data, int len) +static void ssh_pkt_addstring_data(struct Packet *pkt, char *data, int len) { - ssh2_pkt_adddata(pkt, data, len); + ssh_pkt_adddata(pkt, data, len); PUT_32BIT(pkt->data + pkt->savedpos - 4, pkt->length - pkt->savedpos); } -static void ssh2_pkt_addstring(struct Packet *pkt, char *data) +static void ssh_pkt_addstring(struct Packet *pkt, char *data) { - ssh2_pkt_addstring_start(pkt); - ssh2_pkt_addstring_str(pkt, data); + ssh_pkt_addstring_start(pkt); + ssh_pkt_addstring_str(pkt, data); +} +static void ssh1_pkt_addmp(struct Packet *pkt, Bignum b) +{ + int len = ssh1_bignum_length(b); + unsigned char *data = snewn(len, char); + (void) ssh1_write_bignum(data, b); + ssh_pkt_adddata(pkt, data, len); + sfree(data); } static unsigned char *ssh2_mpint_fmt(Bignum b, int *len) { @@ -1644,13 +1540,42 @@ static void ssh2_pkt_addmp(struct Packet *pkt, Bignum b) unsigned char *p; int len; p = ssh2_mpint_fmt(b, &len); - ssh2_pkt_addstring_start(pkt); - ssh2_pkt_addstring_data(pkt, (char *)p, len); + ssh_pkt_addstring_start(pkt); + ssh_pkt_addstring_data(pkt, (char *)p, len); sfree(p); } +static struct Packet *ssh1_pkt_init(int pkt_type) +{ + struct Packet *pkt = ssh_new_packet(); + pkt->length = 4 + 8; /* space for length + max padding */ + ssh_pkt_addbyte(pkt, pkt_type); + pkt->body = pkt->data + pkt->length; + return pkt; +} + +/* For legacy code (SSH-1 and -2 packet construction used to be separate) */ +#define ssh2_pkt_ensure(pkt, length) ssh_pkt_ensure(pkt, length) +#define ssh2_pkt_adddata(pkt, data, len) ssh_pkt_adddata(pkt, data, len) +#define ssh2_pkt_addbyte(pkt, byte) ssh_pkt_addbyte(pkt, byte) +#define ssh2_pkt_adduint32(pkt, value) ssh_pkt_adduint32(pkt, value) +#define ssh2_pkt_addstring_start(pkt) ssh_pkt_addstring_start(pkt) +#define ssh2_pkt_addstring_str(pkt, data) ssh_pkt_addstring_str(pkt, data) +#define ssh2_pkt_addstring_data(pkt, data, len) ssh_pkt_addstring_data(pkt, data, len) +#define ssh2_pkt_addstring(pkt, data) ssh_pkt_addstring(pkt, data) + +static struct Packet *ssh2_pkt_init(int pkt_type) +{ + struct Packet *pkt = ssh_new_packet(); + pkt->length = 5; + pkt->forcepad = 0; + ssh_pkt_addbyte(pkt, (unsigned char) pkt_type); + pkt->body = pkt->data + pkt->length; + return pkt; +} + /* - * Construct an SSH2 final-form packet: compress it, encrypt it, + * Construct an SSH-2 final-form packet: compress it, encrypt it, * put the MAC on it. Final packet, ready to be sent, is stored in * pkt->data. Total length is returned. */ @@ -1661,7 +1586,7 @@ static int ssh2_pkt_construct(Ssh ssh, struct Packet *pkt) if (ssh->logctx) log_packet(ssh->logctx, PKT_OUTGOING, pkt->data[5], ssh2_pkt_type(ssh->pkt_ctx, pkt->data[5]), - pkt->data + 6, pkt->length - 6, + pkt->body, pkt->length - (pkt->body - pkt->data), pkt->nblanks, pkt->blanks); sfree(pkt->blanks); pkt->blanks = NULL; pkt->nblanks = 0; @@ -1748,7 +1673,7 @@ static int ssh2_pkt_construct(Ssh ssh, struct Packet *pkt) */ /* - * Send an SSH2 packet immediately, without queuing or deferring. + * Send an SSH-2 packet immediately, without queuing or deferring. */ static void ssh2_pkt_send_noqueue(Ssh ssh, struct Packet *pkt) { @@ -1769,7 +1694,7 @@ static void ssh2_pkt_send_noqueue(Ssh ssh, struct Packet *pkt) } /* - * Defer an SSH2 packet. + * Defer an SSH-2 packet. */ static void ssh2_pkt_defer_noqueue(Ssh ssh, struct Packet *pkt) { @@ -1787,7 +1712,7 @@ static void ssh2_pkt_defer_noqueue(Ssh ssh, struct Packet *pkt) } /* - * Queue an SSH2 packet. + * Queue an SSH-2 packet. */ static void ssh2_pkt_queue(Ssh ssh, struct Packet *pkt) { @@ -1829,7 +1754,7 @@ static void ssh2_pkt_defer(Ssh ssh, struct Packet *pkt) /* * Send the whole deferred data block constructed by - * ssh2_pkt_defer() or SSH1's defer_packet(). + * ssh2_pkt_defer() or SSH-1's defer_packet(). * * The expected use of the defer mechanism is that you call * ssh2_pkt_defer() a few times, then call ssh_pkt_defersend(). If @@ -1860,7 +1785,7 @@ static void ssh_pkt_defersend(Ssh ssh) } /* - * Send all queued SSH2 packets. We send them by means of + * Send all queued SSH-2 packets. We send them by means of * ssh2_pkt_defer_noqueue(), in case they included a pair of * packets that needed to be lumped together. */ @@ -1901,7 +1826,7 @@ static void sha_mpint(SHA_State * s, Bignum b) } /* - * Packet decode functions for both SSH1 and SSH2. + * Packet decode functions for both SSH-1 and SSH-2. */ static unsigned long ssh_pkt_getuint32(struct Packet *pkt) { @@ -1992,7 +1917,7 @@ static Bignum ssh2_pkt_getmp(struct Packet *pkt) } /* - * Helper function to add an SSH2 signature blob to a packet. + * Helper function to add an SSH-2 signature blob to a packet. * Expects to be shown the public key blob as well as the signature * blob. Normally works just like ssh2_pkt_addstring, but will * fiddle with the signature packet if necessary for @@ -2088,7 +2013,7 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring) * sniffing. */ ssh->remote_bugs |= BUG_CHOKES_ON_SSH1_IGNORE; - logevent("We believe remote version has SSH1 ignore bug"); + logevent("We believe remote version has SSH-1 ignore bug"); } if (ssh->cfg.sshbug_plainpw1 == FORCE_ON || @@ -2100,7 +2025,7 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring) * the password. */ ssh->remote_bugs |= BUG_NEEDS_SSH1_PLAIN_PASSWORD; - logevent("We believe remote version needs a plain SSH1 password"); + logevent("We believe remote version needs a plain SSH-1 password"); } if (ssh->cfg.sshbug_rsa1 == FORCE_ON || @@ -2112,7 +2037,7 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring) * an AUTH_RSA message. */ ssh->remote_bugs |= BUG_CHOKES_ON_RSA; - logevent("We believe remote version can't handle RSA authentication"); + logevent("We believe remote version can't handle SSH-1 RSA authentication"); } if (ssh->cfg.sshbug_hmac2 == FORCE_ON || @@ -2125,7 +2050,7 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring) * These versions have the HMAC bug. */ ssh->remote_bugs |= BUG_SSH2_HMAC; - logevent("We believe remote version has SSH2 HMAC bug"); + logevent("We believe remote version has SSH-2 HMAC bug"); } if (ssh->cfg.sshbug_derivekey2 == FORCE_ON || @@ -2138,7 +2063,7 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring) * generate the keys). */ ssh->remote_bugs |= BUG_SSH2_DERIVEKEY; - logevent("We believe remote version has SSH2 key-derivation bug"); + logevent("We believe remote version has SSH-2 key-derivation bug"); } if (ssh->cfg.sshbug_rsapad2 == FORCE_ON || @@ -2146,21 +2071,21 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring) (wc_match("OpenSSH_2.[5-9]*", imp) || wc_match("OpenSSH_3.[0-2]*", imp)))) { /* - * These versions have the SSH2 RSA padding bug. + * These versions have the SSH-2 RSA padding bug. */ ssh->remote_bugs |= BUG_SSH2_RSA_PADDING; - logevent("We believe remote version has SSH2 RSA padding bug"); + logevent("We believe remote version has SSH-2 RSA padding bug"); } if (ssh->cfg.sshbug_pksessid2 == FORCE_ON || (ssh->cfg.sshbug_pksessid2 == AUTO && wc_match("OpenSSH_2.[0-2]*", imp))) { /* - * These versions have the SSH2 session-ID bug in + * These versions have the SSH-2 session-ID bug in * public-key authentication. */ ssh->remote_bugs |= BUG_SSH2_PK_SESSIONID; - logevent("We believe remote version has SSH2 public-key-session-ID bug"); + logevent("We believe remote version has SSH-2 public-key-session-ID bug"); } if (ssh->cfg.sshbug_rekey2 == FORCE_ON || @@ -2170,10 +2095,10 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring) wc_match("Sun_SSH_1.0", imp) || wc_match("Sun_SSH_1.0.1", imp)))) { /* - * These versions have the SSH2 rekey bug. + * These versions have the SSH-2 rekey bug. */ ssh->remote_bugs |= BUG_SSH2_REKEY; - logevent("We believe remote version has SSH2 rekey bug"); + logevent("We believe remote version has SSH-2 rekey bug"); } } @@ -2314,14 +2239,14 @@ static int do_ssh_init(Ssh ssh, unsigned char c) strcspn(s->vstring, "\015\012")); /* - * Initialise SSHv2 protocol. + * Initialise SSH-2 protocol. */ ssh->protocol = ssh2_protocol; ssh2_protocol_setup(ssh); ssh->s_rdpkt = ssh2_rdpkt; } else { /* - * Initialise SSHv1 protocol. + * Initialise SSH-1 protocol. */ ssh->protocol = ssh1_protocol; ssh1_protocol_setup(ssh); @@ -2444,6 +2369,7 @@ static int ssh_do_close(Ssh ssh, int notify_exit) struct ssh_channel *c; ssh->state = SSH_STATE_CLOSED; + expire_timer_context(ssh); if (ssh->s) { sk_close(ssh->s); ssh->s = NULL; @@ -2661,7 +2587,7 @@ static void ssh_throttle_all(Ssh ssh, int enable, int bufsize) /* * Username and password input, abstracted off into routines - * reusable in several places - even between SSH1 and SSH2. + * reusable in several places - even between SSH-1 and SSH-2. */ /* Set up a username or password input loop on a given buffer. */ @@ -2841,14 +2767,14 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, ptr = ssh_pkt_getdata(pktin, 8); if (!ptr) { - bombout(("SSH1 public key packet stopped before random cookie")); + bombout(("SSH-1 public key packet stopped before random cookie")); crStop(0); } memcpy(cookie, ptr, 8); if (!ssh1_pkt_getrsakey(pktin, &servkey, &s->keystr1) || !ssh1_pkt_getrsakey(pktin, &hostkey, &s->keystr2)) { - bombout(("Failed to read SSH1 public keys from public key packet")); + bombout(("Failed to read SSH-1 public keys from public key packet")); crStop(0); } @@ -2887,7 +2813,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, */ if (hostkey.bits > hostkey.bytes * 8 || servkey.bits > servkey.bytes * 8) { - bombout(("SSH1 public keys were badly formatted")); + bombout(("SSH-1 public keys were badly formatted")); crStop(0); } @@ -2954,7 +2880,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, ret = rsaencrypt(s->rsabuf, hostkey.bytes, &servkey); } if (!ret) { - bombout(("SSH1 public key encryptions failed due to bad formatting")); + bombout(("SSH-1 public key encryptions failed due to bad formatting")); crStop(0); } @@ -2971,7 +2897,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, warn = 1; } else if (next_cipher == CIPHER_AES) { /* XXX Probably don't need to mention this. */ - logevent("AES not supported in SSH1, skipping"); + logevent("AES not supported in SSH-1, skipping"); } else { switch (next_cipher) { case CIPHER_3DES: s->cipher_type = SSH_CIPHER_3DES; @@ -2987,7 +2913,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, } if (!cipher_chosen) { if ((s->supported_ciphers_mask & (1 << SSH_CIPHER_3DES)) == 0) - bombout(("Server violates SSH 1 protocol by not " + bombout(("Server violates SSH-1 protocol by not " "supporting 3DES encryption")); else /* shouldn't happen */ @@ -3177,7 +3103,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, s->p = s->response + 5; s->nkeys = GET_32BIT(s->p); s->p += 4; - logeventf(ssh, "Pageant has %d SSH1 keys", s->nkeys); + logeventf(ssh, "Pageant has %d SSH-1 keys", s->nkeys); for (s->keyi = 0; s->keyi < s->nkeys; s->keyi++) { logeventf(ssh, "Trying Pageant key #%d", s->keyi); if (s->publickey_blob && @@ -3337,6 +3263,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, bombout(("TIS challenge packet was badly formed")); crStop(0); } + c_write_str(ssh, "Using TIS authentication.\r\n"); logevent("Received TIS challenge"); if (challengelen > sizeof(s->prompt) - 1) challengelen = sizeof(s->prompt) - 1;/* prevent overrun */ @@ -3370,6 +3297,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, bombout(("CryptoCard challenge packet was badly formed")); crStop(0); } + c_write_str(ssh, "Using CryptoCard authentication.\r\n"); logevent("Received CryptoCard challenge"); if (challengelen > sizeof(s->prompt) - 1) challengelen = sizeof(s->prompt) - 1;/* prevent overrun */ @@ -3682,10 +3610,8 @@ void sshfwd_close(struct ssh_channel *c) { Ssh ssh = c->ssh; - if (ssh->state != SSH_STATE_SESSION) { - assert(ssh->state == SSH_STATE_CLOSED); + if (ssh->state == SSH_STATE_CLOSED) return; - } if (c && !c->closes) { /* @@ -3722,10 +3648,8 @@ int sshfwd_write(struct ssh_channel *c, char *buf, int len) { Ssh ssh = c->ssh; - if (ssh->state != SSH_STATE_SESSION) { - assert(ssh->state == SSH_STATE_CLOSED); + if (ssh->state == SSH_STATE_CLOSED) return 0; - } if (ssh->version == 1) { send_packet(ssh, SSH1_MSG_CHANNEL_DATA, @@ -3734,7 +3658,7 @@ int sshfwd_write(struct ssh_channel *c, char *buf, int len) PKT_INT, len, PKT_DATA, buf, len, PKTT_OTHER, PKT_END); /* - * In SSH1 we can return 0 here - implying that forwarded + * In SSH-1 we can return 0 here - implying that forwarded * connections are never individually throttled - because * the only circumstance that can cause throttling will be * the whole SSH connection backing up, in which case @@ -3751,10 +3675,8 @@ void sshfwd_unthrottle(struct ssh_channel *c, int bufsize) { Ssh ssh = c->ssh; - if (ssh->state != SSH_STATE_SESSION) { - assert(ssh->state == SSH_STATE_CLOSED); + if (ssh->state == SSH_STATE_CLOSED) return; - } if (ssh->version == 1) { if (c->v.v1.throttling && bufsize < SSH1_BUFFER_LIMIT) { @@ -3906,7 +3828,7 @@ static void ssh_setup_portfwd(Ssh ssh, const Config *cfg) portfwd_strptr++; sports[n] = '\0'; if (ssh->version == 1 && type == 'R') { - logeventf(ssh, "SSH1 cannot handle remote source address " + logeventf(ssh, "SSH-1 cannot handle remote source address " "spec \"%s\"; ignoring", sports); } else strcpy(saddr, sports); @@ -4027,7 +3949,7 @@ static void ssh_setup_portfwd(Ssh ssh, const Config *cfg) if (ssh->version == 1) { /* * We cannot cancel listening ports on the - * server side in SSH1! There's no message + * server side in SSH-1! There's no message * to support it. Instead, we simply remove * the rportfwd record from the local end * so that any connections the server tries @@ -4616,7 +4538,7 @@ static void do_ssh1_connection(Ssh ssh, unsigned char *in, int inlen, /* * Start the shell or command. * - * Special case: if the first-choice command is an SSH2 + * Special case: if the first-choice command is an SSH-2 * subsystem (hence not usable here) and the second choice * exists, we fall straight back to that. */ @@ -4681,7 +4603,7 @@ static void do_ssh1_connection(Ssh ssh, unsigned char *in, int inlen, } /* - * Handle the top-level SSH2 protocol. + * Handle the top-level SSH-2 protocol. */ static void ssh1_msg_debug(Ssh ssh, struct Packet *pktin) { @@ -4801,7 +4723,7 @@ static int first_in_commasep_string(char *needle, char *haystack, int haylen) /* - * SSH2 key creation method. + * SSH-2 key creation method. */ static void ssh2_mkkey(Ssh ssh, Bignum K, unsigned char *H, unsigned char *sessid, char chr, @@ -4826,7 +4748,7 @@ static void ssh2_mkkey(Ssh ssh, Bignum K, unsigned char *H, } /* - * Handle the SSH2 transport layer. + * Handle the SSH-2 transport layer. */ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, struct Packet *pktin) @@ -4930,6 +4852,9 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, case CIPHER_AES: s->preferred_ciphers[s->n_preferred_ciphers++] = &ssh2_aes; break; + case CIPHER_ARCFOUR: + s->preferred_ciphers[s->n_preferred_ciphers++] = &ssh2_arcfour; + break; case CIPHER_WARN: /* Flag for later. Don't bother if it's the last in * the list. */ @@ -5639,7 +5564,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, } /* - * Add data to an SSH2 channel output buffer. + * Add data to an SSH-2 channel output buffer. */ static void ssh2_add_channel_data(struct ssh_channel *c, char *buf, int len) @@ -5648,7 +5573,7 @@ static void ssh2_add_channel_data(struct ssh_channel *c, char *buf, } /* - * Attempt to send data on an SSH2 channel. + * Attempt to send data on an SSH-2 channel. */ static int ssh2_try_send(struct ssh_channel *c) { @@ -5681,8 +5606,34 @@ static int ssh2_try_send(struct ssh_channel *c) return bufchain_size(&c->v.v2.outbuffer); } +static void ssh2_try_send_and_unthrottle(struct ssh_channel *c) +{ + int bufsize; + if (c->closes) + return; /* don't send on closing channels */ + bufsize = ssh2_try_send(c); + if (bufsize == 0) { + switch (c->type) { + case CHAN_MAINSESSION: + /* stdin need not receive an unthrottle + * notification since it will be polled */ + break; + case CHAN_X11: + x11_unthrottle(c->u.x11.s); + break; + case CHAN_AGENT: + /* agent sockets are request/response and need no + * buffer management */ + break; + case CHAN_SOCKDATA: + pfd_unthrottle(c->u.pfd.s); + break; + } + } +} + /* - * Potentially enlarge the window on an SSH2 channel. + * Potentially enlarge the window on an SSH-2 channel. */ static void ssh2_set_window(struct ssh_channel *c, unsigned newwin) { @@ -5719,8 +5670,10 @@ static void ssh2_msg_channel_window_adjust(Ssh ssh, struct Packet *pktin) unsigned i = ssh_pkt_getuint32(pktin); struct ssh_channel *c; c = find234(ssh->channels, &i, ssh_channelfind); - if (c && !c->closes) + if (c && !c->closes) { c->v.v2.remwindow += ssh_pkt_getuint32(pktin); + ssh2_try_send_and_unthrottle(c); + } } static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin) @@ -5879,7 +5832,7 @@ static void ssh2_msg_channel_close(Ssh ssh, struct Packet *pktin) /* * We used to send SSH_MSG_DISCONNECT here, * because I'd believed that _every_ conforming - * SSH2 connection had to end with a disconnect + * SSH-2 connection had to end with a disconnect * being sent by at least one side; apparently * I was wrong and it's perfectly OK to * unceremoniously slam the connection shut @@ -6242,7 +6195,7 @@ static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin) } /* - * Handle the SSH2 userauth and connection layers. + * Handle the SSH-2 userauth and connection layers. */ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, struct Packet *pktin) @@ -6589,7 +6542,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, s->p = s->response + 5; s->nkeys = GET_32BIT(s->p); s->p += 4; - logeventf(ssh, "Pageant has %d SSH2 keys", s->nkeys); + logeventf(ssh, "Pageant has %d SSH-2 keys", s->nkeys); for (s->keyi = 0; s->keyi < s->nkeys; s->keyi++) { void *vret; @@ -6808,6 +6761,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, continue; } + c_write_str(ssh, "Using keyboard-interactive authentication.\r\n"); s->kbd_inter_running = TRUE; s->curr_prompt = 0; } @@ -7417,30 +7371,8 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, /* * Try to send data on all channels if we can. */ - for (i = 0; NULL != (c = index234(ssh->channels, i)); i++) { - int bufsize; - if (c->closes) - continue; /* don't send on closing channels */ - bufsize = ssh2_try_send(c); - if (bufsize == 0) { - switch (c->type) { - case CHAN_MAINSESSION: - /* stdin need not receive an unthrottle - * notification since it will be polled */ - break; - case CHAN_X11: - x11_unthrottle(c->u.x11.s); - break; - case CHAN_AGENT: - /* agent sockets are request/response and need no - * buffer management */ - break; - case CHAN_SOCKDATA: - pfd_unthrottle(c->u.pfd.s); - break; - } - } - } + for (i = 0; NULL != (c = index234(ssh->channels, i)); i++) + ssh2_try_send_and_unthrottle(c); } } @@ -7448,7 +7380,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, } /* - * Handlers for SSH2 messages that might arrive at any moment. + * Handlers for SSH-2 messages that might arrive at any moment. */ static void ssh2_msg_disconnect(Ssh ssh, struct Packet *pktin) { @@ -7506,7 +7438,7 @@ static void ssh2_msg_something_unimplemented(Ssh ssh, struct Packet *pktin) } /* - * Handle the top-level SSH2 protocol. + * Handle the top-level SSH-2 protocol. */ static void ssh2_protocol_setup(Ssh ssh) { @@ -7560,7 +7492,7 @@ static void ssh2_protocol_setup(Ssh ssh) * These special message types we install handlers for. */ ssh->packet_dispatch[SSH2_MSG_DISCONNECT] = ssh2_msg_disconnect; - ssh->packet_dispatch[SSH2_MSG_IGNORE] = ssh_msg_ignore; /* shared with ssh1 */ + ssh->packet_dispatch[SSH2_MSG_IGNORE] = ssh_msg_ignore; /* shared with SSH-1 */ ssh->packet_dispatch[SSH2_MSG_DEBUG] = ssh2_msg_debug; } @@ -7568,6 +7500,9 @@ static void ssh2_timer(void *ctx, long now) { Ssh ssh = (Ssh)ctx; + if (ssh->state == SSH_STATE_CLOSED) + return; + if (!ssh->kex_in_progress && ssh->cfg.ssh_rekey_time != 0 && now - ssh->next_rekey >= 0) { do_ssh2_transport(ssh, "timeout", -1, NULL); @@ -8052,6 +7987,7 @@ static void ssh_special(void *handle, Telnet_Special code) struct Packet *pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_EOF); ssh2_pkt_adduint32(pktout, ssh->mainchan->remoteid); ssh2_pkt_send(ssh, pktout); + ssh->send_ok = 0; /* now stop trying to read from stdin */ } logevent("Sent EOF message"); } else if (code == TS_PING || code == TS_NOP) { @@ -8073,7 +8009,7 @@ static void ssh_special(void *handle, Telnet_Special code) if (ssh->state == SSH_STATE_CLOSED || ssh->state == SSH_STATE_PREPACKET) return; if (ssh->version == 1) { - logevent("Unable to send BREAK signal in SSH1"); + logevent("Unable to send BREAK signal in SSH-1"); } else if (ssh->mainchan) { pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST); ssh2_pkt_adduint32(pktout, ssh->mainchan->remoteid);