Compare commits

...

10 Commits

Author SHA1 Message Date
openeuler-ci-bot
891f7f8bdc
!66 [sync] PR-63: fix CVE-2025-2704
From: @openeuler-sync-bot 
Reviewed-by: @wk333 
Signed-off-by: @wk333
2025-04-07 02:14:50 +00:00
Funda Wang
6c33e2ce2a fix CVE-2025-2704
(cherry picked from commit 43352c9e19dca93f5133655adde9fdf55559989a)
2025-04-07 09:53:44 +08:00
openeuler-ci-bot
30cd3fc3b4
!56 [sync] PR-51: fix CVE-2024-5594
From: @openeuler-sync-bot 
Reviewed-by: @wk333 
Signed-off-by: @wk333
2024-07-22 06:39:08 +00:00
Funda Wang
486b1533a0 fix CVE-2024-5594
(cherry picked from commit 383821b950b988c4d3cb236b3fb89d891f8806e9)
2024-07-22 14:27:39 +08:00
openeuler-ci-bot
30bef67c00
!45 [sync] PR-42: Fix CVE-2024-28882
From: @openeuler-sync-bot 
Reviewed-by: @wk333 
Signed-off-by: @wk333
2024-07-09 07:50:22 +00:00
zhangxianting
51b1fd72e4 Fix CVE-2024-28882
(cherry picked from commit bb621f2f8872a18394a6484de1e776ccfd5c6998)
2024-07-09 15:29:54 +08:00
openeuler-ci-bot
d48d34aacf
!41 Modify changelog description
From: @starlet-dx 
Reviewed-by: @caodongxia 
Signed-off-by: @caodongxia
2024-02-26 03:06:03 +00:00
starlet-dx
46e41f321d Modify changelog description 2024-02-26 09:59:50 +08:00
openeuler-ci-bot
c481d84550
!39 Upgrade to 2.6.9 for fix CVE-2023-7235
From: @starlet-dx 
Reviewed-by: @wk333 
Signed-off-by: @wk333
2024-02-22 11:00:15 +00:00
starlet-dx
55187fdb53 Upgrade to 2.6.9 for fix CVE-2023-7235 2024-02-22 16:55:18 +08:00
6 changed files with 803 additions and 3 deletions

140
CVE-2024-28882.patch Normal file
View File

