diff options
author | Matt Johnston <matt@ucc.asn.au> | 2020-05-21 23:00:22 +0800 |
---|---|---|
committer | Matt Johnston <matt@ucc.asn.au> | 2020-05-21 23:00:22 +0800 |
commit | 331d4a714f65772e384e15ff55b850a6e9e6786b (patch) | |
tree | 16c000a69313d6f49610d48af4c74d6d07d1d899 | |
parent | 2a81289ed32d9e1ca612a41975974bfa258d2ace (diff) |
Make server send SSH_MSG_EXT_INFO
Ensure that only valid hostkey algorithms are sent in the first kex guess
-rw-r--r-- | algo.h | 6 | ||||
-rw-r--r-- | common-algo.c | 43 | ||||
-rw-r--r-- | common-kex.c | 27 | ||||
-rw-r--r-- | kex.h | 1 | ||||
-rw-r--r-- | svr-kex.c | 21 | ||||
-rw-r--r-- | svr-runopts.c | 6 |
6 files changed, 75 insertions, 29 deletions
@@ -47,7 +47,7 @@ typedef struct Algo_Type algo_type; /* lists mapping ssh types of algorithms to internal values */ extern algo_type sshkex[]; -extern algo_type sshhostkey[]; +extern algo_type sigalgs[]; extern algo_type sshciphers[]; extern algo_type sshhashes[]; extern algo_type ssh_compress[]; @@ -112,11 +112,15 @@ struct dropbear_kex { const struct ltc_hash_descriptor *hash_desc; }; +/* 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[]); #define KEXGUESS2_ALGO_NAME "kexguess2@matt.ucc.asn.au" 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[], int kexguess2, int *goodguess); diff --git a/common-algo.c b/common-algo.c index 938695d..54bc559 100644 --- a/common-algo.c +++ b/common-algo.c @@ -222,7 +222,7 @@ algo_type ssh_nocompress[] = { {NULL, 0, NULL, 0, NULL} }; -algo_type sshhostkey[] = { +algo_type sigalgs[] = { #if DROPBEAR_ED25519 {"ssh-ed25519", DROPBEAR_SIGNATURE_ED25519, NULL, 1, NULL}, #endif @@ -321,25 +321,34 @@ algo_type sshkex[] = { }; /* 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 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++) { - if (localalgos[i].usable) { - if (donefirst) - buf_putbyte(algolist, ','); + if (localalgos[i].usable || useall) { + if (donefirst) { + buf_putbyte(buf, ','); + } donefirst = 1; 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); - TRACE(("algolist add '%*s'", algolist->len, algolist->data)) - buf_free(algolist); + /* Fill out the length */ + len = buf->pos - startpos - 4; + 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. @@ -408,6 +417,16 @@ int buf_has_algo(buffer *buf, const char *algo) { 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 * also in localalgos[], or return NULL on failure. * (*goodguess) is set to 1 if the preferred client/server algos match, diff --git a/common-kex.c b/common-kex.c index 8172796..7063c81 100644 --- a/common-kex.c +++ b/common-kex.c @@ -65,7 +65,7 @@ void send_msg_kexinit() { buf_put_algolist(ses.writepayload, sshkex); /* server_host_key_algorithms */ - buf_put_algolist(ses.writepayload, sshhostkey); + buf_put_algolist(ses.writepayload, sigalgs); /* encryption_algorithms_client_to_server */ buf_put_algolist(ses.writepayload, sshciphers); @@ -110,8 +110,8 @@ void send_msg_kexinit() { ses.newkeys = (struct key_context*)m_malloc(sizeof(struct key_context)); if (ses.send_kex_first_guess) { - ses.newkeys->algo_kex = sshkex[0].data; - ses.newkeys->algo_signature = sshhostkey[0].val; + ses.newkeys->algo_kex = first_usable_algo(sshkex)->data; + 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(); } @@ -834,9 +834,10 @@ static void read_kex_algos() { #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) { + 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; } @@ -855,7 +856,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, sigalgs, kexguess2, &goodguess); allgood &= goodguess; if (algo == NULL) { erralgo = "hostkey"; @@ -866,7 +867,7 @@ static void read_kex_algos() { 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); + c2s_cipher_algo = buf_match_algo(ses.payload, sshciphers, 0, NULL); if (c2s_cipher_algo == NULL) { erralgo = "enc c->s"; goto error; @@ -874,7 +875,7 @@ static void read_kex_algos() { TRACE(("enc c2s is %s", c2s_cipher_algo->name)) /* 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) { erralgo = "enc s->c"; goto error; @@ -882,7 +883,7 @@ static void read_kex_algos() { TRACE(("enc s2c is %s", s2c_cipher_algo->name)) /* 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 (c2s_hash_algo == NULL) { erralgo = "mac c->s"; goto error; @@ -890,7 +891,7 @@ static void read_kex_algos() { TRACE(("hash c2s is %s", c2s_hash_algo->name)) /* 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 (s2c_hash_algo == NULL) { erralgo = "mac s->c"; goto error; @@ -898,7 +899,7 @@ static void read_kex_algos() { TRACE(("hash s2c is %s", s2c_hash_algo->name)) /* 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) { erralgo = "comp c->s"; goto error; @@ -906,7 +907,7 @@ static void read_kex_algos() { TRACE(("hash c2s is %s", c2s_comp_algo->name)) /* 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) { erralgo = "comp s->c"; goto error; @@ -61,6 +61,7 @@ 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 */ @@ -86,6 +86,11 @@ void recv_msg_kexdh_init() { } send_msg_newkeys(); + + if (ses.allow_ext_info) { + send_msg_ext_info(); + } + ses.requirenext = SSH_MSG_NEWKEYS; TRACE(("leave recv_msg_kexdh_init")) } @@ -242,3 +247,19 @@ static void send_msg_kexdh_reply(mp_int *dh_e, buffer *ecdh_qs) { TRACE(("leave send_msg_kexdh_reply")) } +/* Only used for server-sig-algs on the server side */ +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")) + +} diff --git a/svr-runopts.c b/svr-runopts.c index d430825..770f70a 100644 --- a/svr-runopts.c +++ b/svr-runopts.c @@ -485,9 +485,9 @@ static void addportandaddress(const char* spec) { static void disablekey(int type) { int i; TRACE(("Disabling key type %d", type)) - for (i = 0; sshhostkey[i].name != NULL; i++) { - if (sshhostkey[i].val == type) { - sshhostkey[i].usable = 0; + for (i = 0; sigalgs[i].name != NULL; i++) { + if (sigalgs[i].val == type) { + sigalgs[i].usable = 0; break; } } |