mirror of
https://github.com/clearml/dropbear
synced 2025-04-09 15:15:07 +00:00
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:
parent
2a81289ed3
commit
331d4a714f
6
algo.h
6
algo.h
@ -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);
|
||||
|
||||
|
@ -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,
|
||||
|
27
common-kex.c
27
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;
|
||||
|
1
kex.h
1
kex.h
@ -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 */
|
||||
|
21
svr-kex.c
21
svr-kex.c
@ -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"))
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user