@ -0,0 +1,140 @@
From 65fb67cd6c320a426567b2922c4282fb8738ba3f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Reynir=20Bj=C3=B6rnsson?= <reynir@reynir.dk>
Date: Thu, 16 May 2024 13:58:08 +0200
Subject: [PATCH] Only schedule_exit() once
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
If an exit has already been scheduled we should not schedule it again.
Otherwise, the exit signal is never emitted if the peer reschedules the
exit before the timeout occurs.
schedule_exit() now only takes the context as argument. The signal is
hard coded to SIGTERM, and the interval is read directly from the
context options.
Furthermore, schedule_exit() now returns a bool signifying whether an
exit was scheduled; false if exit is already scheduled. The call sites
are updated accordingly. A notable difference is that management is only
notified *once* when an exit is scheduled - we no longer notify
management on redundant exit.
This patch was assigned a CVE number after already reviewed and ACKed,
because it was discovered that a misbehaving client can use the (now
fixed) server behaviour to avoid being disconnected by means of a
managment interface "client-kill" command - the security issue here is
"client can circumvent security policy set by management interface".
This only affects previously authenticated clients, and only management
client-kill, so normal renegotion / AUTH_FAIL ("your session ends") is not
affected.
CVE: 2024-28882
Change-Id: I9457f005f4ba970502e6b667d9dc4299a588d661
Signed-off-by: Reynir Björnsson <reynir@reynir.dk>
Acked-by: Arne Schwabe <arne-openvpn@rfc2549.org>
Message-Id: <20240516120434.23499-1-gert@greenie.muc.de>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg28679.html
Signed-off-by: Gert Doering <gert@greenie.muc.de>
(cherry picked from commit 55bb3260c12bae33b6a8eac73cbb6972f8517411)
---
src/openvpn/forward.c | 15 +++++++++++----
src/openvpn/forward.h | 2 +-
src/openvpn/push.c | 12 +++++++-----
3 files changed, 19 insertions(+), 10 deletions(-)
diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c
index e9811b9c..29e812ff 100644
--- a/src/openvpn/forward.c
+++ b/src/openvpn/forward.c
@@ -514,17 +514,24 @@ check_server_poll_timeout(struct context *c)
}
/*
- * Schedule a signal n_seconds from now.
+ * Schedule a SIGTERM signal c->options.scheduled_exit_interval seconds from now.
*/
-void
-schedule_exit(struct context *c, const int n_seconds, const int signal)
+bool
+schedule_exit(struct context *c)
{
+ const int n_seconds = c->options.scheduled_exit_interval;
+ /* don't reschedule if already scheduled. */
+ if (event_timeout_defined(&c->c2.scheduled_exit))
+ {
+ return false;
+ }
tls_set_single_session(c->c2.tls_multi);
update_time();
reset_coarse_timers(c);
event_timeout_init(&c->c2.scheduled_exit, n_seconds, now);
- c->c2.scheduled_exit_signal = signal;
+ c->c2.scheduled_exit_signal = SIGTERM;
msg(D_SCHED_EXIT, "Delayed exit in %d seconds", n_seconds);
+ return true;
}
/*
diff --git a/src/openvpn/forward.h b/src/openvpn/forward.h
index 060fc374..245a8029 100644
--- a/src/openvpn/forward.h
+++ b/src/openvpn/forward.h
@@ -302,7 +302,7 @@ void reschedule_multi_process(struct context *c);
void process_ip_header(struct context *c, unsigned int flags, struct buffer *buf);
-void schedule_exit(struct context *c, const int n_seconds, const int signal);
+bool schedule_exit(struct context *c);
static inline struct link_socket_info *
get_link_socket_info(struct context *c)
diff --git a/src/openvpn/push.c b/src/openvpn/push.c
index 1b406b9c..d220eeb9 100644
--- a/src/openvpn/push.c
+++ b/src/openvpn/push.c
@@ -204,7 +204,11 @@ receive_exit_message(struct context *c)
* */
if (c->options.mode == MODE_SERVER)
{
- schedule_exit(c, c->options.scheduled_exit_interval, SIGTERM);
+ if (!schedule_exit(c))
+ {
+ /* Return early when we don't need to notify management */
+ return;
+ }
}
else
{
@@ -391,7 +395,7 @@ __attribute__ ((format(__printf__, 4, 5)))
void
send_auth_failed(struct context *c, const char *client_reason)
{
- if (event_timeout_defined(&c->c2.scheduled_exit))
+ if (!schedule_exit(c))
{
msg(D_TLS_DEBUG, "exit already scheduled for context");
return;
@@ -401,8 +405,6 @@ send_auth_failed(struct context *c, const char *client_reason)
static const char auth_failed[] = "AUTH_FAILED";
size_t len;
- schedule_exit(c, c->options.scheduled_exit_interval, SIGTERM);
-
len = (client_reason ? strlen(client_reason)+1 : 0) + sizeof(auth_failed);
if (len > PUSH_BUNDLE_SIZE)
{
@@ -492,7 +494,7 @@ send_auth_pending_messages(struct tls_multi *tls_multi,
void
send_restart(struct context *c, const char *kill_msg)
{
- schedule_exit(c, c->options.scheduled_exit_interval, SIGTERM);
+ schedule_exit(c);
send_control_channel_string(c, kill_msg ? kill_msg : "RESTART", D_PUSH);
}
--
2.20.1

363
CVE-2024-5594.patch Normal file
View File

@ -0,0 +1,363 @@
From 90e7a858e5594d9a019ad2b4ac6154124986291a Mon Sep 17 00:00:00 2001
From: Arne Schwabe <arne@rfc2549.org>
Date: Mon, 27 May 2024 15:02:41 +0200
Subject: [PATCH] Properly handle null bytes and invalid characters in control
messages
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This makes OpenVPN more picky in accepting control message in two aspects:
- Characters are checked in the whole buffer and not until the first
NUL byte
- if the message contains invalid characters, we no longer continue
evaluating a fixed up version of the message but rather stop
processing it completely.
Previously it was possible to get invalid characters to end up in log
files or on a terminal.
This also prepares the logic a bit in the direction of having a proper
framing of control messages separated by null bytes instead of relying
on the TLS framing for that. All OpenVPN implementations write the 0
bytes between control commands.
This patch also include several improvement suggestion from Reynir
(thanks!).
CVE: 2024-5594
Reported-By: Reynir Björnsson <reynir@reynir.dk>
Change-Id: I0d926f910637dabc89bf5fa919dc6beef1eb46d9
Signed-off-by: Arne Schwabe <arne@rfc2549.org>
Acked-by: Antonio Quartulli <a@unstable.cc>
Message-Id: <20240619103004.56460-1-gert@greenie.muc.de>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg28791.html
Signed-off-by: Gert Doering <gert@greenie.muc.de>
(cherry picked from commit 414f428fa29694090ec4c46b10a8aba419c85659)
---
src/openvpn/buffer.c | 17 ++++
src/openvpn/buffer.h | 11 +++
src/openvpn/forward.c | 121 ++++++++++++++++---------
tests/unit_tests/openvpn/test_buffer.c | 109 ++++++++++++++++++++++
4 files changed, 215 insertions(+), 43 deletions(-)
diff --git a/src/openvpn/buffer.c b/src/openvpn/buffer.c
index 2fab9e43ff4..78a13ef81bd 100644
--- a/src/openvpn/buffer.c
+++ b/src/openvpn/buffer.c
@@ -1113,6 +1113,23 @@ string_mod(char *str, const unsigned int inclusive, const unsigned int exclusive
return ret;
}
+bool
+string_check_buf(struct buffer *buf, const unsigned int inclusive, const unsigned int exclusive)
+{
+ ASSERT(buf);
+
+ for (int i = 0; i < BLEN(buf); i++)
+ {
+ char c = BSTR(buf)[i];
+
+ if (!char_inc_exc(c, inclusive, exclusive))
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
const char *
string_mod_const(const char *str,
const unsigned int inclusive,
diff --git a/src/openvpn/buffer.h b/src/openvpn/buffer.h
index cea4af6b398..d988ef256aa 100644
--- a/src/openvpn/buffer.h
+++ b/src/openvpn/buffer.h
@@ -945,6 +945,17 @@ bool string_class(const char *str, const unsigned int inclusive, const unsigned
bool string_mod(char *str, const unsigned int inclusive, const unsigned int exclusive, const char replace);
+/**
+ * Check a buffer if it only consists of allowed characters.
+ *
+ * @param buf The buffer to be checked.
+ * @param inclusive The character classes that are allowed.
+ * @param exclusive Character classes that are not allowed even if they are also in inclusive.
+ * @return True if the string consists only of allowed characters, false otherwise.
+ */
+bool
+string_check_buf(struct buffer *buf, const unsigned int inclusive, const unsigned int exclusive);
+
const char *string_mod_const(const char *str,
const unsigned int inclusive,
const unsigned int exclusive,
diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c
index 29e812ffd17..ce71752469f 100644
--- a/src/openvpn/forward.c
+++ b/src/openvpn/forward.c
@@ -230,6 +230,51 @@ check_tls(struct context *c)
}
}
+static void
+parse_incoming_control_channel_command(struct context *c, struct buffer *buf)
+{
+ if (buf_string_match_head_str(buf, "AUTH_FAILED"))
+ {
+ receive_auth_failed(c, buf);
+ }
+ else if (buf_string_match_head_str(buf, "PUSH_"))
+ {
+ incoming_push_message(c, buf);
+ }
+ else if (buf_string_match_head_str(buf, "RESTART"))
+ {
+ server_pushed_signal(c, buf, true, 7);
+ }
+ else if (buf_string_match_head_str(buf, "HALT"))
+ {
+ server_pushed_signal(c, buf, false, 4);
+ }
+ else if (buf_string_match_head_str(buf, "INFO_PRE"))
+ {
+ server_pushed_info(c, buf, 8);
+ }
+ else if (buf_string_match_head_str(buf, "INFO"))
+ {
+ server_pushed_info(c, buf, 4);
+ }
+ else if (buf_string_match_head_str(buf, "CR_RESPONSE"))
+ {
+ receive_cr_response(c, buf);
+ }
+ else if (buf_string_match_head_str(buf, "AUTH_PENDING"))
+ {
+ receive_auth_pending(c, buf);
+ }
+ else if (buf_string_match_head_str(buf, "EXIT"))
+ {
+ receive_exit_message(c);
+ }
+ else
+ {
+ msg(D_PUSH_ERRORS, "WARNING: Received unknown control message: %s", BSTR(buf));
+ }
+}
+
/*
* Handle incoming configuration
* messages on the control channel.
@@ -245,51 +290,41 @@ check_incoming_control_channel(struct context *c)
struct buffer buf = alloc_buf_gc(len, &gc);
if (tls_rec_payload(c->c2.tls_multi, &buf))
{
- /* force null termination of message */
- buf_null_terminate(&buf);
-
- /* enforce character class restrictions */
- string_mod(BSTR(&buf), CC_PRINT, CC_CRLF, 0);
- if (buf_string_match_head_str(&buf, "AUTH_FAILED"))
+ while (BLEN(&buf) > 1)
{
- receive_auth_failed(c, &buf);
- }
- else if (buf_string_match_head_str(&buf, "PUSH_"))
- {
- incoming_push_message(c, &buf);
- }
- else if (buf_string_match_head_str(&buf, "RESTART"))
- {
- server_pushed_signal(c, &buf, true, 7);
- }
- else if (buf_string_match_head_str(&buf, "HALT"))
- {
- server_pushed_signal(c, &buf, false, 4);
- }
- else if (buf_string_match_head_str(&buf, "INFO_PRE"))
- {
- server_pushed_info(c, &buf, 8);
- }
- else if (buf_string_match_head_str(&buf, "INFO"))
- {
- server_pushed_info(c, &buf, 4);
- }
- else if (buf_string_match_head_str(&buf, "CR_RESPONSE"))
- {
- receive_cr_response(c, &buf);
- }
- else if (buf_string_match_head_str(&buf, "AUTH_PENDING"))
- {
- receive_auth_pending(c, &buf);
- }
- else if (buf_string_match_head_str(&buf, "EXIT"))
- {
- receive_exit_message(c);
- }
- else
- {
- msg(D_PUSH_ERRORS, "WARNING: Received unknown control message: %s", BSTR(&buf));
+ /* commands on the control channel are seperated by 0x00 bytes.
+ * cmdlen does not include the 0 byte of the string */
+ int cmdlen = (int)strnlen(BSTR(&buf), BLEN(&buf));
+
+ if (cmdlen < BLEN(&buf))
+ {
+ /* include the NUL byte and ensure NUL termination */
+ int cmdlen = (int)strlen(BSTR(&buf)) + 1;
+
+ /* Construct a buffer that only holds the current command and
+ * its closing NUL byte */
+ struct buffer cmdbuf = alloc_buf_gc(cmdlen, &gc);
+ buf_write(&cmdbuf, BPTR(&buf), cmdlen);
+
+ /* check we have only printable characters or null byte in the
+ * command string and no newlines */
+ if (!string_check_buf(&buf, CC_PRINT | CC_NULL, CC_CRLF))
+ {
+ msg(D_PUSH_ERRORS, "WARNING: Received control with invalid characters: %s",
+ format_hex(BPTR(&buf), BLEN(&buf), 256, &gc));
+ }
+ else
+ {
+ parse_incoming_control_channel_command(c, &cmdbuf);
+ }
+ }
+ else
+ {
+ msg(D_PUSH_ERRORS, "WARNING: Ignoring control channel "
+ "message command without NUL termination");
+ }
+ buf_advance(&buf, cmdlen);
}
}
else
diff --git a/tests/unit_tests/openvpn/test_buffer.c b/tests/unit_tests/openvpn/test_buffer.c
index 92ac77a7879..df1fdcb15ec 100644
--- a/tests/unit_tests/openvpn/test_buffer.c
+++ b/tests/unit_tests/openvpn/test_buffer.c
@@ -259,6 +259,112 @@ test_buffer_gc_realloc(void **state)
gc_free(&gc);
}
+static void
+test_character_class(void **state)
+{
+ char buf[256];
+ strcpy(buf, "There is \x01 a nice 1234 year old tr\x7f ee!");
+ assert_false(string_mod(buf, CC_PRINT, 0, '@'));
+ assert_string_equal(buf, "There is @ a nice 1234 year old tr@ ee!");
+
+ strcpy(buf, "There is \x01 a nice 1234 year old tr\x7f ee!");
+ assert_true(string_mod(buf, CC_ANY, 0, '@'));
+ assert_string_equal(buf, "There is \x01 a nice 1234 year old tr\x7f ee!");
+
+ /* 0 as replace removes characters */
+ strcpy(buf, "There is \x01 a nice 1234 year old tr\x7f ee!");
+ assert_false(string_mod(buf, CC_PRINT, 0, '\0'));
+ assert_string_equal(buf, "There is a nice 1234 year old tr ee!");
+
+ strcpy(buf, "There is \x01 a nice 1234 year old tr\x7f ee!");
+ assert_false(string_mod(buf, CC_PRINT, CC_DIGIT, '@'));
+ assert_string_equal(buf, "There is @ a nice @@@@ year old tr@ ee!");
+
+ strcpy(buf, "There is \x01 a nice 1234 year old tr\x7f ee!");
+ assert_false(string_mod(buf, CC_ALPHA, CC_DIGIT, '.'));
+ assert_string_equal(buf, "There.is...a.nice......year.old.tr..ee.");
+
+ strcpy(buf, "There is \x01 a 'nice' \"1234\"\n year old \ntr\x7f ee!");
+ assert_false(string_mod(buf, CC_ALPHA|CC_DIGIT|CC_NEWLINE|CC_SINGLE_QUOTE, CC_DOUBLE_QUOTE|CC_BLANK, '.'));
+ assert_string_equal(buf, "There.is...a.'nice'..1234.\n.year.old.\ntr..ee.");
+
+ strcpy(buf, "There is a \\'nice\\' \"1234\" [*] year old \ntree!");
+ assert_false(string_mod(buf, CC_PRINT, CC_BACKSLASH|CC_ASTERISK, '.'));
+ assert_string_equal(buf, "There is a .'nice.' \"1234\" [.] year old .tree!");
+}
+
+
+static void
+test_character_string_mod_buf(void **state)
+{
+ struct gc_arena gc = gc_new();
+
+ struct buffer buf = alloc_buf_gc(1024, &gc);
+
+ const char test1[] = "There is a nice 1234\x00 year old tree!";
+ buf_write(&buf, test1, sizeof(test1));
+
+ /* allow the null bytes and string but not the ! */
+ assert_false(string_check_buf(&buf, CC_ALNUM | CC_SPACE | CC_NULL, 0));
+
+ /* remove final ! and null byte to pass */
+ buf_inc_len(&buf, -2);
+ assert_true(string_check_buf(&buf, CC_ALNUM | CC_SPACE | CC_NULL, 0));
+
+ /* Check excluding digits works */
+ assert_false(string_check_buf(&buf, CC_ALNUM | CC_SPACE | CC_NULL, CC_DIGIT));
+ gc_free(&gc);
+}
+
+static void
+test_snprintf(void **state)
+{
+ /* we used to have a custom openvpn_snprintf function because some
+ * OS (the comment did not specify which) did not always put the
+ * null byte there. So we unit test this to be sure.
+ *
+ * This probably refers to the MSVC behaviour, see also
+ * https://stackoverflow.com/questions/7706936/is-snprintf-always-null-terminating
+ */
+
+ /* Instead of trying to trick the compiler here, disable the warnings
+ * for this unit test. We know that the results will be truncated
+ * and we want to test that */
+#if defined(__GNUC__)
+/* some clang version do not understand -Wformat-truncation, so ignore the
+ * warning to avoid warnings/errors (-Werror) about unknown pragma/option */
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunknown-warning-option"
+#endif
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wformat-truncation"
+#endif
+
+ char buf[10] = { 'a' };
+ int ret = 0;
+
+ ret = snprintf(buf, sizeof(buf), "0123456789abcde");
+ assert_int_equal(ret, 15);
+ assert_int_equal(buf[9], '\0');
+
+ memset(buf, 'b', sizeof(buf));
+ ret = snprintf(buf, sizeof(buf), "- %d - %d -", 77, 88);
+ assert_int_equal(ret, 11);
+ assert_int_equal(buf[9], '\0');
+
+ memset(buf, 'c', sizeof(buf));
+ ret = snprintf(buf, sizeof(buf), "- %8.2f", 77.8899);
+ assert_int_equal(ret, 10);
+ assert_int_equal(buf[9], '\0');
+
+#if defined(__GNUC__)
+#pragma GCC diagnostic pop
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+#endif
+}
int
main(void)
@@ -289,6 +395,9 @@ main(void)
cmocka_unit_test(test_buffer_free_gc_one),
cmocka_unit_test(test_buffer_free_gc_two),
cmocka_unit_test(test_buffer_gc_realloc),
+ cmocka_unit_test(test_character_class),
+ cmocka_unit_test(test_character_string_mod_buf),
+ cmocka_unit_test(test_snprintf)
};
return cmocka_run_group_tests_name("buffer", tests, NULL, NULL);

282
CVE-2025-2704.patch Normal file
View File

@ -0,0 +1,282 @@
From d3015bfd65348db629dab51e20a9d4e2f3b23493 Mon Sep 17 00:00:00 2001
From: Arne Schwabe <arne@rfc2549.org>
Date: Tue, 1 Apr 2025 19:30:37 +0200
Subject: [PATCH] Allow tls-crypt-v2 to be setup only on initial packet of a
session
This fixes an internal server error condition that can be triggered by a
malicous authenticated client, a very unlucky corruption of packets in
transit or by an attacker that is able to inject a specially created
packet at the right time and is able to observe the traffic to construct
the packet.
The error condition results in an ASSERT statement being triggered,
NOTE: due to the security sensitive nature, this patch was prepared
under embargo on the security@openvpn.net mailing list, and thus has
no publically available "mailing list discussion before merge" URL.
CVE: 2025-2704
Change-Id: I07c1352204d308e5bde5f0b85e561a5dd0bc63c8
Signed-off-by: Arne Schwabe <arne@rfc2549.org>
Acked-by: Gert Doering <gert@greenie.muc.de>
Message-Id: <385d88f0-d7c9-4330-82ff-9f5931183afd@rfc2549.org>
Signed-off-by: Gert Doering <gert@greenie.muc.de>
(cherry picked from commit 82ee2fe4b42d9988c59ae3f83bd56a54d54e8c76)
---
src/openvpn/ssl.c | 26 +++++++++++++++++++----
src/openvpn/ssl_common.h | 15 +++++++------
src/openvpn/ssl_pkt.c | 7 +++---
src/openvpn/ssl_pkt.h | 12 +++++++++--
src/openvpn/tls_crypt.c | 24 ++++++++++++++++++++-
src/openvpn/tls_crypt.h | 7 +++++-
tests/unit_tests/openvpn/test_tls_crypt.c | 2 +-
7 files changed, 75 insertions(+), 18 deletions(-)
diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c
index 4fa7ea6fc45..5a0bf95aace 100644
--- a/src/openvpn/ssl.c
+++ b/src/openvpn/ssl.c
@@ -848,6 +848,9 @@ state_name(int state)
case S_INITIAL:
return "S_INITIAL";
+ case S_PRE_START_SKIP:
+ return "S_PRE_START_SKIP";
+
case S_PRE_START:
return "S_PRE_START";
@@ -2598,7 +2601,7 @@ session_move_pre_start(const struct tls_session *session,
}
INCR_GENERATED;
- ks->state = S_PRE_START;
+ ks->state = skip_initial_send ? S_PRE_START_SKIP : S_PRE_START;
struct gc_arena gc = gc_new();
dmsg(D_TLS_DEBUG, "TLS: Initial Handshake, sid=%s",
@@ -3801,7 +3804,7 @@ tls_pre_decrypt(struct tls_multi *multi,
}
if (!read_control_auth(buf, tls_session_get_tls_wrap(session, key_id), from,
- session->opt))
+ session->opt, true))
{
goto error;
}
@@ -3871,7 +3874,7 @@ tls_pre_decrypt(struct tls_multi *multi,
if (op == P_CONTROL_SOFT_RESET_V1 && ks->state >= S_GENERATED_KEYS)
{
if (!read_control_auth(buf, tls_session_get_tls_wrap(session, key_id),
- from, session->opt))
+ from, session->opt, false))
{
goto error;
}
@@ -3884,6 +3887,15 @@ tls_pre_decrypt(struct tls_multi *multi,
}
else
{
+ bool initial_packet = false;
+ if (ks->state == S_PRE_START_SKIP)
+ {
+ /* When we are coming from the session_skip_to_pre_start
+ * method, we allow this initial packet to setup the
+ * tls-crypt-v2 peer specific key */
+ initial_packet = true;
+ ks->state = S_PRE_START;
+ }
/*
* Remote responding to our key renegotiation request?
*/
@@ -3893,8 +3905,14 @@ tls_pre_decrypt(struct tls_multi *multi,
}
if (!read_control_auth(buf, tls_session_get_tls_wrap(session, key_id),
- from, session->opt))
+ from, session->opt, initial_packet))
{
+ /* if an initial packet in read_control_auth, we rather
+ * error out than anything else */
+ if (initial_packet)
+ {
+ multi->n_hard_errors++;
+ }
goto error;
}
diff --git a/src/openvpn/ssl_common.h b/src/openvpn/ssl_common.h
index 085256347ec..8b6ae3f2430 100644
--- a/src/openvpn/ssl_common.h
+++ b/src/openvpn/ssl_common.h
@@ -80,22 +80,25 @@
#define S_INITIAL 1 /**< Initial \c key_state state after
* initialization by \c key_state_init()
* before start of three-way handshake. */
-#define S_PRE_START 2 /**< Waiting for the remote OpenVPN peer
+#define S_PRE_START_SKIP 2 /**< Waiting for the remote OpenVPN peer
* to acknowledge during the initial
* three-way handshake. */
-#define S_START 3 /**< Three-way handshake is complete,
+#define S_PRE_START 3 /**< Waiting for the remote OpenVPN peer
+ * to acknowledge during the initial
+ * three-way handshake. */
+#define S_START 4 /**< Three-way handshake is complete,
* start of key exchange. */
-#define S_SENT_KEY 4 /**< Local OpenVPN process has sent its
+#define S_SENT_KEY 5 /**< Local OpenVPN process has sent its
* part of the key material. */
-#define S_GOT_KEY 5 /**< Local OpenVPN process has received
+#define S_GOT_KEY 6 /**< Local OpenVPN process has received
* the remote's part of the key
* material. */
-#define S_ACTIVE 6 /**< Operational \c key_state state
+#define S_ACTIVE 7 /**< Operational \c key_state state
* immediately after negotiation has
* completed while still within the
* handshake window. Deferred auth and
* client connect can still be pending. */
-#define S_GENERATED_KEYS 7 /**< The data channel keys have been generated
+#define S_GENERATED_KEYS 8 /**< The data channel keys have been generated
* The TLS session is fully authenticated
* when reaching this state. */
diff --git a/src/openvpn/ssl_pkt.c b/src/openvpn/ssl_pkt.c
index 689cd7f99f9..41299f462db 100644
--- a/src/openvpn/ssl_pkt.c
+++ b/src/openvpn/ssl_pkt.c
@@ -200,7 +200,8 @@ bool
read_control_auth(struct buffer *buf,
struct tls_wrap_ctx *ctx,
const struct link_socket_actual *from,
- const struct tls_options *opt)
+ const struct tls_options *opt,
+ bool initial_packet)
{
struct gc_arena gc = gc_new();
bool ret = false;
@@ -208,7 +209,7 @@ read_control_auth(struct buffer *buf,
const uint8_t opcode = *(BPTR(buf)) >> P_OPCODE_SHIFT;
if ((opcode == P_CONTROL_HARD_RESET_CLIENT_V3
|| opcode == P_CONTROL_WKC_V1)
- && !tls_crypt_v2_extract_client_key(buf, ctx, opt))
+ && !tls_crypt_v2_extract_client_key(buf, ctx, opt, initial_packet))
{
msg(D_TLS_ERRORS,
"TLS Error: can not extract tls-crypt-v2 client key from %s",
@@ -373,7 +374,7 @@ tls_pre_decrypt_lite(const struct tls_auth_standalone *tas,
* into newbuf or just setting newbuf to point to the start of control
* message */
bool status = read_control_auth(&state->newbuf, &state->tls_wrap_tmp,
- from, NULL);
+ from, NULL, true);
if (!status)
{
diff --git a/src/openvpn/ssl_pkt.h b/src/openvpn/ssl_pkt.h
index c8a27fba9d7..2033da61ff7 100644
--- a/src/openvpn/ssl_pkt.h
+++ b/src/openvpn/ssl_pkt.h
@@ -207,14 +207,22 @@ write_control_auth(struct tls_session *session,
bool prepend_ack);
-/*
+
+/**
* Read a control channel authentication record.
+ * @param buf buffer that holds the incoming packet
+ * @param ctx control channel security context
+ * @param from incoming link socket address
+ * @param opt tls options struct for the session
+ * @param initial_packet whether this is the initial packet for the connection
+ * @return if the packet was successfully processed
*/
bool
read_control_auth(struct buffer *buf,
struct tls_wrap_ctx *ctx,
const struct link_socket_actual *from,
- const struct tls_options *opt);
+ const struct tls_options *opt,
+ bool initial_packet);
/**
diff --git a/src/openvpn/tls_crypt.c b/src/openvpn/tls_crypt.c
index 975d31fafb5..50228e786e0 100644
--- a/src/openvpn/tls_crypt.c
+++ b/src/openvpn/tls_crypt.c
@@ -612,7 +612,8 @@ tls_crypt_v2_verify_metadata(const struct tls_wrap_ctx *ctx,
bool
tls_crypt_v2_extract_client_key(struct buffer *buf,
struct tls_wrap_ctx *ctx,
- const struct tls_options *opt)
+ const struct tls_options *opt,
+ bool initial_packet)
{
if (!ctx->tls_crypt_v2_server_key.cipher)
{
@@ -641,6 +642,27 @@ tls_crypt_v2_extract_client_key(struct buffer *buf,
return false;
}
+ if (!initial_packet)
+ {
+ /* This might be a harmless resend of the packet but it is better to
+ * just ignore the WKC part than trying to setup tls-crypt keys again.
+ *
+ * A CONTROL_WKC_V1 packets has a normal packet part and an appended
+ * wrapped control key. These are authenticated individually. We already
+ * set up tls-crypt with the wrapped key, so we are ignoring this part
+ * of the message but we return the normal packet part as the normal
+ * part of the message might have been corrupted earlier and discarded
+ * and this is resend. So return the normal part of the packet,
+ * basically transforming the CONTROL_WKC_V1 into a normal CONTROL_V1
+ * packet*/
+ msg(D_TLS_ERRORS, "control channel security already setup ignoring "
+ "wrapped key part of packet.");
+
+ /* Remove client key from buffer so tls-crypt code can unwrap message */
+ ASSERT(buf_inc_len(buf, -(BLEN(&wrapped_client_key))));
+ return true;
+ }
+
ctx->tls_crypt_v2_metadata = alloc_buf(TLS_CRYPT_V2_MAX_METADATA_LEN);
if (!tls_crypt_v2_unwrap_client_key(&ctx->original_wrap_keydata,
&ctx->tls_crypt_v2_metadata,
diff --git a/src/openvpn/tls_crypt.h b/src/openvpn/tls_crypt.h
index 8c87e2080a1..331c0c060a7 100644
--- a/src/openvpn/tls_crypt.h
+++ b/src/openvpn/tls_crypt.h
@@ -207,11 +207,16 @@ void tls_crypt_v2_init_client_key(struct key_ctx_bi *key,
* message.
* @param ctx tls-wrap context to be initialized with the client key.
*
+ * @param initial_packet whether this is the initial packet of the
+ * connection. Only in these scenarios unwrapping
+ * of a tls-crypt-v2 key is allowed
+ *
* @returns true if a key was successfully extracted.
*/
bool tls_crypt_v2_extract_client_key(struct buffer *buf,
struct tls_wrap_ctx *ctx,
- const struct tls_options *opt);
+ const struct tls_options *opt,
+ bool initial_packet);
/**
* Generate a tls-crypt-v2 server key, and write to file.
diff --git a/tests/unit_tests/openvpn/test_tls_crypt.c b/tests/unit_tests/openvpn/test_tls_crypt.c
index 465543a740e..3eac04cf9ce 100644
--- a/tests/unit_tests/openvpn/test_tls_crypt.c
+++ b/tests/unit_tests/openvpn/test_tls_crypt.c
@@ -533,7 +533,7 @@ tls_crypt_v2_wrap_unwrap_max_metadata(void **state)
.mode = TLS_WRAP_CRYPT,
.tls_crypt_v2_server_key = ctx->server_keys.encrypt,
};
- assert_true(tls_crypt_v2_extract_client_key(&ctx->wkc, &wrap_ctx, NULL));
+ assert_true(tls_crypt_v2_extract_client_key(&ctx->wkc, &wrap_ctx, NULL, true));
tls_wrap_free(&wrap_ctx);
}

Binary file not shown.

BIN
openvpn-2.6.9.tar.gz Normal file

Binary file not shown.

View File

@ -1,11 +1,14 @@
Name: openvpn
Version: 2.6.8
Release: 1
Version: 2.6.9
Release: 4
Summary: A full-featured open source SSL VPN solution
License: GPL-2.0-or-later and OpenSSL and SSLeay
URL: https://community.openvpn.net/openvpn
Source0: https://build.openvpn.net/downloads/releases/%{name}-%{version}.tar.gz
Patch0: openvpn-2.4-change-tmpfiles-permissions.patch
Patch1: CVE-2024-28882.patch
Patch2: CVE-2024-5594.patch
Patch3: CVE-2025-2704.patch
BuildRequires: openssl-devel lz4-devel systemd-devel lzo-devel gcc
BuildRequires: iproute pam-devel pkcs11-helper-devel >= 1.11
BuildRequires: libselinux-devel
@ -124,6 +127,18 @@ fi
%{_mandir}/man5/openvpn-examples.5.gz
%changelog
* Fri Apr 04 2025 Funda Wang <fundawang@yeah.net> - 2.6.9-4
- fix CVE-2025-2704
* Sat Jul 20 2024 Funda Wang <fundawang@yeah.net> - 2.6.9-3
- fix CVE-2024-5594
* Tue Jul 09 2024 zhangxianting <zhangxianting@uninontech.com> - 2.6.9-2
- Fix CVE-2024-28882
* Thu Feb 22 2024 yaoxin <yao_xin001@hoperun.com> - 2.6.9-1
- Upgrade to 2.6.9
* Mon Jan 08 2024 Ge Wang <wang__ge@126.com> - 2.6.8-1
- Update to version 2.6.8