X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/2e85c969d67eb14a07314802d4ac5dd63eef660b..a1a1fae491039fc8fba576474e6bb56800f54652:/ssh.c diff --git a/ssh.c b/ssh.c index 41ef92fa..8b986ffa 100644 --- a/ssh.c +++ b/ssh.c @@ -166,6 +166,110 @@ static const char *const ssh2_disconnect_reasons[] = { #define BUG_SSH2_REKEY 64 #define BUG_SSH2_PK_SESSIONID 128 +/* + * Codes for terminal modes. + * Most of these are the same in SSH-1 and SSH-2. + * This list is derived from draft-ietf-secsh-connect-25 and + * SSH-1 RFC-1.2.31. + */ +static const struct { + const char* const mode; + int opcode; + enum { TTY_OP_CHAR, TTY_OP_BOOL } type; +} ssh_ttymodes[] = { + /* "V" prefix discarded for special characters relative to SSH specs */ + { "INTR", 1, TTY_OP_CHAR }, + { "QUIT", 2, TTY_OP_CHAR }, + { "ERASE", 3, TTY_OP_CHAR }, + { "KILL", 4, TTY_OP_CHAR }, + { "EOF", 5, TTY_OP_CHAR }, + { "EOL", 6, TTY_OP_CHAR }, + { "EOL2", 7, TTY_OP_CHAR }, + { "START", 8, TTY_OP_CHAR }, + { "STOP", 9, TTY_OP_CHAR }, + { "SUSP", 10, TTY_OP_CHAR }, + { "DSUSP", 11, TTY_OP_CHAR }, + { "REPRINT", 12, TTY_OP_CHAR }, + { "WERASE", 13, TTY_OP_CHAR }, + { "LNEXT", 14, TTY_OP_CHAR }, + { "FLUSH", 15, TTY_OP_CHAR }, + { "SWTCH", 16, TTY_OP_CHAR }, + { "STATUS", 17, TTY_OP_CHAR }, + { "DISCARD", 18, TTY_OP_CHAR }, + { "IGNPAR", 30, TTY_OP_BOOL }, + { "PARMRK", 31, TTY_OP_BOOL }, + { "INPCK", 32, TTY_OP_BOOL }, + { "ISTRIP", 33, TTY_OP_BOOL }, + { "INLCR", 34, TTY_OP_BOOL }, + { "IGNCR", 35, TTY_OP_BOOL }, + { "ICRNL", 36, TTY_OP_BOOL }, + { "IUCLC", 37, TTY_OP_BOOL }, + { "IXON", 38, TTY_OP_BOOL }, + { "IXANY", 39, TTY_OP_BOOL }, + { "IXOFF", 40, TTY_OP_BOOL }, + { "IMAXBEL", 41, TTY_OP_BOOL }, + { "ISIG", 50, TTY_OP_BOOL }, + { "ICANON", 51, TTY_OP_BOOL }, + { "XCASE", 52, TTY_OP_BOOL }, + { "ECHO", 53, TTY_OP_BOOL }, + { "ECHOE", 54, TTY_OP_BOOL }, + { "ECHOK", 55, TTY_OP_BOOL }, + { "ECHONL", 56, TTY_OP_BOOL }, + { "NOFLSH", 57, TTY_OP_BOOL }, + { "TOSTOP", 58, TTY_OP_BOOL }, + { "IEXTEN", 59, TTY_OP_BOOL }, + { "ECHOCTL", 60, TTY_OP_BOOL }, + { "ECHOKE", 61, TTY_OP_BOOL }, + { "PENDIN", 62, TTY_OP_BOOL }, /* XXX is this a real mode? */ + { "OPOST", 70, TTY_OP_BOOL }, + { "OLCUC", 71, TTY_OP_BOOL }, + { "ONLCR", 72, TTY_OP_BOOL }, + { "OCRNL", 73, TTY_OP_BOOL }, + { "ONOCR", 74, TTY_OP_BOOL }, + { "ONLRET", 75, TTY_OP_BOOL }, + { "CS7", 90, TTY_OP_BOOL }, + { "CS8", 91, TTY_OP_BOOL }, + { "PARENB", 92, TTY_OP_BOOL }, + { "PARODD", 93, TTY_OP_BOOL } +}; + +/* Miscellaneous other tty-related constants. */ +#define SSH_TTY_OP_END 0 +/* The opcodes for ISPEED/OSPEED differ between SSH-1 and SSH-2. */ +#define SSH1_TTY_OP_ISPEED 192 +#define SSH1_TTY_OP_OSPEED 193 +#define SSH2_TTY_OP_ISPEED 128 +#define SSH2_TTY_OP_OSPEED 129 + +/* Helper functions for parsing tty-related config. */ +static unsigned int ssh_tty_parse_specchar(char *s) +{ + unsigned int ret; + if (*s) { + char *next = NULL; + ret = ctrlparse(s, &next); + if (!next) ret = s[0]; + } else { + ret = 255; /* special value meaning "don't set" */ + } + return ret; +} +static unsigned int ssh_tty_parse_boolean(char *s) +{ + if (stricmp(s, "yes") == 0 || + stricmp(s, "on") == 0 || + stricmp(s, "true") == 0 || + stricmp(s, "+") == 0) + return 1; /* true */ + else if (stricmp(s, "no") == 0 || + stricmp(s, "off") == 0 || + stricmp(s, "false") == 0 || + stricmp(s, "-") == 0) + return 0; /* false */ + else + return (atoi(s) != 0); +} + #define translate(x) if (type == x) return #x #define translatec(x,ctx) if (type == x && (pkt_ctx & ctx)) return #x static char *ssh1_pkt_type(int type) @@ -256,18 +360,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 +411,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 *); @@ -364,34 +461,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) @@ -534,15 +608,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 SSH-2 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 @@ -815,6 +889,29 @@ static void end_log_omission(Ssh ssh, struct Packet *pkt) pkt->logmode = PKTLOG_EMIT; } +/* Helper function for common bits of parsing cfg.ttymodes. */ +static void parse_ttymodes(Ssh ssh, char *modes, + void (*do_mode)(void *data, char *mode, char *val), + void *data) +{ + while (*modes) { + char *t = strchr(modes, '\t'); + char *m = snewn(t-modes+1, char); + char *val; + strncpy(m, modes, t-modes); + m[t-modes] = '\0'; + if (*(t+1) == 'A') + val = get_ttymode(ssh->frontend, m); + else + val = dupstr(t+2); + if (val) + do_mode(data, m, val); + sfree(m); + sfree(val); + modes += strlen(modes) + 1; + } +} + static int ssh_channelcmp(void *av, void *bv) { struct ssh_channel *a = (struct ssh_channel *) av; @@ -982,7 +1079,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 +1379,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) -{ - 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) +static int s_wrpkt_prepare(Ssh ssh, struct Packet *pkt, int *offset_p) { - int pad, biglen, i; + int pad, biglen, i, pktoffs; unsigned long crc; #ifdef __SC__ /* @@ -1324,12 +1394,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 +1406,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 +1511,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 +1519,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 +1560,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 +1576,74 @@ static void sha_uint32(SHA_State * s, unsigned i) } /* - * SSH-2 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) +static void ssh_pkt_addbyte(struct Packet *pkt, unsigned char byte) { - ssh2_pkt_adddata(pkt, &byte, 1); -} -static struct Packet *ssh2_pkt_init(int pkt_type) -{ - 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,11 +1667,40 @@ 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 SSH-2 final-form packet: compress it, encrypt it, * put the MAC on it. Final packet, ready to be sent, is stored in @@ -1661,7 +1713,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; @@ -1745,8 +1797,19 @@ static int ssh2_pkt_construct(Ssh ssh, struct Packet *pkt) * ssh2_pkt_send() or ssh2_pkt_defer() either go straight to one of * these or get queued, and then when the queue is later emptied * the packets are all passed to defer_noqueue(). + * + * When using a CBC-mode cipher, it's necessary to ensure that an + * attacker can't provide data to be encrypted using an IV that they + * know. We ensure this by prefixing each packet that might contain + * user data with an SSH_MSG_IGNORE. This is done using the deferral + * mechanism, so in this case send_noqueue() ends up redirecting to + * defer_noqueue(). If you don't like this inefficiency, don't use + * CBC. */ +static void ssh2_pkt_defer_noqueue(Ssh, struct Packet *, int); +static void ssh_pkt_defersend(Ssh); + /* * Send an SSH-2 packet immediately, without queuing or deferring. */ @@ -1754,6 +1817,12 @@ static void ssh2_pkt_send_noqueue(Ssh ssh, struct Packet *pkt) { int len; int backlog; + if (ssh->cscipher != NULL && (ssh->cscipher->flags & SSH_CIPHER_IS_CBC)) { + /* We need to send two packets, so use the deferral mechanism. */ + ssh2_pkt_defer_noqueue(ssh, pkt, FALSE); + ssh_pkt_defersend(ssh); + return; + } len = ssh2_pkt_construct(ssh, pkt); backlog = sk_write(ssh->s, (char *)pkt->data, len); if (backlog > SSH_MAX_BACKLOG) @@ -1771,9 +1840,19 @@ static void ssh2_pkt_send_noqueue(Ssh ssh, struct Packet *pkt) /* * Defer an SSH-2 packet. */ -static void ssh2_pkt_defer_noqueue(Ssh ssh, struct Packet *pkt) +static void ssh2_pkt_defer_noqueue(Ssh ssh, struct Packet *pkt, int noignore) { - int len = ssh2_pkt_construct(ssh, pkt); + int len; + if (ssh->cscipher != NULL && (ssh->cscipher->flags & SSH_CIPHER_IS_CBC) && + ssh->deferred_len == 0 && !noignore) { + /* + * Interpose an SSH_MSG_IGNORE to ensure that user data don't + * get encrypted with a known IV. + */ + struct Packet *ipkt = ssh2_pkt_init(SSH2_MSG_IGNORE); + ssh2_pkt_defer_noqueue(ssh, ipkt, TRUE); + } + len = ssh2_pkt_construct(ssh, pkt); 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, @@ -1823,7 +1902,7 @@ static void ssh2_pkt_defer(Ssh ssh, struct Packet *pkt) if (ssh->queueing) ssh2_pkt_queue(ssh, pkt); else - ssh2_pkt_defer_noqueue(ssh, pkt); + ssh2_pkt_defer_noqueue(ssh, pkt, FALSE); } #endif @@ -1871,7 +1950,7 @@ static void ssh2_pkt_queuesend(Ssh ssh) assert(!ssh->queueing); for (i = 0; i < ssh->queuelen; i++) - ssh2_pkt_defer_noqueue(ssh, ssh->queue[i]); + ssh2_pkt_defer_noqueue(ssh, ssh->queue[i], FALSE); ssh->queuelen = 0; ssh_pkt_defersend(ssh); @@ -2112,7 +2191,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 || @@ -2331,6 +2410,8 @@ static int do_ssh_init(Ssh ssh, unsigned char c) strcspn(verstring, "\015\012"), verstring); sk_write(ssh->s, verstring, strlen(verstring)); sfree(verstring); + if (ssh->version == 2) + do_ssh2_transport(ssh, NULL, -1, NULL); } logeventf(ssh, "Using SSH protocol version %d", ssh->version); @@ -2444,6 +2525,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; @@ -4485,6 +4567,27 @@ static void ssh1_smsg_exit_status(Ssh ssh, struct Packet *pktin) ssh_closing((Plug)ssh, NULL, 0, 0); } +/* Helper function to deal with sending tty modes for REQUEST_PTY */ +static void ssh1_send_ttymode(void *data, char *mode, char *val) +{ + struct Packet *pktout = (struct Packet *)data; + int i = 0; + unsigned int arg = 0; + while (strcmp(mode, ssh_ttymodes[i].mode) != 0) i++; + if (i == lenof(ssh_ttymodes)) return; + switch (ssh_ttymodes[i].type) { + case TTY_OP_CHAR: + arg = ssh_tty_parse_specchar(val); + break; + case TTY_OP_BOOL: + arg = ssh_tty_parse_boolean(val); + break; + } + ssh2_pkt_addbyte(pktout, ssh_ttymodes[i].opcode); + ssh2_pkt_addbyte(pktout, arg); +} + + static void do_ssh1_connection(Ssh ssh, unsigned char *in, int inlen, struct Packet *pktin) { @@ -4529,14 +4632,23 @@ static void do_ssh1_connection(Ssh ssh, unsigned char *in, int inlen, ssh->x11auth = x11_invent_auth(proto, sizeof(proto), data, sizeof(data), ssh->cfg.x11_auth); x11_get_real_auth(ssh->x11auth, ssh->cfg.x11_display); + /* + * Note that while we blank the X authentication data here, we don't + * take any special action to blank the start of an X11 channel, + * so using MIT-MAGIC-COOKIE-1 and actually opening an X connection + * without having session blanking enabled is likely to leak your + * cookie into the log. + */ if (ssh->v1_local_protoflags & SSH1_PROTOFLAG_SCREEN_NUMBER) { send_packet(ssh, SSH1_CMSG_X11_REQUEST_FORWARDING, - PKT_STR, proto, PKT_STR, data, + PKT_STR, proto, + PKTT_PASSWORD, PKT_STR, data, PKTT_OTHER, PKT_INT, x11_get_screen_number(ssh->cfg.x11_display), PKT_END); } else { send_packet(ssh, SSH1_CMSG_X11_REQUEST_FORWARDING, - PKT_STR, proto, PKT_STR, data, PKT_END); + PKT_STR, proto, + PKTT_PASSWORD, PKT_STR, data, PKTT_OTHER, PKT_END); } do { crReturnV; @@ -4558,19 +4670,26 @@ static void do_ssh1_connection(Ssh ssh, unsigned char *in, int inlen, ssh->packet_dispatch[SSH1_MSG_PORT_OPEN] = ssh1_msg_port_open; if (!ssh->cfg.nopty) { + struct Packet *pkt; /* Unpick the terminal-speed string. */ /* XXX perhaps we should allow no speeds to be sent. */ ssh->ospeed = 38400; ssh->ispeed = 38400; /* last-resort defaults */ sscanf(ssh->cfg.termspeed, "%d,%d", &ssh->ospeed, &ssh->ispeed); /* Send the pty request. */ - send_packet(ssh, SSH1_CMSG_REQUEST_PTY, - PKT_STR, ssh->cfg.termtype, - PKT_INT, ssh->term_height, - PKT_INT, ssh->term_width, - PKT_INT, 0, PKT_INT, 0, /* width,height in pixels */ - PKT_CHAR, 192, PKT_INT, ssh->ispeed, /* TTY_OP_ISPEED */ - PKT_CHAR, 193, PKT_INT, ssh->ospeed, /* TTY_OP_OSPEED */ - PKT_CHAR, 0, PKT_END); + pkt = ssh1_pkt_init(SSH1_CMSG_REQUEST_PTY); + ssh_pkt_addstring(pkt, ssh->cfg.termtype); + ssh_pkt_adduint32(pkt, ssh->term_height); + ssh_pkt_adduint32(pkt, ssh->term_width); + ssh_pkt_adduint32(pkt, 0); /* width in pixels */ + ssh_pkt_adduint32(pkt, 0); /* height in pixels */ + parse_ttymodes(ssh, ssh->cfg.ttymodes, + ssh1_send_ttymode, (void *)pkt); + ssh_pkt_addbyte(pkt, SSH1_TTY_OP_ISPEED); + ssh_pkt_adduint32(pkt, ssh->ispeed); + ssh_pkt_addbyte(pkt, SSH1_TTY_OP_OSPEED); + ssh_pkt_adduint32(pkt, ssh->ospeed); + ssh_pkt_addbyte(pkt, SSH_TTY_OP_END); + s_wrpkt(ssh, pkt); ssh->state = SSH_STATE_INTERMED; do { crReturnV; @@ -4926,6 +5045,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. */ @@ -5677,6 +5799,32 @@ 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 SSH-2 channel. */ @@ -5715,8 +5863,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) @@ -6237,6 +6387,26 @@ static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin) } } +/* Helper function to deal with sending tty modes for "pty-req" */ +static void ssh2_send_ttymode(void *data, char *mode, char *val) +{ + struct Packet *pktout = (struct Packet *)data; + int i = 0; + unsigned int arg = 0; + while (strcmp(mode, ssh_ttymodes[i].mode) != 0) i++; + if (i == lenof(ssh_ttymodes)) return; + switch (ssh_ttymodes[i].type) { + case TTY_OP_CHAR: + arg = ssh_tty_parse_specchar(val); + break; + case TTY_OP_BOOL: + arg = ssh_tty_parse_boolean(val); + break; + } + ssh2_pkt_addbyte(pktout, ssh_ttymodes[i].opcode); + ssh2_pkt_adduint32(pktout, arg); +} + /* * Handle the SSH-2 userauth and connection layers. */ @@ -6258,6 +6428,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, AUTH_TYPE_KEYBOARD_INTERACTIVE, AUTH_TYPE_KEYBOARD_INTERACTIVE_QUIET } type; + int done_service_req; int gotit, need_pw, can_pubkey, can_passwd, can_keyb_inter; int tried_pubkey_config, tried_agent; int kbd_inter_running, kbd_inter_refused; @@ -6285,16 +6456,33 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, crBegin(ssh->do_ssh2_authconn_crstate); - /* - * Request userauth protocol, and await a response to it. - */ - s->pktout = ssh2_pkt_init(SSH2_MSG_SERVICE_REQUEST); - ssh2_pkt_addstring(s->pktout, "ssh-userauth"); - ssh2_pkt_send(ssh, s->pktout); - crWaitUntilV(pktin); - if (pktin->type != SSH2_MSG_SERVICE_ACCEPT) { - bombout(("Server refused user authentication protocol")); - crStopV; + s->done_service_req = FALSE; + s->we_are_in = FALSE; + if (!ssh->cfg.ssh_no_userauth) { + /* + * Request userauth protocol, and await a response to it. + */ + s->pktout = ssh2_pkt_init(SSH2_MSG_SERVICE_REQUEST); + ssh2_pkt_addstring(s->pktout, "ssh-userauth"); + ssh2_pkt_send(ssh, s->pktout); + crWaitUntilV(pktin); + if (pktin->type == SSH2_MSG_SERVICE_ACCEPT) + s->done_service_req = TRUE; + } + if (!s->done_service_req) { + /* + * Request connection protocol directly, without authentication. + */ + s->pktout = ssh2_pkt_init(SSH2_MSG_SERVICE_REQUEST); + ssh2_pkt_addstring(s->pktout, "ssh-connection"); + ssh2_pkt_send(ssh, s->pktout); + crWaitUntilV(pktin); + if (pktin->type == SSH2_MSG_SERVICE_ACCEPT) { + s->we_are_in = TRUE; /* no auth required */ + } else { + bombout(("Server refused service request")); + crStopV; + } } /* @@ -6323,7 +6511,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, */ s->username[0] = '\0'; s->got_username = FALSE; - do { + while (!s->we_are_in) { /* * Get a username. */ @@ -7064,12 +7252,10 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, crStopV; } } - } while (!s->we_are_in); + } /* - * Now we're authenticated for the connection protocol. The - * connection protocol will automatically have started at this - * point; there's no need to send SERVICE_REQUEST. + * Now the connection protocol has started, one way or another. */ ssh->channels = newtree234(ssh_channelcmp); @@ -7153,7 +7339,16 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, ssh2_pkt_addbool(s->pktout, 1); /* want reply */ ssh2_pkt_addbool(s->pktout, 0); /* many connections */ ssh2_pkt_addstring(s->pktout, proto); + /* + * Note that while we blank the X authentication data here, we don't + * take any special action to blank the start of an X11 channel, + * so using MIT-MAGIC-COOKIE-1 and actually opening an X connection + * without having session blanking enabled is likely to leak your + * cookie into the log. + */ + dont_log_password(ssh, s->pktout, PKTLOG_BLANK); ssh2_pkt_addstring(s->pktout, data); + end_log_omission(ssh, s->pktout); ssh2_pkt_adduint32(s->pktout, x11_get_screen_number(ssh->cfg.x11_display)); ssh2_pkt_send(ssh, s->pktout); @@ -7222,9 +7417,11 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, ssh2_pkt_adduint32(s->pktout, 0); /* pixel width */ ssh2_pkt_adduint32(s->pktout, 0); /* pixel height */ ssh2_pkt_addstring_start(s->pktout); - ssh2_pkt_addbyte(s->pktout, 128); /* TTY_OP_ISPEED */ + parse_ttymodes(ssh, ssh->cfg.ttymodes, + ssh2_send_ttymode, (void *)s->pktout); + ssh2_pkt_addbyte(s->pktout, SSH2_TTY_OP_ISPEED); ssh2_pkt_adduint32(s->pktout, ssh->ispeed); - ssh2_pkt_addbyte(s->pktout, 129); /* TTY_OP_OSPEED */ + ssh2_pkt_addbyte(s->pktout, SSH2_TTY_OP_OSPEED); ssh2_pkt_adduint32(s->pktout, ssh->ospeed); ssh2_pkt_addstring_data(s->pktout, "\0", 1); /* TTY_OP_END */ ssh2_pkt_send(ssh, s->pktout); @@ -7414,30 +7611,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); } } @@ -7565,6 +7740,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); @@ -8049,6 +8227,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) {