X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/5e0d7cb8ae28452ec8f23751a583d6477ec2fec0..4a8fc3c43e71f1500b1f1203431ce944a832110e:/ssh.c diff --git a/ssh.c b/ssh.c index 9354cba6..033a0d27 100644 --- a/ssh.c +++ b/ssh.c @@ -19,8 +19,9 @@ if ((flags & FLAG_STDERR) && (flags & FLAG_VERBOSE)) \ fprintf(stderr, "%s\n", s); } -#define bombout(msg) ( ssh_state = SSH_STATE_CLOSED, sk_close(s), \ - s = NULL, connection_fatal msg ) +#define bombout(msg) ( ssh_state = SSH_STATE_CLOSED, \ + (s ? sk_close(s), s = NULL : 0), \ + connection_fatal msg ) #define SSH1_MSG_DISCONNECT 1 /* 0x1 */ #define SSH1_SMSG_PUBLIC_KEY 2 /* 0x2 */ @@ -289,6 +290,8 @@ static int size_needed = FALSE, eof_needed = FALSE; static struct Packet pktin = { 0, 0, NULL, NULL, 0 }; static struct Packet pktout = { 0, 0, NULL, NULL, 0 }; +static unsigned char *deferred_send_data = NULL; +static int deferred_len = 0, deferred_size = 0; static int ssh_version; static void (*ssh_protocol)(unsigned char *in, int inlen, int ispkt); @@ -890,7 +893,13 @@ static void ssh2_pkt_addmp(Bignum b) { ssh2_pkt_addstring_data(p, len); sfree(p); } -static void ssh2_pkt_send(void) { + +/* + * Construct an SSH2 final-form packet: compress it, encrypt it, + * put the MAC on it. Final packet, ready to be sent, is stored in + * pktout.data. Total length is returned. + */ +static int ssh2_pkt_construct(void) { int cipherblk, maclen, padding, i; static unsigned long outgoing_sequence = 0; @@ -943,7 +952,48 @@ static void ssh2_pkt_send(void) { if (cscipher) cscipher->encrypt(pktout.data, pktout.length + padding); - sk_write(s, pktout.data, pktout.length + padding + maclen); + /* Ready-to-send packet starts at pktout.data. We return length. */ + return pktout.length + padding + maclen; +} + +/* + * Construct and send an SSH2 packet immediately. + */ +static void ssh2_pkt_send(void) { + int len = ssh2_pkt_construct(); + sk_write(s, pktout.data, len); +} + +/* + * Construct an SSH2 packet and add it to a deferred data block. + * Useful for sending multiple packets in a single sk_write() call, + * to prevent a traffic-analysing listener from being able to work + * out the length of any particular packet (such as the password + * packet). + * + * Note that because SSH2 sequence-numbers its packets, this can + * NOT be used as an m4-style `defer' allowing packets to be + * constructed in one order and sent in another. + */ +static void ssh2_pkt_defer(void) { + int len = ssh2_pkt_construct(); + if (deferred_len + len > deferred_size) { + deferred_size = deferred_len + len + 128; + deferred_send_data = srealloc(deferred_send_data, deferred_size); + } + memcpy(deferred_send_data+deferred_len, pktout.data, len); + deferred_len += len; +} + +/* + * Send the whole deferred data block constructed by + * ssh2_pkt_defer(). + */ +static void ssh2_pkt_defersend(void) { + sk_write(s, deferred_send_data, deferred_len); + deferred_len = deferred_size = 0; + sfree(deferred_send_data); + deferred_send_data = NULL; } #if 0 @@ -1152,11 +1202,11 @@ static int ssh_receive(Socket skt, int urgent, char *data, int len) { if (urgent==3) { /* A socket error has occurred. */ ssh_state = SSH_STATE_CLOSED; + sk_close(s); s = NULL; connection_fatal(data); - len = 0; - } - if (!len) { + return 0; + } else if (!len) { /* Connection has closed. */ ssh_state = SSH_STATE_CLOSED; sk_close(s); @@ -1218,7 +1268,7 @@ static char *connect_to_host(char *host, int port, char **realhost) /* * Open socket. */ - s = sk_new(addr, port, 0, ssh_receive); + s = sk_new(addr, port, 0, 1, ssh_receive); if ( (err = sk_socket_error(s)) ) return err; @@ -2453,9 +2503,9 @@ static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt) * transport. If we ever see a KEXINIT, we must go back to the * start. */ - do { + while (!(ispkt && pktin.type == SSH2_MSG_KEXINIT)) { crReturn(1); - } while (!(ispkt && pktin.type == SSH2_MSG_KEXINIT)); + } logevent("Server initiated key re-exchange"); goto begin_key_exchange; @@ -2631,13 +2681,46 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt) c_write("\r\n", 2); } + /* + * We send the password packet lumped tightly together with + * an SSH_MSG_IGNORE packet. The IGNORE packet contains a + * string long enough to make the total length of the two + * packets constant. This should ensure that a passive + * listener doing traffic analyis can't work out the length + * of the password. + * + * For this to work, we need an assumption about the + * maximum length of the password packet. I think 256 is + * pretty conservative. Anyone using a password longer than + * that probably doesn't have much to worry about from + * people who find out how long their password is! + */ ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); ssh2_pkt_addstring(username); ssh2_pkt_addstring("ssh-connection"); /* service requested */ ssh2_pkt_addstring("password"); ssh2_pkt_addbool(FALSE); ssh2_pkt_addstring(password); - ssh2_pkt_send(); + ssh2_pkt_defer(); + /* + * We'll include a string that's an exact multiple of the + * cipher block size. If the cipher is NULL for some + * reason, we don't do this trick at all because we gain + * nothing by it. + */ + if (cscipher) { + int i, j; + ssh2_pkt_init(SSH2_MSG_IGNORE); + ssh2_pkt_addstring_start(); + for (i = deferred_len; i <= 256; i += cscipher->blksize) { + for (j = 0; j < cscipher->blksize; j++) { + char c = (char)random_byte(); + ssh2_pkt_addstring_data(&c, 1); + } + } + ssh2_pkt_defer(); + } + ssh2_pkt_defersend(); crWaitUntilV(ispkt); if (pktin.type != SSH2_MSG_USERAUTH_SUCCESS) { @@ -2775,7 +2858,11 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt) */ ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST); ssh2_pkt_adduint32(mainchan->remoteid); /* recipient channel */ - if (*cfg.remote_cmd) { + if (cfg.ssh_subsys) { + ssh2_pkt_addstring("subsystem"); + ssh2_pkt_addbool(1); /* want reply */ + ssh2_pkt_addstring(cfg.remote_cmd); + } else if (*cfg.remote_cmd) { ssh2_pkt_addstring("exec"); ssh2_pkt_addbool(1); /* want reply */ ssh2_pkt_addstring(cfg.remote_cmd);