summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorMatt Johnston <matt@ucc.asn.au>2004-08-08 16:17:05 +0000
committerMatt Johnston <matt@ucc.asn.au>2004-08-08 16:17:05 +0000
commit39dce0098015867dcea115c88ef485485cd6032a (patch)
tree3863ec9a3a9dcc14d9c0ec96f83bd64f288fa07f
parent333eac7f9a976d646e319a044923872a9b876789 (diff)
- 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
-rw-r--r--cli-kex.c137
-rw-r--r--dbutil.c46
-rw-r--r--options.h4
-rw-r--r--signkey.c84
-rw-r--r--signkey.h5
-rw-r--r--svr-authpubkey.c93
6 files changed, 258 insertions, 111 deletions
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);
+}
diff --git a/dbutil.c b/dbutil.c
index 1c0648c..dd68416 100644
--- a/dbutil.c
+++ b/dbutil.c
@@ -320,6 +320,52 @@ int buf_readfile(buffer* buf, const char* filename) {
return DROPBEAR_SUCCESS;
}
+/* get a line from the file into buffer in the style expected for an
+ * authkeys file.
+ * Will return DROPBEAR_SUCCESS if data is read, or DROPBEAR_FAILURE on EOF.*/
+/* Only used for ~/.ssh/known_hosts and ~/.ssh/authorized_keys */
+#if defined(DROPBEAR_CLIENT) || defined(DROPBEAR_PUBKEY_AUTH)
+int buf_getline(buffer * line, FILE * authfile) {
+
+ int c = EOF;
+
+ TRACE(("enter buf_getline"));
+
+ buf_setpos(line, 0);
+ buf_setlen(line, 0);
+
+ while (line->pos < line->size) {
+
+ c = fgetc(authfile); /*getc() is weird with some uClibc systems*/
+ if (c == EOF || c == '\n' || c == '\r') {
+ goto out;
+ }
+
+ buf_putbyte(line, (unsigned char)c);
+ }
+
+ TRACE(("leave getauthline: line too long"));
+ /* We return success, but the line length will be zeroed - ie we just
+ * ignore that line */
+ buf_setlen(line, 0);
+
+out:
+
+ buf_setpos(line, 0);
+
+ /* if we didn't read anything before EOF or error, exit */
+ if (c == EOF && line->pos == 0) {
+ TRACE(("leave getauthline: failure"));
+ return DROPBEAR_FAILURE;
+ } else {
+ TRACE(("leave getauthline: success"));
+ return DROPBEAR_SUCCESS;
+ }
+
+ TRACE(("leave buf_getline"));
+}
+#endif
+
/* loop until the socket is closed (in case of EINTR) or
* we get and error.
* Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
diff --git a/options.h b/options.h
index 55755b2..d4abf09 100644
--- a/options.h
+++ b/options.h
@@ -301,6 +301,10 @@
#define USING_LISTENERS
#endif
+#if defined(DROPBEAR_CLIENT) || defined(DROPBEAR_PUBKEY_AUTH)
+#define DROPBEAR_KEY_LINES /* ie we're using authorized_keys or known_hosts */
+#endif
+
/* We use dropbear_client and dropbear_server as shortcuts to avoid redundant
* code, if we're just compiling as client or server */
#if defined(DROPBEAR_SERVER) && defined(DROPBEAR_CLIENT)
diff --git a/signkey.c b/signkey.c
index ec6ea74..3efcc2b 100644
--- a/signkey.c
+++ b/signkey.c
@@ -272,25 +272,20 @@ static char hexdig(unsigned char x) {
/* Since we're not sure if we'll have md5 or sha1, we present both.
* MD5 is used in preference, but sha1 could still be useful */
#ifdef DROPBEAR_MD5_HMAC
-static char * sign_key_md5_fingerprint(sign_key *key, int type) {
+static char * sign_key_md5_fingerprint(unsigned char* keyblob,
+ unsigned int keybloblen) {
char * ret;
hash_state hs;
- buffer *pubkeys;
unsigned char hash[MD5_HASH_SIZE];
unsigned int h, i;
unsigned int buflen;
md5_init(&hs);
- pubkeys = buf_new(1000);
- buf_put_pub_key(pubkeys, key, type);
/* skip the size int of the string - this is a bit messy */
- buf_setpos(pubkeys, 4);
- md5_process(&hs, buf_getptr(pubkeys, pubkeys->len-pubkeys->pos),
- pubkeys->len-pubkeys->pos);
+ md5_process(&hs, keyblob, keybloblen);
- buf_free(pubkeys);
md5_done(&hs, hash);
/* "md5 hexfingerprinthere\0", each hex digit is "AB:" etc */
@@ -311,25 +306,20 @@ static char * sign_key_md5_fingerprint(sign_key *key, int type) {
}
#else /* use SHA1 rather than MD5 for fingerprint */
-static char * sign_key_sha1_fingerprint(sign_key *key, int type) {
+static char * sign_key_sha1_fingerprint(unsigned char* keyblob,
+ unsigned int keybloblen) {
char * ret;
hash_state hs;
- buffer *pubkeys;
unsigned char hash[SHA1_HASH_SIZE];
unsigned int h, i;
unsigned int buflen;
sha1_init(&hs);
- pubkeys = buf_new(1000);
- buf_put_pub_key(pubkeys, key, type);
- buf_setpos(pubkeys, 4);
/* skip the size int of the string - this is a bit messy */
- sha1_process(&hs, buf_getptr(pubkeys, pubkeys->len-pubkeys->pos),
- pubkeys->len-pubkeys->pos);
+ sha1_process(&hs, keyblob, keybloblen);
- buf_free(pubkeys);
sha1_done(&hs, hash);
/* "sha1 hexfingerprinthere\0", each hex digit is "AB:" etc */
@@ -352,12 +342,12 @@ static char * sign_key_sha1_fingerprint(sign_key *key, int type) {
/* This will return a freshly malloced string, containing a fingerprint
* in either sha1 or md5 */
-char * sign_key_fingerprint(sign_key *key, int type) {
+char * sign_key_fingerprint(unsigned char* keyblob, unsigned int keybloblen) {
#ifdef DROPBEAR_MD5_HMAC
- return sign_key_md5_fingerprint(key, type);
+ return sign_key_md5_fingerprint(keyblob, keybloblen);
#else
- return sign_key_sha1_fingerprint(key, type);
+ return sign_key_sha1_fingerprint(keyblob, keybloblen);
#endif
}
@@ -427,3 +417,59 @@ int buf_verify(buffer * buf, sign_key *key, const unsigned char *data,
return DROPBEAR_FAILURE;
}
#endif /* DROPBEAR_SIGNKEY_VERIFY */
+
+#ifdef DROPBEAR_KEY_LINES /* ie we're using authorized_keys or known_hosts */
+
+/* Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE when given a buffer containing
+ * a key, a key, and a type. The buffer is positioned at the start of the
+ * base64 data, and contains no trailing data */
+int cmp_base64_key(const unsigned char* keyblob, unsigned int keybloblen,
+ const unsigned char* algoname, unsigned int algolen,
+ buffer * line) {
+
+ buffer * decodekey = NULL;
+ int ret = DROPBEAR_FAILURE;
+ unsigned int len, filealgolen;
+ unsigned long decodekeylen;
+ unsigned char* filealgo = NULL;
+
+ /* now we have the actual data */
+ len = line->len - line->pos;
+ decodekeylen = len * 2; /* big to be safe */
+ decodekey = buf_new(decodekeylen);
+
+ if (base64_decode(buf_getptr(line, len), len,
+ buf_getwriteptr(decodekey, decodekey->size),
+ &decodekeylen) != CRYPT_OK) {
+ TRACE(("checkpubkey: base64 decode failed"));
+ goto out;
+ }
+ TRACE(("checkpubkey: base64_decode success"));
+ buf_incrlen(decodekey, decodekeylen);
+
+ /* compare the keys */
+ if ( ( decodekeylen != keybloblen )
+ || memcmp( buf_getptr(decodekey, decodekey->len),
+ keyblob, decodekey->len) != 0) {
+ TRACE(("checkpubkey: compare failed"));
+ goto out;
+ }
+
+ /* ... and also check that the algo specified and the algo in the key
+ * itself match */
+ filealgolen = buf_getint(decodekey);
+ filealgo = buf_getptr(decodekey, filealgolen);
+ if (filealgolen != algolen || memcmp(filealgo, algoname, algolen) != 0) {
+ TRACE(("checkpubkey: algo match failed"));
+ goto out;
+ }
+
+ /* All checks passed */
+ ret = DROPBEAR_SUCCESS;
+
+out:
+ buf_free(decodekey);
+ decodekey = NULL;
+ return ret;
+}
+#endif
diff --git a/signkey.h b/signkey.h
index 3bd5c58..8bc7e8f 100644
--- a/signkey.h
+++ b/signkey.h
@@ -54,7 +54,10 @@ void buf_put_sign(buffer* buf, sign_key *key, int type,
#ifdef DROPBEAR_SIGNKEY_VERIFY
int buf_verify(buffer * buf, sign_key *key, const unsigned char *data,
unsigned int len);
-char * sign_key_fingerprint(sign_key *key, int type);
+char * sign_key_fingerprint(unsigned char* keyblob, unsigned int keybloblen);
#endif
+int cmp_base64_key(const unsigned char* keyblob, unsigned int keybloblen,
+ const unsigned char* algoname, unsigned int algolen,
+ buffer * line);
#endif /* _SIGNKEY_H_ */
diff --git a/svr-authpubkey.c b/svr-authpubkey.c
index e6cc606..53d5c06 100644
--- a/svr-authpubkey.c
+++ b/svr-authpubkey.c
@@ -38,7 +38,7 @@
#ifdef DROPBEAR_PUBKEY_AUTH
#define MIN_AUTHKEYS_LINE 10 /* "ssh-rsa AB" - short but doesn't matter */
-#define MAX_AUTHKEYS_LINE 1000 /* max length of a line in authkeys */
+#define MAX_AUTHKEYS_LINE 4200 /* max length of a line in authkeys */
static int checkpubkey(unsigned char* algo, unsigned int algolen,
unsigned char* keyblob, unsigned int keybloblen);
@@ -46,7 +46,6 @@ static int checkpubkeyperms();
static void send_msg_userauth_pk_ok(unsigned char* algo, unsigned int algolen,
unsigned char* keyblob, unsigned int keybloblen);
static int checkfileperm(char * filename);
-static int getauthline(buffer * line, FILE * authfile);
/* process a pubkey auth request, sending success or failure message as
* appropriate */
@@ -102,7 +101,7 @@ void svr_auth_pubkey() {
buf_setpos(signbuf, 0);
/* ... and finally verify the signature */
- fp = sign_key_fingerprint(key, type);
+ fp = sign_key_fingerprint(keyblob, keybloblen);
if (buf_verify(ses.payload, key, buf_getptr(signbuf, signbuf->len),
signbuf->len) == DROPBEAR_SUCCESS) {
dropbear_log(LOG_NOTICE,
@@ -160,10 +159,6 @@ static int checkpubkey(unsigned char* algo, unsigned int algolen,
char * filename = NULL;
int ret = DROPBEAR_FAILURE;
buffer * line = NULL;
- buffer * decodekey = NULL;
- unsigned long decodekeylen;
- unsigned char* filealgo = NULL;
- unsigned int filealgolen;
unsigned int len, pos;
TRACE(("enter checkpubkey"));
@@ -202,14 +197,8 @@ static int checkpubkey(unsigned char* algo, unsigned int algolen,
/* iterate through the lines */
do {
- /* free reused vars */
- if (decodekey) {
- buf_free(decodekey);
- decodekey = NULL;
- }
- m_free(filealgo);
- if (getauthline(line, authfile) == DROPBEAR_FAILURE) {
+ if (buf_getline(line, authfile) == DROPBEAR_FAILURE) {
/* EOF reached */
TRACE(("checkpubkey: authorized_keys EOF reached"));
break;
@@ -243,38 +232,12 @@ static int checkpubkey(unsigned char* algo, unsigned int algolen,
TRACE(("checkpubkey: line pos = %d len = %d", line->pos, line->len));
- /* now we have the actual data */
- decodekeylen = (line->len - line->pos) * 2;
- decodekey = buf_new(decodekeylen);
- if (base64_decode(buf_getptr(line, line->len - line->pos),
- line->len - line->pos,
- buf_getwriteptr(decodekey, decodekey->size),
- &decodekeylen) != CRYPT_OK) {
- TRACE(("checkpubkey: base64 decode failed"));
- continue;
- }
- TRACE(("checkpubkey: base64_decode success"));
- buf_incrlen(decodekey, decodekeylen);
-
- /* compare the keys */
- if (decodekeylen != keybloblen || memcmp(
- buf_getptr(decodekey, decodekey->len),
- keyblob, decodekey->len) != 0) {
- TRACE(("checkpubkey: compare failed"));
- continue;
- }
-
- /* and also check that the algo specified and the algo in the key
- * itself match */
- filealgo = buf_getstring(decodekey, &filealgolen);
- if (filealgolen != algolen || memcmp(filealgo, algo, algolen) != 0) {
- TRACE(("checkpubkey: algo match failed"));
- continue;
+ ret = cmp_base64_key(keyblob, keybloblen, algo, algolen, line);
+ if (ret == DROPBEAR_SUCCESS) {
+ break;
}
- /* now we know this key is good */
- ret = DROPBEAR_SUCCESS;
- break;
+ /* We continue to the next line otherwise */
} while (1);
@@ -285,53 +248,11 @@ out:
if (line) {
buf_free(line);
}
- if (decodekey) {
- buf_free(decodekey);
- }
m_free(filename);
- m_free(filealgo);
TRACE(("leave checkpubkey: ret=%d", ret));
return ret;
}
-/* get a line from the file into buffer in the style expected for an
- * authkeys file.
- * Will return DROPBEAR_SUCCESS if data is read, or DROPBEAR_FAILURE on EOF.*/
-static int getauthline(buffer * line, FILE * authfile) {
-
- int c = EOF;
-
- TRACE(("enter getauthline"));
-
- buf_setpos(line, 0);
- buf_setlen(line, 0);
-
- while (line->pos < line->size) {
- c = fgetc(authfile); /*getc() is weird with some uClibc systems*/
- if (c == EOF || c == '\n' || c == '\r') {
- goto out;
- }
- buf_putbyte(line, (unsigned char)c);
- }
-
- TRACE(("leave getauthline: line too long"));
- return DROPBEAR_FAILURE;
-
-out:
-
- buf_setpos(line, 0);
-
- /* if we didn't read anything before EOF or error, exit */
- if (c == EOF && line->pos == 0) {
- TRACE(("leave getauthline: failure"));
- return DROPBEAR_FAILURE;
- } else {
- TRACE(("leave getauthline: success"));
- return DROPBEAR_SUCCESS;
- }
-
- TRACE(("leave getauthline"));
-}
/* Returns DROPBEAR_SUCCESS if file permissions for pubkeys are ok,
* DROPBEAR_FAILURE otherwise.