merge rsa-sha256

This commit is contained in:
Matt Johnston 2020-05-26 00:24:02 +08:00
commit d277f140ba
36 changed files with 699 additions and 269 deletions

View File

@ -46,6 +46,7 @@ install:
- autoheader - autoheader
- ./configure $CONFIGURE_FLAGS CFLAGS="-O2 -Wall -Wno-pointer-sign $WEXTRAFLAGS $EXTRACFLAGS" --prefix="$HOME/inst" || (cat config.log; exit 1) - ./configure $CONFIGURE_FLAGS CFLAGS="-O2 -Wall -Wno-pointer-sign $WEXTRAFLAGS $EXTRACFLAGS" --prefix="$HOME/inst" || (cat config.log; exit 1)
- if [ "$NOWRITEV" = "1" ]; then sed -i -e s/HAVE_WRITEV/DONT_HAVE_WRITEV/ config.h ; fi - if [ "$NOWRITEV" = "1" ]; then sed -i -e s/HAVE_WRITEV/DONT_HAVE_WRITEV/ config.h ; fi
- make lint
- make -j3 - make -j3
- test -z $DO_FUZZ || make fuzzstandalone - test -z $DO_FUZZ || make fuzzstandalone
# avoid concurrent install, osx/freebsd is racey (https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=208093) # avoid concurrent install, osx/freebsd is racey (https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=208093)

View File

@ -236,7 +236,7 @@ $(STATIC_LTC): $(OPTION_HEADERS)
$(STATIC_LTM): $(OPTION_HEADERS) $(STATIC_LTM): $(OPTION_HEADERS)
$(MAKE) -C libtommath $(MAKE) -C libtommath
.PHONY : clean sizes thisclean distclean tidy ltc-clean ltm-clean .PHONY : clean sizes thisclean distclean tidy ltc-clean ltm-clean lint
ltc-clean: ltc-clean:
$(MAKE) -C libtomcrypt clean $(MAKE) -C libtomcrypt clean
@ -262,6 +262,9 @@ distclean: clean tidy
tidy: tidy:
-rm -f *~ *.gcov */*~ -rm -f *~ *.gcov */*~
lint:
cd $(srcdir); ./dropbear_lint.sh
## Fuzzing targets ## Fuzzing targets
# list of fuzz targets # list of fuzz targets

View File

@ -32,6 +32,9 @@
#if DROPBEAR_CLI_AGENTFWD #if DROPBEAR_CLI_AGENTFWD
/* From OpenSSH authfd.h */
#define SSH_AGENT_RSA_SHA2_256 0x02
/* An agent reply can be reasonably large, as it can /* An agent reply can be reasonably large, as it can
* contain a list of all public keys held by the agent. * contain a list of all public keys held by the agent.
* 10000 is arbitrary */ * 10000 is arbitrary */
@ -40,7 +43,7 @@
/* client functions */ /* client functions */
void cli_load_agent_keys(m_list * ret_list); void cli_load_agent_keys(m_list * ret_list);
void agent_buf_sign(buffer *sigblob, sign_key *key, void agent_buf_sign(buffer *sigblob, sign_key *key,
const buffer *data_buf); const buffer *data_buf, enum signature_type type);
void cli_setup_agent(const struct Channel *channel); void cli_setup_agent(const struct Channel *channel);
#ifdef __hpux #ifdef __hpux

18
algo.h
View File

