[PATCH] hostfile: list known names (if any) for new hostkeys

Oskari Saarenmaa os at ohmu.fi
Fri Dec 28 02:15:06 EST 2012


When connecting to a host for which there's no known hostkey, check if the
relevant key has been accepted for other hostnames.  This is useful when
connecting to a host with a dymamic IP address or multiple names.
---
 auth.c        |  4 ++--
 hostfile.c    | 42 ++++++++++++++++++++++++++++--------------
 hostfile.h    |  8 ++++++--
 sshconnect.c  | 39 +++++++++++++++++++++++++++++++++------
 sshconnect2.c |  4 ++--
 5 files changed, 71 insertions(+), 26 deletions(-)

diff --git a/auth.c b/auth.c
index 7bc6f40..1ca07e1 100644
--- a/auth.c
+++ b/auth.c
@@ -379,7 +379,7 @@ check_key_in_hostfiles(struct passwd *pw, Key *key, const char *host,
 	const struct hostkey_entry *found;
 
 	hostkeys = init_hostkeys();
-	load_hostkeys(hostkeys, host, sysfile);
+	load_hostkeys(hostkeys, host, NULL, sysfile);
 	if (userfile != NULL) {
 		user_hostfile = tilde_expand_filename(userfile, pw->pw_uid);
 		if (options.strict_modes &&
@@ -393,7 +393,7 @@ check_key_in_hostfiles(struct passwd *pw, Key *key, const char *host,
 			    user_hostfile);
 		} else {
 			temporarily_use_uid(pw);
-			load_hostkeys(hostkeys, host, user_hostfile);
+			load_hostkeys(hostkeys, host, NULL, user_hostfile);
 			restore_uid();
 		}
 		xfree(user_hostfile);
diff --git a/hostfile.c b/hostfile.c
index b6f924b..e493c91 100644
--- a/hostfile.c
+++ b/hostfile.c
@@ -58,11 +58,6 @@
 #include "log.h"
 #include "misc.h"
 
-struct hostkeys {
-	struct hostkey_entry *entries;
-	u_int num_entries;
-};
-
 static int
 extract_salt(const char *s, u_int l, char *salt, size_t salt_len)
 {
@@ -236,20 +231,22 @@ init_hostkeys(void)
 }
 
 void
-load_hostkeys(struct hostkeys *hostkeys, const char *host, const char *path)
+load_hostkeys(struct hostkeys *hostkeys, const char *lookup_host,
+    const Key *lookup_key, const char *path)
 {
 	FILE *f;
 	char line[8192];
 	u_long linenum = 0, num_loaded = 0;
 	char *cp, *cp2, *hashed_host;
+	const char *current_host;
 	HostkeyMarker marker;
 	Key *key;
 	int kbits;
 
 	if ((f = fopen(path, "r")) == NULL)
 		return;
-	debug3("%s: loading entries for host \"%.100s\" from file \"%s\"",
-	    __func__, host, path);
+	debug3("%s: loading entries for host \"%.100s\"%s from file \"%s\"",
+	    __func__, lookup_host, (lookup_key ? " and key" : ""), path);
 	while (read_keyfile_line(f, path, line, sizeof(line), &linenum) == 0) {
 		cp = line;
 
@@ -269,11 +266,11 @@ load_hostkeys(struct hostkeys *hostkeys, const char *host, const char *path)
 		for (cp2 = cp; *cp2 && *cp2 != ' ' && *cp2 != '\t'; cp2++)
 			;
 
-		/* Check if the host name matches. */
-		if (match_hostname(host, cp, (u_int) (cp2 - cp)) != 1) {
+		/* Check if the host name matches if we're looking for a host. */
+		if (lookup_host && match_hostname(lookup_host, cp, (u_int) (cp2 - cp)) != 1) {
 			if (*cp != HASH_DELIM)
 				continue;
-			hashed_host = host_hash(host, cp, (u_int) (cp2 - cp));
+			hashed_host = host_hash(lookup_host, cp, (u_int) (cp2 - cp));
 			if (hashed_host == NULL) {
 				debug("Invalid hashed host line %lu of %s",
 				    linenum, path);
@@ -283,7 +280,17 @@ load_hostkeys(struct hostkeys *hostkeys, const char *host, const char *path)
 				continue;
 		}
 
-		/* Got a match.  Skip host name. */
+		/* If we're looking for a key grab the hostname and ignore hashed entries. */
+		if (lookup_key) {
+			if (*cp == HASH_DELIM)
+				continue;
+			*cp2++ = 0;
+			current_host = cp;
+		} else {
+			current_host = lookup_host;
+		}
+
+		/* Move pointer past the hostname. */
 		cp = cp2;
 
 		/*
@@ -299,7 +306,14 @@ load_hostkeys(struct hostkeys *hostkeys, const char *host, const char *path)
 				continue;
 			}
 		}
-		if (!hostfile_check_key(kbits, key, host, path, linenum))
+
+		/* Check if the key matches if we're looking for a key. */
+		if (lookup_key) {
+		        if (!key_equal(lookup_key, key))
+		                continue;
+		}
+
+		if (!hostfile_check_key(kbits, key, current_host, path, linenum))
 			continue;
 
 		debug3("%s: found %skey type %s in file %s:%lu", __func__,
@@ -308,7 +322,7 @@ load_hostkeys(struct hostkeys *hostkeys, const char *host, const char *path)
 		    key_type(key), path, linenum);
 		hostkeys->entries = xrealloc(hostkeys->entries,
 		    hostkeys->num_entries + 1, sizeof(*hostkeys->entries));
-		hostkeys->entries[hostkeys->num_entries].host = xstrdup(host);
+		hostkeys->entries[hostkeys->num_entries].host = xstrdup(current_host);
 		hostkeys->entries[hostkeys->num_entries].file = xstrdup(path);
 		hostkeys->entries[hostkeys->num_entries].line = linenum;
 		hostkeys->entries[hostkeys->num_entries].key = key;
diff --git a/hostfile.h b/hostfile.h
index d84d422..c2965f9 100644
--- a/hostfile.h
+++ b/hostfile.h
@@ -29,10 +29,14 @@ struct hostkey_entry {
 	Key *key;
 	HostkeyMarker marker;
 };
-struct hostkeys;
+
+struct hostkeys {
+	struct hostkey_entry *entries;
+	u_int num_entries;
+};
 
 struct hostkeys *init_hostkeys(void);
-void	 load_hostkeys(struct hostkeys *, const char *, const char *);
+void	 load_hostkeys(struct hostkeys *, const char *, const Key *, const char *);
 void	 free_hostkeys(struct hostkeys *);
 
 HostStatus check_key_in_hostkeys(struct hostkeys *, Key *,
diff --git a/sshconnect.c b/sshconnect.c
index 07800a6..62306ac 100644
--- a/sshconnect.c
+++ b/sshconnect.c
@@ -718,13 +718,13 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
 	Key *raw_key = NULL;
 	char *ip = NULL, *host = NULL;
 	char hostline[1000], *hostp, *fp, *ra;
-	char msg[1024];
+	char msg[2048];
 	const char *type;
 	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;
-	struct hostkeys *host_hostkeys, *ip_hostkeys;
+	struct hostkeys *host_hostkeys, *ip_hostkeys, *key_hostkeys = NULL;
 	u_int i;
 
 	/*
@@ -758,17 +758,17 @@ 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, NULL, user_hostfiles[i]);
 	for (i = 0; i < num_system_hostfiles; i++)
-		load_hostkeys(host_hostkeys, host, system_hostfiles[i]);
+		load_hostkeys(host_hostkeys, host, NULL, system_hostfiles[i]);
 
 	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, NULL, user_hostfiles[i]);
 		for (i = 0; i < num_system_hostfiles; i++)
-			load_hostkeys(ip_hostkeys, ip, system_hostfiles[i]);
+			load_hostkeys(ip_hostkeys, ip, NULL, system_hostfiles[i]);
 	}
 
  retry:
@@ -879,6 +879,29 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
 					    "No matching host key fingerprint"
 					    " found in DNS.\n");
 			}
+			/* Has this key been accepted for other hostnames? */
+			key_hostkeys = init_hostkeys();
+			for (i = 0; i < num_user_hostfiles; i++)
+				load_hostkeys(key_hostkeys, NULL, host_key,
+				    user_hostfiles[i]);
+			for (i = 0; i < num_system_hostfiles; i++)
+				load_hostkeys(key_hostkeys, NULL, host_key,
+				    system_hostfiles[i]);
+			if (key_hostkeys->num_entries > 0) {
+				strlcat(msg2, "You have previously accepted "
+				    "this key for the following hostnames:",
+				    sizeof(msg2));
+				for (i = 0; i < key_hostkeys->num_entries; i++) {
+					strlcat(msg2, "\n\t", sizeof(msg2));
+					strlcat(msg2, key_hostkeys->entries[i].host,
+					    sizeof(msg2));
+				}
+				if (strlcat(msg2, "\n", sizeof(msg2)) >=
+				    sizeof(msg2)) {
+					/* truncate at last newline. */
+					*(strrchr(msg2, '\n') + 1) = 0;
+				}
+			}
 			snprintf(msg, sizeof(msg),
 			    "The authenticity of host '%.200s (%s)' can't be "
 			    "established%s\n"
@@ -1097,6 +1120,8 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
 		free_hostkeys(host_hostkeys);
 	if (ip_hostkeys != NULL)
 		free_hostkeys(ip_hostkeys);
+	if (key_hostkeys != NULL)
+		free_hostkeys(key_hostkeys);
 	return 0;
 
 fail:
@@ -1120,6 +1145,8 @@ fail:
 		free_hostkeys(host_hostkeys);
 	if (ip_hostkeys != NULL)
 		free_hostkeys(ip_hostkeys);
+	if (key_hostkeys != NULL)
+		free_hostkeys(key_hostkeys);
 	return -1;
 }
 
diff --git a/sshconnect2.c b/sshconnect2.c
index 6791ea3..a3ed37a 100644
--- a/sshconnect2.c
+++ b/sshconnect2.c
@@ -115,9 +115,9 @@ order_hostkeyalgs(char *host, struct sockaddr *hostaddr, u_short port)
 	get_hostfile_hostname_ipaddr(host, hostaddr, port, &hostname, NULL);
 	hostkeys = init_hostkeys();
 	for (i = 0; i < options.num_user_hostfiles; i++)
-		load_hostkeys(hostkeys, hostname, options.user_hostfiles[i]);
+		load_hostkeys(hostkeys, hostname, NULL, options.user_hostfiles[i]);
 	for (i = 0; i < options.num_system_hostfiles; i++)
-		load_hostkeys(hostkeys, hostname, options.system_hostfiles[i]);
+		load_hostkeys(hostkeys, hostname, NULL, options.system_hostfiles[i]);
 
 	oavail = avail = xstrdup(KEX_DEFAULT_PK_ALG);
 	maxlen = strlen(avail) + 1;
-- 
1.8.0.2



More information about the openssh-unix-dev mailing list