]> andersk Git - gssapi-openssh.git/blobdiff - openssh/ssh.c
merged OpenSSH 3.9p1 to trunk
[gssapi-openssh.git] / openssh / ssh.c
index d374399f85c8090d6c5cfb331dd78cd629423cc0..7a7adc00ebc8425c81561e7e80100df20a1f1e27 100644 (file)
@@ -1,3 +1,4 @@
+/* $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
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: ssh.c,v 1.234 2005/03/10 22:01:06 deraadt Exp $");
+
+#include <sys/types.h>
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#include <sys/resource.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+#endif
+#include <pwd.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
 
 #include <openssl/evp.h>
 #include <openssl/err.h>
+#include "openbsd-compat/openssl-compat.h"
 
+#include "xmalloc.h"
 #include "ssh.h"
 #include "ssh1.h"
 #include "ssh2.h"
 #include "compat.h"
 #include "cipher.h"
-#include "xmalloc.h"
 #include "packet.h"
 #include "buffer.h"
-#include "bufaux.h"
 #include "channels.h"
 #include "key.h"
 #include "authfd.h"
@@ -72,6 +100,7 @@ RCSID("$OpenBSD: ssh.c,v 1.234 2005/03/10 22:01:06 deraadt Exp $");
 #include "msg.h"
 #include "monitor_fdpass.h"
 #include "uidswap.h"
+#include "version.h"
 
 #ifdef SMARTCARD
 #include "scard.h"
@@ -145,7 +174,7 @@ pid_t proxy_command_pid = 0;
 int control_fd = -1;
 
 /* Multiplexing control command */
-static u_int mux_command = SSHMUX_COMMAND_OPEN;
+static u_int mux_command = 0;
 
 /* Only used in control client mode */
 volatile sig_atomic_t control_client_terminate = 0;
@@ -157,14 +186,14 @@ static void
 usage(void)
 {
        fprintf(stderr,
-"usage: ssh [-1246AaCfgkMNnqsTtVvXxY] [-b bind_address] [-c cipher_spec]\n"
-"           [-D port] [-e escape_char] [-F configfile]\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"
 "           [-R [bind_address:]port:host:hostport] [-S ctl_path]\n"
-"           [user@]hostname [command]\n"
+"           [-w local_tun[:remote_tun]] [user@]hostname [command]\n"
        );
-       exit(1);
+       exit(255);
 }
 
 static int ssh_session(void);
@@ -182,11 +211,15 @@ 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;
        Forward fwd;
 
+       /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
+       sanitise_stdfd();
+
        __progname = ssh_get_progname(av[0]);
        init_rng();
 
@@ -219,7 +252,7 @@ main(int ac, char **av)
        pw = getpwuid(original_real_uid);
        if (!pw) {
                logit("You don't exist, go away!");
-               exit(1);
+               exit(255);
        }
        /* Take a copy of the returned structure. */
        pw = pwcopy(pw);
@@ -238,9 +271,9 @@ main(int ac, char **av)
        /* Parse command-line arguments. */
        host = NULL;
 
-again:
+ again:
        while ((opt = getopt(ac, av,
-           "1246ab:c:e:fgi:kl:m:no:p:qstvxACD:F:I:L:MNO:PR:S:TVXY")) != -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;
@@ -294,6 +327,10 @@ again:
                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 "
@@ -336,6 +373,15 @@ again:
                        if (opt == 'V')
                                exit(0);
                        break;
+               case 'w':
+                       if (options.tun_open == -1)
+                               options.tun_open = SSH_TUNMODE_DEFAULT;
+                       options.tun_local = a2tun(optarg, &options.tun_remote);
+                       if (options.tun_local == SSH_TUNID_ERR) {
+                               fprintf(stderr, "Bad tun device '%s'\n", optarg);
+                               exit(255);
+                       }
+                       break;
                case 'q':
                        options.log_level = SYSLOG_LEVEL_QUIET;
                        break;
@@ -351,7 +397,7 @@ again:
                        else {
                                fprintf(stderr, "Bad escape character '%s'.\n",
                                    optarg);
-                               exit(1);
+                               exit(255);
                        }
                        break;
                case 'c':
@@ -366,7 +412,7 @@ again:
                                        fprintf(stderr,
                                            "Unknown cipher type '%s'\n",
                                            optarg);
-                                       exit(1);
+                                       exit(255);
                                }
                                if (options.cipher == SSH_CIPHER_3DES)
                                        options.ciphers = "3des-cbc";
