merge of 3b1edf7489e1de452e30aaaec37d647db44e4328

and 6cdb6b2c0e0e0a600eeb5975e499c9303fe2d4d4

--HG--
extra : convert_revision : 6bf41e419b773e153b574b15005ab1e498643400
This commit is contained in:
Matt Johnston 2004-08-09 03:21:20 +00:00
commit 41f5e21044
10 changed files with 268 additions and 119 deletions

View File

@ -10,7 +10,7 @@
# This makefile is quite evil.
ifndef PROGRAMS
PROGRAMS=dropbear dbclient dropbearkey dropbearmulti
PROGRAMS=dropbear dbclient dropbearkey dropbearkey
endif
LTC=libtomcrypt/libtomcrypt.a
@ -27,7 +27,8 @@ SVROBJS=svr-kex.o svr-algo.o svr-auth.o sshpty.o \
svr-chansession.o svr-runopts.o svr-agentfwd.o svr-main.o svr-x11fwd.o
CLIOBJS=cli-algo.o cli-main.o cli-auth.o cli-authpasswd.o cli-kex.o \
cli-session.o cli-service.o cli-runopts.o cli-chansession.o
cli-session.o cli-service.o cli-runopts.o cli-chansession.o \
cli-authpubkey.o
CLISVROBJS=common-session.o packet.o common-algo.o common-kex.o \
common-channel.o common-chansession.o termcodes.o loginrec.o \

137
cli-kex.c
View File

@ -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);
}

View File

@ -43,10 +43,10 @@ static void printhelp() {
"Usage: %s [options] user@host\n"
"Options are:\n"
"-p <remoteport>\n"
"-t Allocate a pty"
"-T Don't allocate a pty"
"-t Allocate a pty\n"
"-T Don't allocate a pty\n"
#ifdef DROPBEAR_PUBKEY_AUTH
"-i <identityfile> (multiple allowed)"
"-i <identityfile> (multiple allowed)\n"
#endif
,DROPBEAR_VERSION, cli_opts.progname);
}

View File

@ -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 */

View File

@ -49,6 +49,7 @@ int connect_remote(const char* remotehost, const char* remoteport,
int nonblocking, char ** errstring);
char* getaddrhostname(struct sockaddr * addr);
int buf_readfile(buffer* buf, const char* filename);
int buf_getline(buffer * line, FILE * authfile);
int m_close(int fd);
void * m_malloc(size_t size);

View File

@ -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)

View File

@ -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

View File

@ -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_ */

View File

@ -50,7 +50,7 @@ void svr_auth_password() {
passwdcrypt = ses.authstate.pw->pw_passwd;
#ifdef HAVE_SHADOW_H
/* get the shadow password if possible */
spasswd = getspnam(ses.authstate.pw->pw_name);
spasswd = getspnam(ses.authstate.printableuser);
if (spasswd != NULL && spasswd->sp_pwdp != NULL) {
passwdcrypt = spasswd->sp_pwdp;
}
@ -87,6 +87,8 @@ void svr_auth_password() {
/* the first bytes of passwdcrypt are the salt */
testcrypt = crypt((char*)password, passwdcrypt);
m_burn(password, passwordlen);
m_free(password);
if (strcmp(testcrypt, passwdcrypt) == 0) {
/* successful authentication */
@ -101,8 +103,6 @@ void svr_auth_password() {
send_msg_userauth_failure(0, 1);
}
m_burn(password, passwordlen);
m_free(password);
}
#endif /* DROPBEAR_PASSWORD_AUTH */

View File

@ -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;
ret = cmp_base64_key(keyblob, keybloblen, algo, algolen, line);
if (ret == DROPBEAR_SUCCESS) {
break;
}
/* 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;
}
/* 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.