-/* $OpenBSD: sshconnect.c,v 1.237 2013/02/22 19:13:56 markus Exp $ */
+/* $OpenBSD: sshconnect.c,v 1.246 2014/02/06 22:21:01 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
#include "misc.h"
#include "dns.h"
#include "roaming.h"
+#include "monitor_fdpass.h"
#include "ssh2.h"
#include "version.h"
static int show_other_keys(struct hostkeys *, Key *);
static void warn_changed_key(Key *);
+/* Expand a proxy command */
+static char *
+expand_proxy_command(const char *proxy_command, const char *user,
+ const char *host, int port)
+{
+ char *tmp, *ret, strport[NI_MAXSERV];
+
+ snprintf(strport, sizeof strport, "%d", port);
+ xasprintf(&tmp, "exec %s", proxy_command);
+ ret = percent_expand(tmp, "h", host, "p", strport,
+ "r", options.user, (char *)NULL);
+ free(tmp);
+ return ret;
+}
+
+/*
+ * Connect to the given ssh server using a proxy command that passes a
+ * a connected fd back to us.
+ */
+static int
+ssh_proxy_fdpass_connect(const char *host, u_short port,
+ const char *proxy_command)
+{
+ char *command_string;
+ int sp[2], sock;
+ pid_t pid;
+ char *shell;
+
+ if ((shell = getenv("SHELL")) == NULL)
+ shell = _PATH_BSHELL;
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) < 0)
+ fatal("Could not create socketpair to communicate with "
+ "proxy dialer: %.100s", strerror(errno));
+
+ command_string = expand_proxy_command(proxy_command, options.user,
+ host, port);
+ debug("Executing proxy dialer command: %.500s", command_string);
+
+ /* Fork and execute the proxy command. */
+ if ((pid = fork()) == 0) {
+ char *argv[10];
+
+ /* Child. Permanently give up superuser privileges. */
+ permanently_drop_suid(original_real_uid);
+
+ close(sp[1]);
+ /* Redirect stdin and stdout. */
+ if (sp[0] != 0) {
+ if (dup2(sp[0], 0) < 0)
+ perror("dup2 stdin");
+ }
+ if (sp[0] != 1) {
+ if (dup2(sp[0], 1) < 0)
+ perror("dup2 stdout");
+ }
+ if (sp[0] >= 2)
+ close(sp[0]);
+
+ /*
+ * Stderr is left as it is so that error messages get
+ * printed on the user's terminal.
+ */
+ argv[0] = shell;
+ argv[1] = "-c";
+ argv[2] = command_string;
+ argv[3] = NULL;
+
+ /*
+ * Execute the proxy command.
+ * Note that we gave up any extra privileges above.
+ */
+ execv(argv[0], argv);
+ perror(argv[0]);
+ exit(1);
+ }
+ /* Parent. */
+ if (pid < 0)
+ fatal("fork failed: %.100s", strerror(errno));
+ close(sp[0]);
+ free(command_string);
+
+ if ((sock = mm_receive_fd(sp[1])) == -1)
+ fatal("proxy dialer did not pass back a connection");
+
+ while (waitpid(pid, NULL, 0) == -1)
+ if (errno != EINTR)
+ fatal("Couldn't wait for child: %s", strerror(errno));
+
+ /* Set the connection file descriptors. */
+ packet_set_connection(sock, sock);
+
+ return 0;
+}
+
/*
* Connect to the given ssh server using a proxy command.
*/
static int
ssh_proxy_connect(const char *host, u_short port, const char *proxy_command)
{
- char *command_string, *tmp;
+ char *command_string;
int pin[2], pout[2];
pid_t pid;
- char *shell, strport[NI_MAXSERV];
-
- if (!strcmp(proxy_command, "-")) {
- packet_set_connection(STDIN_FILENO, STDOUT_FILENO);
- packet_set_timeout(options.server_alive_interval,
- options.server_alive_count_max);
- return 0;
- }
+ char *shell;
if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
shell = _PATH_BSHELL;
- /* Convert the port number into a string. */
- snprintf(strport, sizeof strport, "%hu", port);
-
- /*
- * Build the final command string in the buffer by making the
- * appropriate substitutions to the given proxy command.
- *
- * Use "exec" to avoid "sh -c" processes on some platforms
- * (e.g. Solaris)
- */
- xasprintf(&tmp, "exec %s", proxy_command);
- command_string = percent_expand(tmp, "h", host, "p", strport,
- "r", options.user, (char *)NULL);
- xfree(tmp);
-
/* Create pipes for communicating with the proxy. */
if (pipe(pin) < 0 || pipe(pout) < 0)
fatal("Could not create pipes to communicate with the proxy: %.100s",
strerror(errno));
+ command_string = expand_proxy_command(proxy_command, options.user,
+ host, port);
debug("Executing proxy command: %.500s", command_string);
/* Fork and execute the proxy command. */
close(pout[1]);
/* Free the command name. */
- xfree(command_string);
+ free(command_string);
/* Set the connection file descriptors. */
packet_set_connection(pout[0], pin[1]);
- packet_set_timeout(options.server_alive_interval,
- options.server_alive_count_max);
/* Indicate OK return */
return 0;
static int
ssh_create_socket(int privileged, struct addrinfo *ai)
{
- int sock, gaierr;
- struct addrinfo hints, *res;
+ int sock, r, gaierr;
+ struct addrinfo hints, *res = NULL;
- /*
- * If we are running as root and want to connect to a privileged
- * port, bind our own socket to a privileged port.
- */
- if (privileged) {
- int p = IPPORT_RESERVED - 1;
- PRIV_START;
- sock = rresvport_af(&p, ai->ai_family);
- PRIV_END;
- if (sock < 0)
- error("rresvport: af=%d %.100s", ai->ai_family,
- strerror(errno));
- else
- debug("Allocated local port %d.", p);
- return sock;
- }
sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (sock < 0) {
- error("socket: %.100s", strerror(errno));
+ error("socket: %s", strerror(errno));
return -1;
}
fcntl(sock, F_SETFD, FD_CLOEXEC);
/* Bind the socket to an alternative local IP address */
- if (options.bind_address == NULL)
+ if (options.bind_address == NULL && !privileged)
return sock;
- memset(&hints, 0, sizeof(hints));
- hints.ai_family = ai->ai_family;
- hints.ai_socktype = ai->ai_socktype;
- hints.ai_protocol = ai->ai_protocol;
- hints.ai_flags = AI_PASSIVE;
- gaierr = getaddrinfo(options.bind_address, NULL, &hints, &res);
- if (gaierr) {
- error("getaddrinfo: %s: %s", options.bind_address,
- ssh_gai_strerror(gaierr));
- close(sock);
- return -1;
+ if (options.bind_address) {
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = ai->ai_family;
+ hints.ai_socktype = ai->ai_socktype;
+ hints.ai_protocol = ai->ai_protocol;
+ hints.ai_flags = AI_PASSIVE;
+ gaierr = getaddrinfo(options.bind_address, NULL, &hints, &res);
+ if (gaierr) {
+ error("getaddrinfo: %s: %s", options.bind_address,
+ ssh_gai_strerror(gaierr));
+ close(sock);
+ return -1;
+ }
}
- if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) {
- error("bind: %s: %s", options.bind_address, strerror(errno));
- close(sock);
- freeaddrinfo(res);
- return -1;
+ /*
+ * If we are running as root and want to connect to a privileged
+ * port, bind our own socket to a privileged port.
+ */
+ if (privileged) {
+ PRIV_START;
+ r = bindresvport_sa(sock, res ? res->ai_addr : NULL);
+ PRIV_END;
+ if (r < 0) {
+ error("bindresvport_sa: af=%d %s", ai->ai_family,
+ strerror(errno));
+ goto fail;
+ }
+ } else {
+ if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) {
+ error("bind: %s: %s", options.bind_address,
+ strerror(errno));
+ fail:
+ close(sock);
+ freeaddrinfo(res);
+ return -1;
+ }
}
- freeaddrinfo(res);
+ if (res != NULL)
+ freeaddrinfo(res);
return sock;
}
fatal("Bogus return (%d) from select()", rc);
}
- xfree(fdset);
+ free(fdset);
done:
if (result == 0 && *timeoutp > 0) {
* and %p substituted for host and port, respectively) to use to contact
* the daemon.
*/
-int
-ssh_connect(const char *host, struct sockaddr_storage * hostaddr,
- u_short port, int family, int connection_attempts, int *timeout_ms,
- int want_keepalive, int needpriv, const char *proxy_command)
+static int
+ssh_connect_direct(const char *host, struct addrinfo *aitop,
+ struct sockaddr_storage *hostaddr, u_short port, int family,
+ int connection_attempts, int *timeout_ms, int want_keepalive, int needpriv)
{
- int gaierr;
int on = 1;
int sock = -1, attempt;
char ntop[NI_MAXHOST], strport[NI_MAXSERV];
- struct addrinfo hints, *ai, *aitop;
+ struct addrinfo *ai;
debug2("ssh_connect: needpriv %d", needpriv);
- /* If a proxy command is given, connect using it. */
- if (proxy_command != NULL)
- return ssh_proxy_connect(host, port, proxy_command);
-
- /* No proxy command. */
-
- memset(&hints, 0, sizeof(hints));
- hints.ai_family = family;
- hints.ai_socktype = SOCK_STREAM;
- snprintf(strport, sizeof strport, "%u", port);
- if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0)
- fatal("%s: Could not resolve hostname %.100s: %s", __progname,
- host, ssh_gai_strerror(gaierr));
-
for (attempt = 0; attempt < connection_attempts; attempt++) {
if (attempt > 0) {
/* Sleep a moment before retrying. */
* sequence until the connection succeeds.
*/
for (ai = aitop; ai; ai = ai->ai_next) {
- if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
+ if (ai->ai_family != AF_INET &&
+ ai->ai_family != AF_INET6)
continue;
if (getnameinfo(ai->ai_addr, ai->ai_addrlen,
ntop, sizeof(ntop), strport, sizeof(strport),
break; /* Successful connection. */
}
- freeaddrinfo(aitop);
-
/* Return failure if we didn't get a successful connection. */
if (sock == -1) {
error("ssh: connect to host %s port %s: %s",
/* Set the connection. */
packet_set_connection(sock, sock);
- packet_set_timeout(options.server_alive_interval,
- options.server_alive_count_max);
return 0;
}
+int
+ssh_connect(const char *host, struct addrinfo *addrs,
+ struct sockaddr_storage *hostaddr, u_short port, int family,
+ int connection_attempts, int *timeout_ms, int want_keepalive, int needpriv)
+{
+ if (options.proxy_command == NULL) {
+ return ssh_connect_direct(host, addrs, hostaddr, port, family,
+ connection_attempts, timeout_ms, want_keepalive, needpriv);
+ } else if (strcmp(options.proxy_command, "-") == 0) {
+ packet_set_connection(STDIN_FILENO, STDOUT_FILENO);
+ return 0; /* Always succeeds */
+ } else if (options.proxy_use_fdpass) {
+ return ssh_proxy_fdpass_connect(host, port,
+ options.proxy_command);
+ }
+ return ssh_proxy_connect(host, port, options.proxy_command);
+}
+
static void
send_client_banner(int connection_out, int minor1)
{
debug("ssh_exchange_identification: %s", buf);
}
server_version_string = xstrdup(buf);
- xfree(fdset);
+ free(fdset);
/*
* Check that the versions match. In future this might accept
fatal("Protocol major versions differ: %d vs. %d",
(options.protocol & SSH_PROTO_2) ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1,
remote_major);
+ if ((datafellows & SSH_BUG_DERIVEKEY) != 0)
+ fatal("Server version \"%.100s\" uses unsafe key agreement; "
+ "refusing connection", remote_version);
+ if ((datafellows & SSH_BUG_RSASIGMD5) != 0)
+ logit("Server version \"%.100s\" uses unsafe RSA signature "
+ "scheme; disabling use of RSA keys", remote_version);
if (!client_banner_sent)
send_client_banner(connection_out, minor1);
chop(server_version_string);
ret = 0;
if (p && strncasecmp(p, "yes", 3) == 0)
ret = 1;
- if (p)
- xfree(p);
+ free(p);
if (ret != -1)
return ret;
}
ra = key_fingerprint(host_key, SSH_FP_MD5,
SSH_FP_RANDOMART);
logit("Host key fingerprint is %s\n%s\n", fp, ra);
- xfree(ra);
- xfree(fp);
+ free(ra);
+ free(fp);
}
break;
case HOST_NEW:
options.visual_host_key ? "\n" : "",
options.visual_host_key ? ra : "",
msg2);
- xfree(ra);
- xfree(fp);
+ free(ra);
+ free(fp);
if (!confirm(msg))
goto fail;
}
}
}
- xfree(ip);
- xfree(host);
+ free(ip);
+ free(host);
if (host_hostkeys != NULL)
free_hostkeys(host_hostkeys);
if (ip_hostkeys != NULL)
}
if (raw_key != NULL)
key_free(raw_key);
- xfree(ip);
- xfree(host);
+ free(ip);
+ free(host);
if (host_hostkeys != NULL)
free_hostkeys(host_hostkeys);
if (ip_hostkeys != NULL)
fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX);
debug("Server host key: %s %s", key_type(host_key), fp);
- xfree(fp);
+ free(fp);
/* XXX certs are not yet supported for DNS */
if (!key_is_cert(host_key) && options.verify_host_key_dns &&
ssh_login(Sensitive *sensitive, const char *orighost,
struct sockaddr *hostaddr, u_short port, struct passwd *pw, int timeout_ms)
{
- char *host, *cp;
+ char *host;
char *server_user, *local_user;
local_user = xstrdup(pw->pw_name);
/* Convert the user-supplied hostname into all lowercase. */
host = xstrdup(orighost);
- for (cp = host; *cp; cp++)
- if (isupper(*cp))
- *cp = (char)tolower(*cp);
+ lowercase(host);
/* Exchange protocol version identification strings with the server. */
ssh_exchange_identification(timeout_ms);
ssh_kex(host, hostaddr);
ssh_userauth1(local_user, server_user, host, sensitive);
}
- xfree(local_user);
+ free(local_user);
}
void
padded = xcalloc(1, size);
strlcpy(padded, password, size);
packet_put_string(padded, size);
- memset(padded, 0, size);
- xfree(padded);
+ explicit_bzero(padded, size);
+ free(padded);
}
/* print all known host keys for a given host, but skip keys of given type */
static int
show_other_keys(struct hostkeys *hostkeys, Key *key)
{
- int type[] = { KEY_RSA1, KEY_RSA, KEY_DSA, KEY_ECDSA, -1};
+ int type[] = {
+ KEY_RSA1,
+ KEY_RSA,
+ KEY_DSA,
+ KEY_ECDSA,
+ KEY_ED25519,
+ -1
+ };
int i, ret = 0;
char *fp, *ra;
const struct hostkey_entry *found;
key_type(found->key), fp);
if (options.visual_host_key)
logit("%s", ra);
- xfree(ra);
- xfree(fp);
+ free(ra);
+ free(fp);
ret = 1;
}
return ret;
key_type(host_key), fp);
error("Please contact your system administrator.");
- xfree(fp);
+ free(fp);
}
/*