X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/putty/blobdiff_plain/c6ccd5c22a25aca610b0120b3eec1e6efd1aab97..fc2464c6987e8ed8899c746ec29c01b73a23e327:/ssh.c diff --git a/ssh.c b/ssh.c index f26eafd2..605e0e6f 100644 --- a/ssh.c +++ b/ssh.c @@ -748,6 +748,7 @@ struct ssh_tag { */ int fallback_cmd; + bufchain banner; /* accumulates banners during do_ssh2_authconn */ /* * Used for username and password input. */ @@ -1797,8 +1798,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. */ @@ -1806,6 +1818,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) @@ -1823,9 +1841,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, @@ -1875,7 +1903,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 @@ -1923,7 +1951,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); @@ -2383,6 +2411,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); @@ -3484,9 +3514,9 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, PKT_STR, "No more passwords available to try", PKT_END); logevent("Unable to authenticate"); - connection_fatal(ssh->frontend, "Unable to authenticate"); ssh->close_expected = TRUE; - ssh_closing((Plug)ssh, NULL, 0, 0); + ssh_closing((Plug)ssh, NULL, 0, 0); + connection_fatal(ssh->frontend, "Unable to authenticate"); crStop(1); } } else { @@ -4603,14 +4633,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; @@ -6098,9 +6137,9 @@ static void ssh2_msg_channel_request(Ssh ssh, struct Packet *pktin) ssh2_pkt_addstring(pktout, buf); ssh2_pkt_addstring(pktout, "en"); /* language tag */ ssh2_pkt_send_noqueue(ssh, pktout); - connection_fatal(ssh->frontend, "%s", buf); ssh->close_expected = TRUE; ssh_closing((Plug)ssh, NULL, 0, 0); + connection_fatal(ssh->frontend, "%s", buf); return; } @@ -6349,6 +6388,21 @@ static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin) } } +/* + * Buffer banner messages for later display at some convenient point. + */ +static void ssh2_msg_userauth_banner(Ssh ssh, struct Packet *pktin) +{ + /* Arbitrary limit to prevent unbounded inflation of buffer */ + if (bufchain_size(&ssh->banner) <= 131072) { + char *banner = NULL; + int size = 0; + ssh_pkt_getstring(pktin, &banner, &size); + if (banner) + bufchain_add(&ssh->banner, banner, size); + } +} + /* Helper function to deal with sending tty modes for "pty-req" */ static void ssh2_send_ttymode(void *data, char *mode, char *val) { @@ -6390,6 +6444,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; @@ -6417,16 +6472,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; + } } /* @@ -6455,7 +6527,10 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, */ s->username[0] = '\0'; s->got_username = FALSE; - do { + bufchain_init(&ssh->banner); + ssh->packet_dispatch[SSH2_MSG_USERAUTH_BANNER] = + ssh2_msg_userauth_banner; + while (!s->we_are_in) { /* * Get a username. */ @@ -6555,9 +6630,14 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, */ if (!s->gotit) crWaitUntilV(pktin); - while (pktin->type == SSH2_MSG_USERAUTH_BANNER) { - char *banner; - int size; + /* + * Now is a convenient point to spew any banner material + * that we've accumulated. (This should ensure that when + * we exit the auth loop, we haven't any left to deal + * with.) + */ + { + int size = bufchain_size(&ssh->banner); /* * Don't show the banner if we're operating in * non-verbose non-interactive mode. (It's probably @@ -6566,12 +6646,13 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * the banner will screw up processing on the * output of (say) plink.) */ - if (flags & (FLAG_VERBOSE | FLAG_INTERACTIVE)) { - ssh_pkt_getstring(pktin, &banner, &size); - if (banner) - c_write_untrusted(ssh, banner, size); + if (size && (flags & (FLAG_VERBOSE | FLAG_INTERACTIVE))) { + char *banner = snewn(size, char); + bufchain_fetch(&ssh->banner, banner, size); + c_write_untrusted(ssh, banner, size); + sfree(banner); } - crWaitUntilV(pktin); + bufchain_clear(&ssh->banner); } if (pktin->type == SSH2_MSG_USERAUTH_SUCCESS) { logevent("Access granted"); @@ -7027,10 +7108,10 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, ssh2_pkt_addstring(s->pktout, "en"); /* language tag */ ssh2_pkt_send_noqueue(ssh, s->pktout); logevent("Unable to authenticate"); + ssh->close_expected = TRUE; + ssh_closing((Plug)ssh, NULL, 0, 0); connection_fatal(ssh->frontend, "Unable to authenticate"); - ssh->close_expected = TRUE; - ssh_closing((Plug)ssh, NULL, 0, 0); crStopV; } } else { @@ -7196,12 +7277,11 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, crStopV; } } - } while (!s->we_are_in); + } + ssh->packet_dispatch[SSH2_MSG_USERAUTH_BANNER] = NULL; /* - * 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); @@ -7285,7 +7365,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); @@ -7935,7 +8024,8 @@ static void ssh_reconfig(void *handle, Config *cfg) unsigned long old_max_data_size; pinger_reconfig(ssh->pinger, &ssh->cfg, cfg); - ssh_setup_portfwd(ssh, cfg); + if (ssh->portfwds) + ssh_setup_portfwd(ssh, cfg); if (ssh->cfg.ssh_rekey_time != cfg->ssh_rekey_time && cfg->ssh_rekey_time != 0) {