diff options
Diffstat (limited to 'sshconnect.c')
-rw-r--r-- | sshconnect.c | 339 |
1 files changed, 138 insertions, 201 deletions
diff --git a/sshconnect.c b/sshconnect.c index 948b638ad114..dc7a704d2a2d 100644 --- a/sshconnect.c +++ b/sshconnect.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshconnect.c,v 1.273 2017/03/10 03:22:40 dtucker Exp $ */ +/* $OpenBSD: sshconnect.c,v 1.287 2017/09/14 04:32:21 djm Exp $ */ /* * Author: Tatu Ylonen <ylo@cs.hut.fi> * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland @@ -34,6 +34,9 @@ #include <paths.h> #endif #include <pwd.h> +#ifdef HAVE_POLL_H +#include <poll.h> +#endif #include <signal.h> #include <stdarg.h> #include <stdio.h> @@ -45,7 +48,6 @@ #include "key.h" #include "hostfile.h" #include "ssh.h" -#include "rsa.h" #include "buffer.h" #include "packet.h" #include "uidswap.h" @@ -67,7 +69,7 @@ char *client_version_string = NULL; char *server_version_string = NULL; -Key *previous_host_key = NULL; +struct sshkey *previous_host_key = NULL; static int matching_host_key_dns = 0; @@ -79,8 +81,8 @@ extern char *__progname; extern uid_t original_real_uid; extern uid_t original_effective_uid; -static int show_other_keys(struct hostkeys *, Key *); -static void warn_changed_key(Key *); +static int show_other_keys(struct hostkeys *, struct sshkey *); +static void warn_changed_key(struct sshkey *); /* Expand a proxy command */ static char * @@ -102,7 +104,7 @@ expand_proxy_command(const char *proxy_command, const char *user, * a connected fd back to us. */ static int -ssh_proxy_fdpass_connect(const char *host, u_short port, +ssh_proxy_fdpass_connect(struct ssh *ssh, const char *host, u_short port, const char *proxy_command) { char *command_string; @@ -173,7 +175,8 @@ ssh_proxy_fdpass_connect(const char *host, u_short port, fatal("Couldn't wait for child: %s", strerror(errno)); /* Set the connection file descriptors. */ - packet_set_connection(sock, sock); + if (ssh_packet_set_connection(ssh, sock, sock) == NULL) + return -1; /* ssh_packet_set_connection logs error */ return 0; } @@ -182,7 +185,8 @@ ssh_proxy_fdpass_connect(const char *host, u_short port, * 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) +ssh_proxy_connect(struct ssh *ssh, const char *host, u_short port, + const char *proxy_command) { char *command_string; int pin[2], pout[2]; @@ -249,9 +253,9 @@ ssh_proxy_connect(const char *host, u_short port, const char *proxy_command) free(command_string); /* Set the connection file descriptors. */ - packet_set_connection(pout[0], pin[1]); + if (ssh_packet_set_connection(ssh, pout[0], pin[1]) == NULL) + return -1; /* ssh_packet_set_connection logs error */ - /* Indicate OK return */ return 0; } @@ -328,87 +332,71 @@ ssh_create_socket(int privileged, struct addrinfo *ai) return sock; } +/* + * 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 -timeout_connect(int sockfd, const struct sockaddr *serv_addr, - socklen_t addrlen, int *timeoutp) +waitrfd(int fd, int *timeoutp) { - fd_set *fdset; - struct timeval tv, t_start; - socklen_t optlen; - int optval, rc, result = -1; + struct pollfd pfd; + struct timeval t_start; + int oerrno, r; gettimeofday(&t_start, NULL); - - if (*timeoutp <= 0) { - result = connect(sockfd, serv_addr, addrlen); - goto done; - } - - set_nonblock(sockfd); - rc = connect(sockfd, serv_addr, addrlen); - if (rc == 0) { - unset_nonblock(sockfd); - result = 0; - goto done; - } - if (errno != EINPROGRESS) { - result = -1; - goto done; + 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; +} - fdset = xcalloc(howmany(sockfd + 1, NFDBITS), - sizeof(fd_mask)); - FD_SET(sockfd, fdset); - ms_to_timeval(&tv, *timeoutp); +static int +timeout_connect(int sockfd, const struct sockaddr *serv_addr, + socklen_t addrlen, int *timeoutp) +{ + int optval = 0; + socklen_t optlen = sizeof(optval); - for (;;) { - rc = select(sockfd + 1, NULL, fdset, NULL, &tv); - if (rc != -1 || errno != EINTR) - break; - } + /* No timeout: just do a blocking connect() */ + if (*timeoutp <= 0) + return connect(sockfd, serv_addr, addrlen); - switch (rc) { - case 0: - /* Timed out */ - errno = ETIMEDOUT; - break; - case -1: - /* Select error */ - debug("select: %s", strerror(errno)); - break; - case 1: - /* Completed or failed */ - optval = 0; - optlen = sizeof(optval); - if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, - &optlen) == -1) { - debug("getsockopt: %s", strerror(errno)); - break; - } - if (optval != 0) { - errno = optval; - break; - } - result = 0; + set_nonblock(sockfd); + if (connect(sockfd, serv_addr, addrlen) == 0) { + /* Succeeded already? */ unset_nonblock(sockfd); - break; - default: - /* Should not occur */ - fatal("Bogus return (%d) from select()", rc); - } + return 0; + } else if (errno != EINPROGRESS) + return -1; - free(fdset); + if (waitrfd(sockfd, timeoutp) == -1) + return -1; - done: - if (result == 0 && *timeoutp > 0) { - ms_subtract_diff(&t_start, timeoutp); - if (*timeoutp <= 0) { - errno = ETIMEDOUT; - result = -1; - } + /* Completed or failed */ + if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, &optlen) == -1) { + debug("getsockopt: %s", strerror(errno)); + return -1; } - - return (result); + if (optval != 0) { + errno = optval; + return -1; + } + unset_nonblock(sockfd); + return 0; } /* @@ -423,7 +411,7 @@ timeout_connect(int sockfd, const struct sockaddr *serv_addr, * the daemon. */ static int -ssh_connect_direct(const char *host, struct addrinfo *aitop, +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 needpriv) { @@ -497,40 +485,39 @@ ssh_connect_direct(const char *host, struct addrinfo *aitop, error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); /* Set the connection. */ - packet_set_connection(sock, sock); + if (ssh_packet_set_connection(ssh, sock, sock) == NULL) + return -1; /* ssh_packet_set_connection logs error */ - return 0; + return 0; } int -ssh_connect(const char *host, struct addrinfo *addrs, +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 needpriv) { if (options.proxy_command == NULL) { - return ssh_connect_direct(host, addrs, hostaddr, port, family, - connection_attempts, timeout_ms, want_keepalive, needpriv); + return ssh_connect_direct(ssh, 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 */ + if ((ssh_packet_set_connection(ssh, + STDIN_FILENO, STDOUT_FILENO)) == NULL) + return -1; /* ssh_packet_set_connection logs error */ + return 0; } else if (options.proxy_use_fdpass) { - return ssh_proxy_fdpass_connect(host, port, + return ssh_proxy_fdpass_connect(ssh, host, port, options.proxy_command); } - return ssh_proxy_connect(host, port, options.proxy_command); + 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. */ - if (compat20) { - xasprintf(&client_version_string, "SSH-%d.%d-%.100s\r\n", - PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2, SSH_VERSION); - } else { - xasprintf(&client_version_string, "SSH-%d.%d-%.100s\n", - PROTOCOL_MAJOR_1, minor1, SSH_VERSION); - } + 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)); @@ -549,50 +536,27 @@ ssh_exchange_identification(int timeout_ms) int remote_major, remote_minor, mismatch; int connection_in = packet_get_connection_in(); int connection_out = packet_get_connection_out(); - int minor1 = PROTOCOL_MINOR_1, client_banner_sent = 0; u_int i, n; size_t len; - int fdsetsz, remaining, rc; - struct timeval t_start, t_remaining; - fd_set *fdset; - - fdsetsz = howmany(connection_in + 1, NFDBITS) * sizeof(fd_mask); - fdset = xcalloc(1, fdsetsz); + int rc; - /* - * If we are SSH2-only then we can send the banner immediately and - * save a round-trip. - */ - if (options.protocol == SSH_PROTO_2) { - enable_compat20(); - send_client_banner(connection_out, 0); - client_banner_sent = 1; - } + send_client_banner(connection_out, 0); /* Read other side's version identification. */ - remaining = timeout_ms; for (n = 0;;) { for (i = 0; i < sizeof(buf) - 1; i++) { if (timeout_ms > 0) { - gettimeofday(&t_start, NULL); - ms_to_timeval(&t_remaining, remaining); - FD_SET(connection_in, fdset); - rc = select(connection_in + 1, fdset, NULL, - fdset, &t_remaining); - ms_subtract_diff(&t_start, &remaining); - if (rc == 0 || remaining <= 0) + rc = waitrfd(connection_in, &timeout_ms); + if (rc == -1 && errno == ETIMEDOUT) { fatal("Connection timed out during " "banner exchange"); - if (rc == -1) { - if (errno == EINTR) - continue; - fatal("ssh_exchange_identification: " - "select: %s", strerror(errno)); + } 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"); @@ -618,7 +582,6 @@ ssh_exchange_identification(int timeout_ms) debug("ssh_exchange_identification: %s", buf); } server_version_string = xstrdup(buf); - free(fdset); /* * Check that the versions match. In future this might accept @@ -634,51 +597,25 @@ ssh_exchange_identification(int timeout_ms) mismatch = 0; switch (remote_major) { + case 2: + break; case 1: - if (remote_minor == 99 && - (options.protocol & SSH_PROTO_2) && - !(options.protocol & SSH_PROTO_1_PREFERRED)) { - enable_compat20(); - break; - } - if (!(options.protocol & SSH_PROTO_1)) { + if (remote_minor != 99) mismatch = 1; - break; - } - if (remote_minor < 3) { - fatal("Remote machine has too old SSH software version."); - } else if (remote_minor == 3 || remote_minor == 4) { - /* We speak 1.3, too. */ - enable_compat13(); - minor1 = 3; - if (options.forward_agent) { - logit("Agent forwarding disabled for protocol 1.3"); - options.forward_agent = 0; - } - } break; - case 2: - if (options.protocol & SSH_PROTO_2) { - enable_compat20(); - break; - } - /* FALLTHROUGH */ default: mismatch = 1; break; } if (mismatch) fatal("Protocol major versions differ: %d vs. %d", - (options.protocol & SSH_PROTO_2) ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, - remote_major); + PROTOCOL_MAJOR_2, 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); } @@ -707,7 +644,7 @@ confirm(const char *prompt) } static int -check_host_cert(const char *host, const Key *host_key) +check_host_cert(const char *host, const struct sshkey *host_key) { const char *reason; @@ -805,13 +742,13 @@ get_hostfile_hostname_ipaddr(char *hostname, struct sockaddr *hostaddr, #define ROQUIET 2 static int check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, - Key *host_key, int readonly, + struct sshkey *host_key, int readonly, char **user_hostfiles, u_int num_user_hostfiles, char **system_hostfiles, u_int num_system_hostfiles) { HostStatus host_status; HostStatus ip_status; - Key *raw_key = NULL; + struct sshkey *raw_key = NULL; char *ip = NULL, *host = NULL; char hostline[1000], *hostp, *fp, *ra; char msg[1024]; @@ -819,7 +756,7 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, const struct hostkey_entry *host_found, *ip_found; int len, cancelled_forwarding = 0; int local = sockaddr_is_local(hostaddr); - int r, want_cert = key_is_cert(host_key), host_ip_differ = 0; + int r, want_cert = sshkey_is_cert(host_key), host_ip_differ = 0; int hostkey_trusted = 0; /* Known or explicitly accepted by user */ struct hostkeys *host_hostkeys, *ip_hostkeys; u_int i; @@ -870,8 +807,8 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, retry: /* Reload these as they may have changed on cert->key downgrade */ - want_cert = key_is_cert(host_key); - type = key_type(host_key); + want_cert = sshkey_is_cert(host_key); + type = sshkey_type(host_key); /* * Check if the host key is present in the user's list of known @@ -891,7 +828,7 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, if (host_status == HOST_CHANGED && (ip_status != HOST_CHANGED || (ip_found != NULL && - !key_equal(ip_found->key, host_found->key)))) + !sshkey_equal(ip_found->key, host_found->key)))) host_ip_differ = 1; } else ip_status = host_status; @@ -903,7 +840,9 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, host, type, want_cert ? "certificate" : "key"); debug("Found %s in %s:%lu", want_cert ? "CA key" : "key", host_found->file, host_found->line); - if (want_cert && !check_host_cert(hostname, host_key)) + if (want_cert && + !check_host_cert(options.host_key_alias == NULL ? + hostname : options.host_key_alias, host_key)) goto fail; if (options.check_host_ip && ip_status == HOST_NEW) { if (readonly || want_cert) @@ -947,7 +886,8 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, if (readonly || want_cert) goto fail; /* The host is new. */ - if (options.strict_host_key_checking == 1) { + if (options.strict_host_key_checking == + SSH_STRICT_HOSTKEY_YES) { /* * User has requested strict host key checking. We * will not add the host key automatically. The only @@ -956,7 +896,8 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, error("No %s host key is known for %.200s and you " "have requested strict checking.", type, host); goto fail; - } else if (options.strict_host_key_checking == 2) { + } else if (options.strict_host_key_checking == + SSH_STRICT_HOSTKEY_ASK) { char msg1[1024], msg2[1024]; if (show_other_keys(host_hostkeys, host_key)) @@ -1000,8 +941,8 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, hostkey_trusted = 1; /* user explicitly confirmed */ } /* - * If not in strict mode, add the key automatically to the - * local known_hosts file. + * If in "new" or "off" strict mode, add the key automatically + * to the local known_hosts file. */ if (options.check_host_ip && ip_status == HOST_NEW) { snprintf(hostline, sizeof(hostline), "%s,%s", host, ip); @@ -1043,7 +984,8 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, * If strict host key checking is in use, the user will have * to edit the key manually and we can only abort. */ - if (options.strict_host_key_checking) { + if (options.strict_host_key_checking != + SSH_STRICT_HOSTKEY_OFF) { error("%s host key for %.200s was revoked and you have " "requested strict checking.", type, host); goto fail; @@ -1088,14 +1030,16 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, warn_changed_key(host_key); error("Add correct host key in %.100s to get rid of this message.", user_hostfiles[0]); - error("Offending %s key in %s:%lu", key_type(host_found->key), + error("Offending %s key in %s:%lu", + sshkey_type(host_found->key), host_found->file, host_found->line); /* * If strict host key checking is in use, the user will have * to edit the key manually and we can only abort. */ - if (options.strict_host_key_checking) { + if (options.strict_host_key_checking != + SSH_STRICT_HOSTKEY_OFF) { error("%s host key for %.200s has changed and you have " "requested strict checking.", type, host); goto fail; @@ -1182,15 +1126,17 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, "\nMatching host key in %s:%lu", host_found->file, host_found->line); } - if (options.strict_host_key_checking == 1) { - logit("%s", msg); - error("Exiting, you have requested strict checking."); - goto fail; - } else if (options.strict_host_key_checking == 2) { + if (options.strict_host_key_checking == + SSH_STRICT_HOSTKEY_ASK) { strlcat(msg, "\nAre you sure you want " "to continue connecting (yes/no)? ", sizeof(msg)); if (!confirm(msg)) goto fail; + } else if (options.strict_host_key_checking != + SSH_STRICT_HOSTKEY_OFF) { + logit("%s", msg); + error("Exiting, you have requested strict checking."); + goto fail; } else { logit("%s", msg); } @@ -1217,14 +1163,16 @@ fail: * search normally. */ debug("No matching CA found. Retry with plain key"); - raw_key = key_from_private(host_key); - if (key_drop_cert(raw_key) != 0) - fatal("Couldn't drop certificate"); + if ((r = sshkey_from_private(host_key, &raw_key)) != 0) + fatal("%s: sshkey_from_private: %s", + __func__, ssh_err(r)); + if ((r = sshkey_drop_cert(raw_key)) != 0) + fatal("Couldn't drop certificate: %s", ssh_err(r)); host_key = raw_key; goto retry; } if (raw_key != NULL) - key_free(raw_key); + sshkey_free(raw_key); free(ip); free(host); if (host_hostkeys != NULL) @@ -1236,7 +1184,7 @@ fail: /* returns 0 if key verifies or -1 if key does NOT verify */ int -verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) +verify_host_key(char *host, struct sockaddr *hostaddr, struct sshkey *host_key) { u_int i; int r = -1, flags = 0; @@ -1272,8 +1220,7 @@ verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) host_key->cert->principals[i]); } } else { - debug("Server host key: %s %s", compat20 ? - sshkey_ssh_name(host_key) : sshkey_type(host_key), fp); + debug("Server host key: %s %s", sshkey_ssh_name(host_key), fp); } if (sshkey_equal(previous_host_key, host_key)) { @@ -1341,8 +1288,8 @@ out: free(fp); free(cafp); if (r == 0 && host_key != NULL) { - key_free(previous_host_key); - previous_host_key = key_from_private(host_key); + sshkey_free(previous_host_key); + r = sshkey_from_private(host_key, &previous_host_key); } return r; @@ -1378,17 +1325,8 @@ ssh_login(Sensitive *sensitive, const char *orighost, /* key exchange */ /* authenticate user */ debug("Authenticating to %s:%d as '%s'", host, port, server_user); - if (compat20) { - ssh_kex2(host, hostaddr, port); - ssh_userauth2(local_user, server_user, host, sensitive); - } else { -#ifdef WITH_SSH1 - ssh_kex(host, hostaddr); - ssh_userauth1(local_user, server_user, host, sensitive); -#else - fatal("ssh1 is not supported"); -#endif - } + ssh_kex2(host, hostaddr, port); + ssh_userauth2(local_user, server_user, host, sensitive); free(local_user); } @@ -1412,10 +1350,9 @@ ssh_put_password(char *password) /* 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) +show_other_keys(struct hostkeys *hostkeys, struct sshkey *key) { int type[] = { - KEY_RSA1, KEY_RSA, KEY_DSA, KEY_ECDSA, @@ -1453,7 +1390,7 @@ show_other_keys(struct hostkeys *hostkeys, Key *key) } static void -warn_changed_key(Key *host_key) +warn_changed_key(struct sshkey *host_key) { char *fp; @@ -1516,7 +1453,7 @@ ssh_local_cmd(const char *args) } void -maybe_add_key_to_agent(char *authfile, Key *private, char *comment, +maybe_add_key_to_agent(char *authfile, struct sshkey *private, char *comment, char *passphrase) { int auth_sock = -1, r; |