aboutsummaryrefslogtreecommitdiff
path: root/sshconnect.c
diff options
context:
space:
mode:
Diffstat (limited to 'sshconnect.c')
-rw-r--r--sshconnect.c571
1 files changed, 420 insertions, 151 deletions
diff --git a/sshconnect.c b/sshconnect.c
index af08be415..fcf87bb76 100644
--- a/sshconnect.c
+++ b/sshconnect.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sshconnect.c,v 1.329 2020/03/13 04:01:56 djm Exp $ */
+/* $OpenBSD: sshconnect.c,v 1.355 2021/07/02 05:11:21 dtucker Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -30,6 +30,7 @@
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
+#include <limits.h>
#include <netdb.h>
#ifdef HAVE_PATHS_H
#include <paths.h>
@@ -89,11 +90,14 @@ expand_proxy_command(const char *proxy_command, const char *user,
const char *host, const char *host_arg, int port)
{
char *tmp, *ret, strport[NI_MAXSERV];
+ const char *keyalias = options.host_key_alias ?
+ options.host_key_alias : host_arg;
snprintf(strport, sizeof strport, "%d", port);
xasprintf(&tmp, "exec %s", proxy_command);
ret = percent_expand(tmp,
"h", host,
+ "k", keyalias,
"n", host_arg,
"p", strport,
"r", options.user,
@@ -102,24 +106,6 @@ 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.
@@ -166,8 +152,8 @@ ssh_proxy_fdpass_connect(struct ssh *ssh, const char *host,
* error messages may be printed on the user's terminal.
*/
if (!debug_flag && options.control_path != NULL &&
- options.control_persist)
- stderr_null();
+ options.control_persist && stdfd_devnull(0, 0, 1) == -1)
+ error_f("stdfd_devnull failed");
argv[0] = shell;
argv[1] = "-c";
@@ -249,16 +235,18 @@ ssh_proxy_connect(struct ssh *ssh, const char *host, const char *host_arg,
* error messages may be printed on the user's terminal.
*/
if (!debug_flag && options.control_path != NULL &&
- options.control_persist)
- stderr_null();
+ options.control_persist && stdfd_devnull(0, 0, 1) == -1)
+ error_f("stdfd_devnull failed");
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. */
+ /*
+ * Execute the proxy command. Note that we gave up any
+ * extra privileges above.
+ */
ssh_signal(SIGPIPE, SIG_DFL);
execv(argv[0], argv);
perror(argv[0]);
@@ -329,8 +317,7 @@ check_ifaddrs(const char *ifname, int af, const struct ifaddrs *ifaddrs,
htonl(INADDR_LOOPBACK))
continue;
if (*rlenp < sizeof(struct sockaddr_in)) {
- error("%s: v4 addr doesn't fit",
- __func__);
+ error_f("v4 addr doesn't fit");
return -1;
}
*rlenp = sizeof(struct sockaddr_in);
@@ -344,8 +331,7 @@ check_ifaddrs(const char *ifname, int af, const struct ifaddrs *ifaddrs,
IN6_IS_ADDR_LOOPBACK(v6addr)))
continue;
if (*rlenp < sizeof(struct sockaddr_in6)) {
- error("%s: v6 addr doesn't fit",
- __func__);
+ error_f("v6 addr doesn't fit");
return -1;
}
*rlenp = sizeof(struct sockaddr_in6);
@@ -380,6 +366,10 @@ ssh_create_socket(struct addrinfo *ai)
}
fcntl(sock, F_SETFD, FD_CLOEXEC);
+ /* Use interactive QOS (if specified) until authentication completed */
+ if (options.ip_qos_interactive != INT_MAX)
+ set_sock_tos(sock, options.ip_qos_interactive);
+
/* Bind the socket to an alternative local IP address */
if (options.bind_address == NULL && options.bind_interface == NULL)
return sock;
@@ -406,14 +396,14 @@ ssh_create_socket(struct addrinfo *ai)
#ifdef HAVE_IFADDRS_H
if ((r = getifaddrs(&ifaddrs)) != 0) {
error("getifaddrs: %s: %s", options.bind_interface,
- strerror(errno));
+ strerror(errno));
goto fail;
}
bindaddrlen = sizeof(bindaddr);
if (check_ifaddrs(options.bind_interface, ai->ai_family,
ifaddrs, &bindaddr, &bindaddrlen) != 0) {
logit("getifaddrs: %s: no suitable addresses",
- options.bind_interface);
+ options.bind_interface);
goto fail;
}
#else
@@ -422,15 +412,14 @@ ssh_create_socket(struct addrinfo *ai)
}
if ((r = getnameinfo((struct sockaddr *)&bindaddr, bindaddrlen,
ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST)) != 0) {
- error("%s: getnameinfo failed: %s", __func__,
- ssh_gai_strerror(r));
+ error_f("getnameinfo failed: %s", ssh_gai_strerror(r));
goto fail;
}
if (bind(sock, (struct sockaddr *)&bindaddr, bindaddrlen) != 0) {
error("bind %s: %s", ntop, strerror(errno));
goto fail;
}
- debug("%s: bound to %s", __func__, ntop);
+ debug_f("bound to %s", ntop);
/* success */
goto out;
fail:
@@ -457,15 +446,15 @@ fail:
*/
static int
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)
+ struct sockaddr_storage *hostaddr, u_short port, int connection_attempts,
+ int *timeout_ms, int want_keepalive)
{
int on = 1, saved_timeout_ms = *timeout_ms;
int oerrno, sock = -1, attempt;
char ntop[NI_MAXHOST], strport[NI_MAXSERV];
struct addrinfo *ai;
- debug2("%s", __func__);
+ debug3_f("entering");
memset(ntop, 0, sizeof(ntop));
memset(strport, 0, sizeof(strport));
@@ -489,7 +478,7 @@ ssh_connect_direct(struct ssh *ssh, const char *host, struct addrinfo *aitop,
ntop, sizeof(ntop), strport, sizeof(strport),
NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
oerrno = errno;
- error("%s: getnameinfo failed", __func__);
+ error_f("getnameinfo failed");
errno = oerrno;
continue;
}
@@ -548,19 +537,19 @@ ssh_connect_direct(struct ssh *ssh, const char *host, struct addrinfo *aitop,
int
ssh_connect(struct ssh *ssh, const char *host, const char *host_arg,
struct addrinfo *addrs, struct sockaddr_storage *hostaddr, u_short port,
- int family, int connection_attempts, int *timeout_ms, int want_keepalive)
+ 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);
+ connection_attempts, timeout_ms, want_keepalive);
} else if (strcmp(options.proxy_command, "-") == 0) {
if ((in = dup(STDIN_FILENO)) == -1 ||
(out = dup(STDOUT_FILENO)) == -1) {
if (in >= 0)
close(in);
- error("%s: dup() in/out failed", __func__);
+ error_f("dup() in/out failed");
return -1; /* ssh_packet_set_connection logs error */
}
if ((ssh_packet_set_connection(ssh, in, out)) == NULL)
@@ -594,7 +583,7 @@ confirm(const char *prompt, const char *fingerprint)
if (p[0] == '\0' || strcasecmp(p, "no") == 0)
ret = 0;
else if (strcasecmp(p, "yes") == 0 || (fingerprint != NULL &&
- strcasecmp(p, fingerprint) == 0))
+ strcmp(p, fingerprint) == 0))
ret = 1;
free(cp);
if (ret != -1)
@@ -603,32 +592,6 @@ confirm(const char *prompt, const char *fingerprint)
}
static int
-check_host_cert(const char *host, const struct sshkey *key)
-{
- const char *reason;
- int r;
-
- if (sshkey_cert_check_authority(key, 1, 0, host, &reason) != 0) {
- error("%s", reason);
- return 0;
- }
- if (sshbuf_len(key->cert->critical) != 0) {
- error("Certificate for %s contains unsupported "
- "critical options(s)", host);
- return 0;
- }
- if ((r = sshkey_check_cert_sigtype(key,
- options.ca_sign_algorithms)) != 0) {
- logit("%s: certificate signature algorithm %s: %s", __func__,
- (key->cert == NULL || key->cert->signature_type == NULL) ?
- "(null)" : key->cert->signature_type, ssh_err(r));
- return 0;
- }
-
- return 1;
-}
-
-static int
sockaddr_is_local(struct sockaddr *hostaddr)
{
switch (hostaddr->sa_family) {
@@ -677,7 +640,7 @@ get_hostfile_hostname_ipaddr(char *hostname, struct sockaddr *hostaddr,
if (options.proxy_command == NULL) {
if (getnameinfo(hostaddr, addrlen,
ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST) != 0)
- fatal("%s: getnameinfo failed", __func__);
+ fatal_f("getnameinfo failed");
*hostfile_ipaddr = put_host_port(ntop, port);
} else {
*hostfile_ipaddr = xstrdup("<no hostip for proxy "
@@ -701,6 +664,257 @@ get_hostfile_hostname_ipaddr(char *hostname, struct sockaddr *hostaddr,
}
}
+/* returns non-zero if path appears in hostfiles, or 0 if not. */
+static int
+path_in_hostfiles(const char *path, char **hostfiles, u_int num_hostfiles)
+{
+ u_int i;
+
+ for (i = 0; i < num_hostfiles; i++) {
+ if (strcmp(path, hostfiles[i]) == 0)
+ return 1;
+ }
+ return 0;
+}
+
+struct find_by_key_ctx {
+ const char *host, *ip;
+ const struct sshkey *key;
+ char **names;
+ u_int nnames;
+};
+
+/* Try to replace home directory prefix (per $HOME) with a ~/ sequence */
+static char *
+try_tilde_unexpand(const char *path)
+{
+ char *home, *ret = NULL;
+ size_t l;
+
+ if (*path != '/')
+ return xstrdup(path);
+ if ((home = getenv("HOME")) == NULL || (l = strlen(home)) == 0)
+ return xstrdup(path);
+ if (strncmp(path, home, l) != 0)
+ return xstrdup(path);
+ /*
+ * ensure we have matched on a path boundary: either the $HOME that
+ * we just compared ends with a '/' or the next character of the path
+ * must be a '/'.
+ */
+ if (home[l - 1] != '/' && path[l] != '/')
+ return xstrdup(path);
+ if (path[l] == '/')
+ l++;
+ xasprintf(&ret, "~/%s", path + l);
+ return ret;
+}
+
+static int
+hostkeys_find_by_key_cb(struct hostkey_foreach_line *l, void *_ctx)
+{
+ struct find_by_key_ctx *ctx = (struct find_by_key_ctx *)_ctx;
+ char *path;
+
+ /* we are looking for keys with names that *do not* match */
+ if ((l->match & HKF_MATCH_HOST) != 0)
+ return 0;
+ /* not interested in marker lines */
+ if (l->marker != MRK_NONE)
+ return 0;
+ /* we are only interested in exact key matches */
+ if (l->key == NULL || !sshkey_equal(ctx->key, l->key))
+ return 0;
+ path = try_tilde_unexpand(l->path);
+ debug_f("found matching key in %s:%lu", path, l->linenum);
+ ctx->names = xrecallocarray(ctx->names,
+ ctx->nnames, ctx->nnames + 1, sizeof(*ctx->names));
+ xasprintf(&ctx->names[ctx->nnames], "%s:%lu: %s", path, l->linenum,
+ strncmp(l->hosts, HASH_MAGIC, strlen(HASH_MAGIC)) == 0 ?
+ "[hashed name]" : l->hosts);
+ ctx->nnames++;
+ free(path);
+ return 0;
+}
+
+static int
+hostkeys_find_by_key_hostfile(const char *file, const char *which,
+ struct find_by_key_ctx *ctx)
+{
+ int r;
+
+ debug3_f("trying %s hostfile \"%s\"", which, file);
+ if ((r = hostkeys_foreach(file, hostkeys_find_by_key_cb, ctx,
+ ctx->host, ctx->ip, HKF_WANT_PARSE_KEY, 0)) != 0) {
+ if (r == SSH_ERR_SYSTEM_ERROR && errno == ENOENT) {
+ debug_f("hostkeys file %s does not exist", file);
+ return 0;
+ }
+ error_fr(r, "hostkeys_foreach failed for %s", file);
+ return r;
+ }
+ return 0;
+}
+
+/*
+ * Find 'key' in known hosts file(s) that do not match host/ip.
+ * Used to display also-known-as information for previously-unseen hostkeys.
+ */
+static void
+hostkeys_find_by_key(const char *host, const char *ip, const struct sshkey *key,
+ char **user_hostfiles, u_int num_user_hostfiles,
+ char **system_hostfiles, u_int num_system_hostfiles,
+ char ***names, u_int *nnames)
+{
+ struct find_by_key_ctx ctx = {0, 0, 0, 0, 0};
+ u_int i;
+
+ *names = NULL;
+ *nnames = 0;
+
+ if (key == NULL || sshkey_is_cert(key))
+ return;
+
+ ctx.host = host;
+ ctx.ip = ip;
+ ctx.key = key;
+
+ for (i = 0; i < num_user_hostfiles; i++) {
+ if (hostkeys_find_by_key_hostfile(user_hostfiles[i],
+ "user", &ctx) != 0)
+ goto fail;
+ }
+ for (i = 0; i < num_system_hostfiles; i++) {
+ if (hostkeys_find_by_key_hostfile(system_hostfiles[i],
+ "system", &ctx) != 0)
+ goto fail;
+ }
+ /* success */
+ *names = ctx.names;
+ *nnames = ctx.nnames;
+ ctx.names = NULL;
+ ctx.nnames = 0;
+ return;
+ fail:
+ for (i = 0; i < ctx.nnames; i++)
+ free(ctx.names[i]);
+ free(ctx.names);
+}
+
+#define MAX_OTHER_NAMES 8 /* Maximum number of names to list */
+static char *
+other_hostkeys_message(const char *host, const char *ip,
+ const struct sshkey *key,
+ char **user_hostfiles, u_int num_user_hostfiles,
+ char **system_hostfiles, u_int num_system_hostfiles)
+{
+ char *ret = NULL, **othernames = NULL;
+ u_int i, n, num_othernames = 0;
+
+ hostkeys_find_by_key(host, ip, key,
+ user_hostfiles, num_user_hostfiles,
+ system_hostfiles, num_system_hostfiles,
+ &othernames, &num_othernames);
+ if (num_othernames == 0)
+ return xstrdup("This key is not known by any other names");
+
+ xasprintf(&ret, "This host key is known by the following other "
+ "names/addresses:");
+
+ n = num_othernames;
+ if (n > MAX_OTHER_NAMES)
+ n = MAX_OTHER_NAMES;
+ for (i = 0; i < n; i++) {
+ xextendf(&ret, "\n", " %s", othernames[i]);
+ }
+ if (n < num_othernames) {
+ xextendf(&ret, "\n", " (%d additional names omitted)",
+ num_othernames - n);
+ }
+ for (i = 0; i < num_othernames; i++)
+ free(othernames[i]);
+ free(othernames);
+ return ret;
+}
+
+void
+load_hostkeys_command(struct hostkeys *hostkeys, const char *command_template,
+ const char *invocation, const struct ssh_conn_info *cinfo,
+ const struct sshkey *host_key, const char *hostfile_hostname)
+{
+ int r, i, ac = 0;
+ char *key_fp = NULL, *keytext = NULL, *tmp;
+ char *command = NULL, *tag = NULL, **av = NULL;
+ FILE *f = NULL;
+ pid_t pid;
+ void (*osigchld)(int);
+
+ xasprintf(&tag, "KnownHostsCommand-%s", invocation);
+
+ if (host_key != NULL) {
+ if ((key_fp = sshkey_fingerprint(host_key,
+ options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
+ fatal_f("sshkey_fingerprint failed");
+ if ((r = sshkey_to_base64(host_key, &keytext)) != 0)
+ fatal_fr(r, "sshkey_to_base64 failed");
+ }
+ /*
+ * NB. all returns later this function should go via "out" to
+ * ensure the original SIGCHLD handler is restored properly.
+ */
+ osigchld = ssh_signal(SIGCHLD, SIG_DFL);
+
+ /* Turn the command into an argument vector */
+ if (argv_split(command_template, &ac, &av, 0) != 0) {
+ error("%s \"%s\" contains invalid quotes", tag,
+ command_template);
+ goto out;
+ }
+ if (ac == 0) {
+ error("%s \"%s\" yielded no arguments", tag,
+ command_template);
+ goto out;
+ }
+ for (i = 1; i < ac; i++) {
+ tmp = percent_dollar_expand(av[i],
+ DEFAULT_CLIENT_PERCENT_EXPAND_ARGS(cinfo),
+ "H", hostfile_hostname,
+ "I", invocation,
+ "t", host_key == NULL ? "NONE" : sshkey_ssh_name(host_key),
+ "f", key_fp == NULL ? "NONE" : key_fp,
+ "K", keytext == NULL ? "NONE" : keytext,
+ (char *)NULL);
+ if (tmp == NULL)
+ fatal_f("percent_expand failed");
+ free(av[i]);
+ av[i] = tmp;
+ }
+ /* Prepare a printable command for logs, etc. */
+ command = argv_assemble(ac, av);
+
+ if ((pid = subprocess(tag, command, ac, av, &f,
+ SSH_SUBPROCESS_STDOUT_CAPTURE|SSH_SUBPROCESS_UNSAFE_PATH|
+ SSH_SUBPROCESS_PRESERVE_ENV, NULL, NULL, NULL)) == 0)
+ goto out;
+
+ load_hostkeys_file(hostkeys, hostfile_hostname, tag, f, 1);
+
+ if (exited_cleanly(pid, tag, command, 0) != 0)
+ fatal("KnownHostsCommand failed");
+
+ out:
+ if (f != NULL)
+ fclose(f);
+ ssh_signal(SIGCHLD, osigchld);
+ for (i = 0; i < ac; i++)
+ free(av[i]);
+ free(av);
+ free(tag);
+ free(command);
+ free(key_fp);
+ free(keytext);
+}
+
/*
* check whether the supplied host key is valid, return -1 if the key
* is not valid. user_hostfile[0] will not be updated if 'readonly' is true.
@@ -709,19 +923,20 @@ get_hostfile_hostname_ipaddr(char *hostname, struct sockaddr *hostaddr,
#define RDONLY 1
#define ROQUIET 2
static int
-check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
- struct sshkey *host_key, int readonly,
+check_host_key(char *hostname, const struct ssh_conn_info *cinfo,
+ struct sockaddr *hostaddr, u_short port,
+ struct sshkey *host_key, int readonly, int clobber_port,
char **user_hostfiles, u_int num_user_hostfiles,
- char **system_hostfiles, u_int num_system_hostfiles)
+ char **system_hostfiles, u_int num_system_hostfiles,
+ const char *hostfile_command)
{
- HostStatus host_status;
- HostStatus ip_status;
+ HostStatus host_status = -1, ip_status = -1;
struct sshkey *raw_key = NULL;
char *ip = NULL, *host = NULL;
char hostline[1000], *hostp, *fp, *ra;
char msg[1024];
- const char *type;
- const struct hostkey_entry *host_found, *ip_found;
+ const char *type, *fail_reason;
+ const struct hostkey_entry *host_found = NULL, *ip_found = NULL;
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;
@@ -741,6 +956,7 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
options.host_key_alias == NULL) {
debug("Forcing accepting of host key for "
"loopback/localhost.");
+ options.update_hostkeys = 0;
return 0;
}
@@ -748,7 +964,8 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
* Prepare the hostname and address strings used for hostkey lookup.
* In some cases, these will have a port number appended.
*/
- get_hostfile_hostname_ipaddr(hostname, hostaddr, port, &host, &ip);
+ get_hostfile_hostname_ipaddr(hostname, hostaddr,
+ clobber_port ? 0 : port, &host, &ip);
/*
* Turn off check_host_ip if the connection is to localhost, via proxy
@@ -760,17 +977,25 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
host_hostkeys = init_hostkeys();
for (i = 0; i < num_user_hostfiles; i++)
- load_hostkeys(host_hostkeys, host, user_hostfiles[i]);
+ load_hostkeys(host_hostkeys, host, user_hostfiles[i], 0);
for (i = 0; i < num_system_hostfiles; i++)
- load_hostkeys(host_hostkeys, host, system_hostfiles[i]);
+ load_hostkeys(host_hostkeys, host, system_hostfiles[i], 0);
+ if (hostfile_command != NULL && !clobber_port) {
+ load_hostkeys_command(host_hostkeys, hostfile_command,
+ "HOSTNAME", cinfo, host_key, host);
+ }
ip_hostkeys = NULL;
if (!want_cert && options.check_host_ip) {
ip_hostkeys = init_hostkeys();
for (i = 0; i < num_user_hostfiles; i++)
- load_hostkeys(ip_hostkeys, ip, user_hostfiles[i]);
+ load_hostkeys(ip_hostkeys, ip, user_hostfiles[i], 0);
for (i = 0; i < num_system_hostfiles; i++)
- load_hostkeys(ip_hostkeys, ip, system_hostfiles[i]);
+ load_hostkeys(ip_hostkeys, ip, system_hostfiles[i], 0);
+ if (hostfile_command != NULL && !clobber_port) {
+ load_hostkeys_command(ip_hostkeys, hostfile_command,
+ "ADDRESS", cinfo, host_key, ip);
+ }
}
retry:
@@ -786,6 +1011,14 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
&host_found);
/*
+ * If there are no hostfiles, or if the hostkey was found via
+ * KnownHostsCommand, then don't try to touch the disk.
+ */
+ if (!readonly && (num_user_hostfiles == 0 ||
+ (host_found != NULL && host_found->note != 0)))
+ readonly = RDONLY;
+
+ /*
* Also perform check for the ip address, skip the check if we are
* localhost, looking for a certificate, or the hostname was an ip
* address to begin with.
@@ -808,10 +1041,40 @@ 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(options.host_key_alias == NULL ?
- hostname : options.host_key_alias, host_key))
- goto fail;
+ if (want_cert) {
+ if (sshkey_cert_check_host(host_key,
+ options.host_key_alias == NULL ?
+ hostname : options.host_key_alias, 0,
+ options.ca_sign_algorithms, &fail_reason) != 0) {
+ error("%s", fail_reason);
+ goto fail;
+ }
+ /*
+ * Do not attempt hostkey update if a certificate was
+ * successfully matched.
+ */
+ if (options.update_hostkeys != 0) {
+ options.update_hostkeys = 0;
+ debug3_f("certificate host key in use; "
+ "disabling UpdateHostkeys");
+ }
+ }
+ /* Turn off UpdateHostkeys if key was in system known_hosts */
+ if (options.update_hostkeys != 0 &&
+ (path_in_hostfiles(host_found->file,
+ system_hostfiles, num_system_hostfiles) ||
+ (ip_status == HOST_OK && ip_found != NULL &&
+ path_in_hostfiles(ip_found->file,
+ system_hostfiles, num_system_hostfiles)))) {
+ options.update_hostkeys = 0;
+ debug3_f("host key found in GlobalKnownHostsFile; "
+ "disabling UpdateHostkeys");
+ }
+ if (options.update_hostkeys != 0 && host_found->note) {
+ options.update_hostkeys = 0;
+ debug3_f("host key found via KnownHostsCommand; "
+ "disabling UpdateHostkeys");
+ }
if (options.check_host_ip && ip_status == HOST_NEW) {
if (readonly || want_cert)
logit("%s host key for IP address "
@@ -833,7 +1096,7 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
ra = sshkey_fingerprint(host_key,
options.fingerprint_hash, SSH_FP_RANDOMART);
if (fp == NULL || ra == NULL)
- fatal("%s: sshkey_fingerprint fail", __func__);
+ fatal_f("sshkey_fingerprint failed");
logit("Host key fingerprint is %s\n%s", fp, ra);
free(ra);
free(fp);
@@ -842,11 +1105,13 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
break;
case HOST_NEW:
if (options.host_key_alias == NULL && port != 0 &&
- port != SSH_DEFAULT_PORT) {
+ port != SSH_DEFAULT_PORT && !clobber_port) {
debug("checking without port identifier");
- if (check_host_key(hostname, hostaddr, 0, host_key,
- ROQUIET, user_hostfiles, num_user_hostfiles,
- system_hostfiles, num_system_hostfiles) == 0) {
+ if (check_host_key(hostname, cinfo, hostaddr, 0,
+ host_key, ROQUIET, 1,
+ user_hostfiles, num_user_hostfiles,
+ system_hostfiles, num_system_hostfiles,
+ hostfile_command) == 0) {
debug("found matching key w/out port");
break;
}
@@ -866,45 +1131,48 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
goto fail;
} else if (options.strict_host_key_checking ==
SSH_STRICT_HOSTKEY_ASK) {
- char msg1[1024], msg2[1024];
+ char *msg1 = NULL, *msg2 = NULL;
+
+ xasprintf(&msg1, "The authenticity of host "
+ "'%.200s (%s)' can't be established", host, ip);
+
+ if (show_other_keys(host_hostkeys, host_key)) {
+ xextendf(&msg1, "\n", "but keys of different "
+ "type are already known for this host.");
+ } else
+ xextendf(&msg1, "", ".");
- if (show_other_keys(host_hostkeys, host_key))
- snprintf(msg1, sizeof(msg1),
- "\nbut keys of different type are already"
- " known for this host.");
- else
- snprintf(msg1, sizeof(msg1), ".");
- /* The default */
fp = sshkey_fingerprint(host_key,
options.fingerprint_hash, SSH_FP_DEFAULT);
ra = sshkey_fingerprint(host_key,
options.fingerprint_hash, SSH_FP_RANDOMART);
if (fp == NULL || ra == NULL)
- fatal("%s: sshkey_fingerprint fail", __func__);
- msg2[0] = '\0';
+ fatal_f("sshkey_fingerprint failed");
+ xextendf(&msg1, "\n", "%s key fingerprint is %s.",
+ type, fp);
+ if (options.visual_host_key)
+ xextendf(&msg1, "\n", "%s", ra);
if (options.verify_host_key_dns) {
- if (matching_host_key_dns)
- snprintf(msg2, sizeof(msg2),
- "Matching host key fingerprint"
- " found in DNS.\n");
- else
- snprintf(msg2, sizeof(msg2),
- "No matching host key fingerprint"
- " found in DNS.\n");
+ xextendf(&msg1, "\n",
+ "%s host key fingerprint found in DNS.",
+ matching_host_key_dns ?
+ "Matching" : "No matching");
}
- snprintf(msg, sizeof(msg),
- "The authenticity of host '%.200s (%s)' can't be "
- "established%s\n"
- "%s key fingerprint is %s.%s%s\n%s"
+ /* msg2 informs for other names matching this key */
+ if ((msg2 = other_hostkeys_message(host, ip, host_key,
+ user_hostfiles, num_user_hostfiles,
+ system_hostfiles, num_system_hostfiles)) != NULL)
+ xextendf(&msg1, "\n", "%s", msg2);
+
+ xextendf(&msg1, "\n",
"Are you sure you want to continue connecting "
- "(yes/no/[fingerprint])? ",
- host, ip, msg1, type, fp,
- options.visual_host_key ? "\n" : "",
- options.visual_host_key ? ra : "",
- msg2);
+ "(yes/no/[fingerprint])? ");
+
+ confirmed = confirm(msg1, fp);
free(ra);
- confirmed = confirm(msg, fp);
free(fp);
+ free(msg1);
+ free(msg2);
if (!confirmed)
goto fail;
hostkey_trusted = 1; /* user explicitly confirmed */
@@ -1009,8 +1277,8 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
*/
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);
+ error("Host key for %.200s has changed and you have "
+ "requested strict checking.", host);
goto fail;
}
@@ -1030,13 +1298,6 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
error("Keyboard-interactive authentication is disabled"
" to avoid man-in-the-middle attacks.");
options.kbd_interactive_authentication = 0;
- options.challenge_response_authentication = 0;
- cancelled_forwarding = 1;
- }
- if (options.challenge_response_authentication) {
- error("Challenge/response authentication is disabled"
- " to avoid man-in-the-middle attacks.");
- options.challenge_response_authentication = 0;
cancelled_forwarding = 1;
}
if (options.forward_agent) {
@@ -1065,6 +1326,11 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
options.tun_open = SSH_TUNMODE_NO;
cancelled_forwarding = 1;
}
+ if (options.update_hostkeys != 0) {
+ error("UpdateHostkeys is disabled because the host "
+ "key is not trusted.");
+ options.update_hostkeys = 0;
+ }
if (options.exit_on_forward_failure && cancelled_forwarding)
fatal("Error: forwarding disabled due to host key "
"check failure");
@@ -1073,7 +1339,7 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
* XXX Should permit the user to change to use the new id.
* This could be done by converting the host key to an
* identifying sentence, tell that the host identifies itself
- * by that sentence, and ask the user if he/she wishes to
+ * by that sentence, and ask the user if they wish to
* accept the authentication.
*/
break;
@@ -1112,8 +1378,8 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
}
if (!hostkey_trusted && options.update_hostkeys) {
- debug("%s: hostkey not known or explicitly trusted: "
- "disabling UpdateHostkeys", __func__);
+ debug_f("hostkey not known or explicitly trusted: "
+ "disabling UpdateHostkeys");
options.update_hostkeys = 0;
}
@@ -1133,10 +1399,9 @@ fail:
*/
debug("No matching CA found. Retry with plain key");
if ((r = sshkey_from_private(host_key, &raw_key)) != 0)
- fatal("%s: sshkey_from_private: %s",
- __func__, ssh_err(r));
+ fatal_fr(r, "decode key");
if ((r = sshkey_drop_cert(raw_key)) != 0)
- fatal("Couldn't drop certificate: %s", ssh_err(r));
+ fatal_r(r, "Couldn't drop certificate");
host_key = raw_key;
goto retry;
}
@@ -1152,7 +1417,8 @@ fail:
/* returns 0 if key verifies or -1 if key does NOT verify */
int
-verify_host_key(char *host, struct sockaddr *hostaddr, struct sshkey *host_key)
+verify_host_key(char *host, struct sockaddr *hostaddr, struct sshkey *host_key,
+ const struct ssh_conn_info *cinfo)
{
u_int i;
int r = -1, flags = 0;
@@ -1161,7 +1427,7 @@ verify_host_key(char *host, struct sockaddr *hostaddr, struct sshkey *host_key)
if ((fp = sshkey_fingerprint(host_key,
options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) {
- error("%s: fingerprint host key: %s", __func__, ssh_err(r));
+ error_fr(r, "fingerprint host key");
r = -1;
goto out;
}
@@ -1169,8 +1435,7 @@ verify_host_key(char *host, struct sockaddr *hostaddr, struct sshkey *host_key)
if (sshkey_is_cert(host_key)) {
if ((cafp = sshkey_fingerprint(host_key->cert->signature_key,
options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) {
- error("%s: fingerprint CA key: %s",
- __func__, ssh_err(r));
+ error_fr(r, "fingerprint CA key");
r = -1;
goto out;
}
@@ -1192,8 +1457,8 @@ verify_host_key(char *host, struct sockaddr *hostaddr, struct sshkey *host_key)
}
if (sshkey_equal(previous_host_key, host_key)) {
- debug2("%s: server host key %s %s matches cached key",
- __func__, sshkey_type(host_key), fp);
+ debug2_f("server host key %s %s matches cached key",
+ sshkey_type(host_key), fp);
r = 0;
goto out;
}
@@ -1211,9 +1476,9 @@ verify_host_key(char *host, struct sockaddr *hostaddr, struct sshkey *host_key)
r = -1;
goto out;
default:
- error("Error checking host key %s %s in "
- "revoked keys file %s: %s", sshkey_type(host_key),
- fp, options.revoked_host_keys, ssh_err(r));
+ error_r(r, "Error checking host key %s %s in "
+ "revoked keys file %s", sshkey_type(host_key),
+ fp, options.revoked_host_keys);
r = -1;
goto out;
}
@@ -1247,9 +1512,10 @@ verify_host_key(char *host, struct sockaddr *hostaddr, struct sshkey *host_key)
}
}
}
- r = check_host_key(host, hostaddr, options.port, host_key, RDRW,
- options.user_hostfiles, options.num_user_hostfiles,
- options.system_hostfiles, options.num_system_hostfiles);
+ r = check_host_key(host, cinfo, hostaddr, options.port, host_key,
+ RDRW, 0, options.user_hostfiles, options.num_user_hostfiles,
+ options.system_hostfiles, options.num_system_hostfiles,
+ options.known_hosts_command);
out:
sshkey_free(plain);
@@ -1272,7 +1538,8 @@ out:
*/
void
ssh_login(struct ssh *ssh, Sensitive *sensitive, const char *orighost,
- struct sockaddr *hostaddr, u_short port, struct passwd *pw, int timeout_ms)
+ struct sockaddr *hostaddr, u_short port, struct passwd *pw, int timeout_ms,
+ const struct ssh_conn_info *cinfo)
{
char *host;
char *server_user, *local_user;
@@ -1295,7 +1562,7 @@ ssh_login(struct ssh *ssh, Sensitive *sensitive, const char *orighost,
/* key exchange */
/* authenticate user */
debug("Authenticating to %s:%d as '%s'", host, port, server_user);
- ssh_kex2(ssh, host, hostaddr, port);
+ ssh_kex2(ssh, host, hostaddr, port, cinfo);
ssh_userauth2(ssh, local_user, server_user, host, sensitive);
free(local_user);
free(host);
@@ -1320,14 +1587,15 @@ show_other_keys(struct hostkeys *hostkeys, struct sshkey *key)
for (i = 0; type[i] != -1; i++) {
if (type[i] == key->type)
continue;
- if (!lookup_key_in_hostkeys_by_type(hostkeys, type[i], &found))
+ if (!lookup_key_in_hostkeys_by_type(hostkeys, type[i],
+ -1, &found))
continue;
fp = sshkey_fingerprint(found->key,
options.fingerprint_hash, SSH_FP_DEFAULT);
ra = sshkey_fingerprint(found->key,
options.fingerprint_hash, SSH_FP_RANDOMART);
if (fp == NULL || ra == NULL)
- fatal("%s: sshkey_fingerprint fail", __func__);
+ fatal_f("sshkey_fingerprint fail");
logit("WARNING: %s key found for host %s\n"
"in %s:%lu\n"
"%s key fingerprint %s.",
@@ -1351,7 +1619,7 @@ warn_changed_key(struct sshkey *host_key)
fp = sshkey_fingerprint(host_key, options.fingerprint_hash,
SSH_FP_DEFAULT);
if (fp == NULL)
- fatal("%s: sshkey_fingerprint fail", __func__);
+ fatal_f("sshkey_fingerprint fail");
error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
error("@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @");
@@ -1430,7 +1698,8 @@ maybe_add_key_to_agent(const char *authfile, struct sshkey *private,
if (sshkey_is_sk(private))
skprovider = options.sk_provider;
if ((r = ssh_add_identity_constrained(auth_sock, private,
- comment == NULL ? authfile : comment, 0,
+ comment == NULL ? authfile : comment,
+ options.add_keys_to_agent_lifespan,
(options.add_keys_to_agent == 3), 0, skprovider)) == 0)
debug("identity added to agent: %s", authfile);
else