changeset 1676:d5cdc60db08e

ext-info handling for server-sig-algs only client side is handled
author Matt Johnston <matt@ucc.asn.au>
date Tue, 19 May 2020 00:31:41 +0800
parents ae41624c2198
children e05c0e394f1d
files algo.h buffer.c buffer.h cli-authpubkey.c cli-kex.c cli-main.c cli-session.c common-algo.c common-kex.c fuzzer-verify.c kex.h session.h signkey.c signkey.h ssh.h svr-session.c
diffstat 16 files changed, 256 insertions(+), 91 deletions(-) [+]
line wrap: on
line diff
--- a/algo.h	Sun May 17 23:58:31 2020 +0800
+++ b/algo.h	Tue May 19 00:31:41 2020 +0800
@@ -114,18 +114,11 @@
 
 void buf_put_algolist(buffer * buf, const algo_type localalgos[]);
 
-enum kexguess2_used {
-	KEXGUESS2_LOOK,
-	KEXGUESS2_NO,
-	KEXGUESS2_YES,
-};
+#define KEXGUESS2_ALGO_NAME "[email protected]"
 
-#define KEXGUESS2_ALGO_NAME "[email protected]"
-#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, 
--- a/buffer.c	Sun May 17 23:58:31 2020 +0800
+++ b/buffer.c	Tue May 19 00:31:41 2020 +0800
@@ -228,19 +228,37 @@
 }
 
 /* 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) {
--- a/buffer.h	Sun May 17 23:58:31 2020 +0800
+++ b/buffer.h	Tue May 19 00:31:41 2020 +0800
@@ -58,6 +58,7 @@
 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);
--- a/cli-authpubkey.c	Sun May 17 23:58:31 2020 +0800
+++ b/cli-authpubkey.c	Tue May 19 00:31:41 2020 +0800
@@ -184,6 +184,7 @@
 
 /* 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 @@
 		/* 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;
--- a/cli-kex.c	Sun May 17 23:58:31 2020 +0800
+++ b/cli-kex.c	Tue May 19 00:31:41 2020 +0800
@@ -411,3 +411,28 @@
 	}
 	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);
+	}
+}
--- a/cli-main.c	Sun May 17 23:58:31 2020 +0800
+++ b/cli-main.c	Tue May 19 00:31:41 2020 +0800
@@ -106,6 +106,7 @@
 
 	/* 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) {
--- a/cli-session.c	Sun May 17 23:58:31 2020 +0800
+++ b/cli-session.c	Tue May 19 00:31:41 2020 +0800
@@ -81,6 +81,7 @@
 	{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 @@
 	(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() {
--- a/common-algo.c	Sun May 17 23:58:31 2020 +0800
+++ b/common-algo.c	Tue May 19 00:31:41 2020 +0800
@@ -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_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 @@
 	{"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 @@
 	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 @@
 	/* 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 @@
 			}
 			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;
--- a/common-kex.c	Sun May 17 23:58:31 2020 +0800
+++ b/common-kex.c	Tue May 19 00:31:41 2020 +0800
@@ -820,21 +820,33 @@
 	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 @@
 	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";
--- a/fuzzer-verify.c	Sun May 17 23:58:31 2020 +0800
+++ b/fuzzer-verify.c	Tue May 19 00:31:41 2020 +0800
@@ -29,7 +29,7 @@
 		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 @@
 				} 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.
--- a/kex.h	Sun May 17 23:58:31 2020 +0800
+++ b/kex.h	Tue May 19 00:31:41 2020 +0800
@@ -65,6 +65,8 @@
 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 */
--- a/session.h	Sun May 17 23:58:31 2020 +0800
+++ b/session.h	Tue May 19 00:31:41 2020 +0800
@@ -185,6 +185,9 @@
 
 	/* 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 @@
 #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
--- a/signkey.c	Sun May 17 23:58:31 2020 +0800
+++ b/signkey.c	Tue May 19 00:31:41 2020 +0800
@@ -114,13 +114,17 @@
 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 @@
 	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 @@
 
 #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
--- a/signkey.h	Sun May 17 23:58:31 2020 +0800
+++ b/signkey.h	Tue May 19 00:31:41 2020 +0800
@@ -120,6 +120,8 @@
 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);
--- a/ssh.h	Sun May 17 23:58:31 2020 +0800
+++ b/ssh.h	Tue May 19 00:31:41 2020 +0800
@@ -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
--- a/svr-session.c	Sun May 17 23:58:31 2020 +0800
+++ b/svr-session.c	Tue May 19 00:31:41 2020 +0800
@@ -330,13 +330,16 @@
 }
 
 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;
+		}
 	}
-#endif
 }