]> andersk Git - gssapi-openssh.git/blobdiff - openssh/ssh.c
merged OpenSSH 3.9p1 to trunk
[gssapi-openssh.git] / openssh / ssh.c
index ed03f819ff3c21ed4722174b39f2338823a35d6c..7a7adc00ebc8425c81561e7e80100df20a1f1e27 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh.c,v 1.293 2006/08/03 03:34:42 deraadt Exp $ */
+/* $OpenBSD: ssh.c,v 1.309 2008/01/19 20:51:26 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -72,6 +72,7 @@
 
 #include <openssl/evp.h>
 #include <openssl/err.h>
+#include "openbsd-compat/openssl-compat.h"
 
 #include "xmalloc.h"
 #include "ssh.h"
@@ -185,7 +186,7 @@ static void
 usage(void)
 {
        fprintf(stderr,
-"usage: ssh [-1246AaCfgkMNnqsTtVvXxY] [-b bind_address] [-c cipher_spec]\n"
+"usage: ssh [-1246AaCfgKkMNnqsTtVvXxY] [-b bind_address] [-c cipher_spec]\n"
 "           [-D [bind_address:]port] [-e escape_char] [-F configfile]\n"
 "           [-i identity_file] [-L [bind_address:]port:host:hostport]\n"
 "           [-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port]\n"
@@ -210,7 +211,7 @@ main(int ac, char **av)
        char *p, *cp, *line, buf[256];
        struct stat st;
        struct passwd *pw;
-       int dummy;
+       int dummy, timeout_ms;
        extern int optind, optreset;
        extern char *optarg;
        struct servent *sp;
@@ -272,7 +273,7 @@ main(int ac, char **av)
 
  again:
        while ((opt = getopt(ac, av,
-           "1246ab:c:e:fgi:kl:m:no:p:qstvxACD:F:I:L:MNO:PR:S:TVw:XY")) != -1) {
+           "1246ab:c:e:fgi:kl:m:no:p:qstvxACD:F:I:KL:MNO:PR:S:TVw:XY")) != -1) {
                switch (opt) {
                case '1':
                        options.protocol = SSH_PROTO_1;
@@ -326,6 +327,10 @@ main(int ac, char **av)
                case 'k':
                        options.gss_deleg_creds = 0;
                        break;
+               case 'K':
+                       options.gss_authentication = 1;
+                       options.gss_deleg_creds = 1;
+                       break;
                case 'i':
                        if (stat(optarg, &st) < 0) {
                                fprintf(stderr, "Warning: Identity file %s "
@@ -497,11 +502,13 @@ main(int ac, char **av)
                case 'N':
                        no_shell_flag = 1;
                        no_tty_flag = 1;
-                       options.none_switch = 0;
                        break;
                case 'T':
                        no_tty_flag = 1;
-                       options.none_switch = 0;
+                       /* ensure that the user doesn't try to backdoor a */
+                       /* null cipher switch on an interactive session */
+                       /* so explicitly disable it no matter what */
+                       options.none_switch=0;
                        break;
                case 'o':
                        dummy = 1;
@@ -525,14 +532,6 @@ main(int ac, char **av)
                case 'F':
                        config = optarg;
                        break;
-               case 'z':
-                       /* make sure we can't turn on the none_switch */
-                       /* if they try to force a no tty flag on a tty session */
-                       if (!no_tty_flag) {
-                               options.none_switch = 1;
-                       }
-                       break;
-
                default:
                        usage();
                }
@@ -624,7 +623,7 @@ main(int ac, char **av)
                if (!read_config_file(config, host, &options, 0))
                        fatal("Can't open user config file %.100s: "
                            "%.100s", config, strerror(errno));
