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