@@ -382,18 +428,20 @@ again:
                        else {
                                fprintf(stderr, "Unknown mac type '%s'\n",
                                    optarg);
-                               exit(1);
+                               exit(255);
                        }
                        break;
                case 'M':
-                       options.control_master =
-                           (options.control_master >= 1) ? 2 : 1;
+                       if (options.control_master == SSHCTL_MASTER_YES)
+                               options.control_master = SSHCTL_MASTER_ASK;
+                       else
+                               options.control_master = SSHCTL_MASTER_YES;
                        break;
                case 'p':
                        options.port = a2port(optarg);
                        if (options.port == 0) {
                                fprintf(stderr, "Bad port '%s'\n", optarg);
-                               exit(1);
+                               exit(255);
                        }
                        break;
                case 'l':
@@ -407,7 +455,7 @@ again:
                                fprintf(stderr,
                                    "Bad local forwarding specification '%s'\n",
                                    optarg);
-                               exit(1);
+                               exit(255);
                        }
                        break;
 
@@ -418,7 +466,7 @@ again:
                                fprintf(stderr,
                                    "Bad remote forwarding specification "
                                    "'%s'\n", optarg);
-                               exit(1);
+                               exit(255);
                        }
                        break;
 
@@ -429,20 +477,20 @@ again:
                        if ((fwd.listen_host = hpdelim(&cp)) == NULL) {
                                fprintf(stderr, "Bad dynamic forwarding "
                                    "specification '%.100s'\n", optarg);
-                               exit(1);
+                               exit(255);
                        }
                        if (cp != NULL) {
                                fwd.listen_port = a2port(cp);
                                fwd.listen_host = cleanhostname(fwd.listen_host);
                        } else {
                                fwd.listen_port = a2port(fwd.listen_host);
-                               fwd.listen_host = "";
+                               fwd.listen_host = NULL;
                        }
 
                        if (fwd.listen_port == 0) {
                                fprintf(stderr, "Bad dynamic port '%s'\n",
                                    optarg);
-                               exit(1);
+                               exit(255);
                        }
                        add_local_forward(&options, &fwd);
                        xfree(p);
@@ -457,13 +505,17 @@ again:
                        break;
                case 'T':
                        no_tty_flag = 1;
+                       /* 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;
                        line = xstrdup(optarg);
                        if (process_config_line(&options, host ? host : "",
                            line, "command-line", 0, &dummy) != 0)
-                               exit(1);
+                               exit(255);
                        xfree(line);
                        break;
                case 's':
@@ -550,7 +602,7 @@ again:
        if (no_tty_flag)
                tty_flag = 0;
        /* Do not allocate a tty if stdin is not a tty. */
-       if (!isatty(fileno(stdin)) && !force_tty_flag) {
+       if ((!isatty(fileno(stdin)) || stdin_null_flag) && !force_tty_flag) {
                if (tty_flag)
                        logit("Pseudo-terminal will not be allocated because stdin is not a terminal.");
                tty_flag = 0;
@@ -571,7 +623,7 @@ again:
                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
@@ -627,30 +679,60 @@ again:
        if (options.host_key_alias != NULL) {
                for (p = options.host_key_alias; *p; p++)
                        if (isupper(*p))
-                               *p = tolower(*p);
+                               *p = (char)tolower(*p);
+       }
+
+       /* Get default port if port has not been set. */
+       if (options.port == 0) {
+               sp = getservbyname(SSH_SERVICE_NAME, "tcp");
+               options.port = sp ? ntohs(sp->s_port) : SSH_DEFAULT_PORT;
        }
 
        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) {
+               xfree(options.control_path);
+               options.control_path = NULL;
+       }
 
        if (options.control_path != NULL) {
-               options.control_path = tilde_expand_filename(
-                  options.control_path, original_real_uid);
+               char thishost[NI_MAXHOST];
+
+               if (gethostname(thishost, sizeof(thishost)) == -1)
+                       fatal("gethostname: %s", strerror(errno));
+               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);
        }
