]> andersk Git - gssapi-openssh.git/blobdiff - openssh/clientloop.c
Import of OpenSSH 3.9p1
[gssapi-openssh.git] / openssh / clientloop.c
index 626b29a5a24fd1a4af6a2b736aa2f907a5084cae..0b9a0fb29c82ce05b25a241be1721184faf18954 100644 (file)
@@ -59,7 +59,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: clientloop.c,v 1.117 2003/12/16 15:49:51 markus Exp $");
+RCSID("$OpenBSD: clientloop.c,v 1.130 2004/08/11 21:43:04 avsm Exp $");
 
 #include "ssh.h"
 #include "ssh1.h"
@@ -79,9 +79,11 @@ RCSID("$OpenBSD: clientloop.c,v 1.117 2003/12/16 15:49:51 markus Exp $");
 #include "clientloop.h"
 #include "authfd.h"
 #include "atomicio.h"
-#include "sshtty.h"
+#include "sshpty.h"
 #include "misc.h"
-#include "readpass.h"
+#include "monitor_fdpass.h"
+#include "match.h"
+#include "msg.h"
 
 /* import options */
 extern Options options;
@@ -92,6 +94,9 @@ 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,16 +137,27 @@ 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) {
-               (void) fcntl(fileno(stdin), F_SETFL, 0);
+               unset_nonblock(fileno(stdin));
                in_non_blocking_mode = 0;
        }
 }
@@ -152,7 +168,7 @@ static void
 enter_non_blocking(void)
 {
        in_non_blocking_mode = 1;
-       (void) fcntl(fileno(stdin), F_SETFL, O_NONBLOCK);
+       set_nonblock(fileno(stdin));
 }
 
 /*
@@ -292,19 +308,13 @@ client_check_window_change(void)
        /** XXX race */
        received_window_change_signal = 0;
 
-       if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0)
-               return;
-
        debug2("client_check_window_change: changed");
 
        if (compat20) {
-               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();
+               channel_send_window_changes();
        } 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);
@@ -336,10 +346,9 @@ server_alive_check(void)
  * 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, int *nallocp, int rekeying)
+    int *maxfdp, u_int *nallocp, int rekeying)
 {
        struct timeval tv, *tvp;
        int ret;
@@ -382,6 +391,9 @@ client_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp,
        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
@@ -390,7 +402,7 @@ client_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp,
 
        if (options.server_alive_interval == 0 || !compat20)
                tvp = NULL;
-       else {  
+       else {
                tv.tv_sec = options.server_alive_interval;
                tv.tv_usec = 0;
                tvp = &tv;
@@ -500,6 +512,222 @@ client_process_net_input(fd_set * readset)
        }
 }
 
+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)
 {
@@ -507,6 +735,7 @@ process_cmdline(void)
        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();
@@ -516,44 +745,77 @@ process_cmdline(void)
                goto out;
        while (*s && isspace(*s))
                s++;
-       if (*s == 0)
+       if (*s == '-')
+               s++;    /* Skip cmdline '-', if any */
+       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 (strlen(s) < 2 || s[0] != '-' || !(s[1] == 'L' || s[1] == 'R')) {
+       }
+
+       if (*s == 'K') {
+               delete = 1;
+               s++;
+       }
+       if (*s != 'L' && *s != 'R') {
                logit("Invalid command.");
                goto out;
        }
-       if (s[1] == 'L')
+       if (*s == 'L')
                local = 1;
-       if (!local && !compat20) {
+       if (local && delete) {
+               logit("Not supported.");
+               goto out;
+       }
+       if ((!local || delete) && !compat20) {
                logit("Not supported for SSH protocol version 1.");
                goto out;
        }
-       s += 2;
+
+       s++;
        while (*s && isspace(*s))
                s++;
 
-       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.");
+       if (delete) {
+               if (sscanf(s, "%5[0-9]", sfwd_host_port) != 1) {
+                       logit("Bad forwarding specification.");
+                       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;
                }
-       } else
-               channel_request_remote_forwarding(fwd_port, buf,
-                   fwd_host_port);
-       logit("Forwarding port.");
+               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.");
+       }
+
 out:
        signal(SIGINT, handler);
        enter_raw_mode();
@@ -868,9 +1130,6 @@ simple_escape_filter(Channel *c, char *buf, int len)
 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();
@@ -888,7 +1147,8 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id)
 {
        fd_set *readset = NULL, *writeset = NULL;
        double start_time, total_time;
-       int max_fd = 0, max_fd2 = 0, len, rekeying = 0, nalloc = 0;
+       int max_fd = 0, max_fd2 = 0, len, rekeying = 0;
+       u_int nalloc = 0;
        char buf[100];
 
        debug("Entering interactive session.");
@@ -904,6 +1164,8 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id)
        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 */
@@ -1021,6 +1283,9 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id)
                /* Buffer input from the connection.  */
                client_process_net_input(readset);
 
+               /* Accept control connections.  */
+               client_process_control(readset);
+
                if (quit_pending)
                        break;
 
@@ -1352,7 +1617,7 @@ static void
 client_input_channel_req(int type, u_int32_t seq, void *ctxt)
 {
        Channel *c = NULL;
-       int id, reply, success = 0;
+       int exitval, id, reply, success = 0;
        char *rtype;
 
        id = packet_get_int();
@@ -1362,24 +1627,28 @@ client_input_channel_req(int type, u_int32_t seq, void *ctxt)
        debug("client_input_channel_req: channel %d rtype %s reply %d",
            id, rtype, reply);
 
-       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) {
+       if (id == -1) {
+               error("client_input_channel_req: request for channel -1");
+       } else if ((c = channel_lookup(id)) == NULL) {
                error("client_input_channel_req: channel %d: unknown channel", id);
        } else if (strcmp(rtype, "exit-status") == 0) {
-               success = 1;
-               exit_status = packet_get_int();
+               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;
+               }
                packet_check_eom();
        }
        if (reply) {
                packet_start(success ?
                    SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE);
-               packet_put_int(c->remote_id);
+               packet_put_int(id);
                packet_send();
        }
        xfree(rtype);
@@ -1404,6 +1673,97 @@ client_input_global_request(int type, u_int32_t seq, void *ctxt)
        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)
 {
@@ -1470,5 +1830,7 @@ cleanup_exit(int i)
 {
        leave_raw_mode();
        leave_non_blocking();
+       if (options.control_path != NULL && control_fd != -1)
+               unlink(options.control_path);
        _exit(i);
 }
This page took 0.056214 seconds and 4 git commands to generate.