forked from openwrt/openwrt
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Backport strict KEX mode as specified by OpenSSH with kex-strict-c-v00@openssh.com and kex-strict-s-v00@openssh.com. Fixes: CVE-2023-48795 ("Terrapin") Signed-off-by: Daniel Golle <daniel@makrotopia.org>
- Loading branch information
Showing
2 changed files
with
217 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
216 changes: 216 additions & 0 deletions
216
package/network/services/dropbear/patches/010-Implement-Strict-KEX-mode.patch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,216 @@ | ||
From 6e43be5c7b99dbee49dc72b6f989f29fdd7e9356 Mon Sep 17 00:00:00 2001 | ||
From: Matt Johnston <matt@ucc.asn.au> | ||
Date: Mon, 20 Nov 2023 14:02:47 +0800 | ||
Subject: [PATCH] Implement Strict KEX mode | ||
|
||
As specified by OpenSSH with kex-strict-c-v00@openssh.com and | ||
kex-strict-s-v00@openssh.com. | ||
--- | ||
src/cli-session.c | 11 +++++++++++ | ||
src/common-algo.c | 6 ++++++ | ||
src/common-kex.c | 26 +++++++++++++++++++++++++- | ||
src/kex.h | 3 +++ | ||
src/process-packet.c | 34 +++++++++++++++++++--------------- | ||
src/ssh.h | 4 ++++ | ||
src/svr-session.c | 3 +++ | ||
7 files changed, 71 insertions(+), 16 deletions(-) | ||
|
||
--- a/cli-session.c | ||
+++ b/cli-session.c | ||
@@ -46,6 +46,7 @@ static void cli_finished(void) ATTRIB_NO | ||
static void recv_msg_service_accept(void); | ||
static void cli_session_cleanup(void); | ||
static void recv_msg_global_request_cli(void); | ||
+static void cli_algos_initialise(void); | ||
|
||
struct clientsession cli_ses; /* GLOBAL */ | ||
|
||
@@ -117,6 +118,7 @@ void cli_session(int sock_in, int sock_o | ||
} | ||
|
||
chaninitialise(cli_chantypes); | ||
+ cli_algos_initialise(); | ||
|
||
/* Set up cli_ses vars */ | ||
cli_session_init(proxy_cmd_pid); | ||
@@ -487,3 +489,12 @@ void cli_dropbear_log(int priority, cons | ||
fflush(stderr); | ||
} | ||
|
||
+static void cli_algos_initialise(void) { | ||
+ algo_type *algo; | ||
+ for (algo = sshkex; algo->name; algo++) { | ||
+ if (strcmp(algo->name, SSH_STRICT_KEX_S) == 0) { | ||
+ algo->usable = 0; | ||
+ } | ||
+ } | ||
+} | ||
+ | ||
--- a/common-algo.c | ||
+++ b/common-algo.c | ||
@@ -315,6 +315,12 @@ algo_type sshkex[] = { | ||
{SSH_EXT_INFO_C, 0, NULL, 1, NULL}, | ||
#endif | ||
#endif | ||
+#if DROPBEAR_CLIENT | ||
+ {SSH_STRICT_KEX_C, 0, NULL, 1, NULL}, | ||
+#endif | ||
+#if DROPBEAR_SERVER | ||
+ {SSH_STRICT_KEX_S, 0, NULL, 1, NULL}, | ||
+#endif | ||
{NULL, 0, NULL, 0, NULL} | ||
}; | ||
|
||
--- a/common-kex.c | ||
+++ b/common-kex.c | ||
@@ -183,6 +183,10 @@ void send_msg_newkeys() { | ||
gen_new_keys(); | ||
switch_keys(); | ||
|
||
+ if (ses.kexstate.strict_kex) { | ||
+ ses.transseq = 0; | ||
+ } | ||
+ | ||
TRACE(("leave send_msg_newkeys")) | ||
} | ||
|
||
@@ -193,7 +197,11 @@ void recv_msg_newkeys() { | ||
|
||
ses.kexstate.recvnewkeys = 1; | ||
switch_keys(); | ||
- | ||
+ | ||
+ if (ses.kexstate.strict_kex) { | ||
+ ses.recvseq = 0; | ||
+ } | ||
+ | ||
TRACE(("leave recv_msg_newkeys")) | ||
} | ||
|
||
@@ -550,6 +558,10 @@ void recv_msg_kexinit() { | ||
|
||
ses.kexstate.recvkexinit = 1; | ||
|
||
+ if (ses.kexstate.strict_kex && !ses.kexstate.donefirstkex && ses.recvseq != 1) { | ||
+ dropbear_exit("First packet wasn't kexinit"); | ||
+ } | ||
+ | ||
TRACE(("leave recv_msg_kexinit")) | ||
} | ||
|
||
@@ -859,6 +871,18 @@ static void read_kex_algos() { | ||
} | ||
#endif | ||
|
||
+ if (!ses.kexstate.donefirstkex) { | ||
+ const char* strict_name; | ||
+ if (IS_DROPBEAR_CLIENT) { | ||
+ strict_name = SSH_STRICT_KEX_S; | ||
+ } else { | ||
+ strict_name = SSH_STRICT_KEX_C; | ||
+ } | ||
+ if (buf_has_algo(ses.payload, strict_name) == DROPBEAR_SUCCESS) { | ||
+ ses.kexstate.strict_kex = 1; | ||
+ } | ||
+ } | ||
+ | ||
algo = buf_match_algo(ses.payload, sshkex, kexguess2, &goodguess); | ||
allgood &= goodguess; | ||
if (algo == NULL || algo->data == NULL) { | ||
--- a/kex.h | ||
+++ b/kex.h | ||
@@ -83,6 +83,9 @@ struct KEXState { | ||
|
||
unsigned our_first_follows_matches : 1; | ||
|
||
+ /* Boolean indicating that strict kex mode is in use */ | ||
+ unsigned int strict_kex; | ||
+ | ||
time_t lastkextime; /* time of the last kex */ | ||
unsigned int datatrans; /* data transmitted since last kex */ | ||
unsigned int datarecv; /* data received since last kex */ | ||
--- a/process-packet.c | ||
+++ b/process-packet.c | ||
@@ -44,6 +44,7 @@ void process_packet() { | ||
|
||
unsigned char type; | ||
unsigned int i; | ||
+ unsigned int first_strict_kex = ses.kexstate.strict_kex && !ses.kexstate.donefirstkex; | ||
time_t now; | ||
|
||
TRACE2(("enter process_packet")) | ||
@@ -54,22 +55,24 @@ void process_packet() { | ||
now = monotonic_now(); | ||
ses.last_packet_time_keepalive_recv = now; | ||
|
||
- /* These packets we can receive at any time */ | ||
- switch(type) { | ||
|
||
- case SSH_MSG_IGNORE: | ||
- goto out; | ||
- case SSH_MSG_DEBUG: | ||
- goto out; | ||
- | ||
- case SSH_MSG_UNIMPLEMENTED: | ||
- /* debugging XXX */ | ||
- TRACE(("SSH_MSG_UNIMPLEMENTED")) | ||
- goto out; | ||
- | ||
- case SSH_MSG_DISCONNECT: | ||
- /* TODO cleanup? */ | ||
- dropbear_close("Disconnect received"); | ||
+ if (type == SSH_MSG_DISCONNECT) { | ||
+ /* Allowed at any time */ | ||
+ dropbear_close("Disconnect received"); | ||
+ } | ||
+ | ||
+ /* These packets may be received at any time, | ||
+ except during first kex with strict kex */ | ||
+ if (!first_strict_kex) { | ||
+ switch(type) { | ||
+ case SSH_MSG_IGNORE: | ||
+ goto out; | ||
+ case SSH_MSG_DEBUG: | ||
+ goto out; | ||
+ case SSH_MSG_UNIMPLEMENTED: | ||
+ TRACE(("SSH_MSG_UNIMPLEMENTED")) | ||
+ goto out; | ||
+ } | ||
} | ||
|
||
/* Ignore these packet types so that keepalives don't interfere with | ||
@@ -98,7 +101,8 @@ void process_packet() { | ||
if (type >= 1 && type <= 49 | ||
&& type != SSH_MSG_SERVICE_REQUEST | ||
&& type != SSH_MSG_SERVICE_ACCEPT | ||
- && type != SSH_MSG_KEXINIT) | ||
+ && type != SSH_MSG_KEXINIT | ||
+ && !first_strict_kex) | ||
{ | ||
TRACE(("unknown allowed packet during kexinit")) | ||
recv_unimplemented(); | ||
--- a/ssh.h | ||
+++ b/ssh.h | ||
@@ -100,6 +100,10 @@ | ||
#define SSH_EXT_INFO_C "ext-info-c" | ||
#define SSH_SERVER_SIG_ALGS "server-sig-algs" | ||
|
||
+/* OpenSSH strict KEX feature */ | ||
+#define SSH_STRICT_KEX_S "kex-strict-s-v00@openssh.com" | ||
+#define SSH_STRICT_KEX_C "kex-strict-c-v00@openssh.com" | ||
+ | ||
/* service types */ | ||
#define SSH_SERVICE_USERAUTH "ssh-userauth" | ||
#define SSH_SERVICE_USERAUTH_LEN 12 | ||
--- a/svr-session.c | ||
+++ b/svr-session.c | ||
@@ -368,6 +368,9 @@ static void svr_algos_initialise(void) { | ||
algo->usable = 0; | ||
} | ||
#endif | ||
+ if (strcmp(algo->name, SSH_STRICT_KEX_C) == 0) { | ||
+ algo->usable = 0; | ||
+ } | ||
} | ||
} | ||
|