X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/ca20bfcf9f32391ac683100cd3f1bfeb48083236..3ad9d396e3e57477b4da4b20665ca33edd5d7f67:/ssh.c diff --git a/ssh.c b/ssh.c index f8ba5217..c03cd6db 100644 --- a/ssh.c +++ b/ssh.c @@ -428,6 +428,13 @@ static struct Packet pktout = { 0, 0, NULL, NULL, 0 }; static unsigned char *deferred_send_data = NULL; static int deferred_len = 0, deferred_size = 0; +/* + * Gross hack: pscp will try to start SFTP but fall back to scp1 if + * that fails. This variable is the means by which scp.c can reach + * into the SSH code and find out which one it got. + */ +int ssh_fallback_cmd = 0; + static int ssh_version; static int ssh1_throttle_count; static int ssh_overall_bufsize; @@ -500,7 +507,7 @@ static int ssh_rportcmp_ssh2(void *av, void *bv) { struct ssh_rportfwd *a = (struct ssh_rportfwd *) av; struct ssh_rportfwd *b = (struct ssh_rportfwd *) bv; - int i; + if (a->sport > b->sport) return +1; if (a->sport < b->sport) @@ -1679,6 +1686,11 @@ static char *connect_to_host(char *host, int port, char **realhost) /* * Try to find host. */ + { + char buf[200]; + sprintf(buf, "Looking up host \"%.170s\"", host); + logevent(buf); + } addr = sk_namelookup(host, realhost); if ((err = sk_addr_error(addr))) return err; @@ -1690,6 +1702,12 @@ static char *connect_to_host(char *host, int port, char **realhost) /* * Open socket. */ + { + char buf[200], addrbuf[100]; + sk_getaddr(addr, addrbuf, 100); + sprintf(buf, "Connecting to %.100s port %d", addrbuf, port); + logevent(buf); + } s = sk_new(addr, port, 0, 1, &fn_table_ptr); if ((err = sk_socket_error(s))) return err; @@ -2663,12 +2681,26 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt) zlib_decompress_init(); } - if (*cfg.remote_cmd_ptr) - send_packet(SSH1_CMSG_EXEC_CMD, PKT_STR, cfg.remote_cmd_ptr, - PKT_END); - else - send_packet(SSH1_CMSG_EXEC_SHELL, PKT_END); - logevent("Started session"); + /* + * Start the shell or command. + * + * Special case: if the first-choice command is an SSH2 + * subsystem (hence not usable here) and the second choice + * exists, we fall straight back to that. + */ + { + char *cmd = cfg.remote_cmd_ptr; + + if (cfg.ssh_subsys && cfg.remote_cmd_ptr2) { + cmd = cfg.remote_cmd_ptr2; + ssh_fallback_cmd = TRUE; + } + if (*cmd) + send_packet(SSH1_CMSG_EXEC_CMD, PKT_STR, cmd, PKT_END); + else + send_packet(SSH1_CMSG_EXEC_SHELL, PKT_END); + logevent("Started session"); + } ssh_state = SSH_STATE_SESSION; if (size_needed) @@ -2688,7 +2720,7 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt) int bufsize = from_backend(pktin.type == SSH1_SMSG_STDERR_DATA, pktin.body + 4, len); - if (bufsize > SSH1_BUFFER_LIMIT) { + if (!ssh1_stdout_throttling && bufsize > SSH1_BUFFER_LIMIT) { ssh1_stdout_throttling = 1; ssh1_throttle(+1); } @@ -2820,6 +2852,19 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt) pfd_confirm(c->u.pfd.s); } + } else if (pktin.type == SSH1_MSG_CHANNEL_OPEN_FAILURE) { + unsigned int remoteid = GET_32BIT(pktin.body); + unsigned int localid = GET_32BIT(pktin.body+4); + struct ssh_channel *c; + + c = find234(ssh_channels, &remoteid, ssh_channelfind); + if (c && c->type == CHAN_SOCKDATA_DORMANT) { + logevent("Forwarded connection refused by server"); + pfd_close(c->u.pfd.s); + del234(ssh_channels, c); + sfree(c); + } + } else if (pktin.type == SSH1_MSG_CHANNEL_CLOSE || pktin.type == SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION) { /* Remote side closes a channel. */ @@ -2921,7 +2966,7 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt) bufsize = 0; /* agent channels never back up */ break; } - if (bufsize > SSH1_BUFFER_LIMIT) { + if (!c->v.v1.throttling && bufsize > SSH1_BUFFER_LIMIT) { c->v.v1.throttling = 1; ssh1_throttle(+1); } @@ -3772,66 +3817,12 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt) in_commasep_string("publickey", methods, methlen); can_passwd = in_commasep_string("password", methods, methlen); - can_passwd = - in_commasep_string("password", methods, methlen); can_keyb_inter = in_commasep_string("keyboard-interactive", methods, methlen); } method = 0; - if (!method && can_keyb_inter && !tried_keyb_inter) { - method = AUTH_KEYBOARD_INTERACTIVE; - type = AUTH_TYPE_KEYBOARD_INTERACTIVE; - tried_keyb_inter = TRUE; - - ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); - ssh2_pkt_addstring(username); - ssh2_pkt_addstring("ssh-connection"); /* service requested */ - ssh2_pkt_addstring("keyboard-interactive"); /* method */ - ssh2_pkt_addstring(""); /* lang */ - ssh2_pkt_addstring(""); - ssh2_pkt_send(); - - crWaitUntilV(ispkt); - if (pktin.type != SSH2_MSG_USERAUTH_INFO_REQUEST) { - if (pktin.type == SSH2_MSG_USERAUTH_FAILURE) - gotit = TRUE; - logevent("Keyboard-interactive authentication refused"); - type = AUTH_TYPE_KEYBOARD_INTERACTIVE_QUIET; - continue; - } - - kbd_inter_running = TRUE; - } - - if (kbd_inter_running) { - method = AUTH_KEYBOARD_INTERACTIVE; - type = AUTH_TYPE_KEYBOARD_INTERACTIVE; - tried_keyb_inter = TRUE; - - /* We've got packet with that "interactive" info - dump banners, and set its prompt as ours */ - { - char *name, *inst, *lang, *prompt; - int name_len, inst_len, lang_len, prompt_len; - ssh2_pkt_getstring(&name, &name_len); - ssh2_pkt_getstring(&inst, &inst_len); - ssh2_pkt_getstring(&lang, &lang_len); - if (name_len > 0) - c_write_untrusted(name, name_len); - if (inst_len > 0) - c_write_untrusted(inst, inst_len); - num_prompts = ssh2_pkt_getuint32(); - - ssh2_pkt_getstring(&prompt, &prompt_len); - strncpy(pwprompt, prompt, sizeof(pwprompt)); - need_pw = TRUE; - - echo = ssh2_pkt_getbool(); - } - } - if (!method && can_pubkey && agent_exists() && !tried_agent) { /* * Attempt public-key authentication using Pageant. @@ -4025,6 +4016,58 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt) } } + if (!method && can_keyb_inter && !tried_keyb_inter) { + method = AUTH_KEYBOARD_INTERACTIVE; + type = AUTH_TYPE_KEYBOARD_INTERACTIVE; + tried_keyb_inter = TRUE; + + ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); + ssh2_pkt_addstring(username); + ssh2_pkt_addstring("ssh-connection"); /* service requested */ + ssh2_pkt_addstring("keyboard-interactive"); /* method */ + ssh2_pkt_addstring(""); /* lang */ + ssh2_pkt_addstring(""); + ssh2_pkt_send(); + + crWaitUntilV(ispkt); + if (pktin.type != SSH2_MSG_USERAUTH_INFO_REQUEST) { + if (pktin.type == SSH2_MSG_USERAUTH_FAILURE) + gotit = TRUE; + logevent("Keyboard-interactive authentication refused"); + type = AUTH_TYPE_KEYBOARD_INTERACTIVE_QUIET; + continue; + } + + kbd_inter_running = TRUE; + } + + if (kbd_inter_running) { + method = AUTH_KEYBOARD_INTERACTIVE; + type = AUTH_TYPE_KEYBOARD_INTERACTIVE; + tried_keyb_inter = TRUE; + + /* We've got packet with that "interactive" info + dump banners, and set its prompt as ours */ + { + char *name, *inst, *lang, *prompt; + int name_len, inst_len, lang_len, prompt_len; + ssh2_pkt_getstring(&name, &name_len); + ssh2_pkt_getstring(&inst, &inst_len); + ssh2_pkt_getstring(&lang, &lang_len); + if (name_len > 0) + c_write_untrusted(name, name_len); + if (inst_len > 0) + c_write_untrusted(inst, inst_len); + num_prompts = ssh2_pkt_getuint32(); + + ssh2_pkt_getstring(&prompt, &prompt_len); + strncpy(pwprompt, prompt, sizeof(pwprompt)); + need_pw = TRUE; + + echo = ssh2_pkt_getbool(); + } + } + if (!method && can_passwd) { method = AUTH_PASSWORD; sprintf(pwprompt, "%.90s@%.90s's password: ", username, @@ -4492,43 +4535,70 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt) } /* - * Start a shell or a remote command. + * Start a shell or a remote command. We may have to attempt + * this twice if the config data has provided a second choice + * of command. */ - ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST); - ssh2_pkt_adduint32(mainchan->remoteid); /* recipient channel */ - if (cfg.ssh_subsys) { - ssh2_pkt_addstring("subsystem"); - ssh2_pkt_addbool(1); /* want reply */ - ssh2_pkt_addstring(cfg.remote_cmd_ptr); - } else if (*cfg.remote_cmd_ptr) { - ssh2_pkt_addstring("exec"); - ssh2_pkt_addbool(1); /* want reply */ - ssh2_pkt_addstring(cfg.remote_cmd_ptr); - } else { - ssh2_pkt_addstring("shell"); - ssh2_pkt_addbool(1); /* want reply */ - } - ssh2_pkt_send(); - do { - crWaitUntilV(ispkt); - if (pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST) { - unsigned i = ssh2_pkt_getuint32(); - struct ssh_channel *c; - c = find234(ssh_channels, &i, ssh_channelfind); - if (!c) - continue; /* nonexistent channel */ - c->v.v2.remwindow += ssh2_pkt_getuint32(); + while (1) { + int subsys; + char *cmd; + + if (ssh_fallback_cmd) { + subsys = cfg.ssh_subsys2; + cmd = cfg.remote_cmd_ptr2; + } else { + subsys = cfg.ssh_subsys; + cmd = cfg.remote_cmd_ptr; + } + + ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST); + ssh2_pkt_adduint32(mainchan->remoteid); /* recipient channel */ + if (subsys) { + ssh2_pkt_addstring("subsystem"); + ssh2_pkt_addbool(1); /* want reply */ + ssh2_pkt_addstring(cmd); + } else if (*cmd) { + ssh2_pkt_addstring("exec"); + ssh2_pkt_addbool(1); /* want reply */ + ssh2_pkt_addstring(cmd); + } else { + ssh2_pkt_addstring("shell"); + ssh2_pkt_addbool(1); /* want reply */ } - } while (pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST); - if (pktin.type != SSH2_MSG_CHANNEL_SUCCESS) { - if (pktin.type != SSH2_MSG_CHANNEL_FAILURE) { - bombout(("Server got confused by shell/command request")); + ssh2_pkt_send(); + do { + crWaitUntilV(ispkt); + if (pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST) { + unsigned i = ssh2_pkt_getuint32(); + struct ssh_channel *c; + c = find234(ssh_channels, &i, ssh_channelfind); + if (!c) + continue; /* nonexistent channel */ + c->v.v2.remwindow += ssh2_pkt_getuint32(); + } + } while (pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST); + if (pktin.type != SSH2_MSG_CHANNEL_SUCCESS) { + if (pktin.type != SSH2_MSG_CHANNEL_FAILURE) { + bombout(("Server got confused by shell/command request")); + crReturnV; + } + /* + * We failed to start the command. If this is the + * fallback command, we really are finished; if it's + * not, and if the fallback command exists, try falling + * back to it before complaining. + */ + if (!ssh_fallback_cmd && cfg.remote_cmd_ptr2 != NULL) { + logevent("Primary command failed; attempting fallback"); + ssh_fallback_cmd = TRUE; + continue; + } + bombout(("Server refused to start a shell/command")); crReturnV; + } else { + logevent("Started a shell/command"); } - bombout(("Server refused to start a shell/command")); - crReturnV; - } else { - logevent("Started a shell/command"); + break; } ssh_state = SSH_STATE_SESSION; @@ -4668,22 +4738,29 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt) c = find234(ssh_channels, &i, ssh_channelfind); if (!c) continue; /* nonexistent channel */ - if (c->closes == 0) { - ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE); - ssh2_pkt_adduint32(c->remoteid); - ssh2_pkt_send(); - } /* Do pre-close processing on the channel. */ switch (c->type) { case CHAN_MAINSESSION: break; /* nothing to see here, move along */ case CHAN_X11: + if (c->u.x11.s != NULL) + x11_close(c->u.x11.s); + sshfwd_close(c); break; case CHAN_AGENT: + sshfwd_close(c); break; case CHAN_SOCKDATA: + if (c->u.pfd.s != NULL) + pfd_close(c->u.pfd.s); + sshfwd_close(c); break; } + if (c->closes == 0) { + ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE); + ssh2_pkt_adduint32(c->remoteid); + ssh2_pkt_send(); + } del234(ssh_channels, c); bufchain_clear(&c->v.v2.outbuffer); sfree(c); @@ -4725,6 +4802,21 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt) c->v.v2.remmaxpkt = ssh2_pkt_getuint32(); bufchain_init(&c->v.v2.outbuffer); pfd_confirm(c->u.pfd.s); + } else if (pktin.type == SSH2_MSG_CHANNEL_OPEN_FAILURE) { + unsigned i = ssh2_pkt_getuint32(); + struct ssh_channel *c; + c = find234(ssh_channels, &i, ssh_channelfind); + if (!c) + continue; /* nonexistent channel */ + if (c->type != CHAN_SOCKDATA_DORMANT) + continue; /* dunno why they're failing this */ + + logevent("Forwarded connection refused by server"); + + pfd_close(c->u.pfd.s); + + del234(ssh_channels, c); + sfree(c); } else if (pktin.type == SSH2_MSG_CHANNEL_OPEN) { char *type; int typelen; @@ -4882,6 +4974,7 @@ static char *ssh_init(char *host, int port, char **realhost) ssh_echoing = 0; ssh1_throttle_count = 0; ssh_overall_bufsize = 0; + ssh_fallback_cmd = 0; p = connect_to_host(host, port, realhost); if (p != NULL) @@ -5031,7 +5124,7 @@ void *new_sock_channel(Socket s) void ssh_unthrottle(int bufsize) { if (ssh_version == 1) { - if (bufsize < SSH1_BUFFER_LIMIT) { + if (ssh1_stdout_throttling && bufsize < SSH1_BUFFER_LIMIT) { ssh1_stdout_throttling = 0; ssh1_throttle(-1); }