@ -47,7 +47,7 @@ typedef struct Algo_Type algo_type;
/* lists mapping ssh types of algorithms to internal values */ /* lists mapping ssh types of algorithms to internal values */
extern algo_type sshkex[]; extern algo_type sshkex[];
extern algo_type sshhostkey[]; extern algo_type sigalgs[];
extern algo_type sshciphers[]; extern algo_type sshciphers[];
extern algo_type sshhashes[]; extern algo_type sshhashes[];
extern algo_type ssh_compress[]; extern algo_type ssh_compress[];
@ -120,21 +120,17 @@ struct dropbear_kex {
const struct ltc_hash_descriptor *hash_desc; const struct ltc_hash_descriptor *hash_desc;
}; };
int have_algo(const char* algo, size_t algolen, const algo_type algos[]); /* Includes all algorithms is useall is set */
void buf_put_algolist_all(buffer * buf, const algo_type localalgos[], int useall);
/* Includes "usable" algorithms */
void buf_put_algolist(buffer * buf, const algo_type localalgos[]); void buf_put_algolist(buffer * buf, const algo_type localalgos[]);
enum kexguess2_used {
KEXGUESS2_LOOK,
KEXGUESS2_NO,
KEXGUESS2_YES,
};
#define KEXGUESS2_ALGO_NAME "kexguess2@matt.ucc.asn.au" #define KEXGUESS2_ALGO_NAME "kexguess2@matt.ucc.asn.au"
#define KEXGUESS2_ALGO_ID 99
int buf_has_algo(buffer *buf, const char *algo);
algo_type * first_usable_algo(algo_type algos[]);
algo_type * buf_match_algo(buffer* buf, algo_type localalgos[], algo_type * buf_match_algo(buffer* buf, algo_type localalgos[],
enum kexguess2_used *kexguess2, int *goodguess); int kexguess2, int *goodguess);
#if DROPBEAR_USER_ALGO_LIST #if DROPBEAR_USER_ALGO_LIST
int check_user_algos(const char* user_algo_list, algo_type * algos, int check_user_algos(const char* user_algo_list, algo_type * algos,

View File

@ -228,19 +228,37 @@ char* buf_getstring(buffer* buf, unsigned int *retlen) {
} }
/* Return a string as a newly allocated buffer */ /* Return a string as a newly allocated buffer */
buffer * buf_getstringbuf(buffer *buf) { static buffer * buf_getstringbuf_int(buffer *buf, int incllen) {
buffer *ret = NULL; buffer *ret = NULL;
unsigned int len = buf_getint(buf); unsigned int len = buf_getint(buf);
int extra = 0;
if (len > MAX_STRING_LEN) { if (len > MAX_STRING_LEN) {
dropbear_exit("String too long"); dropbear_exit("String too long");
} }
ret = buf_new(len); if (incllen) {
extra = 4;
}
ret = buf_new(len+extra);
if (incllen) {
buf_putint(ret, len);
}
memcpy(buf_getwriteptr(ret, len), buf_getptr(buf, len), len); memcpy(buf_getwriteptr(ret, len), buf_getptr(buf, len), len);
buf_incrpos(buf, len); buf_incrpos(buf, len);
buf_incrlen(ret, len); buf_incrlen(ret, len);
buf_setpos(ret, 0);
return ret; return ret;
} }
/* Return a string as a newly allocated buffer */
buffer * buf_getstringbuf(buffer *buf) {
return buf_getstringbuf_int(buf, 0);
}
/* Returns a string in a new buffer, including the length */
buffer * buf_getbuf(buffer *buf) {
return buf_getstringbuf_int(buf, 1);
}
/* Just increment the buffer position the same as if we'd used buf_getstring, /* Just increment the buffer position the same as if we'd used buf_getstring,
* but don't bother copying/malloc()ing for it */ * but don't bother copying/malloc()ing for it */
void buf_eatstring(buffer *buf) { void buf_eatstring(buffer *buf) {

View File

@ -58,6 +58,7 @@ unsigned char* buf_getptr(const buffer* buf, unsigned int len);
unsigned char* buf_getwriteptr(const buffer* buf, unsigned int len); unsigned char* buf_getwriteptr(const buffer* buf, unsigned int len);
char* buf_getstring(buffer* buf, unsigned int *retlen); char* buf_getstring(buffer* buf, unsigned int *retlen);
buffer * buf_getstringbuf(buffer *buf); buffer * buf_getstringbuf(buffer *buf);
buffer * buf_getbuf(buffer *buf);
void buf_eatstring(buffer *buf); void buf_eatstring(buffer *buf);
void buf_putint(buffer* buf, unsigned int val); void buf_putint(buffer* buf, unsigned int val);
void buf_putstring(buffer* buf, const char* str, unsigned int len); void buf_putstring(buffer* buf, const char* str, unsigned int len);

View File

@ -255,11 +255,12 @@ void cli_load_agent_keys(m_list *ret_list) {
} }
void agent_buf_sign(buffer *sigblob, sign_key *key, void agent_buf_sign(buffer *sigblob, sign_key *key,
const buffer *data_buf) { const buffer *data_buf, enum signature_type sigtype) {
buffer *request_data = NULL; buffer *request_data = NULL;
buffer *response = NULL; buffer *response = NULL;
unsigned int siglen; unsigned int siglen;
int packet_type; int packet_type;
int flags = 0;
/* Request format /* Request format
byte SSH2_AGENTC_SIGN_REQUEST byte SSH2_AGENTC_SIGN_REQUEST
@ -271,7 +272,12 @@ void agent_buf_sign(buffer *sigblob, sign_key *key,
buf_put_pub_key(request_data, key, key->type); buf_put_pub_key(request_data, key, key->type);
buf_putbufstring(request_data, data_buf); buf_putbufstring(request_data, data_buf);
buf_putint(request_data, 0); #if DROPBEAR_RSA_SHA256
if (sigtype == DROPBEAR_SIGNATURE_RSA_SHA256) {
flags |= SSH_AGENT_RSA_SHA2_256;
}
#endif
buf_putint(request_data, flags);
response = agent_request(SSH2_AGENTC_SIGN_REQUEST, request_data); response = agent_request(SSH2_AGENTC_SIGN_REQUEST, request_data);

View File

@ -33,7 +33,7 @@
#include "agentfwd.h" #include "agentfwd.h"
#if DROPBEAR_CLI_PUBKEY_AUTH #if DROPBEAR_CLI_PUBKEY_AUTH
static void send_msg_userauth_pubkey(sign_key *key, int type, int realsign); static void send_msg_userauth_pubkey(sign_key *key, enum signature_type sigtype, int realsign);
/* Called when we receive a SSH_MSG_USERAUTH_FAILURE for a pubkey request. /* Called when we receive a SSH_MSG_USERAUTH_FAILURE for a pubkey request.
* We use it to remove the key we tried from the list */ * We use it to remove the key we tried from the list */
@ -59,13 +59,15 @@ void recv_msg_userauth_pk_ok() {
char* algotype = NULL; char* algotype = NULL;
unsigned int algolen; unsigned int algolen;
enum signkey_type keytype; enum signkey_type keytype;
enum signature_type sigtype;
unsigned int remotelen; unsigned int remotelen;
TRACE(("enter recv_msg_userauth_pk_ok")) TRACE(("enter recv_msg_userauth_pk_ok"))
algotype = buf_getstring(ses.payload, &algolen); algotype = buf_getstring(ses.payload, &algolen);
keytype = signkey_type_from_name(algotype, algolen); sigtype = signature_type_from_name(algotype, algolen);
TRACE(("recv_msg_userauth_pk_ok: type %d", keytype)) keytype = signkey_type_from_signature(sigtype);
TRACE(("recv_msg_userauth_pk_ok: type %d", sigtype))
m_free(algotype); m_free(algotype);
keybuf = buf_new(MAX_PUBKEY_SIZE); keybuf = buf_new(MAX_PUBKEY_SIZE);
@ -112,7 +114,7 @@ void recv_msg_userauth_pk_ok() {
TRACE(("matching key")) TRACE(("matching key"))
/* XXX TODO: if it's an encrypted key, here we ask for their /* XXX TODO: if it's an encrypted key, here we ask for their
* password */ * password */
send_msg_userauth_pubkey((sign_key*)iter->item, keytype, 1); send_msg_userauth_pubkey((sign_key*)iter->item, sigtype, 1);
} else { } else {
TRACE(("That was whacky. We got told that a key was valid, but it didn't match our list. Sounds like dodgy code on Dropbear's part")) TRACE(("That was whacky. We got told that a key was valid, but it didn't match our list. Sounds like dodgy code on Dropbear's part"))
} }
@ -120,31 +122,32 @@ void recv_msg_userauth_pk_ok() {
TRACE(("leave recv_msg_userauth_pk_ok")) TRACE(("leave recv_msg_userauth_pk_ok"))
} }
void cli_buf_put_sign(buffer* buf, sign_key *key, int type, static void cli_buf_put_sign(buffer* buf, sign_key *key, enum signature_type sigtype,
const buffer *data_buf) { const buffer *data_buf) {
#if DROPBEAR_CLI_AGENTFWD #if DROPBEAR_CLI_AGENTFWD
// TODO: rsa-sha256 agent
if (key->source == SIGNKEY_SOURCE_AGENT) { if (key->source == SIGNKEY_SOURCE_AGENT) {
/* Format the agent signature ourselves, as buf_put_sign would. */ /* Format the agent signature ourselves, as buf_put_sign would. */
buffer *sigblob; buffer *sigblob;
sigblob = buf_new(MAX_PUBKEY_SIZE); sigblob = buf_new(MAX_PUBKEY_SIZE);
agent_buf_sign(sigblob, key, data_buf); agent_buf_sign(sigblob, key, data_buf, sigtype);
buf_putbufstring(buf, sigblob); buf_putbufstring(buf, sigblob);
buf_free(sigblob); buf_free(sigblob);
} else } else
#endif /* DROPBEAR_CLI_AGENTFWD */ #endif /* DROPBEAR_CLI_AGENTFWD */
{ {
buf_put_sign(buf, key, type, data_buf); buf_put_sign(buf, key, sigtype, data_buf);
} }
} }
/* TODO: make it take an agent reference to use as well */ static void send_msg_userauth_pubkey(sign_key *key, enum signature_type sigtype, int realsign) {
static void send_msg_userauth_pubkey(sign_key *key, int type, int realsign) {
const char *algoname = NULL; const char *algoname = NULL;
unsigned int algolen; unsigned int algolen;
buffer* sigbuf = NULL; buffer* sigbuf = NULL;
enum signkey_type keytype = signkey_type_from_signature(sigtype);
TRACE(("enter send_msg_userauth_pubkey")) TRACE(("enter send_msg_userauth_pubkey sigtype %d", sigtype))
CHECKCLEARTOWRITE(); CHECKCLEARTOWRITE();
buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_REQUEST); buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_REQUEST);
@ -160,10 +163,9 @@ static void send_msg_userauth_pubkey(sign_key *key, int type, int realsign) {
buf_putbyte(ses.writepayload, realsign); buf_putbyte(ses.writepayload, realsign);
algoname = signkey_name_from_type(type, &algolen); algoname = signature_name_from_type(sigtype, &algolen);
buf_putstring(ses.writepayload, algoname, algolen); buf_putstring(ses.writepayload, algoname, algolen);
buf_put_pub_key(ses.writepayload, key, type); buf_put_pub_key(ses.writepayload, key, keytype);
if (realsign) { if (realsign) {
TRACE(("realsign")) TRACE(("realsign"))
@ -172,7 +174,7 @@ static void send_msg_userauth_pubkey(sign_key *key, int type, int realsign) {
sigbuf = buf_new(4 + ses.session_id->len + ses.writepayload->len); sigbuf = buf_new(4 + ses.session_id->len + ses.writepayload->len);
buf_putbufstring(sigbuf, ses.session_id); buf_putbufstring(sigbuf, ses.session_id);
buf_putbytes(sigbuf, ses.writepayload->data, ses.writepayload->len); buf_putbytes(sigbuf, ses.writepayload->data, ses.writepayload->len);
cli_buf_put_sign(ses.writepayload, key, type, sigbuf); cli_buf_put_sign(ses.writepayload, key, sigtype, sigbuf);
buf_free(sigbuf); /* Nothing confidential in the buffer */ buf_free(sigbuf); /* Nothing confidential in the buffer */
} }
@ -182,7 +184,7 @@ static void send_msg_userauth_pubkey(sign_key *key, int type, int realsign) {
/* Returns 1 if a key was tried */ /* Returns 1 if a key was tried */
int cli_auth_pubkey() { int cli_auth_pubkey() {
enum signature_type sigtype;
TRACE(("enter cli_auth_pubkey")) TRACE(("enter cli_auth_pubkey"))
#if DROPBEAR_CLI_AGENTFWD #if DROPBEAR_CLI_AGENTFWD
@ -190,13 +192,79 @@ int cli_auth_pubkey() {
/* get the list of available keys from the agent */ /* get the list of available keys from the agent */
cli_load_agent_keys(cli_opts.privkeys); cli_load_agent_keys(cli_opts.privkeys);
cli_opts.agent_keys_loaded = 1; cli_opts.agent_keys_loaded = 1;
TRACE(("cli_auth_pubkey: agent keys loaded"))
} }
#endif #endif
/* iterate through privkeys to remove ones not allowed in server-sig-algs */
while (cli_opts.privkeys->first) {
sign_key * key = (sign_key*)cli_opts.privkeys->first->item;
if (cli_ses.server_sig_algs) {
#if DROPBEAR_RSA
if (key->type == DROPBEAR_SIGNKEY_RSA) {
#if DROPBEAR_RSA_SHA256
if (buf_has_algo(cli_ses.server_sig_algs, SSH_SIGNATURE_RSA_SHA256)
== DROPBEAR_SUCCESS) {
sigtype = DROPBEAR_SIGNATURE_RSA_SHA256;
TRACE(("server-sig-algs allows rsa sha256"))
break;
}
#endif /* DROPBEAR_RSA_SHA256 */
#if DROPBEAR_RSA_SHA1
if (buf_has_algo(cli_ses.server_sig_algs, SSH_SIGNKEY_RSA)
== DROPBEAR_SUCCESS) {
sigtype = DROPBEAR_SIGNATURE_RSA_SHA1;
TRACE(("server-sig-algs allows rsa sha1"))
break;
}
#endif /* DROPBEAR_RSA_SHA256 */
} else
#endif /* DROPBEAR_RSA */
{
/* Not RSA */
const char *name = NULL;
sigtype = signature_type_from_signkey(key->type);
name = signature_name_from_type(sigtype, NULL);
if (buf_has_algo(cli_ses.server_sig_algs, name)
== DROPBEAR_SUCCESS) {
TRACE(("server-sig-algs allows %s", name))
break;
}
}
/* No match, skip this key */
TRACE(("server-sig-algs no match keytype %d, skipping", key->type))
key = list_remove(cli_opts.privkeys->first);
sign_key_free(key);
continue;
} else {
/* Server didn't provide a server-sig-algs list, we'll
assume all except rsa-sha256 are OK. */
#if DROPBEAR_RSA
if (key->type == DROPBEAR_SIGNKEY_RSA) {
#if DROPBEAR_RSA_SHA1
sigtype = DROPBEAR_SIGNATURE_RSA_SHA1;
TRACE(("no server-sig-algs, using rsa sha1"))
break;
#else
/* only support rsa-sha256, skip this key */
TRACE(("no server-sig-algs, skipping rsa sha256"))
key = list_remove(cli_opts.privkeys->first);
sign_key_free(key);
continue;
#endif
} /* key->type == DROPBEAR_SIGNKEY_RSA */
#endif /* DROPBEAR_RSA */
sigtype = signature_type_from_signkey(key->type);
TRACE(("no server-sig-algs, using key"))
break;
}
}
if (cli_opts.privkeys->first) { if (cli_opts.privkeys->first) {
sign_key * key = (sign_key*)cli_opts.privkeys->first->item; sign_key * key = (sign_key*)cli_opts.privkeys->first->item;
/* Send a trial request */ /* Send a trial request */
send_msg_userauth_pubkey(key, key->type, 0); send_msg_userauth_pubkey(key, sigtype, 0);
cli_ses.lastprivkey = key; cli_ses.lastprivkey = key;
TRACE(("leave cli_auth_pubkey-success")) TRACE(("leave cli_auth_pubkey-success"))
return 1; return 1;

View File

@ -94,7 +94,7 @@ void send_msg_kexdh_init() {
void recv_msg_kexdh_reply() { void recv_msg_kexdh_reply() {
sign_key *hostkey = NULL; sign_key *hostkey = NULL;
unsigned int type, keybloblen; unsigned int keytype, keybloblen;
unsigned char* keyblob = NULL; unsigned char* keyblob = NULL;
TRACE(("enter recv_msg_kexdh_reply")) TRACE(("enter recv_msg_kexdh_reply"))
@ -102,8 +102,8 @@ void recv_msg_kexdh_reply() {
if (cli_ses.kex_state != KEXDH_INIT_SENT) { if (cli_ses.kex_state != KEXDH_INIT_SENT) {
dropbear_exit("Received out-of-order kexdhreply"); dropbear_exit("Received out-of-order kexdhreply");
} }
type = ses.newkeys->algo_hostkey; keytype = ses.newkeys->algo_hostkey;
TRACE(("type is %d", type)) TRACE(("keytype is %d", keytype))
hostkey = new_sign_key(); hostkey = new_sign_key();
keybloblen = buf_getint(ses.payload); keybloblen = buf_getint(ses.payload);
@ -114,7 +114,7 @@ void recv_msg_kexdh_reply() {
checkhostkey(keyblob, keybloblen); checkhostkey(keyblob, keybloblen);
} }
if (buf_get_pub_key(ses.payload, hostkey, &type) != DROPBEAR_SUCCESS) { if (buf_get_pub_key(ses.payload, hostkey, &keytype) != DROPBEAR_SUCCESS) {
TRACE(("failed getting pubkey")) TRACE(("failed getting pubkey"))
dropbear_exit("Bad KEX packet"); dropbear_exit("Bad KEX packet");
} }
@ -173,7 +173,8 @@ void recv_msg_kexdh_reply() {
#endif #endif
cli_ses.param_kex_algo = NULL; cli_ses.param_kex_algo = NULL;
if (buf_verify(ses.payload, hostkey, ses.hash) != DROPBEAR_SUCCESS) { if (buf_verify(ses.payload, hostkey, ses.newkeys->algo_signature,
ses.hash) != DROPBEAR_SUCCESS) {
dropbear_exit("Bad hostkey signature"); dropbear_exit("Bad hostkey signature");
} }
@ -410,3 +411,38 @@ out:
} }
m_free(fingerprint); m_free(fingerprint);
} }
void recv_msg_ext_info(void) {
/* This message is not client-specific in the protocol but Dropbear only handles
a server-sent message at present. */
unsigned int num_ext;
unsigned int i;
TRACE(("enter recv_msg_ext_info"))
/* Must be after the first SSH_MSG_NEWKEYS */
TRACE(("last %d, donefirst %d, donescond %d", ses.lastpacket, ses.kexstate.donefirstkex, ses.kexstate.donesecondkex))
if (!(ses.lastpacket == SSH_MSG_NEWKEYS && !ses.kexstate.donesecondkex)) {
TRACE(("leave recv_msg_ext_info: ignoring packet received at the wrong time"))
return;
}
num_ext = buf_getint(ses.payload);
TRACE(("received SSH_MSG_EXT_INFO with %d items", num_ext))
for (i = 0; i < num_ext; i++) {
unsigned int name_len;
char *ext_name = buf_getstring(ses.payload, &name_len);
TRACE(("extension %d name '%s'", i, ext_name))
if (cli_ses.server_sig_algs == NULL
&& name_len == strlen(SSH_SERVER_SIG_ALGS)
&& strcmp(ext_name, SSH_SERVER_SIG_ALGS) == 0) {
cli_ses.server_sig_algs = buf_getbuf(ses.payload);
} else {
/* valid extension values could be >MAX_STRING_LEN */
buf_eatstring(ses.payload);
}
m_free(ext_name);
}
TRACE(("leave recv_msg_ext_info"))
}

View File

@ -106,6 +106,7 @@ static void cli_dropbear_exit(int exitcode, const char* format, va_list param) {
/* Render the formatted exit message */ /* Render the formatted exit message */
vsnprintf(exitmsg, sizeof(exitmsg), format, param); vsnprintf(exitmsg, sizeof(exitmsg), format, param);
TRACE(("Exited, cleaning up: %s", exitmsg))
/* Add the prefix depending on session/auth state */ /* Add the prefix depending on session/auth state */
if (!ses.init_done) { if (!ses.init_done) {

View File

@ -81,6 +81,7 @@ static const packettype cli_packettypes[] = {
{SSH_MSG_REQUEST_SUCCESS, ignore_recv_response}, {SSH_MSG_REQUEST_SUCCESS, ignore_recv_response},
{SSH_MSG_REQUEST_FAILURE, ignore_recv_response}, {SSH_MSG_REQUEST_FAILURE, ignore_recv_response},
#endif #endif
{SSH_MSG_EXT_INFO, recv_msg_ext_info},
{0, NULL} /* End */ {0, NULL} /* End */
}; };
@ -352,7 +353,9 @@ static void cli_session_cleanup(void) {
(void)fcntl(cli_ses.stderrcopy, F_SETFL, cli_ses.stderrflags); (void)fcntl(cli_ses.stderrcopy, F_SETFL, cli_ses.stderrflags);
cli_tty_cleanup(); cli_tty_cleanup();
if (cli_ses.server_sig_algs) {
buf_free(cli_ses.server_sig_algs);
}
} }
static void cli_finished() { static void cli_finished() {

View File

@ -32,6 +32,7 @@
#include "ecc.h" #include "ecc.h"
#include "gcm.h" #include "gcm.h"
#include "chachapoly.h" #include "chachapoly.h"
#include "ssh.h"
/* This file (algo.c) organises the ciphers which can be used, and is used to /* This file (algo.c) organises the ciphers which can be used, and is used to
* decide which ciphers/hashes/compression/signing to use during key exchange*/ * decide which ciphers/hashes/compression/signing to use during key exchange*/
@ -242,26 +243,31 @@ algo_type ssh_nocompress[] = {
{NULL, 0, NULL, 0, NULL} {NULL, 0, NULL, 0, NULL}
}; };
algo_type sshhostkey[] = { algo_type sigalgs[] = {
#if DROPBEAR_ED25519 #if DROPBEAR_ED25519
{"ssh-ed25519", DROPBEAR_SIGNKEY_ED25519, NULL, 1, NULL}, {"ssh-ed25519", DROPBEAR_SIGNATURE_ED25519, NULL, 1, NULL},
#endif #endif
#if DROPBEAR_ECDSA #if DROPBEAR_ECDSA
#if DROPBEAR_ECC_256 #if DROPBEAR_ECC_256
{"ecdsa-sha2-nistp256", DROPBEAR_SIGNKEY_ECDSA_NISTP256, NULL, 1, NULL}, {"ecdsa-sha2-nistp256", DROPBEAR_SIGNATURE_ECDSA_NISTP256, NULL, 1, NULL},
#endif #endif
#if DROPBEAR_ECC_384 #if DROPBEAR_ECC_384
{"ecdsa-sha2-nistp384", DROPBEAR_SIGNKEY_ECDSA_NISTP384, NULL, 1, NULL}, {"ecdsa-sha2-nistp384", DROPBEAR_SIGNATURE_ECDSA_NISTP384, NULL, 1, NULL},
#endif #endif
#if DROPBEAR_ECC_521 #if DROPBEAR_ECC_521
{"ecdsa-sha2-nistp521", DROPBEAR_SIGNKEY_ECDSA_NISTP521, NULL, 1, NULL}, {"ecdsa-sha2-nistp521", DROPBEAR_SIGNATURE_ECDSA_NISTP521, NULL, 1, NULL},
#endif #endif
#endif #endif
#if DROPBEAR_RSA #if DROPBEAR_RSA
{"ssh-rsa", DROPBEAR_SIGNKEY_RSA, NULL, 1, NULL}, #if DROPBEAR_RSA_SHA256
{"rsa-sha2-256", DROPBEAR_SIGNATURE_RSA_SHA256, NULL, 1, NULL},
#endif
#if DROPBEAR_RSA_SHA1
{"ssh-rsa", DROPBEAR_SIGNATURE_RSA_SHA1, NULL, 1, NULL},
#endif
#endif #endif
#if DROPBEAR_DSS #if DROPBEAR_DSS
{"ssh-dss", DROPBEAR_SIGNKEY_DSS, NULL, 1, NULL}, {"ssh-dss", DROPBEAR_SIGNATURE_DSS, NULL, 1, NULL},
#endif #endif
{NULL, 0, NULL, 0, NULL} {NULL, 0, NULL, 0, NULL}
}; };
@ -279,8 +285,6 @@ static const struct dropbear_kex kex_dh_group14_sha256 = {DROPBEAR_KEX_NORMAL_DH
static const struct dropbear_kex kex_dh_group16_sha512 = {DROPBEAR_KEX_NORMAL_DH, dh_p_16, DH_P_16_LEN, NULL, &sha512_desc }; static const struct dropbear_kex kex_dh_group16_sha512 = {DROPBEAR_KEX_NORMAL_DH, dh_p_16, DH_P_16_LEN, NULL, &sha512_desc };
#endif #endif
/* These can't be const since dropbear_ecc_fill_dp() fills out
ecc_curve at runtime */
#if DROPBEAR_ECDH #if DROPBEAR_ECDH
#if DROPBEAR_ECC_256 #if DROPBEAR_ECC_256
static const struct dropbear_kex kex_ecdh_nistp256 = {DROPBEAR_KEX_ECDH, NULL, 0, &ecc_curve_nistp256, &sha256_desc }; static const struct dropbear_kex kex_ecdh_nistp256 = {DROPBEAR_KEX_ECDH, NULL, 0, &ecc_curve_nistp256, &sha256_desc };
@ -298,6 +302,7 @@ static const struct dropbear_kex kex_ecdh_nistp521 = {DROPBEAR_KEX_ECDH, NULL, 0
static const struct dropbear_kex kex_curve25519 = {DROPBEAR_KEX_CURVE25519, NULL, 0, NULL, &sha256_desc }; static const struct dropbear_kex kex_curve25519 = {DROPBEAR_KEX_CURVE25519, NULL, 0, NULL, &sha256_desc };
#endif #endif
/* data == NULL for non-kex algorithm identifiers */
algo_type sshkex[] = { algo_type sshkex[] = {
#if DROPBEAR_CURVE25519 #if DROPBEAR_CURVE25519
{"curve25519-sha256", 0, &kex_curve25519, 1, NULL}, {"curve25519-sha256", 0, &kex_curve25519, 1, NULL},
@ -327,49 +332,122 @@ algo_type sshkex[] = {
{"diffie-hellman-group16-sha512", 0, &kex_dh_group16_sha512, 1, NULL}, {"diffie-hellman-group16-sha512", 0, &kex_dh_group16_sha512, 1, NULL},
#endif #endif
#if DROPBEAR_KEXGUESS2 #if DROPBEAR_KEXGUESS2
{KEXGUESS2_ALGO_NAME, KEXGUESS2_ALGO_ID, NULL, 1, NULL}, {KEXGUESS2_ALGO_NAME, 0, NULL, 1, NULL},
#endif
#if DROPBEAR_EXT_INFO
#if DROPBEAR_CLIENT
/* Set unusable by svr_algos_initialise() */
{SSH_EXT_INFO_C, 0, NULL, 1, NULL},
#endif
#endif #endif
{NULL, 0, NULL, 0, NULL} {NULL, 0, NULL, 0, NULL}
}; };
/* algolen specifies the length of algo, algos is our local list to match
* against.
* Returns DROPBEAR_SUCCESS if we have a match for algo, DROPBEAR_FAILURE
* otherwise */
int have_algo(const char* algo, size_t algolen, const algo_type algos[]) {
int i;
for (i = 0; algos[i].name != NULL; i++) {
if (strlen(algos[i].name) == algolen
&& (strncmp(algos[i].name, algo, algolen) == 0)) {
return DROPBEAR_SUCCESS;
}
}
return DROPBEAR_FAILURE;
}
/* Output a comma separated list of algorithms to a buffer */ /* Output a comma separated list of algorithms to a buffer */
void buf_put_algolist(buffer * buf, const algo_type localalgos[]) { void buf_put_algolist_all(buffer * buf, const algo_type localalgos[], int useall) {
unsigned int i, len; unsigned int i, len;
unsigned int donefirst = 0; unsigned int donefirst = 0;
buffer *algolist = NULL; unsigned int startpos;
algolist = buf_new(300); startpos = buf->pos;
/* Placeholder for length */
buf_putint(buf, 0);
for (i = 0; localalgos[i].name != NULL; i++) { for (i = 0; localalgos[i].name != NULL; i++) {
if (localalgos[i].usable) { if (localalgos[i].usable || useall) {
if (donefirst) if (donefirst) {
buf_putbyte(algolist, ','); buf_putbyte(buf, ',');
}
donefirst = 1; donefirst = 1;
len = strlen(localalgos[i].name); len = strlen(localalgos[i].name);
buf_putbytes(algolist, (const unsigned char *) localalgos[i].name, len); buf_putbytes(buf, (const unsigned char *) localalgos[i].name, len);
} }
} }
buf_putstring(buf, (const char*)algolist->data, algolist->len); /* Fill out the length */
TRACE(("algolist add '%*s'", algolist->len, algolist->data)) len = buf->pos - startpos - 4;
buf_free(algolist); buf_setpos(buf, startpos);
buf_putint(buf, len);
TRACE(("algolist add %d '%*s'", len, len, buf_getptr(buf, len)))
buf_incrwritepos(buf, len);
}
void buf_put_algolist(buffer * buf, const algo_type localalgos[]) {
buf_put_algolist_all(buf, localalgos, 0);
}
/* returns a list of pointers into algolist, of null-terminated names.
ret_list should be passed in with space for *ret_count elements,
on return *ret_count has the number of names filled.
algolist is modified. */
static void get_algolist(char* algolist, unsigned int algolist_len,
const char* *ret_list, unsigned int *ret_count) {
unsigned int max_count = *ret_count;
unsigned int i;
if (*ret_count == 0) {
return;
}
if (algolist_len > MAX_PROPOSED_ALGO*(MAX_NAME_LEN+1)) {
*ret_count = 0;
}
/* ret_list will contain a list of the strings parsed out.
We will have at least one string (even if it's just "") */
ret_list[0] = algolist;
*ret_count = 1;
for (i = 0; i < algolist_len; i++) {
if (algolist[i] == '\0') {
/* someone is trying something strange */
*ret_count = 0;
return;
}
if (algolist[i] == ',') {
if (*ret_count >= max_count) {
/* Too many */
*ret_count = 0;
return;
}
algolist[i] = '\0';
ret_list[*ret_count] = &algolist[i+1];
(*ret_count)++;
}
}
}
/* Return DROPBEAR_SUCCESS if the namelist contains algo,
DROPBEAR_FAILURE otherwise. buf position is not incremented. */
int buf_has_algo(buffer *buf, const char *algo) {
unsigned char* algolist = NULL;
unsigned int orig_pos = buf->pos;
unsigned int len, remotecount, i;
const char *remotenames[MAX_PROPOSED_ALGO];
int ret = DROPBEAR_FAILURE;
algolist = buf_getstring(buf, &len);
remotecount = MAX_PROPOSED_ALGO;
get_algolist(algolist, len, remotenames, &remotecount);
for (i = 0; i < remotecount; i++)
{
if (strcmp(remotenames[i], algo) == 0) {
ret = DROPBEAR_SUCCESS;
break;
}
}
if (algolist) {
m_free(algolist);
}
buf_setpos(buf, orig_pos);
return ret;
}
algo_type * first_usable_algo(algo_type algos[]) {
int i;
for (i = 0; algos[i].name != NULL; i++) {
if (algos[i].usable) {
return &algos[i];
}
}
return NULL;
} }
/* match the first algorithm in the comma-separated list in buf which is /* match the first algorithm in the comma-separated list in buf which is
@ -378,9 +456,7 @@ void buf_put_algolist(buffer * buf, const algo_type localalgos[]) {
* 0 otherwise. This is used for checking if the kexalgo/hostkeyalgos are * 0 otherwise. This is used for checking if the kexalgo/hostkeyalgos are
* guessed correctly */ * guessed correctly */
algo_type * buf_match_algo(buffer* buf, algo_type localalgos[], algo_type * buf_match_algo(buffer* buf, algo_type localalgos[],
enum kexguess2_used *kexguess2, int *goodguess) int kexguess2, int *goodguess) {
{
char * algolist = NULL; char * algolist = NULL;
const char *remotenames[MAX_PROPOSED_ALGO], *localnames[MAX_PROPOSED_ALGO]; const char *remotenames[MAX_PROPOSED_ALGO], *localnames[MAX_PROPOSED_ALGO];
unsigned int len; unsigned int len;
@ -395,40 +471,8 @@ algo_type * buf_match_algo(buffer* buf, algo_type localalgos[],
/* get the comma-separated list from the buffer ie "algo1,algo2,algo3" */ /* get the comma-separated list from the buffer ie "algo1,algo2,algo3" */
algolist = buf_getstring(buf, &len); algolist = buf_getstring(buf, &len);
TRACE(("buf_match_algo: %s", algolist)) TRACE(("buf_match_algo: %s", algolist))
if (len > MAX_PROPOSED_ALGO*(MAX_NAME_LEN+1)) { remotecount = MAX_PROPOSED_ALGO;
goto out; get_algolist(algolist, len, remotenames, &remotecount);
}
/* remotenames will contain a list of the strings parsed out */
/* We will have at least one string (even if it's just "") */
remotenames[0] = algolist;
remotecount = 1;
for (i = 0; i < len; i++) {
if (algolist[i] == '\0') {
/* someone is trying something strange */
goto out;
}
if (algolist[i] == ',') {
algolist[i] = '\0';
remotenames[remotecount] = &algolist[i+1];
remotecount++;
}
if (remotecount >= MAX_PROPOSED_ALGO) {
break;
}
}
if (kexguess2 && *kexguess2 == KEXGUESS2_LOOK) {
for (i = 0; i < remotecount; i++)
{
if (strcmp(remotenames[i], KEXGUESS2_ALGO_NAME) == 0) {
*kexguess2 = KEXGUESS2_YES;
break;
}
}
if (*kexguess2 == KEXGUESS2_LOOK) {
*kexguess2 = KEXGUESS2_NO;
}
}
for (i = 0; localalgos[i].name != NULL; i++) { for (i = 0; localalgos[i].name != NULL; i++) {
if (localalgos[i].usable) { if (localalgos[i].usable) {
@ -460,12 +504,11 @@ algo_type * buf_match_algo(buffer* buf, algo_type localalgos[],
} }
if (strcmp(servnames[j], clinames[i]) == 0) { if (strcmp(servnames[j], clinames[i]) == 0) {
/* set if it was a good guess */ /* set if it was a good guess */
if (goodguess && kexguess2) { if (goodguess != NULL) {
if (*kexguess2 == KEXGUESS2_YES) { if (kexguess2) {
if (i == 0) { if (i == 0) {
*goodguess = 1; *goodguess = 1;
} }
} else { } else {
if (i == 0 && j == 0) { if (i == 0 && j == 0) {
*goodguess = 1; *goodguess = 1;

View File

@ -65,7 +65,7 @@ void send_msg_kexinit() {
buf_put_algolist(ses.writepayload, sshkex); buf_put_algolist(ses.writepayload, sshkex);
/* server_host_key_algorithms */ /* server_host_key_algorithms */
buf_put_algolist(ses.writepayload, sshhostkey); buf_put_algolist(ses.writepayload, sigalgs);
/* encryption_algorithms_client_to_server */ /* encryption_algorithms_client_to_server */
buf_put_algolist(ses.writepayload, sshciphers); buf_put_algolist(ses.writepayload, sshciphers);
@ -110,8 +110,9 @@ void send_msg_kexinit() {
ses.newkeys = (struct key_context*)m_malloc(sizeof(struct key_context)); ses.newkeys = (struct key_context*)m_malloc(sizeof(struct key_context));
if (ses.send_kex_first_guess) { if (ses.send_kex_first_guess) {
ses.newkeys->algo_kex = sshkex[0].data; ses.newkeys->algo_kex = first_usable_algo(sshkex)->data;
ses.newkeys->algo_hostkey = sshhostkey[0].val; ses.newkeys->algo_signature = first_usable_algo(sigalgs)->val;
ses.newkeys->algo_hostkey = signkey_type_from_signature(ses.newkeys->algo_signature);
ses.send_kex_first_guess(); ses.send_kex_first_guess();
} }
@ -152,6 +153,7 @@ static void switch_keys() {
TRACE(("switch_keys done")) TRACE(("switch_keys done"))
ses.keys->algo_kex = ses.newkeys->algo_kex; ses.keys->algo_kex = ses.newkeys->algo_kex;
ses.keys->algo_hostkey = ses.newkeys->algo_hostkey; ses.keys->algo_hostkey = ses.newkeys->algo_hostkey;
ses.keys->algo_signature = ses.newkeys->algo_signature;
ses.keys->allow_compress = 0; ses.keys->allow_compress = 0;
m_free(ses.newkeys); m_free(ses.newkeys);
ses.newkeys = NULL; ses.newkeys = NULL;
@ -173,6 +175,9 @@ void send_msg_newkeys() {
/* set up our state */ /* set up our state */
ses.kexstate.sentnewkeys = 1; ses.kexstate.sentnewkeys = 1;
if (ses.kexstate.donefirstkex) {
ses.kexstate.donesecondkex = 1;
}
ses.kexstate.donefirstkex = 1; ses.kexstate.donefirstkex = 1;
ses.dataallowed = 1; /* we can send other packets again now */ ses.dataallowed = 1; /* we can send other packets again now */
gen_new_keys(); gen_new_keys();
@ -195,8 +200,6 @@ void recv_msg_newkeys() {
/* Set up the kex for the first time */ /* Set up the kex for the first time */
void kexfirstinitialise() { void kexfirstinitialise() {
ses.kexstate.donefirstkex = 0;
#ifdef DISABLE_ZLIB #ifdef DISABLE_ZLIB
ses.compress_algos = ssh_nocompress; ses.compress_algos = ssh_nocompress;
#else #else
@ -332,8 +335,8 @@ static void gen_new_keys() {
int recv_cipher = -1; int recv_cipher = -1;
if (ses.newkeys->recv.algo_crypt->cipherdesc->name != NULL) { if (ses.newkeys->recv.algo_crypt->cipherdesc->name != NULL) {
recv_cipher = find_cipher(ses.newkeys->recv.algo_crypt->cipherdesc->name); recv_cipher = find_cipher(ses.newkeys->recv.algo_crypt->cipherdesc->name);
if (recv_cipher < 0) if (recv_cipher < 0)
dropbear_exit("Crypto error"); dropbear_exit("Crypto error");
} }
if (ses.newkeys->recv.crypt_mode->start(recv_cipher, if (ses.newkeys->recv.crypt_mode->start(recv_cipher,
recv_IV, recv_key, recv_IV, recv_key,
@ -347,8 +350,8 @@ static void gen_new_keys() {
int trans_cipher = -1; int trans_cipher = -1;
if (ses.newkeys->trans.algo_crypt->cipherdesc->name != NULL) { if (ses.newkeys->trans.algo_crypt->cipherdesc->name != NULL) {
trans_cipher = find_cipher(ses.newkeys->trans.algo_crypt->cipherdesc->name); trans_cipher = find_cipher(ses.newkeys->trans.algo_crypt->cipherdesc->name);
if (trans_cipher < 0) if (trans_cipher < 0)
dropbear_exit("Crypto error"); dropbear_exit("Crypto error");
} }
if (ses.newkeys->trans.crypt_mode->start(trans_cipher, if (ses.newkeys->trans.crypt_mode->start(trans_cipher,
trans_IV, trans_key, trans_IV, trans_key,
@ -824,21 +827,36 @@ static void read_kex_algos() {
int goodguess = 0; int goodguess = 0;
int allgood = 1; /* we AND this with each goodguess and see if its still int allgood = 1; /* we AND this with each goodguess and see if its still
true after */ true after */
int kexguess2 = 0;
#if DROPBEAR_KEXGUESS2
enum kexguess2_used kexguess2 = KEXGUESS2_LOOK;
#else
enum kexguess2_used kexguess2 = KEXGUESS2_NO;
#endif
buf_incrpos(ses.payload, 16); /* start after the cookie */ buf_incrpos(ses.payload, 16); /* start after the cookie */
memset(ses.newkeys, 0x0, sizeof(*ses.newkeys)); memset(ses.newkeys, 0x0, sizeof(*ses.newkeys));
/* kex_algorithms */ /* kex_algorithms */
algo = buf_match_algo(ses.payload, sshkex, &kexguess2, &goodguess); #if DROPBEAR_KEXGUESS2
if (buf_has_algo(ses.payload, KEXGUESS2_ALGO_NAME) == DROPBEAR_SUCCESS) {
kexguess2 = 1;
}
#endif
#if DROPBEAR_EXT_INFO
/* Determine if SSH_MSG_EXT_INFO messages should be sent.
Should be done for the first key exchange. Only required on server side
for server-sig-algs */
if (IS_DROPBEAR_SERVER) {
if (!ses.kexstate.donefirstkex) {
if (buf_has_algo(ses.payload, SSH_EXT_INFO_C) == DROPBEAR_SUCCESS) {
ses.allow_ext_info = 1;
}
}
}
#endif
algo = buf_match_algo(ses.payload, sshkex, kexguess2, &goodguess);
allgood &= goodguess; allgood &= goodguess;
if (algo == NULL || algo->val == KEXGUESS2_ALGO_ID) { if (algo == NULL || algo->data == NULL) {
/* kexguess2, ext-info-c, ext-info-s should not match negotiation */
erralgo = "kex"; erralgo = "kex";
goto error; goto error;
} }
@ -847,17 +865,18 @@ static void read_kex_algos() {
ses.newkeys->algo_kex = algo->data; ses.newkeys->algo_kex = algo->data;
/* server_host_key_algorithms */ /* server_host_key_algorithms */
algo = buf_match_algo(ses.payload, sshhostkey, &kexguess2, &goodguess); algo = buf_match_algo(ses.payload, sigalgs, kexguess2, &goodguess);
allgood &= goodguess; allgood &= goodguess;
if (algo == NULL) { if (algo == NULL) {
erralgo = "hostkey"; erralgo = "hostkey";
goto error; goto error;
} }
TRACE(("hostkey algo %s", algo->name)) TRACE(("signature algo %s", algo->name))
ses.newkeys->algo_hostkey = algo->val; ses.newkeys->algo_signature = algo->val;
ses.newkeys->algo_hostkey = signkey_type_from_signature(ses.newkeys->algo_signature);
/* encryption_algorithms_client_to_server */ /* encryption_algorithms_client_to_server */
c2s_cipher_algo = buf_match_algo(ses.payload, sshciphers, NULL, NULL); c2s_cipher_algo = buf_match_algo(ses.payload, sshciphers, 0, NULL);
if (c2s_cipher_algo == NULL) { if (c2s_cipher_algo == NULL) {
erralgo = "enc c->s"; erralgo = "enc c->s";
goto error; goto error;
@ -865,7 +884,7 @@ static void read_kex_algos() {
TRACE(("enc c2s is %s", c2s_cipher_algo->name)) TRACE(("enc c2s is %s", c2s_cipher_algo->name))
/* encryption_algorithms_server_to_client */ /* encryption_algorithms_server_to_client */
s2c_cipher_algo = buf_match_algo(ses.payload, sshciphers, NULL, NULL); s2c_cipher_algo = buf_match_algo(ses.payload, sshciphers, 0, NULL);
if (s2c_cipher_algo == NULL) { if (s2c_cipher_algo == NULL) {
erralgo = "enc s->c"; erralgo = "enc s->c";
goto error; goto error;
@ -873,7 +892,7 @@ static void read_kex_algos() {
TRACE(("enc s2c is %s", s2c_cipher_algo->name)) TRACE(("enc s2c is %s", s2c_cipher_algo->name))
/* mac_algorithms_client_to_server */ /* mac_algorithms_client_to_server */
c2s_hash_algo = buf_match_algo(ses.payload, sshhashes, NULL, NULL); c2s_hash_algo = buf_match_algo(ses.payload, sshhashes, 0, NULL);
#if DROPBEAR_AEAD_MODE #if DROPBEAR_AEAD_MODE
if (((struct dropbear_cipher_mode*)c2s_cipher_algo->mode)->aead_crypt != NULL) { if (((struct dropbear_cipher_mode*)c2s_cipher_algo->mode)->aead_crypt != NULL) {
c2s_hash_algo = NULL; c2s_hash_algo = NULL;
@ -886,7 +905,7 @@ static void read_kex_algos() {
TRACE(("hash c2s is %s", c2s_hash_algo ? c2s_hash_algo->name : "<implicit>")) TRACE(("hash c2s is %s", c2s_hash_algo ? c2s_hash_algo->name : "<implicit>"))
/* mac_algorithms_server_to_client */ /* mac_algorithms_server_to_client */
s2c_hash_algo = buf_match_algo(ses.payload, sshhashes, NULL, NULL); s2c_hash_algo = buf_match_algo(ses.payload, sshhashes, 0, NULL);
#if DROPBEAR_AEAD_MODE #if DROPBEAR_AEAD_MODE
if (((struct dropbear_cipher_mode*)s2c_cipher_algo->mode)->aead_crypt != NULL) { if (((struct dropbear_cipher_mode*)s2c_cipher_algo->mode)->aead_crypt != NULL) {
s2c_hash_algo = NULL; s2c_hash_algo = NULL;
@ -899,7 +918,7 @@ static void read_kex_algos() {
TRACE(("hash s2c is %s", s2c_hash_algo ? s2c_hash_algo->name : "<implicit>")) TRACE(("hash s2c is %s", s2c_hash_algo ? s2c_hash_algo->name : "<implicit>"))
/* compression_algorithms_client_to_server */ /* compression_algorithms_client_to_server */
c2s_comp_algo = buf_match_algo(ses.payload, ses.compress_algos, NULL, NULL); c2s_comp_algo = buf_match_algo(ses.payload, ses.compress_algos, 0, NULL);
if (c2s_comp_algo == NULL) { if (c2s_comp_algo == NULL) {
erralgo = "comp c->s"; erralgo = "comp c->s";
goto error; goto error;
@ -907,7 +926,7 @@ static void read_kex_algos() {
TRACE(("hash c2s is %s", c2s_comp_algo->name)) TRACE(("hash c2s is %s", c2s_comp_algo->name))
/* compression_algorithms_server_to_client */ /* compression_algorithms_server_to_client */
s2c_comp_algo = buf_match_algo(ses.payload, ses.compress_algos, NULL, NULL); s2c_comp_algo = buf_match_algo(ses.payload, ses.compress_algos, 0, NULL);
if (s2c_comp_algo == NULL) { if (s2c_comp_algo == NULL) {
erralgo = "comp s->c"; erralgo = "comp s->c";
goto error; goto error;

View File

@ -66,7 +66,9 @@ extern int debug_trace;
/* To debug with GDB it is easier to run with no forking of child processes. /* To debug with GDB it is easier to run with no forking of child processes.
You will need to pass "-F" as well. */ You will need to pass "-F" as well. */
/* #define DEBUG_NOFORK */ #ifndef DEBUG_NOFORK
#define DEBUG_NOFORK 0
#endif
/* For testing as non-root on shadowed systems, include the crypt of a password /* For testing as non-root on shadowed systems, include the crypt of a password

8
dropbear_lint.sh Executable file
View File

@ -0,0 +1,8 @@
#!/bin/sh
EXITCODE=0
# #ifdef instead of #if
grep '#ifdef DROPBEAR' -I *.c *.h && EXITCODE=1
exit $EXITCODE

View File

@ -133,7 +133,7 @@ static void check_signkey_bits(enum signkey_type type, int bits)
} }
break; break;
#endif #endif
#ifdef DROPEAR_DSS #if DROPEAR_DSS
case DROPBEAR_SIGNKEY_DSS: case DROPBEAR_SIGNKEY_DSS:
if (bits != 1024) { if (bits != 1024) {
dropbear_exit("DSS keys have a fixed size of 1024 bits\n"); dropbear_exit("DSS keys have a fixed size of 1024 bits\n");

2
dss.h
View File

@ -30,7 +30,7 @@
#if DROPBEAR_DSS #if DROPBEAR_DSS
typedef struct { typedef struct dropbear_DSS_Key {
mp_int* p; mp_int* p;
mp_int* q; mp_int* q;

View File

@ -32,7 +32,7 @@
#define CURVE25519_LEN 32 #define CURVE25519_LEN 32
typedef struct { typedef struct dropbear_ED25519_Key {
unsigned char priv[CURVE25519_LEN]; unsigned char priv[CURVE25519_LEN];
unsigned char pub[CURVE25519_LEN]; unsigned char pub[CURVE25519_LEN];

View File

@ -27,7 +27,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
unsigned int algolen; unsigned int algolen;
char* algoname = buf_getstring(keyblob, &algolen); char* algoname = buf_getstring(keyblob, &algolen);
if (have_algo(algoname, algolen, sshhostkey) == DROPBEAR_FAILURE) { if (signature_type_from_name(algoname, algolen) == DROPBEAR_SIGNKEY_NONE) {
dropbear_exit("fuzzer imagined a bogus algorithm"); dropbear_exit("fuzzer imagined a bogus algorithm");
} }

View File

@ -27,15 +27,27 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
if (setjmp(fuzz.jmp) == 0) { if (setjmp(fuzz.jmp) == 0) {
sign_key *key = new_sign_key(); sign_key *key = new_sign_key();
enum signkey_type type = DROPBEAR_SIGNKEY_ANY; enum signkey_type keytype = DROPBEAR_SIGNKEY_ANY;
if (buf_get_pub_key(fuzz.input, key, &type) == DROPBEAR_SUCCESS) { if (buf_get_pub_key(fuzz.input, key, &keytype) == DROPBEAR_SUCCESS) {
if (buf_verify(fuzz.input, key, verifydata) == DROPBEAR_SUCCESS) { enum signature_type sigtype;
if (keytype == DROPBEAR_SIGNKEY_RSA) {
/* Flip a coin to decide rsa signature type */
int flag = buf_getbyte(fuzz_input);
if (flag & 0x01) {
sigtype = DROPBEAR_SIGNATURE_RSA_SHA256;
} else {
sigtype = DROPBEAR_SIGNATURE_RSA_SHA1;
}
} else {
sigtype = signature_type_from_signkey(keytype);
}
if (buf_verify(fuzz.input, key, sigtype, verifydata) == DROPBEAR_SUCCESS) {
/* The fuzzer is capable of generating keys with a signature to match. /* The fuzzer is capable of generating keys with a signature to match.
We don't want false positives if the key is bogus, since a client/server We don't want false positives if the key is bogus, since a client/server
wouldn't be trusting a bogus key anyway */ wouldn't be trusting a bogus key anyway */
int boguskey = 0; int boguskey = 0;
if (type == DROPBEAR_SIGNKEY_DSS) { if (keytype == DROPBEAR_SIGNKEY_DSS) {
/* So far have seen dss keys with bad p/q/g domain parameters */ /* So far have seen dss keys with bad p/q/g domain parameters */
int pprime, qprime, trials; int pprime, qprime, trials;
trials = mp_prime_rabin_miller_trials(mp_count_bits(key->dsskey->p)); trials = mp_prime_rabin_miller_trials(mp_count_bits(key->dsskey->p));

5
kex.h
View File

@ -65,6 +65,8 @@ void recv_msg_kexdh_init(void); /* server */
void send_msg_kexdh_init(void); /* client */ void send_msg_kexdh_init(void); /* client */
void recv_msg_kexdh_reply(void); /* client */ void recv_msg_kexdh_reply(void); /* client */
void recv_msg_ext_info(void);
struct KEXState { struct KEXState {
unsigned sentkexinit : 1; /*set when we've sent/recv kexinit packet */ unsigned sentkexinit : 1; /*set when we've sent/recv kexinit packet */
@ -73,8 +75,9 @@ struct KEXState {
unsigned sentnewkeys : 1; /* set once we've send MSG_NEWKEYS (will be cleared once we have also received */ unsigned sentnewkeys : 1; /* set once we've send MSG_NEWKEYS (will be cleared once we have also received */
unsigned recvnewkeys : 1; /* set once we've received MSG_NEWKEYS (cleared once we have also sent */ unsigned recvnewkeys : 1; /* set once we've received MSG_NEWKEYS (cleared once we have also sent */
unsigned donefirstkex : 1; /* Set to 1 after the first kex has completed, unsigned int donefirstkex; /* Set to 1 after the first kex has completed,
ie the transport layer has been set up */ ie the transport layer has been set up */
unsigned int donesecondkex; /* Set to 1 after the second kex has completed */
unsigned our_first_follows_matches : 1; unsigned our_first_follows_matches : 1;

View File

@ -36,6 +36,9 @@
#include "dbutil.h" #include "dbutil.h"
#include "ecc.h" #include "ecc.h"
#include "ssh.h" #include "ssh.h"
#include "rsa.h"
#include "dss.h"
#include "ed25519.h"
static const unsigned char OSSH_PKEY_BLOB[] = static const unsigned char OSSH_PKEY_BLOB[] =
"openssh-key-v1\0" /* AUTH_MAGIC */ "openssh-key-v1\0" /* AUTH_MAGIC */

View File

@ -51,8 +51,6 @@ void process_packet() {
type = buf_getbyte(ses.payload); type = buf_getbyte(ses.payload);
TRACE(("process_packet: packet type = %d, len %d", type, ses.payload->len)) TRACE(("process_packet: packet type = %d, len %d", type, ses.payload->len))
ses.lastpacket = type;
now = monotonic_now(); now = monotonic_now();
ses.last_packet_time_keepalive_recv = now; ses.last_packet_time_keepalive_recv = now;
@ -156,6 +154,7 @@ void process_packet() {
recv_unimplemented(); recv_unimplemented();
out: out:
ses.lastpacket = type;
buf_free(ses.payload); buf_free(ses.payload);
ses.payload = NULL; ses.payload = NULL;

View File

@ -30,6 +30,7 @@ fi
hg archive "$RELDIR" || exit 2 hg archive "$RELDIR" || exit 2
(cd "$RELDIR" && autoconf && autoheader) || exit 2 (cd "$RELDIR" && autoconf && autoheader) || exit 2
(cd "$RELDIR" && make lint) || exit 2
rm -r "$RELDIR/autom4te.cache" || exit 2 rm -r "$RELDIR/autom4te.cache" || exit 2

101
rsa.c
View File

@ -35,11 +35,16 @@
#include "buffer.h" #include "buffer.h"
#include "ssh.h" #include "ssh.h"
#include "dbrandom.h" #include "dbrandom.h"
#include "signkey.h"
#if DROPBEAR_RSA #if DROPBEAR_RSA
#if !(DROPBEAR_RSA_SHA1 || DROPBEAR_RSA_SHA256)
#error Somehow RSA was enabled with neither DROPBEAR_RSA_SHA1 nor DROPBEAR_RSA_SHA256
#endif
static void rsa_pad_em(const dropbear_rsa_key * key, static void rsa_pad_em(const dropbear_rsa_key * key,
const buffer *data_buf, mp_int * rsa_em); const buffer *data_buf, mp_int * rsa_em, enum signature_type sigtype);
/* Load a public rsa key from a buffer, initialising the values. /* Load a public rsa key from a buffer, initialising the values.
* The key will have the same format as buf_put_rsa_key. * The key will have the same format as buf_put_rsa_key.
@ -191,7 +196,8 @@ void buf_put_rsa_priv_key(buffer* buf, const dropbear_rsa_key *key) {
#if DROPBEAR_SIGNKEY_VERIFY #if DROPBEAR_SIGNKEY_VERIFY
/* Verify a signature in buf, made on data by the key given. /* Verify a signature in buf, made on data by the key given.
* Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
int buf_rsa_verify(buffer * buf, const dropbear_rsa_key *key, const buffer *data_buf) { int buf_rsa_verify(buffer * buf, const dropbear_rsa_key *key,
enum signature_type sigtype, const buffer *data_buf) {
unsigned int slen; unsigned int slen;
DEF_MP_INT(rsa_s); DEF_MP_INT(rsa_s);
DEF_MP_INT(rsa_mdash); DEF_MP_INT(rsa_mdash);
@ -223,7 +229,7 @@ int buf_rsa_verify(buffer * buf, const dropbear_rsa_key *key, const buffer *data
} }
/* create the magic PKCS padded value */ /* create the magic PKCS padded value */
rsa_pad_em(key, data_buf, &rsa_em); rsa_pad_em(key, data_buf, &rsa_em, sigtype);
if (mp_exptmod(&rsa_s, key->e, key->n, &rsa_mdash) != MP_OKAY) { if (mp_exptmod(&rsa_s, key->e, key->n, &rsa_mdash) != MP_OKAY) {
TRACE(("failed exptmod rsa_s")) TRACE(("failed exptmod rsa_s"))
@ -246,8 +252,10 @@ out:
/* Sign the data presented with key, writing the signature contents /* Sign the data presented with key, writing the signature contents
* to the buffer */ * to the buffer */
void buf_put_rsa_sign(buffer* buf, const dropbear_rsa_key *key, const buffer *data_buf) { void buf_put_rsa_sign(buffer* buf, const dropbear_rsa_key *key,
unsigned int nsize, ssize; enum signature_type sigtype, const buffer *data_buf) {
const char *name = NULL;
unsigned int nsize, ssize, namelen = 0;
unsigned int i; unsigned int i;
DEF_MP_INT(rsa_s); DEF_MP_INT(rsa_s);
DEF_MP_INT(rsa_tmp1); DEF_MP_INT(rsa_tmp1);
@ -259,7 +267,7 @@ void buf_put_rsa_sign(buffer* buf, const dropbear_rsa_key *key, const buffer *da
m_mp_init_multi(&rsa_s, &rsa_tmp1, &rsa_tmp2, &rsa_tmp3, NULL); m_mp_init_multi(&rsa_s, &rsa_tmp1, &rsa_tmp2, &rsa_tmp3, NULL);
rsa_pad_em(key, data_buf, &rsa_tmp1); rsa_pad_em(key, data_buf, &rsa_tmp1, sigtype);
/* the actual signing of the padded data */ /* the actual signing of the padded data */
@ -311,7 +319,8 @@ void buf_put_rsa_sign(buffer* buf, const dropbear_rsa_key *key, const buffer *da
mp_clear_multi(&rsa_tmp1, &rsa_tmp2, &rsa_tmp3, NULL); mp_clear_multi(&rsa_tmp1, &rsa_tmp2, &rsa_tmp3, NULL);
/* create the signature to return */ /* create the signature to return */
buf_putstring(buf, SSH_SIGNKEY_RSA, SSH_SIGNKEY_RSA_LEN); name = signature_name_from_type(sigtype, &namelen);
buf_putstring(buf, name, namelen);
nsize = mp_unsigned_bin_size(key->n); nsize = mp_unsigned_bin_size(key->n);
@ -340,51 +349,73 @@ void buf_put_rsa_sign(buffer* buf, const dropbear_rsa_key *key, const buffer *da
TRACE(("leave buf_put_rsa_sign")) TRACE(("leave buf_put_rsa_sign"))
} }
/* Creates the message value as expected by PKCS, see rfc2437 etc */ /* Creates the message value as expected by PKCS,
/* format to be padded to is: see rfc8017 section 9.2 */
* EM = 01 | FF* | 00 | prefix | hash
*
* where FF is repeated enough times to make EM one byte
* shorter than the size of key->n
*
* prefix is the ASN1 designator prefix,
* hex 30 21 30 09 06 05 2B 0E 03 02 1A 05 00 04 14
*
* rsa_em must be a pointer to an initialised mp_int.
*/
static void rsa_pad_em(const dropbear_rsa_key * key, static void rsa_pad_em(const dropbear_rsa_key * key,
const buffer *data_buf, mp_int * rsa_em) { const buffer *data_buf, mp_int * rsa_em, enum signature_type sigtype) {
/* EM = 0x00 || 0x01 || PS || 0x00 || T
PS is padding of 0xff to make EM the size of key->n
/* ASN1 designator (including the 0x00 preceding) */ T is the DER encoding of the hash alg (sha1 or sha256)
const unsigned char rsa_asn1_magic[] = */
{0x00, 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b,
/* From rfc8017 page 46 */
#if DROPBEAR_RSA_SHA1
const unsigned char T_sha1[] =
{0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b,
0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14}; 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14};
const unsigned int RSA_ASN1_MAGIC_LEN = 16; #endif
#if DROPBEAR_RSA_SHA256
const unsigned char T_sha256[] =
{0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20};
#endif
int Tlen = 0;
const unsigned char *T = NULL;
const struct ltc_hash_descriptor *hash_desc = NULL;
buffer * rsa_EM = NULL; buffer * rsa_EM = NULL;
hash_state hs; hash_state hs;
unsigned int nsize; unsigned int nsize;
dropbear_assert(key != NULL); switch (sigtype) {
#if DROPBEAR_RSA_SHA1
case DROPBEAR_SIGNATURE_RSA_SHA1:
Tlen = sizeof(T_sha1);
T = T_sha1;
hash_desc = &sha1_desc;
break;
#endif
#if DROPBEAR_RSA_SHA256
case DROPBEAR_SIGNATURE_RSA_SHA256:
Tlen = sizeof(T_sha256);
T = T_sha256;
hash_desc = &sha256_desc;
break;
#endif
default:
assert(0);
}
nsize = mp_unsigned_bin_size(key->n); nsize = mp_unsigned_bin_size(key->n);
rsa_EM = buf_new(nsize-1); rsa_EM = buf_new(nsize);
/* type byte */ /* type byte */
buf_putbyte(rsa_EM, 0x00);
buf_putbyte(rsa_EM, 0x01); buf_putbyte(rsa_EM, 0x01);
/* Padding with 0xFF bytes */ /* Padding with PS 0xFF bytes */
while(rsa_EM->pos != rsa_EM->size - RSA_ASN1_MAGIC_LEN - SHA1_HASH_SIZE) { while(rsa_EM->pos != rsa_EM->size - (1 + Tlen + hash_desc->hashsize)) {
buf_putbyte(rsa_EM, 0xff); buf_putbyte(rsa_EM, 0xff);
} }
buf_putbyte(rsa_EM, 0x00);
/* Magic ASN1 stuff */ /* Magic ASN1 stuff */
memcpy(buf_getwriteptr(rsa_EM, RSA_ASN1_MAGIC_LEN), buf_putbytes(rsa_EM, T, Tlen);
rsa_asn1_magic, RSA_ASN1_MAGIC_LEN);
buf_incrwritepos(rsa_EM, RSA_ASN1_MAGIC_LEN);
/* The hash of the data */ /* The hash of the data */
sha1_init(&hs); hash_desc->init(&hs);
sha1_process(&hs, data_buf->data, data_buf->len); hash_desc->process(&hs, data_buf->data, data_buf->len);
sha1_done(&hs, buf_getwriteptr(rsa_EM, SHA1_HASH_SIZE)); hash_desc->done(&hs, buf_getwriteptr(rsa_EM, hash_desc->hashsize));
buf_incrwritepos(rsa_EM, SHA1_HASH_SIZE); buf_incrwritepos(rsa_EM, hash_desc->hashsize);
dropbear_assert(rsa_EM->pos == rsa_EM->size); dropbear_assert(rsa_EM->pos == rsa_EM->size);

11
rsa.h
View File

@ -26,13 +26,12 @@
#define DROPBEAR_RSA_H_ #define DROPBEAR_RSA_H_
#include "includes.h" #include "includes.h"
#include "signkey.h"
#include "buffer.h" #include "buffer.h"
#if DROPBEAR_RSA #if DROPBEAR_RSA
#define RSA_SIGNATURE_SIZE (4+7+4+40) typedef struct dropbear_RSA_Key {
typedef struct {
mp_int* n; mp_int* n;
mp_int* e; mp_int* e;
@ -43,9 +42,11 @@ typedef struct {
} dropbear_rsa_key; } dropbear_rsa_key;
void buf_put_rsa_sign(buffer* buf, const dropbear_rsa_key *key, const buffer *data_buf); void buf_put_rsa_sign(buffer* buf, const dropbear_rsa_key *key,
enum signature_type sigtype, const buffer *data_buf);
#if DROPBEAR_SIGNKEY_VERIFY #if DROPBEAR_SIGNKEY_VERIFY
int buf_rsa_verify(buffer * buf, const dropbear_rsa_key *key, const buffer *data_buf); int buf_rsa_verify(buffer * buf, const dropbear_rsa_key *key,
enum signature_type sigtype, const buffer *data_buf);
#endif #endif
int buf_get_rsa_pub_key(buffer* buf, dropbear_rsa_key *key); int buf_get_rsa_pub_key(buffer* buf, dropbear_rsa_key *key);
int buf_get_rsa_priv_key(buffer* buf, dropbear_rsa_key *key); int buf_get_rsa_priv_key(buffer* buf, dropbear_rsa_key *key);

View File

@ -102,7 +102,8 @@ struct key_context {
struct key_context_directional trans; struct key_context_directional trans;
const struct dropbear_kex *algo_kex; const struct dropbear_kex *algo_kex;
int algo_hostkey; enum signkey_type algo_hostkey; /* server key type */
enum signature_type algo_signature; /* server signature type */
int allow_compress; /* whether compression has started (useful in int allow_compress; /* whether compression has started (useful in
zlib@openssh.com delayed compression case) */ zlib@openssh.com delayed compression case) */
@ -195,6 +196,9 @@ struct sshsession {
/* Enables/disables compression */ /* Enables/disables compression */
algo_type *compress_algos; algo_type *compress_algos;
/* Other side allows SSH_MSG_EXT_INFO. Currently only set for server */
int allow_ext_info;
/* a list of queued replies that should be sent after a KEX has /* a list of queued replies that should be sent after a KEX has
concluded (ie, while dataallowed was unset)*/ concluded (ie, while dataallowed was unset)*/
struct packetlist *reply_queue_head, *reply_queue_tail; struct packetlist *reply_queue_head, *reply_queue_tail;
@ -259,13 +263,12 @@ struct serversession {
#endif #endif
#if DROPBEAR_PLUGIN #if DROPBEAR_PLUGIN
/* The shared library handle */ /* The shared library handle */
void *plugin_handle; void *plugin_handle;
/* The instance created by the plugin_new function */ /* The instance created by the plugin_new function */
struct PluginInstance *plugin_instance; struct PluginInstance *plugin_instance;
#endif #endif
}; };
typedef enum { typedef enum {
@ -294,7 +297,6 @@ struct clientsession {
cli_kex_state kex_state; /* Used for progressing KEX */ cli_kex_state kex_state; /* Used for progressing KEX */
cli_state state; /* Used to progress auth/channelsession etc */ cli_state state; /* Used to progress auth/channelsession etc */
unsigned donefirstkex : 1; /* Set when we set sentnewkeys, never reset */
int tty_raw_mode; /* Whether we're in raw mode (and have to clean up) */ int tty_raw_mode; /* Whether we're in raw mode (and have to clean up) */
struct termios saved_tio; struct termios saved_tio;
@ -322,6 +324,8 @@ struct clientsession {
#endif #endif
sign_key *lastprivkey; sign_key *lastprivkey;
buffer *server_sig_algs;
int retval; /* What the command exit status was - we emulate it */ int retval; /* What the command exit status was - we emulate it */
#if 0 #if 0
TODO TODO

118
signkey.c
View File

@ -28,6 +28,9 @@
#include "buffer.h" #include "buffer.h"
#include "ssh.h" #include "ssh.h"
#include "ecdsa.h" #include "ecdsa.h"
#include "rsa.h"
#include "dss.h"
#include "ed25519.h"
static const char * const signkey_names[DROPBEAR_SIGNKEY_NUM_NAMED] = { static const char * const signkey_names[DROPBEAR_SIGNKEY_NUM_NAMED] = {
#if DROPBEAR_RSA #if DROPBEAR_RSA
@ -44,6 +47,7 @@ static const char * const signkey_names[DROPBEAR_SIGNKEY_NUM_NAMED] = {
#if DROPBEAR_ED25519 #if DROPBEAR_ED25519
"ssh-ed25519", "ssh-ed25519",
#endif /* DROPBEAR_ED25519 */ #endif /* DROPBEAR_ED25519 */
/* "rsa-sha2-256" is special-cased below since it is only a signature name, not key type */
}; };
/* malloc a new sign_key and set the dss and rsa keys to NULL */ /* malloc a new sign_key and set the dss and rsa keys to NULL */
@ -105,6 +109,70 @@ enum signkey_type signkey_type_from_name(const char* name, unsigned int namelen)
return DROPBEAR_SIGNKEY_NONE; return DROPBEAR_SIGNKEY_NONE;
} }
/* Special case for rsa-sha2-256. This could be generalised if more
signature names are added that aren't 1-1 with public key names */
const char* signature_name_from_type(enum signature_type type, unsigned int *namelen) {
#if DROPBEAR_RSA_SHA256
if (type == DROPBEAR_SIGNATURE_RSA_SHA256) {
if (namelen) {
*namelen = strlen(SSH_SIGNATURE_RSA_SHA256);
}
return SSH_SIGNATURE_RSA_SHA256;
}
#endif
#if DROPBEAR_RSA_SHA1
if (type == DROPBEAR_SIGNATURE_RSA_SHA1) {
if (namelen) {
*namelen = strlen(SSH_SIGNKEY_RSA);
}
return SSH_SIGNKEY_RSA;
}
#endif
return signkey_name_from_type((enum signkey_type)type, namelen);
}
/* Returns DROPBEAR_SIGNATURE_NONE if none match */
enum signature_type signature_type_from_name(const char* name, unsigned int namelen) {
#if DROPBEAR_RSA_SHA256
if (namelen == strlen(SSH_SIGNATURE_RSA_SHA256)
&& memcmp(name, SSH_SIGNATURE_RSA_SHA256, namelen) == 0) {
return DROPBEAR_SIGNATURE_RSA_SHA256;
}
#endif
#if DROPBEAR_RSA_SHA1
if (namelen == strlen(SSH_SIGNKEY_RSA)
&& memcmp(name, SSH_SIGNKEY_RSA, namelen) == 0) {
return DROPBEAR_SIGNATURE_RSA_SHA1;
}
#endif
return (enum signature_type)signkey_type_from_name(name, namelen);
}
/* Returns the signature type from a key type. Must not be called
with RSA keytype */
enum signature_type signature_type_from_signkey(enum signkey_type keytype) {
#if DROPBEAR_RSA
assert(keytype != DROPBEAR_SIGNKEY_RSA);
#endif
assert(keytype < DROPBEAR_SIGNKEY_NUM_NAMED);
return (enum signature_type)keytype;
}
enum signkey_type signkey_type_from_signature(enum signature_type sigtype) {
#if DROPBEAR_RSA_SHA256
if (sigtype == DROPBEAR_SIGNATURE_RSA_SHA256) {
return DROPBEAR_SIGNKEY_RSA;
}
#endif
#if DROPBEAR_RSA_SHA1
if (sigtype == DROPBEAR_SIGNATURE_RSA_SHA1) {
return DROPBEAR_SIGNKEY_RSA;
}
#endif
assert(sigtype < DROPBEAR_SIGNKEY_NUM_NAMED);
return (enum signkey_type)sigtype;
}
/* Returns a pointer to the key part specific to "type". /* Returns a pointer to the key part specific to "type".
Be sure to check both (ret != NULL) and (*ret != NULL) */ Be sure to check both (ret != NULL) and (*ret != NULL) */
void ** void **
@ -526,31 +594,39 @@ char * sign_key_fingerprint(const unsigned char* keyblob, unsigned int keybloble
#endif #endif
} }
void buf_put_sign(buffer* buf, sign_key *key, enum signkey_type type, void buf_put_sign(buffer* buf, sign_key *key, enum signature_type sigtype,
const buffer *data_buf) { const buffer *data_buf) {
buffer *sigblob; buffer *sigblob = buf_new(MAX_PUBKEY_SIZE);
sigblob = buf_new(MAX_PUBKEY_SIZE); enum signkey_type keytype = signkey_type_from_signature(sigtype);
#if DEBUG_TRACE
{
const char* signame = signature_name_from_type(sigtype, NULL);
TRACE(("buf_put_sign type %d %s", sigtype, signame));
}
#endif
#if DROPBEAR_DSS #if DROPBEAR_DSS
if (type == DROPBEAR_SIGNKEY_DSS) { if (keytype == DROPBEAR_SIGNKEY_DSS) {
buf_put_dss_sign(sigblob, key->dsskey, data_buf); buf_put_dss_sign(sigblob, key->dsskey, data_buf);
} }
#endif #endif
#if DROPBEAR_RSA #if DROPBEAR_RSA
if (type == DROPBEAR_SIGNKEY_RSA) { if (keytype == DROPBEAR_SIGNKEY_RSA) {
buf_put_rsa_sign(sigblob, key->rsakey, data_buf); buf_put_rsa_sign(sigblob, key->rsakey, sigtype, data_buf);
} }
#endif #endif
#if DROPBEAR_ECDSA #if DROPBEAR_ECDSA
if (signkey_is_ecdsa(type)) { if (signkey_is_ecdsa(keytype)) {
ecc_key **eck = (ecc_key**)signkey_key_ptr(key, type); ecc_key **eck = (ecc_key**)signkey_key_ptr(key, keytype);
if (eck && *eck) { if (eck && *eck) {
buf_put_ecdsa_sign(sigblob, *eck, data_buf); buf_put_ecdsa_sign(sigblob, *eck, data_buf);
} }
} }
#endif #endif
#if DROPBEAR_ED25519 #if DROPBEAR_ED25519
if (type == DROPBEAR_SIGNKEY_ED25519) { if (keytype == DROPBEAR_SIGNKEY_ED25519) {
buf_put_ed25519_sign(sigblob, key->ed25519key, data_buf); buf_put_ed25519_sign(sigblob, key->ed25519key, data_buf);
} }
#endif #endif
@ -567,21 +643,27 @@ void buf_put_sign(buffer* buf, sign_key *key, enum signkey_type type,
* If FAILURE is returned, the position of * If FAILURE is returned, the position of
* buf is undefined. If SUCCESS is returned, buf will be positioned after the * buf is undefined. If SUCCESS is returned, buf will be positioned after the
* signature blob */ * signature blob */
int buf_verify(buffer * buf, sign_key *key, const buffer *data_buf) { int buf_verify(buffer * buf, sign_key *key, enum signature_type expect_sigtype, const buffer *data_buf) {
char *type_name = NULL; char *type_name = NULL;
unsigned int type_name_len = 0; unsigned int type_name_len = 0;
enum signkey_type type; enum signature_type sigtype;
enum signkey_type keytype;
TRACE(("enter buf_verify")) TRACE(("enter buf_verify"))
buf_getint(buf); /* blob length */ buf_getint(buf); /* blob length */
type_name = buf_getstring(buf, &type_name_len); type_name = buf_getstring(buf, &type_name_len);
type = signkey_type_from_name(type_name, type_name_len); sigtype = signature_type_from_name(type_name, type_name_len);
m_free(type_name); m_free(type_name);
if (expect_sigtype != sigtype) {
dropbear_exit("Non-matching signing type");
}
keytype = signkey_type_from_signature(sigtype);
#if DROPBEAR_DSS #if DROPBEAR_DSS
if (type == DROPBEAR_SIGNKEY_DSS) { if (keytype == DROPBEAR_SIGNKEY_DSS) {
if (key->dsskey == NULL) { if (key->dsskey == NULL) {
dropbear_exit("No DSS key to verify signature"); dropbear_exit("No DSS key to verify signature");
} }
@ -590,23 +672,23 @@ int buf_verify(buffer * buf, sign_key *key, const buffer *data_buf) {
#endif #endif
#if DROPBEAR_RSA #if DROPBEAR_RSA
if (type == DROPBEAR_SIGNKEY_RSA) { if (keytype == DROPBEAR_SIGNKEY_RSA) {
if (key->rsakey == NULL) { if (key->rsakey == NULL) {
dropbear_exit("No RSA key to verify signature"); dropbear_exit("No RSA key to verify signature");
} }
return buf_rsa_verify(buf, key->rsakey, data_buf); return buf_rsa_verify(buf, key->rsakey, sigtype, data_buf);
} }
#endif #endif
#if DROPBEAR_ECDSA #if DROPBEAR_ECDSA
if (signkey_is_ecdsa(type)) { if (signkey_is_ecdsa(keytype)) {
ecc_key **eck = (ecc_key**)signkey_key_ptr(key, type); ecc_key **eck = (ecc_key**)signkey_key_ptr(key, keytype);
if (eck && *eck) { if (eck && *eck) {
return buf_ecdsa_verify(buf, *eck, data_buf); return buf_ecdsa_verify(buf, *eck, data_buf);
} }
} }
#endif #endif
#if DROPBEAR_ED25519 #if DROPBEAR_ED25519
if (type == DROPBEAR_SIGNKEY_ED25519) { if (keytype == DROPBEAR_SIGNKEY_ED25519) {
if (key->ed25519key == NULL) { if (key->ed25519key == NULL) {
dropbear_exit("No Ed25519 key to verify signature"); dropbear_exit("No Ed25519 key to verify signature");
} }

View File

@ -26,10 +26,13 @@
#define DROPBEAR_SIGNKEY_H_ #define DROPBEAR_SIGNKEY_H_
#include "buffer.h" #include "buffer.h"
#include "dss.h"
#include "rsa.h"
#include "ed25519.h"
/* Forward declarations */
struct dropbear_DSS_Key;
struct dropbear_RSA_Key;
struct dropbear_ED25519_Key;
/* Must match with signature_type below */
enum signkey_type { enum signkey_type {
#if DROPBEAR_RSA #if DROPBEAR_RSA
DROPBEAR_SIGNKEY_RSA, DROPBEAR_SIGNKEY_RSA,
@ -51,6 +54,28 @@ enum signkey_type {
DROPBEAR_SIGNKEY_NONE = 90, DROPBEAR_SIGNKEY_NONE = 90,
}; };
/* Must match with signkey_type above, apart from rsa */
enum signature_type {
#if DROPBEAR_DSS
DROPBEAR_SIGNATURE_DSS = DROPBEAR_SIGNKEY_DSS,
#endif
#if DROPBEAR_ECDSA
DROPBEAR_SIGNATURE_ECDSA_NISTP256 = DROPBEAR_SIGNKEY_ECDSA_NISTP256,
DROPBEAR_SIGNATURE_ECDSA_NISTP384 = DROPBEAR_SIGNKEY_ECDSA_NISTP384,
DROPBEAR_SIGNATURE_ECDSA_NISTP521 = DROPBEAR_SIGNKEY_ECDSA_NISTP521,
#endif /* DROPBEAR_ECDSA */
#if DROPBEAR_ED25519
DROPBEAR_SIGNATURE_ED25519 = DROPBEAR_SIGNKEY_ED25519,
#endif
#if DROPBEAR_RSA_SHA1
DROPBEAR_SIGNATURE_RSA_SHA1 = 100, /* ssh-rsa signature (sha1) */
#endif
#if DROPBEAR_RSA_SHA256
DROPBEAR_SIGNATURE_RSA_SHA256 = 101, /* rsa-sha2-256 signature. has a ssh-rsa key */
#endif
DROPBEAR_SIGNATURE_NONE = DROPBEAR_SIGNKEY_NONE,
};
/* Sources for signing keys */ /* Sources for signing keys */
typedef enum { typedef enum {
@ -66,10 +91,10 @@ struct SIGN_key {
char *filename; char *filename;
#if DROPBEAR_DSS #if DROPBEAR_DSS
dropbear_dss_key * dsskey; struct dropbear_DSS_Key * dsskey;
#endif #endif
#if DROPBEAR_RSA #if DROPBEAR_RSA
dropbear_rsa_key * rsakey; struct dropbear_RSA_Key * rsakey;
#endif #endif
#if DROPBEAR_ECDSA #if DROPBEAR_ECDSA
#if DROPBEAR_ECC_256 #if DROPBEAR_ECC_256
@ -83,7 +108,7 @@ struct SIGN_key {
#endif #endif
#endif #endif
#if DROPBEAR_ED25519 #if DROPBEAR_ED25519
dropbear_ed25519_key * ed25519key; struct dropbear_ED25519_Key * ed25519key;
#endif #endif
}; };
@ -92,14 +117,19 @@ typedef struct SIGN_key sign_key;
sign_key * new_sign_key(void); sign_key * new_sign_key(void);
const char* signkey_name_from_type(enum signkey_type type, unsigned int *namelen); const char* signkey_name_from_type(enum signkey_type type, unsigned int *namelen);
enum signkey_type signkey_type_from_name(const char* name, unsigned int namelen); enum signkey_type signkey_type_from_name(const char* name, unsigned int namelen);
const char* signature_name_from_type(enum signature_type type, unsigned int *namelen);
enum signature_type signature_type_from_name(const char* name, unsigned int namelen);
enum signkey_type signkey_type_from_signature(enum signature_type sigtype);
enum signature_type signature_type_from_signkey(enum signkey_type keytype);
int buf_get_pub_key(buffer *buf, sign_key *key, enum signkey_type *type); int buf_get_pub_key(buffer *buf, sign_key *key, enum signkey_type *type);
int buf_get_priv_key(buffer* buf, sign_key *key, enum signkey_type *type); int buf_get_priv_key(buffer* buf, sign_key *key, enum signkey_type *type);
void buf_put_pub_key(buffer* buf, sign_key *key, enum signkey_type type); void buf_put_pub_key(buffer* buf, sign_key *key, enum signkey_type type);
void buf_put_priv_key(buffer* buf, sign_key *key, enum signkey_type type); void buf_put_priv_key(buffer* buf, sign_key *key, enum signkey_type type);
void sign_key_free(sign_key *key); void sign_key_free(sign_key *key);
void buf_put_sign(buffer* buf, sign_key *key, enum signkey_type type, const buffer *data_buf); void buf_put_sign(buffer* buf, sign_key *key, enum signature_type sigtype, const buffer *data_buf);
#if DROPBEAR_SIGNKEY_VERIFY #if DROPBEAR_SIGNKEY_VERIFY
int buf_verify(buffer * buf, sign_key *key, const buffer *data_buf); int buf_verify(buffer * buf, sign_key *key, enum signature_type expect_sigtype, const buffer *data_buf);
char * sign_key_fingerprint(const unsigned char* keyblob, unsigned int keybloblen); char * sign_key_fingerprint(const unsigned char* keyblob, unsigned int keybloblen);
#endif #endif
int cmp_base64_key(const unsigned char* keyblob, unsigned int keybloblen, int cmp_base64_key(const unsigned char* keyblob, unsigned int keybloblen,

10
ssh.h
View File

@ -32,6 +32,7 @@
#define SSH_MSG_DEBUG 4 #define SSH_MSG_DEBUG 4
#define SSH_MSG_SERVICE_REQUEST 5 #define SSH_MSG_SERVICE_REQUEST 5
#define SSH_MSG_SERVICE_ACCEPT 6 #define SSH_MSG_SERVICE_ACCEPT 6
#define SSH_MSG_EXT_INFO 7
#define SSH_MSG_KEXINIT 20 #define SSH_MSG_KEXINIT 20
#define SSH_MSG_NEWKEYS 21 #define SSH_MSG_NEWKEYS 21
#define SSH_MSG_KEXDH_INIT 30 #define SSH_MSG_KEXDH_INIT 30
@ -94,19 +95,26 @@
#define SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE 14 #define SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE 14
#define SSH_DISCONNECT_ILLEGAL_USER_NAME 15 #define SSH_DISCONNECT_ILLEGAL_USER_NAME 15
/* rfc8308 */
#define SSH_EXT_INFO_S "ext-info-s"
#define SSH_EXT_INFO_C "ext-info-c"
#define SSH_SERVER_SIG_ALGS "server-sig-algs"
/* service types */ /* service types */
#define SSH_SERVICE_USERAUTH "ssh-userauth" #define SSH_SERVICE_USERAUTH "ssh-userauth"
#define SSH_SERVICE_USERAUTH_LEN 12 #define SSH_SERVICE_USERAUTH_LEN 12
#define SSH_SERVICE_CONNECTION "ssh-connection" #define SSH_SERVICE_CONNECTION "ssh-connection"
#define SSH_SERVICE_CONNECTION_LEN 14 #define SSH_SERVICE_CONNECTION_LEN 14
/* public key types */ /* public/signature key types */
#define SSH_SIGNKEY_DSS "ssh-dss" #define SSH_SIGNKEY_DSS "ssh-dss"
#define SSH_SIGNKEY_DSS_LEN 7 #define SSH_SIGNKEY_DSS_LEN 7
#define SSH_SIGNKEY_RSA "ssh-rsa" #define SSH_SIGNKEY_RSA "ssh-rsa"
#define SSH_SIGNKEY_RSA_LEN 7 #define SSH_SIGNKEY_RSA_LEN 7
#define SSH_SIGNKEY_ED25519 "ssh-ed25519" #define SSH_SIGNKEY_ED25519 "ssh-ed25519"
#define SSH_SIGNKEY_ED25519_LEN 11 #define SSH_SIGNKEY_ED25519_LEN 11
/* signature type */
#define SSH_SIGNATURE_RSA_SHA256 "rsa-sha2-256"
/* Agent commands. These aren't part of the spec, and are defined /* Agent commands. These aren't part of the spec, and are defined
* only on the openssh implementation. */ * only on the openssh implementation. */

View File

@ -70,10 +70,10 @@
#define MIN_AUTHKEYS_LINE 10 /* "ssh-rsa AB" - short but doesn't matter */ #define MIN_AUTHKEYS_LINE 10 /* "ssh-rsa AB" - short but doesn't matter */
#define MAX_AUTHKEYS_LINE 4200 /* max length of a line in authkeys */ #define MAX_AUTHKEYS_LINE 4200 /* max length of a line in authkeys */
static int checkpubkey(const char* algo, unsigned int algolen, static int checkpubkey(const char* keyalgo, unsigned int keyalgolen,
const unsigned char* keyblob, unsigned int keybloblen); const unsigned char* keyblob, unsigned int keybloblen);
static int checkpubkeyperms(void); static int checkpubkeyperms(void);
static void send_msg_userauth_pk_ok(const char* algo, unsigned int algolen, static void send_msg_userauth_pk_ok(const char* sigalgo, unsigned int sigalgolen,
const unsigned char* keyblob, unsigned int keybloblen); const unsigned char* keyblob, unsigned int keybloblen);
static int checkfileperm(char * filename); static int checkfileperm(char * filename);
@ -82,16 +82,19 @@ static int checkfileperm(char * filename);
void svr_auth_pubkey(int valid_user) { void svr_auth_pubkey(int valid_user) {
unsigned char testkey; /* whether we're just checking if a key is usable */ unsigned char testkey; /* whether we're just checking if a key is usable */
char* algo = NULL; /* pubkey algo */ char* sigalgo = NULL;
unsigned int algolen; unsigned int sigalgolen;
const char* keyalgo;
unsigned int keyalgolen;
unsigned char* keyblob = NULL; unsigned char* keyblob = NULL;
unsigned int keybloblen; unsigned int keybloblen;
unsigned int sign_payload_length; unsigned int sign_payload_length;
buffer * signbuf = NULL; buffer * signbuf = NULL;
sign_key * key = NULL; sign_key * key = NULL;
char* fp = NULL; char* fp = NULL;
enum signkey_type type = -1; enum signature_type sigtype;
int auth_failure = 1; enum signkey_type keytype;
int auth_failure = 1;
TRACE(("enter pubkeyauth")) TRACE(("enter pubkeyauth"))
@ -99,7 +102,7 @@ void svr_auth_pubkey(int valid_user) {
* actual attempt*/ * actual attempt*/
testkey = (buf_getbool(ses.payload) == 0); testkey = (buf_getbool(ses.payload) == 0);
algo = buf_getstring(ses.payload, &algolen); sigalgo = buf_getstring(ses.payload, &sigalgolen);
keybloblen = buf_getint(ses.payload); keybloblen = buf_getint(ses.payload);
keyblob = buf_getptr(ses.payload, keybloblen); keyblob = buf_getptr(ses.payload, keybloblen);
@ -111,14 +114,24 @@ void svr_auth_pubkey(int valid_user) {
send_msg_userauth_failure(0, 0); send_msg_userauth_failure(0, 0);
goto out; goto out;
} }
sigtype = signature_type_from_name(sigalgo, sigalgolen);
if (sigtype == DROPBEAR_SIGNATURE_NONE) {
send_msg_userauth_failure(0, 0);
goto out;
}
keytype = signkey_type_from_signature(sigtype);
keyalgo = signkey_name_from_type(keytype, &keyalgolen);
#if DROPBEAR_PLUGIN #if DROPBEAR_PLUGIN
if (svr_ses.plugin_instance != NULL) { if (svr_ses.plugin_instance != NULL) {
char *options_buf; char *options_buf;
if (svr_ses.plugin_instance->checkpubkey( if (svr_ses.plugin_instance->checkpubkey(
svr_ses.plugin_instance, svr_ses.plugin_instance,
&ses.plugin_session, &ses.plugin_session,
algo, keyalgo,
algolen, keyalgolen,
keyblob, keyblob,
keybloblen, keybloblen,
ses.authstate.username) == DROPBEAR_SUCCESS) { ses.authstate.username) == DROPBEAR_SUCCESS) {
@ -146,7 +159,7 @@ void svr_auth_pubkey(int valid_user) {
#endif #endif
/* check if the key is valid */ /* check if the key is valid */
if (auth_failure) { if (auth_failure) {
auth_failure = checkpubkey(algo, algolen, keyblob, keybloblen) == DROPBEAR_FAILURE; auth_failure = checkpubkey(keyalgo, keyalgolen, keyblob, keybloblen) == DROPBEAR_FAILURE;
} }
if (auth_failure) { if (auth_failure) {
@ -156,7 +169,7 @@ void svr_auth_pubkey(int valid_user) {
/* let them know that the key is ok to use */ /* let them know that the key is ok to use */
if (testkey) { if (testkey) {
send_msg_userauth_pk_ok(algo, algolen, keyblob, keybloblen); send_msg_userauth_pk_ok(sigalgo, sigalgolen, keyblob, keybloblen);
goto out; goto out;
} }
@ -164,8 +177,7 @@ void svr_auth_pubkey(int valid_user) {
/* get the key */ /* get the key */
key = new_sign_key(); key = new_sign_key();
type = DROPBEAR_SIGNKEY_ANY; if (buf_get_pub_key(ses.payload, key, &keytype) == DROPBEAR_FAILURE) {
if (buf_get_pub_key(ses.payload, key, &type) == DROPBEAR_FAILURE) {
send_msg_userauth_failure(0, 1); send_msg_userauth_failure(0, 1);
goto out; goto out;
} }
@ -188,7 +200,7 @@ void svr_auth_pubkey(int valid_user) {
/* ... and finally verify the signature */ /* ... and finally verify the signature */
fp = sign_key_fingerprint(keyblob, keybloblen); fp = sign_key_fingerprint(keyblob, keybloblen);
if (buf_verify(ses.payload, key, signbuf) == DROPBEAR_SUCCESS) { if (buf_verify(ses.payload, key, sigtype, signbuf) == DROPBEAR_SUCCESS) {
dropbear_log(LOG_NOTICE, dropbear_log(LOG_NOTICE,
"Pubkey auth succeeded for '%s' with key %s from %s", "Pubkey auth succeeded for '%s' with key %s from %s",
ses.authstate.pw_name, fp, svr_ses.addrstring); ses.authstate.pw_name, fp, svr_ses.addrstring);
@ -213,8 +225,8 @@ out:
if (signbuf) { if (signbuf) {
buf_free(signbuf); buf_free(signbuf);
} }
if (algo) { if (sigalgo) {
m_free(algo); m_free(sigalgo);
} }
if (key) { if (key) {
sign_key_free(key); sign_key_free(key);
@ -230,14 +242,14 @@ out:
/* Reply that the key is valid for auth, this is sent when the user sends /* Reply that the key is valid for auth, this is sent when the user sends
* a straight copy of their pubkey to test, to avoid having to perform * a straight copy of their pubkey to test, to avoid having to perform
* expensive signing operations with a worthless key */ * expensive signing operations with a worthless key */
static void send_msg_userauth_pk_ok(const char* algo, unsigned int algolen, static void send_msg_userauth_pk_ok(const char* sigalgo, unsigned int sigalgolen,
const unsigned char* keyblob, unsigned int keybloblen) { const unsigned char* keyblob, unsigned int keybloblen) {
TRACE(("enter send_msg_userauth_pk_ok")) TRACE(("enter send_msg_userauth_pk_ok"))
CHECKCLEARTOWRITE(); CHECKCLEARTOWRITE();
buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_PK_OK); buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_PK_OK);
buf_putstring(ses.writepayload, algo, algolen); buf_putstring(ses.writepayload, sigalgo, sigalgolen);
buf_putstring(ses.writepayload, (const char*)keyblob, keybloblen); buf_putstring(ses.writepayload, (const char*)keyblob, keybloblen);
encrypt_packet(); encrypt_packet();
@ -354,7 +366,7 @@ out:
/* Checks whether a specified publickey (and associated algorithm) is an /* Checks whether a specified publickey (and associated algorithm) is an
* acceptable key for authentication */ * acceptable key for authentication */
/* Returns DROPBEAR_SUCCESS if key is ok for auth, DROPBEAR_FAILURE otherwise */ /* Returns DROPBEAR_SUCCESS if key is ok for auth, DROPBEAR_FAILURE otherwise */
static int checkpubkey(const char* algo, unsigned int algolen, static int checkpubkey(const char* keyalgo, unsigned int keyalgolen,
const unsigned char* keyblob, unsigned int keybloblen) { const unsigned char* keyblob, unsigned int keybloblen) {
FILE * authfile = NULL; FILE * authfile = NULL;
@ -368,14 +380,6 @@ static int checkpubkey(const char* algo, unsigned int algolen,
TRACE(("enter checkpubkey")) TRACE(("enter checkpubkey"))
/* check that we can use the algo */
if (have_algo(algo, algolen, sshhostkey) == DROPBEAR_FAILURE) {
dropbear_log(LOG_WARNING,
"Pubkey auth attempt with unknown algo for '%s' from %s",
ses.authstate.pw_name, svr_ses.addrstring);
goto out;
}
/* check file permissions, also whether file exists */ /* check file permissions, also whether file exists */
if (checkpubkeyperms() == DROPBEAR_FAILURE) { if (checkpubkeyperms() == DROPBEAR_FAILURE) {
TRACE(("bad authorized_keys permissions, or file doesn't exist")) TRACE(("bad authorized_keys permissions, or file doesn't exist"))
@ -427,7 +431,7 @@ static int checkpubkey(const char* algo, unsigned int algolen,
} }
line_num++; line_num++;
ret = checkpubkey_line(line, line_num, filename, algo, algolen, keyblob, keybloblen); ret = checkpubkey_line(line, line_num, filename, keyalgo, keyalgolen, keyblob, keybloblen);
if (ret == DROPBEAR_SUCCESS) { if (ret == DROPBEAR_SUCCESS) {
break; break;
} }

View File

@ -38,13 +38,15 @@
#include "gensignkey.h" #include "gensignkey.h"
static void send_msg_kexdh_reply(mp_int *dh_e, buffer *ecdh_qs); static void send_msg_kexdh_reply(mp_int *dh_e, buffer *ecdh_qs);
#if DROPBEAR_EXT_INFO
static void send_msg_ext_info(void);
#endif
/* Handle a diffie-hellman key exchange initialisation. This involves /* Handle a diffie-hellman key exchange initialisation. This involves
* calculating a session key reply value, and corresponding hash. These * calculating a session key reply value, and corresponding hash. These
* are carried out by send_msg_kexdh_reply(). recv_msg_kexdh_init() calls * are carried out by send_msg_kexdh_reply(). recv_msg_kexdh_init() calls
* that function, then brings the new keys into use */ * that function, then brings the new keys into use */
void recv_msg_kexdh_init() { void recv_msg_kexdh_init() {
DEF_MP_INT(dh_e); DEF_MP_INT(dh_e);
buffer *ecdh_qs = NULL; buffer *ecdh_qs = NULL;
@ -86,6 +88,14 @@ void recv_msg_kexdh_init() {
} }
send_msg_newkeys(); send_msg_newkeys();
#if DROPBEAR_EXT_INFO
/* Only send it following the first newkeys */
if (!ses.kexstate.donesecondkex && ses.allow_ext_info) {
send_msg_ext_info();
}
#endif
ses.requirenext = SSH_MSG_NEWKEYS; ses.requirenext = SSH_MSG_NEWKEYS;
TRACE(("leave recv_msg_kexdh_init")) TRACE(("leave recv_msg_kexdh_init"))
} }
@ -234,7 +244,7 @@ static void send_msg_kexdh_reply(mp_int *dh_e, buffer *ecdh_qs) {
/* calc the signature */ /* calc the signature */
buf_put_sign(ses.writepayload, svr_opts.hostkey, buf_put_sign(ses.writepayload, svr_opts.hostkey,
ses.newkeys->algo_hostkey, ses.hash); ses.newkeys->algo_signature, ses.hash);
/* the SSH_MSG_KEXDH_REPLY is done */ /* the SSH_MSG_KEXDH_REPLY is done */
encrypt_packet(); encrypt_packet();
@ -242,3 +252,20 @@ static void send_msg_kexdh_reply(mp_int *dh_e, buffer *ecdh_qs) {
TRACE(("leave send_msg_kexdh_reply")) TRACE(("leave send_msg_kexdh_reply"))
} }
#if DROPBEAR_EXT_INFO
/* Only used for server-sig-algs on the server side */
static void send_msg_ext_info(void) {
TRACE(("enter send_msg_ext_info"))
buf_putbyte(ses.writepayload, SSH_MSG_EXT_INFO);
/* nr-extensions */
buf_putint(ses.writepayload, 1);
buf_putstring(ses.writepayload, SSH_SERVER_SIG_ALGS, strlen(SSH_SERVER_SIG_ALGS));
buf_put_algolist_all(ses.writepayload, sigalgs, 1);
encrypt_packet();
TRACE(("leave send_msg_ext_info"))
}
#endif

View File

@ -273,7 +273,7 @@ static void main_noinetd() {
goto out; goto out;
} }
#ifdef DEBUG_NOFORK #if DEBUG_NOFORK
fork_ret = 0; fork_ret = 0;
#else #else
fork_ret = fork(); fork_ret = fork();

View File

@ -485,9 +485,9 @@ static void addportandaddress(const char* spec) {
static void disablekey(int type) { static void disablekey(int type) {
int i; int i;
TRACE(("Disabling key type %d", type)) TRACE(("Disabling key type %d", type))
for (i = 0; sshhostkey[i].name != NULL; i++) { for (i = 0; sigalgs[i].name != NULL; i++) {
if (sshhostkey[i].val == type) { if (sigalgs[i].val == type) {
sshhostkey[i].usable = 0; sigalgs[i].usable = 0;
break; break;
} }
} }
@ -567,7 +567,7 @@ static void addhostkey(const char *keyfile) {
void load_all_hostkeys() { void load_all_hostkeys() {
int i; int i;
int any_keys = 0; int any_keys = 0;
#ifdef DROPBEAR_ECDSA #if DROPBEAR_ECDSA
int loaded_any_ecdsa = 0; int loaded_any_ecdsa = 0;
#endif #endif

View File

@ -330,13 +330,18 @@ static void svr_remoteclosed() {
} }
static void svr_algos_initialise(void) { static void svr_algos_initialise(void) {
#if DROPBEAR_DH_GROUP1 && DROPBEAR_DH_GROUP1_CLIENTONLY
algo_type *algo; algo_type *algo;
for (algo = sshkex; algo->name; algo++) { for (algo = sshkex; algo->name; algo++) {
#if DROPBEAR_DH_GROUP1 && DROPBEAR_DH_GROUP1_CLIENTONLY
if (strcmp(algo->name, "diffie-hellman-group1-sha1") == 0) { if (strcmp(algo->name, "diffie-hellman-group1-sha1") == 0) {
algo->usable = 0; algo->usable = 0;
} }
}
#endif #endif
#if DROPBEAR_EXT_INFO
if (strcmp(algo->name, SSH_EXT_INFO_C) == 0) {
algo->usable = 0;
}
#endif
}
} }

View File

@ -143,9 +143,17 @@ If you test it please contact the Dropbear author */
* signing operations slightly slower. */ * signing operations slightly slower. */
#define DROPBEAR_RSA_BLINDING 1 #define DROPBEAR_RSA_BLINDING 1
#ifndef DROPBEAR_RSA_SHA1
#define DROPBEAR_RSA_SHA1 DROPBEAR_RSA
#endif
#ifndef DROPBEAR_RSA_SHA256
#define DROPBEAR_RSA_SHA256 DROPBEAR_RSA
#endif
/* hashes which will be linked and registered */ /* hashes which will be linked and registered */
#define DROPBEAR_SHA256 ((DROPBEAR_SHA2_256_HMAC) || (DROPBEAR_ECC_256) \ #define DROPBEAR_SHA256 ((DROPBEAR_SHA2_256_HMAC) || (DROPBEAR_ECC_256) \
|| (DROPBEAR_CURVE25519) || (DROPBEAR_DH_GROUP14_SHA256)) || (DROPBEAR_CURVE25519) || (DROPBEAR_DH_GROUP14_SHA256) \
|| (DROPBEAR_RSA_SHA256))
#define DROPBEAR_SHA384 (DROPBEAR_ECC_384) #define DROPBEAR_SHA384 (DROPBEAR_ECC_384)
/* LTC SHA384 depends on SHA512 */ /* LTC SHA384 depends on SHA512 */
#define DROPBEAR_SHA512 ((DROPBEAR_SHA2_512_HMAC) || (DROPBEAR_ECC_521) \ #define DROPBEAR_SHA512 ((DROPBEAR_SHA2_512_HMAC) || (DROPBEAR_ECC_521) \
@ -157,6 +165,10 @@ If you test it please contact the Dropbear author */
#define DROPBEAR_NORMAL_DH ((DROPBEAR_DH_GROUP1) || (DROPBEAR_DH_GROUP14) || (DROPBEAR_DH_GROUP16)) #define DROPBEAR_NORMAL_DH ((DROPBEAR_DH_GROUP1) || (DROPBEAR_DH_GROUP14) || (DROPBEAR_DH_GROUP16))
/* Dropbear only uses server-sig-algs, only needed if we have rsa-sha256 pubkey auth */
#define DROPBEAR_EXT_INFO ((DROPBEAR_RSA_SHA256) \
&& ((DROPBEAR_CLI_PUBKEY_AUTH) || (DROPBEAR_SVR_PUBKEY_AUTH)))
/* roughly 2x 521 bits */ /* roughly 2x 521 bits */
#define MAX_ECC_SIZE 140 #define MAX_ECC_SIZE 140