-       } else  {
+       } else {
            /*
             * Since the config file parsing code aborts if it sees
             * options it doesn't recognize, allow users to put
@@ -690,11 +689,15 @@ main(int ac, char **av)
        }
 
        if (options.proxy_command != NULL &&
-           strcmp(options.proxy_command, "none") == 0)
+           strcmp(options.proxy_command, "none") == 0) {
+               xfree(options.proxy_command);
                options.proxy_command = NULL;
+       }
        if (options.control_path != NULL &&
-           strcmp(options.control_path, "none") == 0)
+           strcmp(options.control_path, "none") == 0) {
+               xfree(options.control_path);
                options.control_path = NULL;
+       }
 
        if (options.control_path != NULL) {
                char thishost[NI_MAXHOST];
@@ -704,6 +707,7 @@ main(int ac, char **av)
                snprintf(buf, sizeof(buf), "%d", options.port);
                cp = tilde_expand_filename(options.control_path,
                    original_real_uid);
+               xfree(options.control_path);
                options.control_path = percent_expand(cp, "p", buf, "h", host,
                    "r", options.user, "l", thishost, (char *)NULL);
                xfree(cp);
@@ -713,9 +717,12 @@ main(int ac, char **av)
        if (options.control_path != NULL)
                control_client(options.control_path);
 
+       timeout_ms = options.connection_timeout * 1000;
+
        /* Open a connection to the remote host. */
        if (ssh_connect(host, &hostaddr, options.port,
-           options.address_family, options.connection_attempts,
+           options.address_family, options.connection_attempts, &timeout_ms,
+           options.tcp_keep_alive, 
 #ifdef HAVE_CYGWIN
            options.use_privileged_port,
 #else
@@ -724,6 +731,9 @@ main(int ac, char **av)
            options.proxy_command) != 0)
                exit(255);
 
+       if (timeout_ms > 0)
+               debug3("timeout: %d ms remain after connect", timeout_ms);
+
        /*
         * If we successfully made the connection, load the host private key
         * in case we will need it later for combined rsa-rhosts
@@ -799,7 +809,8 @@ main(int ac, char **av)
        signal(SIGPIPE, SIG_IGN); /* ignore SIGPIPE early */
 
        /* Log into the remote system.  This never returns if the login fails. */
-       ssh_login(&sensitive_data, host, (struct sockaddr *)&hostaddr, pw);
+       ssh_login(&sensitive_data, host, (struct sockaddr *)&hostaddr,
+           pw, timeout_ms);
 
        /* We no longer need the private host keys.  Clear them now. */
        if (sensitive_data.nkeys != 0) {
@@ -890,6 +901,17 @@ ssh_init_forwarding(void)
                                    "forwarding.");
                }
        }
+
+       /* Initiate tunnel forwarding. */
+       if (options.tun_open != SSH_TUNMODE_NO) {
+               if (client_request_tun_fwd(options.tun_open,
+                   options.tun_local, options.tun_remote) == -1) {
+                       if (options.exit_on_forward_failure)
+                               fatal("Could not request tunnel forwarding.");
+                       else
+                               error("Could not request tunnel forwarding.");
+               }
+       }                       
 }
 
 static void
@@ -1012,6 +1034,11 @@ ssh_session(void)
        /* Initiate port forwardings. */
        ssh_init_forwarding();
 
+       /* Execute a local command */
+       if (options.local_command != NULL &&
+           options.permit_local_command)
+               ssh_local_cmd(options.local_command);
+
        /* If requested, let ssh continue in the background. */
        if (fork_after_authentication_flag)
                if (daemon(1, 1) < 0)
@@ -1152,38 +1179,6 @@ ssh_session2_setup(int id, void *arg)
                packet_send();
        }
 
