Attention is currently required from: plaisthos.

Hello plaisthos,

I'd like you to do a code review.
Please visit

    http://gerrit.openvpn.net/c/openvpn/+/1965?usp=email

to review the following change.


Change subject: Add a shared TLV codec and use it for early negotiation
......................................................................

Add a shared TLV codec and use it for early negotiation

Early negotiation in the reset packets is a sequence of TLVs, and the
out-of-band and control data messages use the same TLV format and type
space. Add one TLV codec for all of them, control_msg.c/h, and move
early negotiation onto it.

The codec only handles the framing: reading and writing TLV headers,
writing a 16-bit TLV, and walking a payload TLV by TLV with each value
checked against the payload. How to treat an unknown type is up to each
message. All TLV types are defined in control_msg.h;
TLV_TYPE_EARLY_NEG_FLAGS moves there from ssl_pkt.h.

The bytes on the wire do not change. Since the top bit of the type
field is the "optional" flag, a TLV of type 0x8001 is now read as the
flags TLV marked optional rather than skipped as unknown, so one whose
length is not 2 now fails the handshake as malformed.

Change-Id: I89c3133e009dc75dc2c10c607b3f0a24c04e8146
Signed-off-by: Lev Stipakov <[email protected]>
---
M CMakeLists.txt
M src/openvpn/Makefile.am
A src/openvpn/control_msg.c
A src/openvpn/control_msg.h
M src/openvpn/ssl.c
M src/openvpn/ssl_pkt.c
M src/openvpn/ssl_pkt.h
M tests/unit_tests/openvpn/Makefile.am
A tests/unit_tests/openvpn/test_control_msg.c
M tests/unit_tests/openvpn/test_pkt.c
10 files changed, 461 insertions(+), 23 deletions(-)



  git pull ssh://gerrit.openvpn.net:29418/openvpn refs/changes/65/1965/1

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6eb5954..e1a6079f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -445,6 +445,8 @@
     src/openvpn/console.c
     src/openvpn/console_builtin.c
     src/openvpn/console.h
+    src/openvpn/control_msg.c
+    src/openvpn/control_msg.h
     src/openvpn/crypto.c
     src/openvpn/crypto.h
     src/openvpn/crypto_backend.h
