-/* $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
#include <openssl/evp.h>
#include <openssl/err.h>
+#include "openbsd-compat/openssl-compat.h"
#include "xmalloc.h"
#include "ssh.h"
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"
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;
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;
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 "
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;
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();
}
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.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];
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)
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
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
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) {
"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
/* 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)
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);
{
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);
+ /* 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;
"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");
/* XXX should be pre-session */
ssh_init_forwarding();
- ssh_control_listener();
if (!no_shell_flag || (datafellows & SSH_BUG_DUMMYCHAN))
id = ssh_session2_open();
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)
load_public_identity_files(void)
{
char *filename, *cp, thishost[NI_MAXHOST];
+ char *pwdir = NULL, *pwname = NULL;
int i = 0;
Key *public;
struct passwd *pw;
#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);
options.identity_files[i] = filename;
options.identity_keys[i] = public;
}
+ bzero(pwname, strlen(pwname));
+ xfree(pwname);
+ bzero(pwdir, strlen(pwdir));
+ xfree(pwdir);
}
static void
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
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;
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);
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 (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]);
}