-       if (options.control_path != NULL && options.control_master == 0)
-               control_client(options.control_path); /* This doesn't return */
+       if (mux_command != 0 && options.control_path == NULL)
+               fatal("No ControlPath specified for \"-O\" command");
+       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
            original_effective_uid == 0 && options.use_privileged_port,
 #endif
            options.proxy_command) != 0)
-               exit(1);
+               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
@@ -666,16 +748,16 @@ again:
        if (options.rhosts_rsa_authentication ||
            options.hostbased_authentication) {
                sensitive_data.nkeys = 3;
-               sensitive_data.keys = xmalloc(sensitive_data.nkeys *
+               sensitive_data.keys = xcalloc(sensitive_data.nkeys,
                    sizeof(Key));
 
                PRIV_START;
                sensitive_data.keys[0] = key_load_private_type(KEY_RSA1,
-                   _PATH_HOST_KEY_FILE, "", NULL);
+                   _PATH_HOST_KEY_FILE, "", NULL, NULL);
                sensitive_data.keys[1] = key_load_private_type(KEY_DSA,
-                   _PATH_HOST_DSA_KEY_FILE, "", NULL);
+                   _PATH_HOST_DSA_KEY_FILE, "", NULL, NULL);
                sensitive_data.keys[2] = key_load_private_type(KEY_RSA,
-                   _PATH_HOST_RSA_KEY_FILE, "", NULL);
+                   _PATH_HOST_RSA_KEY_FILE, "", NULL, NULL);
                PRIV_END;
 
                if (options.hostbased_authentication == 1 &&
@@ -703,7 +785,7 @@ again:
 
        /*
         * Now that we are back to our own permissions, create ~/.ssh
-        * directory if it doesn\'t already exist.
+        * directory if it doesn't already exist.
         */
        snprintf(buf, sizeof buf, "%.100s%s%.100s", pw->pw_dir, strcmp(pw->pw_dir, "/") ? "/" : "", _PATH_SSH_USER_DIR);
        if (stat(buf, &st) < 0)
@@ -727,7 +809,8 @@ again:
        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) {
@@ -768,110 +851,6 @@ again:
        return exit_status;
 }
 
