[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