X-Git-Url: http://andersk.mit.edu/gitweb/openssh.git/blobdiff_plain/148de80c5366ede880c9e7a26d5b1e1599610c57..2db5d1e99d07df3ac8023af52aaa09f0513a9e61:/ssh.c diff --git a/ssh.c b/ssh.c index 55021844..fe2f1adf 100644 --- a/ssh.c +++ b/ssh.c @@ -1,3 +1,4 @@ +/* $OpenBSD: ssh.c,v 1.309 2008/01/19 20:51:26 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -47,26 +48,39 @@ #endif #include #include +#include #include #include +#include +#include +#include #ifdef HAVE_PATHS_H #include #endif +#include #include +#include +#include +#include +#include +#include +#include + +#include +#include #include #include +#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" @@ -85,6 +99,7 @@ #include "msg.h" #include "monitor_fdpass.h" #include "uidswap.h" +#include "version.h" #ifdef SMARTCARD #include "scard.h" @@ -170,12 +185,12 @@ 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" " [-R [bind_address:]port:host:hostport] [-S ctl_path]\n" -" [-w tunnel:tunnel] [user@]hostname [command]\n" +" [-w local_tun[:remote_tun]] [user@]hostname [command]\n" ); exit(255); } @@ -195,7 +210,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; @@ -257,7 +272,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; @@ -311,6 +326,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 " @@ -599,7 +618,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 { snprintf(buf, sizeof buf, "%.100s/%.100s", pw->pw_dir, _PATH_SSH_USER_CONFFILE); (void)read_config_file(buf, host, &options, 1); @@ -629,7 +648,7 @@ main(int ac, char **av) 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. */ @@ -639,22 +658,27 @@ 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 me[NI_MAXHOST]; + char thishost[NI_MAXHOST]; - if (gethostname(me, sizeof(me)) == -1) + 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", me, (char *)NULL); + "r", options.user, "l", thishost, (char *)NULL); xfree(cp); } if (mux_command != 0 && options.control_path == NULL) @@ -662,9 +686,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 @@ -673,6 +700,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 @@ -687,16 +717,16 @@ main(int ac, char **av) 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 && @@ -748,7 +778,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) { @@ -812,6 +843,8 @@ ssh_init_forwarding(void) options.local_forwards[i].connect_port, options.gateway_ports); } + 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."); @@ -824,12 +857,29 @@ ssh_init_forwarding(void) 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 @@ -952,6 +1002,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) @@ -1010,9 +1065,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 @@ -1041,7 +1103,7 @@ ssh_control_listener(void) 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", @@ -1085,33 +1147,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) { - c = channel_new("tun", SSH_CHANNEL_OPENING, fd, fd, -1, - CHAN_TCP_WINDOW_DEFAULT, 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); @@ -1171,7 +1206,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(); @@ -1181,6 +1215,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) @@ -1193,15 +1230,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++; @@ -1219,9 +1258,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); @@ -1229,6 +1279,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 @@ -1240,21 +1294,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])) @@ -1267,7 +1325,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; @@ -1299,7 +1357,7 @@ 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) { + if (connect(sock, (struct sockaddr *)&addr, addr_len) == -1) { if (mux_command != SSHMUX_COMMAND_OPEN) { fatal("Control socket connect(%.100s): %s", path, strerror(errno)); @@ -1335,6 +1393,8 @@ control_client(const char *path) if (options.forward_agent) flags |= SSHMUX_FLAG_AGENT_FWD; + signal(SIGPIPE, SIG_IGN); + buffer_init(&m); /* Send our command to server */ @@ -1396,9 +1456,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); @@ -1416,29 +1477,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]); }