summaryrefslogtreecommitdiff
path: root/sshconnect.c
diff options
context:
space:
mode:
Diffstat (limited to 'sshconnect.c')
-rw-r--r--sshconnect.c278
1 files changed, 63 insertions, 215 deletions
diff --git a/sshconnect.c b/sshconnect.c
index 6d819279ee66..fdcdcd85544a 100644
--- a/sshconnect.c
+++ b/sshconnect.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sshconnect.c,v 1.305 2018/09/20 03:30:44 djm Exp $ */
+/* $OpenBSD: sshconnect.c,v 1.314 2019/02/27 19:37:01 markus Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -68,9 +68,8 @@
#include "authfile.h"
#include "ssherr.h"
#include "authfd.h"
+#include "kex.h"
-char *client_version_string = NULL;
-char *server_version_string = NULL;
struct sshkey *previous_host_key = NULL;
static int matching_host_key_dns = 0;
@@ -78,6 +77,7 @@ static int matching_host_key_dns = 0;
static pid_t proxy_command_pid = 0;
/* import */
+extern int debug_flag;
extern Options options;
extern char *__progname;
@@ -99,6 +99,24 @@ expand_proxy_command(const char *proxy_command, const char *user,
return ret;
}
+static void
+stderr_null(void)
+{
+ int devnull;
+
+ if ((devnull = open(_PATH_DEVNULL, O_WRONLY)) == -1) {
+ error("Can't open %s for stderr redirection: %s",
+ _PATH_DEVNULL, strerror(errno));
+ return;
+ }
+ if (devnull == STDERR_FILENO)
+ return;
+ if (dup2(devnull, STDERR_FILENO) == -1)
+ error("Cannot redirect stderr to %s", _PATH_DEVNULL);
+ if (devnull > STDERR_FILENO)
+ close(devnull);
+}
+
/*
* Connect to the given ssh server using a proxy command that passes a
* a connected fd back to us.
@@ -141,9 +159,13 @@ ssh_proxy_fdpass_connect(struct ssh *ssh, const char *host, u_short port,
close(sp[0]);
/*
- * Stderr is left as it is so that error messages get
- * printed on the user's terminal.
+ * Stderr is left for non-ControlPersist connections is so
+ * error messages may be printed on the user's terminal.
*/
+ if (!debug_flag && options.control_path != NULL &&
+ options.control_persist)
+ stderr_null();
+
argv[0] = shell;
argv[1] = "-c";
argv[2] = command_string;
@@ -219,8 +241,14 @@ ssh_proxy_connect(struct ssh *ssh, const char *host, u_short port,
/* Cannot be 1 because pin allocated two descriptors. */
close(pout[1]);
- /* Stderr is left as it is so that error messages get
- printed on the user's terminal. */
+ /*
+ * Stderr is left for non-ControlPersist connections is so
+ * error messages may be printed on the user's terminal.
+ */
+ if (!debug_flag && options.control_path != NULL &&
+ options.control_persist)
+ stderr_null();
+
argv[0] = shell;
argv[1] = "-c";
argv[2] = command_string;
@@ -369,10 +397,6 @@ ssh_create_socket(struct addrinfo *ai)
error("getaddrinfo: no addrs");
goto fail;
}
- if (res->ai_addrlen > sizeof(bindaddr)) {
- error("%s: addr doesn't fit", __func__);
- goto fail;
- }
memcpy(&bindaddr, res->ai_addr, res->ai_addrlen);
bindaddrlen = res->ai_addrlen;
} else if (options.bind_interface != NULL) {
@@ -420,73 +444,6 @@ fail:
}
/*
- * Wait up to *timeoutp milliseconds for fd to be readable. Updates
- * *timeoutp with time remaining.
- * Returns 0 if fd ready or -1 on timeout or error (see errno).
- */
-static int
-waitrfd(int fd, int *timeoutp)
-{
- struct pollfd pfd;
- struct timeval t_start;
- int oerrno, r;
-
- monotime_tv(&t_start);
- pfd.fd = fd;
- pfd.events = POLLIN;
- for (; *timeoutp >= 0;) {
- r = poll(&pfd, 1, *timeoutp);
- oerrno = errno;
- ms_subtract_diff(&t_start, timeoutp);
- errno = oerrno;
- if (r > 0)
- return 0;
- else if (r == -1 && errno != EAGAIN)
- return -1;
- else if (r == 0)
- break;
- }
- /* timeout */
- errno = ETIMEDOUT;
- return -1;
-}
-
-static int
-timeout_connect(int sockfd, const struct sockaddr *serv_addr,
- socklen_t addrlen, int *timeoutp)
-{
- int optval = 0;
- socklen_t optlen = sizeof(optval);
-
- /* No timeout: just do a blocking connect() */
- if (*timeoutp <= 0)
- return connect(sockfd, serv_addr, addrlen);
-
- set_nonblock(sockfd);
- if (connect(sockfd, serv_addr, addrlen) == 0) {
- /* Succeeded already? */
- unset_nonblock(sockfd);
- return 0;
- } else if (errno != EINPROGRESS)
- return -1;
-
- if (waitrfd(sockfd, timeoutp) == -1)
- return -1;
-
- /* Completed or failed */
- if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, &optlen) == -1) {
- debug("getsockopt: %s", strerror(errno));
- return -1;
- }
- if (optval != 0) {
- errno = optval;
- return -1;
- }
- unset_nonblock(sockfd);
- return 0;
-}
-
-/*
* Opens a TCP/IP connection to the remote server on the given host.
* The address of the remote host will be returned in hostaddr.
* If port is 0, the default port will be used.
@@ -500,7 +457,7 @@ ssh_connect_direct(struct ssh *ssh, 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 on = 1;
+ int on = 1, saved_timeout_ms = *timeout_ms;
int oerrno, sock = -1, attempt;
char ntop[NI_MAXHOST], strport[NI_MAXSERV];
struct addrinfo *ai;
@@ -544,6 +501,7 @@ ssh_connect_direct(struct ssh *ssh, const char *host, struct addrinfo *aitop,
continue;
}
+ *timeout_ms = saved_timeout_ms;
if (timeout_connect(sock, ai->ai_addr, ai->ai_addrlen,
timeout_ms) >= 0) {
/* Successful connection. */
@@ -589,12 +547,20 @@ ssh_connect(struct ssh *ssh, 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 in, out;
+
if (options.proxy_command == NULL) {
return ssh_connect_direct(ssh, host, addrs, hostaddr, port,
family, connection_attempts, timeout_ms, want_keepalive);
} else if (strcmp(options.proxy_command, "-") == 0) {
- if ((ssh_packet_set_connection(ssh,
- STDIN_FILENO, STDOUT_FILENO)) == NULL)
+ if ((in = dup(STDIN_FILENO)) < 0 ||
+ (out = dup(STDOUT_FILENO)) < 0) {
+ if (in >= 0)
+ close(in);
+ error("%s: dup() in/out failed", __func__);
+ return -1; /* ssh_packet_set_connection logs error */
+ }
+ if ((ssh_packet_set_connection(ssh, in, out)) == NULL)
return -1; /* ssh_packet_set_connection logs error */
return 0;
} else if (options.proxy_use_fdpass) {
@@ -604,128 +570,26 @@ ssh_connect(struct ssh *ssh, const char *host, struct addrinfo *addrs,
return ssh_proxy_connect(ssh, host, port, options.proxy_command);
}
-static void
-send_client_banner(int connection_out, int minor1)
-{
- /* Send our own protocol version identification. */
- xasprintf(&client_version_string, "SSH-%d.%d-%.100s\r\n",
- PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2, SSH_VERSION);
- if (atomicio(vwrite, connection_out, client_version_string,
- strlen(client_version_string)) != strlen(client_version_string))
- fatal("write: %.100s", strerror(errno));
- chop(client_version_string);
- debug("Local version string %.100s", client_version_string);
-}
-
-/*
- * Waits for the server identification string, and sends our own
- * identification string.
- */
-void
-ssh_exchange_identification(int timeout_ms)
-{
- char buf[256], remote_version[256]; /* must be same size! */
- int remote_major, remote_minor, mismatch;
- int connection_in = packet_get_connection_in();
- int connection_out = packet_get_connection_out();
- u_int i, n;
- size_t len;
- int rc;
-
- send_client_banner(connection_out, 0);
-
- /* Read other side's version identification. */
- for (n = 0;;) {
- for (i = 0; i < sizeof(buf) - 1; i++) {
- if (timeout_ms > 0) {
- rc = waitrfd(connection_in, &timeout_ms);
- if (rc == -1 && errno == ETIMEDOUT) {
- fatal("Connection timed out during "
- "banner exchange");
- } else if (rc == -1) {
- fatal("%s: %s",
- __func__, strerror(errno));
- }
- }
-
- len = atomicio(read, connection_in, &buf[i], 1);
- if (len != 1 && errno == EPIPE)
- fatal("ssh_exchange_identification: "
- "Connection closed by remote host");
- else if (len != 1)
- fatal("ssh_exchange_identification: "
- "read: %.100s", strerror(errno));
- if (buf[i] == '\r') {
- buf[i] = '\n';
- buf[i + 1] = 0;
- continue; /**XXX wait for \n */
- }
- if (buf[i] == '\n') {
- buf[i + 1] = 0;
- break;
- }
- if (++n > 65536)
- fatal("ssh_exchange_identification: "
- "No banner received");
- }
- buf[sizeof(buf) - 1] = 0;
- if (strncmp(buf, "SSH-", 4) == 0)
- break;
- debug("ssh_exchange_identification: %s", buf);
- }
- server_version_string = xstrdup(buf);
-
- /*
- * Check that the versions match. In future this might accept
- * several versions and set appropriate flags to handle them.
- */
- if (sscanf(server_version_string, "SSH-%d.%d-%[^\n]\n",
- &remote_major, &remote_minor, remote_version) != 3)
- fatal("Bad remote protocol version identification: '%.100s'", buf);
- debug("Remote protocol version %d.%d, remote software version %.100s",
- remote_major, remote_minor, remote_version);
-
- active_state->compat = compat_datafellows(remote_version);
- mismatch = 0;
-
- switch (remote_major) {
- case 2:
- break;
- case 1:
- if (remote_minor != 99)
- mismatch = 1;
- break;
- default:
- mismatch = 1;
- break;
- }
- if (mismatch)
- fatal("Protocol major versions differ: %d vs. %d",
- PROTOCOL_MAJOR_2, remote_major);
- if ((datafellows & SSH_BUG_RSASIGMD5) != 0)
- logit("Server version \"%.100s\" uses unsafe RSA signature "
- "scheme; disabling use of RSA keys", remote_version);
- chop(server_version_string);
-}
-
/* defaults to 'no' */
static int
-confirm(const char *prompt)
+confirm(const char *prompt, const char *fingerprint)
{
const char *msg, *again = "Please type 'yes' or 'no': ";
+ const char *again_fp = "Please type 'yes', 'no' or the fingerprint: ";
char *p;
int ret = -1;
if (options.batch_mode)
return 0;
- for (msg = prompt;;msg = again) {
+ for (msg = prompt;;msg = fingerprint ? again_fp : again) {
p = read_passphrase(msg, RP_ECHO);
if (p == NULL)
return 0;
p[strcspn(p, "\n")] = '\0';
if (p[0] == '\0' || strcasecmp(p, "no") == 0)
ret = 0;
- else if (strcasecmp(p, "yes") == 0)
+ else if (strcasecmp(p, "yes") == 0 || (fingerprint != NULL &&
+ strcasecmp(p, fingerprint) == 0))
ret = 1;
free(p);
if (ret != -1)
@@ -853,7 +717,7 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
char msg[1024];
const char *type;
const struct hostkey_entry *host_found, *ip_found;
- int len, cancelled_forwarding = 0;
+ int len, cancelled_forwarding = 0, confirmed;
int local = sockaddr_is_local(hostaddr);
int r, want_cert = sshkey_is_cert(host_key), host_ip_differ = 0;
int hostkey_trusted = 0; /* Known or explicitly accepted by user */
@@ -1028,14 +892,15 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
"established%s\n"
"%s key fingerprint is %s.%s%s\n%s"
"Are you sure you want to continue connecting "
- "(yes/no)? ",
+ "(yes/no/[fingerprint])? ",
host, ip, msg1, type, fp,
options.visual_host_key ? "\n" : "",
options.visual_host_key ? ra : "",
msg2);
free(ra);
+ confirmed = confirm(msg, fp);
free(fp);
- if (!confirm(msg))
+ if (!confirmed)
goto fail;
hostkey_trusted = 1; /* user explicitly confirmed */
}
@@ -1229,7 +1094,7 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
SSH_STRICT_HOSTKEY_ASK) {
strlcat(msg, "\nAre you sure you want "
"to continue connecting (yes/no)? ", sizeof(msg));
- if (!confirm(msg))
+ if (!confirm(msg, NULL))
goto fail;
} else if (options.strict_host_key_checking !=
SSH_STRICT_HOSTKEY_OFF) {
@@ -1401,7 +1266,7 @@ out:
* This function does not require super-user privileges.
*/
void
-ssh_login(Sensitive *sensitive, const char *orighost,
+ssh_login(struct ssh *ssh, Sensitive *sensitive, const char *orighost,
struct sockaddr *hostaddr, u_short port, struct passwd *pw, int timeout_ms)
{
char *host;
@@ -1415,37 +1280,20 @@ ssh_login(Sensitive *sensitive, const char *orighost,
lowercase(host);
/* Exchange protocol version identification strings with the server. */
- ssh_exchange_identification(timeout_ms);
+ if (kex_exchange_identification(ssh, timeout_ms, NULL) != 0)
+ cleanup_exit(255); /* error already logged */
/* Put the connection into non-blocking mode. */
- packet_set_nonblocking();
+ ssh_packet_set_nonblocking(ssh);
/* key exchange */
/* authenticate user */
debug("Authenticating to %s:%d as '%s'", host, port, server_user);
- ssh_kex2(host, hostaddr, port);
- ssh_userauth2(local_user, server_user, host, sensitive);
+ ssh_kex2(ssh, host, hostaddr, port);
+ ssh_userauth2(ssh, local_user, server_user, host, sensitive);
free(local_user);
}
-void
-ssh_put_password(char *password)
-{
- int size;
- char *padded;
-
- if (datafellows & SSH_BUG_PASSWORDPAD) {
- packet_put_cstring(password);
- return;
- }
- size = ROUNDUP(strlen(password) + 1, 32);
- padded = xcalloc(1, size);
- strlcpy(padded, password, size);
- packet_put_string(padded, size);
- 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, struct sshkey *key)