-#define SSH_X11_PROTO "MIT-MAGIC-COOKIE-1"
-
-static void
-x11_get_proto(char **_proto, char **_data)
-{
-       char cmd[1024];
-       char line[512];
-       char xdisplay[512];
-       static char proto[512], data[512];
-       FILE *f;
-       int got_data = 0, generated = 0, do_unlink = 0, i;
-       char *display, *xauthdir, *xauthfile;
-       struct stat st;
-
-       xauthdir = xauthfile = NULL;
-       *_proto = proto;
-       *_data = data;
-       proto[0] = data[0] = '\0';
-
-       if (!options.xauth_location ||
-           (stat(options.xauth_location, &st) == -1)) {
-               debug("No xauth program.");
-       } else {
-               if ((display = getenv("DISPLAY")) == NULL) {
-                       debug("x11_get_proto: DISPLAY not set");
-                       return;
-               }
-               /*
-                * Handle FamilyLocal case where $DISPLAY does
-                * not match an authorization entry.  For this we
-                * just try "xauth list unix:displaynum.screennum".
-                * XXX: "localhost" match to determine FamilyLocal
-                *      is not perfect.
-                */
-               if (strncmp(display, "localhost:", 10) == 0) {
-                       snprintf(xdisplay, sizeof(xdisplay), "unix:%s",
-                           display + 10);
-                       display = xdisplay;
-               }
-               if (options.forward_x11_trusted == 0) {
-                       xauthdir = xmalloc(MAXPATHLEN);
-                       xauthfile = xmalloc(MAXPATHLEN);
-                       strlcpy(xauthdir, "/tmp/ssh-XXXXXXXXXX", MAXPATHLEN);
-                       if (mkdtemp(xauthdir) != NULL) {
-                               do_unlink = 1;
-                               snprintf(xauthfile, MAXPATHLEN, "%s/xauthfile",
-                                   xauthdir);
-                               snprintf(cmd, sizeof(cmd),
-                                   "%s -f %s generate %s " SSH_X11_PROTO
-                                   " untrusted timeout 1200 2>" _PATH_DEVNULL,
-                                   options.xauth_location, xauthfile, display);
-                               debug2("x11_get_proto: %s", cmd);
-                               if (system(cmd) == 0)
-                                       generated = 1;
-                       }
-               }
-               snprintf(cmd, sizeof(cmd),
-                   "%s %s%s list %s . 2>" _PATH_DEVNULL,
-                   options.xauth_location,
-                   generated ? "-f " : "" ,
-                   generated ? xauthfile : "",
-                   display);
-               debug2("x11_get_proto: %s", cmd);
-               f = popen(cmd, "r");
-               if (f && fgets(line, sizeof(line), f) &&
-                   sscanf(line, "%*s %511s %511s", proto, data) == 2)
-                       got_data = 1;
-               if (f)
-                       pclose(f);
-       }
-
-       if (do_unlink) {
-               unlink(xauthfile);
-               rmdir(xauthdir);
-       }
-       if (xauthdir)
-               xfree(xauthdir);
-       if (xauthfile)
-               xfree(xauthfile);
-
-       /*
-        * If we didn't get authentication data, just make up some
-        * data.  The forwarding code will check the validity of the
-        * response anyway, and substitute this data.  The X11
-        * server, however, will ignore this fake data and use
-        * whatever authentication mechanisms it was using otherwise
-        * for the local connection.
-        */
-       if (!got_data) {
-               u_int32_t rnd = 0;
-
-               logit("Warning: No xauth data; "
-                   "using fake authentication data for X11 forwarding.");
-               strlcpy(proto, SSH_X11_PROTO, sizeof proto);
-               for (i = 0; i < 16; i++) {
-                       if (i % 4 == 0)
-                               rnd = arc4random();
-                       snprintf(data + 2 * i, sizeof data - 2 * i, "%02x",
-                           rnd & 0xff);
-                       rnd >>= 8;
-               }
-       }
-}
-
 static void
 ssh_init_forwarding(void)
 {
@@ -893,8 +872,11 @@ ssh_init_forwarding(void)
                    options.local_forwards[i].listen_port,
                    options.local_forwards[i].connect_host,
                    options.local_forwards[i].connect_port,
-                   options.gateway_ports);
+                   options.gateway_ports, options.hpn_disabled,
+                   options.hpn_buffer_size);
        }
+       if (i > 0 && success != i && options.exit_on_forward_failure)
+               fatal("Could not request local forwarding.");
        if (i > 0 && success == 0)
                error("Could not request local forwarding.");
 
