X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/cc18f25619e3eca6233c4e162985390ee80e26a9..c7730e2d4e7b2ab1985fc368c8d2728700a164f0:/ssh.c diff --git a/ssh.c b/ssh.c index 439f1152..3ab9725e 100644 --- a/ssh.c +++ b/ssh.c @@ -572,6 +572,20 @@ enum { /* channel types */ CHAN_ZOMBIE }; +typedef void (*handler_fn_t)(Ssh ssh, struct Packet *pktin); +typedef void (*chandler_fn_t)(Ssh ssh, struct Packet *pktin, void *ctx); +typedef void (*cchandler_fn_t)(struct ssh_channel *, struct Packet *, void *); + +/* + * Each channel has a queue of outstanding CHANNEL_REQUESTS and their + * handlers. + */ +struct outstanding_channel_request { + cchandler_fn_t handler; + void *ctx; + struct outstanding_channel_request *next; +}; + /* * little structure to keep track of outstanding WINDOW_ADJUSTs */ @@ -646,10 +660,10 @@ struct ssh_channel { */ int remlocwin; /* - * These store the list of window adjusts that haven't + * These store the list of channel requests that haven't * been acked. */ - struct winadj *winadj_head, *winadj_tail; + struct outstanding_channel_request *chanreq_head, *chanreq_tail; enum { THROTTLED, UNTHROTTLING, UNTHROTTLED } throttle_state; } v2; } v; @@ -766,6 +780,7 @@ static void ssh_pkt_getstring(struct Packet *pkt, char **p, int *length); static void ssh2_timer(void *ctx, long now); static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, struct Packet *pktin); +static void ssh2_msg_unexpected(Ssh ssh, struct Packet *pktin); struct rdpkt1_state_tag { long len, pad, biglen, to_read; @@ -784,9 +799,6 @@ struct rdpkt2_state_tag { struct Packet *pktin; }; -typedef void (*handler_fn_t)(Ssh ssh, struct Packet *pktin); -typedef void (*chandler_fn_t)(Ssh ssh, struct Packet *pktin, void *ctx); - struct queued_handler; struct queued_handler { int msg1, msg2; @@ -6571,14 +6583,37 @@ static void ssh2_channel_init(struct ssh_channel *c) c->throttling_conn = FALSE; c->v.v2.locwindow = c->v.v2.locmaxwin = c->v.v2.remlocwin = conf_get_int(ssh->conf, CONF_ssh_simple) ? OUR_V2_BIGWIN : OUR_V2_WINSIZE; - c->v.v2.winadj_head = c->v.v2.winadj_tail = NULL; + c->v.v2.chanreq_head = NULL; c->v.v2.throttle_state = UNTHROTTLED; bufchain_init(&c->v.v2.outbuffer); } /* + * CHANNEL_FAILURE doesn't come with any indication of what message + * caused it, so we have to keep track of the outstanding + * CHANNEL_REQUESTs ourselves. + */ +static void ssh2_queue_chanreq_handler(struct ssh_channel *c, + cchandler_fn_t handler, void *ctx) +{ + struct outstanding_channel_request *ocr = + snew(struct outstanding_channel_request); + + ocr->handler = handler; + ocr->ctx = ctx; + ocr->next = NULL; + if (!c->v.v2.chanreq_head) + c->v.v2.chanreq_head = ocr; + else + c->v.v2.chanreq_tail->next = ocr; + c->v.v2.chanreq_tail = ocr; +} + +/* * Potentially enlarge the window on an SSH-2 channel. */ +static void ssh2_handle_winadj_response(struct ssh_channel *, struct Packet *, + void *); static void ssh2_set_window(struct ssh_channel *c, int newwin) { Ssh ssh = c->ssh; @@ -6609,7 +6644,7 @@ static void ssh2_set_window(struct ssh_channel *c, int newwin) */ if (newwin / 2 >= c->v.v2.locwindow) { struct Packet *pktout; - struct winadj *wa; + unsigned *up; /* * In order to keep track of how much window the client @@ -6620,14 +6655,8 @@ static void ssh2_set_window(struct ssh_channel *c, int newwin) * This is only necessary if we're opening the window wide. * If we're not, then throughput is being constrained by * something other than the maximum window size anyway. - * - * We also only send this if the main channel has finished its - * initial CHANNEL_REQUESTs and installed the default - * CHANNEL_FAILURE handler, so as not to risk giving it - * unexpected CHANNEL_FAILUREs. */ if (newwin == c->v.v2.locmaxwin && - ssh->packet_dispatch[SSH2_MSG_CHANNEL_FAILURE] && !(ssh->remote_bugs & BUG_CHOKES_ON_WINADJ)) { pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST); ssh2_pkt_adduint32(pktout, c->remoteid); @@ -6635,19 +6664,9 @@ static void ssh2_set_window(struct ssh_channel *c, int newwin) ssh2_pkt_addbool(pktout, TRUE); ssh2_pkt_send(ssh, pktout); - /* - * CHANNEL_FAILURE doesn't come with any indication of - * what message caused it, so we have to keep track of the - * outstanding CHANNEL_REQUESTs ourselves. - */ - wa = snew(struct winadj); - wa->size = newwin - c->v.v2.locwindow; - wa->next = NULL; - if (!c->v.v2.winadj_head) - c->v.v2.winadj_head = wa; - else - c->v.v2.winadj_tail->next = wa; - c->v.v2.winadj_tail = wa; + up = snew(unsigned); + *up = newwin - c->v.v2.locwindow; + ssh2_queue_chanreq_handler(c, ssh2_handle_winadj_response, up); if (c->v.v2.throttle_state != UNTHROTTLED) c->v.v2.throttle_state = UNTHROTTLING; } else { @@ -6687,14 +6706,21 @@ static struct ssh_channel *ssh2_channel_msg(Ssh ssh, struct Packet *pktin) return c; } -static int ssh2_handle_winadj_response(struct ssh_channel *c) +static void ssh2_handle_winadj_response(struct ssh_channel *c, + struct Packet *pktin, void *ctx) { - struct winadj *wa = c->v.v2.winadj_head; - if (!wa) - return FALSE; - c->v.v2.winadj_head = wa->next; - c->v.v2.remlocwin += wa->size; - sfree(wa); + unsigned *sizep = ctx; + + /* + * Winadj responses should always be failures. However, at least + * one server ("boks_sshd") is known to return SUCCESS for channel + * requests it's never heard of, such as "winadj@putty". Raised + * with foxt.com as bug 090916-090424, but for the sake of a quiet + * life, we don't worry about what kind of response we got. + */ + + c->v.v2.remlocwin += *sizep; + sfree(sizep); /* * winadj messages are only sent when the window is fully open, so * if we get an ack of one, we know any pending unthrottle is @@ -6702,56 +6728,28 @@ static int ssh2_handle_winadj_response(struct ssh_channel *c) */ if (c->v.v2.throttle_state == UNTHROTTLING) c->v.v2.throttle_state = UNTHROTTLED; - /* - * We may now initiate channel-closing procedures, if that winadj - * was the last thing outstanding before we send CHANNEL_CLOSE. - */ - ssh2_channel_check_close(c); - return TRUE; } -static void ssh2_msg_channel_success(Ssh ssh, struct Packet *pktin) +static void ssh2_msg_channel_response(Ssh ssh, struct Packet *pktin) { - /* - * This should never get called. All channel requests are either - * sent with want_reply false, are sent before this handler gets - * installed, or are "winadj@putty" requests, which servers should - * never respond to with success. - * - * However, at least one server ("boks_sshd") is known to return - * SUCCESS for channel requests it's never heard of, such as - * "winadj@putty". Raised with foxt.com as bug 090916-090424, but - * for the sake of a quiet life, we handle it just the same as the - * expected FAILURE. - */ - struct ssh_channel *c; + struct ssh_channel *c = ssh2_channel_msg(ssh, pktin); + struct outstanding_channel_request *ocr; - c = ssh2_channel_msg(ssh, pktin); - if (!c) + if (!c) return; + ocr = c->v.v2.chanreq_head; + if (!ocr) { + ssh2_msg_unexpected(ssh, pktin); return; - if (!ssh2_handle_winadj_response(c)) - ssh_disconnect(ssh, NULL, - "Received unsolicited SSH_MSG_CHANNEL_SUCCESS", - SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE); -} - -static void ssh2_msg_channel_failure(Ssh ssh, struct Packet *pktin) -{ + } + ocr->handler(c, pktin, ocr->ctx); + c->v.v2.chanreq_head = ocr->next; + sfree(ocr); /* - * The only time this should get called is for "winadj@putty" - * messages sent above. All other channel requests are either - * sent with want_reply false or are sent before this handler gets - * installed. + * We may now initiate channel-closing procedures, if that + * CHANNEL_REQUEST was the last thing outstanding before we send + * CHANNEL_CLOSE. */ - struct ssh_channel *c; - - c = ssh2_channel_msg(ssh, pktin); - if (!c) - return; - if (!ssh2_handle_winadj_response(c)) - ssh_disconnect(ssh, NULL, - "Received unsolicited SSH_MSG_CHANNEL_FAILURE", - SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE); + ssh2_channel_check_close(c); } static void ssh2_msg_channel_window_adjust(Ssh ssh, struct Packet *pktin) @@ -6928,7 +6926,7 @@ static void ssh2_channel_check_close(struct ssh_channel *c) struct Packet *pktout; if ((c->closes & (CLOSES_SENT_EOF | CLOSES_RCVD_EOF | CLOSES_SENT_CLOSE)) - == (CLOSES_SENT_EOF | CLOSES_RCVD_EOF) && !c->v.v2.winadj_head) { + == (CLOSES_SENT_EOF | CLOSES_RCVD_EOF) && !c->v.v2.chanreq_head) { /* * We have both sent and received EOF, and we have no * outstanding winadj channel requests, which means the @@ -7449,6 +7447,243 @@ static void ssh2_send_ttymode(void *data, char *mode, char *val) ssh2_pkt_adduint32(pktout, arg); } +static void ssh2_msg_authconn(Ssh ssh, struct Packet *pktin); +static void ssh2_maybe_setup_x11(struct ssh_channel *c, struct Packet *pktin, + void *ctx) +{ + struct ssh2_maybe_setup_x11_state { + int crLine; + }; + Ssh ssh = c->ssh; + struct Packet *pktout; + crStateP(ssh2_maybe_setup_x11_state, ctx); + + crBeginState; + + /* + * Potentially enable X11 forwarding. + */ + if (ssh->mainchan && !ssh->ncmode && conf_get_int(ssh->conf, CONF_x11_forward) && + (ssh->x11disp = x11_setup_display(conf_get_str(ssh->conf, CONF_x11_display), + conf_get_int(ssh->conf, CONF_x11_auth), ssh->conf))) { + logevent("Requesting X11 forwarding"); + pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST); + ssh2_pkt_adduint32(pktout, ssh->mainchan->remoteid); + ssh2_pkt_addstring(pktout, "x11-req"); + ssh2_pkt_addbool(pktout, 1); /* want reply */ + ssh2_pkt_addbool(pktout, 0); /* many connections */ + ssh2_pkt_addstring(pktout, ssh->x11disp->remoteauthprotoname); + /* + * 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, pktout, PKTLOG_BLANK); + ssh2_pkt_addstring(pktout, ssh->x11disp->remoteauthdatastring); + end_log_omission(ssh, pktout); + ssh2_pkt_adduint32(pktout, ssh->x11disp->screennum); + ssh2_pkt_send(ssh, pktout); + + ssh2_queue_chanreq_handler(ssh->mainchan, ssh2_maybe_setup_x11, s); + + crWaitUntilV(pktin); + + if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) { + if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) { + bombout(("Unexpected response to X11 forwarding request:" + " packet type %d", pktin->type)); + sfree(s); + crStopV; + } + logevent("X11 forwarding refused"); + } else { + logevent("X11 forwarding enabled"); + ssh->X11_fwd_enabled = TRUE; + } + } + sfree(s); + crFinishV; +} + +static void ssh2_maybe_setup_agent(struct ssh_channel *c, struct Packet *pktin, + void *ctx) +{ + struct ssh2_maybe_setup_agent_state { + int crLine; + }; + Ssh ssh = c->ssh; + struct Packet *pktout; + crStateP(ssh2_maybe_setup_agent_state, ctx); + + crBeginState; + + if (ssh->mainchan && !ssh->ncmode && conf_get_int(ssh->conf, CONF_agentfwd) && agent_exists()) { + logevent("Requesting OpenSSH-style agent forwarding"); + pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST); + ssh2_pkt_adduint32(pktout, ssh->mainchan->remoteid); + ssh2_pkt_addstring(pktout, "auth-agent-req@openssh.com"); + ssh2_pkt_addbool(pktout, 1); /* want reply */ + ssh2_pkt_send(ssh, pktout); + + ssh2_queue_chanreq_handler(ssh->mainchan, ssh2_maybe_setup_agent, s); + + crWaitUntilV(pktin); + + if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) { + if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) { + bombout(("Unexpected response to agent forwarding request:" + " packet type %d", pktin->type)); + crStopV; + } + logevent("Agent forwarding refused"); + } else { + logevent("Agent forwarding enabled"); + ssh->agentfwd_enabled = TRUE; + } + } + sfree(s); + crFinishV; +} + +static void ssh2_maybe_setup_pty(struct ssh_channel *c, struct Packet *pktin, + void *ctx) +{ + struct ssh2_maybe_setup_pty_state { + int crLine; + }; + Ssh ssh = c->ssh; + struct Packet *pktout; + crStateP(ssh2_maybe_setup_pty_state, ctx); + + crBeginState; + + if (ssh->mainchan && !ssh->ncmode && !conf_get_int(ssh->conf, CONF_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 */ + sscanf(conf_get_str(ssh->conf, CONF_termspeed), "%d,%d", &ssh->ospeed, &ssh->ispeed); + /* Build the pty request. */ + pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST); + ssh2_pkt_adduint32(pktout, ssh->mainchan->remoteid); /* recipient channel */ + ssh2_pkt_addstring(pktout, "pty-req"); + ssh2_pkt_addbool(pktout, 1); /* want reply */ + ssh2_pkt_addstring(pktout, conf_get_str(ssh->conf, CONF_termtype)); + ssh2_pkt_adduint32(pktout, ssh->term_width); + ssh2_pkt_adduint32(pktout, ssh->term_height); + ssh2_pkt_adduint32(pktout, 0); /* pixel width */ + ssh2_pkt_adduint32(pktout, 0); /* pixel height */ + ssh2_pkt_addstring_start(pktout); + parse_ttymodes(ssh, ssh2_send_ttymode, (void *)pktout); + ssh2_pkt_addbyte(pktout, SSH2_TTY_OP_ISPEED); + ssh2_pkt_adduint32(pktout, ssh->ispeed); + ssh2_pkt_addbyte(pktout, SSH2_TTY_OP_OSPEED); + ssh2_pkt_adduint32(pktout, ssh->ospeed); + ssh2_pkt_addstring_data(pktout, "\0", 1); /* TTY_OP_END */ + ssh2_pkt_send(ssh, pktout); + ssh->state = SSH_STATE_INTERMED; + + ssh2_queue_chanreq_handler(ssh->mainchan, ssh2_maybe_setup_pty, s); + + crWaitUntilV(pktin); + + if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) { + if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) { + bombout(("Unexpected response to pty request:" + " packet type %d", pktin->type)); + crStopV; + } + c_write_str(ssh, "Server refused to allocate pty\r\n"); + ssh->editing = ssh->echoing = 1; + } else { + logeventf(ssh, "Allocated pty (ospeed %dbps, ispeed %dbps)", + ssh->ospeed, ssh->ispeed); + ssh->got_pty = TRUE; + } + } else { + ssh->editing = ssh->echoing = 1; + } + sfree(s); + crFinishV; +} + +static void ssh2_setup_env(struct ssh_channel *c, struct Packet *pktin, + void *ctx) +{ + struct ssh2_setup_env_state { + int crLine; + int num_env, env_left, env_ok; + }; + Ssh ssh = c->ssh; + struct Packet *pktout; + crStateP(ssh2_setup_env_state, ctx); + + crBeginState; + + /* + * Send environment variables. + * + * Simplest thing here is to send all the requests at once, and + * then wait for a whole bunch of successes or failures. + */ + s->num_env = 0; + if (ssh->mainchan && !ssh->ncmode) { + char *key, *val; + + for (val = conf_get_str_strs(ssh->conf, CONF_environmt, NULL, &key); + val != NULL; + val = conf_get_str_strs(ssh->conf, CONF_environmt, key, &key)) { + pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST); + ssh2_pkt_adduint32(pktout, ssh->mainchan->remoteid); + ssh2_pkt_addstring(pktout, "env"); + ssh2_pkt_addbool(pktout, 1); /* want reply */ + ssh2_pkt_addstring(pktout, key); + ssh2_pkt_addstring(pktout, val); + ssh2_pkt_send(ssh, pktout); + ssh2_queue_chanreq_handler(ssh->mainchan, ssh2_setup_env, s); + + s->num_env++; + } + if (s->num_env) + logeventf(ssh, "Sent %d environment variables", s->num_env); + } + + if (s->num_env) { + s->env_ok = 0; + s->env_left = s->num_env; + + while (s->env_left > 0) { + crWaitUntilV(pktin); + + if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) { + if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) { + bombout(("Unexpected response to environment request:" + " packet type %d", pktin->type)); + crStopV; + } + } else { + s->env_ok++; + } + + s->env_left--; + } + + if (s->env_ok == s->num_env) { + logevent("All environment variables successfully set"); + } else if (s->env_ok == 0) { + logevent("All environment variables refused"); + c_write_str(ssh, "Server refused to set environment variables\r\n"); + } else { + logeventf(ssh, "%d environment variables refused", + s->num_env - s->env_ok); + c_write_str(ssh, "Server refused to set all environment variables\r\n"); + } + } + sfree(s); + crFinishV; +} + /* * Handle the SSH-2 userauth and connection layers. */ @@ -7457,6 +7692,12 @@ static void ssh2_msg_authconn(Ssh ssh, struct Packet *pktin) do_ssh2_authconn(ssh, NULL, 0, pktin); } +static void ssh2_response_authconn(struct ssh_channel *c, struct Packet *pktin, + void *ctx) +{ + do_ssh2_authconn(c->ssh, NULL, 0, pktin); +} + static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, struct Packet *pktin) { @@ -7500,10 +7741,6 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int siglen, retlen, len; char *q, *agentreq, *ret; int try_send; - int requested_x11; - int requested_agent; - int requested_tty; - int num_env, env_left, env_ok; struct Packet *pktout; Filename *keyfile; #ifndef NO_GSSAPI @@ -7540,9 +7777,6 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, ssh->packet_dispatch[SSH2_MSG_CHANNEL_EXTENDED_DATA] = ssh2_msg_authconn; ssh->packet_dispatch[SSH2_MSG_CHANNEL_EOF] = ssh2_msg_authconn; ssh->packet_dispatch[SSH2_MSG_CHANNEL_CLOSE] = ssh2_msg_authconn; - ssh->packet_dispatch[SSH2_MSG_CHANNEL_REQUEST] = ssh2_msg_authconn; - ssh->packet_dispatch[SSH2_MSG_CHANNEL_SUCCESS] = ssh2_msg_authconn; - ssh->packet_dispatch[SSH2_MSG_CHANNEL_FAILURE] = ssh2_msg_authconn; s->done_service_req = FALSE; s->we_are_in = s->userauth_success = FALSE; @@ -8999,6 +9233,9 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, ssh2_msg_channel_request; ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN] = ssh2_msg_channel_open; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_SUCCESS] = ssh2_msg_channel_response; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_FAILURE] = ssh2_msg_channel_response; + if (ssh->mainchan && conf_get_int(ssh->conf, CONF_ssh_simple)) { /* @@ -9020,200 +9257,29 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, ssh_setup_portfwd(ssh, ssh->conf); /* - * Send the CHANNEL_REQUESTS for the main channel. We send them all - * and then start looking for responses, so it's important that the - * sending and receiving code below it is kept in sync. + * Send the CHANNEL_REQUESTS for the main channel. Each one is + * handled by its own little asynchronous co-routine. */ /* * Potentially enable X11 forwarding. */ - if (ssh->mainchan && !ssh->ncmode && conf_get_int(ssh->conf, CONF_x11_forward) && - (ssh->x11disp = x11_setup_display(conf_get_str(ssh->conf, CONF_x11_display), - conf_get_int(ssh->conf, CONF_x11_auth), ssh->conf))) { - logevent("Requesting X11 forwarding"); - s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST); - ssh2_pkt_adduint32(s->pktout, ssh->mainchan->remoteid); - ssh2_pkt_addstring(s->pktout, "x11-req"); - ssh2_pkt_addbool(s->pktout, 1); /* want reply */ - ssh2_pkt_addbool(s->pktout, 0); /* many connections */ - ssh2_pkt_addstring(s->pktout, ssh->x11disp->remoteauthprotoname); - /* - * 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, ssh->x11disp->remoteauthdatastring); - end_log_omission(ssh, s->pktout); - ssh2_pkt_adduint32(s->pktout, ssh->x11disp->screennum); - ssh2_pkt_send(ssh, s->pktout); - s->requested_x11 = TRUE; - } else - s->requested_x11 = FALSE; + ssh2_maybe_setup_x11(ssh->mainchan, NULL, NULL); /* * Potentially enable agent forwarding. */ - if (ssh->mainchan && !ssh->ncmode && conf_get_int(ssh->conf, CONF_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); - ssh2_pkt_addstring(s->pktout, "auth-agent-req@openssh.com"); - ssh2_pkt_addbool(s->pktout, 1); /* want reply */ - ssh2_pkt_send(ssh, s->pktout); - s->requested_agent = TRUE; - } else - s->requested_agent = FALSE; + ssh2_maybe_setup_agent(ssh->mainchan, NULL, NULL); /* * Now allocate a pty for the session. */ - if (ssh->mainchan && !ssh->ncmode && !conf_get_int(ssh->conf, CONF_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 */ - sscanf(conf_get_str(ssh->conf, CONF_termspeed), "%d,%d", &ssh->ospeed, &ssh->ispeed); - /* Build the pty request. */ - s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST); - ssh2_pkt_adduint32(s->pktout, ssh->mainchan->remoteid); /* recipient channel */ - ssh2_pkt_addstring(s->pktout, "pty-req"); - ssh2_pkt_addbool(s->pktout, 1); /* want reply */ - ssh2_pkt_addstring(s->pktout, conf_get_str(ssh->conf, CONF_termtype)); - ssh2_pkt_adduint32(s->pktout, ssh->term_width); - ssh2_pkt_adduint32(s->pktout, ssh->term_height); - ssh2_pkt_adduint32(s->pktout, 0); /* pixel width */ - ssh2_pkt_adduint32(s->pktout, 0); /* pixel height */ - ssh2_pkt_addstring_start(s->pktout); - parse_ttymodes(ssh, ssh2_send_ttymode, (void *)s->pktout); - ssh2_pkt_addbyte(s->pktout, SSH2_TTY_OP_ISPEED); - ssh2_pkt_adduint32(s->pktout, ssh->ispeed); - ssh2_pkt_addbyte(s->pktout, SSH2_TTY_OP_OSPEED); - ssh2_pkt_adduint32(s->pktout, ssh->ospeed); - ssh2_pkt_addstring_data(s->pktout, "\0", 1); /* TTY_OP_END */ - ssh2_pkt_send(ssh, s->pktout); - ssh->state = SSH_STATE_INTERMED; - s->requested_tty = TRUE; - } else - s->requested_tty = FALSE; + ssh2_maybe_setup_pty(ssh->mainchan, NULL, NULL); /* * Send environment variables. - * - * Simplest thing here is to send all the requests at once, and - * then wait for a whole bunch of successes or failures. */ - s->num_env = 0; - if (ssh->mainchan && !ssh->ncmode) { - char *key, *val; - - for (val = conf_get_str_strs(ssh->conf, CONF_environmt, NULL, &key); - val != NULL; - val = conf_get_str_strs(ssh->conf, CONF_environmt, key, &key)) { - s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST); - ssh2_pkt_adduint32(s->pktout, ssh->mainchan->remoteid); - ssh2_pkt_addstring(s->pktout, "env"); - ssh2_pkt_addbool(s->pktout, 1); /* want reply */ - ssh2_pkt_addstring(s->pktout, key); - ssh2_pkt_addstring(s->pktout, val); - ssh2_pkt_send(ssh, s->pktout); - - s->num_env++; - } - if (s->num_env) - logeventf(ssh, "Sent %d environment variables", s->num_env); - } - - /* - * All CHANNEL_REQUESTs sent. Now collect up the replies. These - * must be in precisely the same order as the requests. - */ - - if (s->requested_x11) { - crWaitUntilV(pktin); - - if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) { - if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) { - bombout(("Unexpected response to X11 forwarding request:" - " packet type %d", pktin->type)); - crStopV; - } - logevent("X11 forwarding refused"); - } else { - logevent("X11 forwarding enabled"); - ssh->X11_fwd_enabled = TRUE; - } - } - - if (s->requested_agent) { - crWaitUntilV(pktin); - - if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) { - if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) { - bombout(("Unexpected response to agent forwarding request:" - " packet type %d", pktin->type)); - crStopV; - } - logevent("Agent forwarding refused"); - } else { - logevent("Agent forwarding enabled"); - ssh->agentfwd_enabled = TRUE; - } - } - - if (s->requested_tty) { - crWaitUntilV(pktin); - - if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) { - if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) { - bombout(("Unexpected response to pty request:" - " packet type %d", pktin->type)); - crStopV; - } - c_write_str(ssh, "Server refused to allocate pty\r\n"); - ssh->editing = ssh->echoing = 1; - } else { - logeventf(ssh, "Allocated pty (ospeed %dbps, ispeed %dbps)", - ssh->ospeed, ssh->ispeed); - ssh->got_pty = TRUE; - } - } else { - ssh->editing = ssh->echoing = 1; - } - - if (s->num_env) { - s->env_ok = 0; - s->env_left = s->num_env; - - while (s->env_left > 0) { - crWaitUntilV(pktin); - - if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) { - if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) { - bombout(("Unexpected response to environment request:" - " packet type %d", pktin->type)); - crStopV; - } - } else { - s->env_ok++; - } - - s->env_left--; - } - - if (s->env_ok == s->num_env) { - logevent("All environment variables successfully set"); - } else if (s->env_ok == 0) { - logevent("All environment variables refused"); - c_write_str(ssh, "Server refused to set environment variables\r\n"); - } else { - logeventf(ssh, "%d environment variables refused", - s->num_env - s->env_ok); - c_write_str(ssh, "Server refused to set all environment variables\r\n"); - } - } + ssh2_setup_env(ssh->mainchan, NULL, NULL); /* * Start a shell or a remote command. We may have to attempt @@ -9247,6 +9313,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, ssh2_pkt_addbool(s->pktout, 1); /* want reply */ } ssh2_pkt_send(ssh, s->pktout); + ssh2_queue_chanreq_handler(ssh->mainchan, ssh2_response_authconn, NULL); crWaitUntilV(pktin); @@ -9284,10 +9351,10 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, /* * All the initial channel requests are done, so install the default - * failure handler. + * response handler. */ - ssh->packet_dispatch[SSH2_MSG_CHANNEL_SUCCESS] = ssh2_msg_channel_success; - ssh->packet_dispatch[SSH2_MSG_CHANNEL_FAILURE] = ssh2_msg_channel_failure; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_SUCCESS] = ssh2_msg_channel_response; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_FAILURE] = ssh2_msg_channel_response; /* * Transfer data! @@ -9391,6 +9458,7 @@ static void ssh2_msg_unexpected(Ssh ssh, struct Packet *pktin) ssh2_pkt_type(ssh->pkt_kctx, ssh->pkt_actx, pktin->type)); ssh_disconnect(ssh, NULL, buf, SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE); + sfree(buf); } static void ssh2_msg_something_unimplemented(Ssh ssh, struct Packet *pktin)