-       if (options.tun_open != SSH_TUNMODE_NO) {
-               Channel *c;
-               int fd;
-
-               debug("Requesting tun.");
-               if ((fd = tun_open(options.tun_local,
-                   options.tun_open)) >= 0) {
-                       if(options.hpn_disabled)
-                               c = channel_new("tun", SSH_CHANNEL_OPENING, fd, fd, -1,
-                                   CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,
-                                   0, "tun", 1);
-                       else
-                               c = channel_new("tun", SSH_CHANNEL_OPENING, fd, fd, -1,
-                                   options.hpn_buffer_size, CHAN_TCP_PACKET_DEFAULT,
-                                   0, "tun", 1);
-                       c->datagram = 1;
-#if defined(SSH_TUN_FILTER)
-                       if (options.tun_open == SSH_TUNMODE_POINTOPOINT)
-                               channel_register_filter(c->self, sys_tun_infilter,
-                                   sys_tun_outfilter);
-#endif
-                       packet_start(SSH2_MSG_CHANNEL_OPEN);
-                       packet_put_cstring("tun@openssh.com");
-                       packet_put_int(c->self);
-                       packet_put_int(c->local_window_max);
-                       packet_put_int(c->local_maxpacket);
-                       packet_put_int(options.tun_open);
-                       packet_put_int(options.tun_remote);
-                       packet_send();
-               }
-       }
-
        client_session2_setup(id, tty_flag, subsystem_flag, getenv("TERM"),
            NULL, fileno(stdin), &command, environ, &ssh_subsystem_reply);
 
