+/* $OpenBSD: ssh.c,v 1.301 2007/08/07 07:32:53 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.233 2005/03/01 17:22:06 jmc 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 "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"
#include "msg.h"
#include "monitor_fdpass.h"
#include "uidswap.h"
+#include "version.h"
#ifdef SMARTCARD
#include "scard.h"
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;
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);
int dummy;
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();
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);
/* 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;
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 "
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;
else {
fprintf(stderr, "Bad escape character '%s'.\n",
optarg);
- exit(1);
+ exit(255);
}
break;
case 'c':
fprintf(stderr,
"Unknown cipher type '%s'\n",
optarg);
- exit(1);
+ exit(255);
}
if (options.cipher == SSH_CIPHER_3DES)
options.ciphers = "3des-cbc";
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':
fprintf(stderr,
"Bad local forwarding specification '%s'\n",
optarg);
- exit(1);
+ exit(255);
}
break;
fprintf(stderr,
"Bad remote forwarding specification "
"'%s'\n", optarg);
- exit(1);
+ exit(255);
}
break;
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);
no_shell_flag = 1;
no_tty_flag = 1;
break;
- case 'T':
- no_tty_flag = 1;
- 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 '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 's':
subsystem_flag = 1;
break;
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;
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
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)
options.proxy_command = NULL;
+ if (options.control_path != NULL &&
+ strcmp(options.control_path, "none") == 0)
+ 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);
+ 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);
/* Open a connection to the remote host. */
if (ssh_connect(host, &hostaddr, options.port,
original_effective_uid == 0 && options.use_privileged_port,
#endif
options.proxy_command) != 0)
- exit(1);
+ exit(255);
/*
* If we successfully made the connection, load the host private key
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 &&
/*
* 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)
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)
{
for (i = 0; i < options.num_local_forwards; i++) {
debug("Local connections to %.200s:%d forwarded to remote "
"address %.200s:%d",
- (options.local_forwards[i].listen_host == NULL) ?
- (options.gateway_ports ? "*" : "LOCALHOST") :
+ (options.local_forwards[i].listen_host == NULL) ?
+ (options.gateway_ports ? "*" : "LOCALHOST") :
options.local_forwards[i].listen_host,
options.local_forwards[i].listen_port,
options.local_forwards[i].connect_host,
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.");
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,
+ (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;
}
int have_tty = 0;
struct winsize ws;
char *cp;
+ const char *display;
/* Enable compression if requested. */
if (options.compression) {
/* 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);
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();
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
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) +
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)
+ 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);
}
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 */
}
{
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);
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;
}
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);
/* 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)
static void
load_public_identity_files(void)
{
- char *filename;
+ char *filename, *cp, thishost[NI_MAXHOST];
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++;
xfree(keys);
}
#endif /* SMARTCARD */
+ if ((pw = getpwuid(original_real_uid)) == NULL)
+ fatal("load_public_identity_files: getpwuid failed");
+ 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", pw->pw_dir,
+ "u", pw->pw_name, "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);
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]))
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));
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;
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");
switch (mux_command) {
case SSHMUX_COMMAND_ALIVE_CHECK:
- fprintf(stderr, "Master running (pid=%d)\r\n",
+ fprintf(stderr, "Master running (pid=%d)\r\n",
control_server_pid);
exit(0);
case SSHMUX_COMMAND_TERMINATE:
}
/* 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));
}
}
- 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);
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);
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]);
}