X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/fef97f438c8c83ac625799c88e8f2da53d5f51cc..ccbfb941a6124efb75b68313c86dd195cfab575f:/ssh.c?ds=sidebyside diff --git a/ssh.c b/ssh.c index 3fae152e..e61654af 100644 --- a/ssh.c +++ b/ssh.c @@ -13,6 +13,28 @@ #include "ssh.h" +#define SSH_MSG_DISCONNECT 1 +#define SSH_SMSG_PUBLIC_KEY 2 +#define SSH_CMSG_SESSION_KEY 3 +#define SSH_CMSG_USER 4 +#define SSH_CMSG_AUTH_PASSWORD 9 +#define SSH_CMSG_REQUEST_PTY 10 +#define SSH_CMSG_EXEC_SHELL 12 +#define SSH_CMSG_STDIN_DATA 16 +#define SSH_SMSG_STDOUT_DATA 17 +#define SSH_SMSG_STDERR_DATA 18 +#define SSH_SMSG_SUCCESS 14 +#define SSH_SMSG_FAILURE 15 +#define SSH_SMSG_EXITSTATUS 20 +#define SSH_MSG_IGNORE 32 +#define SSH_CMSG_EXIT_CONFIRMATION 33 +#define SSH_MSG_DEBUG 36 +#define SSH_CMSG_AUTH_TIS 39 +#define SSH_SMSG_AUTH_TIS_CHALLENGE 40 +#define SSH_CMSG_AUTH_TIS_RESPONSE 41 + +#define SSH_AUTH_TIS 5 + /* Coroutine mechanics for the sillier bits of the code */ #define crBegin1 static int crLine = 0; #define crBegin2 switch(crLine) { case 0:; @@ -47,7 +69,8 @@ static char *savedhost; static enum { SSH_STATE_BEFORE_SIZE, SSH_STATE_INTERMED, - SSH_STATE_SESSION + SSH_STATE_SESSION, + SSH_STATE_CLOSED } ssh_state = SSH_STATE_BEFORE_SIZE; static int size_needed = FALSE; @@ -75,7 +98,6 @@ static int s_read (char *buf, int len) { static void c_write (char *buf, int len) { while (len--) { int new_head = (inbuf_head + 1) & INBUF_MASK; - int c = (unsigned char) *buf; if (new_head != inbuf_reap) { inbuf[inbuf_head] = *buf++; inbuf_head = new_head; @@ -100,10 +122,8 @@ static void ssh_size(void); static void ssh_gotdata(unsigned char *data, int datalen) { static long len, biglen, to_read; - static unsigned char c, *p; + static unsigned char *p; static int i, pad; - static char padding[8]; - static unsigned char word[4]; crBegin; while (1) { @@ -156,8 +176,10 @@ static void ssh_gotdata(unsigned char *data, int datalen) { pktin.type = pktin.data[pad]; pktin.body = pktin.data+pad+1; - if (pktin.type == 36) { /* SSH_MSG_DEBUG */ + if (pktin.type == SSH_MSG_DEBUG) { /* FIXME: log it */ + } else if (pktin.type == SSH_MSG_IGNORE) { + /* do nothing */; } else ssh_protocol(NULL, 0, 1); } @@ -174,8 +196,8 @@ static void s_wrpkt_start(int type, int len) { pktout.length = len-5; if (pktout.maxlen < biglen) { pktout.maxlen = biglen; - pktout.data = (pktout.data == NULL ? malloc(biglen) : - realloc(pktout.data, biglen)); + pktout.data = (pktout.data == NULL ? malloc(biglen+4) : + realloc(pktout.data, biglen+4)); if (!pktout.data) fatalbox("Out of memory"); } @@ -213,6 +235,21 @@ static void s_wrpkt(void) { s_write(pktout.data, biglen+4); } +static int ssh_versioncmp(char *a, char *b) { + char *ae, *be; + unsigned long av, bv; + + av = strtoul(a, &ae, 10); + bv = strtoul(b, &be, 10); + if (av != bv) return (av < bv ? -1 : +1); + if (*ae == '.') ae++; + if (*be == '.') be++; + av = strtoul(ae, &ae, 10); + bv = strtoul(be, &be, 10); + if (av != bv) return (av < bv ? -1 : +1); + return 0; +} + static int do_ssh_init(void) { char c; char version[10]; @@ -248,8 +285,8 @@ static int do_ssh_init(void) { break; } - sprintf(vstring, "SSH-%s-7.7.7\n", - (strcmp(version, "1.5") <= 0 ? version : "1.5")); + sprintf(vstring, "SSH-%s-PuTTY\n", + (ssh_versioncmp(version, "1.5") <= 0 ? version : "1.5")); s_write(vstring, strlen(vstring)); return 1; } @@ -261,8 +298,12 @@ static void ssh_protocol(unsigned char *in, int inlen, int ispkt) { unsigned char cookie[8]; struct RSAKey servkey, hostkey; struct MD5Context md5c; + static unsigned long supported_ciphers_mask, supported_auths_mask; + int cipher_type; extern struct ssh_cipher ssh_3des; + extern struct ssh_cipher ssh_des; + extern struct ssh_cipher ssh_blowfish; crBegin; @@ -271,7 +312,7 @@ static void ssh_protocol(unsigned char *in, int inlen, int ispkt) { while (!ispkt) crReturnV; - if (pktin.type != 2) + if (pktin.type != SSH_SMSG_PUBLIC_KEY) fatalbox("Public key packet not received"); memcpy(cookie, pktin.body, 8); @@ -282,6 +323,16 @@ static void ssh_protocol(unsigned char *in, int inlen, int ispkt) { j = makekey(pktin.body+8+i, &hostkey, &keystr2); + supported_ciphers_mask = ((pktin.body[12+i+j] << 24) | + (pktin.body[13+i+j] << 16) | + (pktin.body[14+i+j] << 8) | + (pktin.body[15+i+j])); + + supported_auths_mask = ((pktin.body[16+i+j] << 24) | + (pktin.body[17+i+j] << 16) | + (pktin.body[18+i+j] << 8) | + (pktin.body[19+i+j])); + MD5Update(&md5c, keystr2, hostkey.bytes); MD5Update(&md5c, keystr1, servkey.bytes); MD5Update(&md5c, pktin.body, 8); @@ -313,8 +364,16 @@ static void ssh_protocol(unsigned char *in, int inlen, int ispkt) { rsaencrypt(rsabuf, hostkey.bytes, &servkey); } - s_wrpkt_start(3, len+15); - pktout.body[0] = 3; /* SSH_CIPHER_3DES */ + cipher_type = cfg.cipher == CIPHER_BLOWFISH ? SSH_CIPHER_BLOWFISH : + cfg.cipher == CIPHER_DES ? SSH_CIPHER_DES : + SSH_CIPHER_3DES; + if ((supported_ciphers_mask & (1 << cipher_type)) == 0) { + c_write("Selected cipher not supported, falling back to 3DES\r\n", 53); + cipher_type = SSH_CIPHER_3DES; + } + + s_wrpkt_start(SSH_CMSG_SESSION_KEY, len+15); + pktout.body[0] = cipher_type; memcpy(pktout.body+1, cookie, 8); pktout.body[9] = (len*8) >> 8; pktout.body[10] = (len*8) & 0xFF; @@ -325,12 +384,14 @@ static void ssh_protocol(unsigned char *in, int inlen, int ispkt) { free(rsabuf); - cipher = &ssh_3des; + cipher = cipher_type == SSH_CIPHER_BLOWFISH ? &ssh_blowfish : + cipher_type == SSH_CIPHER_DES ? &ssh_des : + &ssh_3des; cipher->sesskey(session_key); do { crReturnV; } while (!ispkt); - if (pktin.type != 14) + if (pktin.type != SSH_SMSG_SUCCESS) fatalbox("Encryption not successfully enabled"); fflush(stdout); @@ -380,7 +441,7 @@ static void ssh_protocol(unsigned char *in, int inlen, int ispkt) { sprintf(stuff, "Sent username \"%s\".\r\n", username); c_write(stuff, strlen(stuff)); } - s_wrpkt_start(4, 4+strlen(username)); + s_wrpkt_start(SSH_CMSG_USER, 4+strlen(username)); pktout.body[0] = pktout.body[1] = pktout.body[2] = 0; pktout.body[3] = strlen(username); memcpy(pktout.body+4, username, strlen(username)); @@ -389,11 +450,37 @@ static void ssh_protocol(unsigned char *in, int inlen, int ispkt) { do { crReturnV; } while (!ispkt); - while (pktin.type == 15) { + while (pktin.type == SSH_SMSG_FAILURE) { static char password[100]; static int pos; static char c; - c_write("password: ", 10); + static int pwpkt_type; + + /* + * Show password prompt, having first obtained it via a TIS + * exchange if we're doing TIS authentication. + */ + pwpkt_type = SSH_CMSG_AUTH_PASSWORD; + if (pktin.type == SSH_SMSG_FAILURE && + cfg.try_tis_auth && + (supported_auths_mask & (1<= 0) { do { crReturnV; } while (ispkt); @@ -420,7 +507,7 @@ static void ssh_protocol(unsigned char *in, int inlen, int ispkt) { } } c_write("\r\n", 2); - s_wrpkt_start(9, 4+strlen(password)); + s_wrpkt_start(pwpkt_type, 4+strlen(password)); pktout.body[0] = pktout.body[1] = pktout.body[2] = 0; pktout.body[3] = strlen(password); memcpy(pktout.body+4, password, strlen(password)); @@ -436,7 +523,7 @@ static void ssh_protocol(unsigned char *in, int inlen, int ispkt) { if (!cfg.nopty) { i = strlen(cfg.termtype); - s_wrpkt_start(10, i+5*4+1); + s_wrpkt_start(SSH_CMSG_REQUEST_PTY, i+5*4+1); pktout.body[0] = (i >> 24) & 0xFF; pktout.body[1] = (i >> 16) & 0xFF; pktout.body[2] = (i >> 8) & 0xFF; @@ -455,14 +542,14 @@ static void ssh_protocol(unsigned char *in, int inlen, int ispkt) { s_wrpkt(); ssh_state = SSH_STATE_INTERMED; do { crReturnV; } while (!ispkt); - if (pktin.type != 14 && pktin.type != 15) { + if (pktin.type != SSH_SMSG_SUCCESS && pktin.type != SSH_SMSG_FAILURE) { fatalbox("Protocol confusion"); - } else if (pktin.type == 15) { + } else if (pktin.type == SSH_SMSG_FAILURE) { c_write("Server refused to allocate pty\r\n", 32); } } - s_wrpkt_start(12, 0); + s_wrpkt_start(SSH_CMSG_EXEC_SHELL, 0); s_wrpkt(); ssh_state = SSH_STATE_SESSION; @@ -472,27 +559,27 @@ static void ssh_protocol(unsigned char *in, int inlen, int ispkt) { while (1) { crReturnV; if (ispkt) { - if (pktin.type == 17 || pktin.type == 18) { + if (pktin.type == SSH_SMSG_STDOUT_DATA || + pktin.type == SSH_SMSG_STDERR_DATA) { long len = 0; for (i = 0; i < 4; i++) len = (len << 8) + pktin.body[i]; c_write(pktin.body+4, len); - } else if (pktin.type == 1) { - /* SSH_MSG_DISCONNECT: do nothing */ - } else if (pktin.type == 14) { - /* SSH_MSG_SUCCESS: may be from EXEC_SHELL on some servers */ - } else if (pktin.type == 15) { - /* SSH_MSG_FAILURE: may be from EXEC_SHELL on some servers + } else if (pktin.type == SSH_MSG_DISCONNECT) { + ssh_state = SSH_STATE_CLOSED; + } else if (pktin.type == SSH_SMSG_SUCCESS) { + /* may be from EXEC_SHELL on some servers */ + } else if (pktin.type == SSH_SMSG_FAILURE) { + /* may be from EXEC_SHELL on some servers * if no pty is available or in other odd cases. Ignore */ - } else if (pktin.type == 20) { - /* EXITSTATUS */ - s_wrpkt_start(33, 0); + } else if (pktin.type == SSH_SMSG_EXITSTATUS) { + s_wrpkt_start(SSH_CMSG_EXIT_CONFIRMATION, 0); s_wrpkt(); } else { fatalbox("Strange packet received: type %d", pktin.type); } } else { - s_wrpkt_start(16, 4+inlen); + s_wrpkt_start(SSH_CMSG_STDIN_DATA, 4+inlen); pktout.body[0] = (inlen >> 24) & 0xFF; pktout.body[1] = (inlen >> 16) & 0xFF; pktout.body[2] = (inlen >> 8) & 0xFF; @@ -647,6 +734,7 @@ static int ssh_msg (WPARAM wParam, LPARAM lParam) { return 1; case FD_CLOSE: s = INVALID_SOCKET; + ssh_state = SSH_STATE_CLOSED; return 0; } return 1; /* shouldn't happen, but WTF */ @@ -668,6 +756,7 @@ static void ssh_send (char *buf, int len) { static void ssh_size(void) { switch (ssh_state) { case SSH_STATE_BEFORE_SIZE: + case SSH_STATE_CLOSED: break; /* do nothing */ case SSH_STATE_INTERMED: size_needed = TRUE; /* buffer for later */