diff cli-kex.c @ 51:095d689fed16

- Hostkey checking is mostly there, just aren't appending yet. - Rearranged various bits of the fingerprint/base64 type code, so it can be shared between versions
author Matt Johnston <matt@ucc.asn.au>
date Sun, 08 Aug 2004 16:17:05 +0000
parents e2a1eaa19f22
children bdc97a5719f4
line wrap: on
line diff
--- a/cli-kex.c	Sat Aug 07 15:50:58 2004 +0000
+++ b/cli-kex.c	Sun Aug 08 16:17:05 2004 +0000
@@ -37,6 +37,8 @@
 #include "signkey.h"
 
 
+static void checkhostkey(unsigned char* keyblob, unsigned int keybloblen);
+#define MAX_KNOWNHOSTS_LINE 4500
 
 void send_msg_kexdh_init() {
 
@@ -58,14 +60,22 @@
 
 	mp_int dh_f;
 	sign_key *hostkey = NULL;
-	int type, keylen;
+	unsigned int type, keybloblen;
+	unsigned char* keyblob = NULL;
+
 
 	TRACE(("enter recv_msg_kexdh_reply"));
 	type = ses.newkeys->algo_hostkey;
 	TRACE(("type is %d", type));
 
 	hostkey = new_sign_key();
-	keylen = buf_getint(ses.payload);
+	keybloblen = buf_getint(ses.payload);
+
+	keyblob = buf_getptr(ses.payload, keybloblen);
+	if (!ses.kexstate.donefirstkex) {
+		/* Only makes sense the first time */
+		checkhostkey(keyblob, keybloblen);
+	}
 
 	if (buf_get_pub_key(ses.payload, hostkey, &type) != DROPBEAR_SUCCESS) {
 		TRACE(("failed getting pubkey"));
@@ -86,9 +96,6 @@
 		dropbear_exit("Bad hostkey signature");
 	}
 
-	/* XXX TODO */
-	dropbear_log(LOG_WARNING,"Not checking hostkey fingerprint for the moment");
-
 	sign_key_free(hostkey);
 	hostkey = NULL;
 
@@ -96,3 +103,123 @@
 	ses.requirenext = SSH_MSG_NEWKEYS;
 	TRACE(("leave recv_msg_kexdh_init"));
 }
+
+static void ask_to_confirm(unsigned char* keyblob, unsigned int keybloblen) {
+
+	char* fp = NULL;
+
+	fp = sign_key_fingerprint(keyblob, keybloblen);
+	fprintf(stderr, "\nHost '%s' is not in the trusted hosts file.\n(fingerprint %s)\nDo you want to continue connecting? (y/n)\n", 
+			cli_opts.remotehost, 
+			fp);
+
+	if (getc(stdin) == 'y') {
+		m_free(fp);
+		return;
+	}
+
+	dropbear_exit("Didn't validate host key");
+}
+
+static void checkhostkey(unsigned char* keyblob, unsigned int keybloblen) {
+
+	char * filename = NULL;
+	FILE *hostsfile = NULL;
+	struct passwd *pw = NULL;
+	unsigned int len, hostlen;
+	const char *algoname = NULL;
+	buffer * line = NULL;
+	int ret;
+	
+	pw = getpwuid(getuid());
+
+	if (pw == NULL) {
+		dropbear_exit("Failed to get homedir");
+	}
+
+	len = strlen(pw->pw_dir);
+	filename = m_malloc(len + 18); /* "/.ssh/known_hosts" and null-terminator*/
+
+	snprintf(filename, len+18, "%s/.ssh", pw->pw_dir);
+	/* Check that ~/.ssh exists - easiest way is just to mkdir */
+	if (mkdir(filename, S_IRWXU) != 0) {
+		if (errno != EEXIST) {
+			ask_to_confirm(keyblob, keybloblen);
+			goto out; /* only get here on success */
+		}
+	}
+
+	snprintf(filename, len+18, "%s/.ssh/known_hosts", pw->pw_dir);
+	hostsfile = fopen(filename, "r+");
+	if (hostsfile == NULL) {
+		ask_to_confirm(keyblob, keybloblen);
+		goto out; /* We only get here on success */
+	}
+
+	line = buf_new(MAX_KNOWNHOSTS_LINE);
+	hostlen = strlen(cli_opts.remotehost);
+
+	do {
+		if (buf_getline(line, hostsfile) == DROPBEAR_FAILURE) {
+			TRACE(("failed reading line: prob EOF"));
+			break;
+		}
+
+		/* The line is too short to be sensible */
+		/* "30" is 'enough to hold ssh-dss plus the spaces, ie so we don't
+		 * buf_getfoo() past the end and die horribly - the base64 parsing
+		 * code is what tiptoes up to the end nicely */
+		if (line->len < (hostlen+30) ) {
+			TRACE(("line is too short to be sensible"));
+			continue;
+		}
+
+		/* Compare hostnames */
+		if (strncmp(cli_opts.remotehost, buf_getptr(line, hostlen),
+					hostlen) != 0) {
+			TRACE(("hosts don't match"));
+			continue;
+		}
+
+		buf_incrpos(line, hostlen);
+		if (buf_getbyte(line) != ' ') {
+			/* there wasn't a space after the hostname, something dodgy */
+			TRACE(("missing space afte matching hostname"));
+			continue;
+		}
+
+		algoname = signkey_name_from_type(ses.newkeys->algo_hostkey, &len);
+		if ( strncmp(buf_getptr(line, len), algoname, len) != 0) {
+			TRACE(("algo doesn't match"));
+			continue;
+		}
+
+		buf_incrpos(line, len);
+		if (buf_getbyte(line) != ' ') {
+			TRACE(("missing space after algo"));
+			continue;
+		}
+
+		/* Now we're at the interesting hostkey */
+		ret = cmp_base64_key(keyblob, keybloblen, algoname, len, line);
+
+		if (ret == DROPBEAR_SUCCESS) {
+			/* Good matching key */
+			TRACE(("good matching key"));
+			goto out;
+		}
+
+		/* The keys didn't match. eep. */
+	} while (1); /* keep going 'til something happens */
+
+	/* Key doesn't exist yet */
+	ask_to_confirm(keyblob, keybloblen);
+	/* If we get here, they said yes */
+
+out:
+	if (hostsfile != NULL) {
+		fclose(hostsfile);
+	}
+	m_free(filename);
+	buf_free(line);
+}