@@ -1196,6 +1191,9 @@ ssh_session2_open(void)
 {
        Channel *c;
        int window, packetmax, in, out, err;
+       int sock;
+       int socksize;
+       int socksizelen = sizeof(int);
 
        if (stdin_null_flag) {
                in = open(_PATH_DEVNULL, O_RDONLY);
@@ -1216,10 +1214,67 @@ ssh_session2_open(void)
        if (!isatty(err))
                set_nonblock(err);
 
+       /* we need to check to see if what they want to do about buffer */
+       /* sizes here. In a hpn to nonhpn connection we want to limit */
+       /* the window size to something reasonable in case the far side */
+       /* has the large window bug. In hpn to hpn connection we want to */
+       /* use the max window size but allow the user to override it */
+       /* lastly if they disabled hpn then use the ssh std window size */
+
+       /* so why don't we just do a getsockopt() here and set the */
+       /* ssh window to that? In the case of a autotuning receive */
+       /* window the window would get stuck at the initial buffer */
+       /* size generally less than 96k. Therefore we need to set the */
+       /* maximum ssh window size to the maximum hpn buffer size */
+       /* unless the user has specifically set the tcprcvbufpoll */
+       /* to no. In which case we *can* just set the window to the */
+       /* minimum of the hpn buffer size and tcp receive buffer size */
+       
        if(options.hpn_disabled)
-               window = CHAN_SES_WINDOW_DEFAULT;
-       else
-               window = options.hpn_buffer_size;
+       {
+               options.hpn_buffer_size = CHAN_SES_WINDOW_DEFAULT;
+       }
+       else if (datafellows & SSH_BUG_LARGEWINDOW) 
+       {
+               debug("HPN to Non-HPN Connection");
+               if (options.hpn_buffer_size < 0)
+                       options.hpn_buffer_size = 2*1024*1024;
+       } 
+       else 
+       {
+               if (options.hpn_buffer_size < 0)
+                       options.hpn_buffer_size = BUFFER_MAX_LEN_HPN;
+
+               /*create a socket but don't connect it */
+               /* we use that the get the rcv socket size */
+               sock = socket(AF_INET, SOCK_STREAM, 0);
+               /* if they are using the tcp_rcv_buf option */
+               /* attempt to set the buffer size to that */
+               if (options.tcp_rcv_buf) 
+                       setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (void *)&options.tcp_rcv_buf, 
+                                  sizeof(options.tcp_rcv_buf));
+               getsockopt(sock, SOL_SOCKET, SO_RCVBUF, 
+                          &socksize, &socksizelen);
+               close(sock);
+               debug("socksize %d", socksize);
+               if (options.tcp_rcv_buf_poll <= 0) 
+               {
+                       options.hpn_buffer_size = MIN(socksize,options.hpn_buffer_size);
+                       debug ("MIN of TCP RWIN and HPNBufferSize: %d", options.hpn_buffer_size);
+               } 
+               else
+               {
+                       if (options.tcp_rcv_buf > 0) 
+                               options.hpn_buffer_size = MIN(options.tcp_rcv_buf, options.hpn_buffer_size);
+                               debug ("MIN of TCPRcvBuf and HPNBufferSize: %d", options.hpn_buffer_size);
+               }
+               
+       }
+
+       debug("Final hpn_buffer_size = %d", options.hpn_buffer_size);
+
+       window = options.hpn_buffer_size;
+
        packetmax = CHAN_SES_PACKET_DEFAULT;
        if (tty_flag) {
                window = 4*CHAN_SES_PACKET_DEFAULT;
@@ -1230,6 +1285,7 @@ ssh_session2_open(void)
            "session", SSH_CHANNEL_OPENING, in, out, err,
            window, packetmax, CHAN_EXTENDED_WRITE,
            "client-session", /*nonblock*/0);
+
        if ((options.tcp_rcv_buf_poll > 0) && (!options.hpn_disabled)) {
                c->dynamic_window = 1;
                debug ("Enabled Dynamic Window Scaling\n");
@@ -1250,7 +1306,6 @@ ssh_session2(void)
 
        /* XXX should be pre-session */
        ssh_init_forwarding();
-       ssh_control_listener();
 
        if (!no_shell_flag || (datafellows & SSH_BUG_DUMMYCHAN))
                id = ssh_session2_open();
@@ -1260,6 +1315,9 @@ ssh_session2(void)
            options.permit_local_command)
                ssh_local_cmd(options.local_command);
 
+       /* Start listening for multiplex clients */
+       ssh_control_listener();
+
        /* If requested, let ssh continue in the background. */
        if (fork_after_authentication_flag)
                if (daemon(1, 1) < 0)
@@ -1273,6 +1331,7 @@ static void
 load_public_identity_files(void)
 {
        char *filename, *cp, thishost[NI_MAXHOST];
+       char *pwdir = NULL, *pwname = NULL;
        int i = 0;
        Key *public;
        struct passwd *pw;
@@ -1301,14 +1360,16 @@ load_public_identity_files(void)
 #endif /* SMARTCARD */
        if ((pw = getpwuid(original_real_uid)) == NULL)
                fatal("load_public_identity_files: getpwuid failed");
+       pwname = xstrdup(pw->pw_name);
+       pwdir = xstrdup(pw->pw_dir);
        if (gethostname(thishost, sizeof(thishost)) == -1)
                fatal("load_public_identity_files: gethostname: %s",
                    strerror(errno));
        for (; i < options.num_identity_files; i++) {
                cp = tilde_expand_filename(options.identity_files[i],
                    original_real_uid);
-               filename = percent_expand(cp, "d", pw->pw_dir,
-                   "u", pw->pw_name, "l", thishost, "h", host,
+               filename = percent_expand(cp, "d", pwdir,
+                   "u", pwname, "l", thishost, "h", host,
                    "r", options.user, (char *)NULL);
                xfree(cp);
                public = key_load_public(filename, NULL);
@@ -1318,6 +1379,10 @@ load_public_identity_files(void)
                options.identity_files[i] = filename;
                options.identity_keys[i] = public;
        }
+       bzero(pwname, strlen(pwname));
+       xfree(pwname);
+       bzero(pwdir, strlen(pwdir));
+       xfree(pwdir);
 }
 
 static void
@@ -1329,8 +1394,12 @@ control_client_sighandler(int signo)
 static void
 control_client_sigrelay(int signo)
 {
+       int save_errno = errno;
+
        if (control_server_pid > 1)
                kill(control_server_pid, signo);
+
+       errno = save_errno;
 }
 
 static int
@@ -1356,7 +1425,7 @@ static void
 control_client(const char *path)
 {
        struct sockaddr_un addr;
-       int i, r, fd, sock, exitval, num_env, addr_len;
+       int i, r, fd, sock, exitval[2], num_env, addr_len;
        Buffer m;
        char *term;
        extern char **environ;
@@ -1423,23 +1492,14 @@ control_client(const char *path)
                flags |= SSHMUX_FLAG_X11_FWD;
        if (options.forward_agent)
                flags |= SSHMUX_FLAG_AGENT_FWD;
-       if (options.num_local_forwards > 0)
-               flags |= SSHMUX_FLAG_PORTFORWARD;
+
+       signal(SIGPIPE, SIG_IGN);
+
        buffer_init(&m);
 
        /* Send our command to server */
        buffer_put_int(&m, mux_command);
        buffer_put_int(&m, flags);
-       if (options.num_local_forwards > 0)
-        {
-               if (options.local_forwards[0].listen_host == NULL)
-                       buffer_put_string(&m,"LOCALHOST",11);
-               else
-                       buffer_put_string(&m,options.local_forwards[0].listen_host,512);
-               buffer_put_int(&m,options.local_forwards[0].listen_port);
-               buffer_put_string(&m,options.local_forwards[0].connect_host,512);       
-               buffer_put_int(&m,options.local_forwards[0].connect_port);
-       }
        if (ssh_msg_send(sock, SSHMUX_VER, &m) == -1)
                fatal("%s: msg_send", __func__);
        buffer_clear(&m);
@@ -1496,9 +1556,10 @@ control_client(const char *path)
        if (ssh_msg_send(sock, SSHMUX_VER, &m) == -1)
                fatal("%s: msg_send", __func__);
 
-       mm_send_fd(sock, STDIN_FILENO);
-       mm_send_fd(sock, STDOUT_FILENO);
-       mm_send_fd(sock, STDERR_FILENO);
+       if (mm_send_fd(sock, STDIN_FILENO) == -1 ||
+           mm_send_fd(sock, STDOUT_FILENO) == -1 ||
+           mm_send_fd(sock, STDERR_FILENO) == -1)
+               fatal("%s: send fds failed", __func__);
 
        /* Wait for reply, so master has a chance to gather ttymodes */
        buffer_clear(&m);
@@ -1516,29 +1577,44 @@ control_client(const char *path)
        if (tty_flag)
                enter_raw_mode();
 
-       /* Stick around until the controlee closes the client_fd */
-       exitval = 0;
-       for (;!control_client_terminate;) {
-               r = read(sock, &exitval, sizeof(exitval));
+       /*
+        * Stick around until the controlee closes the client_fd.
+        * Before it does, it is expected to write this process' exit
+        * value (one int). This process must read the value and wait for
+        * the closure of the client_fd; if this one closes early, the 
+        * multiplex master will terminate early too (possibly losing data).
+        */
+       exitval[0] = 0;
+       for (i = 0; !control_client_terminate && i < (int)sizeof(exitval);) {
+               r = read(sock, (char *)exitval + i, sizeof(exitval) - i);
                if (r == 0) {
                        debug2("Received EOF from master");
                        break;
                }
-               if (r > 0)
-                       debug2("Received exit status from master %d", exitval);
-               if (r == -1 && errno != EINTR)
+               if (r == -1) {
+                       if (errno == EINTR)
+                               continue;
                        fatal("%s: read %s", __func__, strerror(errno));
+               }
+               i += r;
        }
 
-       if (control_client_terminate)
-               debug2("Exiting on signal %d", control_client_terminate);
-
        close(sock);
-
        leave_raw_mode();
+       if (i > (int)sizeof(int))
+               fatal("%s: master returned too much data (%d > %lu)",
+                   __func__, i, sizeof(int));
+       if (control_client_terminate) {
+               debug2("Exiting on signal %d", control_client_terminate);
+               exitval[0] = 255;
+       } else if (i < (int)sizeof(int)) {
+               debug2("Control master terminated unexpectedly");
+               exitval[0] = 255;
+       } else
+               debug2("Received exit status from master %d", exitval[0]);
 
        if (tty_flag && options.log_level != SYSLOG_LEVEL_QUIET)
-               fprintf(stderr, "Connection to master closed.\r\n");
+               fprintf(stderr, "Shared connection to %s closed.\r\n", host);
 
-       exit(exitval);
+       exit(exitval[0]);
 }
This page took 0.214392 seconds and 4 git commands to generate.