X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/86916870d36cb70e7ef0ea760e75a6ae8b2d83a5..acddebd9b0442b7a8ac97bfe7989d8be485edcf5:/ssh.c diff --git a/ssh.c b/ssh.c index 70f3a069..82fbf152 100644 --- a/ssh.c +++ b/ssh.c @@ -163,6 +163,7 @@ static const char *const ssh2_disconnect_reasons[] = { #define BUG_SSH2_RSA_PADDING 16 #define BUG_SSH2_DERIVEKEY 32 #define BUG_SSH2_DH_GEX 64 +#define BUG_SSH2_PK_SESSIONID 128 #define translate(x) if (type == x) return #x #define translatec(x,ctx) if (type == x && (pkt_ctx & ctx)) return #x @@ -268,7 +269,15 @@ static char *ssh2_pkt_type(int pkt_ctx, int type) enum { PKT_END, PKT_INT, PKT_CHAR, PKT_DATA, PKT_STR, PKT_BIGNUM }; -/* Coroutine mechanics for the sillier bits of the code */ +/* + * Coroutine mechanics for the sillier bits of the code. If these + * macros look impenetrable to you, you might find it helpful to + * read + * + * http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html + * + * which explains the theory behind these macros. + */ #define crBegin(v) { int *crLine = &v; switch(v) { case 0:; #define crState(t) \ struct t *s; \ @@ -734,7 +743,7 @@ static int alloc_channel_id(Ssh ssh) return low + 1 + CHANNEL_NUMBER_OFFSET; } -static void c_write(Ssh ssh, char *buf, int len) +static void c_write(Ssh ssh, const char *buf, int len) { if ((flags & FLAG_STDERR)) { int i; @@ -746,7 +755,7 @@ static void c_write(Ssh ssh, char *buf, int len) from_backend(ssh->frontend, 1, buf, len); } -static void c_write_untrusted(Ssh ssh, char *buf, int len) +static void c_write_untrusted(Ssh ssh, const char *buf, int len) { int i; for (i = 0; i < len; i++) { @@ -757,7 +766,7 @@ static void c_write_untrusted(Ssh ssh, char *buf, int len) } } -static void c_write_str(Ssh ssh, char *buf) +static void c_write_str(Ssh ssh, const char *buf) { c_write(ssh, buf, strlen(buf)); } @@ -1166,8 +1175,18 @@ static void s_wrpkt_start(Ssh ssh, int type, int len) static int s_wrpkt_prepare(Ssh ssh) { - int pad, len, biglen, i; + int pad, biglen, i; unsigned long crc; +#ifdef __SC__ + /* + * XXX various versions of SC (including 8.8.4) screw up the + * register allocation in this function and use the same register + * (D6) for len and as a temporary, with predictable results. The + * following sledgehammer prevents this. + */ + volatile +#endif + int len; ssh->pktout.body[-1] = ssh->pktout.type; @@ -1719,8 +1738,8 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring) ssh->remote_bugs = 0; - if (ssh->cfg.sshbug_ignore1 == BUG_ON || - (ssh->cfg.sshbug_ignore1 == BUG_AUTO && + if (ssh->cfg.sshbug_ignore1 == FORCE_ON || + (ssh->cfg.sshbug_ignore1 == AUTO && (!strcmp(imp, "1.2.18") || !strcmp(imp, "1.2.19") || !strcmp(imp, "1.2.20") || !strcmp(imp, "1.2.21") || !strcmp(imp, "1.2.22") || !strcmp(imp, "Cisco-1.25")))) { @@ -1733,8 +1752,8 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring) logevent("We believe remote version has SSH1 ignore bug"); } - if (ssh->cfg.sshbug_plainpw1 == BUG_ON || - (ssh->cfg.sshbug_plainpw1 == BUG_AUTO && + if (ssh->cfg.sshbug_plainpw1 == FORCE_ON || + (ssh->cfg.sshbug_plainpw1 == AUTO && (!strcmp(imp, "Cisco-1.25")))) { /* * These versions need a plain password sent; they can't @@ -1745,8 +1764,8 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring) logevent("We believe remote version needs a plain SSH1 password"); } - if (ssh->cfg.sshbug_rsa1 == BUG_ON || - (ssh->cfg.sshbug_rsa1 == BUG_AUTO && + if (ssh->cfg.sshbug_rsa1 == FORCE_ON || + (ssh->cfg.sshbug_rsa1 == AUTO && (!strcmp(imp, "Cisco-1.25")))) { /* * These versions apparently have no clue whatever about @@ -1757,8 +1776,8 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring) logevent("We believe remote version can't handle RSA authentication"); } - if (ssh->cfg.sshbug_hmac2 == BUG_ON || - (ssh->cfg.sshbug_hmac2 == BUG_AUTO && + if (ssh->cfg.sshbug_hmac2 == FORCE_ON || + (ssh->cfg.sshbug_hmac2 == AUTO && (wc_match("2.1.0*", imp) || wc_match("2.0.*", imp) || wc_match("2.2.0*", imp) || wc_match("2.3.0*", imp) || wc_match("2.1 *", imp)))) { @@ -1769,9 +1788,9 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring) logevent("We believe remote version has SSH2 HMAC bug"); } - if (ssh->cfg.sshbug_derivekey2 == BUG_ON || - (ssh->cfg.sshbug_derivekey2 == BUG_AUTO && - (wc_match("2.0.0*", imp) || wc_match("2.0.1[01]*", imp) ))) { + if (ssh->cfg.sshbug_derivekey2 == FORCE_ON || + (ssh->cfg.sshbug_derivekey2 == AUTO && + (wc_match("2.0.0*", imp) || wc_match("2.0.10*", imp) ))) { /* * These versions have the key-derivation bug (failing to * include the literal shared secret in the hashes that @@ -1781,8 +1800,8 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring) logevent("We believe remote version has SSH2 key-derivation bug"); } - if (ssh->cfg.sshbug_rsapad2 == BUG_ON || - (ssh->cfg.sshbug_rsapad2 == BUG_AUTO && + if (ssh->cfg.sshbug_rsapad2 == FORCE_ON || + (ssh->cfg.sshbug_rsapad2 == AUTO && (wc_match("OpenSSH_2.[5-9]*", imp) || wc_match("OpenSSH_3.[0-2]*", imp)))) { /* @@ -1792,7 +1811,18 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring) logevent("We believe remote version has SSH2 RSA padding bug"); } - if (ssh->cfg.sshbug_dhgex2 == BUG_ON) { + if (ssh->cfg.sshbug_pksessid2 == FORCE_ON || + (ssh->cfg.sshbug_pksessid2 == AUTO && + wc_match("OpenSSH_2.[0-2]*", imp))) { + /* + * These versions have the SSH2 session-ID bug in + * public-key authentication. + */ + ssh->remote_bugs |= BUG_SSH2_PK_SESSIONID; + logevent("We believe remote version has SSH2 public-key-session-ID bug"); + } + + if (ssh->cfg.sshbug_dhgex2 == FORCE_ON) { /* * User specified the SSH2 DH GEX bug. */ @@ -2055,7 +2085,7 @@ static char *connect_to_host(Ssh ssh, char *host, int port, * Try to find host. */ logeventf(ssh, "Looking up host \"%s\"", host); - addr = name_lookup(host, port, realhost); + addr = name_lookup(host, port, realhost, &ssh->cfg); if ((err = sk_addr_error(addr)) != NULL) return err; @@ -2068,7 +2098,8 @@ static char *connect_to_host(Ssh ssh, char *host, int port, logeventf(ssh, "Connecting to %s port %d", addrbuf, port); } ssh->fn = &fn_table; - ssh->s = new_connection(addr, *realhost, port, 0, 1, nodelay, (Plug) ssh); + ssh->s = new_connection(addr, *realhost, port, + 0, 1, nodelay, (Plug) ssh, &ssh->cfg); if ((err = sk_socket_error(ssh->s)) != NULL) { ssh->s = NULL; return err; @@ -2180,7 +2211,13 @@ static int process_userpass_input(Ssh ssh, unsigned char *in, int inlen) return -1; break; default: - if (((c >= ' ' && c <= '~') || + /* + * This simplistic check for printability is disabled + * when we're doing password input, because some people + * have control characters in their passwords.o + */ + if ((!ssh->userpass_input_echo || + (c >= ' ' && c <= '~') || ((unsigned char) c >= 160)) && ssh->userpass_input_bufpos < ssh->userpass_input_buflen-1) { ssh->userpass_input_buffer[ssh->userpass_input_bufpos++] = c; @@ -2456,8 +2493,8 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, int ispkt) } s->tis_auth_refused = s->ccard_auth_refused = 0; /* Load the public half of ssh->cfg.keyfile so we notice if it's in Pageant */ - if (*ssh->cfg.keyfile) { - if (!rsakey_pubblob(ssh->cfg.keyfile, + if (!filename_is_null(ssh->cfg.keyfile)) { + if (!rsakey_pubblob(&ssh->cfg.keyfile, &s->publickey_blob, &s->publickey_bloblen)) s->publickey_blob = NULL; } else @@ -2585,7 +2622,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, int ispkt) if (s->authed) break; } - if (*ssh->cfg.keyfile && !s->tried_publickey) + if (!filename_is_null(ssh->cfg.keyfile) && !s->tried_publickey) s->pwpkt_type = SSH1_CMSG_AUTH_RSA; if (ssh->cfg.try_tis_auth && @@ -2650,8 +2687,9 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, int ispkt) char msgbuf[256]; if (flags & FLAG_VERBOSE) c_write_str(ssh, "Trying public key authentication.\r\n"); - logeventf(ssh, "Trying public key \"%s\"", ssh->cfg.keyfile); - type = key_type(ssh->cfg.keyfile); + logeventf(ssh, "Trying public key \"%s\"", + filename_to_str(&ssh->cfg.keyfile)); + type = key_type(&ssh->cfg.keyfile); if (type != SSH_KEYTYPE_SSH1) { sprintf(msgbuf, "Key is of wrong type (%s)", key_type_to_str(type)); @@ -2661,7 +2699,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, int ispkt) s->tried_publickey = 1; continue; } - if (!rsakey_encrypted(ssh->cfg.keyfile, &comment)) { + if (!rsakey_encrypted(&ssh->cfg.keyfile, &comment)) { if (flags & FLAG_VERBOSE) c_write_str(ssh, "No passphrase required.\r\n"); goto tryauth; @@ -2717,10 +2755,10 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, int ispkt) s->tried_publickey = 1; { - int ret = loadrsakey(ssh->cfg.keyfile, &s->key, s->password); + int ret = loadrsakey(&ssh->cfg.keyfile, &s->key, s->password); if (ret == 0) { c_write_str(ssh, "Couldn't load private key from "); - c_write_str(ssh, ssh->cfg.keyfile); + c_write_str(ssh, filename_to_str(&ssh->cfg.keyfile)); c_write_str(ssh, ".\r\n"); continue; /* go and try password */ } @@ -3121,7 +3159,7 @@ static void ssh1_protocol(Ssh ssh, unsigned char *in, int inlen, int ispkt) if (sport && dport) { if (type == 'L') { pfd_addforward(host, dport, *saddr ? saddr : NULL, - sport, ssh); + sport, ssh, &ssh->cfg); logeventf(ssh, "Local port %.*s%.*s%.*s%.*s%d%.*s" " forwarding to %s:%.*s%.*s%d%.*s", (int)(*saddr?strlen(saddr):0), *saddr?saddr:NULL, @@ -3285,7 +3323,7 @@ static void ssh1_protocol(Ssh ssh, unsigned char *in, int inlen, int ispkt) c->ssh = ssh; if (x11_init(&c->u.x11.s, ssh->cfg.x11_display, c, - ssh->x11auth, NULL, -1) != NULL) { + ssh->x11auth, NULL, -1, &ssh->cfg) != NULL) { logevent("opening X11 forward connection failed"); sfree(c); send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_FAILURE, @@ -3363,7 +3401,7 @@ static void ssh1_protocol(Ssh ssh, unsigned char *in, int inlen, int ispkt) sprintf(buf, "Received remote port open request for %s:%d", host, port); logevent(buf); - e = pfd_newconnect(&c->u.pfd.s, host, port, c); + e = pfd_newconnect(&c->u.pfd.s, host, port, c, &ssh->cfg); if (e != NULL) { char buf[256]; sprintf(buf, "Port open failed: %s", e); @@ -4030,6 +4068,7 @@ static int do_ssh2_transport(Ssh ssh, unsigned char *in, int inlen, int ispkt) SHA_Final(&ssh->exhash, s->exchange_hash); dh_cleanup(ssh->kex_ctx); + ssh->kex_ctx = NULL; #if 0 debug(("Exchange hash is:\n")); @@ -4386,20 +4425,22 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt) s->tried_keyb_inter = FALSE; s->kbd_inter_running = FALSE; /* Load the pub half of ssh->cfg.keyfile so we notice if it's in Pageant */ - if (*ssh->cfg.keyfile) { + if (!filename_is_null(ssh->cfg.keyfile)) { int keytype; - logeventf(ssh, "Reading private key file \"%.150s\"", ssh->cfg.keyfile); - keytype = key_type(ssh->cfg.keyfile); + logeventf(ssh, "Reading private key file \"%.150s\"", + filename_to_str(&ssh->cfg.keyfile)); + keytype = key_type(&ssh->cfg.keyfile); if (keytype == SSH_KEYTYPE_SSH2) { s->publickey_blob = - ssh2_userkey_loadpub(ssh->cfg.keyfile, NULL, + ssh2_userkey_loadpub(&ssh->cfg.keyfile, NULL, &s->publickey_bloblen); } else { char *msgbuf; logeventf(ssh, "Unable to use this key file (%s)", key_type_to_str(keytype)); msgbuf = dupprintf("Unable to use key file \"%.150s\"" - " (%s)\r\n", ssh->cfg.keyfile, + " (%s)\r\n", + filename_to_str(&ssh->cfg.keyfile), key_type_to_str(keytype)); c_write_str(ssh, msgbuf); sfree(msgbuf); @@ -4624,6 +4665,8 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt) ssh2_pkt_addstring_data(ssh, s->pkblob, s->pklen); s->siglen = ssh->pktout.length - 5 + 4 + 20; + if (ssh->remote_bugs & BUG_SSH2_PK_SESSIONID) + s->siglen -= 4; s->len = 1; /* message type */ s->len += 4 + s->pklen; /* key blob */ s->len += 4 + s->siglen; /* data to sign */ @@ -4639,8 +4682,10 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt) PUT_32BIT(s->q, s->siglen); s->q += 4; /* Now the data to be signed... */ - PUT_32BIT(s->q, 20); - s->q += 4; + if (!(ssh->remote_bugs & BUG_SSH2_PK_SESSIONID)) { + PUT_32BIT(s->q, 20); + s->q += 4; + } memcpy(s->q, ssh->v2_session_id, 20); s->q += 20; memcpy(s->q, ssh->pktout.data + 5, @@ -4690,7 +4735,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt) * willing to accept it. */ pub_blob = - (unsigned char *)ssh2_userkey_loadpub(ssh->cfg.keyfile, + (unsigned char *)ssh2_userkey_loadpub(&ssh->cfg.keyfile, &algorithm, &pub_blob_len); if (pub_blob) { @@ -4718,7 +4763,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt) * Actually attempt a serious authentication using * the key. */ - if (ssh2_userkey_encrypted(ssh->cfg.keyfile, &comment)) { + if (ssh2_userkey_encrypted(&ssh->cfg.keyfile, &comment)) { sprintf(s->pwprompt, "Passphrase for key \"%.100s\": ", comment); @@ -4870,7 +4915,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt) */ struct ssh2_userkey *key; - key = ssh2_load_userkey(ssh->cfg.keyfile, s->password); + key = ssh2_load_userkey(&ssh->cfg.keyfile, s->password); if (key == SSH2_WRONG_PASSPHRASE || key == NULL) { if (key == SSH2_WRONG_PASSPHRASE) { c_write_str(ssh, "Wrong passphrase\r\n"); @@ -4889,6 +4934,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt) } else { unsigned char *pkblob, *sigblob, *sigdata; int pkblob_len, sigblob_len, sigdata_len; + int p; /* * We have loaded the private key and the server @@ -4914,11 +4960,19 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt) * outgoing packet. */ sigdata_len = ssh->pktout.length - 5 + 4 + 20; + if (ssh->remote_bugs & BUG_SSH2_PK_SESSIONID) + sigdata_len -= 4; sigdata = smalloc(sigdata_len); - PUT_32BIT(sigdata, 20); - memcpy(sigdata + 4, ssh->v2_session_id, 20); - memcpy(sigdata + 24, ssh->pktout.data + 5, + p = 0; + if (!(ssh->remote_bugs & BUG_SSH2_PK_SESSIONID)) { + PUT_32BIT(sigdata+p, 20); + p += 4; + } + memcpy(sigdata+p, ssh->v2_session_id, 20); p += 20; + memcpy(sigdata+p, ssh->pktout.data + 5, ssh->pktout.length - 5); + p += ssh->pktout.length - 5; + assert(p == sigdata_len); sigblob = key->alg->sign(key->data, (char *)sigdata, sigdata_len, &sigblob_len); ssh2_add_sigblob(ssh, pkblob, pkblob_len, @@ -5185,7 +5239,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt) if (sport && dport) { if (type == 'L') { pfd_addforward(host, dport, *saddr ? saddr : NULL, - sport, ssh); + sport, ssh, &ssh->cfg); logeventf(ssh, "Local port %.*s%.*s%.*s%.*s%d%.*s" " forwarding to %s:%.*s%.*s%d%.*s", (int)(*saddr?strlen(saddr):0), *saddr?saddr:NULL, @@ -5746,7 +5800,8 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt) 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, port) != NULL) { + ssh->x11auth, addrstr, port, + &ssh->cfg) != NULL) { error = "Unable to open an X11 connection"; } else { c->type = CHAN_X11; @@ -5765,7 +5820,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt) error = "Remote port is not recognised"; } else { char *e = pfd_newconnect(&c->u.pfd.s, realpf->dhost, - realpf->dport, c); + realpf->dport, c, &ssh->cfg); logeventf(ssh, "Received remote port open request" " for %s:%d", realpf->dhost, realpf->dport); if (e != NULL) { @@ -5900,6 +5955,7 @@ static char *ssh_init(void *frontend_handle, void **backend_handle, ssh->sccomp = NULL; ssh->sc_comp_ctx = NULL; ssh->kex = NULL; + ssh->kex_ctx = NULL; ssh->hostkey = NULL; ssh->exitcode = -1; ssh->state = SSH_STATE_PREPACKET; @@ -5946,6 +6002,9 @@ static char *ssh_init(void *frontend_handle, void **backend_handle, ssh->term_width = ssh->cfg.width; ssh->term_height = ssh->cfg.height; + ssh->channels = NULL; + ssh->rportfwds = NULL; + ssh->send_ok = 0; ssh->editing = 0; ssh->echoing = 0; @@ -5962,6 +6021,65 @@ static char *ssh_init(void *frontend_handle, void **backend_handle, return NULL; } +static void ssh_free(void *handle) +{ + Ssh ssh = (Ssh) handle; + struct ssh_channel *c; + struct ssh_rportfwd *pf; + + if (ssh->v1_cipher_ctx) + ssh->cipher->free_context(ssh->v1_cipher_ctx); + if (ssh->cs_cipher_ctx) + ssh->cscipher->free_context(ssh->cs_cipher_ctx); + if (ssh->sc_cipher_ctx) + ssh->sccipher->free_context(ssh->sc_cipher_ctx); + if (ssh->cs_mac_ctx) + ssh->csmac->free_context(ssh->cs_mac_ctx); + if (ssh->sc_mac_ctx) + ssh->scmac->free_context(ssh->sc_mac_ctx); + if (ssh->cs_comp_ctx) + ssh->cscomp->compress_cleanup(ssh->cs_comp_ctx); + if (ssh->sc_comp_ctx) + ssh->sccomp->compress_cleanup(ssh->sc_comp_ctx); + if (ssh->kex_ctx) + dh_cleanup(ssh->kex_ctx); + sfree(ssh->savedhost); + + if (ssh->channels) { + while ((c = delpos234(ssh->channels, 0)) != NULL) { + switch (c->type) { + case CHAN_X11: + if (c->u.x11.s != NULL) + x11_close(c->u.x11.s); + break; + case CHAN_SOCKDATA: + if (c->u.pfd.s != NULL) + pfd_close(c->u.pfd.s); + break; + } + sfree(c); + } + freetree234(ssh->channels); + } + + if (ssh->rportfwds) { + while ((pf = delpos234(ssh->rportfwds, 0)) != NULL) + sfree(pf); + freetree234(ssh->rportfwds); + } + sfree(ssh->deferred_send_data); + if (ssh->x11auth) + x11_free_auth(ssh->x11auth); + sfree(ssh->do_ssh_init_state); + sfree(ssh->do_ssh1_login_state); + sfree(ssh->do_ssh2_transport_state); + sfree(ssh->do_ssh2_authconn_state); + + if (ssh->s) + sk_close(ssh->s); + sfree(ssh); +} + /* * Reconfigure the SSH backend. * @@ -6234,6 +6352,7 @@ extern int ssh_fallback_cmd(void *handle) Backend ssh_backend = { ssh_init, + ssh_free, ssh_reconfig, ssh_send, ssh_sendbuffer,