X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/23828b7e45c6ce1972eaacfc1e8bafe0c54b9434..fae1a71b5a2fd1b2983be523350c3dae4b5e6903:/ssh.c diff --git a/ssh.c b/ssh.c index a1616045..64a069ef 100644 --- a/ssh.c +++ b/ssh.c @@ -7,6 +7,7 @@ #include #include #include +#include #include "putty.h" #include "tree234.h" @@ -82,6 +83,9 @@ #define SSH2_MSG_KEX_DH_GEX_GROUP 31 /* 0x1f */ #define SSH2_MSG_KEX_DH_GEX_INIT 32 /* 0x20 */ #define SSH2_MSG_KEX_DH_GEX_REPLY 33 /* 0x21 */ +#define SSH2_MSG_KEXRSA_PUBKEY 30 /* 0x1e */ +#define SSH2_MSG_KEXRSA_SECRET 31 /* 0x1f */ +#define SSH2_MSG_KEXRSA_DONE 32 /* 0x20 */ #define SSH2_MSG_USERAUTH_REQUEST 50 /* 0x32 */ #define SSH2_MSG_USERAUTH_FAILURE 51 /* 0x33 */ #define SSH2_MSG_USERAUTH_SUCCESS 52 /* 0x34 */ @@ -111,6 +115,7 @@ */ #define SSH2_PKTCTX_DHGROUP 0x0001 #define SSH2_PKTCTX_DHGEX 0x0002 +#define SSH2_PKTCTX_RSAKEX 0x0004 #define SSH2_PKTCTX_KEX_MASK 0x000F #define SSH2_PKTCTX_PUBLICKEY 0x0010 #define SSH2_PKTCTX_PASSWORD 0x0020 @@ -338,6 +343,9 @@ static char *ssh2_pkt_type(int pkt_ctx, int type) translatec(SSH2_MSG_KEX_DH_GEX_GROUP, SSH2_PKTCTX_DHGEX); translatec(SSH2_MSG_KEX_DH_GEX_INIT, SSH2_PKTCTX_DHGEX); translatec(SSH2_MSG_KEX_DH_GEX_REPLY, SSH2_PKTCTX_DHGEX); + translatec(SSH2_MSG_KEXRSA_PUBKEY, SSH2_PKTCTX_RSAKEX); + translatec(SSH2_MSG_KEXRSA_SECRET, SSH2_PKTCTX_RSAKEX); + translatec(SSH2_MSG_KEXRSA_DONE, SSH2_PKTCTX_RSAKEX); translate(SSH2_MSG_USERAUTH_REQUEST); translate(SSH2_MSG_USERAUTH_FAILURE); translate(SSH2_MSG_USERAUTH_SUCCESS); @@ -1052,7 +1060,7 @@ static void c_write_stderr(int trusted, const char *buf, int len) { int i; for (i = 0; i < len; i++) - if (buf[i] != '\r' && (trusted || buf[i] & 0x60)) + if (buf[i] != '\r' && (trusted || buf[i] == '\n' || (buf[i] & 0x60))) fputc(buf[i], stderr); } @@ -1442,11 +1450,17 @@ static int s_wrpkt_prepare(Ssh ssh, struct Packet *pkt, int *offset_p) return biglen + 4; /* len(length+padding+type+data+CRC) */ } +static int s_write(Ssh ssh, void *data, int len) +{ + log_packet(ssh->logctx, PKT_OUTGOING, -1, NULL, data, len, 0, NULL); + return sk_write(ssh->s, (char *)data, len); +} + static void s_wrpkt(Ssh ssh, struct Packet *pkt) { int len, backlog, offset; len = s_wrpkt_prepare(ssh, pkt, &offset); - backlog = sk_write(ssh->s, (char *)pkt->data + offset, len); + backlog = s_write(ssh, pkt->data + offset, len); if (backlog > SSH_MAX_BACKLOG) ssh_throttle_all(ssh, 1, backlog); ssh_free_packet(pkt); @@ -1483,6 +1497,7 @@ static struct Packet *construct_packet(Ssh ssh, int pkttype, va_list ap) while ((argtype = va_arg(ap, int)) != PKT_END) { unsigned char *argp, argchar; + char *sargp; unsigned long argint; int arglen; switch (argtype) { @@ -1501,8 +1516,8 @@ static struct Packet *construct_packet(Ssh ssh, int pkttype, va_list ap) ssh_pkt_adddata(pkt, argp, arglen); break; case PKT_STR: - argp = va_arg(ap, unsigned char *); - ssh_pkt_addstring(pkt, argp); + sargp = va_arg(ap, char *); + ssh_pkt_addstring(pkt, sargp); break; case PKT_BIGNUM: bn = va_arg(ap, Bignum); @@ -1648,7 +1663,7 @@ static void ssh_pkt_addstring(struct Packet *pkt, char *data) static void ssh1_pkt_addmp(struct Packet *pkt, Bignum b) { int len = ssh1_bignum_length(b); - unsigned char *data = snewn(len, char); + unsigned char *data = snewn(len, unsigned char); (void) ssh1_write_bignum(data, b); ssh_pkt_adddata(pkt, data, len); sfree(data); @@ -1830,7 +1845,7 @@ static void ssh2_pkt_send_noqueue(Ssh ssh, struct Packet *pkt) return; } len = ssh2_pkt_construct(ssh, pkt); - backlog = sk_write(ssh->s, (char *)pkt->data, len); + backlog = s_write(ssh, pkt->data, len); if (backlog > SSH_MAX_BACKLOG) ssh_throttle_all(ssh, 1, backlog); @@ -1856,6 +1871,7 @@ static void ssh2_pkt_defer_noqueue(Ssh ssh, struct Packet *pkt, int noignore) * get encrypted with a known IV. */ struct Packet *ipkt = ssh2_pkt_init(SSH2_MSG_IGNORE); + ssh2_pkt_addstring_start(ipkt); ssh2_pkt_defer_noqueue(ssh, ipkt, TRUE); } len = ssh2_pkt_construct(ssh, pkt); @@ -1898,7 +1914,6 @@ static void ssh2_pkt_send(Ssh ssh, struct Packet *pkt) ssh2_pkt_send_noqueue(ssh, pkt); } -#if 0 /* disused */ /* * Either queue or defer a packet, depending on whether queueing is * set. @@ -1910,7 +1925,6 @@ static void ssh2_pkt_defer(Ssh ssh, struct Packet *pkt) else ssh2_pkt_defer_noqueue(ssh, pkt, FALSE); } -#endif /* * Send the whole deferred data block constructed by @@ -1928,8 +1942,7 @@ static void ssh2_pkt_defer(Ssh ssh, struct Packet *pkt) static void ssh_pkt_defersend(Ssh ssh) { int backlog; - backlog = sk_write(ssh->s, (char *)ssh->deferred_send_data, - ssh->deferred_len); + backlog = s_write(ssh, ssh->deferred_send_data, ssh->deferred_len); ssh->deferred_len = ssh->deferred_size = 0; sfree(ssh->deferred_send_data); ssh->deferred_send_data = NULL; @@ -1945,6 +1958,74 @@ static void ssh_pkt_defersend(Ssh ssh) } /* + * Send a packet whose length needs to be disguised (typically + * passwords or keyboard-interactive responses). + */ +static void ssh2_pkt_send_with_padding(Ssh ssh, struct Packet *pkt, + int padsize) +{ +#if 0 + if (0) { + /* + * The simplest way to do this is to adjust the + * variable-length padding field in the outgoing packet. + * + * Currently compiled out, because some Cisco SSH servers + * don't like excessively padded packets (bah, why's it + * always Cisco?) + */ + pkt->forcepad = padsize; + ssh2_pkt_send(ssh, pkt); + } else +#endif + { + /* + * If we can't do that, however, an alternative approach is + * to use the pkt_defer mechanism to bundle the packet + * tightly together with an SSH_MSG_IGNORE such that their + * combined length is a constant. So first we construct the + * final form of this packet and defer its sending. + */ + ssh2_pkt_defer(ssh, pkt); + + /* + * Now construct an SSH_MSG_IGNORE which includes a string + * that's an exact multiple of the cipher block size. (If + * the cipher is NULL so that the block size is + * unavailable, we don't do this trick at all, because we + * gain nothing by it.) + */ + if (ssh->cscipher) { + int stringlen, i; + + stringlen = (256 - ssh->deferred_len); + stringlen += ssh->cscipher->blksize - 1; + stringlen -= (stringlen % ssh->cscipher->blksize); + if (ssh->cscomp) { + /* + * Temporarily disable actual compression, so we + * can guarantee to get this string exactly the + * length we want it. The compression-disabling + * routine should return an integer indicating how + * many bytes we should adjust our string length + * by. + */ + stringlen -= + ssh->cscomp->disable_compression(ssh->cs_comp_ctx); + } + pkt = ssh2_pkt_init(SSH2_MSG_IGNORE); + ssh2_pkt_addstring_start(pkt); + for (i = 0; i < stringlen; i++) { + char c = (char) random_byte(); + ssh2_pkt_addstring_data(pkt, &c, 1); + } + ssh2_pkt_defer(ssh, pkt); + } + ssh_pkt_defersend(ssh); + } +} + +/* * Send all queued SSH-2 packets. We send them by means of * ssh2_pkt_defer_noqueue(), in case they included a pair of * packets that needed to be lumped together. @@ -2161,6 +2242,13 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring) ssh->remote_bugs = 0; + /* + * General notes on server version strings: + * - Not all servers reporting "Cisco-1.25" have all the bugs listed + * here -- in particular, we've heard of one that's perfectly happy + * with SSH1_MSG_IGNOREs -- but this string never seems to change, + * so we can't distinguish them. + */ if (ssh->cfg.sshbug_ignore1 == FORCE_ON || (ssh->cfg.sshbug_ignore1 == AUTO && (!strcmp(imp, "1.2.18") || !strcmp(imp, "1.2.19") || @@ -2418,7 +2506,7 @@ static int do_ssh_init(Ssh ssh, unsigned char c) } logeventf(ssh, "We claim version: %.*s", strcspn(verstring, "\015\012"), verstring); - sk_write(ssh->s, verstring, strlen(verstring)); + s_write(ssh, verstring, strlen(verstring)); sfree(verstring); if (ssh->version == 2) do_ssh2_transport(ssh, NULL, -1, NULL); @@ -2438,7 +2526,9 @@ static int do_ssh_init(Ssh ssh, unsigned char c) static void ssh_process_incoming_data(Ssh ssh, unsigned char **data, int *datalen) { - struct Packet *pktin = ssh->s_rdpkt(ssh, data, datalen); + struct Packet *pktin; + + pktin = ssh->s_rdpkt(ssh, data, datalen); if (pktin) { ssh->protocol(ssh, NULL, 0, pktin); ssh_free_packet(pktin); @@ -2481,6 +2571,9 @@ static void ssh_set_frozen(Ssh ssh, int frozen) static void ssh_gotdata(Ssh ssh, unsigned char *data, int datalen) { + /* Log raw data, if we're in that mode. */ + log_packet(ssh->logctx, PKT_INCOMING, -1, NULL, data, datalen, 0, NULL); + crBegin(ssh->ssh_gotdata_crstate); /* @@ -2617,6 +2710,9 @@ static int ssh_closing(Plug plug, const char *error_msg, int error_code, error_msg = "Server closed network connection"; } + if (ssh->close_expected && ssh->clean_exit && ssh->exitcode < 0) + ssh->exitcode = 0; + if (need_notify) notify_remote_exit(ssh->frontend); @@ -3476,6 +3572,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, /* and try again */ } else { assert(0 && "unexpected return from loadrsakey()"); + got_passphrase = FALSE; /* placate optimisers */ } } @@ -3693,19 +3790,19 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, * magnitude of the password length, but it will * introduce a bit of extra uncertainty. * - * A few servers (the old 1.2.18 through 1.2.22) - * can't deal with SSH1_MSG_IGNORE. For these - * servers, we need an alternative defence. We make - * use of the fact that the password is interpreted - * as a C string: so we can append a NUL, then some - * random data. + * A few servers can't deal with SSH1_MSG_IGNORE, at + * least in this context. For these servers, we need + * an alternative defence. We make use of the fact + * that the password is interpreted as a C string: + * so we can append a NUL, then some random data. * - * One server (a Cisco one) can deal with neither - * SSH1_MSG_IGNORE _nor_ a padded password string. - * For this server we are left with no defences + * A few servers can deal with neither SSH1_MSG_IGNORE + * here _nor_ a padded password string. + * For these servers we are left with no defences * against password length sniffing. */ - if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE)) { + if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE) && + !(ssh->remote_bugs & BUG_NEEDS_SSH1_PLAIN_PASSWORD)) { /* * The server can deal with SSH1_MSG_IGNORE, so * we can use the primary defence. @@ -3774,10 +3871,8 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, PKTT_OTHER, PKT_END); } else { /* - * The server has _both_ - * BUG_CHOKES_ON_SSH1_IGNORE and - * BUG_NEEDS_SSH1_PLAIN_PASSWORD. There is - * therefore nothing we can do. + * The server is believed unable to cope with + * any of our password camouflage methods. */ int len; len = strlen(s->cur_prompt->prompts[0]->result); @@ -4559,7 +4654,7 @@ static void ssh1_msg_channel_data(Ssh ssh, struct Packet *pktin) /* Data for an agent message. Buffer it. */ while (len > 0) { if (c->u.a.lensofar < 4) { - unsigned int l = min(4 - c->u.a.lensofar, len); + unsigned int l = min(4 - c->u.a.lensofar, (unsigned)len); memcpy(c->u.a.msglen + c->u.a.lensofar, p, l); p += l; @@ -4576,7 +4671,7 @@ static void ssh1_msg_channel_data(Ssh ssh, struct Packet *pktin) if (c->u.a.lensofar >= 4 && len > 0) { unsigned int l = min(c->u.a.totallen - c->u.a.lensofar, - len); + (unsigned)len); memcpy(c->u.a.message + c->u.a.lensofar, p, l); p += l; @@ -5017,9 +5112,10 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, const struct ssh_mac *scmac_tobe; const struct ssh_compress *cscomp_tobe; const struct ssh_compress *sccomp_tobe; - char *hostkeydata, *sigdata, *keystr, *fingerprint; - int hostkeylen, siglen; + char *hostkeydata, *sigdata, *rsakeydata, *keystr, *fingerprint; + int hostkeylen, siglen, rsakeylen; void *hkey; /* actual host key */ + void *rsakey; /* for RSA kex */ unsigned char exchange_hash[SSH2_KEX_MAX_HASH_LEN]; int n_preferred_kex; const struct ssh_kexes *preferred_kex[KEX_MAX]; @@ -5073,6 +5169,10 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, s->preferred_kex[s->n_preferred_kex++] = &ssh_diffiehellman_group1; break; + case KEX_RSA: + s->preferred_kex[s->n_preferred_kex++] = + &ssh_rsa_kex; + break; case KEX_WARN: /* Flag for later. Don't bother if it's the last in * the list. */ @@ -5472,6 +5572,8 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, crWaitUntil(pktin); /* Ignore packet */ } + if (ssh->kex->main_type == KEXTYPE_DH) { + /* XXX The lines below should be reindented before this is committed.*/ /* * Work out the number of bits of key we will need from the key * exchange. We start with the maximum key length of either @@ -5547,6 +5649,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, } set_busy_status(ssh->frontend, BUSY_CPU); /* cogitate */ ssh_pkt_getstring(pktin, &s->hostkeydata, &s->hostkeylen); + s->hkey = ssh->hostkey->newkey(s->hostkeydata, s->hostkeylen); s->f = ssh2_pkt_getmp(pktin); if (!s->f) { bombout(("unable to parse key exchange reply packet")); @@ -5568,11 +5671,120 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, } hash_mpint(ssh->kex->hash, ssh->exhash, s->e); hash_mpint(ssh->kex->hash, ssh->exhash, s->f); + + dh_cleanup(ssh->kex_ctx); + freebn(s->f); + if (!ssh->kex->pdata) { + freebn(s->g); + freebn(s->p); + } + /* XXX end incorrectly-indented section */ + } else { + logeventf(ssh, "Doing RSA key exchange with hash %s", + ssh->kex->hash->text_name); + ssh->pkt_ctx |= SSH2_PKTCTX_RSAKEX; + /* + * RSA key exchange. First expect a KEXRSA_PUBKEY packet + * from the server. + */ + crWaitUntil(pktin); + if (pktin->type != SSH2_MSG_KEXRSA_PUBKEY) { + bombout(("expected RSA public key packet from server")); + crStop(0); + } + + ssh_pkt_getstring(pktin, &s->hostkeydata, &s->hostkeylen); + hash_string(ssh->kex->hash, ssh->exhash, + s->hostkeydata, s->hostkeylen); + s->hkey = ssh->hostkey->newkey(s->hostkeydata, s->hostkeylen); + + { + char *keydata; + ssh_pkt_getstring(pktin, &keydata, &s->rsakeylen); + s->rsakeydata = snewn(s->rsakeylen, char); + memcpy(s->rsakeydata, keydata, s->rsakeylen); + } + + s->rsakey = ssh_rsakex_newkey(s->rsakeydata, s->rsakeylen); + if (!s->rsakey) { + sfree(s->rsakeydata); + bombout(("unable to parse RSA public key from server")); + crStop(0); + } + + hash_string(ssh->kex->hash, ssh->exhash, s->rsakeydata, s->rsakeylen); + + /* + * Next, set up a shared secret K, of precisely KLEN - + * 2*HLEN - 49 bits, where KLEN is the bit length of the + * RSA key modulus and HLEN is the bit length of the hash + * we're using. + */ + { + int klen = ssh_rsakex_klen(s->rsakey); + int nbits = klen - (2*ssh->kex->hash->hlen*8 + 49); + int i, byte = 0; + unsigned char *kstr1, *kstr2, *outstr; + int kstr1len, kstr2len, outstrlen; + + s->K = bn_power_2(nbits - 1); + + for (i = 0; i < nbits; i++) { + if ((i & 7) == 0) { + byte = random_byte(); + } + bignum_set_bit(s->K, i, (byte >> (i & 7)) & 1); + } + + /* + * Encode this as an mpint. + */ + kstr1 = ssh2_mpint_fmt(s->K, &kstr1len); + kstr2 = snewn(kstr2len = 4 + kstr1len, unsigned char); + PUT_32BIT(kstr2, kstr1len); + memcpy(kstr2 + 4, kstr1, kstr1len); + + /* + * Encrypt it with the given RSA key. + */ + outstrlen = (klen + 7) / 8; + outstr = snewn(outstrlen, unsigned char); + ssh_rsakex_encrypt(ssh->kex->hash, kstr2, kstr2len, + outstr, outstrlen, s->rsakey); + + /* + * And send it off in a return packet. + */ + s->pktout = ssh2_pkt_init(SSH2_MSG_KEXRSA_SECRET); + ssh2_pkt_addstring_start(s->pktout); + ssh2_pkt_addstring_data(s->pktout, outstr, outstrlen); + ssh2_pkt_send_noqueue(ssh, s->pktout); + + hash_string(ssh->kex->hash, ssh->exhash, outstr, outstrlen); + + sfree(kstr2); + sfree(kstr1); + sfree(outstr); + } + + ssh_rsakex_freekey(s->rsakey); + + crWaitUntil(pktin); + if (pktin->type != SSH2_MSG_KEXRSA_DONE) { + sfree(s->rsakeydata); + bombout(("expected signature packet from server")); + crStop(0); + } + + ssh_pkt_getstring(pktin, &s->sigdata, &s->siglen); + + sfree(s->rsakeydata); + } + hash_mpint(ssh->kex->hash, ssh->exhash, s->K); assert(ssh->kex->hash->hlen <= sizeof(s->exchange_hash)); ssh->kex->hash->final(ssh->exhash, s->exchange_hash); - dh_cleanup(ssh->kex_ctx); ssh->kex_ctx = NULL; #if 0 @@ -5580,7 +5792,6 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, dmemdump(s->exchange_hash, ssh->kex->hash->hlen); #endif - s->hkey = ssh->hostkey->newkey(s->hostkeydata, s->hostkeylen); if (!s->hkey || !ssh->hostkey->verifysig(s->hkey, s->sigdata, s->siglen, (char *)s->exchange_hash, @@ -5762,14 +5973,9 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, ssh->sccomp->text_name); /* - * Free key exchange data. + * Free shared secret. */ - freebn(s->f); freebn(s->K); - if (!ssh->kex->pdata) { - freebn(s->g); - freebn(s->p); - } /* * Key exchange is over. Loop straight back round if we have a @@ -5996,7 +6202,8 @@ static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin) case CHAN_AGENT: while (length > 0) { if (c->u.a.lensofar < 4) { - unsigned int l = min(4 - c->u.a.lensofar, length); + unsigned int l = min(4 - c->u.a.lensofar, + (unsigned)length); memcpy(c->u.a.msglen + c->u.a.lensofar, data, l); data += l; @@ -6013,7 +6220,7 @@ static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin) if (c->u.a.lensofar >= 4 && length > 0) { unsigned int l = min(c->u.a.totallen - c->u.a.lensofar, - length); + (unsigned)length); memcpy(c->u.a.message + c->u.a.lensofar, data, l); data += l; @@ -6282,11 +6489,13 @@ static void ssh2_msg_channel_request(Ssh ssh, struct Packet *pktin) is_plausible = FALSE; } } + ssh->exitcode = 128; /* means `unknown signal' */ if (is_plausible) { if (is_int) { /* Old non-standard OpenSSH. */ int signum = ssh_pkt_getuint32(pktin); fmt_sig = dupprintf(" %d", signum); + ssh->exitcode = 128 + signum; } else { /* As per the drafts. */ char *sig; @@ -6298,6 +6507,60 @@ static void ssh2_msg_channel_request(Ssh ssh, struct Packet *pktin) fmt_sig = dupprintf(" \"%.*s\"", siglen, sig); } + + /* + * Really hideous method of translating the + * signal description back into a locally + * meaningful number. + */ + + if (0) + ; +#define TRANSLATE_SIGNAL(s) \ + else if (siglen == lenof(#s)-1 && !memcmp(sig, #s, siglen)) \ + ssh->exitcode = 128 + SIG ## s +#ifdef SIGABRT + TRANSLATE_SIGNAL(ABRT); +#endif +#ifdef SIGALRM + TRANSLATE_SIGNAL(ALRM); +#endif +#ifdef SIGFPE + TRANSLATE_SIGNAL(FPE); +#endif +#ifdef SIGHUP + TRANSLATE_SIGNAL(HUP); +#endif +#ifdef SIGILL + TRANSLATE_SIGNAL(ILL); +#endif +#ifdef SIGINT + TRANSLATE_SIGNAL(INT); +#endif +#ifdef SIGKILL + TRANSLATE_SIGNAL(KILL); +#endif +#ifdef SIGPIPE + TRANSLATE_SIGNAL(PIPE); +#endif +#ifdef SIGQUIT + TRANSLATE_SIGNAL(QUIT); +#endif +#ifdef SIGSEGV + TRANSLATE_SIGNAL(SEGV); +#endif +#ifdef SIGTERM + TRANSLATE_SIGNAL(TERM); +#endif +#ifdef SIGUSR1 + TRANSLATE_SIGNAL(USR1); +#endif +#ifdef SIGUSR2 + TRANSLATE_SIGNAL(USR2); +#endif +#undef TRANSLATE_SIGNAL + else + ssh->exitcode = 128; } core = ssh2_pkt_getbool(pktin); ssh_pkt_getstring(pktin, &msg, &msglen); @@ -7358,7 +7621,6 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * Send the responses to the server. */ s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_INFO_RESPONSE); - s->pktout->forcepad = 256; ssh2_pkt_adduint32(s->pktout, s->num_prompts); for (i=0; i < s->num_prompts; i++) { dont_log_password(ssh, s->pktout, PKTLOG_BLANK); @@ -7366,7 +7628,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, s->cur_prompt->prompts[i]->result); end_log_omission(ssh, s->pktout); } - ssh2_pkt_send(ssh, s->pktout); + ssh2_pkt_send_with_padding(ssh, s->pktout, 256); /* * Get the next packet in case it's another @@ -7436,7 +7698,6 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * people who find out how long their password is! */ s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); - s->pktout->forcepad = 256; ssh2_pkt_addstring(s->pktout, s->username); ssh2_pkt_addstring(s->pktout, "ssh-connection"); /* service requested */ @@ -7445,7 +7706,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, dont_log_password(ssh, s->pktout, PKTLOG_BLANK); ssh2_pkt_addstring(s->pktout, s->password); end_log_omission(ssh, s->pktout); - ssh2_pkt_send(ssh, s->pktout); + ssh2_pkt_send_with_padding(ssh, s->pktout, 256); logevent("Sent password"); s->type = AUTH_TYPE_PASSWORD; @@ -7487,6 +7748,21 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, s->cur_prompt->instruction = dupprintf("%.*s", prompt_len, prompt); s->cur_prompt->instr_reqd = TRUE; + /* + * There's no explicit requirement in the protocol + * for the "old" passwords in the original and + * password-change messages to be the same, and + * apparently some Cisco kit supports password change + * by the user entering a blank password originally + * and the real password subsequently, so, + * reluctantly, we prompt for the old password again. + * + * (On the other hand, some servers don't even bother + * to check this field.) + */ + add_prompt(s->cur_prompt, + dupstr("Current password (blank for previously entered password): "), + FALSE, SSH_MAX_PASSWORD_LEN); add_prompt(s->cur_prompt, dupstr("Enter new password: "), FALSE, SSH_MAX_PASSWORD_LEN); add_prompt(s->cur_prompt, dupstr("Confirm new password: "), @@ -7520,10 +7796,25 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, } /* - * Check the two passwords match. + * If the user specified a new original password + * (IYSWIM), overwrite any previously specified + * one. + * (A side effect is that the user doesn't have to + * re-enter it if they louse up the new password.) */ - got_new = (strcmp(s->cur_prompt->prompts[0]->result, - s->cur_prompt->prompts[1]->result) + if (s->cur_prompt->prompts[0]->result[0]) { + memset(s->password, 0, strlen(s->password)); + /* burn the evidence */ + sfree(s->password); + s->password = + dupstr(s->cur_prompt->prompts[0]->result); + } + + /* + * Check the two new passwords match. + */ + got_new = (strcmp(s->cur_prompt->prompts[1]->result, + s->cur_prompt->prompts[2]->result) == 0); if (!got_new) /* They don't. Silly user. */ @@ -7536,7 +7827,6 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * (see above for padding rationale) */ s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); - s->pktout->forcepad = 256; ssh2_pkt_addstring(s->pktout, s->username); ssh2_pkt_addstring(s->pktout, "ssh-connection"); /* service requested */ @@ -7545,10 +7835,10 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, dont_log_password(ssh, s->pktout, PKTLOG_BLANK); ssh2_pkt_addstring(s->pktout, s->password); ssh2_pkt_addstring(s->pktout, - s->cur_prompt->prompts[0]->result); + s->cur_prompt->prompts[1]->result); free_prompts(s->cur_prompt); end_log_omission(ssh, s->pktout); - ssh2_pkt_send(ssh, s->pktout); + ssh2_pkt_send_with_padding(ssh, s->pktout, 256); logevent("Sent new password"); /* @@ -8733,8 +9023,7 @@ static void ssh_unthrottle(void *handle, int bufsize) ssh1_throttle(ssh, -1); } } else { - if (ssh->mainchan && ssh->mainchan->closes == 0) - ssh2_set_window(ssh->mainchan, OUR_V2_WINSIZE - bufsize); + ssh2_set_window(ssh->mainchan, OUR_V2_WINSIZE - bufsize); } }