@@ -883,6 +885,8 @@

     target_sources(test_pkt PRIVATE
         tests/unit_tests/openvpn/mock_win32_execve.c
+        tests/unit_tests/openvpn/test_control_msg.c
+        src/openvpn/control_msg.c
         src/openvpn/argv.c
         src/openvpn/base64.c
         src/openvpn/crypto_epoch.c
diff --git a/src/openvpn/Makefile.am b/src/openvpn/Makefile.am
index 7fd12b4..a17ae8e 100644
--- a/src/openvpn/Makefile.am
+++ b/src/openvpn/Makefile.am
@@ -53,6 +53,7 @@
        common.h \
        comp.c comp.h compstub.c \
        comp-lz4.c comp-lz4.h \
+       control_msg.c control_msg.h \
        crypto.c crypto.h crypto_backend.h \
        crypto_openssl.c crypto_openssl.h \
        crypto_mbedtls_legacy.c crypto_mbedtls_legacy.h \
diff --git a/src/openvpn/control_msg.c b/src/openvpn/control_msg.c
new file mode 100644
index 0000000..ca3aab9
--- /dev/null
+++ b/src/openvpn/control_msg.c
@@ -0,0 +1,83 @@
+/*
+ *  OpenVPN -- An application to securely tunnel IP networks
+ *             over a single TCP/UDP port, with support for SSL/TLS-based
+ *             session authentication and key exchange,
+ *             packet encryption, packet authentication, and
+ *             packet compression.
+ *
+ *  Copyright (C) 2002-2026 OpenVPN Inc <[email protected]>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2
+ *  as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "syshead.h"
+
+#include "control_msg.h"
+
+bool
+ctrl_msg_tlv_write_header(struct buffer *buf, uint16_t type, bool optional, 
uint16_t value_len)
+{
+    uint16_t field = type & CTRL_MSG_TLV_TYPE_MASK;
+    if (optional)
+    {
+        field |= CTRL_MSG_TLV_OPTIONAL_FLAG;
+    }
+    return buf_write_u16(buf, field) && buf_write_u16(buf, value_len);
+}
+
+bool
+ctrl_msg_tlv_write_u16(struct buffer *buf, uint16_t type, bool optional, 
uint16_t value)
+{
+    return ctrl_msg_tlv_write_header(buf, type, optional, sizeof(uint16_t))
+           && buf_write_u16(buf, value);
+}
+
+bool
+ctrl_msg_tlv_read_header(struct buffer *buf, struct ctrl_msg_tlv_header *hdr)
+{
+    int field = buf_read_u16(buf);
+    if (field < 0)
+    {
+        return false;
+    }
+    int len = buf_read_u16(buf);
+    if (len < 0)
+    {
+        return false;
+    }
+    hdr->type = (uint16_t)(field & CTRL_MSG_TLV_TYPE_MASK);
+    hdr->optional = (field & CTRL_MSG_TLV_OPTIONAL_FLAG) != 0;
+    hdr->value_len = (uint16_t)len;
+    return true;
+}
+
+bool
+ctrl_msg_tlv_next(struct buffer *buf, struct ctrl_msg_tlv_header *hdr, struct 
buffer *value)
+{
+    if (!ctrl_msg_tlv_read_header(buf, hdr))
+    {
+        return false; /* fewer than 4 bytes left: truncated header */
+    }
+    /* Advance past the value; fails if buf is shorter than the header claims. 
*/
+    uint8_t *v = buf_read_alloc(buf, hdr->value_len);
+    if (!v)
+    {
+        return false;
+    }
+    buf_set_read(value, v, hdr->value_len);
+    return true;
+}
diff --git a/src/openvpn/control_msg.h b/src/openvpn/control_msg.h
new file mode 100644
index 0000000..73fd0a1
--- /dev/null
+++ b/src/openvpn/control_msg.h
@@ -0,0 +1,99 @@
+/*
+ *  OpenVPN -- An application to securely tunnel IP networks
+ *             over a single TCP/UDP port, with support for SSL/TLS-based
+ *             session authentication and key exchange,
+ *             packet encryption, packet authentication, and
+ *             packet compression.
+ *
+ *  Copyright (C) 2002-2026 OpenVPN Inc <[email protected]>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2
+ *  as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+/**
+ * @file
+ * TLV framing shared by the TLV-based control messages of the wire protocol.
+ *
+ * Such a message payload is a sequence of TLV entries. Each TLV starts with a
+ * 4-byte header: a 16-bit field whose most significant bit is the "optional"
+ * flag and whose remaining 15 bits are the type, followed by a 16-bit length
+ * giving the size of the value that follows the header.
+ *
+ * All of these messages share one TLV type space, so the TLV types are all
+ * defined here. Only the framing is handled here: what a message does with a
+ * TLV type it does not know, marked optional or not, is up to its parser.
+ */
+
+#ifndef CONTROL_MSG_H
+#define CONTROL_MSG_H
+
+#include "buffer.h"
+
+/* TLV types */
+#define TLV_TYPE_EARLY_NEG_FLAGS 0x0001 /* early negotiation, in the reset 
packets */
+
+/* TLV header bit layout of the first 16-bit field */
+#define CTRL_MSG_TLV_OPTIONAL_FLAG 0x8000
+#define CTRL_MSG_TLV_TYPE_MASK     0x7fff
+
+/* The header every TLV carries: the 15-bit type and optional flag packed into
+ * the first 16-bit field, then the length of the value that follows. */
+struct ctrl_msg_tlv_header
+{
+    uint16_t type;      /**< the 15-bit TLV type */
+    bool optional;      /**< value of the optional flag */
+    uint16_t value_len; /**< length of the value following the header */
+};
+
+/**
+ * Write a TLV header (type + optional flag + value length) to buf.
+ *
+ * @return true on success, false if buf has insufficient space.
+ */
+bool ctrl_msg_tlv_write_header(struct buffer *buf, uint16_t type, bool 
optional,
+                               uint16_t value_len);
+
+/**
+ * Write a complete TLV whose value is a single 16-bit integer to buf.
+ *
+ * @return true on success, false if buf has insufficient space.
+ */
+bool ctrl_msg_tlv_write_u16(struct buffer *buf, uint16_t type, bool optional, 
uint16_t value);
+
+/**
+ * Read a TLV header from buf, advancing past it.
+ *
+ * @param buf  buffer positioned at the TLV header
+ * @param hdr  filled with the type, optional flag and value length on success
+ * @return true on success, false if there are not enough bytes for a header.
+ */
+bool ctrl_msg_tlv_read_header(struct buffer *buf, struct ctrl_msg_tlv_header 
*hdr);
+
+/**
+ * Read the next TLV from buf, advancing past its header and value. Call it
+ * while BLEN(buf) > 0 to walk a whole payload.
+ *
+ * The length from the header is validated against buf, so on success the
+ * whole value is present: a header claiming more bytes than buf holds is
+ * rejected.
+ *
+ * @param buf    buffer positioned at a TLV header
+ * @param hdr    filled with the TLV's type, optional flag and value length
+ * @param value  set to a buffer covering exactly the TLV's value; it points
+ *               into buf and owns no storage
+ * @return true on success, false if the header or the value is truncated; buf
+ *         may then be left partly consumed.
+ */
+bool ctrl_msg_tlv_next(struct buffer *buf, struct ctrl_msg_tlv_header *hdr, 
struct buffer *value);
+
+#endif /* ifndef CONTROL_MSG_H */
diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c
index 76150d4..92f1991 100644
--- a/src/openvpn/ssl.c
+++ b/src/openvpn/ssl.c
@@ -52,6 +52,7 @@
 #include "pkcs11.h"
 #include "route.h"
 #include "tls_crypt.h"
