From 3d12521735e7ef7e48be217af0f27d68e23050a7 Mon Sep 17 00:00:00 2001 From: Vladislav Grishenko Date: Wed, 11 Mar 2020 21:09:45 +0500 Subject: Add Ed25519 support (#91) * Add support for Ed25519 as a public key type Ed25519 is a elliptic curve signature scheme that offers better security than ECDSA and DSA and good performance. It may be used for both user and host keys. OpenSSH key import and fuzzer are not supported yet. Initially inspired by Peter Szabo. * Add curve25519 and ed25519 fuzzers * Add import and export of Ed25519 keys --- cli-kex.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'cli-kex.c') diff --git a/cli-kex.c b/cli-kex.c index b02cfc7..7cefb5f 100644 --- a/cli-kex.c +++ b/cli-kex.c @@ -81,7 +81,7 @@ void send_msg_kexdh_init() { } cli_ses.curve25519_param = gen_kexcurve25519_param(); } - buf_putstring(ses.writepayload, (const char*)cli_ses.curve25519_param->pub, CURVE25519_LEN); + buf_putstring(ses.writepayload, cli_ses.curve25519_param->pub, CURVE25519_LEN); break; #endif } -- cgit v1.2.3 From 7dc2f36c3e2d21455ae432da4d8f338e7dc0668c Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Mon, 6 Apr 2020 23:18:26 +0800 Subject: use sigtype where appropriate --- algo.h | 1 - cli-authpubkey.c | 34 ++++++++++++------- cli-kex.c | 11 +++--- common-algo.c | 23 +++---------- common-kex.c | 9 +++-- dss.h | 2 +- ed25519.h | 2 +- fuzzer-pubkey.c | 2 +- keyimport.c | 3 ++ rsa.c | 101 ++++++++++++++++++++++++++++++++++++------------------- rsa.h | 11 +++--- session.h | 3 +- signkey.c | 78 ++++++++++++++++++++++++++++++++---------- signkey.h | 24 ++++++++----- ssh.h | 4 ++- svr-authpubkey.c | 53 ++++++++++++++--------------- svr-kex.c | 2 +- sysoptions.h | 12 +++++-- 18 files changed, 233 insertions(+), 142 deletions(-) (limited to 'cli-kex.c') diff --git a/algo.h b/algo.h index b12fb94..1a6d384 100644 --- a/algo.h +++ b/algo.h @@ -112,7 +112,6 @@ struct dropbear_kex { const struct ltc_hash_descriptor *hash_desc; }; -int have_algo(const char* algo, size_t algolen, const algo_type algos[]); void buf_put_algolist(buffer * buf, const algo_type localalgos[]); enum kexguess2_used { diff --git a/cli-authpubkey.c b/cli-authpubkey.c index 7cee164..d1cc1f6 100644 --- a/cli-authpubkey.c +++ b/cli-authpubkey.c @@ -33,7 +33,7 @@ #include "agentfwd.h" #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 signkey_type sigtype, int realsign); /* 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 */ @@ -58,14 +58,15 @@ void recv_msg_userauth_pk_ok() { buffer* keybuf = NULL; char* algotype = NULL; unsigned int algolen; - enum signkey_type keytype; + enum signkey_type sigtype, keytype; unsigned int remotelen; TRACE(("enter recv_msg_userauth_pk_ok")) algotype = buf_getstring(ses.payload, &algolen); - keytype = signkey_type_from_name(algotype, algolen); - TRACE(("recv_msg_userauth_pk_ok: type %d", keytype)) + sigtype = signature_type_from_name(algotype, algolen); + keytype = signkey_type_from_signature(sigtype); + TRACE(("recv_msg_userauth_pk_ok: type %d", sigtype)) m_free(algotype); keybuf = buf_new(MAX_PUBKEY_SIZE); @@ -120,9 +121,10 @@ void 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 signkey_type sigtype, const buffer *data_buf) { #if DROPBEAR_CLI_AGENTFWD + // TODO: rsa-sha256 agent if (key->source == SIGNKEY_SOURCE_AGENT) { /* Format the agent signature ourselves, as buf_put_sign would. */ buffer *sigblob; @@ -133,16 +135,16 @@ void cli_buf_put_sign(buffer* buf, sign_key *key, int type, } else #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, int type, int realsign) { +static void send_msg_userauth_pubkey(sign_key *key, enum signkey_type sigtype, int realsign) { const char *algoname = NULL; unsigned int algolen; buffer* sigbuf = NULL; + enum signkey_type keytype = signkey_type_from_signature(sigtype); TRACE(("enter send_msg_userauth_pubkey")) CHECKCLEARTOWRITE(); @@ -160,10 +162,9 @@ static void send_msg_userauth_pubkey(sign_key *key, int type, int 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_put_pub_key(ses.writepayload, key, type); + buf_put_pub_key(ses.writepayload, key, keytype); if (realsign) { TRACE(("realsign")) @@ -172,7 +173,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); buf_putbufstring(sigbuf, ses.session_id); 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 */ } @@ -195,8 +196,15 @@ int cli_auth_pubkey() { if (cli_opts.privkeys->first) { sign_key * key = (sign_key*)cli_opts.privkeys->first->item; + enum signkey_type sigtype = key->type; /* Send a trial request */ - send_msg_userauth_pubkey(key, key->type, 0); +#if DROPBEAR_RSA && DROPBEAR_RSA_SHA256 + // TODO: use ext-info to choose rsa kind + if (sigtype == DROPBEAR_SIGNKEY_RSA) { + sigtype = DROPBEAR_SIGNKEY_RSA_SHA256; + } +#endif + send_msg_userauth_pubkey(key, sigtype, 0); cli_ses.lastprivkey = key; TRACE(("leave cli_auth_pubkey-success")) return 1; diff --git a/cli-kex.c b/cli-kex.c index 7cefb5f..7337ffd 100644 --- a/cli-kex.c +++ b/cli-kex.c @@ -94,7 +94,7 @@ void send_msg_kexdh_init() { void recv_msg_kexdh_reply() { sign_key *hostkey = NULL; - unsigned int type, keybloblen; + unsigned int keytype, keybloblen; unsigned char* keyblob = NULL; TRACE(("enter recv_msg_kexdh_reply")) @@ -102,8 +102,8 @@ void recv_msg_kexdh_reply() { if (cli_ses.kex_state != KEXDH_INIT_SENT) { dropbear_exit("Received out-of-order kexdhreply"); } - type = ses.newkeys->algo_hostkey; - TRACE(("type is %d", type)) + keytype = ses.newkeys->algo_hostkey; + TRACE(("keytype is %d", keytype)) hostkey = new_sign_key(); keybloblen = buf_getint(ses.payload); @@ -114,7 +114,7 @@ void recv_msg_kexdh_reply() { 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")) dropbear_exit("Bad KEX packet"); } @@ -173,7 +173,8 @@ void recv_msg_kexdh_reply() { #endif 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"); } diff --git a/common-algo.c b/common-algo.c index 558aad2..28f6744 100644 --- a/common-algo.c +++ b/common-algo.c @@ -237,8 +237,13 @@ algo_type sshhostkey[] = { #endif #endif #if DROPBEAR_RSA +#if DROPBEAR_RSA_SHA256 + {"rsa-sha2-256", DROPBEAR_SIGNKEY_RSA_SHA256, NULL, 1, NULL}, +#endif +#if DROPBEAR_RSA_SHA1 {"ssh-rsa", DROPBEAR_SIGNKEY_RSA, NULL, 1, NULL}, #endif +#endif #if DROPBEAR_DSS {"ssh-dss", DROPBEAR_SIGNKEY_DSS, NULL, 1, NULL}, #endif @@ -311,24 +316,6 @@ algo_type sshkex[] = { {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 */ void buf_put_algolist(buffer * buf, const algo_type localalgos[]) { diff --git a/common-kex.c b/common-kex.c index 16b7e27..c1392c1 100644 --- a/common-kex.c +++ b/common-kex.c @@ -111,7 +111,8 @@ void send_msg_kexinit() { if (ses.send_kex_first_guess) { ses.newkeys->algo_kex = sshkex[0].data; - ses.newkeys->algo_hostkey = sshhostkey[0].val; + ses.newkeys->algo_signature = sshhostkey[0].val; + ses.newkeys->algo_hostkey = signkey_type_from_signature(ses.newkeys->algo_signature); ses.send_kex_first_guess(); } @@ -152,6 +153,7 @@ static void switch_keys() { TRACE(("switch_keys done")) ses.keys->algo_kex = ses.newkeys->algo_kex; ses.keys->algo_hostkey = ses.newkeys->algo_hostkey; + ses.keys->algo_signature = ses.newkeys->algo_signature; ses.keys->allow_compress = 0; m_free(ses.newkeys); ses.newkeys = NULL; @@ -847,8 +849,9 @@ static void read_kex_algos() { erralgo = "hostkey"; goto error; } - TRACE(("hostkey algo %s", algo->name)) - ses.newkeys->algo_hostkey = algo->val; + TRACE(("signature algo %s", algo->name)) + ses.newkeys->algo_signature = algo->val; + ses.newkeys->algo_hostkey = signkey_type_from_signature(ses.newkeys->algo_signature); /* encryption_algorithms_client_to_server */ c2s_cipher_algo = buf_match_algo(ses.payload, sshciphers, NULL, NULL); diff --git a/dss.h b/dss.h index 2eaa2f5..40806e5 100644 --- a/dss.h +++ b/dss.h @@ -30,7 +30,7 @@ #if DROPBEAR_DSS -typedef struct { +typedef struct dropbear_DSS_Key { mp_int* p; mp_int* q; diff --git a/ed25519.h b/ed25519.h index 16c0d7b..622111a 100644 --- a/ed25519.h +++ b/ed25519.h @@ -32,7 +32,7 @@ #define CURVE25519_LEN 32 -typedef struct { +typedef struct dropbear_ED25519_Key { unsigned char priv[CURVE25519_LEN]; unsigned char pub[CURVE25519_LEN]; diff --git a/fuzzer-pubkey.c b/fuzzer-pubkey.c index 033f496..7c12cdc 100644 --- a/fuzzer-pubkey.c +++ b/fuzzer-pubkey.c @@ -27,7 +27,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { unsigned int 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"); } diff --git a/keyimport.c b/keyimport.c index ad0c530..52ddf6e 100644 --- a/keyimport.c +++ b/keyimport.c @@ -36,6 +36,9 @@ #include "dbutil.h" #include "ecc.h" #include "ssh.h" +#include "rsa.h" +#include "dss.h" +#include "ed25519.h" static const unsigned char OSSH_PKEY_BLOB[] = "openssh-key-v1\0" /* AUTH_MAGIC */ diff --git a/rsa.c b/rsa.c index 67b90f7..ef737c8 100644 --- a/rsa.c +++ b/rsa.c @@ -35,11 +35,16 @@ #include "buffer.h" #include "ssh.h" #include "dbrandom.h" +#include "signkey.h" #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, - const buffer *data_buf, mp_int * rsa_em); + const buffer *data_buf, mp_int * rsa_em, enum signkey_type sigtype); /* Load a public rsa key from a buffer, initialising the values. * 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 /* Verify a signature in buf, made on data by the key given. * 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 signkey_type sigtype, const buffer *data_buf) { unsigned int slen; DEF_MP_INT(rsa_s); 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 */ - 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) { TRACE(("failed exptmod rsa_s")) @@ -246,8 +252,10 @@ out: /* Sign the data presented with key, writing the signature contents * to the buffer */ -void buf_put_rsa_sign(buffer* buf, const dropbear_rsa_key *key, const buffer *data_buf) { - unsigned int nsize, ssize; +void buf_put_rsa_sign(buffer* buf, const dropbear_rsa_key *key, + enum signkey_type sigtype, const buffer *data_buf) { + const char *name = NULL; + unsigned int nsize, ssize, namelen = 0; unsigned int i; DEF_MP_INT(rsa_s); 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); - rsa_pad_em(key, data_buf, &rsa_tmp1); + rsa_pad_em(key, data_buf, &rsa_tmp1, sigtype); /* 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); /* 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); @@ -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")) } -/* Creates the message value as expected by PKCS, see rfc2437 etc */ -/* format to be padded to is: - * 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. - */ +/* Creates the message value as expected by PKCS, + see rfc8017 section 9.2 */ 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 signkey_type sigtype) { + /* EM = 0x00 || 0x01 || PS || 0x00 || T + PS is padding of 0xff to make EM the size of key->n + + T is the DER encoding of the hash alg (sha1 or sha256) + */ - /* ASN1 designator (including the 0x00 preceding) */ - 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}; - 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; hash_state hs; unsigned int nsize; + + switch (sigtype) { +#if DROPBEAR_RSA_SHA1 + case DROPBEAR_SIGNKEY_RSA: + Tlen = sizeof(T_sha1); + T = T_sha1; + hash_desc = &sha1_desc; + break; +#endif +#if DROPBEAR_RSA_SHA256 + case DROPBEAR_SIGNKEY_RSA_SHA256: + Tlen = sizeof(T_sha256); + T = T_sha256; + hash_desc = &sha256_desc; + break; +#endif + default: + assert(0); + } - dropbear_assert(key != NULL); nsize = mp_unsigned_bin_size(key->n); - rsa_EM = buf_new(nsize-1); + rsa_EM = buf_new(nsize); /* type byte */ + buf_putbyte(rsa_EM, 0x00); buf_putbyte(rsa_EM, 0x01); - /* Padding with 0xFF bytes */ - while(rsa_EM->pos != rsa_EM->size - RSA_ASN1_MAGIC_LEN - SHA1_HASH_SIZE) { + /* Padding with PS 0xFF bytes */ + while(rsa_EM->pos != rsa_EM->size - (1 + Tlen + hash_desc->hashsize)) { buf_putbyte(rsa_EM, 0xff); } + buf_putbyte(rsa_EM, 0x00); /* Magic ASN1 stuff */ - memcpy(buf_getwriteptr(rsa_EM, RSA_ASN1_MAGIC_LEN), - rsa_asn1_magic, RSA_ASN1_MAGIC_LEN); - buf_incrwritepos(rsa_EM, RSA_ASN1_MAGIC_LEN); + buf_putbytes(rsa_EM, T, Tlen); /* The hash of the data */ - sha1_init(&hs); - sha1_process(&hs, data_buf->data, data_buf->len); - sha1_done(&hs, buf_getwriteptr(rsa_EM, SHA1_HASH_SIZE)); - buf_incrwritepos(rsa_EM, SHA1_HASH_SIZE); + hash_desc->init(&hs); + hash_desc->process(&hs, data_buf->data, data_buf->len); + hash_desc->done(&hs, buf_getwriteptr(rsa_EM, hash_desc->hashsize)); + buf_incrwritepos(rsa_EM, hash_desc->hashsize); dropbear_assert(rsa_EM->pos == rsa_EM->size); diff --git a/rsa.h b/rsa.h index 4fee2e6..39e6004 100644 --- a/rsa.h +++ b/rsa.h @@ -26,13 +26,12 @@ #define DROPBEAR_RSA_H_ #include "includes.h" +#include "signkey.h" #include "buffer.h" #if DROPBEAR_RSA -#define RSA_SIGNATURE_SIZE (4+7+4+40) - -typedef struct { +typedef struct dropbear_RSA_Key { mp_int* n; mp_int* e; @@ -43,9 +42,11 @@ typedef struct { } 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 signkey_type sigtype, const buffer *data_buf); #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 signkey_type sigtype, const buffer *data_buf); #endif int buf_get_rsa_pub_key(buffer* buf, dropbear_rsa_key *key); int buf_get_rsa_priv_key(buffer* buf, dropbear_rsa_key *key); diff --git a/session.h b/session.h index e436882..ebfc915 100644 --- a/session.h +++ b/session.h @@ -92,7 +92,8 @@ struct key_context { struct key_context_directional trans; const struct dropbear_kex *algo_kex; - int algo_hostkey; + int algo_hostkey; /* server key type */ + int algo_signature; /* server signature type */ int allow_compress; /* whether compression has started (useful in zlib@openssh.com delayed compression case) */ diff --git a/signkey.c b/signkey.c index a0af44a..27b09a3 100644 --- a/signkey.c +++ b/signkey.c @@ -28,6 +28,9 @@ #include "buffer.h" #include "ssh.h" #include "ecdsa.h" +#include "rsa.h" +#include "dss.h" +#include "ed25519.h" static const char * const signkey_names[DROPBEAR_SIGNKEY_NUM_NAMED] = { #if DROPBEAR_RSA @@ -44,6 +47,7 @@ static const char * const signkey_names[DROPBEAR_SIGNKEY_NUM_NAMED] = { #if DROPBEAR_ED25519 "ssh-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 */ @@ -105,6 +109,38 @@ enum signkey_type signkey_type_from_name(const char* name, unsigned int namelen) 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 signkey_type type, unsigned int *namelen) { +#if DROPBEAR_RSA_SHA256 + if (type == DROPBEAR_SIGNKEY_RSA_SHA256) { + *namelen = strlen(SSH_SIGNKEY_RSA_SHA256); + return SSH_SIGNKEY_RSA_SHA256; + } +#endif + return signkey_name_from_type(type, namelen); +} + +enum signkey_type signature_type_from_name(const char* name, unsigned int namelen) { +#if DROPBEAR_RSA_SHA256 + if (namelen == strlen(SSH_SIGNKEY_RSA_SHA256) + && memcmp(name, SSH_SIGNKEY_RSA_SHA256, namelen) == 0) { + return DROPBEAR_SIGNKEY_RSA_SHA256; + } +#endif + return signkey_type_from_name(name, namelen); +} + +enum signkey_type signkey_type_from_signature(enum signkey_type sigtype) { +#if DROPBEAR_RSA_SHA256 + if (sigtype == DROPBEAR_SIGNKEY_RSA_SHA256) { + return DROPBEAR_SIGNKEY_RSA; + } +#endif + assert(sigtype < DROPBEAR_SIGNKEY_NUM_NAMED); + return sigtype; +} + /* Returns a pointer to the key part specific to "type". Be sure to check both (ret != NULL) and (*ret != NULL) */ void ** @@ -526,31 +562,31 @@ char * sign_key_fingerprint(const unsigned char* keyblob, unsigned int keybloble #endif } -void buf_put_sign(buffer* buf, sign_key *key, enum signkey_type type, +void buf_put_sign(buffer* buf, sign_key *key, enum signkey_type sigtype, const buffer *data_buf) { - buffer *sigblob; - sigblob = buf_new(MAX_PUBKEY_SIZE); + buffer *sigblob = buf_new(MAX_PUBKEY_SIZE); + enum signkey_type keytype = signkey_type_from_signature(sigtype); #if DROPBEAR_DSS - if (type == DROPBEAR_SIGNKEY_DSS) { + if (keytype == DROPBEAR_SIGNKEY_DSS) { buf_put_dss_sign(sigblob, key->dsskey, data_buf); } #endif #if DROPBEAR_RSA - if (type == DROPBEAR_SIGNKEY_RSA) { - buf_put_rsa_sign(sigblob, key->rsakey, data_buf); + if (keytype == DROPBEAR_SIGNKEY_RSA) { + buf_put_rsa_sign(sigblob, key->rsakey, sigtype, data_buf); } #endif #if DROPBEAR_ECDSA - if (signkey_is_ecdsa(type)) { - ecc_key **eck = (ecc_key**)signkey_key_ptr(key, type); + if (signkey_is_ecdsa(keytype)) { + ecc_key **eck = (ecc_key**)signkey_key_ptr(key, keytype); if (eck && *eck) { buf_put_ecdsa_sign(sigblob, *eck, data_buf); } } #endif #if DROPBEAR_ED25519 - if (type == DROPBEAR_SIGNKEY_ED25519) { + if (keytype == DROPBEAR_SIGNKEY_ED25519) { buf_put_ed25519_sign(sigblob, key->ed25519key, data_buf); } #endif @@ -567,21 +603,27 @@ void buf_put_sign(buffer* buf, sign_key *key, enum signkey_type type, * If FAILURE is returned, the position of * buf is undefined. If SUCCESS is returned, buf will be positioned after the * signature blob */ -int buf_verify(buffer * buf, sign_key *key, const buffer *data_buf) { +int buf_verify(buffer * buf, sign_key *key, enum signkey_type expect_sigtype, const buffer *data_buf) { char *type_name = NULL; unsigned int type_name_len = 0; - enum signkey_type type; + enum signkey_type sigtype, keytype; TRACE(("enter buf_verify")) buf_getint(buf); /* blob length */ 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); + if (expect_sigtype != DROPBEAR_SIGNKEY_ANY + && expect_sigtype != sigtype) { + dropbear_exit("Non-matching signing type"); + } + + keytype = signkey_type_from_signature(sigtype); #if DROPBEAR_DSS - if (type == DROPBEAR_SIGNKEY_DSS) { + if (keytype == DROPBEAR_SIGNKEY_DSS) { if (key->dsskey == NULL) { dropbear_exit("No DSS key to verify signature"); } @@ -590,23 +632,23 @@ int buf_verify(buffer * buf, sign_key *key, const buffer *data_buf) { #endif #if DROPBEAR_RSA - if (type == DROPBEAR_SIGNKEY_RSA) { + if (keytype == DROPBEAR_SIGNKEY_RSA) { if (key->rsakey == NULL) { 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 #if DROPBEAR_ECDSA - if (signkey_is_ecdsa(type)) { - ecc_key **eck = (ecc_key**)signkey_key_ptr(key, type); + if (signkey_is_ecdsa(keytype)) { + ecc_key **eck = (ecc_key**)signkey_key_ptr(key, keytype); if (eck && *eck) { return buf_ecdsa_verify(buf, *eck, data_buf); } } #endif #if DROPBEAR_ED25519 - if (type == DROPBEAR_SIGNKEY_ED25519) { + if (keytype == DROPBEAR_SIGNKEY_ED25519) { if (key->ed25519key == NULL) { dropbear_exit("No Ed25519 key to verify signature"); } diff --git a/signkey.h b/signkey.h index fa39a02..78b237b 100644 --- a/signkey.h +++ b/signkey.h @@ -26,9 +26,11 @@ #define DROPBEAR_SIGNKEY_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; enum signkey_type { #if DROPBEAR_RSA @@ -47,6 +49,9 @@ enum signkey_type { #endif DROPBEAR_SIGNKEY_NUM_NAMED, DROPBEAR_SIGNKEY_ECDSA_KEYGEN = 70, /* just "ecdsa" for keygen */ +#if DROPBEAR_RSA_SHA256 + DROPBEAR_SIGNKEY_RSA_SHA256, /* rsa-sha2-256 signature. has a ssh-rsa key */ +#endif DROPBEAR_SIGNKEY_ANY = 80, DROPBEAR_SIGNKEY_NONE = 90, }; @@ -66,10 +71,10 @@ struct SIGN_key { char *filename; #if DROPBEAR_DSS - dropbear_dss_key * dsskey; + struct dropbear_DSS_Key * dsskey; #endif #if DROPBEAR_RSA - dropbear_rsa_key * rsakey; + struct dropbear_RSA_Key * rsakey; #endif #if DROPBEAR_ECDSA #if DROPBEAR_ECC_256 @@ -83,7 +88,7 @@ struct SIGN_key { #endif #endif #if DROPBEAR_ED25519 - dropbear_ed25519_key * ed25519key; + struct dropbear_ED25519_Key * ed25519key; #endif }; @@ -92,14 +97,17 @@ typedef struct SIGN_key sign_key; sign_key * new_sign_key(void); 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); +const char* signature_name_from_type(enum signkey_type type, unsigned int *namelen); +enum signkey_type signature_type_from_name(const char* name, unsigned int namelen); +enum signkey_type signkey_type_from_signature(enum signkey_type sigtype); 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); 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 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 signkey_type sigtype, const buffer *data_buf); #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 signkey_type type, const buffer *data_buf); char * sign_key_fingerprint(const unsigned char* keyblob, unsigned int keybloblen); #endif int cmp_base64_key(const unsigned char* keyblob, unsigned int keybloblen, diff --git a/ssh.h b/ssh.h index db723b8..fb7f9f2 100644 --- a/ssh.h +++ b/ssh.h @@ -100,13 +100,15 @@ #define SSH_SERVICE_CONNECTION "ssh-connection" #define SSH_SERVICE_CONNECTION_LEN 14 -/* public key types */ +/* public/signature key types */ #define SSH_SIGNKEY_DSS "ssh-dss" #define SSH_SIGNKEY_DSS_LEN 7 #define SSH_SIGNKEY_RSA "ssh-rsa" #define SSH_SIGNKEY_RSA_LEN 7 #define SSH_SIGNKEY_ED25519 "ssh-ed25519" #define SSH_SIGNKEY_ED25519_LEN 11 +/* signature type */ +#define SSH_SIGNKEY_RSA_SHA256 "rsa-sha2-256" /* Agent commands. These aren't part of the spec, and are defined * only on the openssh implementation. */ diff --git a/svr-authpubkey.c b/svr-authpubkey.c index 5c234b9..a52667c 100644 --- a/svr-authpubkey.c +++ b/svr-authpubkey.c @@ -70,10 +70,10 @@ #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 */ -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); 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); static int checkfileperm(char * filename); @@ -82,16 +82,18 @@ static int checkfileperm(char * filename); void svr_auth_pubkey(int valid_user) { unsigned char testkey; /* whether we're just checking if a key is usable */ - char* algo = NULL; /* pubkey algo */ - unsigned int algolen; + char* sigalgo = NULL; + unsigned int sigalgolen; + const char* keyalgo; + unsigned int keyalgolen; unsigned char* keyblob = NULL; unsigned int keybloblen; unsigned int sign_payload_length; buffer * signbuf = NULL; sign_key * key = NULL; char* fp = NULL; - enum signkey_type type = -1; - int auth_failure = 1; + enum signkey_type sigtype, keytype; + int auth_failure = 1; TRACE(("enter pubkeyauth")) @@ -99,7 +101,11 @@ void svr_auth_pubkey(int valid_user) { * actual attempt*/ testkey = (buf_getbool(ses.payload) == 0); - algo = buf_getstring(ses.payload, &algolen); + sigalgo = buf_getstring(ses.payload, &sigalgolen); + sigtype = signature_type_from_name(sigalgo, sigalgolen); + keytype = signkey_type_from_signature(sigtype); + keyalgo = signkey_name_from_type(keytype, &keyalgolen); + keybloblen = buf_getint(ses.payload); keyblob = buf_getptr(ses.payload, keybloblen); @@ -117,8 +123,8 @@ void svr_auth_pubkey(int valid_user) { if (svr_ses.plugin_instance->checkpubkey( svr_ses.plugin_instance, &ses.plugin_session, - algo, - algolen, + keyalgo, + keyalgolen, keyblob, keybloblen, ses.authstate.username) == DROPBEAR_SUCCESS) { @@ -146,7 +152,7 @@ void svr_auth_pubkey(int valid_user) { #endif /* check if the key is valid */ if (auth_failure) { - auth_failure = checkpubkey(algo, algolen, keyblob, keybloblen) == DROPBEAR_FAILURE; + auth_failure = checkpubkey(keyalgo, keyalgolen, keyblob, keybloblen) == DROPBEAR_FAILURE; } if (auth_failure) { @@ -156,7 +162,7 @@ void svr_auth_pubkey(int valid_user) { /* let them know that the key is ok to use */ if (testkey) { - send_msg_userauth_pk_ok(algo, algolen, keyblob, keybloblen); + send_msg_userauth_pk_ok(sigalgo, sigalgolen, keyblob, keybloblen); goto out; } @@ -164,8 +170,7 @@ void svr_auth_pubkey(int valid_user) { /* get the key */ key = new_sign_key(); - type = DROPBEAR_SIGNKEY_ANY; - if (buf_get_pub_key(ses.payload, key, &type) == DROPBEAR_FAILURE) { + if (buf_get_pub_key(ses.payload, key, &keytype) == DROPBEAR_FAILURE) { send_msg_userauth_failure(0, 1); goto out; } @@ -188,7 +193,7 @@ void svr_auth_pubkey(int valid_user) { /* ... and finally verify the signature */ 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, "Pubkey auth succeeded for '%s' with key %s from %s", ses.authstate.pw_name, fp, svr_ses.addrstring); @@ -213,8 +218,8 @@ out: if (signbuf) { buf_free(signbuf); } - if (algo) { - m_free(algo); + if (sigalgo) { + m_free(sigalgo); } if (key) { sign_key_free(key); @@ -230,14 +235,14 @@ out: /* 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 * 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) { TRACE(("enter send_msg_userauth_pk_ok")) CHECKCLEARTOWRITE(); 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); encrypt_packet(); @@ -354,7 +359,7 @@ out: /* Checks whether a specified publickey (and associated algorithm) is an * acceptable key for authentication */ /* 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) { FILE * authfile = NULL; @@ -368,14 +373,6 @@ static int checkpubkey(const char* algo, unsigned int algolen, 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 */ if (checkpubkeyperms() == DROPBEAR_FAILURE) { TRACE(("bad authorized_keys permissions, or file doesn't exist")) @@ -427,7 +424,7 @@ static int checkpubkey(const char* algo, unsigned int algolen, } 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) { break; } diff --git a/svr-kex.c b/svr-kex.c index af16d2f..e3e63d0 100644 --- a/svr-kex.c +++ b/svr-kex.c @@ -234,7 +234,7 @@ static void send_msg_kexdh_reply(mp_int *dh_e, buffer *ecdh_qs) { /* calc the signature */ 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 */ encrypt_packet(); diff --git a/sysoptions.h b/sysoptions.h index 2c27caf..0f52431 100644 --- a/sysoptions.h +++ b/sysoptions.h @@ -139,9 +139,17 @@ If you test it please contact the Dropbear author */ * signing operations slightly slower. */ #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 */ -#define DROPBEAR_SHA256 ((DROPBEAR_SHA2_256_HMAC) || (DROPBEAR_ECC_256) \ - || (DROPBEAR_CURVE25519) || (DROPBEAR_DH_GROUP14_SHA256)) +#define DROPBEAR_SHA256 ((DROPBEAR_SHA2_256_HMAC) || (DROPBEAR_ECC_256) \ + || (DROPBEAR_CURVE25519) || (DROPBEAR_DH_GROUP14_SHA256) \ + || (DROPBEAR_RSA_SHA256)) #define DROPBEAR_SHA384 (DROPBEAR_ECC_384) /* LTC SHA384 depends on SHA512 */ #define DROPBEAR_SHA512 ((DROPBEAR_SHA2_512_HMAC) || (DROPBEAR_ECC_521) \ -- cgit v1.2.3 From 5acee497bf3e44d86bdecf5bfa35042c363ab753 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Tue, 19 May 2020 00:31:41 +0800 Subject: ext-info handling for server-sig-algs only client side is handled --- algo.h | 11 +---- buffer.c | 22 +++++++++- buffer.h | 1 + cli-authpubkey.c | 78 +++++++++++++++++++++++++++++------- cli-kex.c | 25 ++++++++++++ cli-main.c | 1 + cli-session.c | 5 ++- common-algo.c | 119 ++++++++++++++++++++++++++++++++++++------------------- common-kex.c | 30 +++++++++----- fuzzer-verify.c | 4 +- kex.h | 2 + session.h | 11 ++--- signkey.c | 21 ++++++++-- signkey.h | 2 + ssh.h | 6 +++ svr-session.c | 7 +++- 16 files changed, 255 insertions(+), 90 deletions(-) (limited to 'cli-kex.c') diff --git a/algo.h b/algo.h index 1a6d384..1565665 100644 --- a/algo.h +++ b/algo.h @@ -114,18 +114,11 @@ struct dropbear_kex { 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_ID 99 - +int buf_has_algo(buffer *buf, const char *algo); 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 int check_user_algos(const char* user_algo_list, algo_type * algos, diff --git a/buffer.c b/buffer.c index e70ec29..dc8b909 100644 --- a/buffer.c +++ b/buffer.c @@ -228,19 +228,37 @@ char* buf_getstring(buffer* buf, unsigned int *retlen) { } /* 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; unsigned int len = buf_getint(buf); + int extra = 0; if (len > MAX_STRING_LEN) { 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); buf_incrpos(buf, len); buf_incrlen(ret, len); + buf_setpos(ret, 0); 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, * but don't bother copying/malloc()ing for it */ void buf_eatstring(buffer *buf) { diff --git a/buffer.h b/buffer.h index befe1c7..85f913d 100644 --- a/buffer.h +++ b/buffer.h @@ -58,6 +58,7 @@ unsigned char* buf_getptr(const buffer* buf, unsigned int len); unsigned char* buf_getwriteptr(const buffer* buf, unsigned int len); char* buf_getstring(buffer* buf, unsigned int *retlen); buffer * buf_getstringbuf(buffer *buf); +buffer * buf_getbuf(buffer *buf); void buf_eatstring(buffer *buf); void buf_putint(buffer* buf, unsigned int val); void buf_putstring(buffer* buf, const char* str, unsigned int len); diff --git a/cli-authpubkey.c b/cli-authpubkey.c index 0ad80ea..8426e84 100644 --- a/cli-authpubkey.c +++ b/cli-authpubkey.c @@ -184,6 +184,7 @@ static void send_msg_userauth_pubkey(sign_key *key, enum signature_type sigtype, /* Returns 1 if a key was tried */ int cli_auth_pubkey() { + enum signature_type sigtype; TRACE(("enter cli_auth_pubkey")) #if DROPBEAR_CLI_AGENTFWD @@ -191,28 +192,77 @@ int cli_auth_pubkey() { /* get the list of available keys from the agent */ cli_load_agent_keys(cli_opts.privkeys); cli_opts.agent_keys_loaded = 1; + TRACE(("cli_auth_pubkey: agent keys loaded")) } #endif - /* TODO iterate through privkeys to skip ones not in server-sig-algs */ - - /* TODO: testing */ + /* 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) { +#ifdef DROPBEAR_RSA + if (key->type == DROPBEAR_SIGNKEY_RSA) { #if DROPBEAR_RSA_SHA256 - cli_ses.preferred_rsa_sigtype = DROPBEAR_SIGNATURE_RSA_SHA256; -#elif DROPBEAR_RSA_SHA1 - cli_ses.preferred_rsa_sigtype = DROPBEAR_SIGNATURE_RSA_SHA1; + 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) { +#ifdef 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) { sign_key * key = (sign_key*)cli_opts.privkeys->first->item; - /* Determine the signature type to use */ - enum signature_type sigtype = (enum signature_type)key->type; -#if DROPBEAR_RSA - if (key->type == DROPBEAR_SIGNKEY_RSA) { - sigtype = cli_ses.preferred_rsa_sigtype; - } -#endif - /* Send a trial request */ send_msg_userauth_pubkey(key, sigtype, 0); cli_ses.lastprivkey = key; diff --git a/cli-kex.c b/cli-kex.c index 7337ffd..99370eb 100644 --- a/cli-kex.c +++ b/cli-kex.c @@ -411,3 +411,28 @@ out: } 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; + + 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); + } +} diff --git a/cli-main.c b/cli-main.c index 713cb09..b43b1e2 100644 --- a/cli-main.c +++ b/cli-main.c @@ -106,6 +106,7 @@ static void cli_dropbear_exit(int exitcode, const char* format, va_list param) { /* Render the formatted exit message */ vsnprintf(exitmsg, sizeof(exitmsg), format, param); + TRACE(("Exited, cleaning up: %s", exitmsg)) /* Add the prefix depending on session/auth state */ if (!ses.init_done) { diff --git a/cli-session.c b/cli-session.c index 56dd4af..fc81e2d 100644 --- a/cli-session.c +++ b/cli-session.c @@ -81,6 +81,7 @@ static const packettype cli_packettypes[] = { {SSH_MSG_REQUEST_SUCCESS, ignore_recv_response}, {SSH_MSG_REQUEST_FAILURE, ignore_recv_response}, #endif + {SSH_MSG_EXT_INFO, recv_msg_ext_info}, {0, NULL} /* End */ }; @@ -352,7 +353,9 @@ static void cli_session_cleanup(void) { (void)fcntl(cli_ses.stderrcopy, F_SETFL, cli_ses.stderrflags); cli_tty_cleanup(); - + if (cli_ses.server_sig_algs) { + buf_free(cli_ses.server_sig_algs); + } } static void cli_finished() { diff --git a/common-algo.c b/common-algo.c index 2a1047e..938695d 100644 --- a/common-algo.c +++ b/common-algo.c @@ -30,6 +30,7 @@ #include "dh_groups.h" #include "ltc_prng.h" #include "ecc.h" +#include "ssh.h" /* 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*/ @@ -280,6 +281,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 }; #endif +/* data == NULL for non-kex algorithm identifiers */ algo_type sshkex[] = { #if DROPBEAR_CURVE25519 {"curve25519-sha256", 0, &kex_curve25519, 1, NULL}, @@ -309,7 +311,11 @@ algo_type sshkex[] = { {"diffie-hellman-group16-sha512", 0, &kex_dh_group16_sha512, 1, NULL}, #endif #if DROPBEAR_KEXGUESS2 - {KEXGUESS2_ALGO_NAME, KEXGUESS2_ALGO_ID, NULL, 1, NULL}, + {KEXGUESS2_ALGO_NAME, 0, NULL, 1, NULL}, +#endif +#if DROPBEAR_CLIENT + /* Set unusable by svr_algos_initialise() */ + {SSH_EXT_INFO_C, 0, NULL, 1, NULL}, #endif {NULL, 0, NULL, 0, NULL} }; @@ -336,15 +342,79 @@ void buf_put_algolist(buffer * buf, const algo_type localalgos[]) { buf_free(algolist); } +/* 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; +} + /* match the first algorithm in the comma-separated list in buf which is * also in localalgos[], or return NULL on failure. * (*goodguess) is set to 1 if the preferred client/server algos match, * 0 otherwise. This is used for checking if the kexalgo/hostkeyalgos are * guessed correctly */ algo_type * buf_match_algo(buffer* buf, algo_type localalgos[], - enum kexguess2_used *kexguess2, int *goodguess) -{ - + int kexguess2, int *goodguess) { char * algolist = NULL; const char *remotenames[MAX_PROPOSED_ALGO], *localnames[MAX_PROPOSED_ALGO]; unsigned int len; @@ -359,40 +429,8 @@ algo_type * buf_match_algo(buffer* buf, algo_type localalgos[], /* get the comma-separated list from the buffer ie "algo1,algo2,algo3" */ algolist = buf_getstring(buf, &len); TRACE(("buf_match_algo: %s", algolist)) - if (len > MAX_PROPOSED_ALGO*(MAX_NAME_LEN+1)) { - goto out; - } - - /* 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; - } - } + remotecount = MAX_PROPOSED_ALGO; + get_algolist(algolist, len, remotenames, &remotecount); for (i = 0; localalgos[i].name != NULL; i++) { if (localalgos[i].usable) { @@ -424,12 +462,11 @@ algo_type * buf_match_algo(buffer* buf, algo_type localalgos[], } if (strcmp(servnames[j], clinames[i]) == 0) { /* set if it was a good guess */ - if (goodguess && kexguess2) { - if (*kexguess2 == KEXGUESS2_YES) { + if (goodguess != NULL) { + if (kexguess2) { if (i == 0) { *goodguess = 1; } - } else { if (i == 0 && j == 0) { *goodguess = 1; diff --git a/common-kex.c b/common-kex.c index c1392c1..8172796 100644 --- a/common-kex.c +++ b/common-kex.c @@ -820,21 +820,33 @@ static void read_kex_algos() { int goodguess = 0; int allgood = 1; /* we AND this with each goodguess and see if its still true after */ - -#if DROPBEAR_KEXGUESS2 - enum kexguess2_used kexguess2 = KEXGUESS2_LOOK; -#else - enum kexguess2_used kexguess2 = KEXGUESS2_NO; -#endif + int kexguess2 = 0; buf_incrpos(ses.payload, 16); /* start after the cookie */ memset(ses.newkeys, 0x0, sizeof(*ses.newkeys)); /* 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 + + /* Determine if SSH_MSG_EXT_INFO messages should be sent. + Should be done for the first key exchange. */ + if (!ses.kexstate.donefirstkex) { + if (IS_DROPBEAR_SERVER) { + if (buf_has_algo(ses.payload, SSH_EXT_INFO_C) == DROPBEAR_SUCCESS) { + ses.allow_ext_info = 1; + } + } + } + + algo = buf_match_algo(ses.payload, sshkex, kexguess2, &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"; goto error; } @@ -843,7 +855,7 @@ static void read_kex_algos() { ses.newkeys->algo_kex = algo->data; /* server_host_key_algorithms */ - algo = buf_match_algo(ses.payload, sshhostkey, &kexguess2, &goodguess); + algo = buf_match_algo(ses.payload, sshhostkey, kexguess2, &goodguess); allgood &= goodguess; if (algo == NULL) { erralgo = "hostkey"; diff --git a/fuzzer-verify.c b/fuzzer-verify.c index cda7723..cdf7804 100644 --- a/fuzzer-verify.c +++ b/fuzzer-verify.c @@ -29,7 +29,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { sign_key *key = new_sign_key(); enum signkey_type keytype = DROPBEAR_SIGNKEY_ANY; if (buf_get_pub_key(fuzz.input, key, &keytype) == DROPBEAR_SUCCESS) { - enum signature_type sigtype = (enum signature_type)keytype; + enum signature_type sigtype; if (keytype == DROPBEAR_SIGNKEY_RSA) { /* Flip a coin to decide rsa signature type */ int flag = buf_getbyte(fuzz_input); @@ -38,6 +38,8 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { } 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. diff --git a/kex.h b/kex.h index 03dcbb1..3b0ec62 100644 --- a/kex.h +++ b/kex.h @@ -65,6 +65,8 @@ void recv_msg_kexdh_init(void); /* server */ void send_msg_kexdh_init(void); /* client */ void recv_msg_kexdh_reply(void); /* client */ +void recv_msg_ext_info(void); + struct KEXState { unsigned sentkexinit : 1; /*set when we've sent/recv kexinit packet */ diff --git a/session.h b/session.h index cbe9cb5..1059537 100644 --- a/session.h +++ b/session.h @@ -185,6 +185,9 @@ struct sshsession { /* Enables/disables compression */ algo_type *compress_algos; + + /* Other side allows SSH_MSG_EXT_INFO */ + int allow_ext_info; /* a list of queued replies that should be sent after a KEX has concluded (ie, while dataallowed was unset)*/ @@ -313,13 +316,7 @@ struct clientsession { #endif sign_key *lastprivkey; - enum signature_type server_sig_algs[DROPBEAR_SIGNKEY_NUM_NAMED+1]; - int server_sig_algs_count; -#if DROPBEAR_RSA - /* Set to DROPBEAR_SIGNATURE_RSA_SHA256 or DROPBEAR_SIGNATURE_RSA_SHA1 - if depending which the server accepts */ - enum signature_type preferred_rsa_sigtype; -#endif + buffer *server_sig_algs; int retval; /* What the command exit status was - we emulate it */ #if 0 diff --git a/signkey.c b/signkey.c index 47c8c8e..6ed1667 100644 --- a/signkey.c +++ b/signkey.c @@ -114,13 +114,17 @@ 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) { #if DROPBEAR_RSA_SHA256 if (type == DROPBEAR_SIGNATURE_RSA_SHA256) { - *namelen = strlen(SSH_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) { - *namelen = strlen(SSH_SIGNKEY_RSA); + if (namelen) { + *namelen = strlen(SSH_SIGNKEY_RSA); + } return SSH_SIGNKEY_RSA; } #endif @@ -144,6 +148,16 @@ enum signature_type signature_type_from_name(const char* name, unsigned int name 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) { @@ -587,8 +601,7 @@ void buf_put_sign(buffer* buf, sign_key *key, enum signature_type sigtype, #if DEBUG_TRACE { - int namelen; - const char* signame = signature_name_from_type(sigtype, &namelen); + const char* signame = signature_name_from_type(sigtype, NULL); TRACE(("buf_put_sign type %d %s", sigtype, signame)); } #endif diff --git a/signkey.h b/signkey.h index 9468509..2640171 100644 --- a/signkey.h +++ b/signkey.h @@ -120,6 +120,8 @@ 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_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); diff --git a/ssh.h b/ssh.h index 5030fe9..ee4a960 100644 --- a/ssh.h +++ b/ssh.h @@ -32,6 +32,7 @@ #define SSH_MSG_DEBUG 4 #define SSH_MSG_SERVICE_REQUEST 5 #define SSH_MSG_SERVICE_ACCEPT 6 +#define SSH_MSG_EXT_INFO 7 #define SSH_MSG_KEXINIT 20 #define SSH_MSG_NEWKEYS 21 #define SSH_MSG_KEXDH_INIT 30 @@ -94,6 +95,11 @@ #define SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE 14 #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 */ #define SSH_SERVICE_USERAUTH "ssh-userauth" #define SSH_SERVICE_USERAUTH_LEN 12 diff --git a/svr-session.c b/svr-session.c index 0b9eac7..8bc8744 100644 --- a/svr-session.c +++ b/svr-session.c @@ -330,13 +330,16 @@ static void svr_remoteclosed() { } static void svr_algos_initialise(void) { -#if DROPBEAR_DH_GROUP1 && DROPBEAR_DH_GROUP1_CLIENTONLY algo_type *algo; for (algo = sshkex; algo->name; algo++) { +#if DROPBEAR_DH_GROUP1 && DROPBEAR_DH_GROUP1_CLIENTONLY if (strcmp(algo->name, "diffie-hellman-group1-sha1") == 0) { algo->usable = 0; } - } #endif + if (strcmp(algo->name, SSH_EXT_INFO_C) == 0) { + algo->usable = 0; + } + } } -- cgit v1.2.3 From 701d43b8594365b55421e8bc4c53efa920c09ed5 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Sun, 24 May 2020 14:16:58 +0800 Subject: send and handle SSH_MSG_EXT_INFO only at the correct point - other fixes for rsa pubkey auth - only include ext-info handling when rsa pubkey auth is compiled --- cli-authpubkey.c | 4 ++-- cli-kex.c | 10 ++++++++++ common-algo.c | 2 ++ common-kex.c | 7 +++++-- dropbearkey.c | 2 +- kex.h | 4 ++-- process-packet.c | 3 +-- session.h | 12 +++++------- signkey.c | 2 +- svr-kex.c | 14 ++++++++++---- svr-runopts.c | 2 +- svr-session.c | 2 ++ sysoptions.h | 4 ++++ 13 files changed, 46 insertions(+), 22 deletions(-) (limited to 'cli-kex.c') diff --git a/cli-authpubkey.c b/cli-authpubkey.c index 8426e84..49f79c3 100644 --- a/cli-authpubkey.c +++ b/cli-authpubkey.c @@ -200,7 +200,7 @@ int cli_auth_pubkey() { while (cli_opts.privkeys->first) { sign_key * key = (sign_key*)cli_opts.privkeys->first->item; if (cli_ses.server_sig_algs) { -#ifdef DROPBEAR_RSA +#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) @@ -242,7 +242,7 @@ int cli_auth_pubkey() { assume all except rsa-sha256 are OK. */ #if DROPBEAR_RSA if (key->type == DROPBEAR_SIGNKEY_RSA) { -#ifdef DROPBEAR_RSA_SHA1 +#if DROPBEAR_RSA_SHA1 sigtype = DROPBEAR_SIGNATURE_RSA_SHA1; TRACE(("no server-sig-algs, using rsa sha1")) break; diff --git a/cli-kex.c b/cli-kex.c index 99370eb..98b0245 100644 --- a/cli-kex.c +++ b/cli-kex.c @@ -418,6 +418,15 @@ void recv_msg_ext_info(void) { 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)) @@ -435,4 +444,5 @@ void recv_msg_ext_info(void) { } m_free(ext_name); } + TRACE(("leave recv_msg_ext_info")) } diff --git a/common-algo.c b/common-algo.c index 54bc559..8596831 100644 --- a/common-algo.c +++ b/common-algo.c @@ -313,9 +313,11 @@ algo_type sshkex[] = { #if DROPBEAR_KEXGUESS2 {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 {NULL, 0, NULL, 0, NULL} }; diff --git a/common-kex.c b/common-kex.c index 7063c81..1ef1946 100644 --- a/common-kex.c +++ b/common-kex.c @@ -175,6 +175,9 @@ void send_msg_newkeys() { /* set up our state */ ses.kexstate.sentnewkeys = 1; + if (ses.kexstate.donefirstkex) { + ses.kexstate.donesecondkex = 1; + } ses.kexstate.donefirstkex = 1; ses.dataallowed = 1; /* we can send other packets again now */ gen_new_keys(); @@ -197,8 +200,6 @@ void recv_msg_newkeys() { /* Set up the kex for the first time */ void kexfirstinitialise() { - ses.kexstate.donefirstkex = 0; - #ifdef DISABLE_ZLIB ses.compress_algos = ssh_nocompress; #else @@ -833,6 +834,7 @@ static void read_kex_algos() { } #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 */ @@ -843,6 +845,7 @@ static void read_kex_algos() { } } } +#endif algo = buf_match_algo(ses.payload, sshkex, kexguess2, &goodguess); allgood &= goodguess; diff --git a/dropbearkey.c b/dropbearkey.c index f881855..8d8c7c2 100644 --- a/dropbearkey.c +++ b/dropbearkey.c @@ -133,7 +133,7 @@ static void check_signkey_bits(enum signkey_type type, int bits) } break; #endif -#ifdef DROPEAR_DSS +#if DROPEAR_DSS case DROPBEAR_SIGNKEY_DSS: if (bits != 1024) { dropbear_exit("DSS keys have a fixed size of 1024 bits\n"); diff --git a/kex.h b/kex.h index cdec673..7406f65 100644 --- a/kex.h +++ b/kex.h @@ -61,7 +61,6 @@ int is_compress_recv(void); #endif void recv_msg_kexdh_init(void); /* server */ -void send_msg_ext_info(void); /* server */ void send_msg_kexdh_init(void); /* client */ void recv_msg_kexdh_reply(void); /* client */ @@ -76,8 +75,9 @@ struct KEXState { 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 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 */ + unsigned int donesecondkex; /* Set to 1 after the second kex has completed */ unsigned our_first_follows_matches : 1; diff --git a/process-packet.c b/process-packet.c index 4953cf2..9c58c7c 100644 --- a/process-packet.c +++ b/process-packet.c @@ -51,8 +51,6 @@ void process_packet() { type = buf_getbyte(ses.payload); TRACE(("process_packet: packet type = %d, len %d", type, ses.payload->len)) - ses.lastpacket = type; - now = monotonic_now(); ses.last_packet_time_keepalive_recv = now; @@ -154,6 +152,7 @@ void process_packet() { recv_unimplemented(); out: + ses.lastpacket = type; buf_free(ses.payload); ses.payload = NULL; diff --git a/session.h b/session.h index 1059537..a91401e 100644 --- a/session.h +++ b/session.h @@ -186,7 +186,7 @@ struct sshsession { /* Enables/disables compression */ algo_type *compress_algos; - /* Other side allows SSH_MSG_EXT_INFO */ + /* 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 @@ -253,13 +253,12 @@ struct serversession { #endif #if DROPBEAR_PLUGIN - /* The shared library handle */ - void *plugin_handle; + /* The shared library handle */ + void *plugin_handle; - /* The instance created by the plugin_new function */ - struct PluginInstance *plugin_instance; + /* The instance created by the plugin_new function */ + struct PluginInstance *plugin_instance; #endif - }; typedef enum { @@ -288,7 +287,6 @@ struct clientsession { cli_kex_state kex_state; /* Used for progressing KEX */ 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) */ struct termios saved_tio; diff --git a/signkey.c b/signkey.c index 6ed1667..49801f3 100644 --- a/signkey.c +++ b/signkey.c @@ -139,7 +139,7 @@ enum signature_type signature_type_from_name(const char* name, unsigned int name return DROPBEAR_SIGNATURE_RSA_SHA256; } #endif -#if DROPBEAR_RSA_SHA256 +#if DROPBEAR_RSA_SHA1 if (namelen == strlen(SSH_SIGNKEY_RSA) && memcmp(name, SSH_SIGNKEY_RSA, namelen) == 0) { return DROPBEAR_SIGNATURE_RSA_SHA1; diff --git a/svr-kex.c b/svr-kex.c index 44e10ff..df1008b 100644 --- a/svr-kex.c +++ b/svr-kex.c @@ -38,13 +38,15 @@ #include "gensignkey.h" 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 * calculating a session key reply value, and corresponding hash. These * are carried out by send_msg_kexdh_reply(). recv_msg_kexdh_init() calls * that function, then brings the new keys into use */ void recv_msg_kexdh_init() { - DEF_MP_INT(dh_e); buffer *ecdh_qs = NULL; @@ -87,9 +89,12 @@ void recv_msg_kexdh_init() { send_msg_newkeys(); - if (ses.allow_ext_info) { +#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; TRACE(("leave recv_msg_kexdh_init")) @@ -247,8 +252,9 @@ static void send_msg_kexdh_reply(mp_int *dh_e, buffer *ecdh_qs) { TRACE(("leave send_msg_kexdh_reply")) } +#if DROPBEAR_EXT_INFO /* Only used for server-sig-algs on the server side */ -void send_msg_ext_info(void) { +static void send_msg_ext_info(void) { TRACE(("enter send_msg_ext_info")) buf_putbyte(ses.writepayload, SSH_MSG_EXT_INFO); @@ -261,5 +267,5 @@ void send_msg_ext_info(void) { encrypt_packet(); TRACE(("leave send_msg_ext_info")) - } +#endif diff --git a/svr-runopts.c b/svr-runopts.c index 770f70a..2c905dd 100644 --- a/svr-runopts.c +++ b/svr-runopts.c @@ -567,7 +567,7 @@ static void addhostkey(const char *keyfile) { void load_all_hostkeys() { int i; int any_keys = 0; -#ifdef DROPBEAR_ECDSA +#if DROPBEAR_ECDSA int loaded_any_ecdsa = 0; #endif diff --git a/svr-session.c b/svr-session.c index 8bc8744..6c3147f 100644 --- a/svr-session.c +++ b/svr-session.c @@ -337,9 +337,11 @@ static void svr_algos_initialise(void) { algo->usable = 0; } #endif +#if DROPBEAR_EXT_INFO if (strcmp(algo->name, SSH_EXT_INFO_C) == 0) { algo->usable = 0; } +#endif } } diff --git a/sysoptions.h b/sysoptions.h index 0f52431..d5f8da8 100644 --- a/sysoptions.h +++ b/sysoptions.h @@ -161,6 +161,10 @@ If you test it please contact the Dropbear author */ #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 */ #define MAX_ECC_SIZE 140 -- cgit v1.2.3 From 413eaf1ba1dc99e1eb7d2375bd6a7c6d8759ff17 Mon Sep 17 00:00:00 2001 From: Vladislav Grishenko Date: Thu, 28 May 2020 20:01:48 +0500 Subject: Allow DH to be completely disabled (#97) Reduces binary size by ~2kB by default and by 21kB with no other libtommath functions users, ex. with curve25519 kex and ed25519 key only. --- cli-kex.c | 2 ++ common-kex.c | 2 ++ dh_groups.c | 3 +++ dh_groups.h | 4 +++- kex.h | 7 ++++--- 5 files changed, 14 insertions(+), 4 deletions(-) (limited to 'cli-kex.c') diff --git a/cli-kex.c b/cli-kex.c index 98b0245..af1cfcf 100644 --- a/cli-kex.c +++ b/cli-kex.c @@ -155,10 +155,12 @@ void recv_msg_kexdh_reply() { #endif } +#if DROPBEAR_NORMAL_DH if (cli_ses.dh_param) { free_kexdh_param(cli_ses.dh_param); cli_ses.dh_param = NULL; } +#endif #if DROPBEAR_ECDH if (cli_ses.ecdh_param) { free_kexecdh_param(cli_ses.ecdh_param); diff --git a/common-kex.c b/common-kex.c index 3dca3a5..4caa06e 100644 --- a/common-kex.c +++ b/common-kex.c @@ -548,6 +548,7 @@ void recv_msg_kexinit() { TRACE(("leave recv_msg_kexinit")) } +#if DROPBEAR_NORMAL_DH static void load_dh_p(mp_int * dh_p) { bytes_to_mp(dh_p, ses.newkeys->algo_kex->dh_p_bytes, @@ -656,6 +657,7 @@ void kexdh_comb_key(struct kex_dh_param *param, mp_int *dh_pub_them, /* calculate the hash H to sign */ finish_kexhashbuf(); } +#endif #if DROPBEAR_ECDH struct kex_ecdh_param *gen_kexecdh_param() { diff --git a/dh_groups.c b/dh_groups.c index fafac49..164cb48 100644 --- a/dh_groups.c +++ b/dh_groups.c @@ -1,6 +1,8 @@ #include "options.h" #include "dh_groups.h" +#if DROPBEAR_DH_NORMAL + #if DROPBEAR_DH_GROUP1 /* diffie-hellman-group1-sha1 value for p */ const unsigned char dh_p_1[DH_P_1_LEN] = { @@ -92,3 +94,4 @@ const unsigned char dh_p_16[DH_P_16_LEN] = { /* Same for all groups */ const int DH_G_VAL = 2; +#endif diff --git a/dh_groups.h b/dh_groups.h index cddaa17..aa25621 100644 --- a/dh_groups.h +++ b/dh_groups.h @@ -2,6 +2,8 @@ #define DROPBEAR_DH_GROUPS_H #include "options.h" +#if DROPBEAR_DH_NORMAL + #if DROPBEAR_DH_GROUP1 #define DH_P_1_LEN 128 extern const unsigned char dh_p_1[DH_P_1_LEN]; @@ -17,8 +19,8 @@ extern const unsigned char dh_p_14[DH_P_14_LEN]; extern const unsigned char dh_p_16[DH_P_16_LEN]; #endif - extern const int DH_G_VAL; +#endif #endif diff --git a/kex.h b/kex.h index 7406f65..77cf21a 100644 --- a/kex.h +++ b/kex.h @@ -36,10 +36,12 @@ void recv_msg_newkeys(void); void kexfirstinitialise(void); void finish_kexhashbuf(void); +#if DROPBEAR_NORMAL_DH struct kex_dh_param *gen_kexdh_param(void); void free_kexdh_param(struct kex_dh_param *param); void kexdh_comb_key(struct kex_dh_param *param, mp_int *dh_pub_them, sign_key *hostkey); +#endif #if DROPBEAR_ECDH struct kex_ecdh_param *gen_kexecdh_param(void); @@ -87,10 +89,12 @@ struct KEXState { }; +#if DROPBEAR_NORMAL_DH struct kex_dh_param { mp_int pub; /* e */ mp_int priv; /* x */ }; +#endif #if DROPBEAR_ECDH struct kex_ecdh_param { @@ -104,9 +108,6 @@ struct kex_curve25519_param { unsigned char priv[CURVE25519_LEN]; unsigned char pub[CURVE25519_LEN]; }; - -/* No header file for curve25519_donna */ -int curve25519_donna(unsigned char *out, const unsigned char *secret, const unsigned char *other); #endif #endif /* DROPBEAR_KEX_H_ */ -- cgit v1.2.3