mirror of
https://github.com/clearml/dropbear
synced 2025-04-28 09:52:04 +00:00
ext-info handling for server-sig-algs
only client side is handled
This commit is contained in:
parent
972d723484
commit
5acee497bf
11
algo.h
11
algo.h
@ -114,18 +114,11 @@ struct dropbear_kex {
|
|||||||
|
|
||||||
void buf_put_algolist(buffer * buf, const algo_type localalgos[]);
|
void buf_put_algolist(buffer * buf, const algo_type localalgos[]);
|
||||||
|
|
||||||
enum kexguess2_used {
|
|
||||||
KEXGUESS2_LOOK,
|
|
||||||
KEXGUESS2_NO,
|
|
||||||
KEXGUESS2_YES,
|
|
||||||
};
|
|
||||||
|
|
||||||
#define KEXGUESS2_ALGO_NAME "kexguess2@matt.ucc.asn.au"
|
#define KEXGUESS2_ALGO_NAME "kexguess2@matt.ucc.asn.au"
|
||||||
#define KEXGUESS2_ALGO_ID 99
|
|
||||||
|
|
||||||
|
|
||||||
|
int buf_has_algo(buffer *buf, const char *algo);
|
||||||
algo_type * buf_match_algo(buffer* buf, algo_type localalgos[],
|
algo_type * buf_match_algo(buffer* buf, algo_type localalgos[],
|
||||||
enum kexguess2_used *kexguess2, int *goodguess);
|
int kexguess2, int *goodguess);
|
||||||
|
|
||||||
#if DROPBEAR_USER_ALGO_LIST
|
#if DROPBEAR_USER_ALGO_LIST
|
||||||
int check_user_algos(const char* user_algo_list, algo_type * algos,
|
int check_user_algos(const char* user_algo_list, algo_type * algos,
|
||||||
|
22
buffer.c
22
buffer.c
@ -228,19 +228,37 @@ char* buf_getstring(buffer* buf, unsigned int *retlen) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Return a string as a newly allocated buffer */
|
/* Return a string as a newly allocated buffer */
|
||||||
buffer * buf_getstringbuf(buffer *buf) {
|
static buffer * buf_getstringbuf_int(buffer *buf, int incllen) {
|
||||||
buffer *ret = NULL;
|
buffer *ret = NULL;
|
||||||
unsigned int len = buf_getint(buf);
|
unsigned int len = buf_getint(buf);
|
||||||
|
int extra = 0;
|
||||||
if (len > MAX_STRING_LEN) {
|
if (len > MAX_STRING_LEN) {
|
||||||
dropbear_exit("String too long");
|
dropbear_exit("String too long");
|
||||||
}
|
}
|
||||||
ret = buf_new(len);
|
if (incllen) {
|
||||||
|
extra = 4;
|
||||||
|
}
|
||||||
|
ret = buf_new(len+extra);
|
||||||
|
if (incllen) {
|
||||||
|
buf_putint(ret, len);
|
||||||
|
}
|
||||||
memcpy(buf_getwriteptr(ret, len), buf_getptr(buf, len), len);
|
memcpy(buf_getwriteptr(ret, len), buf_getptr(buf, len), len);
|
||||||
buf_incrpos(buf, len);
|
buf_incrpos(buf, len);
|
||||||
buf_incrlen(ret, len);
|
buf_incrlen(ret, len);
|
||||||
|
buf_setpos(ret, 0);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Return a string as a newly allocated buffer */
|
||||||
|
buffer * buf_getstringbuf(buffer *buf) {
|
||||||
|
return buf_getstringbuf_int(buf, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns a string in a new buffer, including the length */
|
||||||
|
buffer * buf_getbuf(buffer *buf) {
|
||||||
|
return buf_getstringbuf_int(buf, 1);
|
||||||
|
}
|
||||||
|
|
||||||
/* Just increment the buffer position the same as if we'd used buf_getstring,
|
/* Just increment the buffer position the same as if we'd used buf_getstring,
|
||||||
* but don't bother copying/malloc()ing for it */
|
* but don't bother copying/malloc()ing for it */
|
||||||
void buf_eatstring(buffer *buf) {
|
void buf_eatstring(buffer *buf) {
|
||||||
|
1
buffer.h
1
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);
|
unsigned char* buf_getwriteptr(const buffer* buf, unsigned int len);
|
||||||
char* buf_getstring(buffer* buf, unsigned int *retlen);
|
char* buf_getstring(buffer* buf, unsigned int *retlen);
|
||||||
buffer * buf_getstringbuf(buffer *buf);
|
buffer * buf_getstringbuf(buffer *buf);
|
||||||
|
buffer * buf_getbuf(buffer *buf);
|
||||||
void buf_eatstring(buffer *buf);
|
void buf_eatstring(buffer *buf);
|
||||||
void buf_putint(buffer* buf, unsigned int val);
|
void buf_putint(buffer* buf, unsigned int val);
|
||||||
void buf_putstring(buffer* buf, const char* str, unsigned int len);
|
void buf_putstring(buffer* buf, const char* str, unsigned int len);
|
||||||
|
@ -184,6 +184,7 @@ static void send_msg_userauth_pubkey(sign_key *key, enum signature_type sigtype,
|
|||||||
|
|
||||||
/* Returns 1 if a key was tried */
|
/* Returns 1 if a key was tried */
|
||||||
int cli_auth_pubkey() {
|
int cli_auth_pubkey() {
|
||||||
|
enum signature_type sigtype;
|
||||||
TRACE(("enter cli_auth_pubkey"))
|
TRACE(("enter cli_auth_pubkey"))
|
||||||
|
|
||||||
#if DROPBEAR_CLI_AGENTFWD
|
#if DROPBEAR_CLI_AGENTFWD
|
||||||
@ -191,28 +192,77 @@ int cli_auth_pubkey() {
|
|||||||
/* get the list of available keys from the agent */
|
/* get the list of available keys from the agent */
|
||||||
cli_load_agent_keys(cli_opts.privkeys);
|
cli_load_agent_keys(cli_opts.privkeys);
|
||||||
cli_opts.agent_keys_loaded = 1;
|
cli_opts.agent_keys_loaded = 1;
|
||||||
|
TRACE(("cli_auth_pubkey: agent keys loaded"))
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* TODO iterate through privkeys to skip ones not in server-sig-algs */
|
/* iterate through privkeys to remove ones not allowed in server-sig-algs */
|
||||||
|
while (cli_opts.privkeys->first) {
|
||||||
/* TODO: testing */
|
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
|
#if DROPBEAR_RSA_SHA256
|
||||||
cli_ses.preferred_rsa_sigtype = DROPBEAR_SIGNATURE_RSA_SHA256;
|
if (buf_has_algo(cli_ses.server_sig_algs, SSH_SIGNATURE_RSA_SHA256)
|
||||||
#elif DROPBEAR_RSA_SHA1
|
== DROPBEAR_SUCCESS) {
|
||||||
cli_ses.preferred_rsa_sigtype = DROPBEAR_SIGNATURE_RSA_SHA1;
|
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
|
#endif
|
||||||
|
} /* key->type == DROPBEAR_SIGNKEY_RSA */
|
||||||
|
#endif /* DROPBEAR_RSA */
|
||||||
|
sigtype = signature_type_from_signkey(key->type);
|
||||||
|
TRACE(("no server-sig-algs, using key"))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (cli_opts.privkeys->first) {
|
if (cli_opts.privkeys->first) {
|
||||||
sign_key * key = (sign_key*)cli_opts.privkeys->first->item;
|
sign_key * key = (sign_key*)cli_opts.privkeys->first->item;
|
||||||
/* 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 a trial request */
|
||||||
send_msg_userauth_pubkey(key, sigtype, 0);
|
send_msg_userauth_pubkey(key, sigtype, 0);
|
||||||
cli_ses.lastprivkey = key;
|
cli_ses.lastprivkey = key;
|
||||||
|
25
cli-kex.c
25
cli-kex.c
@ -411,3 +411,28 @@ out:
|
|||||||
}
|
}
|
||||||
m_free(fingerprint);
|
m_free(fingerprint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void recv_msg_ext_info(void) {
|
||||||
|
/* This message is not client-specific in the protocol but Dropbear only handles
|
||||||
|
a server-sent message at present. */
|
||||||
|
unsigned int num_ext;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -106,6 +106,7 @@ static void cli_dropbear_exit(int exitcode, const char* format, va_list param) {
|
|||||||
|
|
||||||
/* Render the formatted exit message */
|
/* Render the formatted exit message */
|
||||||
vsnprintf(exitmsg, sizeof(exitmsg), format, param);
|
vsnprintf(exitmsg, sizeof(exitmsg), format, param);
|
||||||
|
TRACE(("Exited, cleaning up: %s", exitmsg))
|
||||||
|
|
||||||
/* Add the prefix depending on session/auth state */
|
/* Add the prefix depending on session/auth state */
|
||||||
if (!ses.init_done) {
|
if (!ses.init_done) {
|
||||||
|
@ -81,6 +81,7 @@ static const packettype cli_packettypes[] = {
|
|||||||
{SSH_MSG_REQUEST_SUCCESS, ignore_recv_response},
|
{SSH_MSG_REQUEST_SUCCESS, ignore_recv_response},
|
||||||
{SSH_MSG_REQUEST_FAILURE, ignore_recv_response},
|
{SSH_MSG_REQUEST_FAILURE, ignore_recv_response},
|
||||||
#endif
|
#endif
|
||||||
|
{SSH_MSG_EXT_INFO, recv_msg_ext_info},
|
||||||
{0, NULL} /* End */
|
{0, NULL} /* End */
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -352,7 +353,9 @@ static void cli_session_cleanup(void) {
|
|||||||
(void)fcntl(cli_ses.stderrcopy, F_SETFL, cli_ses.stderrflags);
|
(void)fcntl(cli_ses.stderrcopy, F_SETFL, cli_ses.stderrflags);
|
||||||
|
|
||||||
cli_tty_cleanup();
|
cli_tty_cleanup();
|
||||||
|
if (cli_ses.server_sig_algs) {
|
||||||
|
buf_free(cli_ses.server_sig_algs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cli_finished() {
|
static void cli_finished() {
|
||||||
|
119
common-algo.c
119
common-algo.c
@ -30,6 +30,7 @@
|
|||||||
#include "dh_groups.h"
|
#include "dh_groups.h"
|
||||||
#include "ltc_prng.h"
|
#include "ltc_prng.h"
|
||||||
#include "ecc.h"
|
#include "ecc.h"
|
||||||
|
#include "ssh.h"
|
||||||
|
|
||||||
/* This file (algo.c) organises the ciphers which can be used, and is used to
|
/* This file (algo.c) organises the ciphers which can be used, and is used to
|
||||||
* decide which ciphers/hashes/compression/signing to use during key exchange*/
|
* decide which ciphers/hashes/compression/signing to use during key exchange*/
|
||||||
@ -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 };
|
static const struct dropbear_kex kex_curve25519 = {DROPBEAR_KEX_CURVE25519, NULL, 0, NULL, &sha256_desc };
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* data == NULL for non-kex algorithm identifiers */
|
||||||
algo_type sshkex[] = {
|
algo_type sshkex[] = {
|
||||||
#if DROPBEAR_CURVE25519
|
#if DROPBEAR_CURVE25519
|
||||||
{"curve25519-sha256", 0, &kex_curve25519, 1, NULL},
|
{"curve25519-sha256", 0, &kex_curve25519, 1, NULL},
|
||||||
@ -309,7 +311,11 @@ algo_type sshkex[] = {
|
|||||||
{"diffie-hellman-group16-sha512", 0, &kex_dh_group16_sha512, 1, NULL},
|
{"diffie-hellman-group16-sha512", 0, &kex_dh_group16_sha512, 1, NULL},
|
||||||
#endif
|
#endif
|
||||||
#if DROPBEAR_KEXGUESS2
|
#if DROPBEAR_KEXGUESS2
|
||||||
{KEXGUESS2_ALGO_NAME, KEXGUESS2_ALGO_ID, NULL, 1, NULL},
|
{KEXGUESS2_ALGO_NAME, 0, NULL, 1, NULL},
|
||||||
|
#endif
|
||||||
|
#if DROPBEAR_CLIENT
|
||||||
|
/* Set unusable by svr_algos_initialise() */
|
||||||
|
{SSH_EXT_INFO_C, 0, NULL, 1, NULL},
|
||||||
#endif
|
#endif
|
||||||
{NULL, 0, NULL, 0, NULL}
|
{NULL, 0, NULL, 0, NULL}
|
||||||
};
|
};
|
||||||
@ -336,15 +342,79 @@ void buf_put_algolist(buffer * buf, const algo_type localalgos[]) {
|
|||||||
buf_free(algolist);
|
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
|
/* match the first algorithm in the comma-separated list in buf which is
|
||||||
* also in localalgos[], or return NULL on failure.
|
* also in localalgos[], or return NULL on failure.
|
||||||
* (*goodguess) is set to 1 if the preferred client/server algos match,
|
* (*goodguess) is set to 1 if the preferred client/server algos match,
|
||||||
* 0 otherwise. This is used for checking if the kexalgo/hostkeyalgos are
|
* 0 otherwise. This is used for checking if the kexalgo/hostkeyalgos are
|
||||||
* guessed correctly */
|
* guessed correctly */
|
||||||
algo_type * buf_match_algo(buffer* buf, algo_type localalgos[],
|
algo_type * buf_match_algo(buffer* buf, algo_type localalgos[],
|
||||||
enum kexguess2_used *kexguess2, int *goodguess)
|
int kexguess2, int *goodguess) {
|
||||||
{
|
|
||||||
|
|
||||||
char * algolist = NULL;
|
char * algolist = NULL;
|
||||||
const char *remotenames[MAX_PROPOSED_ALGO], *localnames[MAX_PROPOSED_ALGO];
|
const char *remotenames[MAX_PROPOSED_ALGO], *localnames[MAX_PROPOSED_ALGO];
|
||||||
unsigned int len;
|
unsigned int len;
|
||||||
@ -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" */
|
/* get the comma-separated list from the buffer ie "algo1,algo2,algo3" */
|
||||||
algolist = buf_getstring(buf, &len);
|
algolist = buf_getstring(buf, &len);
|
||||||
TRACE(("buf_match_algo: %s", algolist))
|
TRACE(("buf_match_algo: %s", algolist))
|
||||||
if (len > MAX_PROPOSED_ALGO*(MAX_NAME_LEN+1)) {
|
remotecount = MAX_PROPOSED_ALGO;
|
||||||
goto out;
|
get_algolist(algolist, len, remotenames, &remotecount);
|
||||||
}
|
|
||||||
|
|
||||||
/* remotenames will contain a list of the strings parsed out */
|
|
||||||
/* We will have at least one string (even if it's just "") */
|
|
||||||
remotenames[0] = algolist;
|
|
||||||
remotecount = 1;
|
|
||||||
for (i = 0; i < len; i++) {
|
|
||||||
if (algolist[i] == '\0') {
|
|
||||||
/* someone is trying something strange */
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
if (algolist[i] == ',') {
|
|
||||||
algolist[i] = '\0';
|
|
||||||
remotenames[remotecount] = &algolist[i+1];
|
|
||||||
remotecount++;
|
|
||||||
}
|
|
||||||
if (remotecount >= MAX_PROPOSED_ALGO) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (kexguess2 && *kexguess2 == KEXGUESS2_LOOK) {
|
|
||||||
for (i = 0; i < remotecount; i++)
|
|
||||||
{
|
|
||||||
if (strcmp(remotenames[i], KEXGUESS2_ALGO_NAME) == 0) {
|
|
||||||
*kexguess2 = KEXGUESS2_YES;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (*kexguess2 == KEXGUESS2_LOOK) {
|
|
||||||
*kexguess2 = KEXGUESS2_NO;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; localalgos[i].name != NULL; i++) {
|
for (i = 0; localalgos[i].name != NULL; i++) {
|
||||||
if (localalgos[i].usable) {
|
if (localalgos[i].usable) {
|
||||||
@ -424,12 +462,11 @@ algo_type * buf_match_algo(buffer* buf, algo_type localalgos[],
|
|||||||
}
|
}
|
||||||
if (strcmp(servnames[j], clinames[i]) == 0) {
|
if (strcmp(servnames[j], clinames[i]) == 0) {
|
||||||
/* set if it was a good guess */
|
/* set if it was a good guess */
|
||||||
if (goodguess && kexguess2) {
|
if (goodguess != NULL) {
|
||||||
if (*kexguess2 == KEXGUESS2_YES) {
|
if (kexguess2) {
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
*goodguess = 1;
|
*goodguess = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (i == 0 && j == 0) {
|
if (i == 0 && j == 0) {
|
||||||
*goodguess = 1;
|
*goodguess = 1;
|
||||||
|
30
common-kex.c
30
common-kex.c
@ -820,21 +820,33 @@ static void read_kex_algos() {
|
|||||||
int goodguess = 0;
|
int goodguess = 0;
|
||||||
int allgood = 1; /* we AND this with each goodguess and see if its still
|
int allgood = 1; /* we AND this with each goodguess and see if its still
|
||||||
true after */
|
true after */
|
||||||
|
int kexguess2 = 0;
|
||||||
#if DROPBEAR_KEXGUESS2
|
|
||||||
enum kexguess2_used kexguess2 = KEXGUESS2_LOOK;
|
|
||||||
#else
|
|
||||||
enum kexguess2_used kexguess2 = KEXGUESS2_NO;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
buf_incrpos(ses.payload, 16); /* start after the cookie */
|
buf_incrpos(ses.payload, 16); /* start after the cookie */
|
||||||
|
|
||||||
memset(ses.newkeys, 0x0, sizeof(*ses.newkeys));
|
memset(ses.newkeys, 0x0, sizeof(*ses.newkeys));
|
||||||
|
|
||||||
/* kex_algorithms */
|
/* kex_algorithms */
|
||||||
algo = buf_match_algo(ses.payload, sshkex, &kexguess2, &goodguess);
|
#if DROPBEAR_KEXGUESS2
|
||||||
|
if (buf_has_algo(ses.payload, KEXGUESS2_ALGO_NAME) == DROPBEAR_SUCCESS) {
|
||||||
|
kexguess2 = 1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* 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;
|
allgood &= goodguess;
|
||||||
if (algo == NULL || algo->val == KEXGUESS2_ALGO_ID) {
|
if (algo == NULL || algo->data == NULL) {
|
||||||
|
/* kexguess2, ext-info-c, ext-info-s should not match negotiation */
|
||||||
erralgo = "kex";
|
erralgo = "kex";
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
@ -843,7 +855,7 @@ static void read_kex_algos() {
|
|||||||
ses.newkeys->algo_kex = algo->data;
|
ses.newkeys->algo_kex = algo->data;
|
||||||
|
|
||||||
/* server_host_key_algorithms */
|
/* server_host_key_algorithms */
|
||||||
algo = buf_match_algo(ses.payload, sshhostkey, &kexguess2, &goodguess);
|
algo = buf_match_algo(ses.payload, sshhostkey, kexguess2, &goodguess);
|
||||||
allgood &= goodguess;
|
allgood &= goodguess;
|
||||||
if (algo == NULL) {
|
if (algo == NULL) {
|
||||||
erralgo = "hostkey";
|
erralgo = "hostkey";
|
||||||
|
@ -29,7 +29,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
|||||||
sign_key *key = new_sign_key();
|
sign_key *key = new_sign_key();
|
||||||
enum signkey_type keytype = DROPBEAR_SIGNKEY_ANY;
|
enum signkey_type keytype = DROPBEAR_SIGNKEY_ANY;
|
||||||
if (buf_get_pub_key(fuzz.input, key, &keytype) == DROPBEAR_SUCCESS) {
|
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) {
|
if (keytype == DROPBEAR_SIGNKEY_RSA) {
|
||||||
/* Flip a coin to decide rsa signature type */
|
/* Flip a coin to decide rsa signature type */
|
||||||
int flag = buf_getbyte(fuzz_input);
|
int flag = buf_getbyte(fuzz_input);
|
||||||
@ -38,6 +38,8 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
|||||||
} else {
|
} else {
|
||||||
sigtype = DROPBEAR_SIGNATURE_RSA_SHA1;
|
sigtype = DROPBEAR_SIGNATURE_RSA_SHA1;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
sigtype = signature_type_from_signkey(keytype);
|
||||||
}
|
}
|
||||||
if (buf_verify(fuzz.input, key, sigtype, verifydata) == DROPBEAR_SUCCESS) {
|
if (buf_verify(fuzz.input, key, sigtype, verifydata) == DROPBEAR_SUCCESS) {
|
||||||
/* The fuzzer is capable of generating keys with a signature to match.
|
/* The fuzzer is capable of generating keys with a signature to match.
|
||||||
|
2
kex.h
2
kex.h
@ -65,6 +65,8 @@ void recv_msg_kexdh_init(void); /* server */
|
|||||||
void send_msg_kexdh_init(void); /* client */
|
void send_msg_kexdh_init(void); /* client */
|
||||||
void recv_msg_kexdh_reply(void); /* client */
|
void recv_msg_kexdh_reply(void); /* client */
|
||||||
|
|
||||||
|
void recv_msg_ext_info(void);
|
||||||
|
|
||||||
struct KEXState {
|
struct KEXState {
|
||||||
|
|
||||||
unsigned sentkexinit : 1; /*set when we've sent/recv kexinit packet */
|
unsigned sentkexinit : 1; /*set when we've sent/recv kexinit packet */
|
||||||
|
11
session.h
11
session.h
@ -186,6 +186,9 @@ struct sshsession {
|
|||||||
/* Enables/disables compression */
|
/* Enables/disables compression */
|
||||||
algo_type *compress_algos;
|
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
|
/* a list of queued replies that should be sent after a KEX has
|
||||||
concluded (ie, while dataallowed was unset)*/
|
concluded (ie, while dataallowed was unset)*/
|
||||||
struct packetlist *reply_queue_head, *reply_queue_tail;
|
struct packetlist *reply_queue_head, *reply_queue_tail;
|
||||||
@ -313,13 +316,7 @@ struct clientsession {
|
|||||||
#endif
|
#endif
|
||||||
sign_key *lastprivkey;
|
sign_key *lastprivkey;
|
||||||
|
|
||||||
enum signature_type server_sig_algs[DROPBEAR_SIGNKEY_NUM_NAMED+1];
|
buffer *server_sig_algs;
|
||||||
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
|
|
||||||
|
|
||||||
int retval; /* What the command exit status was - we emulate it */
|
int retval; /* What the command exit status was - we emulate it */
|
||||||
#if 0
|
#if 0
|
||||||
|
17
signkey.c
17
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) {
|
const char* signature_name_from_type(enum signature_type type, unsigned int *namelen) {
|
||||||
#if DROPBEAR_RSA_SHA256
|
#if DROPBEAR_RSA_SHA256
|
||||||
if (type == DROPBEAR_SIGNATURE_RSA_SHA256) {
|
if (type == DROPBEAR_SIGNATURE_RSA_SHA256) {
|
||||||
|
if (namelen) {
|
||||||
*namelen = strlen(SSH_SIGNATURE_RSA_SHA256);
|
*namelen = strlen(SSH_SIGNATURE_RSA_SHA256);
|
||||||
|
}
|
||||||
return SSH_SIGNATURE_RSA_SHA256;
|
return SSH_SIGNATURE_RSA_SHA256;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#if DROPBEAR_RSA_SHA1
|
#if DROPBEAR_RSA_SHA1
|
||||||
if (type == DROPBEAR_SIGNATURE_RSA_SHA1) {
|
if (type == DROPBEAR_SIGNATURE_RSA_SHA1) {
|
||||||
|
if (namelen) {
|
||||||
*namelen = strlen(SSH_SIGNKEY_RSA);
|
*namelen = strlen(SSH_SIGNKEY_RSA);
|
||||||
|
}
|
||||||
return SSH_SIGNKEY_RSA;
|
return SSH_SIGNKEY_RSA;
|
||||||
}
|
}
|
||||||
#endif
|
#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);
|
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) {
|
enum signkey_type signkey_type_from_signature(enum signature_type sigtype) {
|
||||||
#if DROPBEAR_RSA_SHA256
|
#if DROPBEAR_RSA_SHA256
|
||||||
if (sigtype == DROPBEAR_SIGNATURE_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
|
#if DEBUG_TRACE
|
||||||
{
|
{
|
||||||
int namelen;
|
const char* signame = signature_name_from_type(sigtype, NULL);
|
||||||
const char* signame = signature_name_from_type(sigtype, &namelen);
|
|
||||||
TRACE(("buf_put_sign type %d %s", sigtype, signame));
|
TRACE(("buf_put_sign type %d %s", sigtype, signame));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -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);
|
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 signature_type signature_type_from_name(const char* name, unsigned int namelen);
|
||||||
enum signkey_type signkey_type_from_signature(enum signature_type sigtype);
|
enum signkey_type signkey_type_from_signature(enum signature_type sigtype);
|
||||||
|
enum signature_type signature_type_from_signkey(enum signkey_type keytype);
|
||||||
|
|
||||||
int buf_get_pub_key(buffer *buf, sign_key *key, enum signkey_type *type);
|
int buf_get_pub_key(buffer *buf, sign_key *key, enum signkey_type *type);
|
||||||
int buf_get_priv_key(buffer* buf, sign_key *key, enum signkey_type *type);
|
int buf_get_priv_key(buffer* buf, sign_key *key, enum signkey_type *type);
|
||||||
void buf_put_pub_key(buffer* buf, sign_key *key, enum signkey_type type);
|
void buf_put_pub_key(buffer* buf, sign_key *key, enum signkey_type type);
|
||||||
|
6
ssh.h
6
ssh.h
@ -32,6 +32,7 @@
|
|||||||
#define SSH_MSG_DEBUG 4
|
#define SSH_MSG_DEBUG 4
|
||||||
#define SSH_MSG_SERVICE_REQUEST 5
|
#define SSH_MSG_SERVICE_REQUEST 5
|
||||||
#define SSH_MSG_SERVICE_ACCEPT 6
|
#define SSH_MSG_SERVICE_ACCEPT 6
|
||||||
|
#define SSH_MSG_EXT_INFO 7
|
||||||
#define SSH_MSG_KEXINIT 20
|
#define SSH_MSG_KEXINIT 20
|
||||||
#define SSH_MSG_NEWKEYS 21
|
#define SSH_MSG_NEWKEYS 21
|
||||||
#define SSH_MSG_KEXDH_INIT 30
|
#define SSH_MSG_KEXDH_INIT 30
|
||||||
@ -94,6 +95,11 @@
|
|||||||
#define SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE 14
|
#define SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE 14
|
||||||
#define SSH_DISCONNECT_ILLEGAL_USER_NAME 15
|
#define SSH_DISCONNECT_ILLEGAL_USER_NAME 15
|
||||||
|
|
||||||
|
/* rfc8308 */
|
||||||
|
#define SSH_EXT_INFO_S "ext-info-s"
|
||||||
|
#define SSH_EXT_INFO_C "ext-info-c"
|
||||||
|
#define SSH_SERVER_SIG_ALGS "server-sig-algs"
|
||||||
|
|
||||||
/* service types */
|
/* service types */
|
||||||
#define SSH_SERVICE_USERAUTH "ssh-userauth"
|
#define SSH_SERVICE_USERAUTH "ssh-userauth"
|
||||||
#define SSH_SERVICE_USERAUTH_LEN 12
|
#define SSH_SERVICE_USERAUTH_LEN 12
|
||||||
|
@ -330,13 +330,16 @@ static void svr_remoteclosed() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void svr_algos_initialise(void) {
|
static void svr_algos_initialise(void) {
|
||||||
#if DROPBEAR_DH_GROUP1 && DROPBEAR_DH_GROUP1_CLIENTONLY
|
|
||||||
algo_type *algo;
|
algo_type *algo;
|
||||||
for (algo = sshkex; algo->name; algo++) {
|
for (algo = sshkex; algo->name; algo++) {
|
||||||
|
#if DROPBEAR_DH_GROUP1 && DROPBEAR_DH_GROUP1_CLIENTONLY
|
||||||
if (strcmp(algo->name, "diffie-hellman-group1-sha1") == 0) {
|
if (strcmp(algo->name, "diffie-hellman-group1-sha1") == 0) {
|
||||||
algo->usable = 0;
|
algo->usable = 0;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
if (strcmp(algo->name, SSH_EXT_INFO_C) == 0) {
|
||||||
|
algo->usable = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user