diff options
author | Damien Miller <djm@cvs.openbsd.org> | 2020-10-11 22:14:39 +0000 |
---|---|---|
committer | Damien Miller <djm@cvs.openbsd.org> | 2020-10-11 22:14:39 +0000 |
commit | d05c431231fd3fcf7f2fd0547d0d64fe37e119d6 (patch) | |
tree | 467cce26465a97f7ae886dc1b0a55c901f1c3763 | |
parent | 81bf5f09cba065da28f33f99df8d861bc1bf4afe (diff) |
UpdateHostkeys: check for keys under other names
Stop UpdateHostkeys from automatically removing deprecated keys from
known_hosts files if the same keys exist under a different name or
address to the host that is being connected to.
This avoids UpdateHostkeys from making known_hosts inconsistent in
some cases. For example, multiple host aliases sharing address-based
known_hosts on different lines, or hosts that resolves to multiple
addresses.
ok markus@
-rw-r--r-- | usr.bin/ssh/clientloop.c | 89 |
1 files changed, 82 insertions, 7 deletions
diff --git a/usr.bin/ssh/clientloop.c b/usr.bin/ssh/clientloop.c index a7e7b7b2175..6ea31f30a2b 100644 --- a/usr.bin/ssh/clientloop.c +++ b/usr.bin/ssh/clientloop.c @@ -1,4 +1,4 @@ -/* $OpenBSD: clientloop.c,v 1.351 2020/10/11 22:13:37 djm Exp $ */ +/* $OpenBSD: clientloop.c,v 1.352 2020/10/11 22:14:38 djm Exp $ */ /* * Author: Tatu Ylonen <ylo@cs.hut.fi> * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland @@ -1819,6 +1819,7 @@ struct hostkeys_update_ctx { /* Various special cases. */ int complex_hostspec; /* wildcard or manual pattern-list host name */ int ca_available; /* saw CA key for this host */ + int old_key_seen; /* saw old key with other name/addr */ }; static void @@ -1864,6 +1865,7 @@ hostspec_is_complex(const char *hosts) return 0; } +/* callback to search for ctx->keys in known_hosts */ static int hostkeys_find(struct hostkey_foreach_line *l, void *_ctx) { @@ -1909,6 +1911,63 @@ hostkeys_find(struct hostkey_foreach_line *l, void *_ctx) return 0; } +/* callback to search for ctx->old_keys in known_hosts under other names */ +static int +hostkeys_check_old(struct hostkey_foreach_line *l, void *_ctx) +{ + struct hostkeys_update_ctx *ctx = (struct hostkeys_update_ctx *)_ctx; + size_t i; + int hashed; + + /* only care about lines that *don't* match the active host spec */ + if (l->status == HKF_STATUS_MATCHED || l->key == NULL) + return 0; + + hashed = l->match & (HKF_MATCH_HOST_HASHED|HKF_MATCH_IP_HASHED); + for (i = 0; i < ctx->nold; i++) { + if (!sshkey_equal(l->key, ctx->old_keys[i])) + continue; + debug3("%s: found deprecated %s key at %s:%ld as %s", __func__, + sshkey_ssh_name(ctx->keys[i]), l->path, l->linenum, + hashed ? "[HASHED]" : l->hosts); + ctx->old_key_seen = 1; + break; + } + return 0; +} + +/* + * Check known_hosts files for deprecated keys under other names. Returns 0 + * on success or -1 on failure. Updates ctx->old_key_seen if deprecated keys + * exist under names other than the active hostname/IP. + */ +static int +check_old_keys_othernames(struct hostkeys_update_ctx *ctx) +{ + size_t i; + int r; + + debug2("%s: checking for %zu deprecated keys", __func__, ctx->nold); + for (i = 0; i < options.num_user_hostfiles; i++) { + debug3("%s: searching %s for %s / %s", __func__, + options.user_hostfiles[i], ctx->host_str, + ctx->ip_str ? ctx->ip_str : "(none)"); + if ((r = hostkeys_foreach(options.user_hostfiles[i], + hostkeys_check_old, ctx, ctx->host_str, ctx->ip_str, + HKF_WANT_PARSE_KEY)) != 0) { + if (r == SSH_ERR_SYSTEM_ERROR && errno == ENOENT) { + debug("%s: hostkeys file %s does not exist", + __func__, options.user_hostfiles[i]); + continue; + } + error("%s: hostkeys_foreach failed for %s: %s", + __func__, options.user_hostfiles[i], ssh_err(r)); + return -1; + } + } + return 0; +} + static void hostkey_change_preamble(LogLevel loglevel) { @@ -2234,10 +2293,6 @@ client_input_hostkeys(struct ssh *ssh) ctx->nincomplete++; } - /* - * XXX if removing keys, check whether they appear under different - * names/addresses and refuse to proceed if they do. - */ debug3("%s: %zu server keys: %zu new, %zu retained, " "%zu incomplete match. %zu to remove", __func__, ctx->nkeys, @@ -2249,8 +2304,28 @@ client_input_hostkeys(struct ssh *ssh) debug("%s: manual list or wildcard host pattern found, " "skipping UserKnownHostsFile update", __func__); goto out; - } else if (ctx->nnew == 0 && - (ctx->nold != 0 || ctx->nincomplete != 0)) { + } + + /* + * If removing keys, check whether they appear under different + * names/addresses and refuse to proceed if they do. This avoids + * cases such as hosts with multiple names becoming inconsistent + * with regards to CheckHostIP entries. + * XXX UpdateHostkeys=force to override this (and other) checks? + */ + if (ctx->nold != 0) { + if (check_old_keys_othernames(ctx) != 0) + goto out; /* error already logged */ + if (ctx->old_key_seen) { + debug("%s: key(s) for %s%s%s exist under other names; " + "skipping UserKnownHostsFile update", __func__, + ctx->host_str, ctx->ip_str == NULL ? "" : ",", + ctx->ip_str == NULL ? "" : ctx->ip_str); + goto out; + } + } + + if (ctx->nnew == 0 && (ctx->nold != 0 || ctx->nincomplete != 0)) { /* * We have some keys to remove or fix matching for. * We can proceed to do this without requiring a fresh proof |