[patch] tell user about hosts with same key

Nickolai Zeldovich kolya at MIT.EDU
Sun Oct 3 12:03:07 EST 2004


The attached patch implements a feature that would make my interaction
with ssh somewhat more secure.  When connecting to a host whose key is
not in the known_hosts file, this patch makes ssh tell the user about any
other hosts in the known_hosts file that have the same key.

For example, if I have host A in my known_hosts file, and try to connect
to host B which is an alias for A, ssh will tell me that host A has the
same key as B.  As a result, I'm better informed in whether to say "yes"
or "no" -- if I know that B really is an alias for A, then I can safely
say yes without having to verify the fingerprint.

Apologies for the slightly crude coding style, but I was in a hurry and,
unfortunately, probably won't have time to clean it up this month.

-- kolya
-------------- next part --------------
--- sshconnect.c	2004/10/02 21:27:29	1.1
+++ sshconnect.c	2004/10/02 22:01:52
@@ -716,7 +716,7 @@
 			    "have requested strict checking.", type, host);
 			goto fail;
 		} else if (options.strict_host_key_checking == 2) {
-			char msg1[1024], msg2[1024];
+			char msg1[1024], msg2[1024], msg_same_key[1024];
 
 			if (show_other_keys(host, host_key))
 				snprintf(msg1, sizeof(msg1),
@@ -724,6 +724,29 @@
 				   " known for this host.");
 			else
 				snprintf(msg1, sizeof(msg1), ".");
+
+			HostList *keyhosts = NULL;
+			keyhosts = find_hosts_by_key(user_hostfile, host_key, keyhosts);
+			keyhosts = find_hosts_by_key(system_hostfile, host_key, keyhosts);
+			if (keyhosts != NULL) {
+				snprintf(msg_same_key, sizeof(msg_same_key),
+					 "The following hosts are already known to "
+					 "have the same key:\n");
+
+				HostList *x;
+				for (x = keyhosts; x; x = x->next) {
+					if (sizeof(msg_same_key) <
+					    strlen(msg_same_key) + strlen(x->host) + 3)
+						break;
+					strcat(msg_same_key, "\t");
+					strcat(msg_same_key, x->host);
+					strcat(msg_same_key, "\n");
+				}
+				free_hostlist(keyhosts);
+			} else {
+				msg_same_key[0] = '\0';
+			}
+
 			/* The default */
 			fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX);
 			msg2[0] = '\0';
@@ -740,10 +763,11 @@
 			snprintf(msg, sizeof(msg),
 			    "The authenticity of host '%.200s (%s)' can't be "
 			    "established%s\n"
+			    "%s"
 			    "%s key fingerprint is %s.\n%s"
 			    "Are you sure you want to continue connecting "
 			    "(yes/no)? ",
-			    host, ip, msg1, type, fp, msg2);
+			    host, ip, msg1, msg_same_key, type, fp, msg2);
 			xfree(fp);
 			if (!confirm(msg))
 				goto fail;
--- hostfile.c	2004/10/02 21:27:21	1.1
+++ hostfile.c	2004/10/02 21:57:04
@@ -197,6 +197,115 @@
 	    found, numret));
 }
 
+void
+free_hostlist(HostList *l)
+{
+	HostList *n;
+
+	for (; l != NULL; l = n) {
+		n = l->next;
+		free(l->host);
+		free(l);
+	}
+}
+
+static HostList *
+add_host_to_hostlist(HostList *l, char *hostname)
+{
+	HostList *n = malloc(sizeof(*n));
+	n->host = malloc(strlen(hostname) + 1);
+	sprintf(n->host, "%s", hostname);
+	n->next = l;
+	return n;
+}
+
+HostList *
+find_hosts_by_key(const char *filename, const Key *search_key, HostList *initial_hosts)
+{
+	Key *found;
+	FILE *f;
+	char line[8192];
+	int linenum = 0;
+	u_int kbits;
+	char *cp, *cp2;
+	HostList *hostlist;
+	char *thishost = NULL;
+	u_int thishostlen;
+
+	debug3("find_hosts_by_key: filename %s", filename);
+
+	/* Open the file containing the list of known hosts. */
+	f = fopen(filename, "r");
+	if (!f)
+		return initial_hosts;
+
+	hostlist = initial_hosts;
+	found = key_new(search_key->type);
+
+	/* Go through the file. */
+	while (fgets(line, sizeof(line), f)) {
+		cp = line;
+		linenum++;
+
+		/* Skip any leading whitespace, comments and empty lines. */
+		for (; *cp == ' ' || *cp == '\t'; cp++)
+			;
+		if (!*cp || *cp == '#' || *cp == '\n')
+			continue;
+
+		/* Find the end of the host name portion. */
+		for (cp2 = cp; *cp2 && *cp2 != ' ' && *cp2 != '\t'; cp2++)
+			;
+
+		/* Remember the host portion. */
+		if (thishost != NULL)
+			free(thishost);
+		thishostlen = (u_int) (cp2 - cp);
+		thishost = malloc(thishostlen + 1);
+		memcpy(thishost, cp, thishostlen);
+		thishost[thishostlen] = '\0';
+
+		/* Skip host name. */
+		cp = cp2;
+
+		/*
+		 * Extract the key from the line.  This will skip any leading
+		 * whitespace.  Ignore badly formatted lines.
+		 */
+		if (!hostfile_read_key(&cp, &kbits, found))
+			continue;
+
+		if (!hostfile_check_key(kbits, found, thishost, filename, linenum))
+			continue;
+
+		/* Check if the current key is the same as the given key. */
+		if (key_equal(search_key, found)) {
+			/* Ok, they match. */
+			debug3("find_hosts_by_key: match line %d", linenum);
+			cp = thishost;
+			while (cp < thishost + thishostlen) {
+				for (cp2 = cp;
+				     *cp2 != ',' && cp2 < thishost + thishostlen;
+				     cp2++)
+					;
+
+				if (cp2 < thishost + thishostlen)
+					*cp2 = '\0';
+
+				hostlist = add_host_to_hostlist(hostlist, cp);
+				cp = cp2 + 1;
+			}
+		}
+	}
+
+	/* Clear variables and close the file. */
+	fclose(f);
+	if (thishost != NULL)
+		free(thishost);
+
+	return hostlist;
+}
+
 int
 lookup_key_in_hostfile_by_type(const char *filename, const char *host,
     int keytype, Key *found, int *numret)
--- hostfile.h	2004/10/02 21:45:51	1.1
+++ hostfile.h	2004/10/02 21:56:52
@@ -18,11 +18,18 @@
 	HOST_OK, HOST_NEW, HOST_CHANGED, HOST_FOUND
 }       HostStatus;
 
+typedef struct HostList {
+	char *host;
+	struct HostList *next;
+} HostList;
+
 int	 hostfile_read_key(char **, u_int *, Key *);
 HostStatus check_host_in_hostfile(const char *, const char *,
 	    const Key *, Key *, int *);
 int	add_host_to_hostfile(const char *, const char *, const Key *);
 int	lookup_key_in_hostfile_by_type(const char *, const char *,
 	    int, Key *, int *);
+HostList *find_hosts_by_key(const char *, const Key *, HostList *);
+void	free_hostlist(HostList *);
 
 #endif


More information about the openssh-unix-dev mailing list