=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/ssh/clientloop.c,v retrieving revision 1.112.2.2 retrieving revision 1.113 diff -u -r1.112.2.2 -r1.113 --- src/usr.bin/ssh/clientloop.c 2004/08/19 22:37:31 1.112.2.2 +++ src/usr.bin/ssh/clientloop.c 2003/09/19 17:43:35 1.113 @@ -59,7 +59,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: clientloop.c,v 1.112.2.2 2004/08/19 22:37:31 brad Exp $"); +RCSID("$OpenBSD: clientloop.c,v 1.113 2003/09/19 17:43:35 markus Exp $"); #include "ssh.h" #include "ssh1.h" @@ -79,11 +79,9 @@ #include "clientloop.h" #include "authfd.h" #include "atomicio.h" -#include "sshpty.h" +#include "sshtty.h" #include "misc.h" -#include "monitor_fdpass.h" -#include "match.h" -#include "msg.h" +#include "readpass.h" /* import options */ extern Options options; @@ -91,12 +89,6 @@ /* Flag indicating that stdin should be redirected from /dev/null. */ extern int stdin_null_flag; -/* Flag indicating that no shell has been requested */ -extern int no_shell_flag; - -/* Control socket */ -extern int control_fd; - /* * Name of the host we are connecting to. This is the name given on the * command line, or the HostName specified for the user-supplied name in a @@ -132,32 +124,20 @@ static int connection_out; /* Connection to server (output). */ static int need_rekeying; /* Set to non-zero if rekeying is requested. */ static int session_closed = 0; /* In SSH2: login session closed. */ -static int server_alive_timeouts = 0; static void client_init_dispatch(void); int session_ident = -1; -struct confirm_ctx { - int want_tty; - int want_subsys; - Buffer cmd; - char *term; - struct termios tio; - char **env; -}; - /*XXX*/ extern Kex *xxx_kex; -void ssh_process_session2_setup(int, int, int, Buffer *); - /* Restores stdin to blocking mode. */ static void leave_non_blocking(void) { if (in_non_blocking_mode) { - unset_nonblock(fileno(stdin)); + (void) fcntl(fileno(stdin), F_SETFL, 0); in_non_blocking_mode = 0; } } @@ -168,7 +148,7 @@ enter_non_blocking(void) { in_non_blocking_mode = 1; - set_nonblock(fileno(stdin)); + (void) fcntl(fileno(stdin), F_SETFL, O_NONBLOCK); } /* @@ -308,13 +288,19 @@ /** XXX race */ received_window_change_signal = 0; + if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0) + return; + debug2("client_check_window_change: changed"); if (compat20) { - channel_send_window_changes(); + channel_request_start(session_ident, "window-change", 0); + packet_put_int(ws.ws_col); + packet_put_int(ws.ws_row); + packet_put_int(ws.ws_xpixel); + packet_put_int(ws.ws_ypixel); + packet_send(); } else { - if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0) - return; packet_start(SSH_CMSG_WINDOW_SIZE); packet_put_int(ws.ws_row); packet_put_int(ws.ws_col); @@ -324,35 +310,15 @@ } } -static void -client_global_request_reply(int type, u_int32_t seq, void *ctxt) -{ - server_alive_timeouts = 0; - client_global_request_reply_fwd(type, seq, ctxt); -} - -static void -server_alive_check(void) -{ - if (++server_alive_timeouts > options.server_alive_count_max) - packet_disconnect("Timeout, server not responding."); - packet_start(SSH2_MSG_GLOBAL_REQUEST); - packet_put_cstring("keepalive@openssh.com"); - packet_put_char(1); /* boolean: want reply */ - packet_send(); -} - /* * Waits until the client can do something (some data becomes available on * one of the file descriptors). */ + static void client_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, - int *maxfdp, u_int *nallocp, int rekeying) + int *maxfdp, int *nallocp, int rekeying) { - struct timeval tv, *tvp; - int ret; - /* Add any selections by the channel mechanism. */ channel_prepare_select(readsetp, writesetp, maxfdp, nallocp, rekeying); @@ -391,24 +357,16 @@ if (packet_have_data_to_write()) FD_SET(connection_out, *writesetp); - if (control_fd != -1) - FD_SET(control_fd, *readsetp); - /* * Wait for something to happen. This will suspend the process until * some selected descriptor can be read, written, or has some other - * event pending. + * event pending. Note: if you want to implement SSH_MSG_IGNORE + * messages to fool traffic analysis, this might be the place to do + * it: just have a random timeout for the select, and send a random + * SSH_MSG_IGNORE packet when the timeout expires. */ - if (options.server_alive_interval == 0 || !compat20) - tvp = NULL; - else { - tv.tv_sec = options.server_alive_interval; - tv.tv_usec = 0; - tvp = &tv; - } - ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, tvp); - if (ret < 0) { + if (select((*maxfdp)+1, *readsetp, *writesetp, NULL, NULL) < 0) { char buf[100]; /* @@ -425,8 +383,7 @@ snprintf(buf, sizeof buf, "select: %s\r\n", strerror(errno)); buffer_append(&stderr_buffer, buf, strlen(buf)); quit_pending = 1; - } else if (ret == 0) - server_alive_check(); + } } static void @@ -513,229 +470,12 @@ } static void -client_subsystem_reply(int type, u_int32_t seq, void *ctxt) -{ - int id; - Channel *c; - - id = packet_get_int(); - packet_check_eom(); - - if ((c = channel_lookup(id)) == NULL) { - error("%s: no channel for id %d", __func__, id); - return; - } - - if (type == SSH2_MSG_CHANNEL_SUCCESS) - debug2("Request suceeded on channel %d", id); - else if (type == SSH2_MSG_CHANNEL_FAILURE) { - error("Request failed on channel %d", id); - channel_free(c); - } -} - -static void -client_extra_session2_setup(int id, void *arg) -{ - struct confirm_ctx *cctx = arg; - Channel *c; - int i; - - if (cctx == NULL) - fatal("%s: cctx == NULL", __func__); - if ((c = channel_lookup(id)) == NULL) - fatal("%s: no channel for id %d", __func__, id); - - client_session2_setup(id, cctx->want_tty, cctx->want_subsys, - cctx->term, &cctx->tio, c->rfd, &cctx->cmd, cctx->env, - client_subsystem_reply); - - c->confirm_ctx = NULL; - buffer_free(&cctx->cmd); - xfree(cctx->term); - if (cctx->env != NULL) { - for (i = 0; cctx->env[i] != NULL; i++) - xfree(cctx->env[i]); - xfree(cctx->env); - } - xfree(cctx); -} - -static void -client_process_control(fd_set * readset) -{ - Buffer m; - Channel *c; - int client_fd, new_fd[3], ver, i, allowed; - socklen_t addrlen; - struct sockaddr_storage addr; - struct confirm_ctx *cctx; - char *cmd; - u_int len, env_len; - uid_t euid; - gid_t egid; - - /* - * Accept connection on control socket - */ - if (control_fd == -1 || !FD_ISSET(control_fd, readset)) - return; - - memset(&addr, 0, sizeof(addr)); - addrlen = sizeof(addr); - if ((client_fd = accept(control_fd, - (struct sockaddr*)&addr, &addrlen)) == -1) { - error("%s accept: %s", __func__, strerror(errno)); - return; - } - - if (getpeereid(client_fd, &euid, &egid) < 0) { - error("%s getpeereid failed: %s", __func__, strerror(errno)); - close(client_fd); - return; - } - if ((euid != 0) && (getuid() != euid)) { - error("control mode uid mismatch: peer euid %u != uid %u", - (u_int) euid, (u_int) getuid()); - close(client_fd); - return; - } - - allowed = 1; - if (options.control_master == 2) { - char *p, prompt[1024]; - - allowed = 0; - snprintf(prompt, sizeof(prompt), - "Allow shared connection to %s? ", host); - p = read_passphrase(prompt, RP_USE_ASKPASS|RP_ALLOW_EOF); - if (p != NULL) { - /* - * Accept empty responses and responses consisting - * of the word "yes" as affirmative. - */ - if (*p == '\0' || *p == '\n' || - strcasecmp(p, "yes") == 0) - allowed = 1; - xfree(p); - } - } - - unset_nonblock(client_fd); - - buffer_init(&m); - - buffer_put_int(&m, allowed); - buffer_put_int(&m, getpid()); - if (ssh_msg_send(client_fd, /* version */0, &m) == -1) { - error("%s: client msg_send failed", __func__); - close(client_fd); - buffer_free(&m); - return; - } - buffer_clear(&m); - - if (!allowed) { - error("Refused control connection"); - close(client_fd); - buffer_free(&m); - return; - } - - if (ssh_msg_recv(client_fd, &m) == -1) { - error("%s: client msg_recv failed", __func__); - close(client_fd); - buffer_free(&m); - return; - } - - if ((ver = buffer_get_char(&m)) != 0) { - error("%s: wrong client version %d", __func__, ver); - buffer_free(&m); - close(client_fd); - return; - } - - cctx = xmalloc(sizeof(*cctx)); - memset(cctx, 0, sizeof(*cctx)); - - cctx->want_tty = buffer_get_int(&m); - cctx->want_subsys = buffer_get_int(&m); - cctx->term = buffer_get_string(&m, &len); - - cmd = buffer_get_string(&m, &len); - buffer_init(&cctx->cmd); - buffer_append(&cctx->cmd, cmd, strlen(cmd)); - - env_len = buffer_get_int(&m); - env_len = MIN(env_len, 4096); - debug3("%s: receiving %d env vars", __func__, env_len); - if (env_len != 0) { - cctx->env = xmalloc(sizeof(*cctx->env) * (env_len + 1)); - for (i = 0; i < env_len; i++) - cctx->env[i] = buffer_get_string(&m, &len); - cctx->env[i] = NULL; - } - - debug2("%s: accepted tty %d, subsys %d, cmd %s", __func__, - cctx->want_tty, cctx->want_subsys, cmd); - - /* Gather fds from client */ - new_fd[0] = mm_receive_fd(client_fd); - new_fd[1] = mm_receive_fd(client_fd); - new_fd[2] = mm_receive_fd(client_fd); - - debug2("%s: got fds stdin %d, stdout %d, stderr %d", __func__, - new_fd[0], new_fd[1], new_fd[2]); - - /* Try to pick up ttymodes from client before it goes raw */ - if (cctx->want_tty && tcgetattr(new_fd[0], &cctx->tio) == -1) - error("%s: tcgetattr: %s", __func__, strerror(errno)); - - buffer_clear(&m); - if (ssh_msg_send(client_fd, /* version */0, &m) == -1) { - error("%s: client msg_send failed", __func__); - close(client_fd); - close(new_fd[0]); - close(new_fd[1]); - close(new_fd[2]); - buffer_free(&m); - return; - } - buffer_free(&m); - - /* enable nonblocking unless tty */ - if (!isatty(new_fd[0])) - set_nonblock(new_fd[0]); - if (!isatty(new_fd[1])) - set_nonblock(new_fd[1]); - if (!isatty(new_fd[2])) - set_nonblock(new_fd[2]); - - set_nonblock(client_fd); - - c = channel_new("session", SSH_CHANNEL_OPENING, - new_fd[0], new_fd[1], new_fd[2], - CHAN_SES_WINDOW_DEFAULT, CHAN_SES_PACKET_DEFAULT, - CHAN_EXTENDED_WRITE, "client-session", /*nonblock*/0); - - /* XXX */ - c->ctl_fd = client_fd; - - debug3("%s: channel_new: %d", __func__, c->self); - - channel_send_open(c->self); - channel_register_confirm(c->self, client_extra_session2_setup, cctx); -} - -static void process_cmdline(void) { void (*handler)(int); char *s, *cmd; u_short fwd_port, fwd_host_port; char buf[1024], sfwd_port[6], sfwd_host_port[6]; - int delete = 0; int local = 0; leave_raw_mode(); @@ -745,77 +485,44 @@ goto out; while (*s && isspace(*s)) s++; - if (*s == '-') - s++; /* Skip cmdline '-', if any */ - if (*s == '\0') + if (*s == 0) goto out; - - if (*s == 'h' || *s == 'H' || *s == '?') { - logit("Commands:"); - logit(" -Lport:host:hostport Request local forward"); - logit(" -Rport:host:hostport Request remote forward"); - logit(" -KRhostport Cancel remote forward"); - goto out; - } - - if (*s == 'K') { - delete = 1; - s++; - } - if (*s != 'L' && *s != 'R') { + if (strlen(s) < 2 || s[0] != '-' || !(s[1] == 'L' || s[1] == 'R')) { logit("Invalid command."); goto out; } - if (*s == 'L') + if (s[1] == 'L') local = 1; - if (local && delete) { - logit("Not supported."); - goto out; - } - if ((!local || delete) && !compat20) { + if (!local && !compat20) { logit("Not supported for SSH protocol version 1."); goto out; } - - s++; + s += 2; while (*s && isspace(*s)) s++; - if (delete) { - if (sscanf(s, "%5[0-9]", sfwd_host_port) != 1) { - logit("Bad forwarding specification."); + if (sscanf(s, "%5[0-9]:%255[^:]:%5[0-9]", + sfwd_port, buf, sfwd_host_port) != 3 && + sscanf(s, "%5[0-9]/%255[^/]/%5[0-9]", + sfwd_port, buf, sfwd_host_port) != 3) { + logit("Bad forwarding specification."); + goto out; + } + if ((fwd_port = a2port(sfwd_port)) == 0 || + (fwd_host_port = a2port(sfwd_host_port)) == 0) { + logit("Bad forwarding port(s)."); + goto out; + } + if (local) { + if (channel_setup_local_fwd_listener(fwd_port, buf, + fwd_host_port, options.gateway_ports) < 0) { + logit("Port forwarding failed."); goto out; } - if ((fwd_host_port = a2port(sfwd_host_port)) == 0) { - logit("Bad forwarding port(s)."); - goto out; - } - channel_request_rforward_cancel(fwd_host_port); - } else { - if (sscanf(s, "%5[0-9]:%255[^:]:%5[0-9]", - sfwd_port, buf, sfwd_host_port) != 3 && - sscanf(s, "%5[0-9]/%255[^/]/%5[0-9]", - sfwd_port, buf, sfwd_host_port) != 3) { - logit("Bad forwarding specification."); - goto out; - } - if ((fwd_port = a2port(sfwd_port)) == 0 || - (fwd_host_port = a2port(sfwd_host_port)) == 0) { - logit("Bad forwarding port(s)."); - goto out; - } - if (local) { - if (channel_setup_local_fwd_listener(fwd_port, buf, - fwd_host_port, options.gateway_ports) < 0) { - logit("Port forwarding failed."); - goto out; - } - } else - channel_request_remote_forwarding(fwd_port, buf, - fwd_host_port); - logit("Forwarding port."); - } - + } else + channel_request_remote_forwarding(fwd_port, buf, + fwd_host_port); + logit("Forwarding port."); out: signal(SIGINT, handler); enter_raw_mode(); @@ -1130,6 +837,9 @@ static void client_channel_closed(int id, void *arg) { + if (id != session_ident) + error("client_channel_closed: id %d != session_ident %d", + id, session_ident); channel_cancel_cleanup(id); session_closed = 1; leave_raw_mode(); @@ -1147,8 +857,7 @@ { fd_set *readset = NULL, *writeset = NULL; double start_time, total_time; - int max_fd = 0, max_fd2 = 0, len, rekeying = 0; - u_int nalloc = 0; + int max_fd = 0, max_fd2 = 0, len, rekeying = 0, nalloc = 0; char buf[100]; debug("Entering interactive session."); @@ -1164,8 +873,6 @@ connection_in = packet_get_connection_in(); connection_out = packet_get_connection_out(); max_fd = MAX(connection_in, connection_out); - if (control_fd != -1) - max_fd = MAX(max_fd, control_fd); if (!compat20) { /* enable nonblocking unless tty */ @@ -1283,9 +990,6 @@ /* Buffer input from the connection. */ client_process_net_input(readset); - /* Accept control connections. */ - client_process_control(readset); - if (quit_pending) break; @@ -1327,16 +1031,6 @@ if (!isatty(fileno(stderr))) unset_nonblock(fileno(stderr)); - /* - * If there was no shell or command requested, there will be no remote - * exit status to be returned. In that case, clear error code if the - * connection was deliberately terminated at this end. - */ - if (no_shell_flag && received_signal == SIGTERM) { - received_signal = 0; - exit_status = 0; - } - if (received_signal) fatal("Killed by signal %d.", (int) received_signal); @@ -1431,47 +1125,7 @@ /* Flag that we want to exit. */ quit_pending = 1; } -static void -client_input_agent_open(int type, u_int32_t seq, void *ctxt) -{ - Channel *c = NULL; - int remote_id, sock; - /* Read the remote channel number from the message. */ - remote_id = packet_get_int(); - packet_check_eom(); - - /* - * Get a connection to the local authentication agent (this may again - * get forwarded). - */ - sock = ssh_get_authentication_socket(); - - /* - * If we could not connect the agent, send an error message back to - * the server. This should never happen unless the agent dies, - * because authentication forwarding is only enabled if we have an - * agent. - */ - if (sock >= 0) { - c = channel_new("", SSH_CHANNEL_OPEN, sock, sock, - -1, 0, 0, 0, "authentication agent connection", 1); - c->remote_id = remote_id; - c->force_drain = 1; - } - if (c == NULL) { - packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); - packet_put_int(remote_id); - } else { - /* Send a confirmation to the remote host. */ - debug("Forwarding authentication connection."); - packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); - packet_put_int(remote_id); - packet_put_int(c->self); - } - packet_send(); -} - static Channel * client_request_forwarded_tcpip(const char *request_type, int rchan) { @@ -1617,7 +1271,7 @@ client_input_channel_req(int type, u_int32_t seq, void *ctxt) { Channel *c = NULL; - int exitval, id, reply, success = 0; + int id, reply, success = 0; char *rtype; id = packet_get_int(); @@ -1627,28 +1281,24 @@ debug("client_input_channel_req: channel %d rtype %s reply %d", id, rtype, reply); - if (id == -1) { - error("client_input_channel_req: request for channel -1"); - } else if ((c = channel_lookup(id)) == NULL) { + if (session_ident == -1) { + error("client_input_channel_req: no channel %d", session_ident); + } else if (id != session_ident) { + error("client_input_channel_req: channel %d: wrong channel: %d", + session_ident, id); + } + c = channel_lookup(id); + if (c == NULL) { error("client_input_channel_req: channel %d: unknown channel", id); } else if (strcmp(rtype, "exit-status") == 0) { - exitval = packet_get_int(); - if (id == session_ident) { - success = 1; - exit_status = exitval; - } else if (c->ctl_fd == -1) { - error("client_input_channel_req: unexpected channel %d", - session_ident); - } else { - atomicio(vwrite, c->ctl_fd, &exitval, sizeof(exitval)); - success = 1; - } + success = 1; + exit_status = packet_get_int(); packet_check_eom(); } if (reply) { packet_start(success ? SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE); - packet_put_int(id); + packet_put_int(c->remote_id); packet_send(); } xfree(rtype); @@ -1662,8 +1312,7 @@ rtype = packet_get_string(NULL); want_reply = packet_get_char(); - debug("client_input_global_request: rtype %s want_reply %d", - rtype, want_reply); + debug("client_input_global_request: rtype %s want_reply %d", rtype, want_reply); if (want_reply) { packet_start(success ? SSH2_MSG_REQUEST_SUCCESS : SSH2_MSG_REQUEST_FAILURE); @@ -1673,97 +1322,6 @@ xfree(rtype); } -void -client_session2_setup(int id, int want_tty, int want_subsystem, - const char *term, struct termios *tiop, int in_fd, Buffer *cmd, char **env, - dispatch_fn *subsys_repl) -{ - int len; - - debug2("%s: id %d", __func__, id); - - if (want_tty) { - struct winsize ws; - struct termios tio; - - /* Store window size in the packet. */ - if (ioctl(in_fd, TIOCGWINSZ, &ws) < 0) - memset(&ws, 0, sizeof(ws)); - - channel_request_start(id, "pty-req", 0); - packet_put_cstring(term != NULL ? term : ""); - packet_put_int(ws.ws_col); - packet_put_int(ws.ws_row); - packet_put_int(ws.ws_xpixel); - packet_put_int(ws.ws_ypixel); - tio = get_saved_tio(); - tty_make_modes(-1, tiop != NULL ? tiop : &tio); - packet_send(); - /* XXX wait for reply */ - } - - /* Transfer any environment variables from client to server */ - if (options.num_send_env != 0 && env != NULL) { - int i, j, matched; - char *name, *val; - - debug("Sending environment."); - for (i = 0; env[i] != NULL; i++) { - /* Split */ - name = xstrdup(env[i]); - if ((val = strchr(name, '=')) == NULL) { - free(name); - continue; - } - *val++ = '\0'; - - matched = 0; - for (j = 0; j < options.num_send_env; j++) { - if (match_pattern(name, options.send_env[j])) { - matched = 1; - break; - } - } - if (!matched) { - debug3("Ignored env %s", name); - free(name); - continue; - } - - debug("Sending env %s = %s", name, val); - channel_request_start(id, "env", 0); - packet_put_cstring(name); - packet_put_cstring(val); - packet_send(); - free(name); - } - } - - len = buffer_len(cmd); - if (len > 0) { - if (len > 900) - len = 900; - if (want_subsystem) { - debug("Sending subsystem: %.*s", len, (u_char*)buffer_ptr(cmd)); - channel_request_start(id, "subsystem", subsys_repl != NULL); - if (subsys_repl != NULL) { - /* register callback for reply */ - /* XXX we assume that client_loop has already been called */ - dispatch_set(SSH2_MSG_CHANNEL_FAILURE, subsys_repl); - dispatch_set(SSH2_MSG_CHANNEL_SUCCESS, subsys_repl); - } - } else { - debug("Sending command: %.*s", len, (u_char*)buffer_ptr(cmd)); - channel_request_start(id, "exec", 0); - } - packet_put_string(buffer_ptr(cmd), buffer_len(cmd)); - packet_send(); - } else { - channel_request_start(id, "shell", 0); - packet_send(); - } -} - static void client_init_dispatch_20(void) { @@ -1802,7 +1360,7 @@ dispatch_set(SSH_SMSG_STDOUT_DATA, &client_input_stdout_data); dispatch_set(SSH_SMSG_AGENT_OPEN, options.forward_agent ? - &client_input_agent_open : &deny_input_open); + &auth_input_open_request : &deny_input_open); dispatch_set(SSH_SMSG_X11_OPEN, options.forward_x11 ? &x11_input_open : &deny_input_open); } @@ -1826,11 +1384,14 @@ /* client specific fatal cleanup */ void -cleanup_exit(int i) +fatal(const char *fmt,...) { + va_list args; + + va_start(args, fmt); + do_log(SYSLOG_LEVEL_FATAL, fmt, args); + va_end(args); leave_raw_mode(); leave_non_blocking(); - if (options.control_path != NULL && control_fd != -1) - unlink(options.control_path); - _exit(i); + _exit(255); }