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(©, &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