From 39dce0098015867dcea115c88ef485485cd6032a Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Sun, 8 Aug 2004 16:17:05 +0000 Subject: - 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 --HG-- extra : convert_revision : 6b8ab4ec5a6c99733fff584231b81ad9636ff15e --- cli-kex.c | 137 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 132 insertions(+), 5 deletions(-) (limited to 'cli-kex.c') diff --git a/cli-kex.c b/cli-kex.c index d882423..436fdad 100644 --- a/cli-kex.c +++ b/cli-kex.c @@ -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 @@ void recv_msg_kexdh_reply() { 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 @@ void recv_msg_kexdh_reply() { 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 @@ void recv_msg_kexdh_reply() { 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); +} -- cgit v1.2.3