mirror of
https://github.com/openembedded/meta-openembedded.git
synced 2026-01-12 03:24:08 +00:00
proftpd: Fix CVE-2023-48795
Upstream-Status: Backport frombcec15efe6Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> Signed-off-by: Gyorgy Sarvari <skandigraun@gmail.com> (cherry picked from commit6c8ae54fc3) Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com> Signed-off-by: Anuj Mittal <anuj.mittal@oss.qualcomm.com>
This commit is contained in:
committed by
Anuj Mittal
parent
5775e1a643
commit
b4812b18ee
@@ -0,0 +1,785 @@
|
||||
From 05afec052cea4de87a8467f9c5583ffe226ce06e Mon Sep 17 00:00:00 2001
|
||||
From: TJ Saunders <tj@castaglia.org>
|
||||
Date: Tue, 19 Dec 2023 18:55:58 -0800
|
||||
Subject: [PATCH] Issue #1760: Implement the "strict KEX" mitigations for the
|
||||
Terrapin SSH protocol attack (CVE-2023-48795).
|
||||
|
||||
Upstream-Status: Backport [https://github.com/proftpd/proftpd/commit/bcec15efe6c53dac40420731013f1cd2fd54123b]
|
||||
CVE: CVE-2023-48795
|
||||
Signed-off-by: Vijay Anusuri <vanusuri@mvista.com>
|
||||
Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
|
||||
---
|
||||
contrib/mod_sftp/kex.c | 126 +++++++---
|
||||
contrib/mod_sftp/mod_sftp.c | 8 +-
|
||||
contrib/mod_sftp/mod_sftp.h.in | 35 +--
|
||||
contrib/mod_sftp/packet.c | 12 +
|
||||
contrib/mod_sftp/packet.h | 9 +-
|
||||
contrib/mod_sftp/tap.c | 26 ++-
|
||||
contrib/mod_sftp/tap.h | 5 +-
|
||||
doc/contrib/mod_sftp.html | 15 +-
|
||||
tests/t/lib/ProFTPD/Tests/Modules/mod_sftp.pm | 217 ++++++++++++++++++
|
||||
9 files changed, 398 insertions(+), 55 deletions(-)
|
||||
|
||||
diff --git a/contrib/mod_sftp/kex.c b/contrib/mod_sftp/kex.c
|
||||
index 6a19c88bb..3d9068139 100644
|
||||
--- a/contrib/mod_sftp/kex.c
|
||||
+++ b/contrib/mod_sftp/kex.c
|
||||
@@ -149,6 +149,13 @@ static struct sftp_kex *kex_first_kex = NULL;
|
||||
static struct sftp_kex *kex_rekey_kex = NULL;
|
||||
static int kex_sent_kexinit = FALSE;
|
||||
|
||||
+/* Using strict kex? Note that we maintain this value here, rather than
|
||||
+ * in the sftp_kex struct, so that any "use strict KEX" flag set via the
|
||||
+ * first KEXINIT is used through any subsequent KEXINITs.
|
||||
+ */
|
||||
+static int use_strict_kex = FALSE;
|
||||
+static int kex_done_first_kex = FALSE;
|
||||
+
|
||||
/* Diffie-Hellman group moduli */
|
||||
|
||||
static const char *dh_group1_str =
|
||||
@@ -1578,6 +1585,16 @@ static const char *get_kexinit_exchange_list(pool *p) {
|
||||
res = pstrcat(p, res, *res ? "," : "", pstrdup(p, "ext-info-s"), NULL);
|
||||
}
|
||||
|
||||
+ if (!(sftp_opts & SFTP_OPT_NO_STRICT_KEX)) {
|
||||
+ /* Indicate support for OpenSSH's custom "strict KEX" mode extension,
|
||||
+ * but only if we have not done/completed our first KEX.
|
||||
+ */
|
||||
+ if (kex_done_first_kex == FALSE) {
|
||||
+ res = pstrcat(p, res, *res ? "," : "",
|
||||
+ pstrdup(p, "kex-strict-s-v00@openssh.com"), NULL);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -2159,6 +2176,21 @@ static int get_session_names(struct sftp_kex *kex, int *correct_guess) {
|
||||
pr_trace_msg(trace_channel, 20, "client %s EXT_INFO support",
|
||||
kex->use_ext_info ? "signaled" : "did not signal" );
|
||||
|
||||
+ if (!(sftp_opts & SFTP_OPT_NO_STRICT_KEX)) {
|
||||
+ /* Did the client indicate "strict kex" support (Issue 1760)?
|
||||
+ *
|
||||
+ * Note that we only check for this if it is our first KEXINIT.
|
||||
+ * The "strict kex" extension is ignored in any subsequent KEXINITs, as
|
||||
+ * for rekeys.
|
||||
+ */
|
||||
+ if (kex_done_first_kex == FALSE) {
|
||||
+ use_strict_kex = sftp_misc_namelist_contains(kex->pool,
|
||||
+ client_list, "kex-strict-c-v00@openssh.com");
|
||||
+ pr_trace_msg(trace_channel, 20, "client %s strict KEX support",
|
||||
+ use_strict_kex ? "signaled" : "did not signal" );
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
} else {
|
||||
(void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
|
||||
"no shared key exchange algorithm found (client sent '%s', server sent "
|
||||
@@ -4391,7 +4423,6 @@ static int handle_kex_ecdh(struct ssh2_packet *pkt, struct sftp_kex *kex) {
|
||||
destroy_pool(pkt->pool);
|
||||
return 0;
|
||||
}
|
||||
-
|
||||
#endif /* PR_USE_OPENSSL_ECC */
|
||||
|
||||
static struct ssh2_packet *read_kex_packet(pool *p, struct sftp_kex *kex,
|
||||
@@ -4442,6 +4473,10 @@ static struct ssh2_packet *read_kex_packet(pool *p, struct sftp_kex *kex,
|
||||
/* Per RFC 4253, Section 11, DEBUG, DISCONNECT, IGNORE, and UNIMPLEMENTED
|
||||
* messages can occur at any time, even during KEX. We have to be prepared
|
||||
* for this, and Do The Right Thing(tm).
|
||||
+ *
|
||||
+ * However, due to the Terrapin attack, if we are using a "strict KEX"
|
||||
+ * mode, then only DISCONNECT messages can occur during KEX; DEBUG,
|
||||
+ * IGNORE, and UNIMPLEMENTED messages will lead to disconnecting.
|
||||
*/
|
||||
|
||||
mesg_type = sftp_ssh2_packet_get_mesg_type(pkt);
|
||||
@@ -4469,36 +4504,44 @@ static struct ssh2_packet *read_kex_packet(pool *p, struct sftp_kex *kex,
|
||||
break;
|
||||
}
|
||||
|
||||
- switch (mesg_type) {
|
||||
- case SFTP_SSH2_MSG_DEBUG:
|
||||
- sftp_ssh2_packet_handle_debug(pkt);
|
||||
- pr_response_set_pool(NULL);
|
||||
- pkt = NULL;
|
||||
- break;
|
||||
-
|
||||
+ switch (msg_type) {
|
||||
+ /* DISCONNECT messages are always allowed. */
|
||||
case SFTP_SSH2_MSG_DISCONNECT:
|
||||
sftp_ssh2_packet_handle_disconnect(pkt);
|
||||
pr_response_set_pool(NULL);
|
||||
pkt = NULL;
|
||||
break;
|
||||
|
||||
+ case SFTP_SSH2_MSG_DEBUG:
|
||||
+ if (use_strict_kex == FALSE) {
|
||||
+ sftp_ssh2_packet_handle_debug(pkt);
|
||||
+ pr_response_set_pool(NULL);
|
||||
+ pkt = NULL;
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
case SFTP_SSH2_MSG_IGNORE:
|
||||
- sftp_ssh2_packet_handle_ignore(pkt);
|
||||
- pr_response_set_pool(NULL);
|
||||
- pkt = NULL;
|
||||
- break;
|
||||
+ if (use_strict_kex == FALSE) {
|
||||
+ sftp_ssh2_packet_handle_ignore(pkt);
|
||||
+ pr_response_set_pool(NULL);
|
||||
+ pkt = NULL;
|
||||
+ break;
|
||||
+ }
|
||||
|
||||
case SFTP_SSH2_MSG_UNIMPLEMENTED:
|
||||
- sftp_ssh2_packet_handle_unimplemented(pkt);
|
||||
- pr_response_set_pool(NULL);
|
||||
- pkt = NULL;
|
||||
- break;
|
||||
+ if (use_strict_kex == FALSE) {
|
||||
+ sftp_ssh2_packet_handle_unimplemented(pkt);
|
||||
+ pr_response_set_pool(NULL);
|
||||
+ pkt = NULL;
|
||||
+ break;
|
||||
+ }
|
||||
|
||||
default:
|
||||
/* For any other message type, it's considered a protocol error. */
|
||||
(void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
|
||||
- "received %s (%d) unexpectedly, disconnecting",
|
||||
- sftp_ssh2_packet_get_mesg_type_desc(mesg_type), mesg_type);
|
||||
+ "received %s (%d) unexpectedly%s, disconnecting",
|
||||
+ sftp_ssh2_packet_get_msg_type_desc(msg_type), msg_type,
|
||||
+ use_strict_kex ? " during strict KEX" : "");
|
||||
pr_response_set_pool(NULL);
|
||||
destroy_kex(kex);
|
||||
destroy_pool(pkt->pool);
|
||||
@@ -4519,7 +4562,7 @@ int sftp_kex_handle(struct ssh2_packet *pkt) {
|
||||
* initial connect (kex_first_kex not null), or because we
|
||||
* are in a server-initiated rekeying (kex_rekey_kex not null).
|
||||
*/
|
||||
- if (kex_first_kex) {
|
||||
+ if (kex_first_kex != NULL) {
|
||||
kex = kex_first_kex;
|
||||
|
||||
/* We need to assign the client/server versions, which this struct
|
||||
@@ -4528,7 +4571,7 @@ int sftp_kex_handle(struct ssh2_packet *pkt) {
|
||||
kex->client_version = kex_client_version;
|
||||
kex->server_version = kex_server_version;
|
||||
|
||||
- } else if (kex_rekey_kex) {
|
||||
+ } else if (kex_rekey_kex != NULL) {
|
||||
kex = kex_rekey_kex;
|
||||
|
||||
} else {
|
||||
@@ -4564,6 +4607,24 @@ int sftp_kex_handle(struct ssh2_packet *pkt) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
+ if (use_strict_kex == TRUE &&
|
||||
+ kex_done_first_kex == FALSE) {
|
||||
+ uint32_t client_seqno;
|
||||
+
|
||||
+ client_seqno = sftp_ssh2_packet_get_client_seqno();
|
||||
+ if (client_seqno != 1) {
|
||||
+ /* Receiving any messages other than a KEXINIT as the first client
|
||||
+ * message indicates the possibility of the Terrapin attack being
|
||||
+ * conducted (Issue 1760). Thus we disconnect the client in such
|
||||
+ * cases.
|
||||
+ */
|
||||
+ (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
|
||||
+ "'strict KEX' violation, as KEXINIT was not the first message; disconnecting");
|
||||
+ destroy_kex(kex);
|
||||
+ SFTP_DISCONNECT_CONN(SFTP_SSH2_DISCONNECT_BY_APPLICATION, NULL);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
/* Once we have received the client KEXINIT message, we can compare what we
|
||||
* want to send against what we already received from the client.
|
||||
*
|
||||
@@ -4622,7 +4683,7 @@ int sftp_kex_handle(struct ssh2_packet *pkt) {
|
||||
|
||||
destroy_pool(pkt->pool);
|
||||
|
||||
- if (!kex_sent_kexinit) {
|
||||
+ if (kex_sent_kexinit == FALSE) {
|
||||
pkt = sftp_ssh2_packet_create(kex_pool);
|
||||
res = write_kexinit(pkt, kex);
|
||||
if (res < 0) {
|
||||
@@ -4645,7 +4706,7 @@ int sftp_kex_handle(struct ssh2_packet *pkt) {
|
||||
}
|
||||
}
|
||||
|
||||
- if (!kex_sent_kexinit) {
|
||||
+ if (kex_sent_kexinit == FALSE) {
|
||||
pkt = sftp_ssh2_packet_create(kex_pool);
|
||||
res = write_kexinit(pkt, kex);
|
||||
if (res < 0) {
|
||||
@@ -4770,7 +4831,7 @@ int sftp_kex_handle(struct ssh2_packet *pkt) {
|
||||
NULL, 1, SFTP_SSH2_MSG_NEWKEYS);
|
||||
|
||||
/* If we didn't send our NEWKEYS message earlier, do it now. */
|
||||
- if (!sent_newkeys) {
|
||||
+ if (sent_newkeys == FALSE) {
|
||||
struct ssh2_packet *pkt2;
|
||||
|
||||
pr_trace_msg(trace_channel, 9, "sending NEWKEYS message to client");
|
||||
@@ -4794,6 +4855,11 @@ int sftp_kex_handle(struct ssh2_packet *pkt) {
|
||||
destroy_pool(pkt2->pool);
|
||||
}
|
||||
|
||||
+ if (use_strict_kex == TRUE) {
|
||||
+ sftp_ssh2_packet_reset_client_seqno();
|
||||
+ sftp_ssh2_packet_reset_server_seqno();
|
||||
+ }
|
||||
+
|
||||
/* Last but certainly not least, set up the keys for encryption and
|
||||
* authentication, based on H and K.
|
||||
*/
|
||||
@@ -4813,6 +4879,9 @@ int sftp_kex_handle(struct ssh2_packet *pkt) {
|
||||
destroy_pool(pkt->pool);
|
||||
cmd = NULL;
|
||||
|
||||
+ /* We've now completed our KEX, possibly our first. */
|
||||
+ kex_done_first_kex = TRUE;
|
||||
+
|
||||
/* If extension negotiation has not been disabled, AND if we have not
|
||||
* received a service request, AND if the client sent "ext-info-c", THEN
|
||||
* send our EXT_INFO. We do not want send this during rekeys.
|
||||
@@ -4849,6 +4918,12 @@ int sftp_kex_handle(struct ssh2_packet *pkt) {
|
||||
cmd = NULL;
|
||||
}
|
||||
|
||||
+ /* Only start the TAP timer after we have completed our first KEX.
|
||||
+ * Otherwise, we risk sending "illegal" packets prior to, or during,
|
||||
+ * a "strict KEX" session (Issue 1760).
|
||||
+ */
|
||||
+ sftp_tap_start_policy();
|
||||
+
|
||||
/* Reset this flag for the next time through. */
|
||||
kex_sent_kexinit = FALSE;
|
||||
|
||||
@@ -4878,7 +4953,7 @@ int sftp_kex_free(void) {
|
||||
destroy_kex(rekey_kex);
|
||||
}
|
||||
|
||||
- if (kex_pool) {
|
||||
+ if (kex_pool != NULL) {
|
||||
destroy_pool(kex_pool);
|
||||
kex_pool = NULL;
|
||||
}
|
||||
@@ -5050,7 +5125,7 @@ int sftp_kex_send_first_kexinit(void) {
|
||||
struct ssh2_packet *pkt;
|
||||
int res;
|
||||
|
||||
- if (!kex_pool) {
|
||||
+ if (kex_pool == NULL) {
|
||||
kex_pool = make_sub_pool(sftp_pool);
|
||||
pr_pool_tag(kex_pool, "Kex Pool");
|
||||
}
|
||||
@@ -5085,4 +5160,3 @@ int sftp_kex_send_first_kexinit(void) {
|
||||
destroy_pool(pkt->pool);
|
||||
return 0;
|
||||
}
|
||||
-
|
||||
diff --git a/contrib/mod_sftp/mod_sftp.c b/contrib/mod_sftp/mod_sftp.c
|
||||
index a88162c0d..1f94e4d9d 100644
|
||||
--- a/contrib/mod_sftp/mod_sftp.c
|
||||
+++ b/contrib/mod_sftp/mod_sftp.c
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* ProFTPD - mod_sftp
|
||||
- * Copyright (c) 2008-2022 TJ Saunders
|
||||
+ * Copyright (c) 2008-2023 TJ Saunders
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -1415,8 +1415,9 @@ MODRET set_sftpoptions(cmd_rec *cmd) {
|
||||
config_rec *c;
|
||||
unsigned long opts = 0UL;
|
||||
|
||||
- if (cmd->argc-1 == 0)
|
||||
+ if (cmd->argc-1 == 0) {
|
||||
CONF_ERROR(cmd, "wrong number of parameters");
|
||||
+ }
|
||||
|
||||
CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
|
||||
|
||||
@@ -1480,6 +1481,9 @@ MODRET set_sftpoptions(cmd_rec *cmd) {
|
||||
} else if (strcmp(cmd->argv[i], "NoExtensionNegotiation") == 0) {
|
||||
opts |= SFTP_OPT_NO_EXT_INFO;
|
||||
|
||||
+ } else if (strcmp(cmd->argv[i], "NoStrictKex") == 0) {
|
||||
+ opts |= SFTP_OPT_NO_STRICT_KEX;
|
||||
+
|
||||
} else {
|
||||
CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, ": unknown SFTPOption '",
|
||||
cmd->argv[i], "'", NULL));
|
||||
diff --git a/contrib/mod_sftp/mod_sftp.h.in b/contrib/mod_sftp/mod_sftp.h.in
|
||||
index 9af590e8e..bde722f23 100644
|
||||
--- a/contrib/mod_sftp/mod_sftp.h.in
|
||||
+++ b/contrib/mod_sftp/mod_sftp.h.in
|
||||
@@ -126,23 +126,24 @@
|
||||
#define SFTP_SESS_STATE_HAVE_EXT_INFO 0x00010
|
||||
|
||||
/* mod_sftp option flags */
|
||||
-#define SFTP_OPT_IGNORE_SFTP_UPLOAD_PERMS 0x00001
|
||||
-#define SFTP_OPT_IGNORE_SCP_UPLOAD_PERMS 0x00002
|
||||
-#define SFTP_OPT_PESSIMISTIC_KEXINIT 0x00004
|
||||
-#define SFTP_OPT_OLD_PROTO_COMPAT 0x00008
|
||||
-#define SFTP_OPT_MATCH_KEY_SUBJECT 0x00010
|
||||
-#define SFTP_OPT_IGNORE_SFTP_SET_PERMS 0x00020
|
||||
-#define SFTP_OPT_IGNORE_SFTP_SET_TIMES 0x00040
|
||||
-#define SFTP_OPT_IGNORE_SFTP_SET_OWNERS 0x00080
|
||||
-#define SFTP_OPT_IGNORE_SCP_UPLOAD_TIMES 0x00100
|
||||
-#define SFTP_OPT_ALLOW_INSECURE_LOGIN 0x00200
|
||||
-#define SFTP_OPT_INSECURE_HOSTKEY_PERMS 0x00400
|
||||
-#define SFTP_OPT_ALLOW_WEAK_DH 0x00800
|
||||
-#define SFTP_OPT_IGNORE_FIFOS 0x01000
|
||||
-#define SFTP_OPT_IGNORE_SFTP_UPLOAD_XATTRS 0x02000
|
||||
-#define SFTP_OPT_IGNORE_SFTP_SET_XATTRS 0x04000
|
||||
-#define SFTP_OPT_INCLUDE_SFTP_TIMES 0x08000
|
||||
-#define SFTP_OPT_NO_EXT_INFO 0x10000
|
||||
+#define SFTP_OPT_IGNORE_SFTP_UPLOAD_PERMS 0x000001
|
||||
+#define SFTP_OPT_IGNORE_SCP_UPLOAD_PERMS 0x000002
|
||||
+#define SFTP_OPT_PESSIMISTIC_KEXINIT 0x000004
|
||||
+#define SFTP_OPT_OLD_PROTO_COMPAT 0x000008
|
||||
+#define SFTP_OPT_MATCH_KEY_SUBJECT 0x000010
|
||||
+#define SFTP_OPT_IGNORE_SFTP_SET_PERMS 0x000020
|
||||
+#define SFTP_OPT_IGNORE_SFTP_SET_TIMES 0x000040
|
||||
+#define SFTP_OPT_IGNORE_SFTP_SET_OWNERS 0x000080
|
||||
+#define SFTP_OPT_IGNORE_SCP_UPLOAD_TIMES 0x000100
|
||||
+#define SFTP_OPT_ALLOW_INSECURE_LOGIN 0x000200
|
||||
+#define SFTP_OPT_INSECURE_HOSTKEY_PERMS 0x000400
|
||||
+#define SFTP_OPT_ALLOW_WEAK_DH 0x000800
|
||||
+#define SFTP_OPT_IGNORE_FIFOS 0x001000
|
||||
+#define SFTP_OPT_IGNORE_SFTP_UPLOAD_XATTRS 0x002000
|
||||
+#define SFTP_OPT_IGNORE_SFTP_SET_XATTRS 0x004000
|
||||
+#define SFTP_OPT_INCLUDE_SFTP_TIMES 0x008000
|
||||
+#define SFTP_OPT_NO_EXT_INFO 0x010000
|
||||
+#define SFTP_OPT_NO_STRICT_KEX 0x040000
|
||||
|
||||
/* mod_sftp service flags */
|
||||
#define SFTP_SERVICE_FL_SFTP 0x0001
|
||||
diff --git a/contrib/mod_sftp/packet.c b/contrib/mod_sftp/packet.c
|
||||
index b0067ea80..d8c97e92b 100644
|
||||
--- a/contrib/mod_sftp/packet.c
|
||||
+++ b/contrib/mod_sftp/packet.c
|
||||
@@ -1742,6 +1742,18 @@ int sftp_ssh2_packet_rekey_set_size(off_t size) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
+uint32_t sftp_ssh2_packet_get_client_seqno(void) {
|
||||
+ return packet_client_seqno;
|
||||
+}
|
||||
+
|
||||
+void sftp_ssh2_packet_reset_client_seqno(void) {
|
||||
+ packet_client_seqno = 0;
|
||||
+}
|
||||
+
|
||||
+void sftp_ssh2_packet_reset_server_seqno(void) {
|
||||
+ packet_server_seqno = 0;
|
||||
+}
|
||||
+
|
||||
int sftp_ssh2_packet_send_version(void) {
|
||||
if (!sent_version_id) {
|
||||
int res;
|
||||
diff --git a/contrib/mod_sftp/packet.h b/contrib/mod_sftp/packet.h
|
||||
index a424e9b25..fe538cbd7 100644
|
||||
--- a/contrib/mod_sftp/packet.h
|
||||
+++ b/contrib/mod_sftp/packet.h
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* ProFTPD - mod_sftp packet IO
|
||||
- * Copyright (c) 2008-2020 TJ Saunders
|
||||
+ * Copyright (c) 2008-2023 TJ Saunders
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -107,6 +107,13 @@ int sftp_ssh2_packet_rekey_reset(void);
|
||||
int sftp_ssh2_packet_rekey_set_seqno(uint32_t);
|
||||
int sftp_ssh2_packet_rekey_set_size(off_t);
|
||||
|
||||
+/* These are used for implementing the "strict KEX" mitigations of the Terrapin
|
||||
+ * attack (Issue 1760).
|
||||
+ */
|
||||
+uint32_t sftp_ssh2_packet_get_client_seqno(void);
|
||||
+void sftp_ssh2_packet_reset_client_seqno(void);
|
||||
+void sftp_ssh2_packet_reset_server_seqno(void);
|
||||
+
|
||||
int sftp_ssh2_packet_send_version(void);
|
||||
int sftp_ssh2_packet_set_poll_timeout(int);
|
||||
int sftp_ssh2_packet_set_version(const char *);
|
||||
diff --git a/contrib/mod_sftp/tap.c b/contrib/mod_sftp/tap.c
|
||||
index 95f388e43..7eaf959e2 100644
|
||||
--- a/contrib/mod_sftp/tap.c
|
||||
+++ b/contrib/mod_sftp/tap.c
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* ProFTPD - mod_sftp traffic analysis protection
|
||||
- * Copyright (c) 2008-2016 TJ Saunders
|
||||
+ * Copyright (c) 2008-2023 TJ Saunders
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -149,7 +149,6 @@ static void set_policy_chance(struct sftp_tap_policy *policy) {
|
||||
}
|
||||
|
||||
static void set_policy_timer(struct sftp_tap_policy *policy) {
|
||||
-
|
||||
/* Start a timer which checks the last times we received and sent packets.
|
||||
* From there, we may want to inject a TAP message, depending on the
|
||||
* policy.
|
||||
@@ -177,6 +176,16 @@ int sftp_tap_send_packet(void) {
|
||||
int rnd;
|
||||
unsigned int chance;
|
||||
|
||||
+ /* Due to chances of violating client-side "strict KEX" Terrapin
|
||||
+ * mitigations, we will not send packets if we are in the middle of a KEX.
|
||||
+ */
|
||||
+ if (!(sftp_sess_state & SFTP_SESS_STATE_HAVE_KEX) ||
|
||||
+ (sftp_sess_state & SFTP_SESS_STATE_REKEYING)) {
|
||||
+ pr_trace_msg(trace_channel, 11,
|
||||
+ "unwilling to send TAP packet during KEX");
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
if (!sftp_interop_supports_feature(SFTP_SSH2_FEAT_IGNORE_MSG)) {
|
||||
pr_trace_msg(trace_channel, 3,
|
||||
"unable to send TAP packet: IGNORE not supported by client");
|
||||
@@ -205,7 +214,7 @@ int sftp_tap_send_packet(void) {
|
||||
struct ssh2_packet *pkt;
|
||||
unsigned int max_datalen = 8192;
|
||||
|
||||
- if (curr_policy.max_datalen) {
|
||||
+ if (curr_policy.max_datalen > 0) {
|
||||
max_datalen = curr_policy.max_datalen;
|
||||
}
|
||||
|
||||
@@ -246,15 +255,15 @@ int sftp_tap_send_packet(void) {
|
||||
int sftp_tap_set_policy(const char *policy) {
|
||||
register unsigned int i;
|
||||
|
||||
- if (tap_pool) {
|
||||
+ if (tap_pool != NULL) {
|
||||
|
||||
/* Special case: IFF the existing policy is 'none' AND the given
|
||||
* policy is 'rogaway', just return. The 'none' policy must have been
|
||||
* explicitly configured, and it should override the automatic use of
|
||||
* the 'rogaway' policy.
|
||||
*/
|
||||
- if (strncmp(curr_policy.policy, "none", 5) == 0 &&
|
||||
- strncasecmp(policy, "rogaway", 8) == 0) {
|
||||
+ if (strcasecmp(curr_policy.policy, "none") == 0 &&
|
||||
+ strcasecmp(policy, "rogaway") == 0) {
|
||||
(void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
|
||||
"'none' traffic policy explicitly configured, ignoring '%s' policy",
|
||||
policy);
|
||||
@@ -278,7 +287,6 @@ int sftp_tap_set_policy(const char *policy) {
|
||||
if (strcasecmp(tap_policies[i].policy, policy) == 0) {
|
||||
copy_policy(&curr_policy, &(tap_policies[i]));
|
||||
set_policy_chance(&curr_policy);
|
||||
- set_policy_timer(&curr_policy);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -286,3 +294,7 @@ int sftp_tap_set_policy(const char *policy) {
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
+
|
||||
+void sftp_tap_start_policy(void) {
|
||||
+ set_policy_timer(&curr_policy);
|
||||
+}
|
||||
diff --git a/contrib/mod_sftp/tap.h b/contrib/mod_sftp/tap.h
|
||||
index 4a4c065d2..312223595 100644
|
||||
--- a/contrib/mod_sftp/tap.h
|
||||
+++ b/contrib/mod_sftp/tap.h
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* ProFTPD - mod_sftp traffic analysis protection
|
||||
- * Copyright (c) 2008-2016 TJ Saunders
|
||||
+ * Copyright (c) 2008-2013 TJ Saunders
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -63,4 +63,7 @@ int sftp_tap_send_packet(void);
|
||||
*/
|
||||
int sftp_tap_set_policy(const char *);
|
||||
|
||||
+/* Sets the configured TAP policy in motion. */
|
||||
+void sftp_tap_start_policy(void);
|
||||
+
|
||||
#endif /* MOD_SFTP_TAP_H */
|
||||
diff --git a/doc/contrib/mod_sftp.html b/doc/contrib/mod_sftp.html
|
||||
index 60c3436fa..cfc639d05 100644
|
||||
--- a/doc/contrib/mod_sftp.html
|
||||
+++ b/doc/contrib/mod_sftp.html
|
||||
@@ -1186,6 +1186,19 @@ The currently implemented options are:
|
||||
<code>proftpd-1.3.7rc4</code>.
|
||||
</li>
|
||||
|
||||
+ <p>
|
||||
+ <li><code>NoStrictKex</code><br>
|
||||
+ <p>
|
||||
+ By default, <code>mod_sftp</code> will honor/support the OpenSSH
|
||||
+ "strict KEX" mode extension, "kex-strict-c-v00@openssh.com" and
|
||||
+ "kex-strict-s-v00@openssh.com". Use this option to disable support for
|
||||
+ these custom OpenSSH extensions.
|
||||
+
|
||||
+ <p>
|
||||
+ <b>Note</b> that this option first appeared in
|
||||
+ <code>proftpd-1.3.9rc1</code>.
|
||||
+ </li>
|
||||
+
|
||||
<p>
|
||||
<li><code>OldProtocolCompat</code><br>
|
||||
<p>
|
||||
@@ -2642,7 +2655,7 @@ deal with this issue, then, you can hopefully upgrade to ProFTPD 1.3.6 or later,
|
||||
<p>
|
||||
<hr>
|
||||
<font size=2><b><i>
|
||||
-© Copyright 2008-2021 TJ Saunders<br>
|
||||
+© Copyright 2008-2023 TJ Saunders<br>
|
||||
All Rights Reserved<br>
|
||||
</i></b></font>
|
||||
<hr>
|
||||
diff --git a/tests/t/lib/ProFTPD/Tests/Modules/mod_sftp.pm b/tests/t/lib/ProFTPD/Tests/Modules/mod_sftp.pm
|
||||
index b4bdf516b..8c2be5465 100644
|
||||
--- a/tests/t/lib/ProFTPD/Tests/Modules/mod_sftp.pm
|
||||
+++ b/tests/t/lib/ProFTPD/Tests/Modules/mod_sftp.pm
|
||||
@@ -87,6 +87,11 @@ my $TESTS = {
|
||||
test_class => [qw(forking ssh2)],
|
||||
},
|
||||
|
||||
+ ssh2_ext_kex_strict_terrapin_issue1760 => {
|
||||
+ order => ++$order,
|
||||
+ test_class => [qw(bug forking ssh2)],
|
||||
+ },
|
||||
+
|
||||
ssh2_hostkey_rsa => {
|
||||
order => ++$order,
|
||||
test_class => [qw(forking ssh2)],
|
||||
@@ -3885,6 +3890,218 @@ EOC
|
||||
unlink($log_file);
|
||||
}
|
||||
|
||||
+sub ssh2_ext_kex_strict_terrapin_issue1760 {
|
||||
+ my $self = shift;
|
||||
+ my $tmpdir = $self->{tmpdir};
|
||||
+ my $setup = test_setup($tmpdir, 'sftp');
|
||||
+
|
||||
+ my $rsa_host_key = File::Spec->rel2abs('t/etc/modules/mod_sftp/ssh_host_rsa_key');
|
||||
+ my $dsa_host_key = File::Spec->rel2abs('t/etc/modules/mod_sftp/ssh_host_dsa_key');
|
||||
+
|
||||
+ my $rsa_priv_key = File::Spec->rel2abs('t/etc/modules/mod_sftp/test_rsa_key');
|
||||
+ my $rsa_pub_key = File::Spec->rel2abs('t/etc/modules/mod_sftp/test_rsa_key.pub');
|
||||
+ my $rsa_rfc4716_key = File::Spec->rel2abs('t/etc/modules/mod_sftp/authorized_rsa_keys');
|
||||
+
|
||||
+ my $authorized_keys = File::Spec->rel2abs("$tmpdir/.authorized_keys");
|
||||
+ unless (copy($rsa_rfc4716_key, $authorized_keys)) {
|
||||
+ die("Can't copy $rsa_rfc4716_key to $authorized_keys: $!");
|
||||
+ }
|
||||
+
|
||||
+ my $ssh_config = File::Spec->rel2abs("$tmpdir/ssh.conf");
|
||||
+ if (open(my $fh, "> $ssh_config")) {
|
||||
+ print $fh <<EOC;
|
||||
+HostKeyAlgorithms rsa-sha2-256
|
||||
+IdentityAgent none
|
||||
+PubkeyAcceptedKeyTypes rsa-sha2-256
|
||||
+EOC
|
||||
+ unless (close($fh)) {
|
||||
+ die("Can't write $ssh_config: $!");
|
||||
+ }
|
||||
+
|
||||
+ } else {
|
||||
+ die("Can't open $ssh_config: $!");
|
||||
+ }
|
||||
+
|
||||
+ my $batch_file = File::Spec->rel2abs("$tmpdir/sftp-batch.conf");
|
||||
+ if (open(my $fh, "> $batch_file")) {
|
||||
+ print $fh "ls -l\n";
|
||||
+
|
||||
+ unless (close($fh)) {
|
||||
+ die("Can't write $batch_file: $!");
|
||||
+ }
|
||||
+
|
||||
+ } else {
|
||||
+ die("Can't open $batch_file: $!");
|
||||
+ }
|
||||
+
|
||||
+ my $config = {
|
||||
+ PidFile => $setup->{pid_file},
|
||||
+ ScoreboardFile => $setup->{scoreboard_file},
|
||||
+ SystemLog => $setup->{log_file},
|
||||
+ TraceLog => $setup->{log_file},
|
||||
+ Trace => 'ssh2:30 sftp:20 scp:20',
|
||||
+
|
||||
+ AuthUserFile => $setup->{auth_user_file},
|
||||
+ AuthGroupFile => $setup->{auth_group_file},
|
||||
+ AuthOrder => 'mod_auth_file.c',
|
||||
+
|
||||
+ IfModules => {
|
||||
+ 'mod_delay.c' => {
|
||||
+ DelayEngine => 'off',
|
||||
+ },
|
||||
+
|
||||
+ 'mod_sftp.c' => [
|
||||
+ "SFTPEngine on",
|
||||
+ "SFTPLog $setup->{log_file}",
|
||||
+
|
||||
+ "SFTPHostKey $rsa_host_key",
|
||||
+ "SFTPHostKey $dsa_host_key",
|
||||
+
|
||||
+ "SFTPAuthorizedUserKeys file:~/.authorized_keys",
|
||||
+ ],
|
||||
+ },
|
||||
+ };
|
||||
+
|
||||
+ my ($port, $config_user, $config_group) = config_write($setup->{config_file},
|
||||
+ $config);
|
||||
+
|
||||
+ # Open pipes, for use between the parent and child processes. Specifically,
|
||||
+ # the child will indicate when it's done with its test by writing a message
|
||||
+ # to the parent.
|
||||
+ my ($rfh, $wfh);
|
||||
+ unless (pipe($rfh, $wfh)) {
|
||||
+ die("Can't open pipe: $!");
|
||||
+ }
|
||||
+
|
||||
+ require Net::SSH2;
|
||||
+
|
||||
+ my $ex;
|
||||
+
|
||||
+ # Fork child
|
||||
+ $self->handle_sigchld();
|
||||
+ defined(my $pid = fork()) or die("Can't fork: $!");
|
||||
+ if ($pid) {
|
||||
+ eval {
|
||||
+ # We use OpenSSH-9.6p1 to test our "strict KEX" Terrapin mitigations.
|
||||
+ my $sftp = '/Users/tj/local/openssh-9.6p1/bin/sftp';
|
||||
+
|
||||
+ my @cmd = (
|
||||
+ $sftp,
|
||||
+ '-F',
|
||||
+ $ssh_config,
|
||||
+ '-oBatchMode=yes',
|
||||
+ '-oCheckHostIP=no',
|
||||
+ '-oCompression=yes',
|
||||
+ "-oPort=$port",
|
||||
+ "-oIdentityFile=$rsa_priv_key",
|
||||
+ '-oPubkeyAuthentication=yes',
|
||||
+ '-oStrictHostKeyChecking=no',
|
||||
+ '-oUserKnownHostsFile=/dev/null',
|
||||
+ '-vvv',
|
||||
+ '-b',
|
||||
+ $batch_file,
|
||||
+ "$setup->{user}\@127.0.0.1",
|
||||
+ );
|
||||
+
|
||||
+ my $sftp_rh = IO::Handle->new();
|
||||
+ my $sftp_wh = IO::Handle->new();
|
||||
+ my $sftp_eh = IO::Handle->new();
|
||||
+
|
||||
+ $sftp_wh->autoflush(1);
|
||||
+
|
||||
+ sleep(1);
|
||||
+
|
||||
+ local $SIG{CHLD} = 'DEFAULT';
|
||||
+
|
||||
+ # Make sure that the perms on the priv key are what OpenSSH wants
|
||||
+ unless (chmod(0400, $rsa_priv_key)) {
|
||||
+ die("Can't set perms on $rsa_priv_key to 0400: $!");
|
||||
+ }
|
||||
+
|
||||
+ if ($ENV{TEST_VERBOSE}) {
|
||||
+ print STDERR "Executing: ", join(' ', @cmd), "\n";
|
||||
+ }
|
||||
+
|
||||
+ my $sftp_pid = open3($sftp_wh, $sftp_rh, $sftp_eh, @cmd);
|
||||
+ waitpid($sftp_pid, 0);
|
||||
+ my $exit_status = $?;
|
||||
+
|
||||
+ # Restore the perms on the priv key
|
||||
+ unless (chmod(0644, $rsa_priv_key)) {
|
||||
+ die("Can't set perms on $rsa_priv_key to 0644: $!");
|
||||
+ }
|
||||
+
|
||||
+ my ($res, $errstr);
|
||||
+ if ($exit_status >> 8 == 0) {
|
||||
+ $errstr = join('', <$sftp_eh>);
|
||||
+ $res = 0;
|
||||
+
|
||||
+ } else {
|
||||
+ $errstr = join('', <$sftp_eh>);
|
||||
+ if ($ENV{TEST_VERBOSE}) {
|
||||
+ print STDERR "Stderr: $errstr\n";
|
||||
+ }
|
||||
+
|
||||
+ $res = 1;
|
||||
+ }
|
||||
+
|
||||
+ unless ($res == 0) {
|
||||
+ die("Can't list files on server: $errstr");
|
||||
+ }
|
||||
+ };
|
||||
+ if ($@) {
|
||||
+ $ex = $@;
|
||||
+ }
|
||||
+
|
||||
+ $wfh->print("done\n");
|
||||
+ $wfh->flush();
|
||||
+
|
||||
+ } else {
|
||||
+ eval { server_wait($setup->{config_file}, $rfh) };
|
||||
+ if ($@) {
|
||||
+ warn($@);
|
||||
+ exit 1;
|
||||
+ }
|
||||
+
|
||||
+ exit 0;
|
||||
+ }
|
||||
+
|
||||
+ # Stop server
|
||||
+ server_stop($setup->{pid_file});
|
||||
+ $self->assert_child_ok($pid);
|
||||
+
|
||||
+ eval {
|
||||
+ if (open(my $fh, "< $setup->{log_file}")) {
|
||||
+ my $ok = 0;
|
||||
+
|
||||
+ while (my $line = <$fh>) {
|
||||
+ chomp($line);
|
||||
+
|
||||
+ if ($ENV{TEST_VERBOSE}) {
|
||||
+ print STDERR "# $line\n";
|
||||
+ }
|
||||
+
|
||||
+ if ($line =~ /client signaled strict KEX support/) {
|
||||
+ $ok = 1;
|
||||
+ last;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ close($fh);
|
||||
+
|
||||
+ $self->assert($ok, test_msg("Did not see expected 'strict KEX' TraceLog message"));
|
||||
+
|
||||
+ } else {
|
||||
+ die("Can't read $setup->{log_file}: $!");
|
||||
+ }
|
||||
+ };
|
||||
+ if ($@) {
|
||||
+ $ex = $@;
|
||||
+ }
|
||||
+
|
||||
+ test_cleanup($setup->{log_file}, $ex);
|
||||
+}
|
||||
+
|
||||
sub ssh2_hostkey_rsa {
|
||||
my $self = shift;
|
||||
my $tmpdir = $self->{tmpdir};
|
||||
@@ -17,6 +17,7 @@ SRC_URI = "git://github.com/proftpd/proftpd.git;branch=${BRANCH};protocol=https
|
||||
file://proftpd.service \
|
||||
file://CVE-2023-51713.patch \
|
||||
file://CVE-2024-57392.patch \
|
||||
file://CVE-2023-48795.patch \
|
||||
"
|
||||
|
||||
S = "${WORKDIR}/git"
|
||||
|
||||
Reference in New Issue
Block a user