diff cli-kex.c @ 436:7282370416a0

Improve known_hosts checking.
author Matt Johnston <matt@ucc.asn.au>
date Thu, 22 Feb 2007 15:29:32 +0000
parents ab57ba0cb667
children 91939c8c2572
line wrap: on
line diff
--- a/cli-kex.c	Thu Feb 22 14:53:49 2007 +0000
+++ b/cli-kex.c	Thu Feb 22 15:29:32 2007 +0000
@@ -146,31 +146,24 @@
 	dropbear_exit("Didn't validate host key");
 }
 
-static void checkhostkey(unsigned char* keyblob, unsigned int keybloblen) {
-
+static FILE* open_known_hosts_file(int * readonly)
+{
+	FILE * hostsfile = NULL;
 	char * filename = NULL;
-	FILE *hostsfile = NULL;
-	int readonly = 0;
-	struct passwd *pw = NULL;
 	char * homedir = NULL;
-	unsigned int hostlen, algolen;
-	unsigned long len;
-	const char *algoname = NULL;
-	buffer * line = NULL;
-	int ret;
 	
 	homedir = getenv("HOME");
 
 	if (!homedir) {
+		struct passwd * pw = NULL;
 		pw = getpwuid(getuid());
 		if (pw) {
 			homedir = pw->pw_dir;
 		}
-		pw = NULL;
 	}
 
 	if (homedir) {
-
+		unsigned int len;
 		len = strlen(homedir);
 		filename = m_malloc(len + 18); /* "/.ssh/known_hosts" and null-terminator*/
 
@@ -181,8 +174,7 @@
 				dropbear_log(LOG_INFO, "Warning: failed creating %s/.ssh: %s",
 						homedir, strerror(errno));
 				TRACE(("mkdir didn't work: %s", strerror(errno)))
-				ask_to_confirm(keyblob, keybloblen);
-				goto out; /* only get here on success */
+				goto out;
 			}
 		}
 
@@ -190,12 +182,13 @@
 		hostsfile = fopen(filename, "a+");
 		
 		if (hostsfile != NULL) {
+			*readonly = 0;
 			fseek(hostsfile, 0, SEEK_SET);
 		} else {
 			/* We mightn't have been able to open it if it was read-only */
 			if (errno == EACCES || errno == EROFS) {
 					TRACE(("trying readonly: %s", strerror(errno)))
-					readonly = 1;
+					*readonly = 1;
 					hostsfile = fopen(filename, "r");
 			}
 		}
@@ -205,10 +198,32 @@
 		TRACE(("hostsfile didn't open: %s", strerror(errno)))
 		dropbear_log(LOG_WARNING, "Failed to open %s/.ssh/known_hosts",
 				homedir);
+		goto out;
+	}	
+
+out:
+	m_free(filename);
+	return hostsfile;
+}
+
+static void checkhostkey(unsigned char* keyblob, unsigned int keybloblen) {
+
+	FILE *hostsfile = NULL;
+	int readonly = 0;
+	unsigned int hostlen, algolen;
+	unsigned long len;
+	const char *algoname = NULL;
+	char * fingerprint = NULL;
+	buffer * line = NULL;
+	int ret;
+
+	hostsfile = open_known_hosts_file(&readonly);
+	if (!hostsfile)	{
 		ask_to_confirm(keyblob, keybloblen);
-		goto out; /* We only get here on success */
+		/* ask_to_confirm will exit upon failure */
+		return;
 	}
-
+	
 	line = buf_new(MAX_KNOWNHOSTS_LINE);
 	hostlen = strlen(cli_opts.remotehost);
 	algoname = signkey_name_from_type(ses.newkeys->algo_hostkey, &algolen);
@@ -242,7 +257,7 @@
 			continue;
 		}
 
-		if ( strncmp(buf_getptr(line, algolen), algoname, algolen) != 0) {
+		if (strncmp(buf_getptr(line, algolen), algoname, algolen) != 0) {
 			TRACE(("algo doesn't match"))
 			continue;
 		}
@@ -254,7 +269,8 @@
 		}
 
 		/* Now we're at the interesting hostkey */
-		ret = cmp_base64_key(keyblob, keybloblen, algoname, algolen, line);
+		ret = cmp_base64_key(keyblob, keybloblen, algoname, algolen,
+						line, &fingerprint);
 
 		if (ret == DROPBEAR_SUCCESS) {
 			/* Good matching key */
@@ -262,7 +278,15 @@
 			goto out;
 		}
 
-		/* The keys didn't match. eep. */
+		/* The keys didn't match. eep. Note that we're "leaking"
+		   the fingerprint strings here, but we're exiting anyway */
+		dropbear_exit("\n\nHost key mismatch for %s !\n"
+					"Fingerprint is %s\n"
+					"Expected %s\n"
+					"If you know that the host key is correct you can\nremove the bad entry from ~/.ssh/known_hosts", 
+					cli_opts.remotehost,
+					sign_key_fingerprint(keyblob, keybloblen),
+					fingerprint ? fingerprint : "UNKNOWN");
 	} while (1); /* keep going 'til something happens */
 
 	/* Key doesn't exist yet */
@@ -300,7 +324,6 @@
 	if (hostsfile != NULL) {
 		fclose(hostsfile);
 	}
-	m_free(filename);
 	if (line != NULL) {
 		buf_free(line);
 	}