Compare commits
10 Commits
2df8d2d9e4
...
891f7f8bdc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
891f7f8bdc | ||
|
|
6c33e2ce2a | ||
|
|
30cd3fc3b4 | ||
|
|
486b1533a0 | ||
|
|
30bef67c00 | ||
|
|
51b1fd72e4 | ||
|
|
d48d34aacf | ||
|
|
46e41f321d | ||
|
|
c481d84550 | ||
|
|
55187fdb53 |
140
CVE-2024-28882.patch
Normal file
140
CVE-2024-28882.patch
Normal 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
363
CVE-2024-5594.patch
Normal 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
282
CVE-2025-2704.patch
Normal 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
BIN
openvpn-2.6.9.tar.gz
Normal file
Binary file not shown.
21
openvpn.spec
21
openvpn.spec
@ -1,15 +1,18 @@
|
|||||||
Name: openvpn
|
Name: openvpn
|
||||||
Version: 2.6.8
|
Version: 2.6.9
|
||||||
Release: 1
|
Release: 4
|
||||||
Summary: A full-featured open source SSL VPN solution
|
Summary: A full-featured open source SSL VPN solution
|
||||||
License: GPL-2.0-or-later and OpenSSL and SSLeay
|
License: GPL-2.0-or-later and OpenSSL and SSLeay
|
||||||
URL: https://community.openvpn.net/openvpn
|
URL: https://community.openvpn.net/openvpn
|
||||||
Source0: https://build.openvpn.net/downloads/releases/%{name}-%{version}.tar.gz
|
Source0: https://build.openvpn.net/downloads/releases/%{name}-%{version}.tar.gz
|
||||||
Patch0: openvpn-2.4-change-tmpfiles-permissions.patch
|
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: openssl-devel lz4-devel systemd-devel lzo-devel gcc
|
||||||
BuildRequires: iproute pam-devel pkcs11-helper-devel >= 1.11
|
BuildRequires: iproute pam-devel pkcs11-helper-devel >= 1.11
|
||||||
BuildRequires: libselinux-devel
|
BuildRequires: libselinux-devel
|
||||||
BuildRequires: libcap-ng-devel
|
BuildRequires: libcap-ng-devel
|
||||||
|
|
||||||
Requires: iproute
|
Requires: iproute
|
||||||
Requires(pre): /usr/sbin/useradd
|
Requires(pre): /usr/sbin/useradd
|
||||||
@ -124,6 +127,18 @@ fi
|
|||||||
%{_mandir}/man5/openvpn-examples.5.gz
|
%{_mandir}/man5/openvpn-examples.5.gz
|
||||||
|
|
||||||
%changelog
|
%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
|
* Mon Jan 08 2024 Ge Wang <wang__ge@126.com> - 2.6.8-1
|
||||||
- Update to version 2.6.8
|
- Update to version 2.6.8
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user