diff mbox series

[meta-networking,scarthgap,6/6] openvpn: patch CVE-2025-13086

Message ID 20251216071537.3174578-6-ankur.tyagi85@gmail.com
State New
Headers show
Series [meta-networking,scarthgap,1/6] civetweb: patch CVE-2025-9648 | expand

Commit Message

Ankur Tyagi Dec. 16, 2025, 7:15 a.m. UTC
From: Ankur Tyagi <ankur.tyagi85@gmail.com>

Details https://nvd.nist.gov/vuln/detail/CVE-2025-13086

Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
---
 .../openvpn/openvpn/CVE-2025-13086.patch      | 157 ++++++++++++++++++
 .../recipes-support/openvpn/openvpn_2.6.14.bb |   1 +
 2 files changed, 158 insertions(+)
 create mode 100644 meta-networking/recipes-support/openvpn/openvpn/CVE-2025-13086.patch
diff mbox series

Patch

diff --git a/meta-networking/recipes-support/openvpn/openvpn/CVE-2025-13086.patch b/meta-networking/recipes-support/openvpn/openvpn/CVE-2025-13086.patch
new file mode 100644
index 0000000000..a37ef84a8d
--- /dev/null
+++ b/meta-networking/recipes-support/openvpn/openvpn/CVE-2025-13086.patch
@@ -0,0 +1,157 @@ 
+From c56eb06a59ce8ccd601f1d58aa71cbd1211ee8d6 Mon Sep 17 00:00:00 2001
+From: Arne Schwabe <arne@rfc2549.org>
+Date: Mon, 27 Oct 2025 10:05:55 +0100
+Subject: [PATCH] Fix memcmp check for the hmac verification in the 3way
+ handshake being inverted
+
+This is a stupid mistake but causes all hmac cookies to be accepted,
+thus breaking source IP address validation.   As a consequence, TLS
+sessions can be openend and state can be consumed in the server from
+IP addresses that did not initiate an initial connection.
+
+While at it, fix check to only allow [t-2;t] timeslots, disallowing
+HMACs coming in from a future timeslot.
+
+Github: OpenVPN/openvpn-private-issues#56
+
+CVE: 2025-13086
+
+Reported-By: Joshua Rogers <contact@joshua.hu>
+Found-by: ZeroPath (https://zeropath.com/)
+Reported-By: stefan@srlabs.de
+
+Change-Id: I9cbe2bf535575b47ddd7f34e985c5c1c6953a6fc
+Signed-off-by: Arne Schwabe <arne@rfc2549.org>
+Acked-by: Max Fillinger <max@max-fillinger.net>
+(cherry picked from commit 68ec931e7fb4af11d5ba0d4283df0350083fd373)
+
+CVE: CVE-2025-13086
+Upstream-Status: Backport [https://github.com/OpenVPN/openvpn/commit/fa6a1824b0f37bff137204156a74ca28cf5b6f83]
+Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
+---
+ src/openvpn/ssl_pkt.c               |  7 ++--
+ tests/unit_tests/openvpn/test_pkt.c | 57 ++++++++++++++++++++++++++++-
+ 2 files changed, 60 insertions(+), 4 deletions(-)
+
+diff --git a/src/openvpn/ssl_pkt.c b/src/openvpn/ssl_pkt.c
+index 41299f46..e820dc93 100644
+--- a/src/openvpn/ssl_pkt.c
++++ b/src/openvpn/ssl_pkt.c
+@@ -545,13 +545,14 @@ check_session_id_hmac(struct tls_pre_decrypt_state *state,
+         return false;
+     }
+ 
+-    /* check adjacent timestamps too */
+-    for (int offset = -2; offset <= 1; offset++)
++    /* check adjacent timestamps too, the handwindow is split in 2 for the
++     * offset, so we check the current timeslot and the two before that */
++    for (int offset = -2; offset <= 0; offset++)
+     {
+         struct session_id expected_id =
+             calculate_session_id_hmac(state->peer_session_id, from, hmac, handwindow, offset);
+ 
+-        if (memcmp_constant_time(&expected_id, &state->server_session_id, SID_SIZE))
++        if (memcmp_constant_time(&expected_id, &state->server_session_id, SID_SIZE) == 0)
+         {
+             return true;
+         }
+diff --git a/tests/unit_tests/openvpn/test_pkt.c b/tests/unit_tests/openvpn/test_pkt.c
+index 74d7311f..4e97384d 100644
+--- a/tests/unit_tests/openvpn/test_pkt.c
++++ b/tests/unit_tests/openvpn/test_pkt.c
+@@ -429,6 +429,8 @@ test_verify_hmac_tls_auth(void **ut_state)
+     hmac_ctx_t *hmac = session_id_hmac_init();
+ 
+     struct link_socket_actual from = { 0 };
++    from.dest.addr.sa.sa_family = AF_INET;
++    from.dest.addr.in4.sin_addr.s_addr = ntohl(0x01020304);
+     struct tls_auth_standalone tas = { 0 };
+     struct tls_pre_decrypt_state state = { 0 };
+ 
+@@ -456,10 +458,12 @@ test_verify_hmac_tls_auth(void **ut_state)
+ static void
+ test_verify_hmac_none(void **ut_state)
+ {
++    now = 1000;
+     hmac_ctx_t *hmac = session_id_hmac_init();
+ 
+     struct link_socket_actual from = { 0 };
+     from.dest.addr.sa.sa_family = AF_INET;
++    from.dest.addr.in4.sin_addr.s_addr = ntohl(0x01020304);
+ 
+     struct tls_auth_standalone tas = { 0 };
+     struct tls_pre_decrypt_state state = { 0 };
+@@ -475,8 +479,59 @@ test_verify_hmac_none(void **ut_state)
+     assert_int_equal(verdict, VERDICT_VALID_ACK_V1);
+ 
+     bool valid = check_session_id_hmac(&state, &from.dest, hmac, 30);
++    assert_false(valid);
++
++    struct session_id client_id = { { 0xae, 0xb9, 0xaf, 0xe1, 0xf0, 0x1d, 0x79, 0xc8 } };
++    assert_memory_equal(&client_id, &state.peer_session_id, sizeof(struct session_id));
++
++    struct session_id expected_id = calculate_session_id_hmac(client_id, &from.dest, hmac, 30, 0);
++
++    free_tls_pre_decrypt_state(&state);
++    buf_reset_len(&buf);
++
++    /* Write the packet again into the buffer but this time, replacing the peer packet
++     * id with the expected one */
++    buf_write(&buf, client_ack_none_random_id, sizeof(client_ack_none_random_id) - 8);
++    buf_write(&buf, expected_id.id, 8);
++
++    verdict = tls_pre_decrypt_lite(&tas, &state, &from, &buf);
++    assert_int_equal(verdict, VERDICT_VALID_ACK_V1);
++    valid = check_session_id_hmac(&state, &from.dest, hmac, 30);
++
+     assert_true(valid);
+ 
++    /* Our handwindow is 30 so the slices are half of that, so they are
++     * (975,990), (990, 1005), (1005, 1020), (1020, 1035), (1035, 1050)
++     * So setting time to the two future ones should work
++     */
++    now = 980;
++    assert_false(check_session_id_hmac(&state, &from.dest, hmac, 30));
++    now = 1040;
++    assert_false(check_session_id_hmac(&state, &from.dest, hmac, 30));
++    now = 1002;
++    assert_true(check_session_id_hmac(&state, &from.dest, hmac, 30));
++    now = 1022;
++    assert_true(check_session_id_hmac(&state, &from.dest, hmac, 30));
++    now = 1010;
++    assert_true(check_session_id_hmac(&state, &from.dest, hmac, 30));
++
++    /* Changing the IP address should make this invalid */
++    from.dest.addr.in4.sin_addr.s_addr = ntohl(0x01020305);
++    assert_false(check_session_id_hmac(&state, &from.dest, hmac, 30));
++
++    /* Change to the correct one again */
++    from.dest.addr.in4.sin_addr.s_addr = ntohl(0x01020304);
++    assert_true(check_session_id_hmac(&state, &from.dest, hmac, 30));
++
++    /* Modify the peer id, should now fail hmac verification */
++    buf_inc_len(&buf, -4);
++    buf_write_u32(&buf, 0x12345678);
++
++    free_tls_pre_decrypt_state(&state);
++    verdict = tls_pre_decrypt_lite(&tas, &state, &from, &buf);
++    assert_int_equal(verdict, VERDICT_VALID_ACK_V1);
++    assert_false(check_session_id_hmac(&state, &from.dest, hmac, 30));
++
+     free_tls_pre_decrypt_state(&state);
+     free_buf(&buf);
+     hmac_ctx_cleanup(hmac);
+@@ -663,12 +718,12 @@ int
+ main(void)
+ {
+     const struct CMUnitTest tests[] = {
++        cmocka_unit_test(test_verify_hmac_none),
+         cmocka_unit_test(test_tls_decrypt_lite_none),
+         cmocka_unit_test(test_tls_decrypt_lite_auth),
+         cmocka_unit_test(test_tls_decrypt_lite_crypt),
+         cmocka_unit_test(test_parse_ack),
+         cmocka_unit_test(test_calc_session_id_hmac_static),
+-        cmocka_unit_test(test_verify_hmac_none),
+         cmocka_unit_test(test_verify_hmac_tls_auth),
+         cmocka_unit_test(test_generate_reset_packet_plain),
+         cmocka_unit_test(test_generate_reset_packet_tls_auth),
diff --git a/meta-networking/recipes-support/openvpn/openvpn_2.6.14.bb b/meta-networking/recipes-support/openvpn/openvpn_2.6.14.bb
index 5361709f0c..305a69bec4 100644
--- a/meta-networking/recipes-support/openvpn/openvpn_2.6.14.bb
+++ b/meta-networking/recipes-support/openvpn/openvpn_2.6.14.bb
@@ -10,6 +10,7 @@  inherit autotools systemd update-rc.d pkgconfig
 SRC_URI = "http://swupdate.openvpn.org/community/releases/${BP}.tar.gz \
            file://0001-configure.ac-eliminate-build-path-from-openvpn-versi.patch \
            file://openvpn \
+           file://CVE-2025-13086.patch \
           "
 
 UPSTREAM_CHECK_URI = "https://openvpn.net/community-downloads"