@@ -902,25 +884,41 @@ ssh_init_forwarding(void)
        for (i = 0; i < options.num_remote_forwards; i++) {
                debug("Remote connections from %.200s:%d forwarded to "
                    "local address %.200s:%d",
-                   (options.remote_forwards[i].listen_host == NULL) ? 
-                   (options.gateway_ports ? "*" : "LOCALHOST") : 
-                   options.remote_forwards[i].listen_host,
+                   (options.remote_forwards[i].listen_host == NULL) ?
+                   "LOCALHOST" : options.remote_forwards[i].listen_host,
                    options.remote_forwards[i].listen_port,
                    options.remote_forwards[i].connect_host,
                    options.remote_forwards[i].connect_port);
-               channel_request_remote_forwarding(
+               if (channel_request_remote_forwarding(
                    options.remote_forwards[i].listen_host,
                    options.remote_forwards[i].listen_port,
                    options.remote_forwards[i].connect_host,
-                   options.remote_forwards[i].connect_port);
+                   options.remote_forwards[i].connect_port) < 0) {
+                       if (options.exit_on_forward_failure)
+                               fatal("Could not request remote forwarding.");
+                       else
+                               logit("Warning: Could not request remote "
+                                   "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
 check_agent_present(void)
 {
        if (options.forward_agent) {
-               /* Clear agent forwarding if we don\'t have an agent. */
+               /* Clear agent forwarding if we don't have an agent. */
                if (!ssh_agent_present())
                        options.forward_agent = 0;
        }
@@ -934,6 +932,7 @@ ssh_session(void)
        int have_tty = 0;
        struct winsize ws;
        char *cp;
+       const char *display;
 
        /* Enable compression if requested. */
        if (options.compression) {
@@ -972,10 +971,10 @@ ssh_session(void)
                /* Store window size in the packet. */
                if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0)
                        memset(&ws, 0, sizeof(ws));
-               packet_put_int(ws.ws_row);
-               packet_put_int(ws.ws_col);
-               packet_put_int(ws.ws_xpixel);
-               packet_put_int(ws.ws_ypixel);
+               packet_put_int((u_int)ws.ws_row);
+               packet_put_int((u_int)ws.ws_col);
+               packet_put_int((u_int)ws.ws_xpixel);
+               packet_put_int((u_int)ws.ws_ypixel);
 
                /* Store tty modes in the packet. */
                tty_make_modes(fileno(stdin), NULL);
@@ -995,13 +994,15 @@ ssh_session(void)
                        packet_disconnect("Protocol error waiting for pty request response.");
        }
        /* Request X11 forwarding if enabled and DISPLAY is set. */
-       if (options.forward_x11 && getenv("DISPLAY") != NULL) {
+       display = getenv("DISPLAY");
+       if (options.forward_x11 && display != NULL) {
                char *proto, *data;
                /* Get reasonable local authentication information. */
-               x11_get_proto(&proto, &data);
+               client_x11_get_proto(display, options.xauth_location,
+                   options.forward_x11_trusted, &proto, &data);
                /* Request forwarding with authentication spoofing. */
                debug("Requesting X11 forwarding with authentication spoofing.");
-               x11_request_forwarding_with_spoofing(0, proto, data);
+               x11_request_forwarding_with_spoofing(0, display, proto, data);
 
                /* Read response from the server. */
                type = packet_read();
@@ -1033,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)
@@ -1091,9 +1097,16 @@ client_global_request_reply_fwd(int type, u_int32_t seq, void *ctxt)
            options.remote_forwards[i].listen_port,
            options.remote_forwards[i].connect_host,
            options.remote_forwards[i].connect_port);
-       if (type == SSH2_MSG_REQUEST_FAILURE)
-               logit("Warning: remote port forwarding failed for listen "
-                   "port %d", options.remote_forwards[i].listen_port);
+       if (type == SSH2_MSG_REQUEST_FAILURE) {
+               if (options.exit_on_forward_failure)
+                       fatal("Error: remote port forwarding failed for "
+                           "listen port %d",
+                           options.remote_forwards[i].listen_port);
+               else
+                       logit("Warning: remote port forwarding failed for "
+                           "listen port %d",
+                           options.remote_forwards[i].listen_port);
+       }
 }
 
 static void
@@ -1103,9 +1116,12 @@ ssh_control_listener(void)
        mode_t old_umask;
        int addr_len;
 
-       if (options.control_path == NULL || options.control_master <= 0)
+       if (options.control_path == NULL ||
+           options.control_master == SSHCTL_MASTER_NO)
                return;
 
+       debug("setting up multiplex master socket");
+
        memset(&addr, '\0', sizeof(addr));
        addr.sun_family = AF_UNIX;
        addr_len = offsetof(struct sockaddr_un, sun_path) +
@@ -1116,21 +1132,21 @@ ssh_control_listener(void)
                fatal("ControlPath too long");
 
        if ((control_fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
-               fatal("%s socket(): %s\n", __func__, strerror(errno));
+               fatal("%s socket(): %s", __func__, strerror(errno));
 
        old_umask = umask(0177);
-       if (bind(control_fd, (struct sockaddr*)&addr, addr_len) == -1) {
+       if (bind(control_fd, (struct sockaddr *)&addr, addr_len) == -1) {
                control_fd = -1;
                if (errno == EINVAL || errno == EADDRINUSE)
                        fatal("ControlSocket %s already exists",
                            options.control_path);
                else
-                       fatal("%s bind(): %s\n", __func__, strerror(errno));
+                       fatal("%s bind(): %s", __func__, strerror(errno));
        }
        umask(old_umask);
 
        if (listen(control_fd, 64) == -1)
-               fatal("%s listen(): %s\n", __func__, strerror(errno));
+               fatal("%s listen(): %s", __func__, strerror(errno));
 
        set_nonblock(control_fd);
 }
@@ -1140,15 +1156,18 @@ static void
 ssh_session2_setup(int id, void *arg)
 {
        extern char **environ;
-
+       const char *display;
        int interactive = tty_flag;
-       if (options.forward_x11 && getenv("DISPLAY") != NULL) {
+
+       display = getenv("DISPLAY");
+       if (options.forward_x11 && display != NULL) {
                char *proto, *data;
                /* Get reasonable local authentication information. */
-               x11_get_proto(&proto, &data);
+               client_x11_get_proto(display, options.xauth_location,
+                   options.forward_x11_trusted, &proto, &data);
                /* Request forwarding with authentication spoofing. */
                debug("Requesting X11 forwarding with authentication spoofing.");
-               x11_request_forwarding_with_spoofing(id, proto, data);
+               x11_request_forwarding_with_spoofing(id, display, proto, data);
                interactive = 1;
                /* XXX wait for reply */
        }
@@ -1172,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);
@@ -1192,9 +1214,70 @@ ssh_session2_open(void)
        if (!isatty(err))
                set_nonblock(err);
 
-       window = CHAN_SES_WINDOW_DEFAULT;
+       /* 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)
+       {
+               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;
                window >>= 1;
                packetmax >>= 1;
        }
@@ -1203,6 +1286,10 @@ ssh_session2_open(void)
            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");
+       }
        debug3("ssh_session2_open: channel_new: %d", c->self);
 
        channel_send_open(c->self);
@@ -1219,11 +1306,18 @@ 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();
 
+       /* Execute a local command */
+       if (options.local_command != NULL &&
+           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)
@@ -1236,15 +1330,17 @@ ssh_session2(void)
 static void
 load_public_identity_files(void)
 {
-       char *filename;
+       char *filename, *cp, thishost[NI_MAXHOST];
+       char *pwdir = NULL, *pwname = NULL;
        int i = 0;
        Key *public;
+       struct passwd *pw;
 #ifdef SMARTCARD
        Key **keys;
 
        if (options.smartcard_device != NULL &&
            options.num_identity_files < SSH_MAX_IDENTITY_FILES &&
-           (keys = sc_get_keys(options.smartcard_device, NULL)) != NULL ) {
+           (keys = sc_get_keys(options.smartcard_device, NULL)) != NULL) {
                int count = 0;
                for (i = 0; keys[i] != NULL; i++) {
                        count++;
@@ -1262,9 +1358,20 @@ load_public_identity_files(void)
                xfree(keys);
        }
 #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++) {
-               filename = tilde_expand_filename(options.identity_files[i],
+               cp = tilde_expand_filename(options.identity_files[i],
                    original_real_uid);
+               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);
                debug("identity file %s type %d", filename,
                    public ? public->type : -1);
@@ -1272,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
@@ -1283,21 +1394,25 @@ 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
 env_permitted(char *env)
 {
-       int i;
+       int i, ret;
        char name[1024], *cp;
 
-       strlcpy(name, env, sizeof(name));
-       if ((cp = strchr(name, '=')) == NULL)
+       if ((cp = strchr(env, '=')) == NULL || cp == env)
                return (0);
-
-       *cp = '\0';
+       ret = snprintf(name, sizeof(name), "%.*s", (int)(cp - env), env);
+       if (ret <= 0 || (size_t)ret >= sizeof(name))
+               fatal("env_permitted: name '%.100s...' too long", env);
 
        for (i = 0; i < options.num_send_env; i++)
                if (match_pattern(name, options.send_env[i]))
@@ -1310,19 +1425,24 @@ 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;
        u_int  flags;
 
-       if (stdin_null_flag) {
-               if ((fd = open(_PATH_DEVNULL, O_RDONLY)) == -1)
-                       fatal("open(/dev/null): %s", strerror(errno));
-               if (dup2(fd, STDIN_FILENO) == -1)
-                       fatal("dup2: %s", strerror(errno));
-               if (fd > STDERR_FILENO)
-                       close(fd);
+       if (mux_command == 0)
+               mux_command = SSHMUX_COMMAND_OPEN;
+
+       switch (options.control_master) {
+       case SSHCTL_MASTER_AUTO:
+       case SSHCTL_MASTER_AUTO_ASK:
+               debug("auto-mux: Trying existing master");
+               /* FALLTHROUGH */
+       case SSHCTL_MASTER_NO:
+               break;
+       default:
+               return;
        }
 
        memset(&addr, '\0', sizeof(addr));
@@ -1337,31 +1457,57 @@ control_client(const char *path)
        if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
                fatal("%s socket(): %s", __func__, strerror(errno));
 
-       if (connect(sock, (struct sockaddr*)&addr, addr_len) == -1)
-               fatal("Couldn't connect to %s: %s", path, strerror(errno));
+       if (connect(sock, (struct sockaddr *)&addr, addr_len) == -1) {
+               if (mux_command != SSHMUX_COMMAND_OPEN) {
+                       fatal("Control socket connect(%.100s): %s", path,
+                           strerror(errno));
+               }
+               if (errno == ENOENT)
+                       debug("Control socket \"%.100s\" does not exist", path);
+               else {
+                       error("Control socket connect(%.100s): %s", path,
+                           strerror(errno));
+               }
+               close(sock);
+               return;
+       }
 
-       if ((term = getenv("TERM")) == NULL)
-               term = "";
+       if (stdin_null_flag) {
+               if ((fd = open(_PATH_DEVNULL, O_RDONLY)) == -1)
+                       fatal("open(/dev/null): %s", strerror(errno));
+               if (dup2(fd, STDIN_FILENO) == -1)
+                       fatal("dup2: %s", strerror(errno));
+               if (fd > STDERR_FILENO)
+                       close(fd);
+       }
+
+       term = getenv("TERM");
 
        flags = 0;
        if (tty_flag)
                flags |= SSHMUX_FLAG_TTY;
        if (subsystem_flag)
                flags |= SSHMUX_FLAG_SUBSYS;
+       if (options.forward_x11)
+               flags |= SSHMUX_FLAG_X11_FWD;
+       if (options.forward_agent)
+               flags |= SSHMUX_FLAG_AGENT_FWD;
+
+       signal(SIGPIPE, SIG_IGN);
 
        buffer_init(&m);
 
        /* Send our command to server */
        buffer_put_int(&m, mux_command);
        buffer_put_int(&m, flags);
-       if (ssh_msg_send(sock, /* version */1, &m) == -1)
+       if (ssh_msg_send(sock, SSHMUX_VER, &m) == -1)
                fatal("%s: msg_send", __func__);
        buffer_clear(&m);
 
        /* Get authorisation status and PID of controlee */
        if (ssh_msg_recv(sock, &m) == -1)
                fatal("%s: msg_recv", __func__);
-       if (buffer_get_char(&m) != 1)
+       if (buffer_get_char(&m) != SSHMUX_VER)
                fatal("%s: wrong version", __func__);
        if (buffer_get_int(&m) != 1)
                fatal("Connection to master denied");
@@ -1385,7 +1531,7 @@ control_client(const char *path)
        }
 
        /* SSHMUX_COMMAND_OPEN */
-       buffer_put_cstring(&m, term);
+       buffer_put_cstring(&m, term ? term : "");
        buffer_append(&command, "\0", 1);
        buffer_put_cstring(&m, buffer_ptr(&command));
 
@@ -1407,18 +1553,19 @@ control_client(const char *path)
                        }
        }
 
-       if (ssh_msg_send(sock, /* version */1, &m) == -1)
+       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);
        if (ssh_msg_recv(sock, &m) == -1)
                fatal("%s: msg_recv", __func__);
-       if (buffer_get_char(&m) != 1)
+       if (buffer_get_char(&m) != SSHMUX_VER)
                fatal("%s: wrong version", __func__);
        buffer_free(&m);
 
@@ -1430,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.439798 seconds and 4 git commands to generate.