+#include "control_msg.h"

 #include "crypto_epoch.h"
 #include "ssl.h"
@@ -2595,26 +2596,21 @@
 {
     while (buf->len > 0)
     {
-        if (buf_len(buf) < 4)
-        {
-            goto error;
-        }
-        /* read type */
-        int type = buf_read_u16(buf);
-        int len = buf_read_u16(buf);
-        if (type < 0 || len < 0 || buf_len(buf) < len)
+        struct ctrl_msg_tlv_header hdr;
+        struct buffer value;
+        if (!ctrl_msg_tlv_next(buf, &hdr, &value))
         {
             goto error;
         }

-        switch (type)
+        switch (hdr.type)
         {
             case TLV_TYPE_EARLY_NEG_FLAGS:
-                if (len != sizeof(uint16_t))
+                if (BLEN(&value) != sizeof(uint16_t))
                 {
                     goto error;
                 }
-                int flags = buf_read_u16(buf);
+                int flags = buf_read_u16(&value);

                 if (flags & EARLY_NEG_FLAG_RESEND_WKC)
                 {
@@ -2623,8 +2619,8 @@
                 break;

             default:
-                /* Skip types we do not parse */
-                buf_advance(buf, len);
+                /* Skip types we do not parse, marked optional or not */
+                break;
         }
     }
     reliable_mark_deleted(ks->rec_reliable, buf);
diff --git a/src/openvpn/ssl_pkt.c b/src/openvpn/ssl_pkt.c
index 90b2aec..405e5b4 100644
--- a/src/openvpn/ssl_pkt.c
+++ b/src/openvpn/ssl_pkt.c
@@ -33,6 +33,7 @@
 #include "reliable.h"
 #include "siphash.h"
 #include "tls_crypt.h"
+#include "control_msg.h"

 /*
  * Dependent on hmac size, opcode size, and session_id size.
@@ -431,9 +432,8 @@
     /* Add indication for tls-crypt-v2 to resend the WKc with the reply */
     if (request_resend_wkc)
     {
-        buf_write_u16(&buf, TLV_TYPE_EARLY_NEG_FLAGS); /* TYPE: flags */
-        buf_write_u16(&buf, sizeof(uint16_t));
-        buf_write_u16(&buf, EARLY_NEG_FLAG_RESEND_WKC);
+        ASSERT(ctrl_msg_tlv_write_u16(&buf, TLV_TYPE_EARLY_NEG_FLAGS, false,
+                                      EARLY_NEG_FLAG_RESEND_WKC));
     }

     /* Add tls-auth/tls-crypt wrapping, this might replace buf with
diff --git a/src/openvpn/ssl_pkt.h b/src/openvpn/ssl_pkt.h
index 03e8930..5a1d194 100644
--- a/src/openvpn/ssl_pkt.h
+++ b/src/openvpn/ssl_pkt.h
@@ -305,10 +305,8 @@
 #define EARLY_NEG_START 0x0f000000


-/* Early negotiation that part of the server response in the RESET_V2 packet.
- * Since clients that announce early negotiation support will treat the payload
- * of reset packets special and parse it as TLV messages.
- * as TLV (type, length, value) */
-#define TLV_TYPE_EARLY_NEG_FLAGS  0x0001
+/* Early negotiation: clients that announce support for it parse the payload of
+ * the reset packets as TLVs (see control_msg.h). Flags carried in the value of
+ * the TLV_TYPE_EARLY_NEG_FLAGS TLV: */
 #define EARLY_NEG_FLAG_RESEND_WKC 0x0001
 #endif /* ifndef SSL_PKT_H */
diff --git a/tests/unit_tests/openvpn/Makefile.am 
b/tests/unit_tests/openvpn/Makefile.am
index 5954902..d9caa03 100644
--- a/tests/unit_tests/openvpn/Makefile.am
+++ b/tests/unit_tests/openvpn/Makefile.am
@@ -157,10 +157,12 @@
        -I$(top_srcdir)/include -I$(top_srcdir)/src/compat 
-I$(top_srcdir)/src/openvpn \
        @TEST_CFLAGS@
 pkt_testdriver_LDFLAGS = @TEST_LDFLAGS@
-pkt_testdriver_SOURCES = test_pkt.c mock_msg.c mock_msg.h mock_win32_execve.c 
test_common.h \
+pkt_testdriver_SOURCES = test_pkt.c test_control_msg.c mock_msg.c mock_msg.h 
mock_win32_execve.c \
+       test_common.h \
        $(top_srcdir)/src/openvpn/argv.c \
        $(top_srcdir)/src/openvpn/base64.c \
        $(top_srcdir)/src/openvpn/buffer.c \
+       $(top_srcdir)/src/openvpn/control_msg.c \
        $(top_srcdir)/src/openvpn/crypto.c \
        $(top_srcdir)/src/openvpn/crypto_epoch.c \
        $(top_srcdir)/src/openvpn/crypto_mbedtls.c \
diff --git a/tests/unit_tests/openvpn/test_control_msg.c 
b/tests/unit_tests/openvpn/test_control_msg.c
new file mode 100644
index 0000000..40794ab
--- /dev/null
+++ b/tests/unit_tests/openvpn/test_control_msg.c
@@ -0,0 +1,209 @@
+/*
+ *  OpenVPN -- An application to securely tunnel IP networks
+ *             over a single TCP/UDP port, with support for SSL/TLS-based
+ *             session authentication and key exchange,
+ *             packet encryption, packet authentication, and
+ *             packet compression.
+ *
+ *  Copyright (C) 2002-2026 OpenVPN Inc <[email protected]>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2
+ *  as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "syshead.h"
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include "control_msg.h"
+#include "ssl_pkt.h"
+#include "test_common.h"
+
+/* The early negotiation flags TLV of a reset packet asking a tls-crypt-v2
+ * client to resend its WKc, as older versions wrote it by hand. */
+static void
+test_tlv_write_u16_wire_format(void **state)
+{
+    struct gc_arena gc = gc_new();
+    struct buffer buf = alloc_buf_gc(16, &gc);
+
+    assert_true(ctrl_msg_tlv_write_u16(&buf, TLV_TYPE_EARLY_NEG_FLAGS, false,
+                                       EARLY_NEG_FLAG_RESEND_WKC));
+    const uint8_t expected[] = { 0x00, 0x01, 0x00, 0x02, 0x00, 0x01 };
+    assert_int_equal(BLEN(&buf), sizeof(expected));
+    assert_memory_equal(BPTR(&buf), expected, sizeof(expected));
+
+    /* the optional flag is the most significant bit of the type field */
+    buf_clear(&buf);
+    assert_true(ctrl_msg_tlv_write_u16(&buf, TLV_TYPE_EARLY_NEG_FLAGS, true, 
0));
+    assert_int_equal(BPTR(&buf)[0], 0x80);
+    assert_int_equal(BPTR(&buf)[1], 0x01);
+
+    gc_free(&gc);
+}
+
+/* A TLV header survives the round trip, the optional flag kept apart from
+ * the 15-bit type. */
+static void
+test_tlv_header_roundtrip(void **state)
+{
+    struct gc_arena gc = gc_new();
+    struct buffer buf = alloc_buf_gc(16, &gc);
+
+    assert_true(ctrl_msg_tlv_write_header(&buf, 0x1234, true, 5));
+    assert_int_equal(BLEN(&buf), 4);
+
+    struct ctrl_msg_tlv_header hdr;
+    assert_true(ctrl_msg_tlv_read_header(&buf, &hdr));
+    assert_int_equal(hdr.type, 0x1234);
+    assert_true(hdr.optional);
+    assert_int_equal(hdr.value_len, 5);
+    assert_int_equal(BLEN(&buf), 0);
+
+    gc_free(&gc);
+}
+
+/* Walking a payload returns each TLV in turn, with a value covering exactly
+ * its bytes, and consumes the payload. */
+static void
+test_tlv_next_walks_payload(void **state)
+{
+    struct gc_arena gc = gc_new();
+    struct buffer buf = alloc_buf_gc(64, &gc);
+
+    assert_true(ctrl_msg_tlv_write_u16(&buf, TLV_TYPE_EARLY_NEG_FLAGS, false, 
0xabcd));
+    assert_true(ctrl_msg_tlv_write_header(&buf, 0x7ff, true, 4));
+    assert_true(buf_write_u32(&buf, 0x11223344));
+    assert_true(ctrl_msg_tlv_write_header(&buf, 0x5, false, 0)); /* empty 
value */
+
+    struct ctrl_msg_tlv_header hdr;
+    struct buffer value;
+
+    assert_true(ctrl_msg_tlv_next(&buf, &hdr, &value));
+    assert_int_equal(hdr.type, TLV_TYPE_EARLY_NEG_FLAGS);
+    assert_false(hdr.optional);
+    assert_int_equal(BLEN(&value), 2);
+    assert_int_equal(buf_read_u16(&value), 0xabcd);
+
+    assert_true(ctrl_msg_tlv_next(&buf, &hdr, &value));
+    assert_int_equal(hdr.type, 0x7ff);
+    assert_true(hdr.optional);
+    assert_int_equal(BLEN(&value), 4);
+
+    assert_true(ctrl_msg_tlv_next(&buf, &hdr, &value));
+    assert_int_equal(hdr.type, 0x5);
+    assert_int_equal(BLEN(&value), 0);
+
+    assert_int_equal(BLEN(&buf), 0);
+
+    gc_free(&gc);
+}
+
+/* A TLV header claiming more value bytes than the payload holds is rejected,
+ * rather than read past the end of the payload. */
+static void
+test_tlv_next_value_truncated(void **state)
+{
+    struct gc_arena gc = gc_new();
+    struct buffer buf = alloc_buf_gc(64, &gc);
+
+    assert_true(ctrl_msg_tlv_write_header(&buf, TLV_TYPE_EARLY_NEG_FLAGS, 
false, 8));
+    assert_true(buf_write_u32(&buf, 0));
+
+    struct ctrl_msg_tlv_header hdr;
+    struct buffer value;
+    assert_false(ctrl_msg_tlv_next(&buf, &hdr, &value));
+
+    gc_free(&gc);
+}
+
+/* Reading a TLV header must fail when the buffer holds less data than a
+ * complete 4-byte header, rather than read past the available data. */
+static void
+test_tlv_header_truncated(void **state)
+{
+    struct gc_arena gc = gc_new();
+    const uint8_t bytes[] = { 0x00, 0x01, 0x00 };
+
+    /* 0 to 3 bytes: none, part of the type field, the type field, and the type
+     * field with half of the length */
+    for (size_t n = 0; n <= sizeof(bytes); n++)
+    {
+        struct buffer buf = alloc_buf_gc(16, &gc);
+        assert_true(buf_write(&buf, bytes, n));
+        struct buffer copy = buf;
+
+        struct ctrl_msg_tlv_header hdr;
+        struct buffer value;
+        assert_false(ctrl_msg_tlv_read_header(&buf, &hdr));
+        assert_false(ctrl_msg_tlv_next(&copy, &hdr, &value));
+    }
+
+    gc_free(&gc);
+}
+
+/* Writing fails, rather than writes a partial TLV, when buf is too small. */
+static void
+test_tlv_write_no_room(void **state)
+{
+    struct gc_arena gc = gc_new();
+
+    struct buffer buf = alloc_buf_gc(3, &gc);
+    assert_false(ctrl_msg_tlv_write_header(&buf, TLV_TYPE_EARLY_NEG_FLAGS, 
false, 2));
+
+    /* room for the header but not the value */
+    buf = alloc_buf_gc(5, &gc);
+    assert_false(ctrl_msg_tlv_write_u16(&buf, TLV_TYPE_EARLY_NEG_FLAGS, false, 
1));
+
+    gc_free(&gc);
+}
+
+/* A type does not spill into the optional flag: only its 15 bits are written. 
*/
+static void
+test_tlv_write_masks_type(void **state)
+{
+    struct gc_arena gc = gc_new();
+    struct buffer buf = alloc_buf_gc(16, &gc);
+
+    assert_true(ctrl_msg_tlv_write_header(&buf, 0x8123, false, 0));
+
+    struct ctrl_msg_tlv_header hdr;
+    assert_true(ctrl_msg_tlv_read_header(&buf, &hdr));
+    assert_int_equal(hdr.type, 0x0123);
+    assert_false(hdr.optional);
+
+    gc_free(&gc);
+}
+
+/* The TLV codec tests run as a group of pkt_testdriver; see test_pkt.c. */
+int
+run_control_msg_tests(void)
+{
+    const struct CMUnitTest tests[] = {
+        cmocka_unit_test(test_tlv_write_u16_wire_format),
+        cmocka_unit_test(test_tlv_header_roundtrip),
+        cmocka_unit_test(test_tlv_next_walks_payload),
+        cmocka_unit_test(test_tlv_next_value_truncated),
+        cmocka_unit_test(test_tlv_header_truncated),
+        cmocka_unit_test(test_tlv_write_no_room),
+        cmocka_unit_test(test_tlv_write_masks_type),
+    };
+
+    return cmocka_run_group_tests_name("control message TLV tests", tests, 
NULL, NULL);
+}
diff --git a/tests/unit_tests/openvpn/test_pkt.c 
b/tests/unit_tests/openvpn/test_pkt.c
index a732c2b..9ce9c50 100644
--- a/tests/unit_tests/openvpn/test_pkt.c
+++ b/tests/unit_tests/openvpn/test_pkt.c
@@ -37,6 +37,7 @@
 #include "crypto.h"
 #include "options.h"
 #include "ssl_backend.h"
+#include "control_msg.h"
 #include "ssl_pkt.h"
 #include "tls_crypt.h"

@@ -44,6 +45,8 @@
 #include "reliable.h"
 #include "siphash.h"

+int run_control_msg_tests(void); /* test_control_msg.c */
+
 int
 parse_line(const char *line, char **p, const int n, const char *file, const 
int line_num,
            msglvl_t msglevel, struct gc_arena *gc)
@@ -654,6 +657,46 @@
     free_buf(&tas.workbuf);
 }

+/* A reset reply asking a tls-crypt-v2 client to resend its WKc ends in the
+ * early negotiation flags TLV, the bytes older versions wrote by hand, and the
+ * TLV parses back. */
+static void
+test_generate_reset_packet_resend_wkc(void **ut_state)
+{
+    struct tls_auth_standalone tas = { 0 };
+
+    struct session_id client_id = { { 0, 1, 2, 3, 4, 5, 6, 7 } };
+    struct session_id server_id = { { 8, 9, 0, 9, 8, 7, 6, 2 } };
+
+    tas.tls_wrap.mode = TLS_WRAP_NONE;
+    struct frame frame = { .buf = { .headroom = 200, .payload_size = 1400 }, 0 
};
+    tas.frame = frame;
+    tas.workbuf = alloc_buf(1600);
+
+    uint8_t header = 0 | (P_CONTROL_HARD_RESET_SERVER_V2 << P_OPCODE_SHIFT);
+
+    struct buffer plain =
+        tls_reset_standalone(&tas.tls_wrap, &tas, &client_id, &server_id, 
header, false);
+    const int plain_len = BLEN(&plain);
+    struct buffer buf =
+        tls_reset_standalone(&tas.tls_wrap, &tas, &client_id, &server_id, 
header, true);
+
+    const uint8_t tlv[] = { 0x00, 0x01, 0x00, 0x02, 0x00, 0x01 };
+    assert_int_equal(BLEN(&buf), plain_len + (int)sizeof(tlv));
+    assert_memory_equal(BPTR(&buf) + plain_len, tlv, sizeof(tlv));
+
+    struct buffer payload = buf;
+    assert_true(buf_advance(&payload, plain_len));
+    struct ctrl_msg_tlv_header hdr;
+    struct buffer value;
+    assert_true(ctrl_msg_tlv_next(&payload, &hdr, &value));
+    assert_int_equal(hdr.type, TLV_TYPE_EARLY_NEG_FLAGS);
+    assert_int_equal(buf_read_u16(&value), EARLY_NEG_FLAG_RESEND_WKC);
+    assert_int_equal(BLEN(&payload), 0);
+
+    free_buf(&tas.workbuf);
+}
+
 static void
 test_generate_reset_packet_tls_auth(void **ut_state)
 {
@@ -744,9 +787,12 @@
         cmocka_unit_test(test_verify_hmac_tls_auth),
         cmocka_unit_test(test_verify_hmac_none_out_of_range_ack),
         cmocka_unit_test(test_generate_reset_packet_plain),
+        cmocka_unit_test(test_generate_reset_packet_resend_wkc),
         cmocka_unit_test(test_generate_reset_packet_tls_auth),
         cmocka_unit_test(test_extract_control_message)
     };

-    return cmocka_run_group_tests_name("pkt tests", tests, NULL, NULL);
+    int failed = cmocka_run_group_tests_name("pkt tests", tests, NULL, NULL);
+    failed += run_control_msg_tests();
+    return failed ? 1 : 0;
 }

--
To view, visit http://gerrit.openvpn.net/c/openvpn/+/1965?usp=email
To unsubscribe, or for help writing mail filters, visit 
http://gerrit.openvpn.net/settings?usp=email

Gerrit-MessageType: newchange
Gerrit-Project: openvpn
Gerrit-Branch: master
Gerrit-Change-Id: I89c3133e009dc75dc2c10c607b3f0a24c04e8146
Gerrit-Change-Number: 1965
Gerrit-PatchSet: 1
Gerrit-Owner: stipa <[email protected]>
Gerrit-Reviewer: plaisthos <[email protected]>
Gerrit-CC: openvpn-devel <[email protected]>
Gerrit-Attention: plaisthos <[email protected]>
_______________________________________________
Openvpn-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/openvpn-devel

Reply via email to