X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/3c112d53d0733f8dcb05d15a4c5e4f9ffffef89b..c34c50f3334f840b7e5a054006071dc1f360686b:/ssh.c diff --git a/ssh.c b/ssh.c index 969ffbc0..fc75e91a 100644 --- a/ssh.c +++ b/ssh.c @@ -729,6 +729,7 @@ struct ssh_tag { tree234 *channels; /* indexed by local id */ struct ssh_channel *mainchan; /* primary session channel */ + int ncmode; /* is primary channel direct-tcpip? */ int exitcode; int close_expected; int clean_exit; @@ -1051,7 +1052,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); } @@ -1441,11 +1442,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); @@ -1829,7 +1836,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); @@ -1927,8 +1934,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; @@ -2160,6 +2166,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") || @@ -2417,7 +2430,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); @@ -2437,7 +2450,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); @@ -2480,6 +2495,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); /* @@ -3692,19 +3710,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. @@ -3773,10 +3791,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); @@ -7486,6 +7502,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: "), @@ -7519,10 +7550,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.) + */ + 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[0]->result, - s->cur_prompt->prompts[1]->result) + got_new = (strcmp(s->cur_prompt->prompts[1]->result, + s->cur_prompt->prompts[2]->result) == 0); if (!got_new) /* They don't. Silly user. */ @@ -7544,7 +7590,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); 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); @@ -7622,7 +7668,58 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, /* * Create the main session channel. */ - if (!ssh->cfg.ssh_no_shell) { + if (ssh->cfg.ssh_no_shell) { + ssh->mainchan = NULL; + } else if (*ssh->cfg.ssh_nc_host) { + /* + * Just start a direct-tcpip channel and use it as the main + * channel. + */ + ssh->mainchan = snew(struct ssh_channel); + ssh->mainchan->ssh = ssh; + ssh->mainchan->localid = alloc_channel_id(ssh); + 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 = OUR_V2_WINSIZE; + 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); + ssh2_pkt_adduint32(s->pktout, ssh->cfg.ssh_nc_port); + /* + * There's nothing meaningful to put in the originator + * fields, but some servers insist on syntactically correct + * information. + */ + ssh2_pkt_addstring(s->pktout, "0.0.0.0"); + ssh2_pkt_adduint32(s->pktout, 0); + ssh2_pkt_send(ssh, s->pktout); + + crWaitUntilV(pktin); + if (pktin->type != SSH2_MSG_CHANNEL_OPEN_CONFIRMATION) { + bombout(("Server refused to open a direct-tcpip channel")); + crStopV; + /* FIXME: error data comes back in FAILURE packet */ + } + if (ssh_pkt_getuint32(pktin) != ssh->mainchan->localid) { + bombout(("Server's channel confirmation cited wrong channel")); + crStopV; + } + 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"); + ssh->ncmode = TRUE; + } else { ssh->mainchan = snew(struct ssh_channel); ssh->mainchan->ssh = ssh; ssh->mainchan->localid = alloc_channel_id(ssh); @@ -7653,8 +7750,8 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, add234(ssh->channels, ssh->mainchan); update_specials_menu(ssh->frontend); logevent("Opened channel for session"); - } else - ssh->mainchan = NULL; + ssh->ncmode = FALSE; + } /* * Now we have a channel, make dispatch table entries for @@ -7677,7 +7774,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, /* * Potentially enable X11 forwarding. */ - if (ssh->mainchan && ssh->cfg.x11_forward) { + if (ssh->mainchan && !ssh->ncmode && ssh->cfg.x11_forward) { char proto[20], data[64]; logevent("Requesting X11 forwarding"); ssh->x11auth = x11_invent_auth(proto, sizeof(proto), @@ -7725,7 +7822,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, /* * Potentially enable agent forwarding. */ - if (ssh->mainchan && ssh->cfg.agentfwd && agent_exists()) { + if (ssh->mainchan && !ssh->ncmode && ssh->cfg.agentfwd && agent_exists()) { logevent("Requesting OpenSSH-style agent forwarding"); s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST); ssh2_pkt_adduint32(s->pktout, ssh->mainchan->remoteid); @@ -7751,7 +7848,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, /* * Now allocate a pty for the session. */ - if (ssh->mainchan && !ssh->cfg.nopty) { + if (ssh->mainchan && !ssh->ncmode && !ssh->cfg.nopty) { /* Unpick the terminal-speed string. */ /* XXX perhaps we should allow no speeds to be sent. */ ssh->ospeed = 38400; ssh->ispeed = 38400; /* last-resort defaults */ @@ -7801,7 +7898,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * Simplest thing here is to send all the requests at once, and * then wait for a whole bunch of successes or failures. */ - if (ssh->mainchan && *ssh->cfg.environmt) { + if (ssh->mainchan && !ssh->ncmode && *ssh->cfg.environmt) { char *e = ssh->cfg.environmt; char *var, *varend, *val; @@ -7866,7 +7963,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * this twice if the config data has provided a second choice * of command. */ - if (ssh->mainchan) while (1) { + if (ssh->mainchan && !ssh->ncmode) while (1) { int subsys; char *cmd; @@ -8725,10 +8822,10 @@ void ssh_send_port_open(void *channel, char *hostname, int port, char *org) } } -static Socket ssh_socket(void *handle) +static int ssh_connected(void *handle) { Ssh ssh = (Ssh) handle; - return ssh->s; + return ssh->s != NULL; } static int ssh_sendok(void *handle) @@ -8798,7 +8895,7 @@ Backend ssh_backend = { ssh_size, ssh_special, ssh_get_specials, - ssh_socket, + ssh_connected, ssh_return_exitcode, ssh_sendok, ssh_ldisc,