Make server send SSH_MSG_EXT_INFO

Ensure that only valid hostkey algorithms are sent in the first kex guess
This commit is contained in:
Matt Johnston 2020-05-21 23:00:22 +08:00
parent 2a81289ed3
commit 331d4a714f
6 changed files with 75 additions and 29 deletions

6
algo.h
View File

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

View File

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

View File

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

1
kex.h
View File

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

View File

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

View File

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