Skip to content

Commit

Permalink
dropbear: backport strict KEX mode
Browse files Browse the repository at this point in the history
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
dangowrt committed Jan 24, 2024
1 parent 3cf1fe5 commit ef4771e
Show file tree
Hide file tree
Showing 2 changed files with 217 additions and 1 deletion.
2 changes: 1 addition & 1 deletion package/network/services/dropbear/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk

PKG_NAME:=dropbear
PKG_VERSION:=2022.82
PKG_RELEASE:=5
PKG_RELEASE:=6

PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.bz2
PKG_SOURCE_URL:= \
Expand Down
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;
+ }
}
}

0 comments on commit ef4771e

Please sign in to comment.