X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/972a41c8b8558a89b9acd5b19a53367de7b1a7c5..c52ee7f941fa3a31171c58879c11c9a744c25e93:/ssh.c?ds=sidebyside diff --git a/ssh.c b/ssh.c index 61e36ace..295d0e68 100644 --- a/ssh.c +++ b/ssh.c @@ -29,6 +29,11 @@ #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; @@ -96,6 +101,9 @@ static void c_write (char *buf, int len) { if (new_head != inbuf_reap) { inbuf[inbuf_head] = *buf++; inbuf_head = new_head; + } else { + term_out(); + if( inbuf_head == inbuf_reap ) len++; else break; } } } @@ -119,6 +127,7 @@ static void ssh_gotdata(unsigned char *data, int datalen) { static long len, biglen, to_read; static unsigned char *p; static int i, pad; + static unsigned long realcrc, gotcrc; crBegin; while (1) { @@ -144,8 +153,15 @@ static void ssh_gotdata(unsigned char *data, int datalen) { pktin.length = len; if (pktin.maxlen < biglen) { pktin.maxlen = biglen; +#ifdef MSCRYPTOAPI + /* Allocate enough buffer space for extra block + * for MS CryptEncrypt() */ + pktin.data = (pktin.data == NULL ? malloc(biglen+8) : + realloc(pktin.data, biglen+8)); +#else pktin.data = (pktin.data == NULL ? malloc(biglen) : - realloc(pktin.data, biglen)); + realloc(pktin.data, biglen)); +#endif if (!pktin.data) fatalbox("Out of memory"); } @@ -171,6 +187,15 @@ static void ssh_gotdata(unsigned char *data, int datalen) { pktin.type = pktin.data[pad]; pktin.body = pktin.data+pad+1; + realcrc = crc32(pktin.data, biglen-4); + gotcrc = (pktin.data[biglen-4] << 24); + gotcrc |= (pktin.data[biglen-3] << 16); + gotcrc |= (pktin.data[biglen-2] << 8); + gotcrc |= (pktin.data[biglen-1] << 0); + if (gotcrc != realcrc) { + fatalbox("Incorrect CRC received on packet"); + } + if (pktin.type == SSH_MSG_DEBUG) { /* FIXME: log it */ } else if (pktin.type == SSH_MSG_IGNORE) { @@ -191,8 +216,15 @@ static void s_wrpkt_start(int type, int len) { pktout.length = len-5; if (pktout.maxlen < biglen) { pktout.maxlen = biglen; +#ifdef MSCRYPTOAPI + /* Allocate enough buffer space for extra block + * for MS CryptEncrypt() */ + pktout.data = (pktout.data == NULL ? malloc(biglen+8) : + realloc(pktout.data, biglen+8)); +#else pktout.data = (pktout.data == NULL ? malloc(biglen+4) : realloc(pktout.data, biglen+4)); +#endif if (!pktout.data) fatalbox("Out of memory"); } @@ -246,9 +278,10 @@ static int ssh_versioncmp(char *a, char *b) { } static int do_ssh_init(void) { - char c; + char c, *vsp; char version[10]; - char vstring[40]; + char vstring[80]; + char vlog[sizeof(vstring)+20]; int i; #ifdef FWHACK @@ -264,11 +297,15 @@ static int do_ssh_init(void) { if (s_read(&c,1) != 1 || c != 'S') return 0; if (s_read(&c,1) != 1 || c != 'H') return 0; #endif + strcpy(vstring, "SSH-"); + vsp = vstring+4; if (s_read(&c,1) != 1 || c != '-') return 0; i = 0; while (1) { if (s_read(&c,1) != 1) return 0; + if (vsp < vstring+sizeof(vstring)-1) + *vsp++ = c; if (i >= 0) { if (c == '-') { version[i] = '\0'; @@ -280,8 +317,16 @@ static int do_ssh_init(void) { break; } + *vsp = 0; + sprintf(vlog, "Server version: %s", vstring); + vlog[strcspn(vlog, "\r\n")] = '\0'; + logevent(vlog); + sprintf(vstring, "SSH-%s-PuTTY\n", (ssh_versioncmp(version, "1.5") <= 0 ? version : "1.5")); + sprintf(vlog, "We claim version: %s", vstring); + vlog[strcspn(vlog, "\r\n")] = '\0'; + logevent(vlog); s_write(vstring, strlen(vstring)); return 1; } @@ -293,7 +338,7 @@ static void ssh_protocol(unsigned char *in, int inlen, int ispkt) { unsigned char cookie[8]; struct RSAKey servkey, hostkey; struct MD5Context md5c; - unsigned long supported_ciphers_mask; + static unsigned long supported_ciphers_mask, supported_auths_mask; int cipher_type; extern struct ssh_cipher ssh_3des; @@ -310,18 +355,44 @@ static void ssh_protocol(unsigned char *in, int inlen, int ispkt) { if (pktin.type != SSH_SMSG_PUBLIC_KEY) fatalbox("Public key packet not received"); - memcpy(cookie, pktin.body, 8); + logevent("Received public keys"); - MD5Init(&md5c); + memcpy(cookie, pktin.body, 8); i = makekey(pktin.body+8, &servkey, &keystr1); 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]); + /* + * Hash the host key and print the hash in the log box. Just as + * a last resort in case the registry's host key checking is + * compromised, we'll allow the user some ability to verify + * host keys by eye. + */ + MD5Init(&md5c); + MD5Update(&md5c, keystr2, hostkey.bytes); + MD5Final(session_id, &md5c); + { + char logmsg[80]; + int i; + logevent("Host key MD5 is:"); + strcpy(logmsg, " "); + for (i = 0; i < 16; i++) + sprintf(logmsg+strlen(logmsg), "%02x", session_id[i]); + logevent(logmsg); + } + + 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])); + + MD5Init(&md5c); MD5Update(&md5c, keystr2, hostkey.bytes); MD5Update(&md5c, keystr1, servkey.bytes); @@ -338,7 +409,21 @@ static void ssh_protocol(unsigned char *in, int inlen, int ispkt) { if (!rsabuf) fatalbox("Out of memory"); - verify_ssh_host_key(savedhost, &hostkey); + /* + * Verify the host key. + */ + { + /* + * First format the key into a string. + */ + int len = rsastr_len(&hostkey); + char *keystr = malloc(len); + if (!keystr) + fatalbox("Out of memory"); + rsastr_fmt(keystr, &hostkey); + verify_ssh_host_key(savedhost, keystr); + free(keystr); + } for (i=0; i<32; i++) { rsabuf[i] = session_key[i]; @@ -354,6 +439,8 @@ static void ssh_protocol(unsigned char *in, int inlen, int ispkt) { rsaencrypt(rsabuf, hostkey.bytes, &servkey); } + logevent("Encrypted session key"); + cipher_type = cfg.cipher == CIPHER_BLOWFISH ? SSH_CIPHER_BLOWFISH : cfg.cipher == CIPHER_DES ? SSH_CIPHER_DES : SSH_CIPHER_3DES; @@ -361,6 +448,11 @@ static void ssh_protocol(unsigned char *in, int inlen, int ispkt) { c_write("Selected cipher not supported, falling back to 3DES\r\n", 53); cipher_type = SSH_CIPHER_3DES; } + switch (cipher_type) { + case SSH_CIPHER_3DES: logevent("Using 3DES encryption"); break; + case SSH_CIPHER_DES: logevent("Using single-DES encryption"); break; + case SSH_CIPHER_BLOWFISH: logevent("Using Blowfish encryption"); break; + } s_wrpkt_start(SSH_CMSG_SESSION_KEY, len+15); pktout.body[0] = cipher_type; @@ -371,6 +463,7 @@ static void ssh_protocol(unsigned char *in, int inlen, int ispkt) { pktout.body[len+11] = pktout.body[len+12] = 0; /* protocol flags */ pktout.body[len+13] = pktout.body[len+14] = 0; s_wrpkt(); + logevent("Trying to enable encryption..."); free(rsabuf); @@ -384,6 +477,8 @@ static void ssh_protocol(unsigned char *in, int inlen, int ispkt) { if (pktin.type != SSH_SMSG_SUCCESS) fatalbox("Encryption not successfully enabled"); + logevent("Successfully started encryption"); + fflush(stdout); { static char username[100]; @@ -415,7 +510,8 @@ static void ssh_protocol(unsigned char *in, int inlen, int ispkt) { exit(0); break; default: - if (c >= ' ' && c <= '~' && pos < 40) { + if (((c >= ' ' && c <= '~') || + ((unsigned char)c >= 160)) && pos < 40) { username[pos++] = c; c_write(&c, 1); } @@ -432,6 +528,11 @@ static void ssh_protocol(unsigned char *in, int inlen, int ispkt) { c_write(stuff, strlen(stuff)); } s_wrpkt_start(SSH_CMSG_USER, 4+strlen(username)); + { + char userlog[20+sizeof(username)]; + sprintf(userlog, "Sent username \"%s\"", username); + logevent(userlog); + } pktout.body[0] = pktout.body[1] = pktout.body[2] = 0; pktout.body[3] = strlen(username); memcpy(pktout.body+4, username, strlen(username)); @@ -444,7 +545,36 @@ static void ssh_protocol(unsigned char *in, int inlen, int ispkt) { 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); @@ -465,26 +595,31 @@ static void ssh_protocol(unsigned char *in, int inlen, int ispkt) { exit(0); break; default: - if (c >= ' ' && c <= '~' && pos < 40) + if (((c >= ' ' && c <= '~') || + ((unsigned char)c >= 160)) && pos < 40) password[pos++] = c; break; } } c_write("\r\n", 2); - s_wrpkt_start(SSH_CMSG_AUTH_PASSWORD, 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)); s_wrpkt(); + logevent("Sent password"); memset(password, 0, strlen(password)); do { crReturnV; } while (!ispkt); if (pktin.type == 15) { c_write("Access denied\r\n", 15); + logevent("Authentication refused"); } else if (pktin.type != 14) { fatalbox("Strange packet received, type %d", pktin.type); } } + logevent("Authentication successful"); + if (!cfg.nopty) { i = strlen(cfg.termtype); s_wrpkt_start(SSH_CMSG_REQUEST_PTY, i+5*4+1); @@ -506,15 +641,17 @@ 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 != SSH_MSG_SUCCESS && pktin.type != SSH_MSG_FAILURE) { + if (pktin.type != SSH_SMSG_SUCCESS && pktin.type != SSH_SMSG_FAILURE) { fatalbox("Protocol confusion"); - } else if (pktin.type == SSH_MSG_FAILURE) { + } else if (pktin.type == SSH_SMSG_FAILURE) { c_write("Server refused to allocate pty\r\n", 32); } + logevent("Allocated pty"); } s_wrpkt_start(SSH_CMSG_EXEC_SHELL, 0); s_wrpkt(); + logevent("Started session"); ssh_state = SSH_STATE_SESSION; if (size_needed) @@ -531,9 +668,10 @@ static void ssh_protocol(unsigned char *in, int inlen, int ispkt) { c_write(pktin.body+4, len); } else if (pktin.type == SSH_MSG_DISCONNECT) { ssh_state = SSH_STATE_CLOSED; - } else if (pktin.type == SSH_MSG_SUCCESS) { + logevent("Received disconnect request"); + } else if (pktin.type == SSH_SMSG_SUCCESS) { /* may be from EXEC_SHELL on some servers */ - } else if (pktin.type == SSH_MSG_FAILURE) { + } 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 == SSH_SMSG_EXITSTATUS) { @@ -573,6 +711,11 @@ static char *ssh_init (HWND hwnd, char *host, int port, char **realhost) { char *FWhost; int FWport; #endif + +#ifdef MSCRYPTOAPI + if(crypto_startup() == 0) + return "Microsoft high encryption pack not installed!"; +#endif savedhost = malloc(1+strlen(host)); if (!savedhost) @@ -677,14 +820,22 @@ static int ssh_msg (WPARAM wParam, LPARAM lParam) { int ret; char buf[256]; - if (s == INVALID_SOCKET) /* how the hell did we get here?! */ - return -5000; + /* + * Because reading less than the whole of the available pending + * data can generate an FD_READ event, we need to allow for the + * possibility that FD_READ may arrive with FD_CLOSE already in + * the queue; so it's possible that we can get here even with s + * invalid. If so, we return 1 and don't worry about it. + */ + if (s == INVALID_SOCKET) + return 1; if (WSAGETSELECTERROR(lParam) != 0) return -WSAGETSELECTERROR(lParam); switch (WSAGETSELECTEVENT(lParam)) { case FD_READ: + case FD_CLOSE: ret = recv(s, buf, sizeof(buf), 0); if (ret < 0 && WSAGetLastError() == WSAEWOULDBLOCK) return 1; @@ -692,14 +843,10 @@ static int ssh_msg (WPARAM wParam, LPARAM lParam) { return -10000-WSAGetLastError(); if (ret == 0) { s = INVALID_SOCKET; - return 0; /* can't happen, in theory */ + return 0; } ssh_gotdata (buf, ret); return 1; - case FD_CLOSE: - s = INVALID_SOCKET; - ssh_state = SSH_STATE_CLOSED; - return 0; } return 1; /* shouldn't happen, but WTF */ }