X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/0e443c99edd2dd3bed4d916f1209e71c1629f2e1..b3d375b244187cd77f45dc013668e6273e133179:/ssh.c diff --git a/ssh.c b/ssh.c index d1a2c80f..0616effd 100644 --- a/ssh.c +++ b/ssh.c @@ -12,6 +12,10 @@ #include "putty.h" #include "tree234.h" #include "ssh.h" +#ifndef NO_GSSAPI +#include "sshgssc.h" +#include "sshgss.h" +#endif #ifndef FALSE #define FALSE 0 @@ -112,6 +116,12 @@ #define SSH2_MSG_CHANNEL_REQUEST 98 /* 0x62 */ #define SSH2_MSG_CHANNEL_SUCCESS 99 /* 0x63 */ #define SSH2_MSG_CHANNEL_FAILURE 100 /* 0x64 */ +#define SSH2_MSG_USERAUTH_GSSAPI_RESPONSE 60 +#define SSH2_MSG_USERAUTH_GSSAPI_TOKEN 61 +#define SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE 63 +#define SSH2_MSG_USERAUTH_GSSAPI_ERROR 64 +#define SSH2_MSG_USERAUTH_GSSAPI_ERRTOK 65 +#define SSH2_MSG_USERAUTH_GSSAPI_MIC 66 /* * Packet type contexts, so that ssh2_pkt_type can correctly decode @@ -127,6 +137,7 @@ typedef enum { SSH2_PKTCTX_NOAUTH, SSH2_PKTCTX_PUBLICKEY, SSH2_PKTCTX_PASSWORD, + SSH2_PKTCTX_GSSAPI, SSH2_PKTCTX_KBDINTER } Pkt_ACtx; @@ -183,6 +194,8 @@ static const char *const ssh2_disconnect_reasons[] = { #define BUG_SSH2_DERIVEKEY 32 #define BUG_SSH2_REKEY 64 #define BUG_SSH2_PK_SESSIONID 128 +#define BUG_SSH2_MAXPKT 256 +#define BUG_CHOKES_ON_SSH2_IGNORE 512 /* * Codes for terminal modes. @@ -338,6 +351,12 @@ static char *ssh1_pkt_type(int type) } static char *ssh2_pkt_type(Pkt_KCtx pkt_kctx, Pkt_ACtx pkt_actx, int type) { + translatea(SSH2_MSG_USERAUTH_GSSAPI_RESPONSE,SSH2_PKTCTX_GSSAPI); + translatea(SSH2_MSG_USERAUTH_GSSAPI_TOKEN,SSH2_PKTCTX_GSSAPI); + translatea(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE,SSH2_PKTCTX_GSSAPI); + translatea(SSH2_MSG_USERAUTH_GSSAPI_ERROR,SSH2_PKTCTX_GSSAPI); + translatea(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK,SSH2_PKTCTX_GSSAPI); + translatea(SSH2_MSG_USERAUTH_GSSAPI_MIC, SSH2_PKTCTX_GSSAPI); translate(SSH2_MSG_DISCONNECT); translate(SSH2_MSG_IGNORE); translate(SSH2_MSG_UNIMPLEMENTED); @@ -477,6 +496,16 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * * - OUR_V2_BIGWIN is the window size we advertise for the only * channel in a simple connection. It must be <= INT_MAX. + * + * - OUR_V2_MAXPKT is the official "maximum packet size" we send + * to the remote side. This actually has nothing to do with the + * size of the _packet_, but is instead a limit on the amount + * of data we're willing to receive in a single SSH2 channel + * data message. + * + * - OUR_V2_PACKETLIMIT is actually the maximum size of SSH + * _packet_ we're prepared to cope with. It must be a multiple + * of the cipher block size, and must be at least 35000. */ #define SSH1_BUFFER_LIMIT 32768 @@ -484,6 +513,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, #define OUR_V2_WINSIZE 16384 #define OUR_V2_BIGWIN 0x7fffffff #define OUR_V2_MAXPKT 0x4000UL +#define OUR_V2_PACKETLIMIT 0x9000UL /* Maximum length of passwords/passphrases (arbitrary) */ #define SSH_MAX_PASSWORD_LEN 100 @@ -560,10 +590,12 @@ struct ssh_channel { * A channel is completely finished with when all four bits are set. */ int closes; + /* + * True if this channel is causing the underlying connection to be + * throttled. + */ + int throttling_conn; union { - struct ssh1_data_channel { - int throttling; - } v1; struct ssh2_data_channel { bufchain outbuffer; unsigned remwindow, remmaxpkt; @@ -807,10 +839,10 @@ struct ssh_tag { Pkt_KCtx pkt_kctx; Pkt_ACtx pkt_actx; - void *x11auth; + struct X11Display *x11disp; int version; - int v1_throttle_count; + int conn_throttle_count; int overall_bufsize; int throttled_all; int v1_stdout_throttling; @@ -893,6 +925,11 @@ struct ssh_tag { int kex_in_progress; long next_rekey, last_rekey; char *deferred_rekey_reason; /* points to STATIC string; don't free */ + + /* + * Fully qualified host name, which we need if doing GSSAPI. + */ + char *fullhostname; }; #define logevent(s) logevent(ssh->frontend, s) @@ -1265,7 +1302,7 @@ static struct Packet *ssh1_rdpkt(Ssh ssh, unsigned char **data, int *datalen) PKT_INCOMING, st->pktin->type, ssh1_pkt_type(st->pktin->type), st->pktin->body, st->pktin->length, - nblanks, &blank); + nblanks, &blank, NULL); } crFinish(st->pktin); @@ -1287,90 +1324,162 @@ static struct Packet *ssh2_rdpkt(Ssh ssh, unsigned char **data, int *datalen) st->cipherblk = 8; if (st->cipherblk < 8) st->cipherblk = 8; + st->maclen = ssh->scmac ? ssh->scmac->len : 0; - st->pktin->data = snewn(st->cipherblk + APIEXTRA, unsigned char); + if (ssh->sccipher && (ssh->sccipher->flags & SSH_CIPHER_IS_CBC) && + ssh->scmac) { + /* + * When dealing with a CBC-mode cipher, we want to avoid the + * possibility of an attacker's tweaking the ciphertext stream + * so as to cause us to feed the same block to the block + * cipher more than once and thus leak information + * (VU#958563). The way we do this is not to take any + * decisions on the basis of anything we've decrypted until + * we've verified it with a MAC. That includes the packet + * length, so we just read data and check the MAC repeatedly, + * and when the MAC passes, see if the length we've got is + * plausible. + */ - /* - * Acquire and decrypt the first block of the packet. This will - * contain the length and padding details. - */ - for (st->i = st->len = 0; st->i < st->cipherblk; st->i++) { - while ((*datalen) == 0) - crReturn(NULL); - st->pktin->data[st->i] = *(*data)++; - (*datalen)--; - } + /* May as well allocate the whole lot now. */ + st->pktin->data = snewn(OUR_V2_PACKETLIMIT + st->maclen + APIEXTRA, + unsigned char); - if (ssh->sccipher) - ssh->sccipher->decrypt(ssh->sc_cipher_ctx, - st->pktin->data, st->cipherblk); + /* Read an amount corresponding to the MAC. */ + for (st->i = 0; st->i < st->maclen; st->i++) { + while ((*datalen) == 0) + crReturn(NULL); + st->pktin->data[st->i] = *(*data)++; + (*datalen)--; + } - /* - * Now get the length and padding figures. - */ - st->len = GET_32BIT(st->pktin->data); - st->pad = st->pktin->data[4]; + st->packetlen = 0; + { + unsigned char seq[4]; + ssh->scmac->start(ssh->sc_mac_ctx); + PUT_32BIT(seq, st->incoming_sequence); + ssh->scmac->bytes(ssh->sc_mac_ctx, seq, 4); + } - /* - * _Completely_ silly lengths should be stomped on before they - * do us any more damage. - */ - if (st->len < 0 || st->len > 35000 || st->pad < 4 || - st->len - st->pad < 1 || (st->len + 4) % st->cipherblk != 0) { - bombout(("Incoming packet was garbled on decryption")); - ssh_free_packet(st->pktin); - crStop(NULL); - } + for (;;) { /* Once around this loop per cipher block. */ + /* Read another cipher-block's worth, and tack it onto the end. */ + for (st->i = 0; st->i < st->cipherblk; st->i++) { + while ((*datalen) == 0) + crReturn(NULL); + st->pktin->data[st->packetlen+st->maclen+st->i] = *(*data)++; + (*datalen)--; + } + /* Decrypt one more block (a little further back in the stream). */ + ssh->sccipher->decrypt(ssh->sc_cipher_ctx, + st->pktin->data + st->packetlen, + st->cipherblk); + /* Feed that block to the MAC. */ + ssh->scmac->bytes(ssh->sc_mac_ctx, + st->pktin->data + st->packetlen, st->cipherblk); + st->packetlen += st->cipherblk; + /* See if that gives us a valid packet. */ + if (ssh->scmac->verresult(ssh->sc_mac_ctx, + st->pktin->data + st->packetlen) && + (st->len = GET_32BIT(st->pktin->data)) + 4 == st->packetlen) + break; + if (st->packetlen >= OUR_V2_PACKETLIMIT) { + bombout(("No valid incoming packet found")); + ssh_free_packet(st->pktin); + crStop(NULL); + } + } + st->pktin->maxlen = st->packetlen + st->maclen; + st->pktin->data = sresize(st->pktin->data, + st->pktin->maxlen + APIEXTRA, + unsigned char); + } else { + st->pktin->data = snewn(st->cipherblk + APIEXTRA, unsigned char); - /* - * This enables us to deduce the payload length. - */ - st->payload = st->len - st->pad - 1; + /* + * Acquire and decrypt the first block of the packet. This will + * contain the length and padding details. + */ + for (st->i = st->len = 0; st->i < st->cipherblk; st->i++) { + while ((*datalen) == 0) + crReturn(NULL); + st->pktin->data[st->i] = *(*data)++; + (*datalen)--; + } - st->pktin->length = st->payload + 5; + if (ssh->sccipher) + ssh->sccipher->decrypt(ssh->sc_cipher_ctx, + st->pktin->data, st->cipherblk); - /* - * So now we can work out the total packet length. - */ - st->packetlen = st->len + 4; - st->maclen = ssh->scmac ? ssh->scmac->len : 0; + /* + * Now get the length figure. + */ + st->len = GET_32BIT(st->pktin->data); - /* - * Allocate memory for the rest of the packet. - */ - st->pktin->maxlen = st->packetlen + st->maclen; - st->pktin->data = sresize(st->pktin->data, - st->pktin->maxlen + APIEXTRA, - unsigned char); + /* + * _Completely_ silly lengths should be stomped on before they + * do us any more damage. + */ + if (st->len < 0 || st->len > OUR_V2_PACKETLIMIT || + (st->len + 4) % st->cipherblk != 0) { + bombout(("Incoming packet was garbled on decryption")); + ssh_free_packet(st->pktin); + crStop(NULL); + } - /* - * Read and decrypt the remainder of the packet. - */ - for (st->i = st->cipherblk; st->i < st->packetlen + st->maclen; - st->i++) { - while ((*datalen) == 0) - crReturn(NULL); - st->pktin->data[st->i] = *(*data)++; - (*datalen)--; - } - /* Decrypt everything _except_ the MAC. */ - if (ssh->sccipher) - ssh->sccipher->decrypt(ssh->sc_cipher_ctx, - st->pktin->data + st->cipherblk, - st->packetlen - st->cipherblk); + /* + * So now we can work out the total packet length. + */ + st->packetlen = st->len + 4; - st->pktin->encrypted_len = st->packetlen; + /* + * Allocate memory for the rest of the packet. + */ + st->pktin->maxlen = st->packetlen + st->maclen; + st->pktin->data = sresize(st->pktin->data, + st->pktin->maxlen + APIEXTRA, + unsigned char); - /* - * Check the MAC. - */ - if (ssh->scmac - && !ssh->scmac->verify(ssh->sc_mac_ctx, st->pktin->data, st->len + 4, - st->incoming_sequence)) { - bombout(("Incorrect MAC received on packet")); + /* + * Read and decrypt the remainder of the packet. + */ + for (st->i = st->cipherblk; st->i < st->packetlen + st->maclen; + st->i++) { + while ((*datalen) == 0) + crReturn(NULL); + st->pktin->data[st->i] = *(*data)++; + (*datalen)--; + } + /* Decrypt everything _except_ the MAC. */ + if (ssh->sccipher) + ssh->sccipher->decrypt(ssh->sc_cipher_ctx, + st->pktin->data + st->cipherblk, + st->packetlen - st->cipherblk); + + /* + * Check the MAC. + */ + if (ssh->scmac + && !ssh->scmac->verify(ssh->sc_mac_ctx, st->pktin->data, + st->len + 4, st->incoming_sequence)) { + bombout(("Incorrect MAC received on packet")); + ssh_free_packet(st->pktin); + crStop(NULL); + } + } + /* Get and sanity-check the amount of random padding. */ + st->pad = st->pktin->data[4]; + if (st->pad < 4 || st->len - st->pad < 1) { + bombout(("Invalid padding length on received packet")); ssh_free_packet(st->pktin); crStop(NULL); } + /* + * This enables us to deduce the payload length. + */ + st->payload = st->len - st->pad - 1; + + st->pktin->length = st->payload + 5; + st->pktin->encrypted_len = st->packetlen; st->pktin->sequence = st->incoming_sequence++; @@ -1425,7 +1534,7 @@ static struct Packet *ssh2_rdpkt(Ssh ssh, unsigned char **data, int *datalen) ssh2_pkt_type(ssh->pkt_kctx, ssh->pkt_actx, st->pktin->type), st->pktin->data+6, st->pktin->length-6, - nblanks, &blank); + nblanks, &blank, &st->pktin->sequence); } crFinish(st->pktin); @@ -1450,7 +1559,7 @@ static int s_wrpkt_prepare(Ssh ssh, struct Packet *pkt, int *offset_p) 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); + pkt->nblanks, pkt->blanks, NULL); sfree(pkt->blanks); pkt->blanks = NULL; pkt->nblanks = 0; @@ -1490,7 +1599,8 @@ static int s_wrpkt_prepare(Ssh ssh, struct Packet *pkt, int *offset_p) static int s_write(Ssh ssh, void *data, int len) { if (ssh->logctx) - log_packet(ssh->logctx, PKT_OUTGOING, -1, NULL, data, len, 0, NULL); + log_packet(ssh->logctx, PKT_OUTGOING, -1, NULL, data, len, + 0, NULL, NULL); return sk_write(ssh->s, (char *)data, len); } @@ -1773,7 +1883,7 @@ static int ssh2_pkt_construct(Ssh ssh, struct Packet *pkt) log_packet(ssh->logctx, PKT_OUTGOING, pkt->data[5], ssh2_pkt_type(ssh->pkt_kctx, ssh->pkt_actx, pkt->data[5]), pkt->body, pkt->length - (pkt->body - pkt->data), - pkt->nblanks, pkt->blanks); + pkt->nblanks, pkt->blanks, &ssh->v2_outgoing_sequence); sfree(pkt->blanks); pkt->blanks = NULL; pkt->nblanks = 0; @@ -1903,7 +2013,8 @@ static void ssh2_pkt_defer_noqueue(Ssh ssh, struct Packet *pkt, int noignore) { int len; if (ssh->cscipher != NULL && (ssh->cscipher->flags & SSH_CIPHER_IS_CBC) && - ssh->deferred_len == 0 && !noignore) { + ssh->deferred_len == 0 && !noignore && + !(ssh->remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE)) { /* * Interpose an SSH_MSG_IGNORE to ensure that user data don't * get encrypted with a known IV. @@ -2033,7 +2144,8 @@ static void ssh2_pkt_send_with_padding(Ssh ssh, struct Packet *pkt, * unavailable, we don't do this trick at all, because we * gain nothing by it.) */ - if (ssh->cscipher) { + if (ssh->cscipher && + !(ssh->remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE)) { int stringlen, i; stringlen = (256 - ssh->deferred_len); @@ -2389,6 +2501,26 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring) ssh->remote_bugs |= BUG_SSH2_REKEY; logevent("We believe remote version has SSH-2 rekey bug"); } + + if (ssh->cfg.sshbug_maxpkt2 == FORCE_ON || + (ssh->cfg.sshbug_maxpkt2 == AUTO && + (wc_match("1.36_sshlib GlobalSCAPE", imp) || + wc_match("1.36 sshlib: GlobalScape", imp)))) { + /* + * This version ignores our makpkt and needs to be throttled. + */ + ssh->remote_bugs |= BUG_SSH2_MAXPKT; + logevent("We believe remote version ignores SSH-2 maximum packet size"); + } + + if (ssh->cfg.sshbug_ignore2 == FORCE_ON) { + /* + * Servers that don't support SSH2_MSG_IGNORE. Currently, + * none detected automatically. + */ + ssh->remote_bugs |= BUG_CHOKES_ON_SSH2_IGNORE; + logevent("We believe remote version has SSH-2 ignore bug"); + } } /* @@ -2631,7 +2763,7 @@ static void ssh_gotdata(Ssh ssh, unsigned char *data, int datalen) /* Log raw data, if we're in that mode. */ if (ssh->logctx) log_packet(ssh->logctx, PKT_INCOMING, -1, NULL, data, datalen, - 0, NULL); + 0, NULL, NULL); crBegin(ssh->ssh_gotdata_crstate); @@ -2734,6 +2866,8 @@ static int ssh_do_close(Ssh ssh, int notify_exit) del234(ssh->portfwds, pf); /* moving next one to index 0 */ free_portfwd(pf); } + freetree234(ssh->portfwds); + ssh->portfwds = NULL; } return ret; @@ -2824,12 +2958,30 @@ static const char *connect_to_host(Ssh ssh, char *host, int port, SockAddr addr; const char *err; - ssh->savedhost = snewn(1 + strlen(host), char); - strcpy(ssh->savedhost, host); + if (*ssh->cfg.loghost) { + char *colon; + + ssh->savedhost = dupstr(ssh->cfg.loghost); + ssh->savedport = 22; /* default ssh port */ - if (port < 0) - port = 22; /* default ssh port */ - ssh->savedport = port; + /* + * A colon suffix on savedhost also lets us affect + * savedport. + * + * (FIXME: do something about IPv6 address literals here.) + */ + colon = strrchr(ssh->savedhost, ':'); + if (colon) { + *colon++ = '\0'; + if (*colon) + ssh->savedport = atoi(colon); + } + } else { + ssh->savedhost = dupstr(host); + if (port < 0) + port = 22; /* default ssh port */ + ssh->savedport = port; + } /* * Try to find host. @@ -2843,6 +2995,7 @@ static const char *connect_to_host(Ssh ssh, char *host, int port, sk_addr_free(addr); return err; } + ssh->fullhostname = dupstr(*realhost); /* save in case of GSSAPI */ /* * Open socket. @@ -2867,20 +3020,28 @@ static const char *connect_to_host(Ssh ssh, char *host, int port, ssh_send_verstring(ssh, NULL); } + /* + * loghost, if configured, overrides realhost. + */ + if (*ssh->cfg.loghost) { + sfree(*realhost); + *realhost = dupstr(ssh->cfg.loghost); + } + return NULL; } /* * Throttle or unthrottle the SSH connection. */ -static void ssh1_throttle(Ssh ssh, int adjust) +static void ssh_throttle_conn(Ssh ssh, int adjust) { - int old_count = ssh->v1_throttle_count; - ssh->v1_throttle_count += adjust; - assert(ssh->v1_throttle_count >= 0); - if (ssh->v1_throttle_count && !old_count) { + int old_count = ssh->conn_throttle_count; + ssh->conn_throttle_count += adjust; + assert(ssh->conn_throttle_count >= 0); + if (ssh->conn_throttle_count && !old_count) { ssh_set_frozen(ssh, 1); - } else if (!ssh->v1_throttle_count && old_count) { + } else if (!ssh->conn_throttle_count && old_count) { ssh_set_frozen(ssh, 0); } } @@ -3300,7 +3461,8 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, fflush(stdout); /* FIXME eh? */ { - if (!*ssh->cfg.username) { + if (!get_remote_username(&ssh->cfg, s->username, + sizeof(s->username))) { int ret; /* need not be kept over crReturn */ s->cur_prompt = new_prompts(ssh->frontend); s->cur_prompt->to_server = TRUE; @@ -3325,9 +3487,6 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, memcpy(s->username, s->cur_prompt->prompts[0]->result, lenof(s->username)); free_prompts(s->cur_prompt); - } else { - strncpy(s->username, ssh->cfg.username, sizeof(s->username)); - s->username[sizeof(s->username)-1] = '\0'; } send_packet(ssh, SSH1_CMSG_USER, PKT_STR, s->username, PKT_END); @@ -4054,17 +4213,20 @@ int sshfwd_write(struct ssh_channel *c, char *buf, int len) void sshfwd_unthrottle(struct ssh_channel *c, int bufsize) { Ssh ssh = c->ssh; + int buflimit; if (ssh->state == SSH_STATE_CLOSED) return; if (ssh->version == 1) { - if (c->v.v1.throttling && bufsize < SSH1_BUFFER_LIMIT) { - c->v.v1.throttling = 0; - ssh1_throttle(ssh, -1); - } + buflimit = SSH1_BUFFER_LIMIT; } else { - ssh2_set_window(c, c->v.v2.locmaxwin - bufsize); + buflimit = c->v.v2.locmaxwin; + ssh2_set_window(c, bufsize < buflimit ? buflimit - bufsize : 0); + } + if (c->throttling_conn && bufsize <= buflimit) { + c->throttling_conn = 0; + ssh_throttle_conn(ssh, -1); } } @@ -4280,12 +4442,19 @@ static void ssh_setup_portfwd(Ssh ssh, const Config *cfg) epfrec = add234(ssh->portfwds, pfrec); if (epfrec != pfrec) { + if (epfrec->status == DESTROY) { + /* + * We already have a port forwarding up and running + * with precisely these parameters. Hence, no need + * to do anything; simply re-tag the existing one + * as KEEP. + */ + epfrec->status = KEEP; + } /* - * We already have a port forwarding with precisely - * these parameters. Hence, no need to do anything; - * simply tag the existing one as KEEP. + * Anything else indicates that there was a duplicate + * in our input, which we'll silently ignore. */ - epfrec->status = KEEP; free_portfwd(pfrec); } else { pfrec->status = CREATE; @@ -4493,7 +4662,7 @@ static void ssh1_smsg_stdout_stderr_data(Ssh ssh, struct Packet *pktin) string, stringlen); if (!ssh->v1_stdout_throttling && bufsize > SSH1_BUFFER_LIMIT) { ssh->v1_stdout_throttling = 1; - ssh1_throttle(ssh, +1); + ssh_throttle_conn(ssh, +1); } } @@ -4514,8 +4683,8 @@ static void ssh1_smsg_x11_open(Ssh ssh, struct Packet *pktin) c = snew(struct ssh_channel); c->ssh = ssh; - if (x11_init(&c->u.x11.s, ssh->cfg.x11_display, c, - ssh->x11auth, NULL, -1, &ssh->cfg) != NULL) { + if (x11_init(&c->u.x11.s, ssh->x11disp, c, + NULL, -1, &ssh->cfg) != NULL) { logevent("Opening X11 forward connection failed"); sfree(c); send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_FAILURE, @@ -4527,7 +4696,7 @@ static void ssh1_smsg_x11_open(Ssh ssh, struct Packet *pktin) c->halfopen = FALSE; c->localid = alloc_channel_id(ssh); c->closes = 0; - c->v.v1.throttling = 0; + c->throttling_conn = 0; c->type = CHAN_X11; /* identify channel type */ add234(ssh->channels, c); send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION, @@ -4556,7 +4725,7 @@ static void ssh1_smsg_agent_open(Ssh ssh, struct Packet *pktin) c->halfopen = FALSE; c->localid = alloc_channel_id(ssh); c->closes = 0; - c->v.v1.throttling = 0; + c->throttling_conn = 0; c->type = CHAN_AGENT; /* identify channel type */ c->u.a.lensofar = 0; add234(ssh->channels, c); @@ -4610,7 +4779,7 @@ static void ssh1_msg_port_open(Ssh ssh, struct Packet *pktin) c->halfopen = FALSE; c->localid = alloc_channel_id(ssh); c->closes = 0; - c->v.v1.throttling = 0; + c->throttling_conn = 0; c->type = CHAN_SOCKDATA; /* identify channel type */ add234(ssh->channels, c); send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION, @@ -4632,7 +4801,7 @@ static void ssh1_msg_channel_open_confirmation(Ssh ssh, struct Packet *pktin) c->remoteid = localid; c->halfopen = FALSE; c->type = CHAN_SOCKDATA; - c->v.v1.throttling = 0; + c->throttling_conn = 0; pfd_confirm(c->u.pfd.s); } @@ -4768,9 +4937,9 @@ static void ssh1_msg_channel_data(Ssh ssh, struct Packet *pktin) bufsize = 0; /* agent channels never back up */ break; } - if (!c->v.v1.throttling && bufsize > SSH1_BUFFER_LIMIT) { - c->v.v1.throttling = 1; - ssh1_throttle(ssh, +1); + if (!c->throttling_conn && bufsize > SSH1_BUFFER_LIMIT) { + c->throttling_conn = 1; + ssh_throttle_conn(ssh, +1); } } } @@ -4849,12 +5018,10 @@ static void do_ssh1_connection(Ssh ssh, unsigned char *in, int inlen, } } - if (ssh->cfg.x11_forward) { - char proto[20], data[64]; + if (ssh->cfg.x11_forward && + (ssh->x11disp = x11_setup_display(ssh->cfg.x11_display, + ssh->cfg.x11_auth, &ssh->cfg))) { logevent("Requesting X11 forwarding"); - 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, @@ -4864,14 +5031,19 @@ static void do_ssh1_connection(Ssh ssh, unsigned char *in, int inlen, */ if (ssh->v1_local_protoflags & SSH1_PROTOFLAG_SCREEN_NUMBER) { send_packet(ssh, SSH1_CMSG_X11_REQUEST_FORWARDING, - PKT_STR, proto, - PKTT_PASSWORD, PKT_STR, data, PKTT_OTHER, - PKT_INT, x11_get_screen_number(ssh->cfg.x11_display), + PKT_STR, ssh->x11disp->remoteauthprotoname, + PKTT_PASSWORD, + PKT_STR, ssh->x11disp->remoteauthdatastring, + PKTT_OTHER, + PKT_INT, ssh->x11disp->screennum, PKT_END); } else { send_packet(ssh, SSH1_CMSG_X11_REQUEST_FORWARDING, - PKT_STR, proto, - PKTT_PASSWORD, PKT_STR, data, PKTT_OTHER, PKT_END); + PKT_STR, ssh->x11disp->remoteauthprotoname, + PKTT_PASSWORD, + PKT_STR, ssh->x11disp->remoteauthdatastring, + PKTT_OTHER, + PKT_END); } do { crReturnV; @@ -6200,6 +6372,22 @@ static void ssh2_try_send_and_unthrottle(struct ssh_channel *c) } /* + * Set up most of a new ssh_channel for SSH-2. + */ +static void ssh2_channel_init(struct ssh_channel *c) +{ + Ssh ssh = c->ssh; + c->localid = alloc_channel_id(ssh); + c->closes = 0; + c->throttling_conn = FALSE; + c->v.v2.locwindow = c->v.v2.locmaxwin = c->v.v2.remlocwin = + ssh->cfg.ssh_simple ? OUR_V2_BIGWIN : OUR_V2_WINSIZE; + c->v.v2.winadj_head = c->v.v2.winadj_tail = NULL; + c->v.v2.throttle_state = UNTHROTTLED; + bufchain_init(&c->v.v2.outbuffer); +} + +/* * Potentially enlarge the window on an SSH-2 channel. */ static void ssh2_set_window(struct ssh_channel *c, int newwin) @@ -6215,6 +6403,15 @@ static void ssh2_set_window(struct ssh_channel *c, int newwin) return; /* + * If the remote end has a habit of ignoring maxpkt, limit the + * window so that it has no choice (assuming it doesn't ignore the + * window as well). + */ + if ((ssh->remote_bugs & BUG_SSH2_MAXPKT) && newwin > OUR_V2_MAXPKT) + newwin = OUR_V2_MAXPKT; + + + /* * Only send a WINDOW_ADJUST if there's significantly more window * available than the other end thinks there is. This saves us * sending a WINDOW_ADJUST for every character in a shell session. @@ -6276,6 +6473,54 @@ static void ssh2_set_window(struct ssh_channel *c, int newwin) } } +/* + * Find the channel associated with a message. If there's no channel, + * or it's not properly open, make a noise about it and return NULL. + */ +static struct ssh_channel *ssh2_channel_msg(Ssh ssh, struct Packet *pktin) +{ + unsigned localid = ssh_pkt_getuint32(pktin); + struct ssh_channel *c; + + c = find234(ssh->channels, &localid, ssh_channelfind); + if (!c || + (c->halfopen && pktin->type != SSH2_MSG_CHANNEL_OPEN_CONFIRMATION && + pktin->type != SSH2_MSG_CHANNEL_OPEN_FAILURE)) { + char *buf = dupprintf("Received %s for %s channel %u", + ssh2_pkt_type(ssh->pkt_kctx, ssh->pkt_actx, + pktin->type), + c ? "half-open" : "nonexistent", localid); + ssh_disconnect(ssh, NULL, buf, SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE); + sfree(buf); + return NULL; + } + return c; +} + +static void ssh2_msg_channel_success(Ssh ssh, struct Packet *pktin) +{ + /* + * This should never get called. All channel requests are either + * sent with want_reply false or are sent before this handler gets + * installed. + */ + struct ssh_channel *c; + struct winadj *wa; + + c = ssh2_channel_msg(ssh, pktin); + if (!c) + return; + wa = c->v.v2.winadj_head; + if (wa) + ssh_disconnect(ssh, NULL, "Received SSH_MSG_CHANNEL_SUCCESS for " + "\"winadj@putty.projects.tartarus.org\"", + SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE); + else + ssh_disconnect(ssh, NULL, + "Received unsolicited SSH_MSG_CHANNEL_SUCCESS", + SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE); +} + static void ssh2_msg_channel_failure(Ssh ssh, struct Packet *pktin) { /* @@ -6284,36 +6529,38 @@ static void ssh2_msg_channel_failure(Ssh ssh, struct Packet *pktin) * sent with want_reply false or are sent before this handler gets * installed. */ - unsigned i = ssh_pkt_getuint32(pktin); struct ssh_channel *c; struct winadj *wa; - c = find234(ssh->channels, &i, ssh_channelfind); + c = ssh2_channel_msg(ssh, pktin); if (!c) - return; /* nonexistent channel */ + return; wa = c->v.v2.winadj_head; - if (!wa) - logevent("excess SSH_MSG_CHANNEL_FAILURE"); - else { - c->v.v2.winadj_head = wa->next; - c->v.v2.remlocwin += wa->size; - sfree(wa); - /* - * winadj messages are only sent when the window is fully open, - * so if we get an ack of one, we know any pending unthrottle - * is complete. - */ - if (c->v.v2.throttle_state == UNTHROTTLING) - c->v.v2.throttle_state = UNTHROTTLED; + if (!wa) { + ssh_disconnect(ssh, NULL, + "Received unsolicited SSH_MSG_CHANNEL_FAILURE", + SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE); + return; } + c->v.v2.winadj_head = wa->next; + c->v.v2.remlocwin += wa->size; + sfree(wa); + /* + * winadj messages are only sent when the window is fully open, so + * if we get an ack of one, we know any pending unthrottle is + * complete. + */ + if (c->v.v2.throttle_state == UNTHROTTLING) + c->v.v2.throttle_state = UNTHROTTLED; } 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) { + c = ssh2_channel_msg(ssh, pktin); + if (!c) + return; + if (!c->closes) { c->v.v2.remwindow += ssh_pkt_getuint32(pktin); ssh2_try_send_and_unthrottle(c); } @@ -6323,11 +6570,10 @@ static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin) { char *data; int length; - unsigned i = ssh_pkt_getuint32(pktin); struct ssh_channel *c; - c = find234(ssh->channels, &i, ssh_channelfind); + c = ssh2_channel_msg(ssh, pktin); if (!c) - return; /* nonexistent channel */ + return; if (pktin->type == SSH2_MSG_CHANNEL_EXTENDED_DATA && ssh_pkt_getuint32(pktin) != SSH2_EXTENDED_DATA_STDERR) return; /* extended but not stderr */ @@ -6409,17 +6655,27 @@ static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin) */ ssh2_set_window(c, bufsize < c->v.v2.locmaxwin ? c->v.v2.locmaxwin - bufsize : 0); + /* + * If we're either buffering way too much data, or if we're + * buffering anything at all and we're in "simple" mode, + * throttle the whole channel. + */ + if ((bufsize > c->v.v2.locmaxwin || + (ssh->cfg.ssh_simple && bufsize > 0)) && + !c->throttling_conn) { + c->throttling_conn = 1; + ssh_throttle_conn(ssh, +1); + } } } static void ssh2_msg_channel_eof(Ssh ssh, struct Packet *pktin) { - unsigned i = ssh_pkt_getuint32(pktin); struct ssh_channel *c; - c = find234(ssh->channels, &i, ssh_channelfind); + c = ssh2_channel_msg(ssh, pktin); if (!c) - return; /* nonexistent channel */ + return; if (c->type == CHAN_X11) { /* @@ -6427,27 +6683,25 @@ static void ssh2_msg_channel_eof(Ssh ssh, struct Packet *pktin) * wrap up and close the channel ourselves. */ x11_close(c->u.x11.s); + c->u.x11.s = NULL; sshfwd_close(c); } else if (c->type == CHAN_AGENT) { sshfwd_close(c); } else if (c->type == CHAN_SOCKDATA) { pfd_close(c->u.pfd.s); + c->u.pfd.s = NULL; sshfwd_close(c); } } static void ssh2_msg_channel_close(Ssh ssh, struct Packet *pktin) { - unsigned i = ssh_pkt_getuint32(pktin); struct ssh_channel *c; struct Packet *pktout; - c = find234(ssh->channels, &i, ssh_channelfind); - if (!c || c->halfopen) { - bombout(("Received CHANNEL_CLOSE for %s channel %d\n", - c ? "half-open" : "nonexistent", i)); + c = ssh2_channel_msg(ssh, pktin); + if (!c) return; - } /* Do pre-close processing on the channel. */ switch (c->type) { case CHAN_MAINSESSION: @@ -6500,13 +6754,12 @@ static void ssh2_msg_channel_close(Ssh ssh, struct Packet *pktin) static void ssh2_msg_channel_open_confirmation(Ssh ssh, struct Packet *pktin) { - unsigned i = ssh_pkt_getuint32(pktin); struct ssh_channel *c; struct Packet *pktout; - c = find234(ssh->channels, &i, ssh_channelfind); + c = ssh2_channel_msg(ssh, pktin); if (!c) - return; /* nonexistent channel */ + return; if (c->type != CHAN_SOCKDATA_DORMANT) return; /* dunno why they're confirming this */ c->remoteid = ssh_pkt_getuint32(pktin); @@ -6538,14 +6791,13 @@ static void ssh2_msg_channel_open_failure(Ssh ssh, struct Packet *pktin) "Unknown channel type", "Resource shortage", }; - unsigned i = ssh_pkt_getuint32(pktin); unsigned reason_code; char *reason_string; int reason_length; struct ssh_channel *c; - c = find234(ssh->channels, &i, ssh_channelfind); + c = ssh2_channel_msg(ssh, pktin); if (!c) - return; /* nonexistent channel */ + return; if (c->type != CHAN_SOCKDATA_DORMANT) return; /* dunno why they're failing this */ @@ -6564,31 +6816,19 @@ static void ssh2_msg_channel_open_failure(Ssh ssh, struct Packet *pktin) static void ssh2_msg_channel_request(Ssh ssh, struct Packet *pktin) { - unsigned localid; char *type; int typelen, want_reply; int reply = SSH2_MSG_CHANNEL_FAILURE; /* default */ struct ssh_channel *c; struct Packet *pktout; - localid = ssh_pkt_getuint32(pktin); + c = ssh2_channel_msg(ssh, pktin); + if (!c) + return; ssh_pkt_getstring(pktin, &type, &typelen); want_reply = ssh2_pkt_getbool(pktin); /* - * First, check that the channel exists. Otherwise, - * we can instantly disconnect with a rude message. - */ - c = find234(ssh->channels, &localid, ssh_channelfind); - if (!c) { - char *buf = dupprintf("Received channel request for nonexistent" - " channel %d", localid); - ssh_disconnect(ssh, NULL, buf, SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE); - sfree(buf); - return; - } - - /* * Having got the channel number, we now look at * the request type string to see if it's something * we recognise. @@ -6615,7 +6855,7 @@ static void ssh2_msg_channel_request(Ssh ssh, struct Packet *pktin) int msglen = 0, core = FALSE; /* ICK: older versions of OpenSSH (e.g. 3.4p1) * provide an `int' for the signal, despite its - * having been a `string' in the drafts since at + * having been a `string' in the drafts of RFC 4254 since at * least 2001. (Fixed in session.c 1.147.) Try to * infer which we can safely parse it as. */ { @@ -6658,7 +6898,7 @@ static void ssh2_msg_channel_request(Ssh ssh, struct Packet *pktin) fmt_sig = dupprintf(" %d", signum); ssh->exitcode = 128 + signum; } else { - /* As per the drafts. */ + /* As per RFC 4254. */ char *sig; int siglen; ssh_pkt_getstring(pktin, &sig, &siglen); @@ -6797,6 +7037,7 @@ static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin) if (typelen == 3 && !memcmp(type, "x11", 3)) { char *addrstr; + const char *x11err; ssh_pkt_getstring(pktin, &peeraddr, &peeraddrlen); addrstr = snewn(peeraddrlen+1, char); @@ -6809,9 +7050,9 @@ static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin) if (!ssh->X11_fwd_enabled) error = "X11 forwarding is not enabled"; - else if (x11_init(&c->u.x11.s, ssh->cfg.x11_display, c, - ssh->x11auth, addrstr, peerport, - &ssh->cfg) != NULL) { + else if ((x11err = x11_init(&c->u.x11.s, ssh->x11disp, c, + addrstr, peerport, &ssh->cfg)) != NULL) { + logeventf(ssh, "Local X11 connection failed: %s", x11err); error = "Unable to open an X11 connection"; } else { logevent("Opening X11 forward connection succeeded"); @@ -6873,15 +7114,9 @@ static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin) logeventf(ssh, "Rejected channel open: %s", error); sfree(c); } else { - c->localid = alloc_channel_id(ssh); - c->closes = 0; - c->v.v2.locwindow = c->v.v2.locmaxwin = OUR_V2_WINSIZE; + ssh2_channel_init(c); c->v.v2.remwindow = winsize; c->v.v2.remmaxpkt = pktsize; - c->v.v2.remlocwin = OUR_V2_WINSIZE; - c->v.v2.winadj_head = c->v.v2.winadj_tail = NULL; - c->v.v2.throttle_state = UNTHROTTLED; - bufchain_init(&c->v.v2.outbuffer); add234(ssh->channels, c); pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); ssh2_pkt_adduint32(pktout, c->remoteid); @@ -6940,12 +7175,17 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, AUTH_TYPE_PUBLICKEY_OFFER_LOUD, AUTH_TYPE_PUBLICKEY_OFFER_QUIET, AUTH_TYPE_PASSWORD, + AUTH_TYPE_GSSAPI, 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, done_agent; +#ifndef NO_GSSAPI + int can_gssapi; + int tried_gssapi; +#endif int kbd_inter_refused; int we_are_in; prompts_t *cur_prompt; @@ -6969,6 +7209,14 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int try_send; int num_env, env_left, env_ok; struct Packet *pktout; +#ifndef NO_GSSAPI + struct ssh_gss_library *gsslib; + Ssh_gss_ctx gss_ctx; + Ssh_gss_buf gss_buf; + Ssh_gss_buf gss_rcvtok, gss_sndtok; + Ssh_gss_name gss_srv_name; + Ssh_gss_stat gss_stat; +#endif }; crState(do_ssh2_authconn_state); @@ -6976,6 +7224,10 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, s->done_service_req = FALSE; s->we_are_in = FALSE; +#ifndef NO_GSSAPI + s->tried_gssapi = FALSE; +#endif + if (!ssh->cfg.ssh_no_userauth) { /* * Request userauth protocol, and await a response to it. @@ -7159,7 +7411,8 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * with change_username turned off we don't try to get * it again. */ - } else if (!*ssh->cfg.username) { + } else if (!get_remote_username(&ssh->cfg, s->username, + sizeof(s->username))) { int ret; /* need not be kept over crReturn */ s->cur_prompt = new_prompts(ssh->frontend); s->cur_prompt->to_server = TRUE; @@ -7187,8 +7440,6 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, free_prompts(s->cur_prompt); } else { char *stuff; - strncpy(s->username, ssh->cfg.username, sizeof(s->username)); - s->username[sizeof(s->username)-1] = '\0'; if ((flags & FLAG_VERBOSE) || (flags & FLAG_INTERACTIVE)) { stuff = dupprintf("Using username \"%s\".\r\n", s->username); c_write_str(ssh, stuff); @@ -7263,7 +7514,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, break; } - if (pktin->type != SSH2_MSG_USERAUTH_FAILURE) { + if (pktin->type != SSH2_MSG_USERAUTH_FAILURE && s->type != AUTH_TYPE_GSSAPI) { bombout(("Strange packet received during authentication: " "type %d", pktin->type)); crStopV; @@ -7334,6 +7585,12 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, in_commasep_string("password", methods, methlen); s->can_keyb_inter = ssh->cfg.try_ki_auth && in_commasep_string("keyboard-interactive", methods, methlen); +#ifndef NO_GSSAPI + ssh_gss_init(); + s->can_gssapi = ssh->cfg.try_gssapi_auth && + in_commasep_string("gssapi-with-mic", methods, methlen) && + n_ssh_gss_libraries > 0; +#endif } ssh->pkt_actx = SSH2_PKTCTX_NOAUTH; @@ -7662,6 +7919,197 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, key->alg->freekey(key->data); } +#ifndef NO_GSSAPI + } else if (s->can_gssapi && !s->tried_gssapi) { + + /* GSSAPI Authentication */ + + int micoffset, len; + char *data; + Ssh_gss_buf mic; + s->type = AUTH_TYPE_GSSAPI; + s->tried_gssapi = TRUE; + s->gotit = TRUE; + ssh->pkt_actx = SSH2_PKTCTX_GSSAPI; + + /* + * Pick the highest GSS library on the preference + * list. + */ + { + int i, j; + s->gsslib = NULL; + for (i = 0; i < ngsslibs; i++) { + int want_id = ssh->cfg.ssh_gsslist[i]; + for (j = 0; j < n_ssh_gss_libraries; j++) + if (ssh_gss_libraries[j].id == want_id) { + s->gsslib = &ssh_gss_libraries[j]; + goto got_gsslib; /* double break */ + } + } + got_gsslib: + /* + * We always expect to have found something in + * the above loop: we only came here if there + * was at least one viable GSS library, and the + * preference list should always mention + * everything and only change the order. + */ + assert(s->gsslib); + } + + if (s->gsslib->gsslogmsg) + logevent(s->gsslib->gsslogmsg); + + /* Sending USERAUTH_REQUEST with "gssapi-with-mic" method */ + s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); + ssh2_pkt_addstring(s->pktout, s->username); + ssh2_pkt_addstring(s->pktout, "ssh-connection"); + ssh2_pkt_addstring(s->pktout, "gssapi-with-mic"); + + /* add mechanism info */ + s->gsslib->indicate_mech(s->gsslib, &s->gss_buf); + + /* number of GSSAPI mechanisms */ + ssh2_pkt_adduint32(s->pktout,1); + + /* length of OID + 2 */ + ssh2_pkt_adduint32(s->pktout, s->gss_buf.length + 2); + ssh2_pkt_addbyte(s->pktout, SSH2_GSS_OIDTYPE); + + /* length of OID */ + ssh2_pkt_addbyte(s->pktout, (unsigned char) s->gss_buf.length); + + ssh_pkt_adddata(s->pktout, s->gss_buf.value, + s->gss_buf.length); + ssh2_pkt_send(ssh, s->pktout); + crWaitUntilV(pktin); + if (pktin->type != SSH2_MSG_USERAUTH_GSSAPI_RESPONSE) { + logevent("GSSAPI authentication request refused"); + continue; + } + + /* check returned packet ... */ + + ssh_pkt_getstring(pktin, &data, &len); + s->gss_rcvtok.value = data; + s->gss_rcvtok.length = len; + if (s->gss_rcvtok.length != s->gss_buf.length + 2 || + ((char *)s->gss_rcvtok.value)[0] != SSH2_GSS_OIDTYPE || + ((char *)s->gss_rcvtok.value)[1] != s->gss_buf.length || + memcmp((char *)s->gss_rcvtok.value + 2, + s->gss_buf.value,s->gss_buf.length) ) { + logevent("GSSAPI authentication - wrong response from server"); + continue; + } + + /* now start running */ + s->gss_stat = s->gsslib->import_name(s->gsslib, + ssh->fullhostname, + &s->gss_srv_name); + if (s->gss_stat != SSH_GSS_OK) { + if (s->gss_stat == SSH_GSS_BAD_HOST_NAME) + logevent("GSSAPI import name failed - Bad service name"); + else + logevent("GSSAPI import name failed"); + continue; + } + + /* fetch TGT into GSS engine */ + s->gss_stat = s->gsslib->acquire_cred(s->gsslib, &s->gss_ctx); + + if (s->gss_stat != SSH_GSS_OK) { + logevent("GSSAPI authentication failed to get credentials"); + s->gsslib->release_name(s->gsslib, &s->gss_srv_name); + continue; + } + + /* initial tokens are empty */ + SSH_GSS_CLEAR_BUF(&s->gss_rcvtok); + SSH_GSS_CLEAR_BUF(&s->gss_sndtok); + + /* now enter the loop */ + do { + s->gss_stat = s->gsslib->init_sec_context + (s->gsslib, + &s->gss_ctx, + s->gss_srv_name, + ssh->cfg.gssapifwd, + &s->gss_rcvtok, + &s->gss_sndtok); + + if (s->gss_stat!=SSH_GSS_S_COMPLETE && + s->gss_stat!=SSH_GSS_S_CONTINUE_NEEDED) { + logevent("GSSAPI authentication initialisation failed"); + + if (s->gsslib->display_status(s->gsslib, s->gss_ctx, + &s->gss_buf) == SSH_GSS_OK) { + logevent(s->gss_buf.value); + sfree(s->gss_buf.value); + } + + break; + } + logevent("GSSAPI authentication initialised"); + + /* Client and server now exchange tokens until GSSAPI + * no longer says CONTINUE_NEEDED */ + + if (s->gss_sndtok.length != 0) { + s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_GSSAPI_TOKEN); + ssh_pkt_addstring_start(s->pktout); + ssh_pkt_addstring_data(s->pktout,s->gss_sndtok.value,s->gss_sndtok.length); + ssh2_pkt_send(ssh, s->pktout); + s->gsslib->free_tok(s->gsslib, &s->gss_sndtok); + } + + if (s->gss_stat == SSH_GSS_S_CONTINUE_NEEDED) { + crWaitUntilV(pktin); + if (pktin->type != SSH2_MSG_USERAUTH_GSSAPI_TOKEN) { + logevent("GSSAPI authentication - bad server response"); + s->gss_stat = SSH_GSS_FAILURE; + break; + } + ssh_pkt_getstring(pktin, &data, &len); + s->gss_rcvtok.value = data; + s->gss_rcvtok.length = len; + } + } while (s-> gss_stat == SSH_GSS_S_CONTINUE_NEEDED); + + if (s->gss_stat != SSH_GSS_OK) { + s->gsslib->release_name(s->gsslib, &s->gss_srv_name); + s->gsslib->release_cred(s->gsslib, &s->gss_ctx); + continue; + } + logevent("GSSAPI authentication loop finished OK"); + + /* Now send the MIC */ + + s->pktout = ssh2_pkt_init(0); + micoffset = s->pktout->length; + ssh_pkt_addstring_start(s->pktout); + ssh_pkt_addstring_data(s->pktout, (char *)ssh->v2_session_id, ssh->v2_session_id_len); + ssh_pkt_addbyte(s->pktout, SSH2_MSG_USERAUTH_REQUEST); + ssh_pkt_addstring(s->pktout, s->username); + ssh_pkt_addstring(s->pktout, "ssh-connection"); + ssh_pkt_addstring(s->pktout, "gssapi-with-mic"); + + s->gss_buf.value = (char *)s->pktout->data + micoffset; + s->gss_buf.length = s->pktout->length - micoffset; + + s->gsslib->get_mic(s->gsslib, s->gss_ctx, &s->gss_buf, &mic); + s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_GSSAPI_MIC); + ssh_pkt_addstring_start(s->pktout); + ssh_pkt_addstring_data(s->pktout, mic.value, mic.length); + ssh2_pkt_send(ssh, s->pktout); + s->gsslib->free_mic(s->gsslib, &mic); + + s->gotit = FALSE; + + s->gsslib->release_name(s->gsslib, &s->gss_srv_name); + s->gsslib->release_cred(s->gsslib, &s->gss_ctx); + continue; +#endif } else if (s->can_keyb_inter && !s->kbd_inter_refused) { /* @@ -7714,26 +8162,9 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, ssh_pkt_getstring(pktin, &lang, &lang_len); s->cur_prompt = new_prompts(ssh->frontend); s->cur_prompt->to_server = TRUE; - if (name_len) { - /* FIXME: better prefix to distinguish from - * local prompts? */ - s->cur_prompt->name = - dupprintf("SSH server: %.*s", name_len, name); - s->cur_prompt->name_reqd = TRUE; - } else { - s->cur_prompt->name = - dupstr("SSH server authentication"); - s->cur_prompt->name_reqd = FALSE; - } - /* FIXME: ugly to print "Using..." in prompt _every_ - * time round. Can this be done more subtly? */ - s->cur_prompt->instruction = - dupprintf("Using keyboard-interactive authentication.%s%.*s", - inst_len ? "\n" : "", inst_len, inst); - s->cur_prompt->instr_reqd = TRUE; /* - * Get the prompts from the packet. + * Get any prompt(s) from the packet. */ s->num_prompts = ssh_pkt_getuint32(pktin); for (i = 0; i < s->num_prompts; i++) { @@ -7754,10 +8185,38 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, echo, SSH_MAX_PASSWORD_LEN); } + if (name_len) { + /* FIXME: better prefix to distinguish from + * local prompts? */ + s->cur_prompt->name = + dupprintf("SSH server: %.*s", name_len, name); + s->cur_prompt->name_reqd = TRUE; + } else { + s->cur_prompt->name = + dupstr("SSH server authentication"); + s->cur_prompt->name_reqd = FALSE; + } + /* We add a prefix to try to make it clear that a prompt + * has come from the server. + * FIXME: ugly to print "Using..." in prompt _every_ + * time round. Can this be done more subtly? */ + /* Special case: for reasons best known to themselves, + * some servers send k-i requests with no prompts and + * nothing to display. Keep quiet in this case. */ + if (s->num_prompts || name_len || inst_len) { + s->cur_prompt->instruction = + dupprintf("Using keyboard-interactive authentication.%s%.*s", + inst_len ? "\n" : "", inst_len, inst); + s->cur_prompt->instr_reqd = TRUE; + } else { + s->cur_prompt->instr_reqd = FALSE; + } + /* - * Get the user's responses. + * Display any instructions, and get the user's + * response(s). */ - if (s->num_prompts) { + { int ret; /* not live over crReturn */ ret = get_userpass_input(s->cur_prompt, NULL, 0); while (ret < 0) { @@ -7779,7 +8238,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, } /* - * Send the responses to the server. + * Send the response(s) to the server. */ s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_INFO_RESPONSE); ssh2_pkt_adduint32(s->pktout, s->num_prompts); @@ -8082,19 +8541,13 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, */ ssh->mainchan = snew(struct ssh_channel); ssh->mainchan->ssh = ssh; - ssh->mainchan->localid = alloc_channel_id(ssh); + ssh2_channel_init(ssh->mainchan); logeventf(ssh, "Opening direct-tcpip channel to %s:%d in place of session", ssh->cfg.ssh_nc_host, ssh->cfg.ssh_nc_port); s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN); ssh2_pkt_addstring(s->pktout, "direct-tcpip"); ssh2_pkt_adduint32(s->pktout, ssh->mainchan->localid); - ssh->mainchan->v.v2.locwindow = ssh->mainchan->v.v2.locmaxwin = - ssh->mainchan->v.v2.remlocwin = - ssh->cfg.ssh_simple ? OUR_V2_BIGWIN : OUR_V2_WINSIZE; - ssh->mainchan->v.v2.winadj_head = NULL; - ssh->mainchan->v.v2.winadj_tail = NULL; - ssh->mainchan->v.v2.throttle_state = UNTHROTTLED; ssh2_pkt_adduint32(s->pktout, ssh->mainchan->v.v2.locwindow);/* our window size */ ssh2_pkt_adduint32(s->pktout, OUR_V2_MAXPKT); /* our max pkt size */ ssh2_pkt_addstring(s->pktout, ssh->cfg.ssh_nc_host); @@ -8121,10 +8574,8 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, ssh->mainchan->remoteid = ssh_pkt_getuint32(pktin); ssh->mainchan->halfopen = FALSE; ssh->mainchan->type = CHAN_MAINSESSION; - ssh->mainchan->closes = 0; ssh->mainchan->v.v2.remwindow = ssh_pkt_getuint32(pktin); ssh->mainchan->v.v2.remmaxpkt = ssh_pkt_getuint32(pktin); - bufchain_init(&ssh->mainchan->v.v2.outbuffer); add234(ssh->channels, ssh->mainchan); update_specials_menu(ssh->frontend); logevent("Opened direct-tcpip channel"); @@ -8132,16 +8583,10 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, } else { ssh->mainchan = snew(struct ssh_channel); ssh->mainchan->ssh = ssh; - ssh->mainchan->localid = alloc_channel_id(ssh); + ssh2_channel_init(ssh->mainchan); s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN); ssh2_pkt_addstring(s->pktout, "session"); ssh2_pkt_adduint32(s->pktout, ssh->mainchan->localid); - ssh->mainchan->v.v2.locwindow = ssh->mainchan->v.v2.locmaxwin = - ssh->mainchan->v.v2.remlocwin = - ssh->cfg.ssh_simple ? OUR_V2_BIGWIN : OUR_V2_WINSIZE; - ssh->mainchan->v.v2.winadj_head = NULL; - ssh->mainchan->v.v2.winadj_tail = NULL; - ssh->mainchan->v.v2.throttle_state = UNTHROTTLED; ssh2_pkt_adduint32(s->pktout, ssh->mainchan->v.v2.locwindow);/* our window size */ ssh2_pkt_adduint32(s->pktout, OUR_V2_MAXPKT); /* our max pkt size */ ssh2_pkt_send(ssh, s->pktout); @@ -8158,10 +8603,8 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, ssh->mainchan->remoteid = ssh_pkt_getuint32(pktin); ssh->mainchan->halfopen = FALSE; ssh->mainchan->type = CHAN_MAINSESSION; - ssh->mainchan->closes = 0; ssh->mainchan->v.v2.remwindow = ssh_pkt_getuint32(pktin); ssh->mainchan->v.v2.remmaxpkt = ssh_pkt_getuint32(pktin); - bufchain_init(&ssh->mainchan->v.v2.outbuffer); add234(ssh->channels, ssh->mainchan); update_specials_menu(ssh->frontend); logevent("Opened channel for session"); @@ -8186,7 +8629,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN] = ssh2_msg_channel_open; - if (ssh->cfg.ssh_simple) { + if (ssh->mainchan && ssh->cfg.ssh_simple) { /* * This message indicates to the server that we promise * not to try to run any other channel in parallel with @@ -8203,18 +8646,16 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, /* * Potentially enable X11 forwarding. */ - if (ssh->mainchan && !ssh->ncmode && ssh->cfg.x11_forward) { - char proto[20], data[64]; + if (ssh->mainchan && !ssh->ncmode && ssh->cfg.x11_forward && + (ssh->x11disp = x11_setup_display(ssh->cfg.x11_display, + ssh->cfg.x11_auth, &ssh->cfg))) { logevent("Requesting X11 forwarding"); - 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); s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST); ssh2_pkt_adduint32(s->pktout, ssh->mainchan->remoteid); ssh2_pkt_addstring(s->pktout, "x11-req"); ssh2_pkt_addbool(s->pktout, 1); /* want reply */ ssh2_pkt_addbool(s->pktout, 0); /* many connections */ - ssh2_pkt_addstring(s->pktout, proto); + ssh2_pkt_addstring(s->pktout, ssh->x11disp->remoteauthprotoname); /* * 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, @@ -8223,9 +8664,9 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * cookie into the log. */ dont_log_password(ssh, s->pktout, PKTLOG_BLANK); - ssh2_pkt_addstring(s->pktout, data); + ssh2_pkt_addstring(s->pktout, ssh->x11disp->remoteauthdatastring); end_log_omission(ssh, s->pktout); - ssh2_pkt_adduint32(s->pktout, x11_get_screen_number(ssh->cfg.x11_display)); + ssh2_pkt_adduint32(s->pktout, ssh->x11disp->screennum); ssh2_pkt_send(ssh, s->pktout); crWaitUntilV(pktin); @@ -8458,6 +8899,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * All the initial channel requests are done, so install the default * failure handler. */ + ssh->packet_dispatch[SSH2_MSG_CHANNEL_SUCCESS] = ssh2_msg_channel_success; ssh->packet_dispatch[SSH2_MSG_CHANNEL_FAILURE] = ssh2_msg_channel_failure; /* @@ -8508,7 +8950,7 @@ static void ssh2_msg_disconnect(Ssh ssh, struct Packet *pktin) { /* log reason code in disconnect message */ char *buf, *msg; - int nowlen, reason, msglen; + int reason, msglen; reason = ssh_pkt_getuint32(pktin); ssh_pkt_getstring(pktin, &msg, &msglen); @@ -8522,14 +8964,14 @@ static void ssh2_msg_disconnect(Ssh ssh, struct Packet *pktin) } logevent(buf); sfree(buf); - buf = dupprintf("Disconnection message text: %n%.*s", - &nowlen, msglen, msg); + buf = dupprintf("Disconnection message text: %.*s", + msglen, msg); logevent(buf); - bombout(("Server sent disconnect message\ntype %d (%s):\n\"%s\"", + bombout(("Server sent disconnect message\ntype %d (%s):\n\"%.*s\"", reason, (reason > 0 && reason < lenof(ssh2_disconnect_reasons)) ? ssh2_disconnect_reasons[reason] : "unknown", - buf+nowlen)); + msglen, msg)); sfree(buf); } @@ -8715,7 +9157,7 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle, ssh->fallback_cmd = 0; ssh->pkt_kctx = SSH2_PKTCTX_NOKEX; ssh->pkt_actx = SSH2_PKTCTX_NOAUTH; - ssh->x11auth = NULL; + ssh->x11disp = NULL; ssh->v1_compressing = FALSE; ssh->v2_outgoing_sequence = 0; ssh->ssh1_rdpkt_crstate = 0; @@ -8761,7 +9203,7 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle, ssh->send_ok = 0; ssh->editing = 0; ssh->echoing = 0; - ssh->v1_throttle_count = 0; + ssh->conn_throttle_count = 0; ssh->overall_bufsize = 0; ssh->fallback_cmd = 0; @@ -8853,14 +9295,15 @@ static void ssh_free(void *handle) ssh->rportfwds = NULL; } sfree(ssh->deferred_send_data); - if (ssh->x11auth) - x11_free_auth(ssh->x11auth); + if (ssh->x11disp) + x11_free_display(ssh->x11disp); sfree(ssh->do_ssh_init_state); sfree(ssh->do_ssh1_login_state); sfree(ssh->do_ssh2_transport_state); sfree(ssh->do_ssh2_authconn_state); sfree(ssh->v_c); sfree(ssh->v_s); + sfree(ssh->fullhostname); if (ssh->crcda_ctx) { crcda_free_context(ssh->crcda_ctx); ssh->crcda_ctx = NULL; @@ -9031,14 +9474,16 @@ static const struct telnet_special *ssh_get_specials(void *handle) static const struct telnet_special ssh1_ignore_special[] = { {"IGNORE message", TS_NOP} }; - static const struct telnet_special ssh2_transport_specials[] = { + static const struct telnet_special ssh2_ignore_special[] = { {"IGNORE message", TS_NOP}, + }; + static const struct telnet_special ssh2_rekey_special[] = { {"Repeat key exchange", TS_REKEY}, }; static const struct telnet_special ssh2_session_specials[] = { {NULL, TS_SEP}, {"Break", TS_BRK}, - /* These are the signal names defined by draft-ietf-secsh-connect-23. + /* These are the signal names defined by RFC 4254. * They include all the ISO C signals, but are a subset of the POSIX * required signals. */ {"SIGINT (Interrupt)", TS_SIGINT}, @@ -9057,7 +9502,8 @@ static const struct telnet_special *ssh_get_specials(void *handle) {NULL, TS_EXITMENU} }; /* XXX review this length for any changes: */ - static struct telnet_special ssh_specials[lenof(ssh2_transport_specials) + + static struct telnet_special ssh_specials[lenof(ssh2_ignore_special) + + lenof(ssh2_rekey_special) + lenof(ssh2_session_specials) + lenof(specials_end)]; Ssh ssh = (Ssh) handle; @@ -9076,7 +9522,10 @@ static const struct telnet_special *ssh_get_specials(void *handle) if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE)) ADD_SPECIALS(ssh1_ignore_special); } else if (ssh->version == 2) { - ADD_SPECIALS(ssh2_transport_specials); + if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE)) + ADD_SPECIALS(ssh2_ignore_special); + if (!(ssh->remote_bugs & BUG_SSH2_REKEY)) + ADD_SPECIALS(ssh2_rekey_special); if (ssh->mainchan) ADD_SPECIALS(ssh2_session_specials); } /* else we're not ready yet */ @@ -9126,9 +9575,11 @@ static void ssh_special(void *handle, Telnet_Special code) if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE)) send_packet(ssh, SSH1_MSG_IGNORE, PKT_STR, "", PKT_END); } else { - pktout = ssh2_pkt_init(SSH2_MSG_IGNORE); - ssh2_pkt_addstring_start(pktout); - ssh2_pkt_send_noqueue(ssh, pktout); + if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE)) { + pktout = ssh2_pkt_init(SSH2_MSG_IGNORE); + ssh2_pkt_addstring_start(pktout); + ssh2_pkt_send_noqueue(ssh, pktout); + } } } else if (code == TS_REKEY) { if (!ssh->kex_in_progress && ssh->version == 2) { @@ -9187,17 +9638,13 @@ void *new_sock_channel(void *handle, Socket s) Ssh ssh = (Ssh) handle; struct ssh_channel *c; c = snew(struct ssh_channel); - c->ssh = ssh; - if (c) { - c->halfopen = TRUE; - c->localid = alloc_channel_id(ssh); - c->closes = 0; - c->type = CHAN_SOCKDATA_DORMANT;/* identify channel type */ - c->u.pfd.s = s; - bufchain_init(&c->v.v2.outbuffer); - add234(ssh->channels, c); - } + c->ssh = ssh; + ssh2_channel_init(c); + c->halfopen = TRUE; + c->type = CHAN_SOCKDATA_DORMANT;/* identify channel type */ + c->u.pfd.s = s; + add234(ssh->channels, c); return c; } @@ -9208,15 +9655,27 @@ void *new_sock_channel(void *handle, Socket s) static void ssh_unthrottle(void *handle, int bufsize) { Ssh ssh = (Ssh) handle; + int buflimit; + if (ssh->version == 1) { if (ssh->v1_stdout_throttling && bufsize < SSH1_BUFFER_LIMIT) { ssh->v1_stdout_throttling = 0; - ssh1_throttle(ssh, -1); + ssh_throttle_conn(ssh, -1); } } else { - if (ssh->mainchan) + if (ssh->mainchan) { ssh2_set_window(ssh->mainchan, - ssh->mainchan->v.v2.locmaxwin - bufsize); + bufsize < ssh->mainchan->v.v2.locmaxwin ? + ssh->mainchan->v.v2.locmaxwin - bufsize : 0); + if (ssh->cfg.ssh_simple) + buflimit = 0; + else + buflimit = ssh->mainchan->v.v2.locmaxwin; + if (ssh->mainchan->throttling_conn && bufsize <= buflimit) { + ssh->mainchan->throttling_conn = 0; + ssh_throttle_conn(ssh, -1); + } + } } } @@ -9239,10 +9698,6 @@ void ssh_send_port_open(void *channel, char *hostname, int port, char *org) pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN); ssh2_pkt_addstring(pktout, "direct-tcpip"); ssh2_pkt_adduint32(pktout, c->localid); - c->v.v2.locwindow = c->v.v2.locmaxwin = OUR_V2_WINSIZE; - c->v.v2.remlocwin = OUR_V2_WINSIZE; - c->v.v2.winadj_head = c->v.v2.winadj_head = NULL; - c->v.v2.throttle_state = UNTHROTTLED; ssh2_pkt_adduint32(pktout, c->v.v2.locwindow);/* our window size */ ssh2_pkt_adduint32(pktout, OUR_V2_MAXPKT); /* our max pkt size */ ssh2_pkt_addstring(pktout, hostname);