From 680ba43355f6d9e4dcdf6c6eb9ace09946dba8f3 Mon Sep 17 00:00:00 2001 From: Matthias Andree Date: Fri, 27 Jan 2023 21:32:08 +0100 Subject: [PATCH 001/229] make dist: Ship ovpn_dco_freebsd.h, too This file was missing from src/openvpn/Makefile.am. Acked-by: Gert Doering Message-Id: <20230127203208.305638-1-matthias.andree@gmx.de> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26085.html Signed-off-by: Gert Doering (cherry picked from commit ffcf20ca7070027ccb16c3697b2a0e263cbc78a4) --- src/openvpn/Makefile.am | 1 + 1 file changed, 1 insertion(+) diff --git a/src/openvpn/Makefile.am b/src/openvpn/Makefile.am index 3ed73fa4c61..a8e44528c30 100644 --- a/src/openvpn/Makefile.am +++ b/src/openvpn/Makefile.am @@ -78,6 +78,7 @@ openvpn_SOURCES = \ mbuf.c mbuf.h \ memdbg.h \ misc.c misc.h \ + ovpn_dco_freebsd.h \ ovpn_dco_linux.h \ ovpn_dco_win.h \ platform.c platform.h \ From 6241b2f8dbe39062a3273499a0259750d2f02cf8 Mon Sep 17 00:00:00 2001 From: Arne Schwabe Date: Mon, 30 Jan 2023 18:29:32 +0100 Subject: [PATCH 002/229] Fix unaligned access in auth-token The undefined behaviour USAN clang checker found this. The optimiser of clang/gcc will optimise the memcpy away in the auth_token case and output excactly the same assembly on amd64/arm64 but it is still better to not rely on undefined behaviour. Signed-off-by: Arne Schwabe Acked-by: Frank Lichtenheld Message-Id: <20230130172936.3444840-1-arne@rfc2549.org> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26103.html Signed-off-by: Gert Doering (cherry picked from commit f6ccff6d7ea806711f9af59c9de52b7cf80d9c81) --- src/openvpn/auth_token.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/openvpn/auth_token.c b/src/openvpn/auth_token.c index 7b963a9c5f5..e4486eb08d7 100644 --- a/src/openvpn/auth_token.c +++ b/src/openvpn/auth_token.c @@ -324,8 +324,14 @@ verify_auth_token(struct user_pass *up, struct tls_multi *multi, const uint8_t *tstamp_initial = sessid + AUTH_TOKEN_SESSION_ID_LEN; const uint8_t *tstamp = tstamp_initial + sizeof(int64_t); - uint64_t timestamp = ntohll(*((uint64_t *) (tstamp))); - uint64_t timestamp_initial = ntohll(*((uint64_t *) (tstamp_initial))); + /* tstamp, tstamp_initial might not be aligned to an uint64, use memcpy + * to avoid unaligned access */ + uint64_t timestamp = 0, timestamp_initial = 0; + memcpy(×tamp, tstamp, sizeof(uint64_t)); + timestamp = ntohll(timestamp); + + memcpy(×tamp_initial, tstamp_initial, sizeof(uint64_t)); + timestamp_initial = ntohll(timestamp_initial); hmac_ctx_t *ctx = multi->opt.auth_token_key.hmac; if (check_hmac_token(ctx, b64decoded, up->username)) From 3973845ea5bc3fdc15a158917d819b5e9bd92635 Mon Sep 17 00:00:00 2001 From: Arne Schwabe Date: Mon, 30 Jan 2023 18:29:34 +0100 Subject: [PATCH 003/229] Update LibreSSL to 3.7.0 in Github actions The version 3.5.3 triggers undefined behaviour with the usan sanatizer. Updating LibreSSSL to 3.7.0 does unfortunately does not fix the issue but at least we are now using a current version. Signed-off-by: Arne Schwabe Acked-by: Frank Lichtenheld Message-Id: <20230130172936.3444840-3-arne@rfc2549.org> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26105.html Signed-off-by: Gert Doering (cherry picked from commit dc8f1f3963af8bce5e03c333dce9a0b252f6e1fd) --- .github/workflows/build.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 2db90bcdecd..6adb69563d0 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -409,7 +409,7 @@ jobs: with: path: libressl repository: libressl-portable/portable - ref: v3.5.3 + ref: v3.7.0 - name: "libressl: autogen.sh" run: ./autogen.sh working-directory: libressl @@ -417,7 +417,7 @@ jobs: run: autoreconf -fvi working-directory: libressl - name: "libressl: configure" - run: ./configure --enable-openvpn + run: ./configure working-directory: libressl - name: "libressl: make all" run: make -j3 From 0deb1afdba185de464d0c542f60de71712a34b80 Mon Sep 17 00:00:00 2001 From: Arne Schwabe Date: Mon, 30 Jan 2023 18:29:35 +0100 Subject: [PATCH 004/229] Add printing USAN stack trace on github actions This allows identifying the source of undefined behaviour more easily from the github action logs. Signed-off-by: Arne Schwabe Acked-by: Frank Lichtenheld Message-Id: <20230130172936.3444840-4-arne@rfc2549.org> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26102.html Signed-off-by: Gert Doering (cherry picked from commit 98f295004391194d4770a450fda79a30dbaf7d60) --- .github/workflows/build.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 6adb69563d0..132624547cd 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -242,6 +242,9 @@ jobs: name: "clang-asan - ${{matrix.os}} - ${{matrix.ssllib}}" + env: + UBSAN_OPTIONS: print_stacktrace=1 + runs-on: ${{matrix.os}} steps: - name: Install dependencies @@ -291,6 +294,7 @@ jobs: LDFLAGS: ${{ matrix.ldflags }} OPENSSL_CFLAGS: "-I/usr/local/opt/${{matrix.libdir}}/include" OPENSSL_LIBS: "-L/usr/local/opt/${{matrix.libdir}}/lib -lcrypto -lssl" + UBSAN_OPTIONS: print_stacktrace=1 steps: - name: Install dependencies run: brew install openssl@1.1 openssl@3 lzo lz4 man2html cmocka libtool automake autoconf libressl @@ -400,6 +404,7 @@ jobs: CFLAGS: ${{ matrix.cflags }} LDFLAGS: ${{ matrix.ldflags }} CC: ${{matrix.cc}} + UBSAN_OPTIONS: print_stacktrace=1 steps: - name: Install dependencies From c8e94242e31cf94a12f6e897191548be5c4893fe Mon Sep 17 00:00:00 2001 From: Frank Lichtenheld Date: Wed, 1 Feb 2023 14:52:21 +0100 Subject: [PATCH 005/229] Changes.rst: document removal of --keysize When reviweing OpenVPN/openvpn#231 I noticed this was missing from Changes.rst. Signed-off-by: Frank Lichtenheld Acked-by: Gert Doering Message-Id: <20230201135221.36135-1-frank@lichtenheld.com> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26121.html Signed-off-by: Gert Doering (cherry picked from commit b2e49465e6b837d97ecb3a4edbc06aba00584381) --- Changes.rst | 5 +++++ src/openvpn/options.c | 1 - 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Changes.rst b/Changes.rst index 08822f1ded9..ba33a80b0c8 100644 --- a/Changes.rst +++ b/Changes.rst @@ -331,6 +331,11 @@ TLS 1.0 and 1.1 are deprecated a PRNG is better left to a crypto library. So we use the PRNG mbed TLS or OpenSSL now. +``--keysize`` has been removed + The ``--keysize`` option was only useful to change the key length when using the + BF, CAST6 or RC2 ciphers. For all other ciphers the key size is fixed with the + chosen cipher. As OpenVPN v2.6 no longer supports any of these variable length + ciphers, this option was removed as well to avoid confusion. Compression no longer enabled by default Unless an explicit compression option is specified in the configuration, diff --git a/src/openvpn/options.c b/src/openvpn/options.c index f24af3d7ce7..6ae3faf89f6 100644 --- a/src/openvpn/options.c +++ b/src/openvpn/options.c @@ -4176,7 +4176,6 @@ options_postprocess_pull(struct options *o, struct env_set *es) * * --cipher * --auth - * --keysize * --secret * --no-replay * From a85257e78f0d9f922941ad981bc4272d0aaf5594 Mon Sep 17 00:00:00 2001 From: Selva Nair Date: Wed, 1 Feb 2023 12:07:35 -0500 Subject: [PATCH 006/229] block-dns using iservice: fix a potential double free - An item added to undo-list was not removed on error, causing attempt to free again in Undo(). Also fix a memory leak possibility in the same context. Github: fixes OpenVPN/openvpn#232 v2: Split add and delete functions and reuse the delete function for cleanup. Signed-off-by: Selva Nair Acked-by: Lev Stipakov Message-Id: <20230201170735.2266851-1-selva.nair@gmail.com> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26130.html Signed-off-by: Gert Doering (cherry picked from commit b761cb9bc942b6077f0b6e2b85a72e33fc618a0f) --- src/openvpnserv/interactive.c | 132 ++++++++++++++++++++-------------- 1 file changed, 78 insertions(+), 54 deletions(-) diff --git a/src/openvpnserv/interactive.c b/src/openvpnserv/interactive.c index 03361d6678b..a3d43752168 100644 --- a/src/openvpnserv/interactive.c +++ b/src/openvpnserv/interactive.c @@ -777,87 +777,111 @@ CmpAny(LPVOID item, LPVOID any) } static DWORD -HandleBlockDNSMessage(const block_dns_message_t *msg, undo_lists_t *lists) +DeleteBlockDNS(const block_dns_message_t *msg, undo_lists_t *lists) { DWORD err = 0; - block_dns_data_t *interface_data; + block_dns_data_t *interface_data = RemoveListItem(&(*lists)[block_dns], CmpAny, NULL); + + if (interface_data) + { + err = delete_block_dns_filters(interface_data->engine); + if (interface_data->metric_v4 >= 0) + { + set_interface_metric(msg->iface.index, AF_INET, + interface_data->metric_v4); + } + if (interface_data->metric_v6 >= 0) + { + set_interface_metric(msg->iface.index, AF_INET6, + interface_data->metric_v6); + } + free(interface_data); + } + else + { + MsgToEventLog(M_ERR, TEXT("No previous block DNS filters to delete")); + } + + return err; +} + +static DWORD +AddBlockDNS(const block_dns_message_t *msg, undo_lists_t *lists) +{ + DWORD err = 0; + block_dns_data_t *interface_data = NULL; HANDLE engine = NULL; LPCWSTR exe_path; exe_path = settings.exe_path; - if (msg->header.type == msg_add_block_dns) + err = add_block_dns_filters(&engine, msg->iface.index, exe_path, BlockDNSErrHandler); + if (!err) { - err = add_block_dns_filters(&engine, msg->iface.index, exe_path, BlockDNSErrHandler); + interface_data = malloc(sizeof(block_dns_data_t)); + if (!interface_data) + { + err = ERROR_OUTOFMEMORY; + goto out; + } + interface_data->engine = engine; + interface_data->index = msg->iface.index; + int is_auto = 0; + interface_data->metric_v4 = get_interface_metric(msg->iface.index, + AF_INET, &is_auto); + if (is_auto) + { + interface_data->metric_v4 = 0; + } + interface_data->metric_v6 = get_interface_metric(msg->iface.index, + AF_INET6, &is_auto); + if (is_auto) + { + interface_data->metric_v6 = 0; + } + + err = AddListItem(&(*lists)[block_dns], interface_data); if (!err) { - interface_data = malloc(sizeof(block_dns_data_t)); - if (!interface_data) - { - return ERROR_OUTOFMEMORY; - } - interface_data->engine = engine; - interface_data->index = msg->iface.index; - int is_auto = 0; - interface_data->metric_v4 = get_interface_metric(msg->iface.index, - AF_INET, &is_auto); - if (is_auto) - { - interface_data->metric_v4 = 0; - } - interface_data->metric_v6 = get_interface_metric(msg->iface.index, - AF_INET6, &is_auto); - if (is_auto) - { - interface_data->metric_v6 = 0; - } - err = AddListItem(&(*lists)[block_dns], interface_data); + err = set_interface_metric(msg->iface.index, AF_INET, + BLOCK_DNS_IFACE_METRIC); if (!err) { - err = set_interface_metric(msg->iface.index, AF_INET, + err = set_interface_metric(msg->iface.index, AF_INET6, BLOCK_DNS_IFACE_METRIC); - if (!err) - { - set_interface_metric(msg->iface.index, AF_INET6, - BLOCK_DNS_IFACE_METRIC); - } - } - } - } - else - { - interface_data = RemoveListItem(&(*lists)[block_dns], CmpAny, NULL); - if (interface_data) - { - engine = interface_data->engine; - err = delete_block_dns_filters(engine); - engine = NULL; - if (interface_data->metric_v4 >= 0) - { - set_interface_metric(msg->iface.index, AF_INET, - interface_data->metric_v4); } - if (interface_data->metric_v6 >= 0) + if (err) { - set_interface_metric(msg->iface.index, AF_INET6, - interface_data->metric_v6); + /* delete the filters, remove undo item and free interface data */ + DeleteBlockDNS(msg, lists); + engine = NULL; } - free(interface_data); - } - else - { - MsgToEventLog(M_ERR, TEXT("No previous block DNS filters to delete")); } } +out: if (err && engine) { delete_block_dns_filters(engine); + free(interface_data); } return err; } +static DWORD +HandleBlockDNSMessage(const block_dns_message_t *msg, undo_lists_t *lists) +{ + if (msg->header.type == msg_add_block_dns) + { + return AddBlockDNS(msg, lists); + } + else + { + return DeleteBlockDNS(msg, lists); + } +} + /* * Execute a command and return its exit code. If timeout > 0, terminate * the process if still running after timeout milliseconds. In that case From b9d35055eae676790f3bc214c00a01f756b0cd55 Mon Sep 17 00:00:00 2001 From: Frank Lichtenheld Date: Fri, 3 Feb 2023 20:14:36 +0100 Subject: [PATCH 007/229] Windows: fix unused function setenv_foreign_option Signed-off-by: Frank Lichtenheld Acked-by: Lev Stipakov Message-Id: <20230203191440.136050-2-frank@lichtenheld.com> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26145.html Signed-off-by: Gert Doering (cherry picked from commit 8aeec3aa36873489d3e708ae694c57e59b4302ef) --- src/openvpn/options.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/openvpn/options.c b/src/openvpn/options.c index 6ae3faf89f6..ab1b01cf73c 100644 --- a/src/openvpn/options.c +++ b/src/openvpn/options.c @@ -1041,6 +1041,7 @@ setenv_settings(struct env_set *es, const struct options *o) } } +#ifndef _WIN32 static void setenv_foreign_option(struct options *o, const char *argv[], int len, struct env_set *es) { @@ -1078,6 +1079,7 @@ setenv_foreign_option(struct options *o, const char *argv[], int len, struct env gc_free(&gc); } } +#endif /* ifndef _WIN32 */ static in_addr_t get_ip_addr(const char *ip_string, int msglevel, bool *error) From ab46bdd1ef9951b684782de00f37fec4092fd0f1 Mon Sep 17 00:00:00 2001 From: Frank Lichtenheld Date: Fri, 3 Feb 2023 20:14:37 +0100 Subject: [PATCH 008/229] Windows: fix unused variables in delete_route_ipv6 At this point it might be easier to create a dedicated function for Windows... Signed-off-by: Frank Lichtenheld Acked-by: Lev Stipakov Message-Id: <20230203191440.136050-3-frank@lichtenheld.com> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26140.html Signed-off-by: Gert Doering (cherry picked from commit 48495ce3cd8d53663c56d265ef6a82234878e205) --- src/openvpn/route.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/openvpn/route.c b/src/openvpn/route.c index 82519c94ba8..3798bc6579e 100644 --- a/src/openvpn/route.c +++ b/src/openvpn/route.c @@ -2333,12 +2333,6 @@ delete_route_ipv6(const struct route_ipv6 *r6, const struct tuntap *tt, openvpn_net_ctx_t *ctx) { const char *network; -#if !defined(TARGET_LINUX) - const char *gateway; -#else - int metric; -#endif - bool gateway_needed = false; if ((r6->flags & (RT_DEFINED|RT_ADDED)) != (RT_DEFINED|RT_ADDED)) { @@ -2346,19 +2340,34 @@ delete_route_ipv6(const struct route_ipv6 *r6, const struct tuntap *tt, } #ifndef _WIN32 +#if !defined(TARGET_LINUX) + const char *gateway; +#else + int metric; +#endif + bool gateway_needed = false; const char *device = tt->actual_name; if (r6->iface != NULL) /* vpn server special route */ { device = r6->iface; gateway_needed = true; } + + /* if we used a gateway on "add route", we also need to specify it on + * delete, otherwise some OSes will refuse to delete the route + */ + if (tt->type == DEV_TYPE_TAP + && !( (r6->flags & RT_METRIC_DEFINED) && r6->metric == 0 ) ) + { + gateway_needed = true; + } #endif struct gc_arena gc = gc_new(); struct argv argv = argv_new(); network = print_in6_addr( r6->network, 0, &gc); -#if !defined(TARGET_LINUX) +#if !defined(TARGET_LINUX) && !defined(_WIN32) gateway = print_in6_addr( r6->gateway, 0, &gc); #endif @@ -2382,15 +2391,6 @@ delete_route_ipv6(const struct route_ipv6 *r6, const struct tuntap *tt, msg( M_INFO, "delete_route_ipv6(%s/%d)", network, r6->netbits ); - /* if we used a gateway on "add route", we also need to specify it on - * delete, otherwise some OSes will refuse to delete the route - */ - if (tt->type == DEV_TYPE_TAP - && !( (r6->flags & RT_METRIC_DEFINED) && r6->metric == 0 ) ) - { - gateway_needed = true; - } - #if defined(TARGET_LINUX) metric = -1; if ((r6->flags & RT_METRIC_DEFINED) && (r6->metric > 0)) From 37e23c9816754ceeb67d5365b639f0b37f804591 Mon Sep 17 00:00:00 2001 From: Frank Lichtenheld Date: Tue, 7 Feb 2023 14:43:33 +0100 Subject: [PATCH 009/229] Windows: fix wrong printf format in x_check_status Relevant defines/typedefs: typedef UINT_PTR SOCKET; if defined(_WIN64) typedef unsigned __int64 UINT_PTR; else typedef unsigned int UINT_PTR; endif ifdef _WIN64 define PRIuPTR PRIu64 else define PRIuPTR PRIu32 endif Remove duplicated include of inttypes.h Signed-off-by: Frank Lichtenheld Acked-by: Lev Stipakov Message-Id: <20230207134333.52221-1-frank@lichtenheld.com> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26166.html Signed-off-by: Gert Doering (cherry picked from commit a95705be85cb1d3a5868efaeb960ec5d625d2f11) --- src/openvpn/error.c | 4 ++-- src/openvpn/syshead.h | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/openvpn/error.c b/src/openvpn/error.c index 89a08cecd01..a2c9aa4c95a 100644 --- a/src/openvpn/error.c +++ b/src/openvpn/error.c @@ -695,14 +695,14 @@ x_check_status(int status, { if (extended_msg) { - msg(x_cs_info_level, "%s %s [%s]: %s (fd=%d,code=%d)", description, + msg(x_cs_info_level, "%s %s [%s]: %s (fd=" SOCKET_PRINTF ",code=%d)", description, sock ? proto2ascii(sock->info.proto, sock->info.af, true) : "", extended_msg, openvpn_strerror(my_errno, crt_error, &gc), sock ? sock->sd : -1, my_errno); } else { - msg(x_cs_info_level, "%s %s: %s (fd=%d,code=%d)", description, + msg(x_cs_info_level, "%s %s: %s (fd=" SOCKET_PRINTF ",code=%d)", description, sock ? proto2ascii(sock->info.proto, sock->info.af, true) : "", openvpn_strerror(my_errno, crt_error, &gc), sock ? sock->sd : -1, my_errno); diff --git a/src/openvpn/syshead.h b/src/openvpn/syshead.h index fe91bc11366..12ccf2f4dff 100644 --- a/src/openvpn/syshead.h +++ b/src/openvpn/syshead.h @@ -48,7 +48,6 @@ #ifdef _MSC_VER /* Visual Studio */ #define __func__ __FUNCTION__ #define __attribute__(x) -#include #endif #if defined(__APPLE__) @@ -442,9 +441,11 @@ typedef unsigned short sa_family_t; */ #ifdef _WIN32 #define SOCKET_UNDEFINED (INVALID_SOCKET) +#define SOCKET_PRINTF "%" PRIuPTR typedef SOCKET socket_descriptor_t; #else #define SOCKET_UNDEFINED (-1) +#define SOCKET_PRINTF "%d" typedef int socket_descriptor_t; #endif From 4718af50bb7b5a913ae4befb2d3a8dd338b3eb74 Mon Sep 17 00:00:00 2001 From: Frank Lichtenheld Date: Fri, 3 Feb 2023 20:14:39 +0100 Subject: [PATCH 010/229] Windows: fix unused variable in win32_get_arch Signed-off-by: Frank Lichtenheld Acked-by: Lev Stipakov Message-Id: <20230203191440.136050-5-frank@lichtenheld.com> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26141.html Signed-off-by: Gert Doering (cherry picked from commit 2dc2d16559b41d56f662d7cd69ea547e804e27b8) --- src/openvpn/win32.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/openvpn/win32.c b/src/openvpn/win32.c index 44176936ce6..ad3d9bf6dba 100644 --- a/src/openvpn/win32.c +++ b/src/openvpn/win32.c @@ -1358,7 +1358,6 @@ win32_get_arch(arch_t *process_arch, arch_t *host_arch) USHORT process_machine = 0; USHORT native_machine = 0; - BOOL is_wow64 = FALSE; #ifdef _ARM64_ *process_arch = ARCH_ARM64; @@ -1380,8 +1379,8 @@ win32_get_arch(arch_t *process_arch, arch_t *host_arch) if (is_wow64_process2) { /* check if we're running on arm64 or amd64 machine */ - is_wow64 = is_wow64_process2(GetCurrentProcess(), - &process_machine, &native_machine); + BOOL is_wow64 = is_wow64_process2(GetCurrentProcess(), + &process_machine, &native_machine); if (is_wow64) { switch (native_machine) @@ -1403,7 +1402,7 @@ win32_get_arch(arch_t *process_arch, arch_t *host_arch) else { BOOL w64 = FALSE; - is_wow64 = IsWow64Process(GetCurrentProcess(), &w64) && w64; + BOOL is_wow64 = IsWow64Process(GetCurrentProcess(), &w64) && w64; if (is_wow64) { /* we are unable to differentiate between arm64 and amd64 From dabfebc4731b7cccda36a09eaaa11f912b8a9fde Mon Sep 17 00:00:00 2001 From: Arne Schwabe Date: Thu, 9 Feb 2023 17:31:15 +0100 Subject: [PATCH 011/229] Fix LibreSSL not building in Github Actions During the build of LibreSSL portable it pulls in a branch from OpenBSD upstream. Unfortunately they use master there instead of a fixed branch. So we work around this issue. Signed-off-by: Arne Schwabe Acked-by: Gert Doering Message-Id: <20230209163115.465548-1-arne@rfc2549.org> URL: https://www.mail-archive.com/search?l=mid&q=20230209163115.465548-1-arne@rfc2549.org Signed-off-by: Gert Doering (cherry picked from commit 589cca156357e05c6c3f07517184157585c8e9fc) --- .github/workflows/build.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 132624547cd..5888e91e59f 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -415,6 +415,14 @@ jobs: path: libressl repository: libressl-portable/portable ref: v3.7.0 + # LibreSSL has master in the OPENBSD_BRANCH by default even in the tagged releases + # Manually use the OpenBSD branch that is more appropiate and remove git pull that + # breaks when checking out a tag instead of branch + - name: "libressl: fix build" + run: | + echo libressl-v3.7.0 > OPENBSD_BRANCH + sed -i -e 's/ git pull --rebase//' update.sh + working-directory: libressl - name: "libressl: autogen.sh" run: ./autogen.sh working-directory: libressl From 442fde78a1f1f512762a4b2c4db958a3d56410d1 Mon Sep 17 00:00:00 2001 From: Lev Stipakov Date: Tue, 7 Feb 2023 16:54:16 +0200 Subject: [PATCH 012/229] Allow certain DHCP options to be used without DHCP server Followin DHCP options: DOMAIN, ADAPTER_DOMAIN_SUFFIX, DNS, WINS don't require DHCP server in order to be used. This change allows those options to be used with dco and wintun drivers. If an option specified which requires DHCP server and tap-windows6 driver is not used, print a clear error message instead of obscure reference to --ip-win32. Reported-by: Marek Zarychta Signed-off-by: Lev Stipakov Acked-by: Antonio Quartulli Message-Id: <20230207145416.1415-1-lstipakov@gmail.com> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26169.html Signed-off-by: Gert Doering (cherry picked from commit 469158f93ea52a6c2f821890ef599299183aa020) --- src/openvpn/options.c | 39 +++++++++++++++++++++++---------------- src/openvpn/tun.h | 6 +++++- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/src/openvpn/options.c b/src/openvpn/options.c index ab1b01cf73c..ce756128851 100644 --- a/src/openvpn/options.c +++ b/src/openvpn/options.c @@ -1292,7 +1292,7 @@ show_tuntap_options(const struct tuntap_options *o) SHOW_INT(dhcp_masq_offset); SHOW_INT(dhcp_lease_time); SHOW_INT(tap_sleep); - SHOW_BOOL(dhcp_options); + SHOW_UNSIGNED(dhcp_options); SHOW_BOOL(dhcp_renew); SHOW_BOOL(dhcp_pre_release); SHOW_STR(domain); @@ -2480,12 +2480,20 @@ options_postprocess_verify_ce(const struct options *options, msg(M_USAGE, "On Windows, --ip-win32 doesn't make sense unless --ifconfig is also used"); } - if (options->tuntap_options.dhcp_options - && options->windows_driver != WINDOWS_DRIVER_WINTUN - && options->tuntap_options.ip_win32_type != IPW32_SET_DHCP_MASQ - && options->tuntap_options.ip_win32_type != IPW32_SET_ADAPTIVE) + if (options->tuntap_options.dhcp_options & DHCP_OPTIONS_DHCP_REQUIRED) { - msg(M_USAGE, "--dhcp-option requires --ip-win32 dynamic or adaptive"); + const char *prefix = "Some dhcp-options require DHCP server"; + if (options->windows_driver != WINDOWS_DRIVER_TAP_WINDOWS6) + { + msg(M_USAGE, "%s, which is not supported by selected %s driver", + prefix, print_windows_driver(options->windows_driver)); + } + else if (options->tuntap_options.ip_win32_type != IPW32_SET_DHCP_MASQ + && options->tuntap_options.ip_win32_type != IPW32_SET_ADAPTIVE) + { + msg(M_USAGE, "%s, which requires --ip-win32 dynamic or adaptive", + prefix); + } } if (options->windows_driver == WINDOWS_DRIVER_WINTUN && dev != DEV_TYPE_TUN) @@ -8085,16 +8093,17 @@ add_option(struct options *options, { struct tuntap_options *o = &options->tuntap_options; VERIFY_PERMISSION(OPT_P_DHCPDNS); - bool ipv6dns = false; if ((streq(p[1], "DOMAIN") || streq(p[1], "ADAPTER_DOMAIN_SUFFIX")) && p[2] && !p[3]) { o->domain = p[2]; + o->dhcp_options |= DHCP_OPTIONS_DHCP_OPTIONAL; } else if (streq(p[1], "NBS") && p[2] && !p[3]) { o->netbios_scope = p[2]; + o->dhcp_options |= DHCP_OPTIONS_DHCP_REQUIRED; } else if (streq(p[1], "NBT") && p[2] && !p[3]) { @@ -8106,31 +8115,35 @@ add_option(struct options *options, goto err; } o->netbios_node_type = t; + o->dhcp_options |= DHCP_OPTIONS_DHCP_REQUIRED; } else if ((streq(p[1], "DNS") || streq(p[1], "DNS6")) && p[2] && !p[3] && (!strstr(p[2], ":") || ipv6_addr_safe(p[2]))) { if (strstr(p[2], ":")) { - ipv6dns = true; dhcp_option_dns6_parse(p[2], o->dns6, &o->dns6_len, msglevel); } else { dhcp_option_address_parse("DNS", p[2], o->dns, &o->dns_len, msglevel); + o->dhcp_options |= DHCP_OPTIONS_DHCP_OPTIONAL; } } else if (streq(p[1], "WINS") && p[2] && !p[3]) { dhcp_option_address_parse("WINS", p[2], o->wins, &o->wins_len, msglevel); + o->dhcp_options |= DHCP_OPTIONS_DHCP_OPTIONAL; } else if (streq(p[1], "NTP") && p[2] && !p[3]) { dhcp_option_address_parse("NTP", p[2], o->ntp, &o->ntp_len, msglevel); + o->dhcp_options |= DHCP_OPTIONS_DHCP_REQUIRED; } else if (streq(p[1], "NBDD") && p[2] && !p[3]) { dhcp_option_address_parse("NBDD", p[2], o->nbdd, &o->nbdd_len, msglevel); + o->dhcp_options |= DHCP_OPTIONS_DHCP_REQUIRED; } else if (streq(p[1], "DOMAIN-SEARCH") && p[2] && !p[3]) { @@ -8143,10 +8156,12 @@ add_option(struct options *options, msg(msglevel, "--dhcp-option %s: maximum of %d search entries can be specified", p[1], N_SEARCH_LIST_LEN); } + o->dhcp_options |= DHCP_OPTIONS_DHCP_REQUIRED; } else if (streq(p[1], "DISABLE-NBT") && !p[2]) { o->disable_nbt = 1; + o->dhcp_options |= DHCP_OPTIONS_DHCP_REQUIRED; } #if defined(TARGET_ANDROID) else if (streq(p[1], "PROXY_HTTP") && p[3] && !p[4]) @@ -8160,14 +8175,6 @@ add_option(struct options *options, msg(msglevel, "--dhcp-option: unknown option type '%s' or missing or unknown parameter", p[1]); goto err; } - - /* flag that we have options to give to the TAP driver's DHCPv4 server - * - skipped for "DNS6", as that's not a DHCPv4 option - */ - if (!ipv6dns) - { - o->dhcp_options = true; - } } #endif /* if defined(_WIN32) || defined(TARGET_ANDROID) */ #ifdef _WIN32 diff --git a/src/openvpn/tun.h b/src/openvpn/tun.h index 3b0a0d249ab..e19e1a2e2e9 100644 --- a/src/openvpn/tun.h +++ b/src/openvpn/tun.h @@ -62,6 +62,10 @@ enum windows_driver_type { #define IPW32_SET_ADAPTIVE_DELAY_WINDOW 300 #define IPW32_SET_ADAPTIVE_TRY_NETSH 20 +/* bit flags for DHCP options */ +#define DHCP_OPTIONS_DHCP_OPTIONAL (1<<0) +#define DHCP_OPTIONS_DHCP_REQUIRED (1<<1) + struct tuntap_options { /* --ip-win32 options */ bool ip_win32_defined; @@ -90,7 +94,7 @@ struct tuntap_options { /* --dhcp-option options */ - bool dhcp_options; + int dhcp_options; const char *domain; /* DOMAIN (15) */ From 7f72abcf8a56bb35a510a3409e03a4e2aaba50da Mon Sep 17 00:00:00 2001 From: Selva Nair Date: Fri, 3 Feb 2023 19:45:10 -0500 Subject: [PATCH 013/229] Conditionally add subdir-objects option to automake - Eliminates repeated warnings such as warning: source file '$(openvpn_srcdir)/env_set.c' is in a subdirectory, but option 'subdir-objects' is disabled - Enabled only for automake >= 1.16 as older versions have a buggy implementation of this option Main side effect of this option is that object files like openvpnserv-blockdns.o are now created in src/openvpn where block-dns.c resides instead of in src/openvpnserv. Same for object files for sources from $(openvpn_srcdir) compiled into test executables. See also past discussion on this topic: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg00013.html Signed-off-by: Selva Nair Acked-by: Arne Schwabe Message-Id: <20230204004512.250271-1-selva.nair@gmail.com> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26147.html Signed-off-by: Gert Doering (cherry picked from commit 8b915c48252da81b96041de66847272b0902c755) --- configure.ac | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 9150008707c..95d795c3343 100644 --- a/configure.ac +++ b/configure.ac @@ -54,9 +54,22 @@ m4_define([serial_tests], [ awk '{split ($NF,a,"."); if (a[1] == 1 && a[2] >= 12) { print "serial-tests" }}' ]) ]) + +dnl Automake 1.14+ warns if sources are in sub-directories but subdir-objects +dnl options is not enabled. However, automake before 1.15a has a bug that causes +dnl variable expansion to fail in foo_SOURCES when this option is used. +dnl As most of our build systems are now likely to use automake 1.16+ add a +dnl work around to conditionally add subdir-objects option. +m4_define([subdir_objects], [ + m4_esyscmd([automake --version | + head -1 | + awk '{split ($NF,a,"."); if (a[1] == 1 && a[2] >= 16) { print "subdir-objects" }}' + ]) +]) + # This foreign option prevents autoreconf from overriding our COPYING and # INSTALL targets: -AM_INIT_AUTOMAKE(foreign serial_tests 1.9) dnl NB: Do not [quote] this parameter. +AM_INIT_AUTOMAKE(foreign serial_tests subdir_objects 1.9) dnl NB: Do not [quote] this parameter. AC_CANONICAL_HOST AC_USE_SYSTEM_EXTENSIONS From eca101ac4426442c1ad17e6a706d84c7960a8262 Mon Sep 17 00:00:00 2001 From: Gert Doering Date: Wed, 1 Feb 2023 14:15:18 +0000 Subject: [PATCH 014/229] Get rid of unused 'bool tuntap_buffer' arguments. overlapped_io_init() has a "bool tuntap_buffer" argument which is only passed onwards to alloc_buf_sock_tun(), which does nothing with it. Remove from both functions. v2: move alloc_buf_sock_tun() to win32.c v3: leave alloc_buf_sock_tun() where it is, and fix non-WIN32 call from socket.c Signed-off-by: Gert Doering Acked-by: Arne Schwabe Message-Id: <20230130161730.110021-1-gert@greenie.muc.de> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26099.h tml Signed-off-by: Gert Doering (cherry picked from commit bdc842d72e92995261bac3579120c94f93e4064a) Acked-by: Arne Schwabe Message-Id: <20230201141518.119157-1-gert@greenie.muc.de> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26122.html Signed-off-by: Gert Doering (cherry picked from commit 092ceadb762a42a50191e38bd7cf0fe9f6528a59) --- src/openvpn/mtu.c | 3 +-- src/openvpn/mtu.h | 3 +-- src/openvpn/socket.c | 8 +++----- src/openvpn/tun.c | 4 ++-- src/openvpn/win32.c | 5 ++--- src/openvpn/win32.h | 3 +-- 6 files changed, 10 insertions(+), 16 deletions(-) diff --git a/src/openvpn/mtu.c b/src/openvpn/mtu.c index 1d9ebe011c0..748a1cf19c2 100644 --- a/src/openvpn/mtu.c +++ b/src/openvpn/mtu.c @@ -42,8 +42,7 @@ /* allocate a buffer for socket or tun layer */ void alloc_buf_sock_tun(struct buffer *buf, - const struct frame *frame, - const bool tuntap_buffer) + const struct frame *frame) { /* allocate buffer for overlapped I/O */ *buf = alloc_buf(BUF_SIZE(frame)); diff --git a/src/openvpn/mtu.h b/src/openvpn/mtu.h index 0ff4f7bfa2c..65236508f4b 100644 --- a/src/openvpn/mtu.h +++ b/src/openvpn/mtu.h @@ -265,8 +265,7 @@ calc_packet_id_size_dc(const struct options *options, * allocate a buffer for socket or tun layer */ void alloc_buf_sock_tun(struct buffer *buf, - const struct frame *frame, - const bool tuntap_buffer); + const struct frame *frame); /* * EXTENDED_SOCKET_ERROR_CAPABILITY functions -- print extra error info diff --git a/src/openvpn/socket.c b/src/openvpn/socket.c index a883ac4a156..42d95339046 100644 --- a/src/openvpn/socket.c +++ b/src/openvpn/socket.c @@ -1623,8 +1623,8 @@ static void socket_frame_init(const struct frame *frame, struct link_socket *sock) { #ifdef _WIN32 - overlapped_io_init(&sock->reads, frame, FALSE, false); - overlapped_io_init(&sock->writes, frame, TRUE, false); + overlapped_io_init(&sock->reads, frame, FALSE); + overlapped_io_init(&sock->writes, frame, TRUE); sock->rw_handle.read = sock->reads.overlapped.hEvent; sock->rw_handle.write = sock->writes.overlapped.hEvent; #endif @@ -1637,9 +1637,7 @@ socket_frame_init(const struct frame *frame, struct link_socket *sock) sock->sockflags, sock->info.proto); #else - alloc_buf_sock_tun(&sock->stream_buf_data, - frame, - false); + alloc_buf_sock_tun(&sock->stream_buf_data, frame); stream_buf_init(&sock->stream_buf, &sock->stream_buf_data, diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c index 01c85f9ff51..20f80798a81 100644 --- a/src/openvpn/tun.c +++ b/src/openvpn/tun.c @@ -898,8 +898,8 @@ init_tun_post(struct tuntap *tt, return; } - overlapped_io_init(&tt->reads, frame, FALSE, true); - overlapped_io_init(&tt->writes, frame, TRUE, true); + overlapped_io_init(&tt->reads, frame, FALSE); + overlapped_io_init(&tt->writes, frame, TRUE); tt->adapter_index = TUN_ADAPTER_INDEX_INVALID; if (tt->windows_driver == WINDOWS_DRIVER_WINTUN) diff --git a/src/openvpn/win32.c b/src/openvpn/win32.c index ad3d9bf6dba..4c3ea842c91 100644 --- a/src/openvpn/win32.c +++ b/src/openvpn/win32.c @@ -173,8 +173,7 @@ init_security_attributes_allow_all(struct security_attributes *obj) void overlapped_io_init(struct overlapped_io *o, const struct frame *frame, - BOOL event_state, - bool tuntap_buffer) /* if true: tuntap buffer, if false: socket buffer */ + BOOL event_state) { CLEAR(*o); @@ -186,7 +185,7 @@ overlapped_io_init(struct overlapped_io *o, } /* allocate buffer for overlapped I/O */ - alloc_buf_sock_tun(&o->buf_init, frame, tuntap_buffer); + alloc_buf_sock_tun(&o->buf_init, frame); } void diff --git a/src/openvpn/win32.h b/src/openvpn/win32.h index a1a4550319f..72ffb012e3c 100644 --- a/src/openvpn/win32.h +++ b/src/openvpn/win32.h @@ -217,8 +217,7 @@ struct overlapped_io { void overlapped_io_init(struct overlapped_io *o, const struct frame *frame, - BOOL event_state, - bool tuntap_buffer); + BOOL event_state); void overlapped_io_close(struct overlapped_io *o); From 1b06696efb069ff9dfc77bac6cb487de232938cc Mon Sep 17 00:00:00 2001 From: Arne Schwabe Date: Wed, 8 Feb 2023 01:18:18 +0100 Subject: [PATCH 015/229] Add missing stdint.h includes in unit tests files My mingw compiler/headers (mingw-w64 10.0.0 on macOS) seem to be more pendantic than the one that comes with Ubuntu 22.04 (github actions) or any of the other platforms including msvc/normal windows header. Signed-off-by: Arne Schwabe Acked-by: Selva Nair Message-Id: <20230208001819.244694-5-arne@rfc2549.org> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26182.html Signed-off-by: Gert Doering (cherry picked from commit e80720ef9399d7a4e3469cf1004d064643e0f4dd) --- tests/unit_tests/example_test/test.c | 1 + tests/unit_tests/example_test/test2.c | 1 + tests/unit_tests/openvpn/mock_msg.c | 1 + 3 files changed, 3 insertions(+) diff --git a/tests/unit_tests/example_test/test.c b/tests/unit_tests/example_test/test.c index ea31b884d1d..c174025cc4e 100644 --- a/tests/unit_tests/example_test/test.c +++ b/tests/unit_tests/example_test/test.c @@ -4,6 +4,7 @@ #include #include #include +#include #include static int diff --git a/tests/unit_tests/example_test/test2.c b/tests/unit_tests/example_test/test2.c index 5a186d5d769..bb54633c899 100644 --- a/tests/unit_tests/example_test/test2.c +++ b/tests/unit_tests/example_test/test2.c @@ -4,6 +4,7 @@ #include #include #include +#include #include diff --git a/tests/unit_tests/openvpn/mock_msg.c b/tests/unit_tests/openvpn/mock_msg.c index 3ede98c00a9..3fa9a166fb8 100644 --- a/tests/unit_tests/openvpn/mock_msg.c +++ b/tests/unit_tests/openvpn/mock_msg.c @@ -32,6 +32,7 @@ #include #include #include +#include #include From a6d7e8843154bfca8b98a9eb11a11b320a03db36 Mon Sep 17 00:00:00 2001 From: Selva Nair Date: Tue, 7 Feb 2023 19:59:25 -0500 Subject: [PATCH 016/229] Build unit tests in mingw Windows build - Minor changes to the build system to include some dependencies for Windows build - test_tls_crypt not built as it will pull in win32.c and its dependencies - If cross-compiling, "make check" will only build the tests but not run any. Copy to Windows and run manually. Executables are in /tests/unit_tests/openvpn/.libs/ and these depend on cmocka.dll in addition to openssl libs that some tests link to. Building with mingw on Windows should run the tests (untested). v2: networking_testdriver was mistakenly enabled to run, while originally it was only set to build. Corrected. v3: exclude check_engine_keys.sh when cross-compiling As suggested by Arne Schwabe Signed-off-by: Selva Nair Acked-by: Arne Schwabe Message-Id: <20230208005925.393200-1-selva.nair@gmail.com> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26188.html Signed-off-by: Gert Doering (cherry picked from commit e3ad1fc4230fb3a08b484e76ce0a31f2479d3c8f) --- configure.ac | 2 ++ tests/Makefile.am | 2 ++ tests/unit_tests/engine-key/Makefile.am | 2 ++ tests/unit_tests/example_test/Makefile.am | 2 ++ tests/unit_tests/openvpn/Makefile.am | 28 +++++++++++++++++++---- 5 files changed, 31 insertions(+), 5 deletions(-) diff --git a/configure.ac b/configure.ac index 95d795c3343..66ba6f38d87 100644 --- a/configure.ac +++ b/configure.ac @@ -364,6 +364,8 @@ case "$host" in ;; esac +AM_CONDITIONAL([CROSS_COMPILING], test "${cross_compiling}" = "yes") + PKG_PROG_PKG_CONFIG AC_PROG_CPP AC_PROG_INSTALL diff --git a/tests/Makefile.am b/tests/Makefile.am index 87dd7e17b6d..a46f2573d29 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -14,10 +14,12 @@ MAINTAINERCLEANFILES = \ SUBDIRS = unit_tests +if !WIN32 test_scripts = t_client.sh t_lpback.sh t_cltsrv.sh if HAVE_SITNL test_scripts += t_net.sh endif +endif TESTS_ENVIRONMENT = top_srcdir="$(top_srcdir)" TESTS = $(test_scripts) diff --git a/tests/unit_tests/engine-key/Makefile.am b/tests/unit_tests/engine-key/Makefile.am index 246222514ae..0c28885766d 100644 --- a/tests/unit_tests/engine-key/Makefile.am +++ b/tests/unit_tests/engine-key/Makefile.am @@ -12,7 +12,9 @@ TESTS_ENVIRONMENT = srcdir="$(abs_srcdir)"; \ top_srcdir="$(top_srcdir)"; \ export srcdir builddir top_builddir top_srcdir; +if !CROSS_COMPILING TESTS = check_engine_keys.sh +endif check_engine_keys.sh: $(conffiles) CLEANFILES = \ diff --git a/tests/unit_tests/example_test/Makefile.am b/tests/unit_tests/example_test/Makefile.am index 04a5ad3564c..24eb0ba18b6 100644 --- a/tests/unit_tests/example_test/Makefile.am +++ b/tests/unit_tests/example_test/Makefile.am @@ -2,7 +2,9 @@ AUTOMAKE_OPTIONS = foreign check_PROGRAMS = example_testdriver example2_testdriver +if !CROSS_COMPILING TESTS = $(check_PROGRAMS) +endif example_testdriver_CFLAGS = @TEST_CFLAGS@ example_testdriver_LDFLAGS = @TEST_LDFLAGS@ diff --git a/tests/unit_tests/openvpn/Makefile.am b/tests/unit_tests/openvpn/Makefile.am index 7720a85db75..8d2386e08bf 100644 --- a/tests/unit_tests/openvpn/Makefile.am +++ b/tests/unit_tests/openvpn/Makefile.am @@ -7,14 +7,22 @@ test_binaries += argv_testdriver buffer_testdriver endif test_binaries += crypto_testdriver packet_id_testdriver auth_token_testdriver ncp_testdriver misc_testdriver \ - pkt_testdriver + pkt_testdriver if HAVE_LD_WRAP_SUPPORT +if !WIN32 test_binaries += tls_crypt_testdriver endif +endif test_binaries += provider_testdriver +if WIN32 +LDADD = -lws2_32 +endif + +if !CROSS_COMPILING TESTS = $(test_binaries) +endif check_PROGRAMS = $(test_binaries) if HAVE_SITNL @@ -31,12 +39,14 @@ argv_testdriver_SOURCES = test_argv.c mock_msg.c mock_msg.h \ mock_get_random.c \ $(openvpn_srcdir)/platform.c \ $(openvpn_srcdir)/buffer.c \ + $(openvpn_srcdir)/win32-util.c \ $(openvpn_srcdir)/argv.c buffer_testdriver_CFLAGS = @TEST_CFLAGS@ -I$(openvpn_srcdir) -I$(compat_srcdir) buffer_testdriver_LDFLAGS = @TEST_LDFLAGS@ -L$(openvpn_srcdir) -Wl,--wrap=parse_line buffer_testdriver_SOURCES = test_buffer.c mock_msg.c mock_msg.h \ mock_get_random.c \ + $(openvpn_srcdir)/win32-util.c \ $(openvpn_srcdir)/platform.c crypto_testdriver_CFLAGS = @TEST_CFLAGS@ \ @@ -51,6 +61,7 @@ crypto_testdriver_SOURCES = test_crypto.c mock_msg.c mock_msg.h \ $(openvpn_srcdir)/packet_id.c \ $(openvpn_srcdir)/platform.c \ $(openvpn_srcdir)/mtu.c \ + $(openvpn_srcdir)/win32-util.c \ $(openvpn_srcdir)/mss.c packet_id_testdriver_CFLAGS = @TEST_CFLAGS@ \ @@ -63,14 +74,14 @@ packet_id_testdriver_SOURCES = test_packet_id.c mock_msg.c mock_msg.h \ $(openvpn_srcdir)/packet_id.c \ $(openvpn_srcdir)/platform.c \ $(openvpn_srcdir)/reliable.c \ + $(openvpn_srcdir)/win32-util.c \ $(openvpn_srcdir)/session_id.c - pkt_testdriver_CFLAGS = @TEST_CFLAGS@ \ -I$(openvpn_includedir) -I$(compat_srcdir) -I$(openvpn_srcdir) pkt_testdriver_LDFLAGS = @TEST_LDFLAGS@ pkt_testdriver_SOURCES = test_pkt.c mock_msg.c mock_msg.h \ -$(openvpn_srcdir)/argv.c \ + $(openvpn_srcdir)/argv.c \ $(openvpn_srcdir)/base64.c \ $(openvpn_srcdir)/buffer.c \ $(openvpn_srcdir)/crypto.c \ @@ -84,9 +95,10 @@ $(openvpn_srcdir)/argv.c \ $(openvpn_srcdir)/run_command.c \ $(openvpn_srcdir)/session_id.c \ $(openvpn_srcdir)/ssl_pkt.c \ + $(openvpn_srcdir)/win32-util.c \ $(openvpn_srcdir)/tls_crypt.c - +if !WIN32 tls_crypt_testdriver_CFLAGS = @TEST_CFLAGS@ \ -I$(openvpn_includedir) -I$(compat_srcdir) -I$(openvpn_srcdir) tls_crypt_testdriver_LDFLAGS = @TEST_LDFLAGS@ \ @@ -106,6 +118,7 @@ tls_crypt_testdriver_SOURCES = test_tls_crypt.c mock_msg.c mock_msg.h \ $(openvpn_srcdir)/packet_id.c \ $(openvpn_srcdir)/platform.c \ $(openvpn_srcdir)/run_command.c +endif if HAVE_SITNL networking_testdriver_CFLAGS = @TEST_CFLAGS@ \ @@ -136,6 +149,7 @@ provider_testdriver_SOURCES = test_provider.c mock_msg.c \ $(openvpn_srcdir)/buffer.c \ $(openvpn_srcdir)/base64.c \ mock_get_random.c \ + $(openvpn_srcdir)/win32-util.c \ $(openvpn_srcdir)/platform.c auth_token_testdriver_CFLAGS = @TEST_CFLAGS@ \ @@ -152,6 +166,7 @@ auth_token_testdriver_SOURCES = test_auth_token.c mock_msg.c \ $(openvpn_srcdir)/otime.c \ $(openvpn_srcdir)/packet_id.c \ $(openvpn_srcdir)/platform.c \ + $(openvpn_srcdir)/win32-util.c \ $(openvpn_srcdir)/base64.c @@ -169,6 +184,8 @@ ncp_testdriver_SOURCES = test_ncp.c mock_msg.c \ $(openvpn_srcdir)/otime.c \ $(openvpn_srcdir)/packet_id.c \ $(openvpn_srcdir)/platform.c \ + $(openvpn_srcdir)/win32-util.c \ + $(compat_srcdir)/compat-strsep.c \ $(openvpn_srcdir)/ssl_util.c misc_testdriver_CFLAGS = @TEST_CFLAGS@ \ @@ -177,8 +194,9 @@ misc_testdriver_CFLAGS = @TEST_CFLAGS@ \ misc_testdriver_LDFLAGS = @TEST_LDFLAGS@ misc_testdriver_SOURCES = test_misc.c mock_msg.c \ - mock_get_random.c \ + mock_get_random.c \ $(openvpn_srcdir)/buffer.c \ $(openvpn_srcdir)/options_util.c \ $(openvpn_srcdir)/ssl_util.c \ + $(openvpn_srcdir)/win32-util.c \ $(openvpn_srcdir)/platform.c From 3c02417c95567dcbc3eafe25c28451fb39005f43 Mon Sep 17 00:00:00 2001 From: Lev Stipakov Date: Tue, 31 Jan 2023 14:54:48 +0200 Subject: [PATCH 017/229] dco-win: use proper calling convention on x86 WinAPI uses __stdcall calling convention on x86. Wrong calling convention causes UB, which in this case breaks dco-win functionality. Signed-off-by: Lev Stipakov Acked-by: Gert Doering Message-Id: <20230131125448.1913-1-lstipakov@gmail.com> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26113.html Signed-off-by: Gert Doering (cherry picked from commit b78360875cfe2f3ffc0d0aa0785ba3070b597da6) --- src/openvpn/dco_win.c | 2 +- src/openvpn/win32.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/openvpn/dco_win.c b/src/openvpn/dco_win.c index 7594024ceb0..0931fb30a8c 100644 --- a/src/openvpn/dco_win.c +++ b/src/openvpn/dco_win.c @@ -110,7 +110,7 @@ dco_connect_wait(HANDLE handle, OVERLAPPED *ov, int timeout, struct signal_info { volatile int *signal_received = &sig_info->signal_received; /* GetOverlappedResultEx is available starting from Windows 8 */ - typedef BOOL (*get_overlapped_result_ex_t) (HANDLE, LPOVERLAPPED, LPDWORD, DWORD, BOOL); + typedef BOOL (WINAPI *get_overlapped_result_ex_t)(HANDLE, LPOVERLAPPED, LPDWORD, DWORD, BOOL); get_overlapped_result_ex_t get_overlapped_result_ex = (get_overlapped_result_ex_t)GetProcAddress(GetModuleHandle("Kernel32.dll"), "GetOverlappedResultEx"); diff --git a/src/openvpn/win32.c b/src/openvpn/win32.c index 4c3ea842c91..1ae3723f108 100644 --- a/src/openvpn/win32.c +++ b/src/openvpn/win32.c @@ -1351,7 +1351,7 @@ win32_get_arch(arch_t *process_arch, arch_t *host_arch) *process_arch = ARCH_UNKNOWN; *host_arch = ARCH_NATIVE; - typedef BOOL (__stdcall *is_wow64_process2_t)(HANDLE, USHORT *, USHORT *); + typedef BOOL (WINAPI *is_wow64_process2_t)(HANDLE, USHORT *, USHORT *); is_wow64_process2_t is_wow64_process2 = (is_wow64_process2_t) GetProcAddress(GetModuleHandle("Kernel32.dll"), "IsWow64Process2"); From e1fac38c29e0d26eb3bd13812529721d9ba0591e Mon Sep 17 00:00:00 2001 From: Arne Schwabe Date: Fri, 10 Feb 2023 15:27:06 +0100 Subject: [PATCH 018/229] Combine extra_tun/frame parameter of frame_calculate_payload_overhead Instead of passing a value and a bool just pass the value and 0 if the caller does not want the value to be added. This also allows the function to be used by a function without a frame struct. Signed-off-by: Arne Schwabe Acked-by: Frank Lichtenheld Message-Id: <20230210142712.572303-3-arne@rfc2549.org> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26223.html Signed-off-by: Gert Doering (cherry picked from commit e759c0ea6fe8679edf4d5208f2f0dc8cee5e948c) --- src/openvpn/mss.c | 2 +- src/openvpn/mtu.c | 14 +++++--------- src/openvpn/mtu.h | 6 +++--- src/openvpn/occ.c | 3 +-- 4 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/openvpn/mss.c b/src/openvpn/mss.c index 429aa1e9318..98d54068852 100644 --- a/src/openvpn/mss.c +++ b/src/openvpn/mss.c @@ -303,7 +303,7 @@ frame_calculate_mssfix(struct frame *frame, struct key_type *kt, /* Calculate the number of bytes that the payload differs from the payload * MTU. This are fragment/compression/ethernet headers */ - payload_overhead = frame_calculate_payload_overhead(frame, options, kt, true); + payload_overhead = frame_calculate_payload_overhead(frame->extra_tun, options, kt); /* We are in a "liberal" position with respect to MSS, * i.e. we assume that MSS can be calculated from MTU diff --git a/src/openvpn/mtu.c b/src/openvpn/mtu.c index 748a1cf19c2..2925b7feb3a 100644 --- a/src/openvpn/mtu.c +++ b/src/openvpn/mtu.c @@ -107,20 +107,16 @@ frame_calculate_protocol_header_size(const struct key_type *kt, size_t -frame_calculate_payload_overhead(const struct frame *frame, +frame_calculate_payload_overhead(size_t extra_tun, const struct options *options, - const struct key_type *kt, - bool extra_tun) + const struct key_type *kt) { size_t overhead = 0; /* This is the overhead of tap device that is not included in the MTU itself * i.e. Ethernet header that we still need to transmit as part of the - * payload */ - if (extra_tun) - { - overhead += frame->extra_tun; - } + * payload, this is set to 0 by caller if not applicable */ + overhead += extra_tun; #if defined(USE_COMP) /* v1 Compression schemes add 1 byte header. V2 only adds a header when it @@ -157,7 +153,7 @@ frame_calculate_payload_size(const struct frame *frame, const struct key_type *kt) { size_t payload_size = options->ce.tun_mtu; - payload_size += frame_calculate_payload_overhead(frame, options, kt, true); + payload_size += frame_calculate_payload_overhead(frame->extra_tun, options, kt); return payload_size; } diff --git a/src/openvpn/mtu.h b/src/openvpn/mtu.h index 65236508f4b..b602b86b5f2 100644 --- a/src/openvpn/mtu.h +++ b/src/openvpn/mtu.h @@ -217,10 +217,10 @@ frame_calculate_payload_size(const struct frame *frame, * * [IP][UDP][OPENVPN PROTOCOL HEADER][ **PAYLOAD incl compression header** ] */ size_t -frame_calculate_payload_overhead(const struct frame *frame, +frame_calculate_payload_overhead(size_t extra_tun, const struct options *options, - const struct key_type *kt, - bool extra_tun); + const struct key_type *kt); + /** * Calculates the size of the OpenVPN protocol header. This includes diff --git a/src/openvpn/occ.c b/src/openvpn/occ.c index 0fa803cdbc5..94b82e0f5c3 100644 --- a/src/openvpn/occ.c +++ b/src/openvpn/occ.c @@ -305,8 +305,7 @@ check_send_occ_msg_dowork(struct context *c) const struct key_type *kt = &c->c1.ks.key_type; /* OCC message have comp/fragment headers but not ethernet headers */ - payload_hdr = frame_calculate_payload_overhead(&c->c2.frame, &c->options, - kt, false); + payload_hdr = frame_calculate_payload_overhead(0, &c->options, kt); /* Since we do not know the payload size we just pass 0 as size here */ proto_hdr = frame_calculate_protocol_header_size(kt, &c->options, false); From 8cbe09dc98824d992fde0ce1d4f218dd46e0bf2f Mon Sep 17 00:00:00 2001 From: Arne Schwabe Date: Fri, 10 Feb 2023 15:27:09 +0100 Subject: [PATCH 019/229] Update the last sections in the man page to a be a bit less outdated Signed-off-by: Arne Schwabe Acked-by: Frank Lichtenheld Message-Id: <20230210142712.572303-6-arne@rfc2549.org> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26224.html Signed-off-by: Gert Doering (cherry picked from commit 48d27e29e68c6049872abbee38b1375522de249f) --- doc/openvpn.8.rst | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/doc/openvpn.8.rst b/doc/openvpn.8.rst index 99546747864..dd9180ab30a 100644 --- a/doc/openvpn.8.rst +++ b/doc/openvpn.8.rst @@ -97,6 +97,8 @@ https://community.openvpn.net/openvpn/wiki/FAQ HOWTO ===== +The manual ``openvpn-examples``\(5) gives some examples, especially for +small setups. For a more comprehensive guide to setting up OpenVPN in a production setting, see the OpenVPN HOWTO at @@ -107,18 +109,17 @@ https://openvpn.net/community-resources/how-to/ PROTOCOL ======== -For a description of OpenVPN's underlying protocol, see -https://openvpn.net/community-resources/openvpn-protocol/ - +An ongoing effort to document the OpenVPN protocol can be found under +https://github.com/openvpn/openvpn-rfc WEB === -OpenVPN's web site is at https://openvpn.net/ +OpenVPN's web site is at https://community.openvpn.net/ Go here to download the latest version of OpenVPN, subscribe to the -mailing lists, read the mailing list archives, or browse the SVN +mailing lists, read the mailing list archives, or browse the Git repository. From 8e3331a901dbececc8622e97ed0592ddadf56996 Mon Sep 17 00:00:00 2001 From: Lev Stipakov Date: Fri, 10 Feb 2023 15:31:59 +0200 Subject: [PATCH 020/229] Improve format specifier for socket handle in Windows Socket is a handle on Windows, which is usually logged in hex. Also an interesting value is INVALID_SOCKET, which is ~0. PRIuPTR prints decimals, and for INVALID_SOCKET it prints something like 2023-02-10 14:45:21 us=906000 write to TUN/TAP : Jrjestelmkutsulle annettu data-alue on liian pieni. (fd=18446744073709551615,code=122) PRIxPTR prints hex, and INVALID_SOCKET looks a bit nicer: 2023-02-10 15:17:11 us=828000 write to TUN/TAP : Jrjestelmkutsulle annettu data-alue on liian pieni. (fd=ffffffffffffffff,code=122) Reported-by: Selva Nair Signed-off-by: Lev Stipakov Acked-by: Frank Lichtenheld Acked-by: Antonio Quartulli Message-Id: <20230210133159.1336-1-lstipakov@gmail.com> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26220.html Signed-off-by: Gert Doering (cherry picked from commit 6731314a82d1a3c76b5497749985ee20c0c7d8eb) --- src/openvpn/syshead.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openvpn/syshead.h b/src/openvpn/syshead.h index 12ccf2f4dff..5335922558e 100644 --- a/src/openvpn/syshead.h +++ b/src/openvpn/syshead.h @@ -441,7 +441,7 @@ typedef unsigned short sa_family_t; */ #ifdef _WIN32 #define SOCKET_UNDEFINED (INVALID_SOCKET) -#define SOCKET_PRINTF "%" PRIuPTR +#define SOCKET_PRINTF "%" PRIxPTR typedef SOCKET socket_descriptor_t; #else #define SOCKET_UNDEFINED (-1) From adf00ebd51952554b02c6d8a5312b9b035b1f64c Mon Sep 17 00:00:00 2001 From: Arne Schwabe Date: Thu, 9 Feb 2023 17:37:05 +0100 Subject: [PATCH 021/229] Add building unit tests with mingw to github actions This runs each test in its own action since order of stderr and stdout is seemingly random in github action Windows output and this way at least tests outputs are grouped by test Patch v2: use -static-libgcc to avoid comping gcc runtime libraries. Signed-off-by: Arne Schwabe Acked-by: Selva Nair Message-Id: <20230209163705.466173-1-arne@rfc2549.org> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26204.html Signed-off-by: Gert Doering (cherry picked from commit 9719393b7cf94d37f3706ca32c02433e8578599b) --- .github/workflows/build.yaml | 110 ++++++++++++++++++++++++++++++++++- 1 file changed, 108 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 5888e91e59f..699964fd018 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -55,9 +55,10 @@ jobs: PKCS11_HELPER_VERSION: "1.29.0" OPENSSL_VERSION: "${{ matrix.osslver }}" TAP_WINDOWS_VERSION: "9.23.3" + CMOCKA_VERSION: "1.1.5" steps: - name: Install dependencies - run: sudo apt update && sudo apt install -y mingw-w64 libtool automake autoconf man2html unzip + run: sudo apt update && sudo apt install -y mingw-w64 libtool automake autoconf man2html unzip cmake ninja-build build-essential wget - name: Checkout OpenVPN uses: actions/checkout@v3 with: @@ -72,7 +73,7 @@ jobs: uses: actions/cache@v3 with: path: '~/mingw/' - key: ${{ matrix.target }}-mingw-${{ matrix.osslver }}-${{ env.LZO_VERSION }}-${{ env.PKCS11_HELPER_VERSION }}-${{ env.TAP_WINDOWS_VERSION }} + key: ${{ matrix.target }}-mingw-${{ matrix.osslver }}-${{ env.LZO_VERSION }}-${{ env.PKCS11_HELPER_VERSION }}-${{ env.TAP_WINDOWS_VERSION }}--${{ env.CMOCKA_VERSION }} # Repeating if: steps.cache.outputs.cache-hit != 'true' # on every step for building dependencies is ugly but @@ -84,12 +85,33 @@ jobs: wget -c -P download-cache/ "https://build.openvpn.net/downloads/releases/tap-windows-${TAP_WINDOWS_VERSION}.zip" wget -c -P download-cache/ "https://www.oberhumer.com/opensource/lzo/download/lzo-${LZO_VERSION}.tar.gz" wget -c -P download-cache/ "https://github.com/OpenSC/pkcs11-helper/releases/download/pkcs11-helper-${PKCS11_HELPER_VERSION}/pkcs11-helper-${PKCS11_HELPER_VERSION}.tar.bz2" + wget -c -P download-cache/ "https://github.com/coreboot/cmocka/archive/refs/tags/cmocka-${CMOCKA_VERSION}.tar.gz" tar jxf "download-cache/pkcs11-helper-${PKCS11_HELPER_VERSION}.tar.bz2" wget -c -P download-cache/ "https://www.openssl.org/source/old/1.1.1/openssl-${OPENSSL_VERSION}.tar.gz" || wget -c -P download-cache/ "https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz" tar zxf "download-cache/openssl-${OPENSSL_VERSION}.tar.gz" tar zxf "download-cache/lzo-${LZO_VERSION}.tar.gz" + tar zxf "download-cache/cmocka-${CMOCKA_VERSION}.tar.gz" unzip download-cache/tap-windows-${TAP_WINDOWS_VERSION}.zip + - name: create cmocka build directory + if: steps.cache.outputs.cache-hit != 'true' + run: mkdir cmocka-build + + - name: configure cmocka + if: steps.cache.outputs.cache-hit != 'true' + working-directory: "./cmocka-build" + run: cmake -GNinja -DCMAKE_C_COMPILER=${{ matrix.chost }}-gcc -DCMAKE_CXX_COMPILER=${{ matrix.chost }}-g++ -DCMAKE_SYSTEM_NAME=Windows -DCMAKE_SHARED_LINKER_FLAGS=-static-libgcc -DCMAKE_PREFIX_PATH=${HOME}/mingw/opt/lib/pkgconfig/ -DCMAKE_INCLUDE_PATH=${HOME}/mingw/opt/lib/include -DCMAKE_LIBRARY_PATH=${HOME}/mingw/opt/lib -DCMAKE_INSTALL_PREFIX=${HOME}/mingw/opt/ ../cmocka-cmocka-${{ env.CMOCKA_VERSION }} + + - name: build cmocka + if: steps.cache.outputs.cache-hit != 'true' + working-directory: "./cmocka-build" + run: ninja + + - name: install cmocka + if: steps.cache.outputs.cache-hit != 'true' + working-directory: "./cmocka-build" + run: ninja install + - name: Configure OpenSSL if: steps.cache.outputs.cache-hit != 'true' run: ./Configure --cross-compile-prefix=${{ matrix.chost }}- shared ${{ matrix.target }} no-capieng --prefix="${HOME}/mingw/opt" --openssldir="${HOME}/mingw/opt" -static-libgcc @@ -154,6 +176,90 @@ jobs: - name: build OpenVPN run: make -j3 working-directory: openvpn + - name: build OpenVPN unittests + run: make -j3 check + working-directory: openvpn + + # We use multiple upload-artifact here, so it becomes a flat folder + # structure since we need the dlls on the same level as the binaries + - name: Archive cmocka/openssl/lzo dlls + uses: actions/upload-artifact@v3 + with: + retention-days: 1 + name: mingw-unittest-${{matrix.target}}-ossl${{ matrix.osslver }}-dlls + path: '~/mingw/opt/bin/*.dll' + + # libtool puts some wrapper binaries in openvpn/tests/unit_tests/openvpn/ + # and the real binaries in openvpn/tests/unit_tests/openvpn/.libs/ + - name: Archive unittest artifacts + uses: actions/upload-artifact@v3 + with: + retention-days: 1 + name: mingw-unittest-${{matrix.target}}-ossl${{ matrix.osslver }}-tests + path: openvpn/tests/unit_tests/openvpn/.libs/*.exe + + # Currently not used by the unit test but might in the future and also + # helpful if manually downloading and running openvpn.exe from a mingw + # build + - name: Archive openvpn binary + uses: actions/upload-artifact@v3 + with: + retention-days: 1 + name: mingw-unittest-${{matrix.target}}-ossl${{ matrix.osslver }}-tests + path: openvpn/src/openvpn/.libs/*.exe + + mingw-unittest: + needs: [ mingw ] + strategy: + fail-fast: false + matrix: + osslver: [ 1.1.1q, 3.0.5 ] + target: [ mingw64, mingw ] + + runs-on: windows-latest + name: "mingw unittests - ${{matrix.target}} - OSSL ${{ matrix.osslver }}" + steps: + - name: Retrieve mingw unittest dlls + uses: actions/download-artifact@v3 + with: + name: mingw-unittest-${{matrix.target}}-ossl${{ matrix.osslver }}-dlls + path: unittests + + - name: Retrieve mingw unittest + uses: actions/download-artifact@v3 + with: + name: mingw-unittest-${{matrix.target}}-ossl${{ matrix.osslver }}-tests + path: unittests + + - name: List unittests directory + run: "dir unittests" + + - name: Run argvunit test + run: ./unittests/argv_testdriver.exe + + - name: Run auth_tokenunit test + run: ./unittests/auth_token_testdriver.exe + + - name: Run bufferunit test + run: ./unittests/buffer_testdriver.exe + + - name: Run cryptounit test + run: ./unittests/crypto_testdriver.exe + + - name: Run miscunit test + run: ./unittests/misc_testdriver.exe + + - name: Run ncpunit test + run: ./unittests/ncp_testdriver.exe + + - name: Run packet idunit test + run: ./unittests/packet_id_testdriver.exe + + - name: Run pktunit test + run: ./unittests/pkt_testdriver.exe + + - name: Run providerunit test + run: ./unittests/provider_testdriver.exe ubuntu: strategy: From cfbfb801e6432382edbbec758335d7c142707206 Mon Sep 17 00:00:00 2001 From: Arne Schwabe Date: Fri, 10 Feb 2023 15:27:10 +0100 Subject: [PATCH 022/229] Revise the cipher negotiation info about OpenVPN3 in the man page Newer OpenVPN 3 core versions now allow limited configuration of ciphers: // Allow usage of legacy (cipher) algorithm that are no longer // considered safe // This includes BF-CBC, single DES and RC2 private key encryption. // With OpenSSL 3.0 this also instructs OpenSSL to load the legacy // provider. bool enableLegacyAlgorithms = false; // By default modern OpenVPN version (OpenVPN 2.6 and OpenVPN core // 3.7) will only allow // preferred algorithms (AES-GCM, Chacha20-Poly1305) that also work // with the newer DCO // implementations. If this is enabled, we fall back to allowing all // algorithms (if these are // supported by the crypto library) bool enableNonPreferredDCAlgorithms = false; Adjust the man page section accordingly but only really mention the AEAD ciphers to be always present and that they should be included in the data-ciphers option. Signed-off-by: Arne Schwabe Acked-by: Gert Doering Message-Id: <20230210142712.572303-7-arne@rfc2549.org> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26226.html Signed-off-by: Gert Doering (cherry picked from commit 4da513d584b4e7521de5a47a95cc27fa8a342fd3) --- doc/man-sections/cipher-negotiation.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/man-sections/cipher-negotiation.rst b/doc/man-sections/cipher-negotiation.rst index b07176cd2b3..888ffa6fead 100644 --- a/doc/man-sections/cipher-negotiation.rst +++ b/doc/man-sections/cipher-negotiation.rst @@ -42,8 +42,9 @@ options to avoid this behaviour. OpenVPN 3 clients ----------------- Clients based on the OpenVPN 3.x library (https://github.com/openvpn/openvpn3/) -do not have a configurable ``--ncp-ciphers`` or ``--data-ciphers`` option. Instead -these clients will announce support for all their supported AEAD ciphers +do not have a configurable ``--ncp-ciphers`` or ``--data-ciphers`` option. Newer +versions by default disable legacy AES-CBC, BF-CBC, and DES-CBC ciphers. +These clients will always announce support for all their supported AEAD ciphers (`AES-256-GCM`, `AES-128-GCM` and in newer versions also `Chacha20-Poly1305`). To support OpenVPN 3.x based clients at least one of these ciphers needs to be From 0ccfce24b470d924d4184c8a3de08a50154c5e4c Mon Sep 17 00:00:00 2001 From: Selva Nair Date: Sat, 28 Jan 2023 17:34:19 -0500 Subject: [PATCH 023/229] cyryptapi.c: log the selected certificate's name - With various ways of specifying the selector-string to the "--cryptoapicert" option, its not immediately obvious which certificate gets selected from the store. Log it. The "name" logged is a friendly name (if present), or a representative element of the subject (usually the common-name). Signed-off-by: Selva Nair Acked-by: Gert Doering Message-Id: <20230128223421.2207802-3-selva.nair@gmail.com> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26093.html Signed-off-by: Gert Doering (cherry picked from commit ddffcea2922905ec13e0e39239d106e0edbea5de) --- src/openvpn/cryptoapi.c | 29 +++++++++++++++++++++++++++++ src/openvpn/win32-util.c | 15 +++++++++++++++ src/openvpn/win32-util.h | 3 +++ 3 files changed, 47 insertions(+) diff --git a/src/openvpn/cryptoapi.c b/src/openvpn/cryptoapi.c index 661a9a6d742..f698e28c651 100644 --- a/src/openvpn/cryptoapi.c +++ b/src/openvpn/cryptoapi.c @@ -934,12 +934,31 @@ xkey_cng_sign(void *handle, unsigned char *sig, size_t *siglen, const unsigned c #endif /* HAVE_XKEY_PROVIDER */ +static char * +get_cert_name(const CERT_CONTEXT *cc, struct gc_arena *gc) +{ + DWORD len = CertGetNameStringW(cc, CERT_NAME_FRIENDLY_DISPLAY_TYPE, 0, NULL, NULL, 0); + char *name = NULL; + if (len) + { + wchar_t *wname = gc_malloc(len*sizeof(wchar_t), false, gc); + if (!wname + || CertGetNameStringW(cc, CERT_NAME_FRIENDLY_DISPLAY_TYPE, 0, NULL, wname, len) == 0) + { + return NULL; + } + name = utf16to8(wname, gc); + } + return name; +} + int SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop) { HCERTSTORE cs; X509 *cert = NULL; CAPI_DATA *cd = calloc(1, sizeof(*cd)); + struct gc_arena gc = gc_new(); if (cd == NULL) { @@ -974,6 +993,13 @@ SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop) } } + /* try to log the "name" of the selected certificate */ + char *cert_name = get_cert_name(cd->cert_context, &gc); + if (cert_name) + { + msg(D_LOW, "cryptapicert: using certificate with name <%s>", cert_name); + } + /* cert_context->pbCertEncoded is the cert X509 DER encoded. */ cert = d2i_X509(NULL, (const unsigned char **) &cd->cert_context->pbCertEncoded, cd->cert_context->cbCertEncoded); @@ -1017,6 +1043,7 @@ SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop) EVP_PKEY *privkey = xkey_load_generic_key(tls_libctx, cd, pkey, xkey_cng_sign, (XKEY_PRIVKEY_FREE_fn *) CAPI_DATA_free); SSL_CTX_use_PrivateKey(ssl_ctx, privkey); + gc_free(&gc); return 1; /* do not free cd -- its kept by xkey provider */ #else /* ifdef HAVE_XKEY_PROVIDER */ @@ -1042,12 +1069,14 @@ SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop) goto err; } CAPI_DATA_free(cd); /* this will do a ref_count-- */ + gc_free(gc); return 1; #endif /* HAVE_XKEY_PROVIDER */ err: CAPI_DATA_free(cd); + gc_free(&gc); return 0; } #endif /* _WIN32 */ diff --git a/src/openvpn/win32-util.c b/src/openvpn/win32-util.c index 35f2a311cc7..32f7a00b1d5 100644 --- a/src/openvpn/win32-util.c +++ b/src/openvpn/win32-util.c @@ -48,6 +48,21 @@ wide_string(const char *utf8, struct gc_arena *gc) return ucs16; } +char * +utf16to8(const wchar_t *utf16, struct gc_arena *gc) +{ + char *utf8 = NULL; + int n = WideCharToMultiByte(CP_UTF8, 0, utf16, -1, NULL, 0, NULL, NULL); + if (n > 0) + { + utf8 = gc_malloc(n, true, gc); + if (utf8) + { + WideCharToMultiByte(CP_UTF8, 0, utf16, -1, utf8, n, NULL, NULL); + } + } + return utf8; +} /* * Return true if filename is safe to be used on Windows, diff --git a/src/openvpn/win32-util.h b/src/openvpn/win32-util.h index b24242c8ae5..ac37979ff77 100644 --- a/src/openvpn/win32-util.h +++ b/src/openvpn/win32-util.h @@ -34,6 +34,9 @@ /* Convert a string from UTF-8 to UCS-2 */ WCHAR *wide_string(const char *utf8, struct gc_arena *gc); +/* Convert a string from UTF-16 to UTF-8 */ +char *utf16to8(const wchar_t *utf16, struct gc_arena *gc); + /* return true if filename is safe to be used on Windows */ bool win_safe_filename(const char *fn); From f3dd050ed8d95001107a841fa7779d09f3efb944 Mon Sep 17 00:00:00 2001 From: Selva Nair Date: Wed, 1 Feb 2023 18:03:40 -0500 Subject: [PATCH 024/229] cryptoapi.c: remove pre OpenSSL-3.01 support - Require xkey-provider (thus OpenSSL 3.01+) for --cryptoapicert Note: Ideally we should also make ENABLE_CRYPTOAPI conditional on HAVE_XKEY_PROVIDER but that looks hard unless we can agree to move HAVE_XKEY_PROVIDER to configure/config.h. v2: use "binary" instead of "version" in the error message Signed-off-by: Selva Nair Acked-by: Gert Doering Message-Id: <20230201230340.2268781-1-selva.nair@gmail.com> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26131.html Signed-off-by: Gert Doering (cherry picked from commit 4b28831e07a357476cb31a6f439becff3dc8c842) --- src/openvpn/cryptoapi.c | 555 +--------------------------------------- src/openvpn/options.c | 2 +- 2 files changed, 11 insertions(+), 546 deletions(-) diff --git a/src/openvpn/cryptoapi.c b/src/openvpn/cryptoapi.c index f698e28c651..76823e75dec 100644 --- a/src/openvpn/cryptoapi.c +++ b/src/openvpn/cryptoapi.c @@ -55,17 +55,17 @@ #include "xkey_common.h" #ifndef HAVE_XKEY_PROVIDER -/* index for storing external data in EC_KEY: < 0 means uninitialized */ -static int ec_data_idx = -1; -/* Global EVP_PKEY_METHOD used to override the sign operation */ -static EVP_PKEY_METHOD *pmethod; -static int (*default_pkey_sign_init) (EVP_PKEY_CTX *ctx); -static int (*default_pkey_sign) (EVP_PKEY_CTX *ctx, unsigned char *sig, - size_t *siglen, const unsigned char *tbs, size_t tbslen); -#else /* ifndef HAVE_XKEY_PROVIDER */ +int +SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop) +{ + msg(M_NONFATAL, "ERROR: this binary was built without cryptoapicert support"); + return 0; +} + +#else /* HAVE_XKEY_PROVIDER */ + static XKEY_EXTERNAL_SIGN_fn xkey_cng_sign; -#endif /* HAVE_XKEY_PROVIDER */ typedef struct _CAPI_DATA { const CERT_CONTEXT *cert_context; @@ -146,127 +146,6 @@ CAPI_DATA_free(CAPI_DATA *cd) free(cd); } -#ifndef HAVE_XKEY_PROVIDER - -/* Translate OpenSSL padding type to CNG padding type - * Returns 0 for unknown/unsupported padding. - */ -static DWORD -cng_padding_type(int padding) -{ - DWORD pad = 0; - - switch (padding) - { - case RSA_NO_PADDING: - break; - - case RSA_PKCS1_PADDING: - pad = BCRYPT_PAD_PKCS1; - break; - - case RSA_PKCS1_PSS_PADDING: - pad = BCRYPT_PAD_PSS; - break; - - default: - msg(M_WARN|M_INFO, "cryptoapicert: unknown OpenSSL padding type %d.", - padding); - } - - return pad; -} - -/** - * Sign the hash in 'from' using NCryptSignHash(). This requires an NCRYPT - * key handle in cd->crypt_prov. On return the signature is in 'to'. Returns - * the length of the signature or 0 on error. - * This is used only for RSA and padding should be BCRYPT_PAD_PKCS1 or - * BCRYPT_PAD_PSS. - * If the hash_algo is not NULL, PKCS #1 DigestInfo header gets added - * to |from|, else it is signed as is. Use NULL for MD5 + SHA1 hash used - * in TLS 1.1 and earlier. - * In case of PSS padding, |saltlen| should specify the size of salt to use. - * If |to| is NULL returns the required buffer size. - */ -static int -priv_enc_CNG(const CAPI_DATA *cd, const wchar_t *hash_algo, const unsigned char *from, - int flen, unsigned char *to, int tlen, DWORD padding, DWORD saltlen) -{ - NCRYPT_KEY_HANDLE hkey = cd->crypt_prov; - DWORD len = 0; - ASSERT(cd->key_spec == CERT_NCRYPT_KEY_SPEC); - - DWORD status; - - msg(D_LOW, "Signing hash using CNG: data size = %d padding = %lu", flen, padding); - - if (padding == BCRYPT_PAD_PKCS1) - { - BCRYPT_PKCS1_PADDING_INFO padinfo = {hash_algo}; - status = NCryptSignHash(hkey, &padinfo, (BYTE *)from, flen, - to, tlen, &len, padding); - } - else if (padding == BCRYPT_PAD_PSS) - { - BCRYPT_PSS_PADDING_INFO padinfo = {hash_algo, saltlen}; - status = NCryptSignHash(hkey, &padinfo, (BYTE *)from, flen, - to, tlen, &len, padding); - } - else - { - msg(M_NONFATAL, "Error in cryptoapicert: Unknown padding type"); - return 0; - } - - if (status != ERROR_SUCCESS) - { - SetLastError(status); - msg(M_NONFATAL|M_ERRNO, "Error in cryptoapicert: NCryptSignHash failed"); - len = 0; - } - - /* Unlike CAPI, CNG signature is in big endian order. No reversing needed. */ - return len; -} - -/* called at RSA_free */ -static int -rsa_finish(RSA *rsa) -{ - const RSA_METHOD *rsa_meth = RSA_get_method(rsa); - CAPI_DATA *cd = (CAPI_DATA *) RSA_meth_get0_app_data(rsa_meth); - - if (cd == NULL) - { - return 0; - } - CAPI_DATA_free(cd); - RSA_meth_free((RSA_METHOD *) rsa_meth); - return 1; -} - -static EC_KEY_METHOD *ec_method = NULL; - -/** EC_KEY_METHOD callback: called when the key is freed */ -static void -ec_finish(EC_KEY *ec) -{ - EC_KEY_METHOD_free(ec_method); - ec_method = NULL; - CAPI_DATA *cd = EC_KEY_get_ex_data(ec, ec_data_idx); - CAPI_DATA_free(cd); - EC_KEY_set_ex_data(ec, ec_data_idx, NULL); -} - -/** EC_KEY_METHOD callback sign_setup(): we do nothing here */ -static int -ecdsa_sign_setup(EC_KEY *eckey, BN_CTX *ctx_in, BIGNUM **kinvp, BIGNUM **rp) -{ - return 1; -} -#endif /* HAVE_XKEY_PROVIDER */ - /** * Helper to convert ECDSA signature returned by NCryptSignHash * to an ECDSA_SIG structure. @@ -301,141 +180,6 @@ ecdsa_bin2sig(unsigned char *buf, int len) return NULL; } -#ifndef HAVE_XKEY_PROVIDER - -/** EC_KEY_METHOD callback sign_sig(): sign and return an ECDSA_SIG pointer. */ -static ECDSA_SIG * -ecdsa_sign_sig(const unsigned char *dgst, int dgstlen, - const BIGNUM *in_kinv, const BIGNUM *in_r, EC_KEY *ec) -{ - ECDSA_SIG *ecsig = NULL; - CAPI_DATA *cd = (CAPI_DATA *)EC_KEY_get_ex_data(ec, ec_data_idx); - - ASSERT(cd->key_spec == CERT_NCRYPT_KEY_SPEC); - - NCRYPT_KEY_HANDLE hkey = cd->crypt_prov; - BYTE buf[512]; /* large enough buffer for signature to avoid malloc */ - DWORD len = _countof(buf); - - msg(D_LOW, "Cryptoapi: signing hash using EC key: data size = %d", dgstlen); - - DWORD status = NCryptSignHash(hkey, NULL, (BYTE *)dgst, dgstlen, (BYTE *)buf, len, &len, 0); - if (status != ERROR_SUCCESS) - { - SetLastError(status); - msg(M_NONFATAL|M_ERRNO, "Error in cryptoapticert: NCryptSignHash failed"); - } - else - { - /* NCryptSignHash returns r, s concatenated in buf[] */ - ecsig = ecdsa_bin2sig(buf, len); - } - return ecsig; -} - -/** EC_KEY_METHOD callback sign(): sign and return a DER encoded signature */ -static int -ecdsa_sign(int type, const unsigned char *dgst, int dgstlen, unsigned char *sig, - unsigned int *siglen, const BIGNUM *kinv, const BIGNUM *r, EC_KEY *ec) -{ - ECDSA_SIG *s; - - *siglen = 0; - s = ecdsa_sign_sig(dgst, dgstlen, NULL, NULL, ec); - if (s == NULL) - { - return 0; - } - - /* convert internal signature structure 's' to DER encoded byte array in sig */ - int len = i2d_ECDSA_SIG(s, NULL); - if (len > ECDSA_size(ec)) - { - ECDSA_SIG_free(s); - msg(M_NONFATAL, "Error in cryptoapicert: DER encoded ECDSA signature is too long (%d bytes)", len); - return 0; - } - *siglen = i2d_ECDSA_SIG(s, &sig); - ECDSA_SIG_free(s); - - return 1; -} - -static int -ssl_ctx_set_eckey(SSL_CTX *ssl_ctx, CAPI_DATA *cd, EVP_PKEY *pkey) -{ - EC_KEY *ec = NULL; - EVP_PKEY *privkey = NULL; - - /* create a method struct with default callbacks filled in */ - ec_method = EC_KEY_METHOD_new(EC_KEY_OpenSSL()); - if (!ec_method) - { - goto err; - } - - /* We only need to set finish among init methods, and sign methods */ - EC_KEY_METHOD_set_init(ec_method, NULL, ec_finish, NULL, NULL, NULL, NULL); - EC_KEY_METHOD_set_sign(ec_method, ecdsa_sign, ecdsa_sign_setup, ecdsa_sign_sig); - - ec = EC_KEY_dup(EVP_PKEY_get0_EC_KEY(pkey)); - if (!ec) - { - goto err; - } - if (!EC_KEY_set_method(ec, ec_method)) - { - goto err; - } - - /* get an index to store cd as external data */ - if (ec_data_idx < 0) - { - ec_data_idx = EC_KEY_get_ex_new_index(0, "cryptapicert ec key", NULL, NULL, NULL); - if (ec_data_idx < 0) - { - goto err; - } - } - EC_KEY_set_ex_data(ec, ec_data_idx, cd); - - /* cd assigned to ec as ex_data, increase its refcount */ - cd->ref_count++; - - privkey = EVP_PKEY_new(); - if (!EVP_PKEY_assign_EC_KEY(privkey, ec)) - { - EC_KEY_free(ec); - goto err; - } - /* from here on ec will get freed with privkey */ - - if (!SSL_CTX_use_PrivateKey(ssl_ctx, privkey)) - { - goto err; - } - EVP_PKEY_free(privkey); /* this will dn_ref or free ec as well */ - return 1; - -err: - if (privkey) - { - EVP_PKEY_free(privkey); - } - else if (ec) - { - EC_KEY_free(ec); - } - if (ec_method) /* do always set ec_method = NULL after freeing it */ - { - EC_KEY_METHOD_free(ec_method); - ec_method = NULL; - } - return 0; -} - -#endif /* !HAVE_XKEY_PROVIDER */ - static const CERT_CONTEXT * find_certificate_in_store(const char *cert_prop, HCERTSTORE cert_store) { @@ -536,254 +280,6 @@ find_certificate_in_store(const char *cert_prop, HCERTSTORE cert_store) return rv; } -#ifndef HAVE_XKEY_PROVIDER - -static const CAPI_DATA * -retrieve_capi_data(EVP_PKEY *pkey) -{ - const CAPI_DATA *cd = NULL; - - if (pkey && EVP_PKEY_id(pkey) == EVP_PKEY_RSA) - { - RSA *rsa = EVP_PKEY_get0_RSA(pkey); - if (rsa) - { - cd = (CAPI_DATA *)RSA_meth_get0_app_data(RSA_get_method(rsa)); - } - } - return cd; -} - -static int -pkey_rsa_sign_init(EVP_PKEY_CTX *ctx) -{ - msg(D_LOW, "cryptoapicert: enter pkey_rsa_sign_init"); - - EVP_PKEY *pkey = EVP_PKEY_CTX_get0_pkey(ctx); - - if (pkey && retrieve_capi_data(pkey)) - { - return 1; /* Return success */ - } - else if (default_pkey_sign_init) /* Not our key. Call the default method */ - { - return default_pkey_sign_init(ctx); - } - return 1; -} - -/** - * Implementation of EVP_PKEY_sign() using CNG: sign the digest in |tbs| - * and save the the signature in |sig| and its size in |*siglen|. - * If |sig| is NULL the required buffer size is returned in |*siglen|. - * Returns value is 1 on success, 0 or a negative integer on error. - */ -static int -pkey_rsa_sign(EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen, - const unsigned char *tbs, size_t tbslen) -{ - EVP_PKEY *pkey = NULL; - const CAPI_DATA *cd = NULL; - EVP_MD *md = NULL; - const wchar_t *alg = NULL; - - int padding = 0; - int hashlen = 0; - int saltlen = 0; - - pkey = EVP_PKEY_CTX_get0_pkey(ctx); - if (pkey) - { - cd = retrieve_capi_data(pkey); - } - - /* - * We intercept all sign requests, not just the one's for our key. - * Check the key and call the saved OpenSSL method for unknown keys. - */ - if (!pkey || !cd) - { - if (default_pkey_sign) - { - return default_pkey_sign(ctx, sig, siglen, tbs, tbslen); - } - else /* This should not happen */ - { - msg(M_FATAL, "Error in cryptoapicert: Unknown key and no default sign operation to fallback on"); - return -1; - } - } - - if (!EVP_PKEY_CTX_get_rsa_padding(ctx, &padding)) - { - padding = RSA_PKCS1_PADDING; /* Default padding for RSA */ - } - - if (EVP_PKEY_CTX_get_signature_md(ctx, &md)) - { - hashlen = EVP_MD_size(md); - alg = cng_hash_algo(EVP_MD_type(md)); - - /* - * alg == NULL indicates legacy MD5+SHA1 hash, else alg should be a valid - * digest algorithm. - */ - if (alg && wcscmp(alg, L"UNKNOWN") == 0) - { - msg(M_NONFATAL, "Error in cryptoapicert: Unknown hash algorithm <%d>", EVP_MD_type(md)); - return -1; - } - } - else - { - msg(M_NONFATAL, "Error in cryptoapicert: could not determine the signature digest algorithm"); - return -1; - } - - if (tbslen != (size_t)hashlen) - { - msg(M_NONFATAL, "Error in cryptoapicert: data size does not match hash"); - return -1; - } - - /* If padding is PSS, determine parameters to pass to CNG */ - if (padding == RSA_PKCS1_PSS_PADDING) - { - /* - * Ensure the digest type for signature and mask generation match. - * In CNG there is no option to specify separate hash functions for - * the two, but OpenSSL supports it. However, I have not seen the - * two being different in practice. Also the recommended practice is - * to use the same for both (rfc 8017 sec 8.1). - */ - EVP_MD *mgf1md; - if (!EVP_PKEY_CTX_get_rsa_mgf1_md(ctx, &mgf1md) - || EVP_MD_type(mgf1md) != EVP_MD_type(md)) - { - msg(M_NONFATAL, "Error in cryptoapicert: Unknown MGF1 digest type or does" - " not match the signature digest type."); - return -1; - } - - if (!EVP_PKEY_CTX_get_rsa_pss_saltlen(ctx, &saltlen)) - { - msg(M_WARN|M_INFO, "cryptoapicert: unable to get the salt length from context." - " Using the default value."); - saltlen = -1; - } - - /* - * In OpenSSL saltlen = -1 indicates to use the size of the digest as - * size of the salt. A value of -2 or -3 indicates maximum salt length - * that will fit. See RSA_padding_add_PKCS1_PSS_mgf1() of OpenSSL. - */ - if (saltlen == -1) - { - saltlen = hashlen; - } - else if (saltlen < 0) - { - const RSA *rsa = EVP_PKEY_get0_RSA(pkey); - saltlen = RSA_size(rsa) - hashlen - 2; /* max salt length for RSASSA-PSS */ - if (RSA_bits(rsa) &0x7) /* number of bits in the key not a multiple of 8 */ - { - saltlen--; - } - } - - if (saltlen < 0) - { - msg(M_NONFATAL, "Error in cryptoapicert: invalid salt length (%d). Digest too large for keysize?", saltlen); - return -1; - } - msg(D_LOW, "cryptoapicert: PSS padding using saltlen = %d", saltlen); - } - - msg(D_LOW, "cryptoapicert: calling priv_enc_CNG with alg = %ls", alg); - *siglen = priv_enc_CNG(cd, alg, tbs, (int)tbslen, sig, (int)*siglen, - cng_padding_type(padding), (DWORD)saltlen); - - return (*siglen == 0) ? 0 : 1; -} - -static int -ssl_ctx_set_rsakey(SSL_CTX *ssl_ctx, CAPI_DATA *cd, EVP_PKEY *pkey) -{ - RSA *rsa = NULL; - RSA_METHOD *my_rsa_method = NULL; - EVP_PKEY *privkey = NULL; - int ret = 0; - - my_rsa_method = RSA_meth_new("Microsoft Cryptography API RSA Method", - RSA_METHOD_FLAG_NO_CHECK); - check_malloc_return(my_rsa_method); - RSA_meth_set_finish(my_rsa_method, rsa_finish); /* we use this callback to cleanup CAPI_DATA */ - RSA_meth_set0_app_data(my_rsa_method, cd); - - /* pmethod is global -- initialize only if NULL */ - if (!pmethod) - { - pmethod = EVP_PKEY_meth_new(EVP_PKEY_RSA, 0); - if (!pmethod) - { - msg(M_NONFATAL, "Error in cryptoapicert: failed to create EVP_PKEY_METHOD"); - return 0; - } - const EVP_PKEY_METHOD *default_pmethod = EVP_PKEY_meth_find(EVP_PKEY_RSA); - EVP_PKEY_meth_copy(pmethod, default_pmethod); - - /* We want to override only sign_init() and sign() */ - EVP_PKEY_meth_set_sign(pmethod, pkey_rsa_sign_init, pkey_rsa_sign); - EVP_PKEY_meth_add0(pmethod); - - /* Keep a copy of the default sign and sign_init methods */ - - EVP_PKEY_meth_get_sign(default_pmethod, &default_pkey_sign_init, - &default_pkey_sign); - } - - rsa = EVP_PKEY_get1_RSA(pkey); - - RSA_set_flags(rsa, RSA_flags(rsa) | RSA_FLAG_EXT_PKEY); - if (!RSA_set_method(rsa, my_rsa_method)) - { - goto cleanup; - } - my_rsa_method = NULL; /* we do not want to free it in cleanup */ - cd->ref_count++; /* with method, cd gets assigned to the key as well */ - - privkey = EVP_PKEY_new(); - if (!EVP_PKEY_assign_RSA(privkey, rsa)) - { - goto cleanup; - } - rsa = NULL; /* privkey has taken ownership */ - - if (!SSL_CTX_use_PrivateKey(ssl_ctx, privkey)) - { - goto cleanup; - } - ret = 1; - -cleanup: - if (rsa) - { - RSA_free(rsa); - } - if (my_rsa_method) - { - RSA_meth_free(my_rsa_method); - } - if (privkey) - { - EVP_PKEY_free(privkey); - } - - return ret; -} - -#else /* HAVE_XKEY_PROVIDER */ - /** Sign hash in tbs using EC key in cd and NCryptSignHash */ static int xkey_cng_ec_sign(CAPI_DATA *cd, unsigned char *sig, size_t *siglen, const unsigned char *tbs, @@ -932,8 +428,6 @@ xkey_cng_sign(void *handle, unsigned char *sig, size_t *siglen, const unsigned c } } -#endif /* HAVE_XKEY_PROVIDER */ - static char * get_cert_name(const CERT_CONTEXT *cc, struct gc_arena *gc) { @@ -1038,45 +532,16 @@ SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop) X509_free(cert); cert = NULL; -#ifdef HAVE_XKEY_PROVIDER - EVP_PKEY *privkey = xkey_load_generic_key(tls_libctx, cd, pkey, xkey_cng_sign, (XKEY_PRIVKEY_FREE_fn *) CAPI_DATA_free); SSL_CTX_use_PrivateKey(ssl_ctx, privkey); gc_free(&gc); return 1; /* do not free cd -- its kept by xkey provider */ -#else /* ifdef HAVE_XKEY_PROVIDER */ - - if (EVP_PKEY_id(pkey) == EVP_PKEY_RSA) - { - if (!ssl_ctx_set_rsakey(ssl_ctx, cd, pkey)) - { - goto err; - } - } - else if (EVP_PKEY_id(pkey) == EVP_PKEY_EC) - { - if (!ssl_ctx_set_eckey(ssl_ctx, cd, pkey)) - { - goto err; - } - } - else - { - msg(M_WARN|M_INFO, "WARNING: cryptoapicert: key type <%d> not supported", - EVP_PKEY_id(pkey)); - goto err; - } - CAPI_DATA_free(cd); /* this will do a ref_count-- */ - gc_free(gc); - return 1; - -#endif /* HAVE_XKEY_PROVIDER */ - err: CAPI_DATA_free(cd); gc_free(&gc); return 0; } +#endif /* HAVE_XKEY_PROVIDER */ #endif /* _WIN32 */ diff --git a/src/openvpn/options.c b/src/openvpn/options.c index ce756128851..9105449c7f8 100644 --- a/src/openvpn/options.c +++ b/src/openvpn/options.c @@ -8872,7 +8872,7 @@ add_option(struct options *options, listend->next = newlist; } } -#ifdef ENABLE_CRYPTOAPI +#if defined(ENABLE_CRYPTOAPI) && defined(HAVE_XKEY_PROVIDER) else if (streq(p[0], "cryptoapicert") && p[1] && !p[2]) { VERIFY_PERMISSION(OPT_P_GENERAL); From 5a70f5025a3f1aee53b531b2e7713dd99fa175bb Mon Sep 17 00:00:00 2001 From: Selva Nair Date: Fri, 3 Feb 2023 19:43:22 -0500 Subject: [PATCH 025/229] cryptoapi.c: simplify parsing of thumbprint hex string v2: Moved the "parse_hexstring" chunk to a function for clarity and to permit unit-testing. A test is submitted as a follow up patch. Signed-off-by: Selva Nair Acked-by: Gert Doering Message-Id: <20230204004322.250210-1-selva.nair@gmail.com> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26146.html Signed-off-by: Gert Doering (cherry picked from commit 94bbe98b2b135b2da74b694358e6c94c1defbffd) --- src/openvpn/cryptoapi.c | 77 ++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 40 deletions(-) diff --git a/src/openvpn/cryptoapi.c b/src/openvpn/cryptoapi.c index 76823e75dec..6f6be90927c 100644 --- a/src/openvpn/cryptoapi.c +++ b/src/openvpn/cryptoapi.c @@ -180,6 +180,39 @@ ecdsa_bin2sig(unsigned char *buf, int len) return NULL; } +/** + * Parse a hex string with optional embedded spaces into + * a byte array. + * @param p pointer to the input string + * @param arr on output contains the parsed bytes + * @param capacity capacity of the byte array arr + * @returns the number of bytes parsed or 0 on error + */ +int +parse_hexstring(const char *p, unsigned char *arr, size_t capacity) +{ + int i = 0; + for ( ; *p && i < capacity; p += 2) + { + /* skip spaces */ + while (*p == ' ') + { + p++; + } + if (!*p) /* ending with spaces is not an error */ + { + break; + } + + if (!isxdigit(p[0]) || !isxdigit(p[1]) + || sscanf(p, "%2hhx", &arr[i++]) != 1) + { + return 0; + } + } + return i; +} + static const CERT_CONTEXT * find_certificate_in_store(const char *cert_prop, HCERTSTORE cert_store) { @@ -205,51 +238,15 @@ find_certificate_in_store(const char *cert_prop, HCERTSTORE cert_store) } else if (!strncmp(cert_prop, "THUMB:", 6)) { - const char *p; - int i, x = 0; find_type = CERT_FIND_HASH; find_param = &blob; - /* skip the tag */ - cert_prop += 6; - for (p = cert_prop, i = 0; *p && i < sizeof(hash); i++) + blob.cbData = parse_hexstring(cert_prop + 6, hash, sizeof(hash)); + if (blob.cbData == 0) { - if (*p >= '0' && *p <= '9') - { - x = (*p - '0') << 4; - } - else if (*p >= 'A' && *p <= 'F') - { - x = (*p - 'A' + 10) << 4; - } - else if (*p >= 'a' && *p <= 'f') - { - x = (*p - 'a' + 10) << 4; - } - if (!*++p) /* unexpected end of string */ - { - msg(M_WARN|M_INFO, "WARNING: cryptoapicert: error parsing .", cert_prop); - goto out; - } - if (*p >= '0' && *p <= '9') - { - x += *p - '0'; - } - else if (*p >= 'A' && *p <= 'F') - { - x += *p - 'A' + 10; - } - else if (*p >= 'a' && *p <= 'f') - { - x += *p - 'a' + 10; - } - hash[i] = x; - /* skip any space(s) between hex numbers */ - for (p++; *p && *p == ' '; p++) - { - } + msg(M_WARN|M_INFO, "WARNING: cryptoapicert: error parsing <%s>.", cert_prop); + goto out; } - blob.cbData = i; } else { From a8ff15ce49b69816b64387e3387b9a310891d12a Mon Sep 17 00:00:00 2001 From: Selva Nair Date: Sat, 28 Jan 2023 17:34:18 -0500 Subject: [PATCH 026/229] Option --cryptoapicert: support issuer name as a selector - Certificate selection string can now specify a partial issuer name string as "--cryptoapicert ISSUER:" where is matched as a substring of the issuer (CA) name in the certificate. Partial case-insensitive matching against the "issuer name" is used. Here "issuer name" is a text representation of the RDN's separated by commas. E.g., "CA, Ontario, Toronto, Acme Inc., IT, Acme Root CA". See MSDN docs on CertFindCertificateInStore() with CERT_FIND_ISSUER_STR as "FindType" for more details. As the order of RDN's is not well-defined[*] and type names like "OU" or "CN" are not included, its best to match against a single attribute like the CN of the issuer: E.g., --cryptoapicert "ISSUER:Acme Root" [*] Windows appears to order RDN's in the reverse order to which its written in the certificate but do not rely on this. Signed-off-by: Selva Nair Acked-by: Gert Doering Message-Id: <20230128223421.2207802-2-selva.nair@gmail.com> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26092.html Signed-off-by: Gert Doering (cherry picked from commit b9e0e4060798ed88d2170702f2935754616b1200) --- doc/man-sections/windows-options.rst | 13 +++++++++++-- src/openvpn/cryptoapi.c | 5 +++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/doc/man-sections/windows-options.rst b/doc/man-sections/windows-options.rst index 368f7b19025..e87291f4691 100644 --- a/doc/man-sections/windows-options.rst +++ b/doc/man-sections/windows-options.rst @@ -41,13 +41,22 @@ Windows-Specific Options cryptoapicert "SUBJ:Peter Runestig" - To select a certificate, based on certificate's thumbprint: + To select a certificate, based on certificate's thumbprint (SHA1 hash): :: cryptoapicert "THUMB:f6 49 24 41 01 b4 ..." The thumbprint hex string can easily be copy-and-pasted from the Windows - Certificate Store GUI. + Certificate Store GUI. The embedded spaces in the hex string are optional. + + To select a certificate based on a substring in certificate's + issuer name: + :: + + cryptoapicert "ISSUER:Sample CA" + + The first non-expired certificate found in the user's store or the + machine store that matches the select-string is used. --dhcp-release Ask Windows to release the TAP adapter lease on shutdown. This option diff --git a/src/openvpn/cryptoapi.c b/src/openvpn/cryptoapi.c index 6f6be90927c..136c6ffc879 100644 --- a/src/openvpn/cryptoapi.c +++ b/src/openvpn/cryptoapi.c @@ -236,6 +236,11 @@ find_certificate_in_store(const char *cert_prop, HCERTSTORE cert_store) find_param = wide_string(cert_prop + 5, &gc); find_type = CERT_FIND_SUBJECT_STR_W; } + else if (!strncmp(cert_prop, "ISSUER:", 7)) + { + find_param = wide_string(cert_prop + 7, &gc); + find_type = CERT_FIND_ISSUER_STR_W; + } else if (!strncmp(cert_prop, "THUMB:", 6)) { find_type = CERT_FIND_HASH; From d0c1abb4dee233834de540932ddcaef209696e79 Mon Sep 17 00:00:00 2001 From: Arne Schwabe Date: Mon, 20 Feb 2023 14:14:24 +0100 Subject: [PATCH 027/229] Exit if a proper message instead of segfault on Android without management The Android implementation is relying on the management interface to be always available. Trying to run the Android binary without the mangament interface outside the app leads to a segfault. Exit with a FATAL error instead. Signed-off-by: Arne Schwabe Acked-by: Gert Doering Message-Id: <20230220131424.1749736-1-arne@rfc2549.org> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26288.html Signed-off-by: Gert Doering (cherry picked from commit 3132bead497deca24dc638e151bf7194df6f4884) --- src/openvpn/manage.c | 4 ++++ src/openvpn/socket.c | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/src/openvpn/manage.c b/src/openvpn/manage.c index 8397d3cf67f..a55e5d78823 100644 --- a/src/openvpn/manage.c +++ b/src/openvpn/manage.c @@ -2192,6 +2192,10 @@ man_recv_with_fd(int fd, void *ptr, size_t nbytes, int flags, int *recvfd) bool management_android_control(struct management *man, const char *command, const char *msg) { + if (!man) + { + msg(M_FATAL, "Required management interface not available."); + } struct user_pass up; CLEAR(up); strncpy(up.username, msg, sizeof(up.username)-1); diff --git a/src/openvpn/socket.c b/src/openvpn/socket.c index 42d95339046..eff21ca5663 100644 --- a/src/openvpn/socket.c +++ b/src/openvpn/socket.c @@ -1161,6 +1161,11 @@ create_socket(struct link_socket *sock, struct addrinfo *addr) static void protect_fd_nonlocal(int fd, const struct sockaddr *addr) { + if (!management) + { + msg(M_FATAL, "Required management interface not available.") + } + /* pass socket FD to management interface to pass on to VPNService API * as "protected socket" (exempt from being routed into tunnel) */ From 42cda5ad9e8542a48385eb2e0b7807773aa341f1 Mon Sep 17 00:00:00 2001 From: Lev Stipakov Date: Mon, 20 Feb 2023 11:06:01 +0200 Subject: [PATCH 028/229] Disable DCO if proxy is set via management DCO doesn't support proxy and we already disable DCO is proxy is set in profile. Signed-off-by: Lev Stipakov Acked-by: Antonio Quartulli Message-Id: <20230220090601.983-1-lstipakov@gmail.com> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26287.html Signed-off-by: Gert Doering (cherry picked from commit 45a1cb2ad85a50feded10dad706132bd8a7f6133) --- src/openvpn/init.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/openvpn/init.c b/src/openvpn/init.c index b500d354386..622239f6b10 100644 --- a/src/openvpn/init.c +++ b/src/openvpn/init.c @@ -223,6 +223,12 @@ management_callback_proxy_cmd(void *arg, const char **p) } else if (p[2] && p[3]) { + if (dco_enabled(&c->options)) + { + msg(M_INFO, "Proxy set via management, disabling Data Channel Offload."); + c->options.tuntap_options.disable_dco = true; + } + if (streq(p[1], "HTTP")) { struct http_proxy_options *ho; From f63c9b1edbda41491ba2e05ff706bf0233903cb6 Mon Sep 17 00:00:00 2001 From: Frank Lichtenheld Date: Wed, 15 Feb 2023 17:26:54 +0100 Subject: [PATCH 029/229] configure: enable DCO by default on FreeBSD/Linux Automatically disabled when - iproute2 is enabled (Don't want to force people specifying --disable-dco explicitely) - libnv is missing on FreeBSD (FreeBSD version too old anyway) Will still error out if libnl-genl is missing on Linux to make people aware of new dependency. v2: error out when libnl-genl is missing as discussed with ordex on IRC. v3: - improvements to the messages, suggested by Selva - further improvements to the default specification, trying to make it clear - if enabling iproute2, do not test for libnl-genl v4: add updates for GHA v5: - v4 was missing the changes of v3. v5 combines the changes from v3 and v4 - fix build failure GHA/ubuntu1804/mbedtls - fix build failure GHA/ubuntu2204/libressl Signed-off-by: Frank Lichtenheld Acked-by: Selva Nair Acked-by: Antonio Quartulli Message-Id: <20230215162654.52137-1-frank@lichtenheld.com> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26272.html Signed-off-by: Gert Doering (cherry picked from commit 2a1a21e3da3ec66d901864edd7b3b0e2498b3b0a) --- .github/workflows/build.yaml | 22 +++++----- configure.ac | 82 +++++++++++++++++++++++++++--------- 2 files changed, 74 insertions(+), 30 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 699964fd018..162dd8ce8f1 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -265,20 +265,27 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-18.04, ubuntu-20.04, ubuntu-22.04] + os: [ubuntu-20.04, ubuntu-22.04] sslpkg: [libmbedtls-dev] ssllib: [mbedtls] libname: [mbed TLS] include: + - os: ubuntu-18.04 + sslpkg: "libmbedtls-dev" + ssllib: mbedtls + libname: mbed TLS + extraconf: "--disable-dco" - os: ubuntu-18.04 sslpkg: "libssl1.0-dev" ssllib: openssl libname: OpenSSL 1.0.2 + extraconf: "--disable-dco" - os: ubuntu-18.04 sslpkg: "libssl-dev" libname: OpenSSL 1.1.1 ssllib: openssl + extraconf: "--disable-dco" - os: ubuntu-20.04 sslpkg: "libssl-dev" libname: OpenSSL 1.1.1 @@ -312,22 +319,15 @@ jobs: libname: OpenSSL 1.1.1 ssllib: openssl extraconf: "--disable-lzo --disable-lz4" - - os: ubuntu-20.04 - sslpkg: "libssl-dev" - libname: OpenSSL 1.1.1 - ssllib: openssl - extraconf: "--enable-dco" - nlpkg: "libnl-genl-3-dev" name: "gcc - ${{matrix.os}} - ${{matrix.libname}} ${{matrix.extraconf}}" env: SSLPKG: "${{matrix.sslpkg}}" - NLPKG: "${{matrix.nlpkg}}" runs-on: ${{matrix.os}} steps: - name: Install dependencies - run: sudo apt update && sudo apt install -y liblzo2-dev libpam0g-dev liblz4-dev libcap-ng-dev linux-libc-dev man2html libcmocka-dev python3-docutils libtool automake autoconf ${SSLPKG} ${NLPKG} + run: sudo apt update && sudo apt install -y liblzo2-dev libpam0g-dev liblz4-dev libcap-ng-dev libnl-genl-3-dev linux-libc-dev man2html libcmocka-dev python3-docutils libtool automake autoconf ${SSLPKG} - name: Checkout OpenVPN uses: actions/checkout@v3 - name: autoconf @@ -354,7 +354,7 @@ jobs: runs-on: ${{matrix.os}} steps: - name: Install dependencies - run: sudo apt update && sudo apt install -y liblzo2-dev libpam0g-dev liblz4-dev libcap-ng-dev linux-libc-dev man2html clang libcmocka-dev python3-docutils libtool automake autoconf libmbedtls-dev + run: sudo apt update && sudo apt install -y liblzo2-dev libpam0g-dev liblz4-dev libcap-ng-dev libnl-genl-3-dev linux-libc-dev man2html clang libcmocka-dev python3-docutils libtool automake autoconf libmbedtls-dev - name: Checkout OpenVPN uses: actions/checkout@v3 - name: autoconf @@ -514,7 +514,7 @@ jobs: steps: - name: Install dependencies - run: sudo apt update && sudo apt install -y liblzo2-dev libpam0g-dev liblz4-dev linux-libc-dev man2html clang libcmocka-dev python3-docutils libtool automake autoconf pkg-config libcap-ng-dev + run: sudo apt update && sudo apt install -y liblzo2-dev libpam0g-dev liblz4-dev linux-libc-dev man2html clang libcmocka-dev python3-docutils libtool automake autoconf pkg-config libcap-ng-dev libnl-genl-3-dev - name: "libressl: checkout" uses: actions/checkout@v3 with: diff --git a/configure.ac b/configure.ac index 66ba6f38d87..c44d38568d8 100644 --- a/configure.ac +++ b/configure.ac @@ -157,14 +157,27 @@ AC_ARG_ENABLE( AC_ARG_ENABLE( [dco], - [AS_HELP_STRING([--enable-dco], [enable data channel offload support using the ovpn-dco kernel module (always enabled on Windows) @<:@default=no@:>@])], + [AS_HELP_STRING([--disable-dco], [disable data channel offload support using the ovpn-dco kernel module @<:@default=yes@:>@ on Linux/FreeBSD, can't disable on Windows])], , - [enable_dco="no"] + [ + case "$host" in + *-*-linux*) + enable_dco="auto" + ;; + *-*-freebsd*) + enable_dco="auto" + ;; + *) + # note that this does not disable it for Windows + enable_dco="no" + ;; + esac + ] ) AC_ARG_ENABLE( [iproute2], - [AS_HELP_STRING([--enable-iproute2], [enable support for iproute2 @<:@default=no@:>@])], + [AS_HELP_STRING([--enable-iproute2], [enable support for iproute2 (disables DCO) @<:@default=no@:>@])], , [enable_iproute2="no"] ) @@ -549,7 +562,7 @@ AC_CHECK_DECLS( , [[${SOCKET_INCLUDES}]] ) -AC_CHECKING([anonymous union support]) +AC_MSG_CHECKING([anonymous union support]) AC_COMPILE_IFELSE( [AC_LANG_PROGRAM( [[ @@ -784,28 +797,59 @@ PKG_CHECK_MODULES( ) -if test "$enable_dco" = "yes"; then -dnl -dnl Include generic netlink library used to talk to ovpn-dco -dnl +if test "$enable_dco" != "no"; then + enable_dco_arg="$enable_dco" + if test "${enable_iproute2}" = "yes"; then + AC_MSG_WARN([DCO cannot be enabled when using iproute2]) + enable_dco="no" + fi case "$host" in *-*-linux*) - PKG_CHECK_MODULES([LIBNL_GENL], + if test "$enable_dco" = "no"; then + if test "$enable_dco_arg" = "auto"; then + AC_MSG_WARN([DCO support disabled]) + else + AC_MSG_ERROR([DCO support can't be enabled]) + fi + else + dnl + dnl Include generic netlink library used to talk to ovpn-dco + dnl + PKG_CHECK_MODULES([LIBNL_GENL], [libnl-genl-3.0 >= 3.4.0], [have_libnl="yes"], - [AC_MSG_ERROR([libnl-genl-3.0 package not found or too old. Is the development package and pkg-config installed? Must be version 3.4.0 or newer])] - ) - - CFLAGS="${CFLAGS} ${LIBNL_GENL_CFLAGS}" - LIBS="${LIBS} ${LIBNL_GENL_LIBS}" + [ + AC_MSG_ERROR([libnl-genl-3.0 package not found or too old. Is the development package and pkg-config installed? Must be version 3.4.0 or newer for DCO]) + ] + ) + CFLAGS="${CFLAGS} ${LIBNL_GENL_CFLAGS}" + LIBS="${LIBS} ${LIBNL_GENL_LIBS}" - AC_DEFINE(ENABLE_DCO, 1, [Enable shared data channel offload]) - AC_MSG_NOTICE([Enabled ovpn-dco support for Linux]) + AC_DEFINE(ENABLE_DCO, 1, [Enable shared data channel offload]) + AC_MSG_NOTICE([Enabled ovpn-dco support for Linux]) + fi ;; *-*-freebsd*) - LIBS="${LIBS} -lnv" - AC_DEFINE(ENABLE_DCO, 1, [Enable data channel offload for FreeBSD]) - AC_MSG_NOTICE([Enabled ovpn-dco support for FreeBSD]) + AC_CHECK_LIB( + [nv], + [nvlist_create], + [ + LIBS="${LIBS} -lnv" + AC_DEFINE(ENABLE_DCO, 1, [Enable data channel offload for FreeBSD]) + AC_MSG_NOTICE([Enabled ovpn-dco support for FreeBSD]) + ], + [ + enable_dco="no" + AC_MSG_WARN([Name/Value pair library not found.]) + ] + ) + if test "$enable_dco" = "no"; then + if test "$enable_dco_arg" = "auto"; then + AC_MSG_WARN([DCO support disabled]) + else + AC_MSG_ERROR([DCO support can't be enabled]) + fi + fi ;; *-mingw*) AC_MSG_NOTICE([NOTE: --enable-dco ignored on Windows because it's always enabled]) From dff1e78957fa97b3258d5e1b0ef05ad3f853ada2 Mon Sep 17 00:00:00 2001 From: Lev Stipakov Date: Thu, 16 Feb 2023 18:01:29 +0200 Subject: [PATCH 030/229] Add logging for windows driver selection process Signed-off-by: Lev Stipakov Acked-by: Gert Doering Message-Id: <20230216160129.994-1-lstipakov@gmail.com> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26281.html Signed-off-by: Gert Doering (cherry picked from commit 5b748ad099ed69b893124e2805b5ddeb6b0ec8fc) --- src/openvpn/tun.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c index 20f80798a81..a1414d23f04 100644 --- a/src/openvpn/tun.c +++ b/src/openvpn/tun.c @@ -3705,6 +3705,7 @@ get_device_instance_id_interface(struct gc_arena *gc) msg(M_FATAL, "Error [%u] opening device information set key: %s", (unsigned int)err, strerror_win32(err, gc)); } + msg(D_TAP_WIN_DEBUG, "Enumerate device interface lists:"); for (DWORD i = 0;; ++i) { SP_DEVINFO_DATA device_info_data; @@ -3790,6 +3791,10 @@ get_device_instance_id_interface(struct gc_arena *gc) dev_iif->net_cfg_instance_id = (unsigned char *)string_alloc((char *)net_cfg_instance_id, gc); dev_iif->device_interface = string_alloc(dev_if, gc); + msg(D_TAP_WIN_DEBUG, "NetCfgInstanceId: %s, Device Interface: %s", + dev_iif->net_cfg_instance_id, + dev_iif->device_interface); + /* link into return list */ if (!first) { @@ -3835,6 +3840,7 @@ get_tap_reg(struct gc_arena *gc) msg(M_FATAL, "Error opening registry key: %s", ADAPTER_KEY); } + msg(D_TAP_WIN_DEBUG, "Enumerate drivers in registy: "); while (true) { char enum_name[256]; @@ -3942,6 +3948,9 @@ get_tap_reg(struct gc_arena *gc) last->next = reg; } last = reg; + + msg(D_TAP_WIN_DEBUG, "NetCfgInstanceId: %s, Driver: %s", + reg->guid, print_windows_driver(reg->windows_driver)); } } } @@ -6520,6 +6529,8 @@ tun_try_open_device(struct tuntap *tt, const char *device_guid, const struct dev path = tuntap_device_path; } + msg(D_TAP_WIN_DEBUG, "Using device interface: %s", path); + tt->hand = CreateFile(path, GENERIC_READ | GENERIC_WRITE, 0, /* was: FILE_SHARE_READ */ @@ -6529,7 +6540,7 @@ tun_try_open_device(struct tuntap *tt, const char *device_guid, const struct dev 0); if (tt->hand == INVALID_HANDLE_VALUE) { - msg(D_TUNTAP_INFO, "CreateFile failed on %s device: %s", print_windows_driver(tt->windows_driver), path); + msg(D_TUNTAP_INFO | M_ERRNO, "CreateFile failed on %s device: %s", print_windows_driver(tt->windows_driver), path); return false; } From 094aea56ce20d0bb6fe79e6e14a3dfe68ea11786 Mon Sep 17 00:00:00 2001 From: Selva Nair Date: Tue, 14 Feb 2023 15:08:04 -0500 Subject: [PATCH 031/229] Add a unit test for functions in cryptoapi.c - Though named cryptoapi_testdriver, right now this only tests parsing of thumbprint specified as a selector for --cryptioapicert option. More tests coming.. v2: a line that belongs here was mistakenly included in the previous commit. Corrected. v3: add to list of tests run in github actions v4: - correct comment above invalid strings (copy paste error) - make invalid strings differ from correct value only in the explicitly introduced invalid characters/separators (one had two distinct errors which is not a robust test). Signed-off-by: Selva Nair Acked-by: Gert Doering Message-Id: <20230214200804.600405-1-selva.nair@gmail.com> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26268.html Signed-off-by: Gert Doering (cherry picked from commit 8aff5655a51d9f9f67ca31b363d4ebaf5342d410) --- .github/workflows/build.yaml | 3 + tests/unit_tests/openvpn/Makefile.am | 16 +++ tests/unit_tests/openvpn/test_cryptoapi.c | 126 ++++++++++++++++++++++ 3 files changed, 145 insertions(+) create mode 100644 tests/unit_tests/openvpn/test_cryptoapi.c diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 162dd8ce8f1..a3ca7a2ea03 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -243,6 +243,9 @@ jobs: - name: Run bufferunit test run: ./unittests/buffer_testdriver.exe + - name: Run cryptoapi unit test + run: ./unittests/cryptoapi_testdriver.exe + - name: Run cryptounit test run: ./unittests/crypto_testdriver.exe diff --git a/tests/unit_tests/openvpn/Makefile.am b/tests/unit_tests/openvpn/Makefile.am index 8d2386e08bf..ee0a3d8aaa1 100644 --- a/tests/unit_tests/openvpn/Makefile.am +++ b/tests/unit_tests/openvpn/Makefile.am @@ -17,6 +17,7 @@ endif test_binaries += provider_testdriver if WIN32 +test_binaries += cryptoapi_testdriver LDADD = -lws2_32 endif @@ -152,6 +153,21 @@ provider_testdriver_SOURCES = test_provider.c mock_msg.c \ $(openvpn_srcdir)/win32-util.c \ $(openvpn_srcdir)/platform.c +if WIN32 +cryptoapi_testdriver_CFLAGS = @TEST_CFLAGS@ \ + -I$(openvpn_includedir) -I$(compat_srcdir) -I$(openvpn_srcdir) \ + $(OPTIONAL_CRYPTO_CFLAGS) +cryptoapi_testdriver_LDFLAGS = @TEST_LDFLAGS@ \ + $(OPTIONAL_CRYPTO_LIBS) -lcrypt32 -lncrypt +cryptoapi_testdriver_SOURCES = test_cryptoapi.c mock_msg.c \ + $(openvpn_srcdir)/xkey_helper.c \ + $(openvpn_srcdir)/buffer.c \ + $(openvpn_srcdir)/base64.c \ + $(openvpn_srcdir)/platform.c \ + mock_get_random.c \ + $(openvpn_srcdir)/win32-util.c +endif + auth_token_testdriver_CFLAGS = @TEST_CFLAGS@ \ -I$(openvpn_includedir) -I$(compat_srcdir) -I$(openvpn_srcdir) \ $(OPTIONAL_CRYPTO_CFLAGS) diff --git a/tests/unit_tests/openvpn/test_cryptoapi.c b/tests/unit_tests/openvpn/test_cryptoapi.c new file mode 100644 index 00000000000..73ef34e991c --- /dev/null +++ b/tests/unit_tests/openvpn/test_cryptoapi.c @@ -0,0 +1,126 @@ +/* + * OpenVPN -- An application to securely tunnel IP networks + * over a single UDP port, with support for SSL/TLS-based + * session authentication and key exchange, + * packet encryption, packet authentication, and + * packet compression. + * + * Copyright (C) 2023 Selva Nair + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 2 of the License, + * or (at your option) any later version. + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#elif defined(_MSC_VER) +#include "config-msvc.h" +#endif + +#include "syshead.h" +#include "manage.h" +#include "integer.h" +#include "xkey_common.h" + +#if defined(HAVE_XKEY_PROVIDER) && defined (ENABLE_CRYPTOAPI) +#include +#include +#include +#include +#include +#include + +#include +#include /* pull-in the whole file to test static functions */ + +struct management *management; /* global */ + +/* mock a management function that xkey_provider needs */ +char * +management_query_pk_sig(struct management *man, const char *b64_data, + const char *algorithm) +{ + (void) man; + (void) b64_data; + (void) algorithm; + return NULL; +} + +/* tls_libctx is defined in ssl_openssl.c which we do not want to compile in */ +OSSL_LIB_CTX *tls_libctx; + +#ifndef _countof +#define _countof(x) sizeof((x))/sizeof(*(x)) +#endif + +/* test data */ +static const uint8_t test_hash[] = { + 0x77, 0x38, 0x65, 0x00, 0x1e, 0x96, 0x48, 0xc6, 0x57, 0x0b, 0xae, + 0xc0, 0xb7, 0x96, 0xf9, 0x66, 0x4d, 0x5f, 0xd0, 0xb7 +}; + +/* valid test strings to test with and without embedded and trailing spaces */ +static const char *valid_str[] = { + "773865001e9648c6570baec0b796f9664d5fd0b7", + " 77 386500 1e 96 48 c6570b aec0b7 96f9664d5f d0 b7", + " 773865001e9648c6570baec0b796f9664d5fd0b7 ", +}; + +/* some invalid strings to test */ +static const char *invalid_str[] = { + "773 865001e9648c6570baec0b796f9664d5fd0b7", /* space within byte */ + "77:38:65001e9648c6570baec0b796f9664d5fd0b7", /* invalid separator */ + "7738x5001e9648c6570baec0b796f9664d5fd0b7", /* non hex character */ +}; + +static void +test_parse_hexstring(void **state) +{ + unsigned char hash[255]; + (void) state; + + for (int i = 0; i < _countof(valid_str); i++) + { + int len = parse_hexstring(valid_str[i], hash, _countof(hash)); + assert_int_equal(len, sizeof(test_hash)); + assert_memory_equal(hash, test_hash, sizeof(test_hash)); + memset(hash, 0, _countof(hash)); + } + + for (int i = 0; i < _countof(invalid_str); i++) + { + int len = parse_hexstring(invalid_str[i], hash, _countof(hash)); + assert_int_equal(len, 0); + } +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { cmocka_unit_test(test_parse_hexstring) }; + + int ret = cmocka_run_group_tests_name("cryptoapi tests", tests, NULL, NULL); + + return ret; +} + +#else /* ifdef HAVE_XKEY_PROVIDER */ + +int +main(void) +{ + return 0; +} + +#endif /* ifdef HAVE_XKEY_PROVIDER */ From e9ae7cee2ca9b9ab78de01f1d3c562610e5624d2 Mon Sep 17 00:00:00 2001 From: Frank Lichtenheld Date: Fri, 3 Feb 2023 20:14:40 +0100 Subject: [PATCH 032/229] Windows: fix signedness errors with recv/send On Linux those functions actually take void pointer, so no behavior change there. On Windows, we avoid warnings about unsigned char vs char. Signed-off-by: Frank Lichtenheld Acked-by: Gert Doering Message-Id: <20230203191440.136050-6-frank@lichtenheld.com> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26144.html Signed-off-by: Gert Doering (cherry picked from commit 7acd93a6bef2dd9b660571c29b5f41c8ca351161) --- src/openvpn/manage.c | 4 ++-- src/openvpn/proxy.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/openvpn/manage.c b/src/openvpn/manage.c index a55e5d78823..db88e347911 100644 --- a/src/openvpn/manage.c +++ b/src/openvpn/manage.c @@ -2255,7 +2255,7 @@ man_read(struct management *man) man->connection.lastfdreceived = fd; } #else /* ifdef TARGET_ANDROID */ - len = recv(man->connection.sd_cli, buf, sizeof(buf), MSG_NOSIGNAL); + len = recv(man->connection.sd_cli, (void *)buf, sizeof(buf), MSG_NOSIGNAL); #endif if (len == 0) @@ -2352,7 +2352,7 @@ man_write(struct management *man) } else #endif - sent = send(man->connection.sd_cli, BPTR(buf), len, MSG_NOSIGNAL); + sent = send(man->connection.sd_cli, (const void *)BPTR(buf), len, MSG_NOSIGNAL); if (sent >= 0) { buffer_list_advance(man->connection.out, sent); diff --git a/src/openvpn/proxy.c b/src/openvpn/proxy.c index aa10363ce7f..ed47eaa20c5 100644 --- a/src/openvpn/proxy.c +++ b/src/openvpn/proxy.c @@ -126,7 +126,7 @@ recv_line(socket_descriptor_t sd, } /* read single char */ - size = recv(sd, &c, 1, MSG_NOSIGNAL); + size = recv(sd, (void *)&c, 1, MSG_NOSIGNAL); /* error? */ if (size != 1) From 91da6b9463745635b75df171678ab54b5dcdb0de Mon Sep 17 00:00:00 2001 From: Frank Lichtenheld Date: Mon, 6 Feb 2023 14:08:46 +0100 Subject: [PATCH 033/229] configure: fix formatting of --disable-lz4 and --enable-comp-stub Make consistent with the other options. Signed-off-by: Frank Lichtenheld Acked-by: Gert Doering Message-Id: <20230206130846.63415-1-frank@lichtenheld.com> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26156.html Signed-off-by: Gert Doering (cherry picked from commit 24e1d8ff87b189247d56261a9497b1f509b286df) --- configure.ac | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/configure.ac b/configure.ac index c44d38568d8..4c271464b6b 100644 --- a/configure.ac +++ b/configure.ac @@ -80,14 +80,16 @@ AC_ARG_ENABLE( [enable_lzo="yes"] ) -AC_ARG_ENABLE(lz4, - [ --disable-lz4 Disable LZ4 compression support], +AC_ARG_ENABLE( + [lz4], + [AS_HELP_STRING([--disable-lz4], [disable LZ4 compression support @<:@default=yes@:>@])], [enable_lz4="$enableval"], [enable_lz4="yes"] ) -AC_ARG_ENABLE(comp-stub, - [ --enable-comp-stub Don't compile compression support but still allow limited interoperability with compression-enabled peers], +AC_ARG_ENABLE( + [comp-stub], + [AS_HELP_STRING([--enable-comp-stub], [disable compression support but still allow limited interoperability with compression-enabled peers @<:@default=no@:>@])], [enable_comp_stub="$enableval"], [enable_comp_stub="no"] ) From c26609bdcd75da5702befbdfa5d0136a7787e52e Mon Sep 17 00:00:00 2001 From: Lev Stipakov Date: Fri, 17 Feb 2023 14:21:55 +0200 Subject: [PATCH 034/229] Avoid management log loop with verb >= 6 This log message is printed within check_tls(), which is called by pre_select(), which is called on every iteration of event loop. When management is attached (and doesn't use own event loop), this message sets management state to "wait write", which arms event loop. When on the next iteration iowait returns with "management write event is set", we call pre_select() and print that message again, causing the loop. Fix by simply removing this log message. Signed-off-by: Lev Stipakov Acked-by: Antonio Quartulli Message-Id: <20230217122156.541-1-lstipakov@gmail.com> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26284.html Signed-off-by: Gert Doering (cherry picked from commit b8eddda8524bf6f164361667bfce6bbb3fac846b) --- src/openvpn/dco.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/openvpn/dco.c b/src/openvpn/dco.c index 3087a0df8f9..b53332a8777 100644 --- a/src/openvpn/dco.c +++ b/src/openvpn/dco.c @@ -133,8 +133,6 @@ dco_get_secondary_key(struct tls_multi *multi, const struct key_state *primary) bool dco_update_keys(dco_context_t *dco, struct tls_multi *multi) { - msg(D_DCO_DEBUG, "%s: peer_id=%d", __func__, multi->dco_peer_id); - /* this function checks if keys have to be swapped or erased, therefore it * can't do much if we don't have any key installed */ From 27dac5061cfeff75470dca11e07dadb1fb0ad180 Mon Sep 17 00:00:00 2001 From: Arne Schwabe Date: Tue, 14 Feb 2023 14:43:23 +0100 Subject: [PATCH 035/229] Use proper print format/casting when converting msg_channel handle The current casting triggers a warning on 32bit: init.c:1842:66: error: cast from pointer to integer of different size [-Werror=pointer-to-int-cast] Use the proper printf format specifier for printing a pointer avoiding the cast alltogether. In options.c use a cast to intptr_t before converting to a handle to avoid having to ifdef atoll/atol for 32/64 bit. Signed-off-by: Arne Schwabe Acked-by: Lev Stipakov Message-Id: <20230214134323.1033590-1-arne@rfc2549.org> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26255.html Signed-off-by: Gert Doering (cherry picked from commit 9c52e0c610ef1229561c2d038ca41fe2cbefe8da) --- src/openvpn/init.c | 3 ++- src/openvpn/options.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/openvpn/init.c b/src/openvpn/init.c index 622239f6b10..32211f11e53 100644 --- a/src/openvpn/init.c +++ b/src/openvpn/init.c @@ -1850,7 +1850,8 @@ do_open_tun(struct context *c, int *error_flags) #ifdef _WIN32 /* store (hide) interactive service handle in tuntap_options */ c->c1.tuntap->options.msg_channel = c->options.msg_channel; - msg(D_ROUTE, "interactive service msg_channel=%" PRIu64, (unsigned long long) c->options.msg_channel); + msg(D_ROUTE, "interactive service msg_channel=%" PRIuPTR, + (intptr_t) c->options.msg_channel); #endif /* allocate route list structure */ diff --git a/src/openvpn/options.c b/src/openvpn/options.c index 9105449c7f8..2e41eea4e2c 100644 --- a/src/openvpn/options.c +++ b/src/openvpn/options.c @@ -7876,7 +7876,7 @@ add_option(struct options *options, #ifdef _WIN32 VERIFY_PERMISSION(OPT_P_GENERAL); HANDLE process = GetCurrentProcess(); - HANDLE handle = (HANDLE) atoll(p[1]); + HANDLE handle = (HANDLE) ((intptr_t) atoll(p[1])); if (!DuplicateHandle(process, handle, process, &options->msg_channel, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) { From b9a9de156bc3ad517bfc6d1042ad0ef0350b638e Mon Sep 17 00:00:00 2001 From: Kristof Provost Date: Wed, 1 Mar 2023 10:18:51 +0100 Subject: [PATCH 036/229] options.c: enforce a minimal fragment size Very low values for 'fragment' can result in a division by zero in optimal_fragment_size() (because it rounds max_frag_size down with FRAG_SIZE_ROUND_MASK). Enforce a minimal fragment size of 68 bytes, based on RFC 791 ("Every internet module must be able to forward a datagram of 68 octets without further fragmentation.") Signed-off-by: Kristof Provost Acked-by: Gert Doering Message-Id: <20230301091851.82243-1-kprovost@netgate.com> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26313.html Signed-off-by: Gert Doering (cherry picked from commit 78e504210add19343e65f5c5b80be9ea6e9e95ab) --- src/openvpn/options.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/openvpn/options.c b/src/openvpn/options.c index 2e41eea4e2c..45d0e0fa8f3 100644 --- a/src/openvpn/options.c +++ b/src/openvpn/options.c @@ -6549,6 +6549,12 @@ add_option(struct options *options, VERIFY_PERMISSION(OPT_P_MTU|OPT_P_CONNECTION); options->ce.fragment = positive_atoi(p[1]); + if (options->ce.fragment < 68) + { + msg(msglevel, "--fragment needs to be at least 68"); + goto err; + } + if (p[2] && streq(p[2], "mtu")) { options->ce.fragment_encap = true; From 86fb085b6d2582916ef59b4bd8bd5e4a072964a3 Mon Sep 17 00:00:00 2001 From: Kristof Provost Date: Wed, 1 Mar 2023 10:18:48 +0100 Subject: [PATCH 037/229] configure: improve FreeBSD DCO check The libnv check doesn't work as expected on FreeBSD 14.x, because FreeBSD has namespaced libnv to avoid conflicts with libnvpair. This means that the naive check generated by AC_CHECK_LIB() fails to detect libnv even though it's present. Instead check for the if_ovpn.h header. This is a more accurate check anyway, as libnv is present on FreeBSD versions prior to 14 (which do not support DCO). Signed-off-by: Kristof Provost Acked-by: Gert Doering Message-Id: <20230301091848.80760-1-kprovost@netgate.com> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26314.html Signed-off-by: Gert Doering (cherry picked from commit 6f261673dee26ae8cfdf58f77038098d4f81d84a) --- configure.ac | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/configure.ac b/configure.ac index 4c271464b6b..67f680b28e9 100644 --- a/configure.ac +++ b/configure.ac @@ -832,9 +832,7 @@ if test "$enable_dco" != "no"; then fi ;; *-*-freebsd*) - AC_CHECK_LIB( - [nv], - [nvlist_create], + AC_CHECK_HEADERS([net/if_ovpn.h], [ LIBS="${LIBS} -lnv" AC_DEFINE(ENABLE_DCO, 1, [Enable data channel offload for FreeBSD]) @@ -842,7 +840,7 @@ if test "$enable_dco" != "no"; then ], [ enable_dco="no" - AC_MSG_WARN([Name/Value pair library not found.]) + AC_MSG_WARN([DCO header not found.]) ] ) if test "$enable_dco" = "no"; then From 7fdf3e7ad711e1583e960798848c7d7f94e1ad8a Mon Sep 17 00:00:00 2001 From: Kristof Provost Date: Fri, 3 Mar 2023 12:05:11 +0100 Subject: [PATCH 038/229] dco: define OVPN_DEL_PEER_REASON_TRANSPORT_DISCONNECT on FreeBSD FreeBSD's if_ovpn will never emit this as a peer deletion reason (because it doesn't support TCP), but this allows us to align the defines between Linux and FreeBSD, and remove a Linux-specific case from process_incoming_del_peer(). Signed-off-by: Kristof Provost Acked-by: Antonio Quartulli Message-Id: <20230303110511.9569-1-kprovost@netgate.com> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26324.html Signed-off-by: Gert Doering (cherry picked from commit 155cf11531e619cf24b2aa9d0acb4ff834b2e8fa) --- src/openvpn/dco_freebsd.h | 1 + src/openvpn/multi.c | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/openvpn/dco_freebsd.h b/src/openvpn/dco_freebsd.h index 2e35f3acecf..970beca0e74 100644 --- a/src/openvpn/dco_freebsd.h +++ b/src/openvpn/dco_freebsd.h @@ -41,6 +41,7 @@ enum ovpn_del_reason_t { OVPN_DEL_PEER_REASON_EXPIRED, OVPN_DEL_PEER_REASON_TRANSPORT_ERROR, OVPN_DEL_PEER_REASON_USERSPACE, + OVPN_DEL_PEER_REASON_TRANSPORT_DISCONNECT, }; typedef struct dco_context { diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c index f255901689f..99123c3963a 100644 --- a/src/openvpn/multi.c +++ b/src/openvpn/multi.c @@ -3244,12 +3244,9 @@ process_incoming_del_peer(struct multi_context *m, struct multi_instance *mi, reason = "ovpn-dco: transport error"; break; -#ifdef TARGET_LINUX - /* FIXME: this is linux-only today and breaks FreeBSD compilation */ case OVPN_DEL_PEER_REASON_TRANSPORT_DISCONNECT: reason = "ovpn-dco: transport disconnected"; break; -#endif case OVPN_DEL_PEER_REASON_USERSPACE: /* We assume that is ourselves. Unfortunately, sometimes these From 7538557108f6add04f835eb161cb0a33610c6b14 Mon Sep 17 00:00:00 2001 From: Arne Schwabe Date: Tue, 14 Feb 2023 12:20:44 +0100 Subject: [PATCH 039/229] Reduce initialisation spam from verb <= 3 and print summary instead The messages about cipher initialisation are currently very noisy, especially if tls-auth/tls-crypt is in use. Typically messages like this is display for AES-256-CBC with SHA256: Outgoing Data Channel: Cipher 'AES-256-CBC' initialized with 256 bit key Outgoing Data Channel: Using 256 bit message hash 'SHA256' for HMAC authentication Incoming Data Channel: Cipher 'AES-256-CBC' initialized with 256 bit key Incoming Data Channel: Using 256 bit message hash 'SHA256' for HMAC authentication in addition to the tls-crypt/tls-auth messages that has the amount of messages. These message are not that helpful. The only meaningful information is better suited in compat messages. This commit moves the spammy messages to verb 4 and consistently prints out the cipher/auth used in the data channel instead on verb 2: Data Channel: cipher 'AES-256-CBC' auth 'SHA256' This patches also summarises other aspects of the imported options for VPN connection and prints them (even if not coming from pulled options): Data Channel: cipher 'AES-256-GCM' Timers: ping 8, ping-restart 40 Protocol options: explicit-exit-notify 1, protocol-flags tls-ekm And move the OPTIONS IMPORT: xx modified that are included in the new messages to D_PUSH_DEBUG (verb 7) since they do not add any useful information anymore. Patch v2: also compile with compression disabled Signed-off-by: Arne Schwabe Acked-by: Gert Doering Message-Id: <20230214112044.1021962-1-arne@rfc2549.org> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26249.html Signed-off-by: Gert Doering (cherry picked from commit c333a0c05f9d454efb38fc04c1dca1413bfc48ff) --- src/openvpn/crypto.c | 4 +- src/openvpn/errlevel.h | 1 + src/openvpn/init.c | 145 +++++++++++++++++++++++++++++++++++++++-- src/openvpn/occ.h | 16 +++++ src/openvpn/sig.c | 15 ----- src/openvpn/ssl.c | 5 -- src/openvpn/ssl_ncp.c | 2 +- 7 files changed, 161 insertions(+), 27 deletions(-) diff --git a/src/openvpn/crypto.c b/src/openvpn/crypto.c index 073f47e4715..5e1c495b0a1 100644 --- a/src/openvpn/crypto.c +++ b/src/openvpn/crypto.c @@ -835,7 +835,7 @@ init_key_ctx(struct key_ctx *ctx, const struct key *key, cipher_ctx_init(ctx->cipher, key->cipher, kt->cipher, enc); const char *ciphername = cipher_kt_name(kt->cipher); - msg(D_HANDSHAKE, "%s: Cipher '%s' initialized with %d bit key", + msg(D_CIPHER_INIT, "%s: Cipher '%s' initialized with %d bit key", prefix, ciphername, cipher_kt_key_size(kt->cipher) * 8); dmsg(D_SHOW_KEYS, "%s: CIPHER KEY: %s", prefix, @@ -850,7 +850,7 @@ init_key_ctx(struct key_ctx *ctx, const struct key *key, ctx->hmac = hmac_ctx_new(); hmac_ctx_init(ctx->hmac, key->hmac, kt->digest); - msg(D_HANDSHAKE, + msg(D_CIPHER_INIT, "%s: Using %d bit message hash '%s' for HMAC authentication", prefix, md_kt_size(kt->digest) * 8, md_kt_name(kt->digest)); diff --git a/src/openvpn/errlevel.h b/src/openvpn/errlevel.h index c69ea91d6cc..4699d1ac2f3 100644 --- a/src/openvpn/errlevel.h +++ b/src/openvpn/errlevel.h @@ -105,6 +105,7 @@ #define D_MTU_INFO LOGLEV(4, 61, 0) /* show terse MTU info */ #define D_PID_DEBUG_LOW LOGLEV(4, 63, 0) /* show low-freq packet-id debugging info */ #define D_PID_DEBUG_MEDIUM LOGLEV(4, 64, 0) /* show medium-freq packet-id debugging info */ +#define D_CIPHER_INIT LOGLEV(4, 65, 0) /* show messages about cipher init */ #define D_LOG_RW LOGLEV(5, 0, 0) /* Print 'R' or 'W' to stdout for read/write */ diff --git a/src/openvpn/init.c b/src/openvpn/init.c index 32211f11e53..7535c54ac98 100644 --- a/src/openvpn/init.c +++ b/src/openvpn/init.c @@ -2216,6 +2216,141 @@ p2p_set_dco_keepalive(struct context *c) } return true; } + +/** + * Helper function for tls_print_deferred_options_results + * Adds the ", " delimitor if there already some data in the + * buffer. + */ +static void +add_delim_if_non_empty(struct buffer *buf, const char *header) +{ + if (buf_len(buf) > strlen(header)) + { + buf_printf(buf, ", "); + } +} + + +/** + * Prints the results of options imported for the data channel + * @param o + */ +static void +tls_print_deferred_options_results(struct context *c) +{ + struct options *o = &c->options; + + struct buffer out; + uint8_t line[1024] = { 0 }; + buf_set_write(&out, line, sizeof(line)); + + + if (cipher_kt_mode_aead(o->ciphername)) + { + buf_printf(&out, "Data Channel: cipher '%s'", + cipher_kt_name(o->ciphername)); + } + else + { + buf_printf(&out, "Data Channel: cipher '%s', auth '%s'", + cipher_kt_name(o->ciphername), md_kt_name(o->authname)); + } + + if (o->use_peer_id) + { + buf_printf(&out, ", peer-id: %d", o->peer_id); + } + +#ifdef USE_COMP + if (c->c2.comp_context) + { + buf_printf(&out, ", compression: '%s'", c->c2.comp_context->alg.name); + } +#endif + + msg(D_HANDSHAKE, "%s", BSTR(&out)); + + buf_clear(&out); + + const char *header = "Timers: "; + + buf_printf(&out, "%s", header); + + if (o->ping_send_timeout) + { + buf_printf(&out, "ping %d", o->ping_send_timeout); + } + + if (o->ping_rec_timeout_action != PING_UNDEF) + { + /* yes unidirectional ping is possible .... */ + add_delim_if_non_empty(&out, header); + + if (o->ping_rec_timeout_action == PING_EXIT) + { + buf_printf(&out, "ping-exit %d", o->ping_rec_timeout); + } + else + { + buf_printf(&out, "ping-restart %d", o->ping_rec_timeout); + } + } + + if (o->inactivity_timeout) + { + add_delim_if_non_empty(&out, header); + + buf_printf(&out, "inactive %d", o->inactivity_timeout); + if (o->inactivity_minimum_bytes) + { + buf_printf(&out, " %" PRIu64, o->inactivity_minimum_bytes); + } + } + + if (o->session_timeout) + { + add_delim_if_non_empty(&out, header); + buf_printf(&out, "session-timeout %d", o->session_timeout); + } + + if (buf_len(&out) > strlen(header)) + { + msg(D_HANDSHAKE, "%s", BSTR(&out)); + } + + buf_clear(&out); + header = "Protocol options: "; + buf_printf(&out, "%s", header); + + if (c->options.ce.explicit_exit_notification) + { + buf_printf(&out, "explicit-exit-notify %d", + c->options.ce.explicit_exit_notification); + } + if (c->options.imported_protocol_flags) + { + add_delim_if_non_empty(&out, header); + + buf_printf(&out, "protocol-flags"); + + if (o->imported_protocol_flags & CO_USE_CC_EXIT_NOTIFY) + { + buf_printf(&out, " cc-exit"); + } + if (o->imported_protocol_flags & CO_USE_TLS_KEY_MATERIAL_EXPORT) + { + buf_printf(&out, " tls-ekm"); + } + } + + if (buf_len(&out) > strlen(header)) + { + msg(D_HANDSHAKE, "%s", BSTR(&out)); + } +} + + /** * This function is expected to be invoked after open_tun() was performed. * @@ -2377,6 +2512,8 @@ do_up(struct context *c, bool pulled_options, unsigned int option_types_found) initialization_sequence_completed(c, error_flags); /* client/p2p restart with --persist-tun */ } + tls_print_deferred_options_results(c); + c->c2.do_up_ran = true; if (c->c2.tls_multi) { @@ -2477,7 +2614,7 @@ do_deferred_options(struct context *c, const unsigned int found) if (found & OPT_P_TIMER) { do_init_timers(c, true); - msg(D_PUSH, "OPTIONS IMPORT: timers and/or timeouts modified"); + msg(D_PUSH_DEBUG, "OPTIONS IMPORT: timers and/or timeouts modified"); } if (found & OPT_P_EXPLICIT_NOTIFY) @@ -2489,14 +2626,14 @@ do_deferred_options(struct context *c, const unsigned int found) } else { - msg(D_PUSH, "OPTIONS IMPORT: explicit notify parm(s) modified"); + msg(D_PUSH_DEBUG, "OPTIONS IMPORT: explicit notify parm(s) modified"); } } #ifdef USE_COMP if (found & OPT_P_COMP) { - msg(D_PUSH, "OPTIONS IMPORT: compression parms modified"); + msg(D_PUSH_DEBUG, "OPTIONS IMPORT: compression parms modified"); comp_uninit(c->c2.comp_context); c->c2.comp_context = comp_init(&c->options.comp); } @@ -2547,7 +2684,7 @@ do_deferred_options(struct context *c, const unsigned int found) if (found & OPT_P_PEER_ID) { - msg(D_PUSH, "OPTIONS IMPORT: peer-id set"); + msg(D_PUSH_DEBUG, "OPTIONS IMPORT: peer-id set"); c->c2.tls_multi->use_peer_id = true; c->c2.tls_multi->peer_id = c->options.peer_id; } diff --git a/src/openvpn/occ.h b/src/openvpn/occ.h index 4320bd11981..e382482f6d6 100644 --- a/src/openvpn/occ.h +++ b/src/openvpn/occ.h @@ -153,4 +153,20 @@ check_send_occ_msg(struct context *c) } } +/** + * Small helper function to determine if we should send the exit notification + * via control channel. + * @return control channel exit message should be used */ +static inline bool +cc_exit_notify_enabled(struct context *c) +{ + /* Check if we have TLS active at all */ + if (!c->c2.tls_multi) + { + return false; + } + + const struct key_state *ks = get_primary_key(c->c2.tls_multi); + return (ks->crypto_options.flags & CO_USE_CC_EXIT_NOTIFY); +} #endif /* ifndef OCC_H */ diff --git a/src/openvpn/sig.c b/src/openvpn/sig.c index 0d534601014..5b89bb42b9e 100644 --- a/src/openvpn/sig.c +++ b/src/openvpn/sig.c @@ -342,21 +342,6 @@ print_status(const struct context *c, struct status_output *so) } -/* Small helper function to determine if we should send the exit notification - * via control channel */ -static inline bool -cc_exit_notify_enabled(struct context *c) -{ - /* Check if we have TLS active at all */ - if (!c->c2.tls_multi) - { - return false; - } - - const struct key_state *ks = get_primary_key(c->c2.tls_multi); - return (ks->crypto_options.flags & CO_USE_CC_EXIT_NOTIFY); -} - /* * Handle the triggering and time-wait of explicit * exit notification. diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c index 016bdc57f7c..47f3702b25e 100644 --- a/src/openvpn/ssl.c +++ b/src/openvpn/ssl.c @@ -1730,11 +1730,6 @@ tls_session_update_crypto_params_do_work(struct tls_multi *multi, return true; } - if (strcmp(options->ciphername, session->opt->config_ciphername)) - { - msg(D_HANDSHAKE, "Data Channel: using negotiated cipher '%s'", - options->ciphername); - } init_key_type(&session->opt->key_type, options->ciphername, options->authname, true, true); diff --git a/src/openvpn/ssl_ncp.c b/src/openvpn/ssl_ncp.c index fe6f6fa7c5e..97619be5ef3 100644 --- a/src/openvpn/ssl_ncp.c +++ b/src/openvpn/ssl_ncp.c @@ -318,7 +318,7 @@ check_pull_client_ncp(struct context *c, const int found) { if (found & OPT_P_NCP) { - msg(D_PUSH, "OPTIONS IMPORT: data channel crypto options modified"); + msg(D_PUSH_DEBUG, "OPTIONS IMPORT: data channel crypto options modified"); return true; } From 85ad9d2520d571dff93d2b15002f151c10000804 Mon Sep 17 00:00:00 2001 From: Selva Nair Date: Mon, 6 Mar 2023 00:33:45 -0500 Subject: [PATCH 040/229] Do not save pointer to 'struct passwd' returned by getpwnam etc. - This pointer is to a static area which can change on further calls to getpwnam, getpwuid etc. Same with struct group returned by getgrnam. As the only field later referred to is uid or gid, fix by saving them instead. Signed-off-by: Selva Nair Acked-by: Gert Doering Message-Id: <20230306053346.796992-1-selva.nair@gmail.com> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26332.html Signed-off-by: Gert Doering (cherry picked from commit 62024046dffd6ff10309b791cd6600fe80bc46e3) --- src/openvpn/platform.c | 36 +++++++++++++++++++++++------------- src/openvpn/platform.h | 14 ++++---------- src/openvpn/tun.c | 4 ++-- 3 files changed, 29 insertions(+), 25 deletions(-) diff --git a/src/openvpn/platform.c b/src/openvpn/platform.c index 580c4cb8faa..f6b856f3bb3 100644 --- a/src/openvpn/platform.c +++ b/src/openvpn/platform.c @@ -85,11 +85,16 @@ platform_user_get(const char *username, struct platform_state_user *state) if (username) { #if defined(HAVE_GETPWNAM) && defined(HAVE_SETUID) - state->pw = getpwnam(username); - if (!state->pw) + state->uid = -1; + const struct passwd *pw = getpwnam(username); + if (!pw) { msg(M_ERR, "failed to find UID for user %s", username); } + else + { + state->uid = pw->pw_uid; + } state->username = username; ret = true; #else /* if defined(HAVE_GETPWNAM) && defined(HAVE_SETUID) */ @@ -103,9 +108,9 @@ static void platform_user_set(const struct platform_state_user *state) { #if defined(HAVE_GETPWNAM) && defined(HAVE_SETUID) - if (state->username && state->pw) + if (state->username && state->uid >= 0) { - if (setuid(state->pw->pw_uid)) + if (setuid(state->uid)) { msg(M_ERR, "setuid('%s') failed", state->username); } @@ -124,11 +129,16 @@ platform_group_get(const char *groupname, struct platform_state_group *state) if (groupname) { #if defined(HAVE_GETGRNAM) && defined(HAVE_SETGID) - state->gr = getgrnam(groupname); - if (!state->gr) + state->gid = -1; + const struct group *gr = getgrnam(groupname); + if (!gr) { msg(M_ERR, "failed to find GID for group %s", groupname); } + else + { + state->gid = gr->gr_gid; + } state->groupname = groupname; ret = true; #else /* if defined(HAVE_GETGRNAM) && defined(HAVE_SETGID) */ @@ -142,9 +152,9 @@ static void platform_group_set(const struct platform_state_group *state) { #if defined(HAVE_GETGRNAM) && defined(HAVE_SETGID) - if (state->groupname && state->gr) + if (state->groupname && state->gid >= 0) { - if (setgid(state->gr->gr_gid)) + if (setgid(state->gid)) { msg(M_ERR, "setgid('%s') failed", state->groupname); } @@ -152,7 +162,7 @@ platform_group_set(const struct platform_state_group *state) #ifdef HAVE_SETGROUPS { gid_t gr_list[1]; - gr_list[0] = state->gr->gr_gid; + gr_list[0] = state->gid; if (setgroups(1, gr_list)) { msg(M_ERR, "setgroups('%s') failed", state->groupname); @@ -225,13 +235,13 @@ platform_user_group_set(const struct platform_state_user *user_state, * new_uid/new_gid defaults to -1, which will not make * libcap-ng change the UID/GID unless configured */ - if (group_state->groupname && group_state->gr) + if (group_state->groupname && group_state->gid >= 0) { - new_gid = group_state->gr->gr_gid; + new_gid = group_state->gid; } - if (user_state->username && user_state->pw) + if (user_state->username && user_state->uid >= 0) { - new_uid = user_state->pw->pw_uid; + new_uid = user_state->uid; } /* Prepare capabilities before dropping UID/GID */ diff --git a/src/openvpn/platform.h b/src/openvpn/platform.h index a35a571ccb5..d8dad74bbd7 100644 --- a/src/openvpn/platform.h +++ b/src/openvpn/platform.h @@ -63,7 +63,7 @@ struct context; struct platform_state_user { #if defined(HAVE_GETPWNAM) && defined(HAVE_SETUID) const char *username; - struct passwd *pw; + uid_t uid; #else int dummy; #endif @@ -74,7 +74,7 @@ struct platform_state_user { struct platform_state_group { #if defined(HAVE_GETGRNAM) && defined(HAVE_SETGID) const char *groupname; - struct group *gr; + gid_t gid; #else int dummy; #endif @@ -97,10 +97,7 @@ static inline int platform_state_user_uid(const struct platform_state_user *s) { #if defined(HAVE_GETPWNAM) && defined(HAVE_SETUID) - if (s->pw) - { - return s->pw->pw_uid; - } + return s->uid; #endif return -1; } @@ -109,10 +106,7 @@ static inline int platform_state_group_gid(const struct platform_state_group *s) { #if defined(HAVE_GETGRNAM) && defined(HAVE_SETGID) - if (s->gr) - { - return s->gr->gr_gid; - } + return s->gid; #endif return -1; } diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c index a1414d23f04..870332770ea 100644 --- a/src/openvpn/tun.c +++ b/src/openvpn/tun.c @@ -2242,7 +2242,7 @@ tuncfg(const char *dev, const char *dev_type, const char *dev_node, { msg(M_ERR, "Cannot get user entry for %s", username); } - else if (ioctl(tt->fd, TUNSETOWNER, platform_state_user.pw->pw_uid) < 0) + else if (ioctl(tt->fd, TUNSETOWNER, platform_state_user.uid) < 0) { msg(M_ERR, "Cannot ioctl TUNSETOWNER(%s) %s", username, dev); } @@ -2255,7 +2255,7 @@ tuncfg(const char *dev, const char *dev_type, const char *dev_node, { msg(M_ERR, "Cannot get group entry for %s", groupname); } - else if (ioctl(tt->fd, TUNSETGROUP, platform_state_group.gr->gr_gid) < 0) + else if (ioctl(tt->fd, TUNSETGROUP, platform_state_group.gid) < 0) { msg(M_ERR, "Cannot ioctl TUNSETGROUP(%s) %s", groupname, dev); } From 202a934fc32673ef865b5cbcb23ad6057ceb2e0b Mon Sep 17 00:00:00 2001 From: Arne Schwabe Date: Tue, 7 Mar 2023 16:02:33 +0100 Subject: [PATCH 041/229] Dynamic tls-crypt for secure soft_reset/session renegotiation Currently we have only one slot for renegotiation of the session/keys. If a replayed/faked packet is inserted by a malicous attacker, the legimate peer cannot renegotiate anymore. This commit introduces dynamic tls-crypt. When both peer support this feature, both peer create a dynamic tls-crypt key using TLS EKM (export key material) and will enforce using that key and tls-crypt for all renegotiations. This also add an additional protection layer for renegotiations to be taken over by an illegimate client, binding the renegotiations tightly to the original session. Especially when 2FA, webauth or similar authentication is used, many third party setup ignore the need to secure renegotiation with an auth-token. Since one of tls-crypt/tls-crypt-v2 purposes is to provide poor man's post quantum crypto guarantees, we have to ensure that the dynamic key tls-crypt key that replace the original tls-crypt key is as strong as the orginal key to avoid problems if there is a weak RNG or TLS EKM produces weak keys. We ensure this but XORing the original key with the key from TLS EKM. If tls-crypt/tls-cryptv2 is not active, we use just the key generated by TLS EKM. We also do not use hashing or anything else on the original key before XOR to avoid any potential of a structure in the key or something else that might weaken post-quantum use cases. OpenVPN 2.x reserves the TM_ACTIVE session for renegotiations. When a SOFT_RESET_V1 packet is received, the active TLS session is moved from KS_PRIMARY to KS_SECONDARY. Here an attacker could theorectically send a faked/replayed SOFT_RESET_V1 and first packet containing the TLS client hello. If this happens, the session is blocked until the TLS renegotiation attempt times out, blocking the legimitate client. Using a dynamic tls-crypt key here blocks any SOFT_RESET_V1 (and following packets) as replay and fake packets will not have a matching authentication/encryption and will be discarded. HARD_RESET packets that are from a reconnecting peer are instead put in the TM_UNTRUSTED/KS_PRIMARY slot until they are sufficiently verified, so the dynamic tls-crypt key is not used here. Replay/fake packets also do not block the legimitate client. This commit delays the purging of the original tls-crypt key data from directly after passing it to crypto library to tls_wrap_free. We do this to allow us mixing the new exported key with the original key. To be able to generate the dynamic tls-cryptn key, we need the original key, so deleting the key is not an option if we need it later again to generate another key. Even when the client does not support secure renegotiation, deleting the key is not an option since when the reconnecting client or (especially in p2p mode with float) another client does the reconnect, we might need to generate a dynamic tls-crypt key again. Delaying the deletion of the key has also little effect as the key is still present in the OpenSSL/mbed TLS structures in the tls_wrap structure, so only the number of times the keys is in memory would be reduced. Patch v2: fix spellings of reneg and renegotiations. Patch v3: expand comment to original_tlscrypt_keydata and commit message, add Changes.rst Patch v4: improve commit message, Changes.rst Patch v5: fix spelling/grammar mistakes. Add more comments. Patch v6: consistently calld this feature dynamic tls-crypt crypt. Note this changes the export label and makes it incompatible with previous patches. Patch v7: also xor tls-auth key data into the dynamic tls-crypt key like tls-crypt key data Patch v8: Avoid triggering ASSERT added in v7 by properly setting keys.n = 2 when loading tls crypt v2 client keys. Add dyn-tls-crypt to protocol options printout. Signed-off-by: Arne Schwabe Acked-by: Heiko Hund Message-Id: <20230307150233.3551436-1-arne@rfc2549.org> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26341.html Signed-off-by: Gert Doering (cherry picked from commit 6a05768a71ede7a8654fc6f3104f7449509efee0) --- Changes.rst | 12 +++ src/openvpn/auth_token.h | 2 +- src/openvpn/crypto.c | 7 +- src/openvpn/crypto.h | 16 +++- src/openvpn/init.c | 26 +++--- src/openvpn/multi.c | 4 + src/openvpn/openvpn.h | 3 + src/openvpn/options.c | 4 + src/openvpn/push.c | 5 ++ src/openvpn/ssl.c | 25 ++++-- src/openvpn/ssl.h | 5 ++ src/openvpn/ssl_backend.h | 1 + src/openvpn/ssl_common.h | 13 +++ src/openvpn/ssl_ncp.c | 4 + src/openvpn/ssl_pkt.c | 2 +- src/openvpn/ssl_pkt.h | 26 ++++++ src/openvpn/tls_crypt.c | 96 ++++++++++++++++++++--- src/openvpn/tls_crypt.h | 22 +++++- tests/unit_tests/openvpn/test_pkt.c | 17 +++- tests/unit_tests/openvpn/test_tls_crypt.c | 85 ++++++++++++++++++++ 20 files changed, 337 insertions(+), 38 deletions(-) diff --git a/Changes.rst b/Changes.rst index ba33a80b0c8..7230ab489f2 100644 --- a/Changes.rst +++ b/Changes.rst @@ -1,3 +1,15 @@ +Overview of changes in 2.6.1 +============================ + +New features +------------ +- Dynamic TLS Crypt + When both peers are OpenVPN 2.6.1+, OpenVPN will dynamically create + a tls-crypt key that is used for renegotiation. This ensure that only the + previously authenticated peer can do trigger renegotiation and complete + renegotiations. + + Overview of changes in 2.6.0, relative to 2.6_rc2 ================================================= diff --git a/src/openvpn/auth_token.h b/src/openvpn/auth_token.h index ab0ba659365..9ec2e64c965 100644 --- a/src/openvpn/auth_token.h +++ b/src/openvpn/auth_token.h @@ -43,7 +43,7 @@ * * The second timestamp is the time the token was renewed/regenerated and is used * to determine if this token has been renewed in the acceptable time range - * (2 * renogiation timeout) + * (2 * renegotiation timeout) * * The session id is a random string of 12 byte (or 16 in base64) that is not * used by OpenVPN itself but kept intact so that external logging/management diff --git a/src/openvpn/crypto.c b/src/openvpn/crypto.c index 5e1c495b0a1..b5ae17ec8df 100644 --- a/src/openvpn/crypto.c +++ b/src/openvpn/crypto.c @@ -1123,7 +1123,8 @@ void crypto_read_openvpn_key(const struct key_type *key_type, struct key_ctx_bi *ctx, const char *key_file, bool key_inline, const int key_direction, - const char *key_name, const char *opt_name) + const char *key_name, const char *opt_name, + struct key2 *keydata) { struct key2 key2; struct key_direction_state kds; @@ -1151,6 +1152,10 @@ crypto_read_openvpn_key(const struct key_type *key_type, /* initialize key in both directions */ init_key_ctx_bi(ctx, &key2, key_direction, key_type, key_name); + if (keydata) + { + *keydata = key2; + } secure_memzero(&key2, sizeof(key2)); } diff --git a/src/openvpn/crypto.h b/src/openvpn/crypto.h index 3459c43eb14..229a4eb1cde 100644 --- a/src/openvpn/crypto.h +++ b/src/openvpn/crypto.h @@ -234,7 +234,14 @@ struct crypto_options * both sending and receiving * directions. */ struct packet_id packet_id; /**< Current packet ID state for both - * sending and receiving directions. */ + * sending and receiving directions. + * + * This contains the packet id that is + * used for replay protection. + * + * The packet id also used as the IV + * for AEAD/OFB/CFG ciphers. + * */ struct packet_id_persist *pid_persist; /**< Persistent packet ID state for * keeping state between successive @@ -268,6 +275,10 @@ struct crypto_options /**< Bit-flag indicating that explicit exit notifies should be * sent via the control channel instead of using an OCC message */ +#define CO_USE_DYNAMIC_TLS_CRYPT (1<<7) + /**< Bit-flag indicating that renegotiations are using tls-crypt + * with a TLS-EKM derived key. + */ unsigned int flags; /**< Bit-flags determining behavior of * security operation functions. */ @@ -530,7 +541,8 @@ void key2_print(const struct key2 *k, void crypto_read_openvpn_key(const struct key_type *key_type, struct key_ctx_bi *ctx, const char *key_file, bool key_inline, const int key_direction, - const char *key_name, const char *opt_name); + const char *key_name, const char *opt_name, + struct key2 *keydata); /* * Inline functions diff --git a/src/openvpn/init.c b/src/openvpn/init.c index 7535c54ac98..e67b93d3a7c 100644 --- a/src/openvpn/init.c +++ b/src/openvpn/init.c @@ -2342,6 +2342,10 @@ tls_print_deferred_options_results(struct context *c) { buf_printf(&out, " tls-ekm"); } + if (o->imported_protocol_flags & CO_USE_DYNAMIC_TLS_CRYPT) + { + buf_printf(&out, " dyn-tls-crypt"); + } } if (buf_len(&out) > strlen(header)) @@ -3032,7 +3036,7 @@ do_init_crypto_static(struct context *c, const unsigned int flags) options->shared_secret_file, options->shared_secret_file_inline, options->key_direction, "Static Key Encryption", - "secret"); + "secret", NULL); } else { @@ -3072,13 +3076,15 @@ do_init_tls_wrap_key(struct context *c) options->ce.tls_auth_file, options->ce.tls_auth_file_inline, options->ce.key_direction, - "Control Channel Authentication", "tls-auth"); + "Control Channel Authentication", "tls-auth", + &c->c1.ks.original_wrap_keydata); } /* TLS handshake encryption+authentication (--tls-crypt) */ if (options->ce.tls_crypt_file) { tls_crypt_init_key(&c->c1.ks.tls_wrap_key, + &c->c1.ks.original_wrap_keydata, options->ce.tls_crypt_file, options->ce.tls_crypt_file_inline, options->tls_server); @@ -3096,6 +3102,7 @@ do_init_tls_wrap_key(struct context *c) else { tls_crypt_v2_init_client_key(&c->c1.ks.tls_wrap_key, + &c->c1.ks.original_wrap_keydata, &c->c1.ks.tls_crypt_v2_wkc, options->ce.tls_crypt_v2_file, options->ce.tls_crypt_v2_file_inline); @@ -3399,9 +3406,6 @@ do_init_crypto_tls(struct context *c, const unsigned int flags) if (options->ce.tls_auth_file) { to.tls_wrap.mode = TLS_WRAP_AUTH; - to.tls_wrap.opt.key_ctx_bi = c->c1.ks.tls_wrap_key; - to.tls_wrap.opt.pid_persist = &c->c1.pid_persist; - to.tls_wrap.opt.flags |= CO_PACKET_ID_LONG_FORM; } /* TLS handshake encryption (--tls-crypt) */ @@ -3409,19 +3413,21 @@ do_init_crypto_tls(struct context *c, const unsigned int flags) || (options->ce.tls_crypt_v2_file && options->tls_client)) { to.tls_wrap.mode = TLS_WRAP_CRYPT; + } + + if (to.tls_wrap.mode == TLS_WRAP_AUTH || to.tls_wrap.mode == TLS_WRAP_CRYPT) + { to.tls_wrap.opt.key_ctx_bi = c->c1.ks.tls_wrap_key; to.tls_wrap.opt.pid_persist = &c->c1.pid_persist; to.tls_wrap.opt.flags |= CO_PACKET_ID_LONG_FORM; - - if (options->ce.tls_crypt_v2_file) - { - to.tls_wrap.tls_crypt_v2_wkc = &c->c1.ks.tls_crypt_v2_wkc; - } + to.tls_wrap.original_wrap_keydata = c->c1.ks.original_wrap_keydata; } if (options->ce.tls_crypt_v2_file) { to.tls_crypt_v2 = true; + to.tls_wrap.tls_crypt_v2_wkc = &c->c1.ks.tls_crypt_v2_wkc; + if (options->tls_server) { to.tls_wrap.tls_crypt_v2_server_key = c->c1.ks.tls_crypt_v2_server_key; diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c index 99123c3963a..59c980b0df0 100644 --- a/src/openvpn/multi.c +++ b/src/openvpn/multi.c @@ -1817,6 +1817,10 @@ multi_client_set_protocol_options(struct context *c) { o->imported_protocol_flags |= CO_USE_TLS_KEY_MATERIAL_EXPORT; } + if (proto & IV_PROTO_DYN_TLS_CRYPT) + { + o->imported_protocol_flags |= CO_USE_DYNAMIC_TLS_CRYPT; + } #endif if (proto & IV_PROTO_CC_EXIT_NOTIFY) diff --git a/src/openvpn/openvpn.h b/src/openvpn/openvpn.h index 1ad0ae97931..077effeb9ec 100644 --- a/src/openvpn/openvpn.h +++ b/src/openvpn/openvpn.h @@ -65,6 +65,9 @@ struct key_schedule /* optional TLS control channel wrapping */ struct key_type tls_auth_key_type; struct key_ctx_bi tls_wrap_key; + /** original tls-crypt key preserved to xored into the tls_crypt + * renegotiation key */ + struct key2 original_wrap_keydata; struct key_ctx tls_crypt_v2_server_key; struct buffer tls_crypt_v2_wkc; /**< Wrapped client key */ struct key_ctx auth_token_key; diff --git a/src/openvpn/options.c b/src/openvpn/options.c index 45d0e0fa8f3..ce7bd0a7970 100644 --- a/src/openvpn/options.c +++ b/src/openvpn/options.c @@ -8657,6 +8657,10 @@ add_option(struct options *options, { options->imported_protocol_flags |= CO_USE_TLS_KEY_MATERIAL_EXPORT; } + else if (streq(p[j], "dyn-tls-crypt")) + { + options->imported_protocol_flags |= CO_USE_DYNAMIC_TLS_CRYPT; + } #endif else { diff --git a/src/openvpn/push.c b/src/openvpn/push.c index 4d64ad1afa1..4453e426178 100644 --- a/src/openvpn/push.c +++ b/src/openvpn/push.c @@ -667,6 +667,11 @@ prepare_push_reply(struct context *c, struct gc_arena *gc, push_option_fmt(gc, push_list, M_USAGE, "key-derivation tls-ekm"); } + if (o->imported_protocol_flags & CO_USE_DYNAMIC_TLS_CRYPT) + { + buf_printf(&proto_flags, " dyn-tls-crypt"); + } + if (buf_len(&proto_flags) > 0) { push_option_fmt(gc, push_list, M_USAGE, "protocol-flags%s", buf_str(&proto_flags)); diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c index 47f3702b25e..78cec90a1c5 100644 --- a/src/openvpn/ssl.c +++ b/src/openvpn/ssl.c @@ -1202,6 +1202,7 @@ static void tls_session_free(struct tls_session *session, bool clear) { tls_wrap_free(&session->tls_wrap); + tls_wrap_free(&session->tls_wrap_reneg); for (size_t i = 0; i < KS_SIZE; ++i) { @@ -1758,6 +1759,17 @@ tls_session_update_crypto_params_do_work(struct tls_multi *multi, frame_print(frame_fragment, D_MTU_INFO, "Fragmentation MTU parms"); } + if (session->key[KS_PRIMARY].key_id == 0 + && session->opt->crypto_flags & CO_USE_DYNAMIC_TLS_CRYPT) + { + /* If dynamic tls-crypt has been negotiated, and we are on the + * first session (key_id = 0), generate a tls-crypt key for the + * following renegotiations */ + if (!tls_session_generate_dynamic_tls_crypt_key(multi, session)) + { + return false; + } + } return tls_session_generate_data_channel_keys(multi, session); } @@ -2065,6 +2077,7 @@ push_peer_info(struct buffer *buf, struct tls_session *session) #ifdef HAVE_EXPORT_KEYING_MATERIAL iv_proto |= IV_PROTO_TLS_KEY_EXPORT; + iv_proto |= IV_PROTO_DYN_TLS_CRYPT; #endif buf_printf(&out, "IV_PROTO=%d\n", iv_proto); @@ -3642,7 +3655,7 @@ tls_pre_decrypt(struct tls_multi *multi, /* * If --single-session, don't allow any hard-reset connection request - * unless it the first packet of the session. + * unless it is the first packet of the session. */ if (multi->opt.single_session && multi->n_sessions) { @@ -3653,7 +3666,7 @@ tls_pre_decrypt(struct tls_multi *multi, goto error; } - if (!read_control_auth(buf, &session->tls_wrap, from, + if (!read_control_auth(buf, tls_session_get_tls_wrap(session, key_id), from, session->opt)) { goto error; @@ -3723,8 +3736,8 @@ tls_pre_decrypt(struct tls_multi *multi, */ if (op == P_CONTROL_SOFT_RESET_V1 && ks->state >= S_GENERATED_KEYS) { - if (!read_control_auth(buf, &session->tls_wrap, from, - session->opt)) + if (!read_control_auth(buf, tls_session_get_tls_wrap(session, key_id), + from, session->opt)) { goto error; } @@ -3745,8 +3758,8 @@ tls_pre_decrypt(struct tls_multi *multi, do_burst = true; } - if (!read_control_auth(buf, &session->tls_wrap, from, - session->opt)) + if (!read_control_auth(buf, tls_session_get_tls_wrap(session, key_id), + from, session->opt)) { goto error; } diff --git a/src/openvpn/ssl.h b/src/openvpn/ssl.h index b0a2823fbf1..58ff4b9b4e7 100644 --- a/src/openvpn/ssl.h +++ b/src/openvpn/ssl.h @@ -43,6 +43,7 @@ #include "ssl_common.h" #include "ssl_backend.h" #include "ssl_pkt.h" +#include "tls_crypt.h" /* Used in the TLS PRF function */ #define KEY_EXPANSION_ID "OpenVPN" @@ -103,6 +104,9 @@ /** Support for AUTH_FAIL,TEMP messages */ #define IV_PROTO_AUTH_FAIL_TEMP (1<<8) +/** Support to dynamic tls-crypt (renegotiation with TLS-EKM derived tls-crypt key) */ +#define IV_PROTO_DYN_TLS_CRYPT (1<<9) + /* Default field in X509 to be username */ #define X509_USERNAME_FIELD_DEFAULT "CN" @@ -476,6 +480,7 @@ tls_wrap_free(struct tls_wrap_ctx *tls_wrap) free_buf(&tls_wrap->tls_crypt_v2_metadata); free_buf(&tls_wrap->work); + secure_memzero(&tls_wrap->original_wrap_keydata, sizeof(tls_wrap->original_wrap_keydata)); } static inline bool diff --git a/src/openvpn/ssl_backend.h b/src/openvpn/ssl_backend.h index 0ce24987172..3854d59c2f1 100644 --- a/src/openvpn/ssl_backend.h +++ b/src/openvpn/ssl_backend.h @@ -391,6 +391,7 @@ void backend_tls_ctx_reload_crl(struct tls_root_ctx *ssl_ctx, #define EXPORT_KEY_DATA_LABEL "EXPORTER-OpenVPN-datakeys" #define EXPORT_P2P_PEERID_LABEL "EXPORTER-OpenVPN-p2p-peerid" +#define EXPORT_DYNAMIC_TLS_CRYPT_LABEL "EXPORTER-OpenVPN-dynamic-tls-crypt" /** * Keying Material Exporters [RFC 5705] allows additional keying material to be * derived from existing TLS channel. This exported keying material can then be diff --git a/src/openvpn/ssl_common.h b/src/openvpn/ssl_common.h index eeb056f5e1a..27b02947993 100644 --- a/src/openvpn/ssl_common.h +++ b/src/openvpn/ssl_common.h @@ -275,6 +275,15 @@ struct tls_wrap_ctx struct buffer tls_crypt_v2_metadata; /**< Received from client */ bool cleanup_key_ctx; /**< opt.key_ctx_bi is owned by * this context */ + /** original key data to be xored in to the key for dynamic tls-crypt. + * + * We keep the original key data to ensure that the newly generated key + * for the dynamic tls-crypt has the same level of quality by using + * xor with the original key. This gives us the same same entropy/randomness + * as the original tls-crypt key to ensure the post-quantum use case of + * tls-crypt still holds true + * */ + struct key2 original_wrap_keydata; }; /* @@ -468,6 +477,10 @@ struct tls_session /* authenticate control packets */ struct tls_wrap_ctx tls_wrap; + /* Specific tls-crypt for renegotiations, if this is valid, + * tls_wrap_reneg.mode is TLS_WRAP_CRYPT, otherwise ignore it */ + struct tls_wrap_ctx tls_wrap_reneg; + int initial_opcode; /* our initial P_ opcode */ struct session_id session_id; /* our random session ID */ diff --git a/src/openvpn/ssl_ncp.c b/src/openvpn/ssl_ncp.c index 97619be5ef3..22dbe210023 100644 --- a/src/openvpn/ssl_ncp.c +++ b/src/openvpn/ssl_ncp.c @@ -461,6 +461,10 @@ p2p_ncp_set_options(struct tls_multi *multi, struct tls_session *session) } } + if (iv_proto_peer & IV_PROTO_DYN_TLS_CRYPT) + { + session->opt->crypto_flags |= CO_USE_DYNAMIC_TLS_CRYPT; + } #endif /* if defined(HAVE_EXPORT_KEYING_MATERIAL) */ } diff --git a/src/openvpn/ssl_pkt.c b/src/openvpn/ssl_pkt.c index b0d1a7f3c22..17a7f891725 100644 --- a/src/openvpn/ssl_pkt.c +++ b/src/openvpn/ssl_pkt.c @@ -193,7 +193,7 @@ write_control_auth(struct tls_session *session, msg(D_TLS_DEBUG, "%s(): %s", __func__, packet_opcode_name(opcode)); - tls_wrap_control(&session->tls_wrap, header, buf, &session->session_id); + tls_wrap_control(tls_session_get_tls_wrap(session, ks->key_id), header, buf, &session->session_id); *to_link_addr = &ks->remote_addr; } diff --git a/src/openvpn/ssl_pkt.h b/src/openvpn/ssl_pkt.h index dd3c3de0581..ec7b48daf66 100644 --- a/src/openvpn/ssl_pkt.h +++ b/src/openvpn/ssl_pkt.h @@ -273,6 +273,32 @@ packet_opcode_name(int op) } } +/** + * Determines if the current session should use the renegotiation tls wrap + * struct instead the normal one and returns it. + * + * @param session + * @param key_id key_id of the received/or to be send packet + * @return + */ +static inline struct tls_wrap_ctx * +tls_session_get_tls_wrap(struct tls_session *session, int key_id) +{ + /* OpenVPN has the hardcoded assumption in its protocol that + * key-id 0 is always first session and renegotiations use key-id + * 1 to 7 and wrap around to 1 after that. So key-id > 0 is equivalent + * to "this is a renegotiation" + */ + if (key_id > 0 && session->tls_wrap_reneg.mode == TLS_WRAP_CRYPT) + { + return &session->tls_wrap_reneg; + } + else + { + return &session->tls_wrap; + } +} + /* initial packet id (instead of 0) that indicates that the peer supports * early protocol negotiation. This will make the packet id turn a bit faster * but the network time part of the packet id takes care of that. And diff --git a/src/openvpn/tls_crypt.c b/src/openvpn/tls_crypt.c index 1e461fcf6b5..81098355e8c 100644 --- a/src/openvpn/tls_crypt.c +++ b/src/openvpn/tls_crypt.c @@ -60,8 +60,8 @@ tls_crypt_buf_overhead(void) } void -tls_crypt_init_key(struct key_ctx_bi *key, const char *key_file, - bool key_inline, bool tls_server) +tls_crypt_init_key(struct key_ctx_bi *key, struct key2 *keydata, + const char *key_file, bool key_inline, bool tls_server) { const int key_direction = tls_server ? KEY_DIRECTION_NORMAL : KEY_DIRECTION_INVERSE; @@ -71,9 +71,78 @@ tls_crypt_init_key(struct key_ctx_bi *key, const char *key_file, msg(M_FATAL, "ERROR: --tls-crypt not supported"); } crypto_read_openvpn_key(&kt, key, key_file, key_inline, key_direction, - "Control Channel Encryption", "tls-crypt"); + "Control Channel Encryption", "tls-crypt", keydata); } +/** + * Will produce key = key XOR other + */ +static void +xor_key2(struct key2 *key, const struct key2 *other) +{ + ASSERT(key->n == 2 && other->n == 2); + for (int k = 0; k < 2; k++) + { + for (int j = 0; j < MAX_CIPHER_KEY_LENGTH; j++) + { + key->keys[k].cipher[j] = key->keys[k].cipher[j] ^ other->keys[k].cipher[j]; + } + + for (int j = 0; j < MAX_HMAC_KEY_LENGTH; j++) + { + key->keys[k].hmac[j] = key->keys[k].hmac[j] ^ other->keys[k].hmac[j]; + } + + } +} + +bool +tls_session_generate_dynamic_tls_crypt_key(struct tls_multi *multi, + struct tls_session *session) +{ + session->tls_wrap_reneg.opt = session->tls_wrap.opt; + session->tls_wrap_reneg.mode = TLS_WRAP_CRYPT; + session->tls_wrap_reneg.cleanup_key_ctx = true; + session->tls_wrap_reneg.work = alloc_buf(BUF_SIZE(&session->opt->frame)); + session->tls_wrap_reneg.opt.pid_persist = NULL; + + packet_id_init(&session->tls_wrap_reneg.opt.packet_id, + session->opt->replay_window, + session->opt->replay_time, + "TLS_WRAP_RENEG", session->key_id); + + + struct key2 rengokeys; + if (!key_state_export_keying_material(session, EXPORT_DYNAMIC_TLS_CRYPT_LABEL, + strlen(EXPORT_DYNAMIC_TLS_CRYPT_LABEL), + rengokeys.keys, sizeof(rengokeys.keys))) + { + return false; + } + rengokeys.n = 2; + + if (session->tls_wrap.mode == TLS_WRAP_CRYPT + || session->tls_wrap.mode == TLS_WRAP_AUTH) + { + xor_key2(&rengokeys, &session->tls_wrap.original_wrap_keydata); + } + + const int key_direction = session->opt->server ? + KEY_DIRECTION_NORMAL : KEY_DIRECTION_INVERSE; + + struct key_direction_state kds; + key_direction_state_init(&kds, key_direction); + + struct key_type kt = tls_crypt_kt(); + + init_key_ctx_bi(&session->tls_wrap_reneg.opt.key_ctx_bi, &rengokeys, key_direction, + &kt, "dynamic tls-crypt"); + secure_memzero(&rengokeys, sizeof(rengokeys)); + + return true; +} + + bool tls_crypt_wrap(const struct buffer *src, struct buffer *dst, struct crypto_options *opt) @@ -266,8 +335,9 @@ tls_crypt_v2_load_client_key(struct key_ctx_bi *key, const struct key2 *key2, } void -tls_crypt_v2_init_client_key(struct key_ctx_bi *key, struct buffer *wkc_buf, - const char *key_file, bool key_inline) +tls_crypt_v2_init_client_key(struct key_ctx_bi *key, struct key2 *original_key, + struct buffer *wkc_buf, const char *key_file, + bool key_inline) { struct buffer client_key = alloc_buf(TLS_CRYPT_V2_CLIENT_KEY_LEN + TLS_CRYPT_V2_MAX_WKC_LEN); @@ -278,14 +348,14 @@ tls_crypt_v2_init_client_key(struct key_ctx_bi *key, struct buffer *wkc_buf, msg(M_FATAL, "ERROR: invalid tls-crypt-v2 client key format"); } - struct key2 key2; + struct key2 key2 = { .n = 2, .keys = { 0 } }; if (!buf_read(&client_key, &key2.keys, sizeof(key2.keys))) { msg(M_FATAL, "ERROR: not enough data in tls-crypt-v2 client key"); } tls_crypt_v2_load_client_key(key, &key2, false); - secure_memzero(&key2, sizeof(key2)); + *original_key = key2; *wkc_buf = client_key; } @@ -570,15 +640,14 @@ tls_crypt_v2_extract_client_key(struct buffer *buf, return false; } - struct key2 client_key = { 0 }; ctx->tls_crypt_v2_metadata = alloc_buf(TLS_CRYPT_V2_MAX_METADATA_LEN); - if (!tls_crypt_v2_unwrap_client_key(&client_key, + if (!tls_crypt_v2_unwrap_client_key(&ctx->original_wrap_keydata, &ctx->tls_crypt_v2_metadata, wrapped_client_key, &ctx->tls_crypt_v2_server_key)) { msg(D_TLS_ERRORS, "Can not unwrap tls-crypt-v2 client key"); - secure_memzero(&client_key, sizeof(client_key)); + secure_memzero(&ctx->original_wrap_keydata, sizeof(ctx->original_wrap_keydata)); return false; } @@ -587,8 +656,8 @@ tls_crypt_v2_extract_client_key(struct buffer *buf, ctx->cleanup_key_ctx = true; ctx->opt.flags |= CO_PACKET_ID_LONG_FORM; memset(&ctx->opt.key_ctx_bi, 0, sizeof(ctx->opt.key_ctx_bi)); - tls_crypt_v2_load_client_key(&ctx->opt.key_ctx_bi, &client_key, true); - secure_memzero(&client_key, sizeof(client_key)); + tls_crypt_v2_load_client_key(&ctx->opt.key_ctx_bi, + &ctx->original_wrap_keydata, true); /* Remove client key from buffer so tls-crypt code can unwrap message */ ASSERT(buf_inc_len(buf, -(BLEN(&wrapped_client_key)))); @@ -692,8 +761,9 @@ tls_crypt_v2_write_client_key_file(const char *filename, /* Sanity check: load client key (as "client") */ struct key_ctx_bi test_client_key; struct buffer test_wrapped_client_key; + struct key2 keydata; msg(D_GENKEY, "Testing client-side key loading..."); - tls_crypt_v2_init_client_key(&test_client_key, &test_wrapped_client_key, + tls_crypt_v2_init_client_key(&test_client_key, &keydata, &test_wrapped_client_key, client_file, client_inline); free_key_ctx_bi(&test_client_key); diff --git a/src/openvpn/tls_crypt.h b/src/openvpn/tls_crypt.h index d5c73752370..8c87e2080a1 100644 --- a/src/openvpn/tls_crypt.h +++ b/src/openvpn/tls_crypt.h @@ -108,12 +108,27 @@ * @param key The key context to initialize * @param key_file The file to read the key from or the key itself if * key_inline is true. + * @param keydata The keydata used to create key will be written here. * @param key_inline True if key_file contains an inline key, False * otherwise. * @param tls_server Must be set to true is this is a TLS server instance. */ -void tls_crypt_init_key(struct key_ctx_bi *key, const char *key_file, - bool key_inline, bool tls_server); +void tls_crypt_init_key(struct key_ctx_bi *key, struct key2 *keydata, + const char *key_file, bool key_inline, bool tls_server); + +/** + * Generates a TLS-Crypt key to be used with dynamic tls-crypt using the + * TLS EKM exporter function. + * + * All renegotiations of a session use the same generated dynamic key. + * + * @param multi multi session struct + * @param session session that will be used for the TLS EKM exporter + * @return true iff generating the key was successful + */ +bool +tls_session_generate_dynamic_tls_crypt_key(struct tls_multi *multi, + struct tls_session *session); /** * Returns the maximum overhead (in bytes) added to the destination buffer by @@ -169,6 +184,8 @@ void tls_crypt_v2_init_server_key(struct key_ctx *key_ctx, bool encrypt, * * @param key Key structure to be initialized with the client * key. + * @param original_key contains the key data that has been used to + * initialise the key parameter * @param wrapped_key_buf Returns buffer containing the wrapped key that will * be sent to the server when connecting. Caller must * free this buffer when no longer needed. @@ -178,6 +195,7 @@ void tls_crypt_v2_init_server_key(struct key_ctx *key_ctx, bool encrypt, * otherwise. */ void tls_crypt_v2_init_client_key(struct key_ctx_bi *key, + struct key2 *original_key, struct buffer *wrapped_key_buf, const char *key_file, bool key_inline); diff --git a/tests/unit_tests/openvpn/test_pkt.c b/tests/unit_tests/openvpn/test_pkt.c index 3bbd989731e..f11e52a1101 100644 --- a/tests/unit_tests/openvpn/test_pkt.c +++ b/tests/unit_tests/openvpn/test_pkt.c @@ -55,6 +55,16 @@ parse_line(const char *line, char **p, const int n, const char *file, return 0; } +/* Define this function here as dummy since including the ssl_*.c files + * leads to having to include even more unrelated code */ +bool +key_state_export_keying_material(struct tls_session *session, + const char *label, size_t label_size, + void *ekm, size_t ekm_size) +{ + ASSERT(0); +} + const char * print_link_socket_actual(const struct link_socket_actual *act, struct gc_arena *gc) { @@ -191,7 +201,8 @@ init_tas_auth(int key_direction) crypto_read_openvpn_key(&tls_crypt_kt, &tas.tls_wrap.opt.key_ctx_bi, static_key, true, key_direction, - "Control Channel Authentication", "tls-auth"); + "Control Channel Authentication", "tls-auth", + NULL); return tas; } @@ -203,7 +214,9 @@ init_tas_crypt(bool server) tas.tls_wrap.mode = TLS_WRAP_CRYPT; tas.tls_wrap.opt.flags |= (CO_IGNORE_PACKET_ID|CO_PACKET_ID_LONG_FORM); - tls_crypt_init_key(&tas.tls_wrap.opt.key_ctx_bi, static_key, true, server); + tls_crypt_init_key(&tas.tls_wrap.opt.key_ctx_bi, + &tas.tls_wrap.original_wrap_keydata, static_key, + true, server); return tas; } diff --git a/tests/unit_tests/openvpn/test_tls_crypt.c b/tests/unit_tests/openvpn/test_tls_crypt.c index 82bb0a26696..19ae29cc676 100644 --- a/tests/unit_tests/openvpn/test_tls_crypt.c +++ b/tests/unit_tests/openvpn/test_tls_crypt.c @@ -40,6 +40,18 @@ #include "mock_msg.h" +/* Define this function here as dummy since including the ssl_*.c files + * leads to having to include even more unrelated code */ +bool +key_state_export_keying_material(struct tls_session *session, + const char *label, size_t label_size, + void *ekm, size_t ekm_size) +{ + memset(ekm, 0xba, ekm_size); + return true; +} + + #define TESTBUF_SIZE 128 /* Defines for use in the tests and the mock parse_line() */ @@ -141,6 +153,7 @@ struct test_tls_crypt_context { struct buffer unwrapped; }; + static int test_tls_crypt_setup(void **state) { @@ -218,6 +231,75 @@ tls_crypt_loopback(void **state) BLEN(&ctx->source)); } + +/** + * Test generating dynamic tls-crypt key + */ +static void +test_tls_crypt_secure_reneg_key(void **state) +{ + struct test_tls_crypt_context *ctx = + (struct test_tls_crypt_context *)*state; + + struct gc_arena gc = gc_new(); + + struct tls_multi multi = { 0 }; + struct tls_session session = { 0 }; + + struct tls_options tls_opt = { 0 }; + tls_opt.replay_window = 32; + tls_opt.replay_time = 60; + tls_opt.frame.buf.payload_size = 512; + session.opt = &tls_opt; + + tls_session_generate_dynamic_tls_crypt_key(&multi, &session); + + struct tls_wrap_ctx *rctx = &session.tls_wrap_reneg; + + tls_crypt_wrap(&ctx->source, &rctx->work, &rctx->opt); + assert_int_equal(buf_len(&ctx->source) + 40, buf_len(&rctx->work)); + + uint8_t expected_ciphertext[] = { + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x19, 0x27, 0x7f, 0x1c, 0x8d, 0x6e, 0x6a, + 0x77, 0x96, 0xa8, 0x55, 0x33, 0x7b, 0x9c, 0xfb, 0x56, 0xe1, 0xf1, 0x3a, 0x87, 0x0e, 0x66, 0x47, + 0xdf, 0xa1, 0x95, 0xc9, 0x2c, 0x17, 0xa0, 0x15, 0xba, 0x49, 0x67, 0xa1, 0x1d, 0x55, 0xea, 0x1a, + 0x06, 0xa7 + }; + assert_memory_equal(BPTR(&rctx->work), expected_ciphertext, buf_len(&rctx->work)); + tls_wrap_free(&session.tls_wrap_reneg); + + /* Use previous tls-crypt key as 0x00, with xor we should have the same key + * and expect the same result */ + session.tls_wrap.mode = TLS_WRAP_CRYPT; + memset(&session.tls_wrap.original_wrap_keydata.keys, 0x00, sizeof(session.tls_wrap.original_wrap_keydata.keys)); + session.tls_wrap.original_wrap_keydata.n = 2; + + tls_session_generate_dynamic_tls_crypt_key(&multi, &session); + tls_crypt_wrap(&ctx->source, &rctx->work, &rctx->opt); + assert_int_equal(buf_len(&ctx->source) + 40, buf_len(&rctx->work)); + + assert_memory_equal(BPTR(&rctx->work), expected_ciphertext, buf_len(&rctx->work)); + tls_wrap_free(&session.tls_wrap_reneg); + + /* XOR should not force a different key */ + memset(&session.tls_wrap.original_wrap_keydata.keys, 0x42, sizeof(session.tls_wrap.original_wrap_keydata.keys)); + tls_session_generate_dynamic_tls_crypt_key(&multi, &session); + + tls_crypt_wrap(&ctx->source, &rctx->work, &rctx->opt); + assert_int_equal(buf_len(&ctx->source) + 40, buf_len(&rctx->work)); + + /* packet id at the start should be equal */ + assert_memory_equal(BPTR(&rctx->work), expected_ciphertext, 8); + + /* Skip packet id */ + buf_advance(&rctx->work, 8); + assert_memory_not_equal(BPTR(&rctx->work), expected_ciphertext, buf_len(&rctx->work)); + tls_wrap_free(&session.tls_wrap_reneg); + + + gc_free(&gc); +} + /** * Check that zero-byte messages are successfully wrapped-and-unwrapped. */ @@ -632,6 +714,9 @@ main(void) cmocka_unit_test_setup_teardown(tls_crypt_v2_wrap_unwrap_dst_too_small, test_tls_crypt_v2_setup, test_tls_crypt_v2_teardown), + cmocka_unit_test_setup_teardown(test_tls_crypt_secure_reneg_key, + test_tls_crypt_setup, + test_tls_crypt_teardown), cmocka_unit_test(test_tls_crypt_v2_write_server_key_file), cmocka_unit_test(test_tls_crypt_v2_write_client_key_file), cmocka_unit_test(test_tls_crypt_v2_write_client_key_file_metadata), From b48298ac510abd000f65fe935a2d1cf7c25ecbf3 Mon Sep 17 00:00:00 2001 From: Gert Doering Date: Mon, 6 Mar 2023 09:07:44 +0100 Subject: [PATCH 042/229] FreeBSD 12.x workaround for IPv6 ifconfig is needed on 12.4 as well Commit 16d7f2cd4d90 tried to remove an FreeBSD 12.x ifconfig inet6 workaround based on the understanding that the upstream fix for bug 248172 went into 12.4, but that was a misread of the code - 12.4 needs the workaround as well, fixed in 13.0. Also extend comment to point to /etc/network.subr, which is the real source of the problematic code if checkyesno ipv6_activate_all_interfaces; then _ipv6_opts="-ifdisabled" elif [ "$1" != "lo0" ]; then <<<< _ipv6_opts="ifdisabled" <<<< fi Trac: 1226 Signed-off-by: Gert Doering Acked-by: Arne Schwabe Message-Id: <20230306080744.66069-1-gert@greenie.muc.de> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26335.html Signed-off-by: Gert Doering (cherry picked from commit 549fbd83f9d445863cc62b3a658a406afacdaeac) --- src/openvpn/tun.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c index 870332770ea..2ebe4809376 100644 --- a/src/openvpn/tun.c +++ b/src/openvpn/tun.c @@ -1105,11 +1105,11 @@ do_ifconfig_ipv6(struct tuntap *tt, const char *ifname, int tun_mtu, "generic BSD ifconfig inet6 failed"); #if defined(TARGET_FREEBSD) && __FreeBSD_version >= 1200000 \ - && __FreeBSD_version < 1204000 - /* On FreeBSD 12.0-12.3, there is ipv6_activate_all_interfaces="YES" + && __FreeBSD_version < 1300000 + /* On FreeBSD 12.0-12.4, there is ipv6_activate_all_interfaces="YES" * in rc.conf, which is not set by default. If it is *not* set, * "all new interfaces that are not already up" are configured by - * devd + /etc/pccard_ether as "inet6 ifdisabled". + * devd -> /etc/pccard_ether -> /etc/network.subr as "inet6 ifdisabled". * * The "is this interface already up?" test is a non-zero time window * which we manage to hit with our ifconfig often enough to cause From 26417824ff81ba1dd18b03a40822da533018a892 Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Wed, 8 Mar 2023 14:37:43 +0100 Subject: [PATCH 043/229] Avoid warning about missing braces when initialising key struct This avoids the warning from gcc about initialising the key2 struct. Signed-off-by: Antonio Quartulli Acked-by: Gert Doering Message-Id: <20230308133743.5059-1-a@unstable.cc> URL: https://www.mail-archive.com/search?l=mid&q=20230308133743.5059-1-a@unstable.cc Signed-off-by: Gert Doering (cherry picked from commit 5a14a5ea572ec88e2a9e7bfdad9d5fe31025c021) --- src/openvpn/tls_crypt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openvpn/tls_crypt.c b/src/openvpn/tls_crypt.c index 81098355e8c..3b68d186bb4 100644 --- a/src/openvpn/tls_crypt.c +++ b/src/openvpn/tls_crypt.c @@ -348,7 +348,7 @@ tls_crypt_v2_init_client_key(struct key_ctx_bi *key, struct key2 *original_key, msg(M_FATAL, "ERROR: invalid tls-crypt-v2 client key format"); } - struct key2 key2 = { .n = 2, .keys = { 0 } }; + struct key2 key2 = { .n = 2 }; if (!buf_read(&client_key, &key2.keys, sizeof(key2.keys))) { msg(M_FATAL, "ERROR: not enough data in tls-crypt-v2 client key"); From 2c2a98a0e559928c2523bf32fbf4c8beaa160b12 Mon Sep 17 00:00:00 2001 From: Gert Doering Date: Wed, 8 Mar 2023 14:23:59 +0100 Subject: [PATCH 044/229] preparing release 2.6.1 version.m4, ChangeLog, Changes.rst Signed-off-by: Gert Doering --- ChangeLog | 59 +++++++++++++++++++++++++++++++++++++++++ Changes.rst | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++ version.m4 | 4 +-- 3 files changed, 137 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 0a77882a5e3..e0d62222652 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,65 @@ OpenVPN ChangeLog Copyright (C) 2002-2023 OpenVPN Inc +2023.03.08 -- Version 2.6.1 + +Arne Schwabe (13): + Fix unaligned access in auth-token + Update LibreSSL to 3.7.0 in Github actions + Add printing USAN stack trace on github actions + Fix LibreSSL not building in Github Actions + Add missing stdint.h includes in unit tests files + Combine extra_tun/frame parameter of frame_calculate_payload_overhead + Update the last sections in the man page to a be a bit less outdated + Add building unit tests with mingw to github actions + Revise the cipher negotiation info about OpenVPN3 in the man page + Exit if a proper message instead of segfault on Android without management + Use proper print format/casting when converting msg_channel handle + Reduce initialisation spam from verb <= 3 and print summary instead + Dynamic tls-crypt for secure soft_reset/session renegotiation + +Frank Lichtenheld (8): + Changes.rst: document removal of --keysize + Windows: fix unused function setenv_foreign_option + Windows: fix unused variables in delete_route_ipv6 + Windows: fix wrong printf format in x_check_status + Windows: fix unused variable in win32_get_arch + configure: enable DCO by default on FreeBSD/Linux + Windows: fix signedness errors with recv/send + configure: fix formatting of --disable-lz4 and --enable-comp-stub + +Gert Doering (2): + Get rid of unused 'bool tuntap_buffer' arguments. + FreeBSD 12.x workaround for IPv6 ifconfig is needed on 12.4 as well + +Kristof Provost (3): + options.c: enforce a minimal fragment size + configure: improve FreeBSD DCO check + dco: define OVPN_DEL_PEER_REASON_TRANSPORT_DISCONNECT on FreeBSD + +Lev Stipakov (6): + Allow certain DHCP options to be used without DHCP server + dco-win: use proper calling convention on x86 + Improve format specifier for socket handle in Windows + Disable DCO if proxy is set via management + Add logging for windows driver selection process + Avoid management log loop with verb >= 6 + +Matthias Andree (1): + make dist: Ship ovpn_dco_freebsd.h, too + +Selva Nair (9): + block-dns using iservice: fix a potential double free + Conditionally add subdir-objects option to automake + Build unit tests in mingw Windows build + cyryptapi.c: log the selected certificate's name + cryptoapi.c: remove pre OpenSSL-3.01 support + cryptoapi.c: simplify parsing of thumbprint hex string + Option --cryptoapicert: support issuer name as a selector + Add a unit test for functions in cryptoapi.c + Do not save pointer to 'struct passwd' returned by getpwnam etc. + + 2023.01.25 -- Version 2.6.0 Antonio Quartulli (1): diff --git a/Changes.rst b/Changes.rst index 7230ab489f2..ba7952b3296 100644 --- a/Changes.rst +++ b/Changes.rst @@ -9,6 +9,82 @@ New features previously authenticated peer can do trigger renegotiation and complete renegotiations. +- CryptoAPI (Windows): support issuer name as a selector. + Certificate selection string can now specify a partial + issuer name string as "--cryptoapicert ISSUER:" where + is matched as a substring of the issuer (CA) name in + the certificate. + + +User visible changes +-------------------- +- on crypto initialization, move old "quite verbose" messages to --verb 4 + and only print a more compact summary about crypto and timing parameters + by default + +- configure now enables DCO build by default on FreeBSD and Linux, which + brings in a default dependency for libnl-genl (for Linux distributions + that are too old to have this library, use "configure --disable-dco") + +- make "configure --help" output more consistent + +- CryptoAPI (Windows): remove support code for OpenSSL before 3.0.1 + (this will not affect official OpenVPN for Windows installers, as they + will always be built with OpenSSL 3.0.x) + +- CryptoAPI (Windows): log the selected certificate's name + +- "configure" now uses "subdir-objects", for automake >= 1.16 + (less warnings for recent-enough automake versions, will change + the way .o files are created) + + +Bugfixes / minor improvements +----------------------------- +- fixed old IPv6 ifconfig race condition for FreeBSD 12.4 (trac #1226) + +- fix compile-time breakage related to DCO defines on FreeBSD 14 + +- enforce minimum packet size for "--fragment" (avoid division by zero) + +- some alignment fixes to avoid unaligned memory accesses, which will + bring problems on some architectures (Sparc64, some ARM versions) - + found by USAN clang checker + +- windows source code fixes to reduce number of compile time warnings + (eventual goal is to be able to compile with -Werror on MinGW), mostly + related to signed/unsigned char * conversions, printf() format specifiers + and unused variables. + +- avoid endless loop on logging with --management + --verb 6+ + +- build (but not run) unit tests on MinGW cross compiles, and run them + when building with GitHub Actions. + +- add unit test for parts of cryptoapi.c + +- add debug logging to help with diagnosing windows driver selection + +- disable DCO if proxy config is set via management interface + +- do not crash on Android if run without --management + +- improve documentation about cipher negotiation and OpenVPN3 + +- for x86 windows builds, use proper calling conventions for dco-win + (__stdcall) + +- differentiate "dhcp-option ..." options into "needs an interface with + true DHCP service" (tap-windows) and "can also be installed by IPAPI + or service, and can be used on non-DHCP interfaces" (wintun, dco-win) + +- windows interactive service: fix possible double-free if "--block-dns" + installation fails due to "security products" interfering + (Github OpenVPN/openvpn#232) + +- "make dist": package ovpn_dco_freebsd.h to permit building from tarballs + on FreeBSD 14 + Overview of changes in 2.6.0, relative to 2.6_rc2 ================================================= diff --git a/version.m4 b/version.m4 index 64dee8a4c4d..0164fe33ccf 100644 --- a/version.m4 +++ b/version.m4 @@ -3,12 +3,12 @@ define([PRODUCT_NAME], [OpenVPN]) define([PRODUCT_TARNAME], [openvpn]) define([PRODUCT_VERSION_MAJOR], [2]) define([PRODUCT_VERSION_MINOR], [6]) -define([PRODUCT_VERSION_PATCH], [.0]) +define([PRODUCT_VERSION_PATCH], [.1]) m4_append([PRODUCT_VERSION], [PRODUCT_VERSION_MAJOR]) m4_append([PRODUCT_VERSION], [PRODUCT_VERSION_MINOR], [[.]]) m4_append([PRODUCT_VERSION], [PRODUCT_VERSION_PATCH], [[]]) define([PRODUCT_BUGREPORT], [openvpn-users@lists.sourceforge.net]) -define([PRODUCT_VERSION_RESOURCE], [2,6,0,5]) +define([PRODUCT_VERSION_RESOURCE], [2,6,1,0]) dnl define the TAP version define([PRODUCT_TAP_WIN_COMPONENT_ID], [tap0901]) define([PRODUCT_TAP_WIN_MIN_MAJOR], [9]) From 35104bdc937191d49c3505a354444eb6a267e9ee Mon Sep 17 00:00:00 2001 From: Arne Schwabe Date: Wed, 8 Mar 2023 16:19:45 +0100 Subject: [PATCH 045/229] Set netlink socket to be non-blocking Even though we use select/poll to explicitly query when the netlink socket is ready for read, sometimes we end up reading from the socket when it is not ready to read and then the process hangs for several seconds (20-30s). Avoid this situation by setting the socket to be non-blocking, so we get a status in this case that allows us to continue. Change-Id: I35447c23a9350176007df5455bf9451021e9856d Signed-off-by: Arne Schwabe Acked-by: Antonio Quartulli Message-Id: <20230308151945.3670151-1-arne@rfc2549.org> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26353.html Signed-off-by: Gert Doering (cherry picked from commit 7aa3520768a68fb6a73ab64569c7be5d571f86fc) --- src/openvpn/dco_linux.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/openvpn/dco_linux.c b/src/openvpn/dco_linux.c index c84f9cfe1ba..3ca5b50be99 100644 --- a/src/openvpn/dco_linux.c +++ b/src/openvpn/dco_linux.c @@ -355,7 +355,9 @@ ovpn_dco_init_netlink(dco_context_t *dco) nl_geterror(ret)); } + /* set close on exec and non-block on the netlink socket */ set_cloexec(nl_socket_get_fd(dco->nl_sock)); + set_nonblock(nl_socket_get_fd(dco->nl_sock)); dco->nl_cb = nl_cb_alloc(NL_CB_DEFAULT); if (!dco->nl_cb) From ae6068842854278de70264218516b0e4fcdfc6d9 Mon Sep 17 00:00:00 2001 From: Arne Schwabe Date: Thu, 9 Mar 2023 13:00:31 +0100 Subject: [PATCH 046/229] Ensure n = 2 is set in key2 struct in tls_crypt_v2_unwrap_client_key The ASSERT in xor_key2 assumes that all methods that load a key2 struct correctly set n=2. However, tls_crypt_v2_unwrap_client_key loads a key without setting n = 2, triggering the assert. Github: Closes and reported in OpenVPN/openvpn#272 Change-Id: Iaeb163d83b95818e0b26faf9d25e7737dc8ecb23 Signed-off-by: Arne Schwabe Acked-by: Antonio Quartulli Message-Id: <20230309120031.3780130-1-arne@rfc2549.org> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26363.html Signed-off-by: Gert Doering (cherry picked from commit 85832307fcb41c229ccb7ba83984726757eb32f7) --- src/openvpn/tls_crypt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/openvpn/tls_crypt.c b/src/openvpn/tls_crypt.c index 3b68d186bb4..88b2d6d7cce 100644 --- a/src/openvpn/tls_crypt.c +++ b/src/openvpn/tls_crypt.c @@ -532,6 +532,7 @@ tls_crypt_v2_unwrap_client_key(struct key2 *client_key, struct buffer *metadata, } memcpy(&client_key->keys, BPTR(&plaintext), sizeof(client_key->keys)); ASSERT(buf_advance(&plaintext, sizeof(client_key->keys))); + client_key->n = 2; if (!buf_copy(metadata, &plaintext)) { From 5eb94ce9ef2d4095004a0a3c9dbf3aeae78cf371 Mon Sep 17 00:00:00 2001 From: Frank Lichtenheld Date: Wed, 8 Mar 2023 16:07:04 +0100 Subject: [PATCH 047/229] tests/unit_tests: Fix 'make distcheck' with subdir-objects enabled Commit 7f72abcf8a56bb35a510a3409e03a4e2aaba50da enabled subdir-objects when using automake 1.16+. There is an issue with the handling of .deps directories with this option. While automake 1.16 fixed subdir-objects to work at all when _SOURCES contains "unexpanded references" and it did fix subdir-objects to work with out-of-tree build for "source files specified with an explicit '$(srcdir)'" those fixes are not transitive. "unexpanded references" still break out-of-tree builds when enforcing a read-only source dir like 'make distcheck' does. When using *explicit* references to srcdir and top_srcdir it works correctly. Signed-off-by: Frank Lichtenheld Acked-by: Selva Nair Message-Id: <20230308150704.128797-1-frank@lichtenheld.com> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26352.html Signed-off-by: Gert Doering (cherry picked from commit 838474145933199a62d1f59fbc2df14e4fbd57f3) --- tests/unit_tests/openvpn/Makefile.am | 220 +++++++++--------- tests/unit_tests/plugins/auth-pam/Makefile.am | 6 +- 2 files changed, 110 insertions(+), 116 deletions(-) diff --git a/tests/unit_tests/openvpn/Makefile.am b/tests/unit_tests/openvpn/Makefile.am index ee0a3d8aaa1..339c7ef3c09 100644 --- a/tests/unit_tests/openvpn/Makefile.am +++ b/tests/unit_tests/openvpn/Makefile.am @@ -30,189 +30,185 @@ if HAVE_SITNL check_PROGRAMS += networking_testdriver endif -openvpn_includedir = $(top_srcdir)/include -openvpn_srcdir = $(top_srcdir)/src/openvpn -compat_srcdir = $(top_srcdir)/src/compat - -argv_testdriver_CFLAGS = @TEST_CFLAGS@ -I$(openvpn_srcdir) -I$(compat_srcdir) -argv_testdriver_LDFLAGS = @TEST_LDFLAGS@ -L$(openvpn_srcdir) -Wl,--wrap=parse_line +argv_testdriver_CFLAGS = @TEST_CFLAGS@ -I$(top_srcdir)/src/openvpn -I$(top_srcdir)/src/compat +argv_testdriver_LDFLAGS = @TEST_LDFLAGS@ -L$(top_srcdir)/src/openvpn -Wl,--wrap=parse_line argv_testdriver_SOURCES = test_argv.c mock_msg.c mock_msg.h \ mock_get_random.c \ - $(openvpn_srcdir)/platform.c \ - $(openvpn_srcdir)/buffer.c \ - $(openvpn_srcdir)/win32-util.c \ - $(openvpn_srcdir)/argv.c + $(top_srcdir)/src/openvpn/platform.c \ + $(top_srcdir)/src/openvpn/buffer.c \ + $(top_srcdir)/src/openvpn/win32-util.c \ + $(top_srcdir)/src/openvpn/argv.c -buffer_testdriver_CFLAGS = @TEST_CFLAGS@ -I$(openvpn_srcdir) -I$(compat_srcdir) -buffer_testdriver_LDFLAGS = @TEST_LDFLAGS@ -L$(openvpn_srcdir) -Wl,--wrap=parse_line +buffer_testdriver_CFLAGS = @TEST_CFLAGS@ -I$(top_srcdir)/src/openvpn -I$(top_srcdir)/src/compat +buffer_testdriver_LDFLAGS = @TEST_LDFLAGS@ -L$(top_srcdir)/src/openvpn -Wl,--wrap=parse_line buffer_testdriver_SOURCES = test_buffer.c mock_msg.c mock_msg.h \ mock_get_random.c \ - $(openvpn_srcdir)/win32-util.c \ - $(openvpn_srcdir)/platform.c + $(top_srcdir)/src/openvpn/win32-util.c \ + $(top_srcdir)/src/openvpn/platform.c crypto_testdriver_CFLAGS = @TEST_CFLAGS@ \ - -I$(openvpn_includedir) -I$(compat_srcdir) -I$(openvpn_srcdir) + -I$(top_srcdir)/include -I$(top_srcdir)/src/compat -I$(top_srcdir)/src/openvpn crypto_testdriver_LDFLAGS = @TEST_LDFLAGS@ crypto_testdriver_SOURCES = test_crypto.c mock_msg.c mock_msg.h \ - $(openvpn_srcdir)/buffer.c \ - $(openvpn_srcdir)/crypto.c \ - $(openvpn_srcdir)/crypto_mbedtls.c \ - $(openvpn_srcdir)/crypto_openssl.c \ - $(openvpn_srcdir)/otime.c \ - $(openvpn_srcdir)/packet_id.c \ - $(openvpn_srcdir)/platform.c \ - $(openvpn_srcdir)/mtu.c \ - $(openvpn_srcdir)/win32-util.c \ - $(openvpn_srcdir)/mss.c + $(top_srcdir)/src/openvpn/buffer.c \ + $(top_srcdir)/src/openvpn/crypto.c \ + $(top_srcdir)/src/openvpn/crypto_mbedtls.c \ + $(top_srcdir)/src/openvpn/crypto_openssl.c \ + $(top_srcdir)/src/openvpn/otime.c \ + $(top_srcdir)/src/openvpn/packet_id.c \ + $(top_srcdir)/src/openvpn/platform.c \ + $(top_srcdir)/src/openvpn/mtu.c \ + $(top_srcdir)/src/openvpn/win32-util.c \ + $(top_srcdir)/src/openvpn/mss.c packet_id_testdriver_CFLAGS = @TEST_CFLAGS@ \ - -I$(openvpn_includedir) -I$(compat_srcdir) -I$(openvpn_srcdir) + -I$(top_srcdir)/include -I$(top_srcdir)/src/compat -I$(top_srcdir)/src/openvpn packet_id_testdriver_LDFLAGS = @TEST_LDFLAGS@ packet_id_testdriver_SOURCES = test_packet_id.c mock_msg.c mock_msg.h \ mock_get_random.c \ - $(openvpn_srcdir)/buffer.c \ - $(openvpn_srcdir)/otime.c \ - $(openvpn_srcdir)/packet_id.c \ - $(openvpn_srcdir)/platform.c \ - $(openvpn_srcdir)/reliable.c \ - $(openvpn_srcdir)/win32-util.c \ - $(openvpn_srcdir)/session_id.c + $(top_srcdir)/src/openvpn/buffer.c \ + $(top_srcdir)/src/openvpn/otime.c \ + $(top_srcdir)/src/openvpn/packet_id.c \ + $(top_srcdir)/src/openvpn/platform.c \ + $(top_srcdir)/src/openvpn/reliable.c \ + $(top_srcdir)/src/openvpn/win32-util.c \ + $(top_srcdir)/src/openvpn/session_id.c pkt_testdriver_CFLAGS = @TEST_CFLAGS@ \ - -I$(openvpn_includedir) -I$(compat_srcdir) -I$(openvpn_srcdir) + -I$(top_srcdir)/include -I$(top_srcdir)/src/compat -I$(top_srcdir)/src/openvpn pkt_testdriver_LDFLAGS = @TEST_LDFLAGS@ pkt_testdriver_SOURCES = test_pkt.c mock_msg.c mock_msg.h \ - $(openvpn_srcdir)/argv.c \ - $(openvpn_srcdir)/base64.c \ - $(openvpn_srcdir)/buffer.c \ - $(openvpn_srcdir)/crypto.c \ - $(openvpn_srcdir)/crypto_mbedtls.c \ - $(openvpn_srcdir)/crypto_openssl.c \ - $(openvpn_srcdir)/env_set.c \ - $(openvpn_srcdir)/otime.c \ - $(openvpn_srcdir)/packet_id.c \ - $(openvpn_srcdir)/platform.c \ - $(openvpn_srcdir)/reliable.c \ - $(openvpn_srcdir)/run_command.c \ - $(openvpn_srcdir)/session_id.c \ - $(openvpn_srcdir)/ssl_pkt.c \ - $(openvpn_srcdir)/win32-util.c \ - $(openvpn_srcdir)/tls_crypt.c + $(top_srcdir)/src/openvpn/argv.c \ + $(top_srcdir)/src/openvpn/base64.c \ + $(top_srcdir)/src/openvpn/buffer.c \ + $(top_srcdir)/src/openvpn/crypto.c \ + $(top_srcdir)/src/openvpn/crypto_mbedtls.c \ + $(top_srcdir)/src/openvpn/crypto_openssl.c \ + $(top_srcdir)/src/openvpn/env_set.c \ + $(top_srcdir)/src/openvpn/otime.c \ + $(top_srcdir)/src/openvpn/packet_id.c \ + $(top_srcdir)/src/openvpn/platform.c \ + $(top_srcdir)/src/openvpn/reliable.c \ + $(top_srcdir)/src/openvpn/run_command.c \ + $(top_srcdir)/src/openvpn/session_id.c \ + $(top_srcdir)/src/openvpn/ssl_pkt.c \ + $(top_srcdir)/src/openvpn/win32-util.c \ + $(top_srcdir)/src/openvpn/tls_crypt.c if !WIN32 tls_crypt_testdriver_CFLAGS = @TEST_CFLAGS@ \ - -I$(openvpn_includedir) -I$(compat_srcdir) -I$(openvpn_srcdir) + -I$(top_srcdir)/include -I$(top_srcdir)/src/compat -I$(top_srcdir)/src/openvpn tls_crypt_testdriver_LDFLAGS = @TEST_LDFLAGS@ \ -Wl,--wrap=buffer_read_from_file \ -Wl,--wrap=buffer_write_file \ -Wl,--wrap=parse_line \ -Wl,--wrap=rand_bytes tls_crypt_testdriver_SOURCES = test_tls_crypt.c mock_msg.c mock_msg.h \ - $(openvpn_srcdir)/argv.c \ - $(openvpn_srcdir)/base64.c \ - $(openvpn_srcdir)/buffer.c \ - $(openvpn_srcdir)/crypto.c \ - $(openvpn_srcdir)/crypto_mbedtls.c \ - $(openvpn_srcdir)/crypto_openssl.c \ - $(openvpn_srcdir)/env_set.c \ - $(openvpn_srcdir)/otime.c \ - $(openvpn_srcdir)/packet_id.c \ - $(openvpn_srcdir)/platform.c \ - $(openvpn_srcdir)/run_command.c + $(top_srcdir)/src/openvpn/argv.c \ + $(top_srcdir)/src/openvpn/base64.c \ + $(top_srcdir)/src/openvpn/buffer.c \ + $(top_srcdir)/src/openvpn/crypto.c \ + $(top_srcdir)/src/openvpn/crypto_mbedtls.c \ + $(top_srcdir)/src/openvpn/crypto_openssl.c \ + $(top_srcdir)/src/openvpn/env_set.c \ + $(top_srcdir)/src/openvpn/otime.c \ + $(top_srcdir)/src/openvpn/packet_id.c \ + $(top_srcdir)/src/openvpn/platform.c \ + $(top_srcdir)/src/openvpn/run_command.c endif if HAVE_SITNL networking_testdriver_CFLAGS = @TEST_CFLAGS@ \ - -I$(openvpn_includedir) -I$(compat_srcdir) -I$(openvpn_srcdir) \ + -I$(top_srcdir)/include -I$(top_srcdir)/src/compat -I$(top_srcdir)/src/openvpn \ $(OPTIONAL_CRYPTO_CFLAGS) -networking_testdriver_LDFLAGS = @TEST_LDFLAGS@ -L$(openvpn_srcdir) \ +networking_testdriver_LDFLAGS = @TEST_LDFLAGS@ -L$(top_srcdir)/src/openvpn \ $(OPTIONAL_CRYPTO_LIBS) networking_testdriver_SOURCES = test_networking.c mock_msg.c \ - $(openvpn_srcdir)/networking_sitnl.c \ - $(openvpn_srcdir)/buffer.c \ - $(openvpn_srcdir)/crypto.c \ - $(openvpn_srcdir)/crypto_mbedtls.c \ - $(openvpn_srcdir)/crypto_openssl.c \ - $(openvpn_srcdir)/otime.c \ - $(openvpn_srcdir)/packet_id.c \ - $(openvpn_srcdir)/platform.c + $(top_srcdir)/src/openvpn/networking_sitnl.c \ + $(top_srcdir)/src/openvpn/buffer.c \ + $(top_srcdir)/src/openvpn/crypto.c \ + $(top_srcdir)/src/openvpn/crypto_mbedtls.c \ + $(top_srcdir)/src/openvpn/crypto_openssl.c \ + $(top_srcdir)/src/openvpn/otime.c \ + $(top_srcdir)/src/openvpn/packet_id.c \ + $(top_srcdir)/src/openvpn/platform.c endif provider_testdriver_CFLAGS = @TEST_CFLAGS@ \ - -I$(openvpn_includedir) -I$(compat_srcdir) -I$(openvpn_srcdir) \ + -I$(top_srcdir)/include -I$(top_srcdir)/src/compat -I$(top_srcdir)/src/openvpn \ $(OPTIONAL_CRYPTO_CFLAGS) provider_testdriver_LDFLAGS = @TEST_LDFLAGS@ \ $(OPTIONAL_CRYPTO_LIBS) provider_testdriver_SOURCES = test_provider.c mock_msg.c \ - $(openvpn_srcdir)/xkey_helper.c \ - $(openvpn_srcdir)/xkey_provider.c \ - $(openvpn_srcdir)/buffer.c \ - $(openvpn_srcdir)/base64.c \ + $(top_srcdir)/src/openvpn/xkey_helper.c \ + $(top_srcdir)/src/openvpn/xkey_provider.c \ + $(top_srcdir)/src/openvpn/buffer.c \ + $(top_srcdir)/src/openvpn/base64.c \ mock_get_random.c \ - $(openvpn_srcdir)/win32-util.c \ - $(openvpn_srcdir)/platform.c + $(top_srcdir)/src/openvpn/win32-util.c \ + $(top_srcdir)/src/openvpn/platform.c if WIN32 cryptoapi_testdriver_CFLAGS = @TEST_CFLAGS@ \ - -I$(openvpn_includedir) -I$(compat_srcdir) -I$(openvpn_srcdir) \ + -I$(top_srcdir)/include -I$(top_srcdir)/src/compat -I$(top_srcdir)/src/openvpn \ $(OPTIONAL_CRYPTO_CFLAGS) cryptoapi_testdriver_LDFLAGS = @TEST_LDFLAGS@ \ $(OPTIONAL_CRYPTO_LIBS) -lcrypt32 -lncrypt cryptoapi_testdriver_SOURCES = test_cryptoapi.c mock_msg.c \ - $(openvpn_srcdir)/xkey_helper.c \ - $(openvpn_srcdir)/buffer.c \ - $(openvpn_srcdir)/base64.c \ - $(openvpn_srcdir)/platform.c \ + $(top_srcdir)/src/openvpn/xkey_helper.c \ + $(top_srcdir)/src/openvpn/buffer.c \ + $(top_srcdir)/src/openvpn/base64.c \ + $(top_srcdir)/src/openvpn/platform.c \ mock_get_random.c \ - $(openvpn_srcdir)/win32-util.c + $(top_srcdir)/src/openvpn/win32-util.c endif auth_token_testdriver_CFLAGS = @TEST_CFLAGS@ \ - -I$(openvpn_includedir) -I$(compat_srcdir) -I$(openvpn_srcdir) \ + -I$(top_srcdir)/include -I$(top_srcdir)/src/compat -I$(top_srcdir)/src/openvpn \ $(OPTIONAL_CRYPTO_CFLAGS) auth_token_testdriver_LDFLAGS = @TEST_LDFLAGS@ \ $(OPTIONAL_CRYPTO_LIBS) auth_token_testdriver_SOURCES = test_auth_token.c mock_msg.c \ - $(openvpn_srcdir)/buffer.c \ - $(openvpn_srcdir)/crypto.c \ - $(openvpn_srcdir)/crypto_mbedtls.c \ - $(openvpn_srcdir)/crypto_openssl.c \ - $(openvpn_srcdir)/otime.c \ - $(openvpn_srcdir)/packet_id.c \ - $(openvpn_srcdir)/platform.c \ - $(openvpn_srcdir)/win32-util.c \ - $(openvpn_srcdir)/base64.c + $(top_srcdir)/src/openvpn/buffer.c \ + $(top_srcdir)/src/openvpn/crypto.c \ + $(top_srcdir)/src/openvpn/crypto_mbedtls.c \ + $(top_srcdir)/src/openvpn/crypto_openssl.c \ + $(top_srcdir)/src/openvpn/otime.c \ + $(top_srcdir)/src/openvpn/packet_id.c \ + $(top_srcdir)/src/openvpn/platform.c \ + $(top_srcdir)/src/openvpn/win32-util.c \ + $(top_srcdir)/src/openvpn/base64.c ncp_testdriver_CFLAGS = @TEST_CFLAGS@ \ - -I$(openvpn_includedir) -I$(compat_srcdir) -I$(openvpn_srcdir) \ + -I$(top_srcdir)/include -I$(top_srcdir)/src/compat -I$(top_srcdir)/src/openvpn \ $(OPTIONAL_CRYPTO_CFLAGS) ncp_testdriver_LDFLAGS = @TEST_LDFLAGS@ \ $(OPTIONAL_CRYPTO_LIBS) ncp_testdriver_SOURCES = test_ncp.c mock_msg.c \ - $(openvpn_srcdir)/buffer.c \ - $(openvpn_srcdir)/crypto.c \ - $(openvpn_srcdir)/crypto_mbedtls.c \ - $(openvpn_srcdir)/crypto_openssl.c \ - $(openvpn_srcdir)/otime.c \ - $(openvpn_srcdir)/packet_id.c \ - $(openvpn_srcdir)/platform.c \ - $(openvpn_srcdir)/win32-util.c \ - $(compat_srcdir)/compat-strsep.c \ - $(openvpn_srcdir)/ssl_util.c + $(top_srcdir)/src/openvpn/buffer.c \ + $(top_srcdir)/src/openvpn/crypto.c \ + $(top_srcdir)/src/openvpn/crypto_mbedtls.c \ + $(top_srcdir)/src/openvpn/crypto_openssl.c \ + $(top_srcdir)/src/openvpn/otime.c \ + $(top_srcdir)/src/openvpn/packet_id.c \ + $(top_srcdir)/src/openvpn/platform.c \ + $(top_srcdir)/src/openvpn/win32-util.c \ + $(top_srcdir)/src/compat/compat-strsep.c \ + $(top_srcdir)/src/openvpn/ssl_util.c misc_testdriver_CFLAGS = @TEST_CFLAGS@ \ - -I$(openvpn_includedir) -I$(compat_srcdir) -I$(openvpn_srcdir) + -I$(top_srcdir)/include -I$(top_srcdir)/src/compat -I$(top_srcdir)/src/openvpn misc_testdriver_LDFLAGS = @TEST_LDFLAGS@ misc_testdriver_SOURCES = test_misc.c mock_msg.c \ mock_get_random.c \ - $(openvpn_srcdir)/buffer.c \ - $(openvpn_srcdir)/options_util.c \ - $(openvpn_srcdir)/ssl_util.c \ - $(openvpn_srcdir)/win32-util.c \ - $(openvpn_srcdir)/platform.c + $(top_srcdir)/src/openvpn/buffer.c \ + $(top_srcdir)/src/openvpn/options_util.c \ + $(top_srcdir)/src/openvpn/ssl_util.c \ + $(top_srcdir)/src/openvpn/win32-util.c \ + $(top_srcdir)/src/openvpn/platform.c diff --git a/tests/unit_tests/plugins/auth-pam/Makefile.am b/tests/unit_tests/plugins/auth-pam/Makefile.am index 07233eee77e..de5e96e6e19 100644 --- a/tests/unit_tests/plugins/auth-pam/Makefile.am +++ b/tests/unit_tests/plugins/auth-pam/Makefile.am @@ -5,8 +5,6 @@ check_PROGRAMS = auth_pam_testdriver TESTS = $(check_PROGRAMS) endif -sut_sourcedir = $(top_srcdir)/src/plugins/auth-pam - -auth_pam_testdriver_SOURCES = test_search_and_replace.c $(sut_sourcedir)/utils.h $(sut_sourcedir)/utils.c -auth_pam_testdriver_CFLAGS = @TEST_CFLAGS@ -I$(sut_sourcedir) +auth_pam_testdriver_SOURCES = test_search_and_replace.c $(top_srcdir)/src/plugins/auth-pam/utils.h $(top_srcdir)/src/plugins/auth-pam/utils.c +auth_pam_testdriver_CFLAGS = @TEST_CFLAGS@ -I$(top_srcdir)/src/plugins/auth-pam auth_pam_testdriver_LDFLAGS = @TEST_LDFLAGS@ From 321b04fac8aaaad254fe884472109042d8fb83d7 Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Thu, 9 Mar 2023 22:03:44 +0100 Subject: [PATCH 048/229] dco: don't use NetLink to exchange control packets Using NetLink for control messages did not work out as it did lead to kernel side buffer congestion during heavy client activity. With this patch DCO will redirect control packets directly to the transport socket without altering them, so that userspace can happily process them as usual. NOTE: this is an API breaking change. Up to this commit, the userland requests a kernel module called "ovpn-dco" which does control messages via netlink. From this commit on, OpenVPN requests a kernel module named "ovpn-dco-v2" which brings the kernel change corresponding to this commit. If the system only has "the wrong module" available (either way), OpenVPN will log ... Kernel support for ovpn-dco missing, disabling data channel offload. and proceed without kernel support. Change-Id: Ia1297c3ae9a28b188ed21ad21ae96fff3d02ee4d [lev@openvpn.net: ensure win_dco flag is still exposed] Signed-off-by: Antonio Quartulli Acked-by: Arne Schwabe Message-Id: <20230309210344.5763-1-a@unstable.cc> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26384.html Signed-off-by: Gert Doering (cherry picked from commit ac1d24286ad4788415ce6f56e97c18562d1cadbd) --- src/openvpn/dco.c | 12 ----- src/openvpn/dco.h | 16 ------ src/openvpn/dco_freebsd.c | 10 ---- src/openvpn/dco_freebsd.h | 2 - src/openvpn/dco_linux.c | 101 ----------------------------------- src/openvpn/dco_linux.h | 2 - src/openvpn/dco_win.c | 8 --- src/openvpn/forward.c | 63 +++------------------- src/openvpn/init.c | 3 +- src/openvpn/mtcp.c | 22 +------- src/openvpn/multi.c | 39 +------------- src/openvpn/ovpn_dco_linux.h | 16 +----- src/openvpn/socket.c | 8 +-- src/openvpn/socket.h | 29 ++++++---- 14 files changed, 35 insertions(+), 296 deletions(-) diff --git a/src/openvpn/dco.c b/src/openvpn/dco.c index b53332a8777..308578b4703 100644 --- a/src/openvpn/dco.c +++ b/src/openvpn/dco.c @@ -485,7 +485,6 @@ dco_p2p_add_new_peer(struct context *c) } c->c2.tls_multi->dco_peer_id = multi->peer_id; - c->c2.link_socket->dco_installed = true; return 0; } @@ -605,17 +604,6 @@ dco_multi_add_new_peer(struct multi_context *m, struct multi_instance *mi) c->c2.tls_multi->dco_peer_id = peer_id; - if (c->mode == CM_CHILD_TCP) - { - multi_tcp_dereference_instance(m->mtcp, mi); - if (close(sd)) - { - msg(D_DCO|M_ERRNO, "error closing TCP socket after DCO handover"); - } - c->c2.link_socket->dco_installed = true; - c->c2.link_socket->sd = SOCKET_UNDEFINED; - } - return 0; } diff --git a/src/openvpn/dco.h b/src/openvpn/dco.h index 18a9d78be6f..2fe671bf6fc 100644 --- a/src/openvpn/dco.h +++ b/src/openvpn/dco.h @@ -127,15 +127,6 @@ void close_tun_dco(struct tuntap *tt, openvpn_net_ctx_t *ctx); */ int dco_do_read(dco_context_t *dco); -/** - * Write data to the DCO communication channel (control packet expected) - * - * @param dco the DCO context - * @param peer_id the ID of the peer to send the data to - * @param buf the buffer containing the data to send - */ -int dco_do_write(dco_context_t *dco, int peer_id, struct buffer *buf); - /** * Install a DCO in the main event loop */ @@ -301,13 +292,6 @@ dco_do_read(dco_context_t *dco) return 0; } -static inline int -dco_do_write(dco_context_t *dco, int peer_id, struct buffer *buf) -{ - ASSERT(false); - return 0; -} - static inline void dco_event_set(dco_context_t *dco, struct event_set *es, void *arg) { diff --git a/src/openvpn/dco_freebsd.c b/src/openvpn/dco_freebsd.c index cd4083c4968..92de5f04b1d 100644 --- a/src/openvpn/dco_freebsd.c +++ b/src/openvpn/dco_freebsd.c @@ -142,7 +142,6 @@ open_fd(dco_context_t *dco) { dco->open = true; } - dco->dco_packet_in = alloc_buf(PAGE_SIZE); return dco->fd; } @@ -560,15 +559,6 @@ dco_do_read(dco_context_t *dco) return 0; } -int -dco_do_write(dco_context_t *dco, int peer_id, struct buffer *buf) -{ - /* Control packets are passed through the socket, so this should never get - * called. See should_use_dco_socket(). */ - ASSERT(0); - return -EINVAL; -} - bool dco_available(int msglevel) { diff --git a/src/openvpn/dco_freebsd.h b/src/openvpn/dco_freebsd.h index 970beca0e74..a07f9b69fc9 100644 --- a/src/openvpn/dco_freebsd.h +++ b/src/openvpn/dco_freebsd.h @@ -51,8 +51,6 @@ typedef struct dco_context { char ifname[IFNAMSIZ]; - struct buffer dco_packet_in; - int dco_message_type; int dco_message_peer_id; int dco_del_peer_reason; diff --git a/src/openvpn/dco_linux.c b/src/openvpn/dco_linux.c index 3ca5b50be99..2b349529fff 100644 --- a/src/openvpn/dco_linux.c +++ b/src/openvpn/dco_linux.c @@ -436,24 +436,6 @@ ovpn_dco_register(dco_context_t *dco) { msg(M_ERR, "%s: failed to join groups: %d", __func__, ret); } - - /* Register for non-data packets that ovpn-dco may receive. They will be - * forwarded to userspace - */ - struct nl_msg *nl_msg = ovpn_dco_nlmsg_create(dco, OVPN_CMD_REGISTER_PACKET); - if (!nl_msg) - { - msg(M_ERR, "%s: cannot allocate message to register for control packets", - __func__); - } - - ret = ovpn_nl_msg_send(dco, nl_msg, NULL, __func__); - if (ret) - { - msg(M_ERR, "%s: failed to register for control packets: %d", __func__, - ret); - } - nlmsg_free(nl_msg); } int @@ -476,8 +458,6 @@ open_tun_dco(struct tuntap *tt, openvpn_net_ctx_t *ctx, const char *dev) } tt->actual_name = string_alloc(dev, NULL); - uint8_t *dcobuf = malloc(65536); - buf_set_write(&tt->dco.dco_packet_in, dcobuf, 65536); tt->dco.dco_message_peer_id = -1; ovpn_dco_register(&tt->dco); @@ -492,7 +472,6 @@ close_tun_dco(struct tuntap *tt, openvpn_net_ctx_t *ctx) net_iface_del(ctx, tt->actual_name); ovpn_dco_uninit_netlink(&tt->dco); - free(tt->dco.dco_packet_in.data); } int @@ -823,51 +802,6 @@ ovpn_handle_msg(struct nl_msg *msg, void *arg) break; } - case OVPN_CMD_PACKET: - { - if (!attrs[OVPN_ATTR_PACKET]) - { - msg(D_DCO, "ovpn-dco: no packet in OVPN_CMD_PACKET message"); - return NL_SKIP; - } - struct nlattr *pkt_attrs[OVPN_PACKET_ATTR_MAX + 1]; - - if (nla_parse_nested(pkt_attrs, OVPN_PACKET_ATTR_MAX, - attrs[OVPN_ATTR_PACKET], NULL)) - { - msg(D_DCO, "received bogus cmd packet data from ovpn-dco"); - return NL_SKIP; - } - if (!pkt_attrs[OVPN_PACKET_ATTR_PEER_ID]) - { - msg(D_DCO, "ovpn-dco: Received OVPN_CMD_PACKET message without peer id"); - return NL_SKIP; - } - if (!pkt_attrs[OVPN_PACKET_ATTR_PACKET]) - { - msg(D_DCO, "ovpn-dco: Received OVPN_CMD_PACKET message without packet"); - return NL_SKIP; - } - - unsigned int peerid = nla_get_u32(pkt_attrs[OVPN_PACKET_ATTR_PEER_ID]); - - uint8_t *data = nla_data(pkt_attrs[OVPN_PACKET_ATTR_PACKET]); - int len = nla_len(pkt_attrs[OVPN_PACKET_ATTR_PACKET]); - - msg(D_DCO_DEBUG, "ovpn-dco: received OVPN_PACKET_ATTR_PACKET, ifindex: %d peer-id: %d, len %d", - ifindex, peerid, len); - if (BLEN(&dco->dco_packet_in) > 0) - { - msg(D_DCO, "DCO packet buffer still full?!"); - return NL_SKIP; - } - buf_init(&dco->dco_packet_in, 0); - buf_write(&dco->dco_packet_in, data, len); - dco->dco_message_peer_id = peerid; - dco->dco_message_type = OVPN_CMD_PACKET; - break; - } - default: msg(D_DCO, "ovpn-dco: received unknown command: %d", gnlh->cmd); dco->dco_message_type = 0; @@ -886,41 +820,6 @@ dco_do_read(dco_context_t *dco) return ovpn_nl_recvmsgs(dco, __func__); } -int -dco_do_write(dco_context_t *dco, int peer_id, struct buffer *buf) -{ - packet_size_type len = BLEN(buf); - dmsg(D_STREAM_DEBUG, "DCO: WRITE %d offset=%d", (int)len, buf->offset); - - msg(D_DCO_DEBUG, "%s: peer-id %d, len=%d", __func__, peer_id, len); - - struct nl_msg *nl_msg = ovpn_dco_nlmsg_create(dco, OVPN_CMD_PACKET); - - if (!nl_msg) - { - return -ENOMEM; - } - - struct nlattr *attr = nla_nest_start(nl_msg, OVPN_ATTR_PACKET); - int ret = -EMSGSIZE; - NLA_PUT_U32(nl_msg, OVPN_PACKET_ATTR_PEER_ID, peer_id); - NLA_PUT(nl_msg, OVPN_PACKET_ATTR_PACKET, len, BSTR(buf)); - nla_nest_end(nl_msg, attr); - - ret = ovpn_nl_msg_send(dco, nl_msg, NULL, __func__); - if (ret) - { - goto nla_put_failure; - } - - /* return the length of the written data in case of success */ - ret = len; - -nla_put_failure: - nlmsg_free(nl_msg); - return ret; -} - int dco_get_peer_stats_multi(dco_context_t *dco, struct multi_context *m) { diff --git a/src/openvpn/dco_linux.h b/src/openvpn/dco_linux.h index 4d996d98c6b..d28e3658dff 100644 --- a/src/openvpn/dco_linux.h +++ b/src/openvpn/dco_linux.h @@ -48,8 +48,6 @@ typedef struct unsigned int ifindex; - struct buffer dco_packet_in; - int dco_message_type; int dco_message_peer_id; int dco_del_peer_reason; diff --git a/src/openvpn/dco_win.c b/src/openvpn/dco_win.c index 0931fb30a8c..a805c2a0380 100644 --- a/src/openvpn/dco_win.c +++ b/src/openvpn/dco_win.c @@ -393,14 +393,6 @@ dco_do_read(dco_context_t *dco) return 0; } -int -dco_do_write(dco_context_t *dco, int peer_id, struct buffer *buf) -{ - /* no-op on windows */ - ASSERT(0); - return 0; -} - int dco_get_peer_stats_multi(dco_context_t *dco, struct multi_context *m) { diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c index 257c7c75c31..0e86b58c677 100644 --- a/src/openvpn/forward.c +++ b/src/openvpn/forward.c @@ -1191,7 +1191,6 @@ static void process_incoming_dco(struct context *c) { #if defined(ENABLE_DCO) && (defined(TARGET_LINUX) || defined(TARGET_FREEBSD)) - struct link_socket_info *lsi = get_link_socket_info(c); dco_context_t *dco = &c->c1.tuntap->dco; dco_do_read(dco); @@ -1204,35 +1203,23 @@ process_incoming_dco(struct context *c) msg(D_DCO_DEBUG, "%s: received message for mismatching peer-id %d, " "expected %d", __func__, dco->dco_message_peer_id, c->c2.tls_multi->dco_peer_id); - /* ensure we also drop a message if there is one in the buffer */ - buf_init(&dco->dco_packet_in, 0); return; } - if ((dco->dco_message_type == OVPN_CMD_DEL_PEER) - && (dco->dco_del_peer_reason == OVPN_DEL_PEER_REASON_EXPIRED)) + if (dco->dco_message_type != OVPN_CMD_DEL_PEER) { - msg(D_DCO_DEBUG, "%s: received peer expired notification of for peer-id " - "%d", __func__, dco->dco_message_peer_id); - trigger_ping_timeout_signal(c); + msg(D_DCO_DEBUG, "%s: received message of type %u - ignoring", __func__, + dco->dco_message_type); return; } - if (dco->dco_message_type != OVPN_CMD_PACKET) + if (dco->dco_del_peer_reason == OVPN_DEL_PEER_REASON_EXPIRED) { - msg(D_DCO_DEBUG, "%s: received message of type %u - ignoring", __func__, - dco->dco_message_type); + msg(D_DCO_DEBUG, "%s: received peer expired notification of for peer-id " + "%d", __func__, dco->dco_message_peer_id); + trigger_ping_timeout_signal(c); return; } - - struct buffer orig_buff = c->c2.buf; - c->c2.buf = dco->dco_packet_in; - c->c2.from = lsi->lsa->actual; - - process_incoming_link(c); - - c->c2.buf = orig_buff; - buf_init(&dco->dco_packet_in, 0); #endif /* if defined(ENABLE_DCO) && (defined(TARGET_LINUX) || defined(TARGET_FREEBSD)) */ } @@ -1686,30 +1673,6 @@ process_ip_header(struct context *c, unsigned int flags, struct buffer *buf) } } -/* - * Linux DCO implementations pass the socket to the kernel and - * disallow usage of it from userland for TCP, so (control) packets - * sent and received by OpenVPN need to go through the DCO interface. - * - * Windows DCO needs control packets to be sent via the normal - * standard Overlapped I/O. - * - * FreeBSD DCO allows control packets to pass through the socket in both - * directions. - * - * Hide that complexity (...especially if more platforms show up - * in the future...) in a small inline function. - */ -static inline bool -should_use_dco_socket(struct link_socket *ls) -{ -#if defined(TARGET_LINUX) - return ls->dco_installed && proto_is_tcp(ls->info.proto); -#else - return false; -#endif -} - /* * Input: c->c2.to_link */ @@ -1783,17 +1746,7 @@ process_outgoing_link(struct context *c) socks_preprocess_outgoing_link(c, &to_addr, &size_delta); /* Send packet */ - if (should_use_dco_socket(c->c2.link_socket)) - { - size = dco_do_write(&c->c1.tuntap->dco, - c->c2.tls_multi->dco_peer_id, - &c->c2.to_link); - } - else - { - size = link_socket_write(c->c2.link_socket, &c->c2.to_link, - to_addr); - } + size = link_socket_write(c->c2.link_socket, &c->c2.to_link, to_addr); /* Undo effect of prepend */ link_socket_write_post_size_adjust(&size, size_delta, &c->c2.to_link); diff --git a/src/openvpn/init.c b/src/openvpn/init.c index e67b93d3a7c..124ac76bd9f 100644 --- a/src/openvpn/init.c +++ b/src/openvpn/init.c @@ -3914,8 +3914,7 @@ do_close_link_socket(struct context *c) /* in dco-win case, link socket is a tun handle which is * closed in do_close_tun(). Set it to UNDEFINED so * we won't use WinSock API to close it. */ - if (tuntap_is_dco_win(c->c1.tuntap) && c->c2.link_socket - && c->c2.link_socket->dco_installed) + if (tuntap_is_dco_win(c->c1.tuntap) && c->c2.link_socket) { c->c2.link_socket->sd = SOCKET_UNDEFINED; } diff --git a/src/openvpn/mtcp.c b/src/openvpn/mtcp.c index 59131ac9b4e..6c56a260c94 100644 --- a/src/openvpn/mtcp.c +++ b/src/openvpn/mtcp.c @@ -402,18 +402,6 @@ multi_tcp_wait_lite(struct multi_context *m, struct multi_instance *mi, const in tv_clear(&c->c2.timeval); /* ZERO-TIMEOUT */ - if (mi && mi->context.c2.link_socket->dco_installed) - { - /* If we got a socket that has been handed over to the kernel - * we must not call the normal socket function to figure out - * if it is readable or writable */ - /* Assert that we only have the DCO exptected flags */ - ASSERT(action & (TA_SOCKET_READ | TA_SOCKET_WRITE)); - - /* We are always ready! */ - return action; - } - switch (action) { case TA_TUN_READ: @@ -537,10 +525,7 @@ multi_tcp_dispatch(struct multi_context *m, struct multi_instance *mi, const int case TA_INITIAL: ASSERT(mi); - if (!mi->context.c2.link_socket->dco_installed) - { - multi_tcp_set_global_rw_flags(m, mi); - } + multi_tcp_set_global_rw_flags(m, mi); multi_process_post(m, mi, mpp_flags); break; @@ -590,10 +575,7 @@ multi_tcp_post(struct multi_context *m, struct multi_instance *mi, const int act } else { - if (!c->c2.link_socket->dco_installed) - { - multi_tcp_set_global_rw_flags(m, mi); - } + multi_tcp_set_global_rw_flags(m, mi); } break; diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c index 59c980b0df0..53c17b3a462 100644 --- a/src/openvpn/multi.c +++ b/src/openvpn/multi.c @@ -3202,37 +3202,6 @@ multi_signal_instance(struct multi_context *m, struct multi_instance *mi, const #endif #if defined(ENABLE_DCO) && (defined(TARGET_LINUX) || defined(TARGET_FREEBSD)) -static void -process_incoming_dco_packet(struct multi_context *m, struct multi_instance *mi, - dco_context_t *dco) -{ - if (BLEN(&dco->dco_packet_in) < 1) - { - msg(D_DCO, "Received too short packet for peer %d", - dco->dco_message_peer_id); - goto done; - } - - uint8_t *ptr = BPTR(&dco->dco_packet_in); - uint8_t op = ptr[0] >> P_OPCODE_SHIFT; - if ((op == P_DATA_V1) || (op == P_DATA_V2)) - { - msg(D_DCO, "DCO: received data channel packet for peer %d", - dco->dco_message_peer_id); - goto done; - } - - struct buffer orig_buf = mi->context.c2.buf; - mi->context.c2.buf = dco->dco_packet_in; - - multi_process_incoming_link(m, mi, 0); - - mi->context.c2.buf = orig_buf; - -done: - buf_init(&dco->dco_packet_in, 0); -} - static void process_incoming_del_peer(struct multi_context *m, struct multi_instance *mi, dco_context_t *dco) @@ -3299,11 +3268,7 @@ multi_process_incoming_dco(struct multi_context *m) if ((peer_id < m->max_clients) && (m->instances[peer_id])) { mi = m->instances[peer_id]; - if (dco->dco_message_type == OVPN_CMD_PACKET) - { - process_incoming_dco_packet(m, mi, dco); - } - else if (dco->dco_message_type == OVPN_CMD_DEL_PEER) + if (dco->dco_message_type == OVPN_CMD_DEL_PEER) { process_incoming_del_peer(m, mi, dco); } @@ -3326,8 +3291,6 @@ multi_process_incoming_dco(struct multi_context *m) msg(msglevel, "Received DCO message for unknown peer-id: %d, " "type %d, del_peer_reason %d", peer_id, dco->dco_message_type, dco->dco_del_peer_reason); - /* Also clear the buffer if this was incoming packet for a dropped peer */ - buf_init(&dco->dco_packet_in, 0); } dco->dco_message_type = 0; diff --git a/src/openvpn/ovpn_dco_linux.h b/src/openvpn/ovpn_dco_linux.h index 96395886bf9..d3fd9a8926c 100644 --- a/src/openvpn/ovpn_dco_linux.h +++ b/src/openvpn/ovpn_dco_linux.h @@ -11,7 +11,7 @@ #ifndef _UAPI_LINUX_OVPN_DCO_H_ #define _UAPI_LINUX_OVPN_DCO_H_ -#define OVPN_NL_NAME "ovpn-dco" +#define OVPN_NL_NAME "ovpn-dco-v2" #define OVPN_NL_MULTICAST_GROUP_PEERS "peers" @@ -45,19 +45,6 @@ enum ovpn_nl_commands { OVPN_CMD_DEL_KEY, - /** - * @OVPN_CMD_REGISTER_PACKET: Register for specific packet types to be - * forwarded to userspace - */ - OVPN_CMD_REGISTER_PACKET, - - /** - * @OVPN_CMD_PACKET: Send a packet from userspace to kernelspace. Also - * used to send to userspace packets for which a process had registered - * with OVPN_CMD_REGISTER_PACKET - */ - OVPN_CMD_PACKET, - /** * @OVPN_CMD_GET_PEER: Retrieve the status of a peer or all peers */ @@ -105,7 +92,6 @@ enum ovpn_netlink_attrs { OVPN_ATTR_NEW_KEY, OVPN_ATTR_SWAP_KEYS, OVPN_ATTR_DEL_KEY, - OVPN_ATTR_PACKET, OVPN_ATTR_GET_PEER, __OVPN_ATTR_AFTER_LAST, diff --git a/src/openvpn/socket.c b/src/openvpn/socket.c index eff21ca5663..216f2ad7696 100644 --- a/src/openvpn/socket.c +++ b/src/openvpn/socket.c @@ -2151,7 +2151,7 @@ create_socket_dco_win(struct context *c, struct link_socket *sock, get_server_poll_remaining_time(sock->server_poll_timeout), sig_info); - sock->dco_installed = true; + sock->sockflags |= SF_DCO_WIN; if (sig_info->signal_received) { @@ -3505,7 +3505,7 @@ link_socket_write_udp_posix_sendmsg(struct link_socket *sock, static int socket_get_last_error(const struct link_socket *sock) { - if (sock->dco_installed) + if (socket_is_dco_win(sock)) { return GetLastError(); } @@ -3546,7 +3546,7 @@ socket_recv_queue(struct link_socket *sock, int maxsize) ASSERT(ResetEvent(sock->reads.overlapped.hEvent)); sock->reads.flags = 0; - if (sock->dco_installed) + if (socket_is_dco_win(sock)) { status = ReadFile((HANDLE)sock->sd, wsabuf[0].buf, wsabuf[0].len, &sock->reads.size, &sock->reads.overlapped); @@ -3651,7 +3651,7 @@ socket_send_queue(struct link_socket *sock, struct buffer *buf, const struct lin ASSERT(ResetEvent(sock->writes.overlapped.hEvent)); sock->writes.flags = 0; - if (sock->dco_installed) + if (socket_is_dco_win(sock)) { status = WriteFile((HANDLE)sock->sd, wsabuf[0].buf, wsabuf[0].len, &sock->writes.size, &sock->writes.overlapped); diff --git a/src/openvpn/socket.h b/src/openvpn/socket.h index 605b6ad2f32..bfc1253bee2 100644 --- a/src/openvpn/socket.h +++ b/src/openvpn/socket.h @@ -168,7 +168,6 @@ struct link_socket socket_descriptor_t sd; socket_descriptor_t ctrl_sd; /* only used for UDP over Socks */ - bool dco_installed; #ifdef _WIN32 struct overlapped_io reads; @@ -207,6 +206,7 @@ struct link_socket #define SF_PORT_SHARE (1<<2) #define SF_HOST_RANDOMIZE (1<<3) #define SF_GETADDRINFO_DGRAM (1<<4) +#define SF_DCO_WIN (1<<5) unsigned int sockflags; int mark; const char *bind_dev; @@ -1021,6 +1021,17 @@ stream_buf_read_setup(struct link_socket *sock) } } +/** + * Returns true if we are on Windows and this link is running on DCO-WIN. + * This helper is used to enable DCO-WIN specific logic that is not relevant + * to other platforms. + */ +static inline bool +socket_is_dco_win(const struct link_socket *s) +{ + return s->sockflags & SF_DCO_WIN; +} + /* * Socket Read Routines */ @@ -1036,7 +1047,7 @@ link_socket_read_udp_win32(struct link_socket *sock, struct link_socket_actual *from) { sockethandle_t sh = { .s = sock->sd }; - if (sock->dco_installed) + if (socket_is_dco_win(sock)) { *from = sock->info.lsa->actual; sh.is_handle = true; @@ -1058,12 +1069,8 @@ link_socket_read(struct link_socket *sock, struct buffer *buf, struct link_socket_actual *from) { -#ifdef _WIN32 - if (proto_is_udp(sock->info.proto) || sock->dco_installed) -#else - if (proto_is_udp(sock->info.proto)) -#endif - /* unified UDPv4 and UDPv6, for DCO the kernel + if (proto_is_udp(sock->info.proto) || socket_is_dco_win(sock)) + /* unified UDPv4 and UDPv6, for DCO-WIN the kernel * will strip the length header */ { int res; @@ -1105,7 +1112,7 @@ link_socket_write_win32(struct link_socket *sock, { int err = 0; int status = 0; - sockethandle_t sh = { .s = sock->sd, .is_handle = sock->dco_installed }; + sockethandle_t sh = { .s = sock->sd, .is_handle = socket_is_dco_win(sock) }; if (overlapped_io_active(&sock->writes)) { status = sockethandle_finalize(sh, &sock->writes, NULL, NULL); @@ -1179,9 +1186,9 @@ link_socket_write(struct link_socket *sock, struct buffer *buf, struct link_socket_actual *to) { - if (proto_is_udp(sock->info.proto) || sock->dco_installed) + if (proto_is_udp(sock->info.proto) || socket_is_dco_win(sock)) { - /* unified UDPv4 and UDPv6 and DCO (kernel adds size header) */ + /* unified UDPv4, UDPv6 and DCO-WIN (driver adds length header) */ return link_socket_write_udp(sock, buf, to); } else if (proto_is_tcp(sock->info.proto)) /* unified TCPv4 and TCPv6 */ From 9bd6fff74cf34892339fcab2fb3fc3cee54a2051 Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Thu, 9 Mar 2023 14:14:19 +0100 Subject: [PATCH 049/229] dco: print version to log if available In order to provide better support in case of troubleshooting issues, it's important to know what exact DCO version is loaded on the user system. Therefore print the DCO version during bootup. For Windows and FreeBSD we currently implement a placeholder printing 'v0'. This should be improved with a follow-up patch. For Linux we directly fetch the module version from /sys and print something like: DCO version: 0.1.20230206-15-g580608ec7c59 Change-Id: Ie1f6fa5d12a473d353d84fd119c2430b638e8bcd Signed-off-by: Antonio Quartulli Acked-by: Arne Schwabe Message-Id: <20230309131419.29157-1-a@unstable.cc> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26370.html Signed-off-by: Gert Doering (cherry picked from commit 3da238e677b7801607e6777d9d23eb61e38034c2) --- src/openvpn/dco.h | 15 +++++++++++++++ src/openvpn/dco_freebsd.c | 6 ++++++ src/openvpn/dco_linux.c | 27 +++++++++++++++++++++++++++ src/openvpn/dco_win.c | 6 ++++++ src/openvpn/openvpn.c | 2 ++ src/openvpn/options.c | 11 +++++++++++ src/openvpn/options.h | 2 ++ 7 files changed, 69 insertions(+) diff --git a/src/openvpn/dco.h b/src/openvpn/dco.h index 2fe671bf6fc..96d95c21f64 100644 --- a/src/openvpn/dco.h +++ b/src/openvpn/dco.h @@ -58,6 +58,15 @@ struct tuntap; */ bool dco_available(int msglevel); + +/** + * Return a human readable string representing the DCO version + * + * @param gc the garbage collector to use for any dynamic allocation + * @return a pointer to the string (allocated via gc) containing the string + */ +const char *dco_version_string(struct gc_arena *gc); + /** * Check whether the options struct has any option that is not supported by * our current dco implementation. If so print a warning at warning level @@ -250,6 +259,12 @@ dco_available(int msglevel) return false; } +static inline const char * +dco_version_string(struct gc_arena *gc) +{ + return "not-compiled"; +} + static inline bool dco_check_option(int msglevel, const struct options *o) { diff --git a/src/openvpn/dco_freebsd.c b/src/openvpn/dco_freebsd.c index 92de5f04b1d..cbd2ce20580 100644 --- a/src/openvpn/dco_freebsd.c +++ b/src/openvpn/dco_freebsd.c @@ -614,6 +614,12 @@ dco_available(int msglevel) return available; } +const char * +dco_version_string(struct gc_arena *gc) +{ + return "v0"; +} + void dco_event_set(dco_context_t *dco, struct event_set *es, void *arg) { diff --git a/src/openvpn/dco_linux.c b/src/openvpn/dco_linux.c index 2b349529fff..b2fdbf53f1d 100644 --- a/src/openvpn/dco_linux.c +++ b/src/openvpn/dco_linux.c @@ -843,9 +843,36 @@ dco_available(int msglevel) "Note: Kernel support for ovpn-dco missing, disabling data channel offload."); return false; } + return true; } +const char * +dco_version_string(struct gc_arena *gc) +{ + struct buffer out = alloc_buf_gc(256, gc); + FILE *fp = fopen("/sys/module/ovpn_dco_v2/version", "r"); + if (!fp) + { + return "N/A"; + } + + if (!fgets(BSTR(&out), BCAP(&out), fp)) + { + return "ERR"; + } + + /* remove potential newline at the end of the string */ + char *str = BSTR(&out); + char *nl = strchr(str, '\n'); + if (nl) + { + *nl = '\0'; + } + + return BSTR(&out); +} + void dco_event_set(dco_context_t *dco, struct event_set *es, void *arg) { diff --git a/src/openvpn/dco_win.c b/src/openvpn/dco_win.c index a805c2a0380..26463b387de 100644 --- a/src/openvpn/dco_win.c +++ b/src/openvpn/dco_win.c @@ -385,6 +385,12 @@ dco_available(int msglevel) return false; } +const char * +dco_version_string(struct gc_arena *gc) +{ + return "v0"; +} + int dco_do_read(dco_context_t *dco) { diff --git a/src/openvpn/openvpn.c b/src/openvpn/openvpn.c index cba582769e8..1aaddcdfc94 100644 --- a/src/openvpn/openvpn.c +++ b/src/openvpn/openvpn.c @@ -262,6 +262,8 @@ openvpn_main(int argc, char *argv[]) #endif show_library_versions(M_INFO); + show_dco_version(M_INFO); + /* misc stuff */ pre_setup(&c.options); diff --git a/src/openvpn/options.c b/src/openvpn/options.c index ce7bd0a7970..8f0e21940de 100644 --- a/src/openvpn/options.c +++ b/src/openvpn/options.c @@ -4816,6 +4816,16 @@ show_windows_version(const unsigned int flags) } #endif +void +show_dco_version(const unsigned int flags) +{ +#ifdef ENABLE_DCO + struct gc_arena gc = gc_new(); + msg(flags, "DCO version: %s", dco_version_string(&gc)); + gc_free(&gc); +#endif +} + void show_library_versions(const unsigned int flags) { @@ -4839,6 +4849,7 @@ usage_version(void) #ifdef _WIN32 show_windows_version( M_INFO|M_NOPREFIX ); #endif + show_dco_version(M_INFO | M_NOPREFIX); msg(M_INFO|M_NOPREFIX, "Originally developed by James Yonan"); msg(M_INFO|M_NOPREFIX, "Copyright (C) 2002-2023 OpenVPN Inc "); #ifndef ENABLE_SMALL diff --git a/src/openvpn/options.h b/src/openvpn/options.h index 7df717f73f6..2f25f5d0771 100644 --- a/src/openvpn/options.h +++ b/src/openvpn/options.h @@ -796,6 +796,8 @@ void show_windows_version(const unsigned int flags); #endif +void show_dco_version(const unsigned int flags); + void init_options(struct options *o, const bool init_gc); void uninit_options(struct options *o); From 5617f0f01837ffa01b81dfb2c5c7da8ee4c42072 Mon Sep 17 00:00:00 2001 From: Kristof Provost Date: Thu, 9 Mar 2023 13:23:32 +0100 Subject: [PATCH 050/229] dco: print FreeBSD version Implement dco_version_string() for FreeBSD. Unlike Linux and Windows the DCO driver is built into the operating system itself, so we log the OS version as a proxy for the DCO version. Signed-off-by: Kristof Provost Acked-by: Gert Doering Message-Id: <20230309122332.92490-1-kprovost@netgate.com> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26367.html Signed-off-by: Gert Doering (cherry picked from commit fe0853d2e72dd3a639a95e420ad7eeed6b49e81b) --- src/openvpn/dco_freebsd.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/openvpn/dco_freebsd.c b/src/openvpn/dco_freebsd.c index cbd2ce20580..ecca2a07648 100644 --- a/src/openvpn/dco_freebsd.c +++ b/src/openvpn/dco_freebsd.c @@ -31,6 +31,8 @@ #include #include #include +#include + #include #include "dco_freebsd.h" @@ -617,7 +619,15 @@ dco_available(int msglevel) const char * dco_version_string(struct gc_arena *gc) { - return "v0"; + struct utsname *uts; + ALLOC_OBJ_GC(uts, struct utsname, gc); + + if (uname(uts) != 0) + { + return "N/A"; + } + + return uts->version; } void From be7a5640fbce2d3d56bdcee23e3d76ea15a678d1 Mon Sep 17 00:00:00 2001 From: Arne Schwabe Date: Tue, 14 Mar 2023 15:48:54 +0100 Subject: [PATCH 051/229] Fix memory leaks in open_tun_dco() open_tun_dco_generic() already allocates the tt->actual_name string, which shadows the allocation in the FreeBSD/Linux specific methods. Found-By: clang with asan Change-Id: I51f5fcfff4e5f8203fdb9aec0245cfccd17043cc Signed-off-by: Arne Schwabe Acked-by: Gert Doering Message-Id: <20230314144854.182110-2-arne@rfc2549.org> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26411.html Signed-off-by: Gert Doering (cherry picked from commit 021952705ba4b77a1d94bec85f6f02e6c612a37e) --- src/openvpn/dco_freebsd.c | 1 - src/openvpn/dco_linux.c | 1 - 2 files changed, 2 deletions(-) diff --git a/src/openvpn/dco_freebsd.c b/src/openvpn/dco_freebsd.c index ecca2a07648..225b3cf88df 100644 --- a/src/openvpn/dco_freebsd.c +++ b/src/openvpn/dco_freebsd.c @@ -232,7 +232,6 @@ create_interface(struct tuntap *tt, const char *dev) } snprintf(tt->dco.ifname, IFNAMSIZ, "%s", ifr.ifr_data); - tt->actual_name = string_alloc(tt->dco.ifname, NULL); /* see "Interface Flags" in ifnet(9) */ int i = IFF_POINTOPOINT | IFF_MULTICAST; diff --git a/src/openvpn/dco_linux.c b/src/openvpn/dco_linux.c index b2fdbf53f1d..e5cea3c7198 100644 --- a/src/openvpn/dco_linux.c +++ b/src/openvpn/dco_linux.c @@ -457,7 +457,6 @@ open_tun_dco(struct tuntap *tt, openvpn_net_ctx_t *ctx, const char *dev) msg(M_FATAL, "DCO: cannot retrieve ifindex for interface %s", dev); } - tt->actual_name = string_alloc(dev, NULL); tt->dco.dco_message_peer_id = -1; ovpn_dco_register(&tt->dco); From 1e954cefa0941439ca09598b6131203b975950f8 Mon Sep 17 00:00:00 2001 From: Selva Nair Date: Tue, 14 Mar 2023 08:21:34 -0400 Subject: [PATCH 052/229] Bugfix: Convert ECDSA signature form pkcs11-helper to DER encoded form With OpenSSL 3.0 and xkey-provider, we use pkcs11h_certificate_signAny_ex() which returns EC signature as raw r|s concatenated. But OpenSSL expects a DER encoded ASN.1 structure. Do this conversion as done in cryptoapi.c. For code re-use, ecdsa_bin2sig() is consolidated with sig to DER conversion as ecdsa_bin2der() and moved to xkey_helper.c In the past when we used OpenSSL hooks installed by pkcs11-helper, such a conversion was not required as it was internally handled by the library. Reported by: Tom Also see: https://bugzilla.redhat.com/show_bug.cgi?id=2177834 Tested-by: Florian Apolloner Change-Id: Ie20cf81edd643ab8ef3c41321353d11fd66c188c Signed-off-by: Selva Nair Acked-by: Arne Schwabe Message-Id: <20230314122134.1248576-1-selva.nair@gmail.com> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26406.html Signed-off-by: Gert Doering (cherry picked from commit b7cf18f750f2a020032e09b6c4184579896876ee) --- src/openvpn/cryptoapi.c | 61 +++++------------------------------- src/openvpn/pkcs11_openssl.c | 23 ++++++++++++-- src/openvpn/xkey_common.h | 15 +++++++++ src/openvpn/xkey_helper.c | 45 ++++++++++++++++++++++++++ 4 files changed, 88 insertions(+), 56 deletions(-) diff --git a/src/openvpn/cryptoapi.c b/src/openvpn/cryptoapi.c index 136c6ffc879..022f53d4445 100644 --- a/src/openvpn/cryptoapi.c +++ b/src/openvpn/cryptoapi.c @@ -146,40 +146,6 @@ CAPI_DATA_free(CAPI_DATA *cd) free(cd); } -/** - * Helper to convert ECDSA signature returned by NCryptSignHash - * to an ECDSA_SIG structure. - * On entry 'buf[]' of length len contains r and s concatenated. - * Returns a newly allocated ECDSA_SIG or NULL (on error). - */ -static ECDSA_SIG * -ecdsa_bin2sig(unsigned char *buf, int len) -{ - ECDSA_SIG *ecsig = NULL; - DWORD rlen = len/2; - BIGNUM *r = BN_bin2bn(buf, rlen, NULL); - BIGNUM *s = BN_bin2bn(buf+rlen, rlen, NULL); - if (!r || !s) - { - goto err; - } - ecsig = ECDSA_SIG_new(); /* in openssl 1.1 this does not allocate r, s */ - if (!ecsig) - { - goto err; - } - if (!ECDSA_SIG_set0(ecsig, r, s)) /* ecsig takes ownership of r and s */ - { - ECDSA_SIG_free(ecsig); - goto err; - } - return ecsig; -err: - BN_free(r); /* it is ok to free NULL BN */ - BN_free(s); - return NULL; -} - /** * Parse a hex string with optional embedded spaces into * a byte array. @@ -287,12 +253,11 @@ static int xkey_cng_ec_sign(CAPI_DATA *cd, unsigned char *sig, size_t *siglen, const unsigned char *tbs, size_t tbslen) { - BYTE buf[1024]; /* large enough for EC keys upto 1024 bits */ - DWORD len = _countof(buf); + DWORD len = *siglen; msg(D_LOW, "Signing using NCryptSignHash with EC key"); - DWORD status = NCryptSignHash(cd->crypt_prov, NULL, (BYTE *)tbs, tbslen, buf, len, &len, 0); + DWORD status = NCryptSignHash(cd->crypt_prov, NULL, (BYTE *)tbs, tbslen, sig, len, &len, 0); if (status != ERROR_SUCCESS) { @@ -301,26 +266,14 @@ xkey_cng_ec_sign(CAPI_DATA *cd, unsigned char *sig, size_t *siglen, const unsign return 0; } - /* NCryptSignHash returns r|s -- convert to OpenSSL's ECDSA_SIG */ - ECDSA_SIG *ecsig = ecdsa_bin2sig(buf, len); - if (!ecsig) + /* NCryptSignHash returns r|s -- convert to DER encoded buffer expected by OpenSSL */ + int derlen = ecdsa_bin2der(sig, (int) len, *siglen); + if (derlen <= 0) { - msg(M_NONFATAL, "Error in cryptopicert: Failed to convert ECDSA signature"); return 0; } - - /* convert internal signature structure 's' to DER encoded byte array in sig */ - if (i2d_ECDSA_SIG(ecsig, NULL) > EVP_PKEY_size(cd->pubkey)) - { - ECDSA_SIG_free(ecsig); - msg(M_NONFATAL, "Error in cryptoapicert: DER encoded ECDSA signature is too long"); - return 0; - } - - *siglen = i2d_ECDSA_SIG(ecsig, &sig); - ECDSA_SIG_free(ecsig); - - return (*siglen > 0); + *siglen = derlen; + return 1; } /** Sign hash in tbs using RSA key in cd and NCryptSignHash */ diff --git a/src/openvpn/pkcs11_openssl.c b/src/openvpn/pkcs11_openssl.c index 5c1de44e148..eee86e17b6f 100644 --- a/src/openvpn/pkcs11_openssl.c +++ b/src/openvpn/pkcs11_openssl.c @@ -168,6 +168,7 @@ xkey_pkcs11h_sign(void *handle, unsigned char *sig, unsigned char buf[EVP_MAX_MD_SIZE]; size_t buflen; + size_t siglen_max = *siglen; unsigned char enc[EVP_MAX_MD_SIZE + 32]; /* 32 bytes enough for DigestInfo header */ size_t enc_len = sizeof(enc); @@ -234,8 +235,26 @@ xkey_pkcs11h_sign(void *handle, unsigned char *sig, ASSERT(0); /* coding error -- we couldnt have created any such key */ } - return CKR_OK == pkcs11h_certificate_signAny_ex(cert, &mech, - tbs, tbslen, sig, siglen); + if (CKR_OK != pkcs11h_certificate_signAny_ex(cert, &mech, + tbs, tbslen, sig, siglen)) + { + return 0; + } + if (strcmp(sigalg.keytype, "EC")) + { + return 1; + } + + /* For EC keys, pkcs11 returns signature as r|s: convert to der encoded */ + int derlen = ecdsa_bin2der(sig, (int) *siglen, siglen_max); + + if (derlen <= 0) + { + return 0; + } + *siglen = derlen; + + return 1; } /* wrapper for handle free */ diff --git a/src/openvpn/xkey_common.h b/src/openvpn/xkey_common.h index 07b70c08e20..51cdb5b7c43 100644 --- a/src/openvpn/xkey_common.h +++ b/src/openvpn/xkey_common.h @@ -33,6 +33,7 @@ #define HAVE_XKEY_PROVIDER 1 #include #include +#include /** * Initialization function for OpenVPN external key provider for OpenSSL @@ -170,6 +171,20 @@ xkey_max_saltlen(int modBits, int hLen) return emLen - hLen - 2; } + +/** + * @brief Convert raw ECDSA signature to DER encoded + * This function converts ECDSA signature provided as a buffer + * containing r|s to DER encoded ASN.1 expected by OpenSSL + * @param buf signature containing r|s. + * @param len size of signature in bytes + * @param capacity max space in the buffer buf in bytes + * @returns the size of the converted signature or <= 0 on error. + * On success, buf is overwritten by its DER encoding + */ +int +ecdsa_bin2der(unsigned char *buf, int len, size_t capacity); + #endif /* HAVE_XKEY_PROVIDER */ #endif /* ENABLE_CRYPTO_OPENSSL */ diff --git a/src/openvpn/xkey_helper.c b/src/openvpn/xkey_helper.c index 052c60c45af..ee9677ab052 100644 --- a/src/openvpn/xkey_helper.c +++ b/src/openvpn/xkey_helper.c @@ -401,4 +401,49 @@ encode_pkcs1(unsigned char *enc, size_t *enc_len, const char *mdname, return ret; } +/** + * Helper to convert ECDSA signature with r and s concatenated + * to a DER encoded format used by OpenSSL. + * Returns the size of the converted signature or <= 0 on error. + * On success, buf is overwritten by the DER encoded signature. + */ +int +ecdsa_bin2der(unsigned char *buf, int len, size_t capacity) +{ + ECDSA_SIG *ecsig = NULL; + int rlen = len/2; + BIGNUM *r = BN_bin2bn(buf, rlen, NULL); + BIGNUM *s = BN_bin2bn(buf+rlen, rlen, NULL); + if (!r || !s) + { + goto err; + } + ecsig = ECDSA_SIG_new(); /* this does not allocate r, s */ + if (!ecsig) + { + goto err; + } + if (!ECDSA_SIG_set0(ecsig, r, s)) /* ecsig takes ownership of r and s */ + { + ECDSA_SIG_free(ecsig); + goto err; + } + + int derlen = i2d_ECDSA_SIG(ecsig, NULL); + if (derlen > (int) capacity) + { + ECDSA_SIG_free(ecsig); + msg(M_NONFATAL, "Error: DER encoded ECDSA signature is too long (%d)\n", derlen); + return 0; + } + derlen = i2d_ECDSA_SIG(ecsig, &buf); + ECDSA_SIG_free(ecsig); + return derlen; + +err: + BN_free(r); /* it is ok to free NULL BN */ + BN_free(s); + return 0; +} + #endif /* HAVE_XKEY_PROVIDER */ From 0942e1575abbc0bdda62e3158827b130ae3f9ab6 Mon Sep 17 00:00:00 2001 From: Arne Schwabe Date: Wed, 15 Mar 2023 20:55:12 +0100 Subject: [PATCH 053/229] Fix memory leaks in HMAC initial packet generation The HMAC leaks are just forgotten frees/deinitialisations. tls_wrap_control() will sometimes return the original buffer (non tls-crypt) and sometimes tls_wrap.work, so handling this buffer lifetime is a bit more complicated. Instead of further complicating that code just give our work buffer the same lifetime as the other one inside tls_wrap.work (put it into per-session gc_arena) as that is also more consistent. Second, packet_id_init() allocates a buffer with malloc and not using a gc_arena, so we need to also manually free it. Patch v2: add missing deallocations in unit tests of the new workbuf Patch v3: remove useless allocation of 0 size buffer in tls_auth_standalone_init Found-By: clang with asan Change-Id: I0cff44f79ee7e3bcf7b5981fc94f469c15f21af3 Signed-off-by: Arne Schwabe Acked-by: Gert Doering Message-Id: <20230315195512.323070-1-arne@rfc2549.org> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/ Signed-off-by: Gert Doering (cherry picked from commit e8ecaadd2ac38f2c2d4bcd40eeaea7401aa737a1) --- src/openvpn/init.c | 3 +++ src/openvpn/ssl.c | 11 ++++++++++ src/openvpn/ssl.h | 6 ++++++ src/openvpn/ssl_pkt.c | 8 +++++-- src/openvpn/ssl_pkt.h | 2 +- tests/unit_tests/openvpn/test_pkt.c | 33 +++++++++++++++++++---------- 6 files changed, 49 insertions(+), 14 deletions(-) diff --git a/src/openvpn/init.c b/src/openvpn/init.c index 124ac76bd9f..fa2681dc717 100644 --- a/src/openvpn/init.c +++ b/src/openvpn/init.c @@ -3483,6 +3483,7 @@ do_init_frame_tls(struct context *c) frame_print(&c->c2.tls_auth_standalone->frame, D_MTU_INFO, "TLS-Auth MTU parms"); c->c2.tls_auth_standalone->tls_wrap.work = alloc_buf_gc(BUF_SIZE(&c->c2.frame), &c->c2.gc); + c->c2.tls_auth_standalone->workbuf = alloc_buf_gc(BUF_SIZE(&c->c2.frame), &c->c2.gc); } } @@ -3881,6 +3882,8 @@ do_close_tls(struct context *c) md_ctx_cleanup(c->c2.pulled_options_state); md_ctx_free(c->c2.pulled_options_state); } + + tls_auth_standalone_free(c->c2.tls_auth_standalone); } /* diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c index 78cec90a1c5..fe6390fada5 100644 --- a/src/openvpn/ssl.c +++ b/src/openvpn/ssl.c @@ -1361,6 +1361,17 @@ tls_auth_standalone_init(struct tls_options *tls_options, return tas; } +void +tls_auth_standalone_free(struct tls_auth_standalone *tas) +{ + if (!tas) + { + return; + } + + packet_id_free(&tas->tls_wrap.opt.packet_id); +} + /* * Set local and remote option compatibility strings. * Used to verify compatibility of local and remote option diff --git a/src/openvpn/ssl.h b/src/openvpn/ssl.h index 58ff4b9b4e7..a050cd5c981 100644 --- a/src/openvpn/ssl.h +++ b/src/openvpn/ssl.h @@ -180,6 +180,12 @@ void tls_multi_init_finalize(struct tls_multi *multi, int tls_mtu); struct tls_auth_standalone *tls_auth_standalone_init(struct tls_options *tls_options, struct gc_arena *gc); +/** + * Frees a standalone tls-auth verification object. + * @param tas the object to free. May be NULL. + */ +void tls_auth_standalone_free(struct tls_auth_standalone *tas); + /* * Setups the control channel frame size parameters from the data channel * parameters diff --git a/src/openvpn/ssl_pkt.c b/src/openvpn/ssl_pkt.c index 17a7f891725..8b3391e7618 100644 --- a/src/openvpn/ssl_pkt.c +++ b/src/openvpn/ssl_pkt.c @@ -434,7 +434,10 @@ tls_reset_standalone(struct tls_wrap_ctx *ctx, uint8_t header, bool request_resend_wkc) { - struct buffer buf = alloc_buf(tas->frame.buf.payload_size); + /* Copy buffer here to point at the same data but allow tls_wrap_control + * to potentially change buf to point to another buffer without + * modifying the buffer in tas */ + struct buffer buf = tas->workbuf; ASSERT(buf_init(&buf, tas->frame.buf.headroom)); /* Reliable ACK structure */ @@ -461,7 +464,8 @@ tls_reset_standalone(struct tls_wrap_ctx *ctx, buf_write_u16(&buf, EARLY_NEG_FLAG_RESEND_WKC); } - /* Add tls-auth/tls-crypt wrapping, this might replace buf */ + /* Add tls-auth/tls-crypt wrapping, this might replace buf with + * ctx->work */ tls_wrap_control(ctx, header, &buf, own_sid); return buf; diff --git a/src/openvpn/ssl_pkt.h b/src/openvpn/ssl_pkt.h index ec7b48daf66..ef4852e5d70 100644 --- a/src/openvpn/ssl_pkt.h +++ b/src/openvpn/ssl_pkt.h @@ -77,6 +77,7 @@ struct tls_auth_standalone { struct tls_wrap_ctx tls_wrap; + struct buffer workbuf; struct frame frame; }; @@ -220,7 +221,6 @@ read_control_auth(struct buffer *buf, * This function creates a reset packet using the information * from the tls pre decrypt state. * - * The returned buf needs to be free with \c free_buf */ struct buffer tls_reset_standalone(struct tls_wrap_ctx *ctx, diff --git a/tests/unit_tests/openvpn/test_pkt.c b/tests/unit_tests/openvpn/test_pkt.c index f11e52a1101..736f1317859 100644 --- a/tests/unit_tests/openvpn/test_pkt.c +++ b/tests/unit_tests/openvpn/test_pkt.c @@ -203,6 +203,7 @@ init_tas_auth(int key_direction) static_key, true, key_direction, "Control Channel Authentication", "tls-auth", NULL); + tas.workbuf = alloc_buf(1600); return tas; } @@ -217,10 +218,22 @@ init_tas_crypt(bool server) tls_crypt_init_key(&tas.tls_wrap.opt.key_ctx_bi, &tas.tls_wrap.original_wrap_keydata, static_key, true, server); + tas.workbuf = alloc_buf(1600); + tas.tls_wrap.work = alloc_buf(1600); return tas; } +void +free_tas(struct tls_auth_standalone *tas) +{ + /* Not some of these might be null pointers but calling free on null + * pointers is a noop */ + free_key_ctx_bi(&tas->tls_wrap.opt.key_ctx_bi); + free_buf(&tas->workbuf); + free_buf(&tas->tls_wrap.work); +} + void test_tls_decrypt_lite_crypt(void **ut_state) { @@ -228,7 +241,6 @@ test_tls_decrypt_lite_crypt(void **ut_state) struct tls_pre_decrypt_state state = { 0 }; struct tls_auth_standalone tas = init_tas_crypt(true); - struct buffer buf = alloc_buf(1024); /* tls-auth should be invalid */ @@ -263,6 +275,7 @@ test_tls_decrypt_lite_crypt(void **ut_state) } free_key_ctx_bi(&tas.tls_wrap.opt.key_ctx_bi); + free_tas(&tas); free_buf(&buf); } @@ -318,6 +331,7 @@ test_tls_decrypt_lite_auth(void **ut_state) free_tls_pre_decrypt_state(&state); /* Wrong key direction gives a wrong hmac key and should not validate */ free_key_ctx_bi(&tas.tls_wrap.opt.key_ctx_bi); + free_tas(&tas); tas = init_tas_auth(KEY_DIRECTION_INVERSE); buf_reset_len(&buf); @@ -326,7 +340,7 @@ test_tls_decrypt_lite_auth(void **ut_state) assert_int_equal(verdict, VERDICT_INVALID); free_tls_pre_decrypt_state(&state); - free_key_ctx_bi(&tas.tls_wrap.opt.key_ctx_bi); + free_tas(&tas); free_buf(&buf); } @@ -371,6 +385,7 @@ test_tls_decrypt_lite_none(void **ut_state) free_tls_pre_decrypt_state(&state); free_buf(&buf); + free_tas(&tas); } static void @@ -442,10 +457,9 @@ test_verify_hmac_tls_auth(void **ut_state) bool valid = check_session_id_hmac(&state, &from.dest, hmac, 30); assert_false(valid); - free_key_ctx_bi(&tas.tls_wrap.opt.key_ctx_bi); - free_key_ctx(&tas.tls_wrap.tls_crypt_v2_server_key); free_tls_pre_decrypt_state(&state); free_buf(&buf); + free_tas(&tas); hmac_ctx_cleanup(hmac); hmac_ctx_free(hmac); } @@ -563,6 +577,7 @@ test_generate_reset_packet_plain(void **ut_state) tas.tls_wrap.mode = TLS_WRAP_NONE; struct frame frame = { {.headroom = 200, .payload_size = 1400}, 0}; tas.frame = frame; + tas.workbuf = alloc_buf(1600); uint8_t header = 0 | (P_CONTROL_HARD_RESET_CLIENT_V2 << P_OPCODE_SHIFT); @@ -577,10 +592,9 @@ test_generate_reset_packet_plain(void **ut_state) struct buffer buf2 = tls_reset_standalone(&tas.tls_wrap, &tas, &client_id, &server_id, header, false); assert_int_equal(BLEN(&buf), BLEN(&buf2)); assert_memory_equal(BPTR(&buf), BPTR(&buf2), BLEN(&buf)); - free_buf(&buf2); free_tls_pre_decrypt_state(&state); - free_buf(&buf); + free_buf(&tas.workbuf); } static void @@ -614,15 +628,12 @@ test_generate_reset_packet_tls_auth(void **ut_state) assert_int_equal(BLEN(&buf), BLEN(&buf2)); assert_memory_equal(BPTR(&buf), BPTR(&buf2), BLEN(&buf)); - free_buf(&buf2); free_tls_pre_decrypt_state(&state); packet_id_free(&tas_client.tls_wrap.opt.packet_id); - free_buf(&buf); - free_key_ctx_bi(&tas_server.tls_wrap.opt.key_ctx_bi); - free_key_ctx_bi(&tas_client.tls_wrap.opt.key_ctx_bi); - + free_tas(&tas_client); + free_tas(&tas_server); } int From e6011868c40aaf91d30d084b1b454f7709c855fd Mon Sep 17 00:00:00 2001 From: Selva Nair Date: Tue, 14 Mar 2023 21:35:13 -0400 Subject: [PATCH 054/229] Import some sample certificates into Windows store for testing - A few sample certificates are defined and imported into Windows certificate store (user store). This only tests the import process. Use of these certs to test the core functionality of 'cryptoapicert' are in following commits. Change-Id: Ida5fc12c5bad5fde202da0bf0e8cdc71efe548c2 Signed-off-by: Selva Nair Acked-by: Gert Doering Message-Id: <20230315013516.1256700-2-selva.nair@gmail.com> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26417.html Signed-off-by: Gert Doering (cherry picked from commit d6cf0239e835d98b66c71d701e70128db9ca7e9a) --- tests/unit_tests/openvpn/cert_data.h | 166 ++++++++++++++++++++++ tests/unit_tests/openvpn/test_cryptoapi.c | 160 ++++++++++++++++++++- 2 files changed, 324 insertions(+), 2 deletions(-) create mode 100644 tests/unit_tests/openvpn/cert_data.h diff --git a/tests/unit_tests/openvpn/cert_data.h b/tests/unit_tests/openvpn/cert_data.h new file mode 100644 index 00000000000..33de35ec95a --- /dev/null +++ b/tests/unit_tests/openvpn/cert_data.h @@ -0,0 +1,166 @@ +/* + * OpenVPN -- An application to securely tunnel IP networks + * over a single UDP port, with support for SSL/TLS-based + * session authentication and key exchange, + * packet encryption, packet authentication, and + * packet compression. + * + * Copyright (C) 2023 Selva Nair + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 2 of the License, + * or (at your option) any later version. + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef CERT_DATA_H +#define CERT_DATA_H + +/* Some certificates and their private keys for testing cryptoapi.c. + * Two certificates, cert1 (EC) and cert3 (RSA) are signed by one CA + * and the other two, cert2 (EC) and cert4 (RSA), by another to have a + * different issuer name. The common name of cert4 is the same as + * that of cert3 but the former has expired. It is used to test + * retrieval of valid certificate by name when an expired one with same + * common name exists. + * To reduce data volume, certs of same keytype use the same private key. + */ + +/* sample-ec.crt */ +static const char *const cert1 = + "-----BEGIN CERTIFICATE-----\n" + "MIIClzCCAX+gAwIBAgIRAIJr3cy95V63CPEtaAA8JN4wDQYJKoZIhvcNAQELBQAw\n" + "GDEWMBQGA1UEAwwNT1ZQTiBURVNUIENBMTAgFw0yMzAzMTMxNjExMjhaGA8yMTIz\n" + "MDIxNzE2MTEyOFowGDEWMBQGA1UEAwwNb3Zwbi10ZXN0LWVjMTBZMBMGByqGSM49\n" + "AgEGCCqGSM49AwEHA0IABHhJG+dK4Z0mY+K0pupwVtyDLOwwGWHjBY6u3LgjRmUh\n" + "fFjaoSfJvdgrPg50wbOkrsUt9Bl6EeDosZuVwuzgRbujgaQwgaEwCQYDVR0TBAIw\n" + "ADAdBgNVHQ4EFgQUPWeU5BEmD8VEOSKeNf9kAvhcVuowUwYDVR0jBEwwSoAU3MLD\n" + "NDOK13DqflQ8ra7FeGBXK06hHKQaMBgxFjAUBgNVBAMMDU9WUE4gVEVTVCBDQTGC\n" + "FD55ErHXpK2JXS3WkfBm0NB1r3vKMBMGA1UdJQQMMAoGCCsGAQUFBwMCMAsGA1Ud\n" + "DwQEAwIHgDANBgkqhkiG9w0BAQsFAAOCAQEAhH/wOFqP4R+FK5QvU+oW/XacFMku\n" + "+qT8lL9J7BG28WhZ0ZcAy/AmtnyynkDyuZSwnlzGgJ5m4L/RfwTzJKhEHiSU3BvB\n" + "5C1Z1Q8k67MHSfb565iCn8GzPUQLK4zsILCoTkJPvimv2bJ/RZmNaD+D4LWiySD4\n" + "tuOEdHKrxIrbJ5eAaN0WxRrvDdwGlyPvbMFvfhXzd/tbkP4R2xvlm7S2DPeSTJ8s\n" + "srXMaPe0lAea4etMSZsjIRPwGRMXBrwbRmb6iN2Cq40867HdaJoAryYig7IiDwSX\n" + "htCbOA6sX+60+FEOYDEx5cmkogl633Pw7LJ3ICkyzIrUSEt6BOT1Gsc1eQ==\n" + "-----END CERTIFICATE-----\n"; +static const char *const key1 = + "-----BEGIN PRIVATE KEY-----\n" + "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg5Xpw/lLvBrWjAWDq\n" + "L6dm/4a1or6AQ6O3yXYgw78B23ihRANCAAR4SRvnSuGdJmPitKbqcFbcgyzsMBlh\n" + "4wWOrty4I0ZlIXxY2qEnyb3YKz4OdMGzpK7FLfQZehHg6LGblcLs4EW7\n" + "-----END PRIVATE KEY-----\n"; +static const char *const hash1 = "A4B74F1D68AF50691F62CBD675E24C8655369567"; +static const char *const cname1 = "ovpn-test-ec1"; + +static const char *const cert2 = + "-----BEGIN CERTIFICATE-----\n" + "MIIClzCCAX+gAwIBAgIRAN9fIkTDOjX0Bd9adHVcLx8wDQYJKoZIhvcNAQELBQAw\n" + "GDEWMBQGA1UEAwwNT1ZQTiBURVNUIENBMjAgFw0yMzAzMTMxODAzMzFaGA8yMTIz\n" + "MDIxNzE4MDMzMVowGDEWMBQGA1UEAwwNb3Zwbi10ZXN0LWVjMjBZMBMGByqGSM49\n" + "AgEGCCqGSM49AwEHA0IABHhJG+dK4Z0mY+K0pupwVtyDLOwwGWHjBY6u3LgjRmUh\n" + "fFjaoSfJvdgrPg50wbOkrsUt9Bl6EeDosZuVwuzgRbujgaQwgaEwCQYDVR0TBAIw\n" + "ADAdBgNVHQ4EFgQUPWeU5BEmD8VEOSKeNf9kAvhcVuowUwYDVR0jBEwwSoAUyX3c\n" + "tpRP5cKlESsG80rOGhEphsGhHKQaMBgxFjAUBgNVBAMMDU9WUE4gVEVTVCBDQTKC\n" + "FBc8ra53hwYrlIkdY3Ay1WCrrHJ8MBMGA1UdJQQMMAoGCCsGAQUFBwMCMAsGA1Ud\n" + "DwQEAwIHgDANBgkqhkiG9w0BAQsFAAOCAQEAWmA40BvEgBbKb1ReKlKzk64xi2ak\n" + "4tyr3sW9wIYQ2N1zkSomwEV6wGEawLqPADRbXiYdjtAqLz12OJvBnBwgxN3dVmqL\n" + "6UN4ZIwMWJ4fSW9vK/Nt+JNwebN+Jgw/nIXvSdK95ha4iusZZOIZ4qDj3DWwjhjV\n" + "L5/m6zP09L9G9/79j1Tsu4Stl5SI1XxtYc0eVn29vJEMBfpsS7pPD6V9JpY3Y1f3\n" + "HeTsAlHjfFEReVDiNCI9vMQLKFKKWnAorT2+iyRueA3bt2gchf863BBhZvJddL7Q\n" + "KBa0osXw+eGBRAwsm7m1qCho3b3fN2nFAa+k07ptRkOeablmFdXE81nVlA==\n" + "-----END CERTIFICATE-----\n"; +static const char *const key2 = key1; +static const char *const hash2 = "FA18FD34BAABE47D6E2910E080F421C109CA97F5"; +static const char *const cname2 = "ovpn-test-ec2"; + +static const char *const cert3 = + "-----BEGIN CERTIFICATE-----\n" + "MIIDYzCCAkugAwIBAgIRALrXTx4lqa8QgF7uGjISxmcwDQYJKoZIhvcNAQELBQAw\n" + "GDEWMBQGA1UEAwwNT1ZQTiBURVNUIENBMTAgFw0yMzAzMTMxNjA5MThaGA8yMTIz\n" + "MDIxNzE2MDkxOFowGTEXMBUGA1UEAwwOb3Zwbi10ZXN0LXJzYTEwggEiMA0GCSqG\n" + "SIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7xFoR6fmoyfsJIQDKKgbYgFw0MzVuDAmp\n" + "Rx6KTEihgTchkQx9fHddWbKiOUbcEnQi3LNux7P4QVl/4dRR3skisBug6Vd5LXeB\n" + "GZqmpu5XZiF4DgLz1lX21G0aOogFWkie2qGEcso40159x9FBDl5A3sLP18ubeex0\n" + "pd/BzDFv6SLOTyVWO/GCNc8IX/i0uN4mLvoVU00SeqwTPnS+CRXrSq4JjGDJLsXl\n" + "0/PlxkjsgU0yOOA0Z2d8Fzk3wClwP6Hc49BOMWKstUIhLbG2DcIv8l29EuEj2w3j\n" + "u/7gkewol96XQ2twpPvpoVAaiVh/m7hQUcQORQCD6eJcDjOZVCArAgMBAAGjgaQw\n" + "gaEwCQYDVR0TBAIwADAdBgNVHQ4EFgQUqYnRaBHrZmKLtMZES5AuwqzJkGYwUwYD\n" + "VR0jBEwwSoAU3MLDNDOK13DqflQ8ra7FeGBXK06hHKQaMBgxFjAUBgNVBAMMDU9W\n" + "UE4gVEVTVCBDQTGCFD55ErHXpK2JXS3WkfBm0NB1r3vKMBMGA1UdJQQMMAoGCCsG\n" + "AQUFBwMCMAsGA1UdDwQEAwIHgDANBgkqhkiG9w0BAQsFAAOCAQEAZVcXrezA9Aby\n" + "sfUNHAsMxrex/EO0PrIPSrmSmc9sCiD8cCIeB6kL8c5iPPigoWW0uLA9zteDRFes\n" + "ez+Z8wBY6g8VQ0tFPURDooUg5011GZPDcuw7/PsI4+I2J9q6LHEp+6Oo4faSn/kl\n" + "yWYCLjM4FZdGXbOijDacQJiN6HcRv0UdodBrEVRf7YHJJmMCbCI7ZUGW2zef/+rO\n" + "e4Lkxh0MLYqCkNKH5ZfoGTC4Oeb0xKykswAanqgR60r+upaLU8PFuI2L9M3vc6KU\n" + "F6MgVGSxl6eylJgDYckvJiAbmcp2PD/LRQQOxQA0yqeAMg2cbdvclETuYD6zoFfu\n" + "Y8aO7dvDlw==\n" + "-----END CERTIFICATE-----\n"; +static const char *const key3 = + "-----BEGIN PRIVATE KEY-----\n" + "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC7xFoR6fmoyfsJ\n" + "IQDKKgbYgFw0MzVuDAmpRx6KTEihgTchkQx9fHddWbKiOUbcEnQi3LNux7P4QVl/\n" + "4dRR3skisBug6Vd5LXeBGZqmpu5XZiF4DgLz1lX21G0aOogFWkie2qGEcso40159\n" + "x9FBDl5A3sLP18ubeex0pd/BzDFv6SLOTyVWO/GCNc8IX/i0uN4mLvoVU00SeqwT\n" + "PnS+CRXrSq4JjGDJLsXl0/PlxkjsgU0yOOA0Z2d8Fzk3wClwP6Hc49BOMWKstUIh\n" + "LbG2DcIv8l29EuEj2w3ju/7gkewol96XQ2twpPvpoVAaiVh/m7hQUcQORQCD6eJc\n" + "DjOZVCArAgMBAAECggEACqkuWAAJ3cyCBVWrXs8eDmLTWV9i9DmYvtS75ixIn2rf\n" + "v3cl12YevN0f6FgKLuqZT3Vqdqq+DCVhuIIQ9QkKMH8BQpSdE9NCCsFyZ23o8Gtr\n" + "EQ7ymfecb+RFwYx7NpqWrvZI32VJGArgPZH/zorLTTGYrAZbmBtHEqRsXOuEDw97\n" + "slwwcWaa9ztaYC8/N/7fgsnydaCFSaOByRlWuyvSmHvn6ZwLv8ANOshY6fstC0Jb\n" + "BW0GpSe9eZPjpl71VT2RtpghqLV5+iAoFDHoT+eZvBospcUGtfcZSU7RrBjKB8+a\n" + "U1d6hwKhduVs2peIQzl+FiOSdWriLcsZv79q4sBhsQKBgQDUDVTf5BGJ8apOs/17\n" + "YVk+Ad8Ey8sXvsfk49psmlCRa8Z4g0LVXfrP94qzhtl8U5kE9hs3nEF4j/kX1ZWG\n" + "k11tdsNTZN5x5bbAgEgPA6Ap6J/uto0HS8G0vSv0lyBymdKA3p/i5Dx+8Nc9cGns\n" + "LGI9MvviLX7pQFIkvbaCkdKwYwKBgQDirowjWZnm7BgVhF0G1m3DY9nQTYYU185W\n" + "UESaO5/nVzwUrA+FypJamD+AvmlSuY8rJeQAGAS6nQr9G8/617r+GwJnzRtxC6Vl\n" + "4OF5BJRsD70oX4CFOOlycMoJ8tzcYVH7NI8KVocjxb+QW82hqSvEwSsvnwwn3eOW\n" + "nr5u5vIHmQKBgCuc3lL6Dl1ntdZgEIdau0cUjXDoFUo589TwxBDIID/4gaZxoMJP\n" + "hPFXAVDxMDPw4azyjSB/47tPKTUsuYcnMfT8kynIujOEwnSPLcLgxQU5kgM/ynuw\n" + "qhNpQOwaVRMc7f2RTCMXPBYDpNE/GJn5eu8JWGLpZovEreBeoHX0VffvAoGAVrWn\n" + "+3mxykhzaf+oyg3KDNysG+cbq+tlDVVE+K5oG0kePVYX1fjIBQmJ+QhdJ3y9jCbB\n" + "UVveqzeZVXqHEw/kgoD4aZZmsdZfnVnpRa5/y9o1ZDUr50n+2nzUe/u/ijlb77iK\n" + "Is04gnGJNoI3ZWhdyrSNfXjcYH+bKClu9OM4n7kCgYAorc3PAX7M0bsQrrqYxUS8\n" + "56UU0YdhAgYitjM7Fm/0iIm0vDpSevxL9js4HnnsSMVR77spCBAGOCCZrTcI3Ejg\n" + "xKDYzh1xlfMRjJBuBu5Pd55ZAv9NXFGpsX5SO8fDZQJMwpcbQH36+UdqRRFDpjJ0\n" + "ZbX6nKcJ7jciJVKJds59Jg==\n" + "-----END PRIVATE KEY-----\n"; +static const char *const hash3 = "2463628674E362578113F508BA05F29EF142E979"; +static const char *const cname3 = "ovpn-test-rsa1"; + +static const char *const cert4 = + "-----BEGIN CERTIFICATE-----\n" + "MIIDYTCCAkmgAwIBAgIRAPTJucQy27qoIv0oYoE71z8wDQYJKoZIhvcNAQELBQAw\n" + "GDEWMBQGA1UEAwwNT1ZQTiBURVNUIENBMjAeFw0yMzAzMTMxNzQ2MDNaFw0yMzAz\n" + "MTQxNzQ2MDNaMBkxFzAVBgNVBAMMDm92cG4tdGVzdC1yc2ExMIIBIjANBgkqhkiG\n" + "9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu8RaEen5qMn7CSEAyioG2IBcNDM1bgwJqUce\n" + "ikxIoYE3IZEMfXx3XVmyojlG3BJ0Ityzbsez+EFZf+HUUd7JIrAboOlXeS13gRma\n" + "pqbuV2YheA4C89ZV9tRtGjqIBVpIntqhhHLKONNefcfRQQ5eQN7Cz9fLm3nsdKXf\n" + "wcwxb+kizk8lVjvxgjXPCF/4tLjeJi76FVNNEnqsEz50vgkV60quCYxgyS7F5dPz\n" + "5cZI7IFNMjjgNGdnfBc5N8ApcD+h3OPQTjFirLVCIS2xtg3CL/JdvRLhI9sN47v+\n" + "4JHsKJfel0NrcKT76aFQGolYf5u4UFHEDkUAg+niXA4zmVQgKwIDAQABo4GkMIGh\n" + "MAkGA1UdEwQCMAAwHQYDVR0OBBYEFKmJ0WgR62Zii7TGREuQLsKsyZBmMFMGA1Ud\n" + "IwRMMEqAFMl93LaUT+XCpRErBvNKzhoRKYbBoRykGjAYMRYwFAYDVQQDDA1PVlBO\n" + "IFRFU1QgQ0EyghQXPK2ud4cGK5SJHWNwMtVgq6xyfDATBgNVHSUEDDAKBggrBgEF\n" + "BQcDAjALBgNVHQ8EBAMCB4AwDQYJKoZIhvcNAQELBQADggEBAFjJvZFwhY77UOWu\n" + "O6n5yLxcG6/VNWMbD0CazZP8pBqCGJRU9Rq0vXxZ00E0WSYTJLZoq1aFmeWIX0vZ\n" + "sudVkdbfWLdiwuQZDWBS+qC4SkIcnNe5FYSSUlXlvpSUN2CgGCLmryP+SZKHp8YV\n" + "e37pQxDjImXCu5Jdk5AhK6pkFm5IMskdTKfWJjjR69lBgWHPoM2WAwkV8vxKdpy8\n" + "0Bqef8MZZM+qVYw7OguAFos2Am7waLpa3q9SYqCRYctq4Q2++p2WjINv3nkXIwYS\n" + "353PpJJ9s2b/Fqoc4d7udqhQogA7jqbayTKhJxbT134l2NzqDROzuS0kXbX8bXCi\n" + "mXSa4c8=\n" + "-----END CERTIFICATE-----\n"; +static const char *const key4 = key3; +static const char *const hash4 = "E1401D4497C944783E3D62CDBD2A1F69F5E5071E"; +static const char *const cname4 = cname3; /* same CN as that of cert3 */ + +#endif /* CERT_DATA_H */ diff --git a/tests/unit_tests/openvpn/test_cryptoapi.c b/tests/unit_tests/openvpn/test_cryptoapi.c index 73ef34e991c..54dbd094bd4 100644 --- a/tests/unit_tests/openvpn/test_cryptoapi.c +++ b/tests/unit_tests/openvpn/test_cryptoapi.c @@ -32,6 +32,7 @@ #include "manage.h" #include "integer.h" #include "xkey_common.h" +#include "cert_data.h" #if defined(HAVE_XKEY_PROVIDER) && defined (ENABLE_CRYPTOAPI) #include @@ -40,6 +41,7 @@ #include #include #include +#include #include #include /* pull-in the whole file to test static functions */ @@ -84,6 +86,157 @@ static const char *invalid_str[] = { "7738x5001e9648c6570baec0b796f9664d5fd0b7", /* non hex character */ }; +/* Test certificate database: data for cert1, cert2 .. key1, key2 etc. + * are stashed away in cert_data.h + */ +static struct test_cert +{ + const char *const cert; /* certificate as PEM */ + const char *const key; /* key as unencrypted PEM */ + const char *const cname; /* common-name */ + const char *const issuer; /* issuer common-name */ + const char *const friendly_name; /* identifies certs loaded to the store -- keep unique */ + const char *hash; /* SHA1 fingerprint */ + int valid; /* nonzero if certificate has not expired */ +} certs[] = { + {cert1, key1, cname1, "OVPN TEST CA1", "OVPN Test Cert 1", hash1, 1}, + {cert2, key2, cname2, "OVPN TEST CA2", "OVPN Test Cert 2", hash2, 1}, + {cert3, key3, cname3, "OVPN TEST CA1", "OVPN Test Cert 3", hash3, 1}, + {cert4, key4, cname4, "OVPN TEST CA2", "OVPN Test Cert 4", hash4, 0}, + {} +}; + +static bool certs_loaded; +static HCERTSTORE user_store; + +/* Lookup a certificate in our certificate/key db */ +static struct test_cert * +lookup_cert(const char *friendly_name) +{ + struct test_cert *c = certs; + while (c->cert && strcmp(c->friendly_name, friendly_name)) + { + c++; + } + return c->cert ? c : NULL; +} + +/* import sample certificates into windows cert store */ +static void +import_certs(void **state) +{ + (void) state; + if (certs_loaded) + { + return; + } + user_store = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0, CERT_SYSTEM_STORE_CURRENT_USER + |CERT_STORE_OPEN_EXISTING_FLAG, L"MY"); + assert_non_null(user_store); + for (struct test_cert *c = certs; c->cert; c++) + { + /* Convert PEM cert & key to pkcs12 and import */ + const char *pass = "opensesame"; /* some password */ + const wchar_t *wpass = L"opensesame"; /* same as a wide string */ + + X509 *x509 = NULL; + EVP_PKEY *pkey = NULL; + + BIO *buf = BIO_new_mem_buf(c->cert, -1); + if (buf) + { + x509 = PEM_read_bio_X509(buf, NULL, NULL, NULL); + } + BIO_free(buf); + + buf = BIO_new_mem_buf(c->key, -1); + if (buf) + { + pkey = PEM_read_bio_PrivateKey(buf, NULL, NULL, NULL); + } + BIO_free(buf); + + if (!x509 || !pkey) + { + fail_msg("Failed to parse certificate/key data: <%s>", c->friendly_name); + return; + } + + PKCS12 *p12 = PKCS12_create(pass, c->friendly_name, pkey, x509, NULL, 0, 0, 0, 0, 0); + X509_free(x509); + EVP_PKEY_free(pkey); + if (!p12) + { + fail_msg("Failed to convert to PKCS12: <%s>", c->friendly_name); + return; + } + + CRYPT_DATA_BLOB blob = {.cbData = 0, .pbData = NULL}; + int len = i2d_PKCS12(p12, &blob.pbData); /* pbData will be allocated by OpenSSL */ + if (len <= 0) + { + fail_msg("Failed to DER encode PKCS12: <%s>", c->friendly_name); + return; + } + blob.cbData = len; + + DWORD flags = PKCS12_ALLOW_OVERWRITE_KEY|PKCS12_ALWAYS_CNG_KSP; + HCERTSTORE tmp_store = PFXImportCertStore(&blob, wpass, flags); + PKCS12_free(p12); + OPENSSL_free(blob.pbData); + + assert_non_null(tmp_store); + + /* The cert and key get imported into a temp store. We have to move it to + * user's store to accumulate all certs in one place and use them for tests. + * It seems there is no API to directly import a p12 blob into an existing store. + * Nothing in Windows is ever easy. + */ + + const CERT_CONTEXT *ctx = CertEnumCertificatesInStore(tmp_store, NULL); + assert_non_null(ctx); + bool added = CertAddCertificateContextToStore(user_store, ctx, + CERT_STORE_ADD_REPLACE_EXISTING, NULL); + assert_true(added); + + CertFreeCertificateContext(ctx); + CertCloseStore(tmp_store, 0); + } + certs_loaded = true; +} + +static int +cleanup(void **state) +{ + (void) state; + struct gc_arena gc = gc_new(); + if (user_store) /* delete all certs we imported */ + { + const CERT_CONTEXT *ctx = NULL; + while ((ctx = CertEnumCertificatesInStore(user_store, ctx))) + { + char *friendly_name = get_cert_name(ctx, &gc); + if (!lookup_cert(friendly_name)) /* not our cert */ + { + continue; + } + + /* create a dup context to not destroy the state of loop iterator */ + const CERT_CONTEXT *ctx_dup = CertDuplicateCertificateContext(ctx); + if (ctx_dup) + { + CertDeleteCertificateFromStore(ctx_dup); + /* the above also releases ctx_dup */ + } + } + CertCloseStore(user_store, 0); + } + user_store = NULL; + certs_loaded = false; + gc_free(&gc); + return 0; +} + static void test_parse_hexstring(void **state) { @@ -108,9 +261,12 @@ test_parse_hexstring(void **state) int main(void) { - const struct CMUnitTest tests[] = { cmocka_unit_test(test_parse_hexstring) }; + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_parse_hexstring), + cmocka_unit_test(import_certs), + }; - int ret = cmocka_run_group_tests_name("cryptoapi tests", tests, NULL, NULL); + int ret = cmocka_run_group_tests_name("cryptoapi tests", tests, NULL, cleanup); return ret; } From a08d0c770d0c0cd2534a0a900bba358b1c44056f Mon Sep 17 00:00:00 2001 From: Selva Nair Date: Tue, 14 Mar 2023 21:35:14 -0400 Subject: [PATCH 055/229] Add tests for finding certificates in Windows cert store - find_certificate_in_store tested using 'SUBJ:', 'THUMB:' and 'ISSUER:' select strings. Uses test certificates imported into the store during the import test. Change-Id: Ib5138465e6228538af592ca98b3d877277355f59 Signed-off-by: Selva Nair Acked-by: Gert Doering Message-Id: <20230315013516.1256700-3-selva.nair@gmail.com> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26415.html Signed-off-by: Gert Doering (cherry picked from commit b538a334284716757c48026bf6ace95e33258943) --- tests/unit_tests/openvpn/test_cryptoapi.c | 102 ++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/tests/unit_tests/openvpn/test_cryptoapi.c b/tests/unit_tests/openvpn/test_cryptoapi.c index 54dbd094bd4..ccb3207c304 100644 --- a/tests/unit_tests/openvpn/test_cryptoapi.c +++ b/tests/unit_tests/openvpn/test_cryptoapi.c @@ -237,6 +237,105 @@ cleanup(void **state) return 0; } +static void +test_find_cert_bythumb(void **state) +{ + (void) state; + char select_string[64]; + struct gc_arena gc = gc_new(); + const CERT_CONTEXT *ctx; + + import_certs(state); /* a no-op if already imported */ + assert_non_null(user_store); + + for (struct test_cert *c = certs; c->cert; c++) + { + openvpn_snprintf(select_string, sizeof(select_string), "THUMB:%s", c->hash); + ctx = find_certificate_in_store(select_string, user_store); + if (ctx) + { + /* check we got the right certificate and is valid */ + assert_int_equal(c->valid, 1); + char *friendly_name = get_cert_name(ctx, &gc); + assert_string_equal(c->friendly_name, friendly_name); + CertFreeCertificateContext(ctx); + } + else + { + /* find should fail only if the certificate has expired */ + assert_int_equal(c->valid, 0); + } + } + + gc_free(&gc); +} + +static void +test_find_cert_byname(void **state) +{ + (void) state; + char select_string[64]; + struct gc_arena gc = gc_new(); + const CERT_CONTEXT *ctx; + + import_certs(state); /* a no-op if already imported */ + assert_non_null(user_store); + + for (struct test_cert *c = certs; c->cert; c++) + { + openvpn_snprintf(select_string, sizeof(select_string), "SUBJ:%s", c->cname); + ctx = find_certificate_in_store(select_string, user_store); + /* In this case we expect a successful return as there is at least one valid + * cert that matches the common name. But the returned cert may not exactly match + * c->cert as multiple certs with same common names exist in the db. We check that + * the return cert is one from our db, has a matching common name and is valid. + */ + assert_non_null(ctx); + + char *friendly_name = get_cert_name(ctx, &gc); + struct test_cert *found = lookup_cert(friendly_name); + assert_non_null(found); + assert_string_equal(found->cname, c->cname); + assert_int_equal(found->valid, 1); + CertFreeCertificateContext(ctx); + } + + gc_free(&gc); +} + +static void +test_find_cert_byissuer(void **state) +{ + (void) state; + char select_string[64]; + struct gc_arena gc = gc_new(); + const CERT_CONTEXT *ctx; + + import_certs(state); /* a no-op if already imported */ + assert_non_null(user_store); + + for (struct test_cert *c = certs; c->cert; c++) + { + openvpn_snprintf(select_string, sizeof(select_string), "ISSUER:%s", c->issuer); + ctx = find_certificate_in_store(select_string, user_store); + /* In this case we expect a successful return as there is at least one valid + * cert that matches the issuer. But the returned cert may not exactly match + * c->cert as multiple certs with same issuer exist in the db. We check that + * the returned cert is one from our db, has a matching issuer name and is valid. + */ + assert_non_null(ctx); + + char *friendly_name = get_cert_name(ctx, &gc); + struct test_cert *found = lookup_cert(friendly_name); + assert_non_null(found); + assert_string_equal(found->issuer, c->issuer); + assert_int_equal(found->valid, 1); + CertFreeCertificateContext(ctx); + } + + gc_free(&gc); +} + static void test_parse_hexstring(void **state) { @@ -264,6 +363,9 @@ main(void) const struct CMUnitTest tests[] = { cmocka_unit_test(test_parse_hexstring), cmocka_unit_test(import_certs), + cmocka_unit_test(test_find_cert_bythumb), + cmocka_unit_test(test_find_cert_byname), + cmocka_unit_test(test_find_cert_byissuer), }; int ret = cmocka_run_group_tests_name("cryptoapi tests", tests, NULL, cleanup); From 5c2154ca49a591afd8faa8e535a67b149ddbd354 Mon Sep 17 00:00:00 2001 From: Selva Nair Date: Tue, 14 Mar 2023 21:35:15 -0400 Subject: [PATCH 056/229] Refactor SSL_CTX_use_CryptoAPI_certificate() - Loading the certificate and key into the provider is split out of setting up the SSL context. This allows testing of signing by cryptoapi-provider interface without dependence on SSL context or link-time wrapping. Change-Id: I269b94589636425e1ba9bf953047d238fa830376 Signed-off-by: Selva Nair Acked-by: Gert Doering Message-Id: <20230315013516.1256700-4-selva.nair@gmail.com> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26414.html Signed-off-by: Gert Doering (cherry picked from commit 0ad5f4d6c44daedca00dc399a5f914ac5850caa0) --- src/openvpn/cryptoapi.c | 63 +++++++++++++++++++++++++++-------------- 1 file changed, 41 insertions(+), 22 deletions(-) diff --git a/src/openvpn/cryptoapi.c b/src/openvpn/cryptoapi.c index 022f53d4445..20b7d985e0c 100644 --- a/src/openvpn/cryptoapi.c +++ b/src/openvpn/cryptoapi.c @@ -401,11 +401,17 @@ get_cert_name(const CERT_CONTEXT *cc, struct gc_arena *gc) return name; } -int -SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop) +/** + * Load certificate matching 'cert_prop' from Windows cert store + * into xkey provider and return pointers to X509 cert and private key. + * Returns 1 on success, 0 on error. + * Caller must free 'cert' and 'privkey' after use. + */ +static int +Load_CryptoAPI_certificate(const char *cert_prop, X509 **cert, EVP_PKEY **privkey) { + HCERTSTORE cs; - X509 *cert = NULL; CAPI_DATA *cd = calloc(1, sizeof(*cd)); struct gc_arena gc = gc_new(); @@ -450,9 +456,9 @@ SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop) } /* cert_context->pbCertEncoded is the cert X509 DER encoded. */ - cert = d2i_X509(NULL, (const unsigned char **) &cd->cert_context->pbCertEncoded, - cd->cert_context->cbCertEncoded); - if (cert == NULL) + *cert = d2i_X509(NULL, (const unsigned char **) &cd->cert_context->pbCertEncoded, + cd->cert_context->cbCertEncoded); + if (*cert == NULL) { msg(M_NONFATAL, "Error in cryptoapicert: X509 certificate decode failed"); goto err; @@ -468,28 +474,16 @@ SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop) /* private key may be in a token not available, or incompatible with CNG */ msg(M_NONFATAL|M_ERRNO, "Error in cryptoapicert: failed to acquire key. Key not present or " "is in a legacy token not supported by Windows CNG API"); - goto err; - } - - /* Public key in cert is NULL until we call SSL_CTX_use_certificate(), - * so we do it here then... */ - if (!SSL_CTX_use_certificate(ssl_ctx, cert)) - { + X509_free(*cert); goto err; } /* the public key */ - EVP_PKEY *pkey = X509_get_pubkey(cert); + EVP_PKEY *pkey = X509_get_pubkey(*cert); cd->pubkey = pkey; /* will be freed with cd */ - /* SSL_CTX_use_certificate() increased the reference count in 'cert', so - * we decrease it here with X509_free(), or it will never be cleaned up. */ - X509_free(cert); - cert = NULL; - - EVP_PKEY *privkey = xkey_load_generic_key(tls_libctx, cd, pkey, - xkey_cng_sign, (XKEY_PRIVKEY_FREE_fn *) CAPI_DATA_free); - SSL_CTX_use_PrivateKey(ssl_ctx, privkey); + *privkey = xkey_load_generic_key(tls_libctx, cd, pkey, + xkey_cng_sign, (XKEY_PRIVKEY_FREE_fn *) CAPI_DATA_free); gc_free(&gc); return 1; /* do not free cd -- its kept by xkey provider */ @@ -498,5 +492,30 @@ SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop) gc_free(&gc); return 0; } + +int +SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop) +{ + X509 *cert = NULL; + EVP_PKEY *privkey = NULL; + int ret = 0; + + if (!Load_CryptoAPI_certificate(cert_prop, &cert, &privkey)) + { + return ret; + } + if (SSL_CTX_use_certificate(ssl_ctx, cert) + && SSL_CTX_use_PrivateKey(ssl_ctx, privkey)) + { + ret = 1; + } + + /* Always free cert and privkey even if retained by ssl_ctx as + * they are reference counted */ + X509_free(cert); + EVP_PKEY_free(privkey); + return ret; +} + #endif /* HAVE_XKEY_PROVIDER */ #endif /* _WIN32 */ From f970ad99a1a1f30d091853b111e678dbdc3dede9 Mon Sep 17 00:00:00 2001 From: Selva Nair Date: Tue, 14 Mar 2023 21:35:16 -0400 Subject: [PATCH 057/229] Add a test for signing with certificates in Windows store - For each sample certificate/key pair imported into the store, load the key into xkey-provider and sign a test message. As the key is "provided", signing will use appropriate backend (Windows CNG in this case). The signature is then verified using OpenSSL. Change-Id: I520b34ba51e8c6d0247a82edc52bde181ab5a717 Signed-off-by: Selva Nair Acked-by: Gert Doering Message-Id: <20230315013516.1256700-5-selva.nair@gmail.com> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26416.html Signed-off-by: Gert Doering (cherry picked from commit 0267649a21a2af1b60fbddcb78b0ed642080d6fd) --- tests/unit_tests/openvpn/Makefile.am | 1 + tests/unit_tests/openvpn/test_cryptoapi.c | 166 ++++++++++++++++++++++ 2 files changed, 167 insertions(+) diff --git a/tests/unit_tests/openvpn/Makefile.am b/tests/unit_tests/openvpn/Makefile.am index 339c7ef3c09..4391a54e2bc 100644 --- a/tests/unit_tests/openvpn/Makefile.am +++ b/tests/unit_tests/openvpn/Makefile.am @@ -157,6 +157,7 @@ cryptoapi_testdriver_LDFLAGS = @TEST_LDFLAGS@ \ $(OPTIONAL_CRYPTO_LIBS) -lcrypt32 -lncrypt cryptoapi_testdriver_SOURCES = test_cryptoapi.c mock_msg.c \ $(top_srcdir)/src/openvpn/xkey_helper.c \ + $(top_srcdir)/src/openvpn/xkey_provider.c \ $(top_srcdir)/src/openvpn/buffer.c \ $(top_srcdir)/src/openvpn/base64.c \ $(top_srcdir)/src/openvpn/platform.c \ diff --git a/tests/unit_tests/openvpn/test_cryptoapi.c b/tests/unit_tests/openvpn/test_cryptoapi.c index ccb3207c304..b07e8935c3d 100644 --- a/tests/unit_tests/openvpn/test_cryptoapi.c +++ b/tests/unit_tests/openvpn/test_cryptoapi.c @@ -47,6 +47,7 @@ #include /* pull-in the whole file to test static functions */ struct management *management; /* global */ +static OSSL_PROVIDER *prov[2]; /* mock a management function that xkey_provider needs */ char * @@ -66,6 +67,11 @@ OSSL_LIB_CTX *tls_libctx; #define _countof(x) sizeof((x))/sizeof(*(x)) #endif +/* A message for signing */ +static const char *test_msg = "Lorem ipsum dolor sit amet, consectetur " + "adipisici elit, sed eiusmod tempor incidunt " + "ut labore et dolore magna aliqua."; + /* test data */ static const uint8_t test_hash[] = { 0x77, 0x38, 0x65, 0x00, 0x1e, 0x96, 0x48, 0xc6, 0x57, 0x0b, 0xae, @@ -336,6 +342,164 @@ test_find_cert_byissuer(void **state) gc_free(&gc); } +static int +setup_cryptoapi_sign(void **state) +{ + (void) state; + /* Initialize providers in a way matching what OpenVPN core does */ + tls_libctx = OSSL_LIB_CTX_new(); + prov[0] = OSSL_PROVIDER_load(tls_libctx, "default"); + OSSL_PROVIDER_add_builtin(tls_libctx, "ovpn.xkey", xkey_provider_init); + prov[1] = OSSL_PROVIDER_load(tls_libctx, "ovpn.xkey"); + + /* set default propq as we do in ssl_openssl.c */ + EVP_set_default_properties(tls_libctx, "?provider!=ovpn.xkey"); + return 0; +} + +static int +teardown_cryptoapi_sign(void **state) +{ + (void) state; + for (size_t i = 0; i < _countof(prov); i++) + { + if (prov[i]) + { + OSSL_PROVIDER_unload(prov[i]); + prov[i] = NULL; + } + } + OSSL_LIB_CTX_free(tls_libctx); + tls_libctx = NULL; + return 0; +} + +/** + * Sign "test_msg" using a private key. The key may be a "provided" key + * in which case its signed by the provider's backend -- cryptoapi in our + * case. Then verify the signature using OpenSSL. + * Returns 1 on success, 0 on error. + */ +static int +digest_sign_verify(EVP_PKEY *privkey, EVP_PKEY *pubkey) +{ + uint8_t *sig = NULL; + size_t siglen = 0; + int ret = 0; + + OSSL_PARAM params[2] = {OSSL_PARAM_END}; + const char *mdname = "SHA256"; + + if (EVP_PKEY_get_id(privkey) == EVP_PKEY_RSA) + { + const char *padmode = "pss"; /* RSA_PSS: for all other params, use defaults */ + params[0] = OSSL_PARAM_construct_utf8_string(OSSL_SIGNATURE_PARAM_PAD_MODE, + (char *)padmode, 0); + params[1] = OSSL_PARAM_construct_end(); + } + else if (EVP_PKEY_get_id(privkey) == EVP_PKEY_EC) + { + params[0] = OSSL_PARAM_construct_end(); + } + else + { + print_error("Unknown key type in digest_sign_verify()"); + return ret; + } + + EVP_PKEY_CTX *pctx = NULL; + EVP_MD_CTX *mctx = EVP_MD_CTX_new(); + + if (!mctx + || EVP_DigestSignInit_ex(mctx, &pctx, mdname, tls_libctx, NULL, privkey, params) <= 0) + { + /* cmocka assert output for these kinds of failures is hardly explanatory, + * print a message and assert in caller. */ + print_error("Failed to initialize EVP_DigestSignInit_ex()\n"); + goto done; + } + + /* sign with sig = NULL to get required siglen */ + if (EVP_DigestSign(mctx, sig, &siglen, (uint8_t *)test_msg, strlen(test_msg)) != 1) + { + print_error("EVP_DigestSign: failed to get required signature size"); + goto done; + } + assert_true(siglen > 0); + + if ((sig = test_calloc(1, siglen)) == NULL) + { + print_error("Out of memory"); + goto done; + } + if (EVP_DigestSign(mctx, sig, &siglen, (uint8_t *)test_msg, strlen(test_msg)) != 1) + { + print_error("EVP_DigestSign: signing failed"); + goto done; + } + + /* + * Now validate the signature using OpenSSL. Just use the public key + * which is a native OpenSSL key. + */ + EVP_MD_CTX_free(mctx); /* this also frees pctx */ + mctx = EVP_MD_CTX_new(); + pctx = NULL; + if (!mctx + || EVP_DigestVerifyInit_ex(mctx, &pctx, mdname, tls_libctx, NULL, pubkey, params) <= 0) + { + print_error("Failed to initialize EVP_DigestVerifyInit_ex()"); + goto done; + } + if (EVP_DigestVerify(mctx, sig, siglen, (uint8_t *)test_msg, strlen(test_msg)) != 1) + { + print_error("EVP_DigestVerify failed"); + goto done; + } + ret = 1; + +done: + if (mctx) + { + EVP_MD_CTX_free(mctx); /* this also frees pctx */ + } + test_free(sig); + return ret; +} + +/* Load sample certificates & keys, sign a test message using + * them and verify the signature. + */ +void +test_cryptoapi_sign(void **state) +{ + (void) state; + char select_string[64]; + X509 *x509 = NULL; + EVP_PKEY *privkey = NULL; + + import_certs(state); /* a no-op if already imported */ + assert_true(certs_loaded); + + for (struct test_cert *c = certs; c->cert; c++) + { + if (c->valid == 0) + { + continue; + } + openvpn_snprintf(select_string, sizeof(select_string), "THUMB:%s", c->hash); + if (Load_CryptoAPI_certificate(select_string, &x509, &privkey) != 1) + { + fail_msg("Load_CryptoAPI_certificate failed: <%s>", c->friendly_name); + return; + } + EVP_PKEY *pubkey = X509_get_pubkey(x509); + assert_int_equal(digest_sign_verify(privkey, pubkey), 1); + X509_free(x509); + EVP_PKEY_free(privkey); + } +} + static void test_parse_hexstring(void **state) { @@ -366,6 +530,8 @@ main(void) cmocka_unit_test(test_find_cert_bythumb), cmocka_unit_test(test_find_cert_byname), cmocka_unit_test(test_find_cert_byissuer), + cmocka_unit_test_setup_teardown(test_cryptoapi_sign, setup_cryptoapi_sign, + teardown_cryptoapi_sign), }; int ret = cmocka_run_group_tests_name("cryptoapi tests", tests, NULL, cleanup); From fd71bce651d5f606d3c1d430c7c0911fe119f075 Mon Sep 17 00:00:00 2001 From: Lev Stipakov Date: Wed, 15 Mar 2023 15:38:08 +0200 Subject: [PATCH 058/229] Support --inactive option for DCO When DCO is in use, userland doesn't see any traffic which breaks --inactive option. Fix by adding inactivity check to inactivity timeout callback. Get the cumulative tun bytes count (ping packets are excluded) from DCO and compare it to the previous value stored in c2.inactivity_bytes. Reset inactivity timer and update c2.inactivity_bytes if amount of new bytes exceeds inactivity_minimum_bytes, otherwise terminate session due to inactivity. Github: Fixes OpenVPN/openvpn#228 Currently works only on Windows, since we don't yet have single peer stats implementation for Linux and FreeBSD. Change-Id: Ib417b965bc4a2c17b51935b43c9627b106716526 Signed-off-by: Lev Stipakov Acked-by: Heiko Hund Message-Id: <20230315133808.1550-1-lstipakov@gmail.com> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26421.html Signed-off-by: Gert Doering (cherry picked from commit 514eefb14ace41a5790e59b81654d1d5eed60670) --- src/openvpn/dco_win.c | 2 ++ src/openvpn/forward.c | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/src/openvpn/dco_win.c b/src/openvpn/dco_win.c index 26463b387de..4b414efa56d 100644 --- a/src/openvpn/dco_win.c +++ b/src/openvpn/dco_win.c @@ -429,6 +429,8 @@ dco_get_peer_stats(struct context *c) c->c2.dco_read_bytes = stats.TransportBytesReceived; c->c2.dco_write_bytes = stats.TransportBytesSent; + c->c2.tun_read_bytes = stats.TunBytesReceived; + c->c2.tun_write_bytes = stats.TunBytesSent; return 0; } diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c index 0e86b58c677..ddfd5a18358 100644 --- a/src/openvpn/forward.c +++ b/src/openvpn/forward.c @@ -460,10 +460,33 @@ check_add_routes(struct context *c) /* * Should we exit due to inactivity timeout? + * + * In the non-dco case, the timeout is reset via register_activity() + * whenever there is sufficient activity on tun or link, so this function + * is only ever called to raise the TERM signal. + * + * With DCO, OpenVPN does not see incoming or outgoing data packets anymore + * and the logic needs to change - we permit the event to trigger and check + * kernel DCO counters here, returning and rearming the timer if there was + * sufficient traffic. */ static void check_inactivity_timeout(struct context *c) { + if (dco_enabled(&c->options) && dco_get_peer_stats(c) == 0) + { + int64_t tot_bytes = c->c2.tun_read_bytes + c->c2.tun_write_bytes; + int64_t new_bytes = tot_bytes - c->c2.inactivity_bytes; + + if (new_bytes >= c->options.inactivity_minimum_bytes) + { + c->c2.inactivity_bytes = tot_bytes; + event_timeout_reset(&c->c2.inactivity_interval); + + return; + } + } + msg(M_INFO, "Inactivity timeout (--inactive), exiting"); register_signal(c->sig, SIGTERM, "inactive"); } From a05ec70edd5178aac7b7432c57878c32aa838013 Mon Sep 17 00:00:00 2001 From: Michael Baentsch Date: Sun, 19 Mar 2023 08:54:41 +0100 Subject: [PATCH 059/229] using OpenSSL3 API for EVP PKEY type name reporting Signed-off-by: Michael Baentsch Acked-by: Arne Schwabe Message-Id: <20230319075441.13021-1-info@baentsch.ch> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26439.html Signed-off-by: Gert Doering (cherry picked from commit 6c111be9b109a6dbcd39cac7821ea3dd78ff6adf) --- src/openvpn/ssl_openssl.c | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/openvpn/ssl_openssl.c b/src/openvpn/ssl_openssl.c index 2b932af9db2..65b36d1cbf4 100644 --- a/src/openvpn/ssl_openssl.c +++ b/src/openvpn/ssl_openssl.c @@ -1501,7 +1501,11 @@ tls_ctx_use_management_external_key(struct tls_root_ctx *ctx) } EVP_PKEY_free(privkey); #else /* ifdef HAVE_XKEY_PROVIDER */ +#if OPENSSL_VERSION_NUMBER < 0x30000000L if (EVP_PKEY_id(pkey) == EVP_PKEY_RSA) +#else /* OPENSSL_VERSION_NUMBER < 0x30000000L */ + if (EVP_PKEY_is_a(pkey, "RSA")) +#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */ { if (!tls_ctx_use_external_rsa_key(ctx, pkey)) { @@ -1509,7 +1513,11 @@ tls_ctx_use_management_external_key(struct tls_root_ctx *ctx) } } #if (OPENSSL_VERSION_NUMBER > 0x10100000L) && !defined(OPENSSL_NO_EC) +#if OPENSSL_VERSION_NUMBER < 0x30000000L else if (EVP_PKEY_id(pkey) == EVP_PKEY_EC) +#else /* OPENSSL_VERSION_NUMBER < 0x30000000L */ + else if (EVP_PKEY_is_a(pkey, "EC")) +#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */ { if (!tls_ctx_use_external_ec_key(ctx, pkey)) { @@ -2064,10 +2072,15 @@ print_cert_details(X509 *cert, char *buf, size_t buflen) } int typeid = EVP_PKEY_id(pkey); +#if OPENSSL_VERSION_NUMBER < 0x30000000L + bool is_ec = typeid == EVP_PKEY_EC; +#else + bool is_ec = EVP_PKEY_is_a(pkey, "EC"); +#endif #ifndef OPENSSL_NO_EC char groupname[256]; - if (typeid == EVP_PKEY_EC) + if (is_ec) { size_t len; if (EVP_PKEY_get_group_name(pkey, groupname, sizeof(groupname), &len)) @@ -2080,9 +2093,9 @@ print_cert_details(X509 *cert, char *buf, size_t buflen) } } #endif - if (EVP_PKEY_id(pkey) != 0) + if (typeid != 0) { - int typeid = EVP_PKEY_id(pkey); +#if OPENSSL_VERSION_NUMBER < 0x30000000L type = OBJ_nid2sn(typeid); /* OpenSSL reports rsaEncryption, dsaEncryption and @@ -2104,6 +2117,13 @@ print_cert_details(X509 *cert, char *buf, size_t buflen) { type = "unknown type"; } +#else /* OpenSSL >= 3 */ + type = EVP_PKEY_get0_type_name(pkey); + if (type == NULL) + { + type = "(error getting public key type)"; + } +#endif /* if OPENSSL_VERSION_NUMBER < 0x30000000L */ } char sig[128] = { 0 }; From 31279f71ab4124516fd0c2143f67a0c3f008ad20 Mon Sep 17 00:00:00 2001 From: Arne Schwabe Date: Wed, 1 Mar 2023 14:53:52 +0100 Subject: [PATCH 060/229] Use key_state instead of multi for tls_send_payload parameter Currently, this function and other parts of OpenVPN assume that multi->session[TM_ACTIVE].key[KS_PRIMARY] is always the right session to send control message. This assumption was only achieve through complicated session moving and shuffling in our state machine in the past. The old logic basically also always assumed that control messages are always for fully authenticated clients. This assumption was never really true (see AUTH_FAILED message) but has been broken even more by auth-pending. Cleaning up the state machine transitions in 7dcde87b7a broke this assumption even more. This change now allows to specify the key_state/TLS session that is used to send the control message. Signed-off-by: Arne Schwabe Acked-by: Gert Doering Message-Id: <20230301135353.2811069-1-arne@rfc2549.org> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26319.html Signed-off-by: Gert Doering (cherry picked from commit 06af538eb7bde36feb20ef63febb171c9607a5e6) --- src/openvpn/forward.c | 5 ++++- src/openvpn/ssl.c | 7 ++----- src/openvpn/ssl.h | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c index ddfd5a18358..29490a2c4bc 100644 --- a/src/openvpn/forward.c +++ b/src/openvpn/forward.c @@ -372,8 +372,11 @@ send_control_channel_string_dowork(struct tls_multi *multi, struct gc_arena gc = gc_new(); bool stat; + ASSERT(multi); + struct key_state *ks = get_key_scan(multi, 0); + /* buffered cleartext write onto TLS control channel */ - stat = tls_send_payload(multi, (uint8_t *) str, strlen(str) + 1); + stat = tls_send_payload(ks, (uint8_t *) str, strlen(str) + 1); msg(msglevel, "SENT CONTROL [%s]: '%s' (status=%d)", tls_common_name(multi, false), diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c index fe6390fada5..60aaee8da9e 100644 --- a/src/openvpn/ssl.c +++ b/src/openvpn/ssl.c @@ -4007,18 +4007,15 @@ tls_post_encrypt(struct tls_multi *multi, struct buffer *buf) */ bool -tls_send_payload(struct tls_multi *multi, +tls_send_payload(struct key_state *ks, const uint8_t *data, int size) { - struct key_state *ks; bool ret = false; tls_clear_error(); - ASSERT(multi); - - ks = get_key_scan(multi, 0); + ASSERT(ks); if (ks->state >= S_ACTIVE) { diff --git a/src/openvpn/ssl.h b/src/openvpn/ssl.h index a050cd5c981..4ed4cfaa4f4 100644 --- a/src/openvpn/ssl.h +++ b/src/openvpn/ssl.h @@ -424,7 +424,7 @@ void ssl_put_auth_challenge(const char *cr_str); /* * Send a payload over the TLS control channel */ -bool tls_send_payload(struct tls_multi *multi, +bool tls_send_payload(struct key_state *ks, const uint8_t *data, int size); From da083c3b9bc1b5720a4dcbef9c32bbbbec0dcce6 Mon Sep 17 00:00:00 2001 From: Arne Schwabe Date: Wed, 1 Mar 2023 14:53:53 +0100 Subject: [PATCH 061/229] Make sending plain text control message session aware The control messages coming from auth pending should always be on the session that triggered them (i.e. INITIAL or ACTIVE) and not always on the active session. Rework the code path that trigger those messsages from management and plugin/script to specify the TLS session. We only support the two TLS sessions that are supposed to be active. TLS sessions in any lame slot (TM_LAME or KS_LAME) are not considered to be candidates for sending messages as these slots only serve to keep key material around. Unfortunately, this fix requires the management interface to be changed to allow including the specific session the messages should to go to. As there are very few users of this interface with auth-pending, I made this a hard change instead of adding hacky workaround code that is not always working correctly anyway. send_control_channel_string() will continue to only use the primary session and key but the current users of that (push replys and exit notification) already require the established session to be the active one, so there no changes needed at the moment. Github: fixes OpenVPN/openvpn#256 Signed-off-by: Arne Schwabe Acked-by: Gert Doering Message-Id: <20230301135353.2811069-2-arne@rfc2549.org> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26320.html Signed-off-by: Gert Doering (cherry picked from commit a261e173341f8e68505a6ab5a413d09b0797a459) --- Changes.rst | 13 +++++++++++++ doc/management-notes.txt | 13 +++++++++---- src/openvpn/forward.c | 12 ++++++------ src/openvpn/forward.h | 11 ++++++----- src/openvpn/manage.c | 13 ++++++++----- src/openvpn/manage.h | 3 ++- src/openvpn/multi.c | 20 +++++++++++++++++++- src/openvpn/push.c | 27 +++++++++++++++++++-------- src/openvpn/push.h | 8 +++++--- src/openvpn/ssl_verify.c | 9 +++++---- 10 files changed, 92 insertions(+), 37 deletions(-) diff --git a/Changes.rst b/Changes.rst index ba7952b3296..032ef108806 100644 --- a/Changes.rst +++ b/Changes.rst @@ -1,3 +1,16 @@ +Overview of changes in 2.6.2 +============================ + +Bug fixes +--------- +- sending of AUTH_PENDING and INFO_PRE messages fixed (OpenVPN/openvpn#256) + +User visible changes +-------------------- +- The ``client-pending-auth`` management command now requires also the + key id. The management version has been changed to 5 to indicate this change. + + Overview of changes in 2.6.1 ============================ diff --git a/doc/management-notes.txt b/doc/management-notes.txt index 34f301db7e0..b9947fa342a 100644 --- a/doc/management-notes.txt +++ b/doc/management-notes.txt @@ -613,10 +613,10 @@ COMMAND -- client-pending-auth (OpenVPN 2.5 or higher) Instruct OpenVPN server to send AUTH_PENDING and INFO_PRE message to signal a pending authenticating to the client. A pending auth means -that the connecting requires extra authentication like a one time +that connecting requires extra authentication like a one time password or doing a single sign on via web. - client-pending-auth {CID} {EXTRA} {TIMEOUT} + client-pending-auth {CID} {KID} {EXTRA} {TIMEOUT} The server will send AUTH_PENDING and INFO_PRE,{EXTRA} to the client. If the client supports accepting keywords to AUTH_PENDING (announced via IV_PROTO), @@ -639,11 +639,16 @@ Both client and server limit the maximum timeout to the smaller value of half th For the format of {EXTRA} see below. For OpenVPN server this is a stateless operation and needs to be followed by a client-deny/client-auth[-nt] command -(that is the result of the out of band authentication). +(that is the result of the out-of-band authentication). + +Note that the {KID} argument has been added in management version 5 +to specify the pending client key the authentication belongs to. +This ensures that the pending auth message is tied strictly to the +authentication session. Before issuing a client-pending-auth to a client instead of a client-auth/client-deny, the server should check the IV_SSO -environment variable for whether the method is supported. Currently +environment variable for whether the method is supported. Currently, defined methods are crtext for challenge/response using text (e.g., TOTP), openurl (deprecated) and webauth for opening a URL in the client to continue authentication. A client supporting webauth and diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c index 29490a2c4bc..28a96f940d8 100644 --- a/src/openvpn/forward.c +++ b/src/openvpn/forward.c @@ -366,20 +366,20 @@ check_connection_established(struct context *c) } bool -send_control_channel_string_dowork(struct tls_multi *multi, +send_control_channel_string_dowork(struct tls_session *session, const char *str, int msglevel) { struct gc_arena gc = gc_new(); bool stat; - ASSERT(multi); - struct key_state *ks = get_key_scan(multi, 0); + ASSERT(session); + struct key_state *ks = &session->key[KS_PRIMARY]; /* buffered cleartext write onto TLS control channel */ stat = tls_send_payload(ks, (uint8_t *) str, strlen(str) + 1); msg(msglevel, "SENT CONTROL [%s]: '%s' (status=%d)", - tls_common_name(multi, false), + session->common_name ? session->common_name : "UNDEF", sanitize_control_message(str, &gc), (int) stat); @@ -399,8 +399,8 @@ send_control_channel_string(struct context *c, const char *str, int msglevel) { if (c->c2.tls_multi) { - bool ret = send_control_channel_string_dowork(c->c2.tls_multi, - str, msglevel); + struct tls_session *session = &c->c2.tls_multi->session[TM_ACTIVE]; + bool ret = send_control_channel_string_dowork(session, str, msglevel); reschedule_multi_process(c); return ret; diff --git a/src/openvpn/forward.h b/src/openvpn/forward.h index 7376bca23ab..e19115ea178 100644 --- a/src/openvpn/forward.h +++ b/src/openvpn/forward.h @@ -265,21 +265,22 @@ send_control_channel_string(struct context *c, const char *str, int msglevel); /* * Send a string to remote over the TLS control channel. - * Used for push/pull messages, passing username/password, - * etc. + * Used for push/pull messages, auth pending and other clear text + * control messages. * * This variant does not schedule the actual sending of the message * The caller needs to ensure that it is scheduled or call * send_control_channel_string * - * @param multi - The tls_multi structure of the VPN tunnel associated - * with the packet. + * @param session - The session structure of the VPN tunnel associated + * with the packet. The method will always use the + * primary key (KS_PRIMARY) for sending the message * @param str - The message to be sent * @param msglevel - Message level to use for logging */ bool -send_control_channel_string_dowork(struct tls_multi *multi, +send_control_channel_string_dowork(struct tls_session *session, const char *str, int msglevel); diff --git a/src/openvpn/manage.c b/src/openvpn/manage.c index db88e347911..05358af4525 100644 --- a/src/openvpn/manage.c +++ b/src/openvpn/manage.c @@ -1042,22 +1042,25 @@ parse_uint(const char *str, const char *what, unsigned int *uint) * * @param man The management interface struct * @param cid_str The CID in string form + * @param kid_str The key ID in string form * @param extra The string to be send to the client containing * the information of the additional steps */ static void man_client_pending_auth(struct management *man, const char *cid_str, - const char *extra, const char *timeout_str) + const char *kid_str, const char *extra, + const char *timeout_str) { unsigned long cid = 0; + unsigned int kid = 0; unsigned int timeout = 0; - if (parse_cid(cid_str, &cid) + if (parse_cid(cid_str, &cid) && parse_uint(kid_str, "KID", &kid) && parse_uint(timeout_str, "TIMEOUT", &timeout)) { if (man->persist.callback.client_pending_auth) { bool ret = (*man->persist.callback.client_pending_auth) - (man->persist.callback.arg, cid, extra, timeout); + (man->persist.callback.arg, cid, kid, extra, timeout); if (ret) { @@ -1594,9 +1597,9 @@ man_dispatch_command(struct management *man, struct status_output *so, const cha } else if (streq(p[0], "client-pending-auth")) { - if (man_need(man, p, 3, 0)) + if (man_need(man, p, 4, 0)) { - man_client_pending_auth(man, p[1], p[2], p[3]); + man_client_pending_auth(man, p[1], p[2], p[3], p[4]); } } else if (streq(p[0], "rsa-sig")) diff --git a/src/openvpn/manage.h b/src/openvpn/manage.h index 2ced9083581..07317a402ae 100644 --- a/src/openvpn/manage.h +++ b/src/openvpn/manage.h @@ -52,7 +52,7 @@ #include "socket.h" #include "mroute.h" -#define MANAGEMENT_VERSION 4 +#define MANAGEMENT_VERSION 5 #define MANAGEMENT_N_PASSWORD_RETRIES 3 #define MANAGEMENT_LOG_HISTORY_INITIAL_SIZE 100 #define MANAGEMENT_ECHO_BUFFER_SIZE 100 @@ -194,6 +194,7 @@ struct management_callback struct buffer_list *cc_config); /* ownership transferred */ bool (*client_pending_auth) (void *arg, const unsigned long cid, + const unsigned int kid, const char *extra, unsigned int timeout); char *(*get_peer_info) (void *arg, const unsigned long cid); diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c index 53c17b3a462..933bf9bc3d6 100644 --- a/src/openvpn/multi.c +++ b/src/openvpn/multi.c @@ -3989,15 +3989,33 @@ management_kill_by_cid(void *arg, const unsigned long cid, const char *kill_msg) static bool management_client_pending_auth(void *arg, const unsigned long cid, + const unsigned int mda_key_id, const char *extra, unsigned int timeout) { struct multi_context *m = (struct multi_context *) arg; struct multi_instance *mi = lookup_by_cid(m, cid); + if (mi) { + struct tls_multi *multi = mi->context.c2.tls_multi; + struct tls_session *session; + + if (multi->session[TM_INITIAL].key[KS_PRIMARY].mda_key_id == mda_key_id) + { + session = &multi->session[TM_INITIAL]; + } + else if (multi->session[TM_ACTIVE].key[KS_PRIMARY].mda_key_id == mda_key_id) + { + session = &multi->session[TM_ACTIVE]; + } + else + { + return false; + } + /* sends INFO_PRE and AUTH_PENDING messages to client */ - bool ret = send_auth_pending_messages(mi->context.c2.tls_multi, extra, + bool ret = send_auth_pending_messages(multi, session, extra, timeout); reschedule_multi_process(&mi->context); multi_schedule_context_wakeup(m, mi); diff --git a/src/openvpn/push.c b/src/openvpn/push.c index 4453e426178..54e53f6aa48 100644 --- a/src/openvpn/push.c +++ b/src/openvpn/push.c @@ -412,7 +412,16 @@ send_auth_failed(struct context *c, const char *client_reason) { buf_printf(&buf, ",%s", client_reason); } - send_control_channel_string(c, BSTR(&buf), D_PUSH); + + /* We kill the whole session, send the AUTH_FAILED to any TLS session + * that might be active */ + send_control_channel_string_dowork(&c->c2.tls_multi->session[TM_INITIAL], + BSTR(&buf), D_PUSH); + send_control_channel_string_dowork(&c->c2.tls_multi->session[TM_ACTIVE], + BSTR(&buf), D_PUSH); + + reschedule_multi_process(c); + } gc_free(&gc); @@ -420,10 +429,11 @@ send_auth_failed(struct context *c, const char *client_reason) bool -send_auth_pending_messages(struct tls_multi *tls_multi, const char *extra, - unsigned int timeout) +send_auth_pending_messages(struct tls_multi *tls_multi, + struct tls_session *session, + const char *extra, unsigned int timeout) { - struct key_state *ks = get_key_scan(tls_multi, 0); + struct key_state *ks = &session->key[KS_PRIMARY]; static const char info_pre[] = "INFO_PRE,"; @@ -440,7 +450,7 @@ send_auth_pending_messages(struct tls_multi *tls_multi, const char *extra, struct gc_arena gc = gc_new(); if ((proto & IV_PROTO_AUTH_PENDING_KW) == 0) { - send_control_channel_string_dowork(tls_multi, "AUTH_PENDING", D_PUSH); + send_control_channel_string_dowork(session, "AUTH_PENDING", D_PUSH); } else { @@ -451,7 +461,7 @@ send_auth_pending_messages(struct tls_multi *tls_multi, const char *extra, struct buffer buf = alloc_buf_gc(len, &gc); buf_printf(&buf, auth_pre); buf_printf(&buf, "%u", timeout); - send_control_channel_string_dowork(tls_multi, BSTR(&buf), D_PUSH); + send_control_channel_string_dowork(session, BSTR(&buf), D_PUSH); } size_t len = strlen(extra) + 1 + sizeof(info_pre); @@ -464,7 +474,7 @@ send_auth_pending_messages(struct tls_multi *tls_multi, const char *extra, struct buffer buf = alloc_buf_gc(len, &gc); buf_printf(&buf, info_pre); buf_printf(&buf, "%s", extra); - send_control_channel_string_dowork(tls_multi, BSTR(&buf), D_PUSH); + send_control_channel_string_dowork(session, BSTR(&buf), D_PUSH); ks->auth_deferred_expire = now + timeout; @@ -741,6 +751,7 @@ send_push_reply_auth_token(struct tls_multi *multi) { struct gc_arena gc = gc_new(); struct push_list push_list = { 0 }; + struct tls_session *session = &multi->session[TM_ACTIVE]; prepare_auth_token_push_reply(multi, &gc, &push_list); @@ -751,7 +762,7 @@ send_push_reply_auth_token(struct tls_multi *multi) /* Construct a mimimal control channel push reply message */ struct buffer buf = alloc_buf_gc(PUSH_BUNDLE_SIZE, &gc); buf_printf(&buf, "%s,%s", push_reply_cmd, e->option); - send_control_channel_string_dowork(multi, BSTR(&buf), D_PUSH); + send_control_channel_string_dowork(session, BSTR(&buf), D_PUSH); gc_free(&gc); } diff --git a/src/openvpn/push.h b/src/openvpn/push.h index 5e594a30aee..f43ab0966e3 100644 --- a/src/openvpn/push.h +++ b/src/openvpn/push.h @@ -78,16 +78,18 @@ void send_auth_failed(struct context *c, const char *client_reason); * more details on message format */ bool -send_auth_pending_messages(struct tls_multi *tls_multi, const char *extra, +send_auth_pending_messages(struct tls_multi *tls_multi, + struct tls_session *session, const char *extra, unsigned int timeout); void send_restart(struct context *c, const char *kill_msg); /** * Sends a push reply message only containin the auth-token to update - * the auth-token on the client + * the auth-token on the client. Always pushes to the active session * - * @param multi - The tls_multi structure belonging to the instance to push to + * @param multi - The \c tls_multi structure belonging to the instance + * to push to */ void send_push_reply_auth_token(struct tls_multi *multi); diff --git a/src/openvpn/ssl_verify.c b/src/openvpn/ssl_verify.c index 996aee01ffc..1b589f1a6ea 100644 --- a/src/openvpn/ssl_verify.c +++ b/src/openvpn/ssl_verify.c @@ -916,7 +916,8 @@ check_auth_pending_method(const char *peer_info, const char *method) */ static bool key_state_check_auth_pending_file(struct auth_deferred_status *ads, - struct tls_multi *multi) + struct tls_multi *multi, + struct tls_session *session) { bool ret = true; if (ads->auth_pending_file) @@ -965,7 +966,7 @@ key_state_check_auth_pending_file(struct auth_deferred_status *ads, } else { - send_auth_pending_messages(multi, BSTR(extra_buf), timeout); + send_auth_pending_messages(multi, session, BSTR(extra_buf), timeout); } } @@ -1390,7 +1391,7 @@ verify_user_pass_script(struct tls_session *session, struct tls_multi *multi, /* Check if we the plugin has written the pending auth control * file and send the pending auth to the client */ if (!key_state_check_auth_pending_file(&ks->script_auth, - multi)) + multi, session)) { retval = OPENVPN_PLUGIN_FUNC_ERROR; key_state_rm_auth_control_files(&ks->script_auth); @@ -1514,7 +1515,7 @@ verify_user_pass_plugin(struct tls_session *session, struct tls_multi *multi, { /* Check if the plugin has written the pending auth control * file and send the pending auth to the client */ - if (!key_state_check_auth_pending_file(&ks->plugin_auth, multi)) + if (!key_state_check_auth_pending_file(&ks->plugin_auth, multi, session)) { retval = OPENVPN_PLUGIN_FUNC_ERROR; } From 75cc2fa6e15ce806415aed33d7608b8d9cc00e36 Mon Sep 17 00:00:00 2001 From: Arne Schwabe Date: Wed, 1 Mar 2023 14:44:55 +0100 Subject: [PATCH 062/229] Only update frame calculation if we have a valid link sockets Without this, we will caculate a pointer to the linksocket relative to a null pointer in get_link_socket_info(), which itself does not crash and the pointer seems not to be accessed later, so we do not get a crash here. This is still not the correct behaviour and the undefined behaviour sanitiser from llvm/clang finds this. Change-Id: I82a20ac72f60f8770ea1b4ab0c8cdea31868abe7 Signed-off-by: Arne Schwabe Acked-by: Gert Doering Message-Id: <20230301134455.2810114-1-arne@rfc2549.org> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26318.html Signed-off-by: Gert Doering (cherry picked from commit 2d17869f8d9d8e27f64f1a7cd1514fbbb768807b) --- src/openvpn/init.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/openvpn/init.c b/src/openvpn/init.c index fa2681dc717..3a6f624fdaf 100644 --- a/src/openvpn/init.c +++ b/src/openvpn/init.c @@ -4687,14 +4687,15 @@ init_instance(struct context *c, const struct env_set *env, const unsigned int f if (c->mode == CM_P2P || c->mode == CM_TOP || c->mode == CM_CHILD_TCP) { link_socket_init_phase2(c); - } - /* Update dynamic frame calculation as exact transport socket information - * (IP vs IPv6) may be only available after socket phase2 has finished. - * This is only needed for --static or no crypto, NCP will recalculate this - * in tls_session_update_crypto_params (P2MP) */ - frame_calculate_dynamic(&c->c2.frame, &c->c1.ks.key_type, &c->options, - get_link_socket_info(c)); + + /* Update dynamic frame calculation as exact transport socket information + * (IP vs IPv6) may be only available after socket phase2 has finished. + * This is only needed for --static or no crypto, NCP will recalculate this + * in tls_session_update_crypto_params (P2MP) */ + frame_calculate_dynamic(&c->c2.frame, &c->c1.ks.key_type, &c->options, + get_link_socket_info(c)); + } /* * Actually do UID/GID downgrade, and chroot, if requested. From 973083746727cb797248262de56fb0a707bcdb90 Mon Sep 17 00:00:00 2001 From: Selva Nair Date: Sat, 18 Mar 2023 10:43:25 -0400 Subject: [PATCH 063/229] Unit tests: add test for SSL_CTX_use_Cryptoapi_certificate() - This is the only remaining function in cryptoapi.c that has no direct or indirect test. This test confirms that an SSL_CTX context gets a certificate and private key loaded into it and the public key in the certificate matches the private key. As signing with certificate/key pairs fetched from the store is independently tested by the 'cryptoapi_sign' test, signing is not re-tested here. The functions "setup_/teardown_cryptoapi_sign()" are renamed to "setup_/teardown_xkey_provider()" to better reflect their purpose. These are also reused for the new test. While touching this context, also fix a memory leak in test_cryptoapi_sign: X509_get_pubkey() -> X509_get0_pubkey() Signed-off-by: Selva Nair Acked-by: Gert Doering Message-Id: <20230318144325.1316320-1-selva.nair@gmail.com> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26438.html Signed-off-by: Gert Doering (cherry picked from commit 85da9de524f34db3f6bd4ebc110b25c6bcbc273d) --- tests/unit_tests/openvpn/test_cryptoapi.c | 51 ++++++++++++++++++++--- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/tests/unit_tests/openvpn/test_cryptoapi.c b/tests/unit_tests/openvpn/test_cryptoapi.c index b07e8935c3d..e64a1de3e83 100644 --- a/tests/unit_tests/openvpn/test_cryptoapi.c +++ b/tests/unit_tests/openvpn/test_cryptoapi.c @@ -343,7 +343,7 @@ test_find_cert_byissuer(void **state) } static int -setup_cryptoapi_sign(void **state) +setup_xkey_provider(void **state) { (void) state; /* Initialize providers in a way matching what OpenVPN core does */ @@ -358,7 +358,7 @@ setup_cryptoapi_sign(void **state) } static int -teardown_cryptoapi_sign(void **state) +teardown_xkey_provider(void **state) { (void) state; for (size_t i = 0; i < _countof(prov); i++) @@ -493,13 +493,52 @@ test_cryptoapi_sign(void **state) fail_msg("Load_CryptoAPI_certificate failed: <%s>", c->friendly_name); return; } - EVP_PKEY *pubkey = X509_get_pubkey(x509); + EVP_PKEY *pubkey = X509_get0_pubkey(x509); + assert_non_null(pubkey); assert_int_equal(digest_sign_verify(privkey, pubkey), 1); X509_free(x509); EVP_PKEY_free(privkey); } } +/* Test that SSL_CTX_use_Cryptoapi_certificate() sets a matching certificate + * and key in ssl_ctx. + */ +void +test_ssl_ctx_use_cryptoapicert(void **state) +{ + (void) state; + char select_string[64]; + + import_certs(state); /* a no-op if already imported */ + assert_true(certs_loaded); + + for (struct test_cert *c = certs; c->cert; c++) + { + if (c->valid == 0) + { + continue; + } + SSL_CTX *ssl_ctx = SSL_CTX_new_ex(tls_libctx, NULL, SSLv23_client_method()); + assert_non_null(ssl_ctx); + + openvpn_snprintf(select_string, sizeof(select_string), "THUMB:%s", c->hash); + if (!SSL_CTX_use_CryptoAPI_certificate(ssl_ctx, select_string)) + { + fail_msg("SSL_CTX_use_CryptoAPI_certificate failed: <%s>", c->friendly_name); + return; + } + /* Use OpenSSL to check that the cert and private key in ssl_ctx "match" */ + if (!SSL_CTX_check_private_key(ssl_ctx)) + { + fail_msg("Certificate and private key in ssl_ctx do not match for <%s>", c->friendly_name); + return; + } + + SSL_CTX_free(ssl_ctx); + } +} + static void test_parse_hexstring(void **state) { @@ -530,8 +569,10 @@ main(void) cmocka_unit_test(test_find_cert_bythumb), cmocka_unit_test(test_find_cert_byname), cmocka_unit_test(test_find_cert_byissuer), - cmocka_unit_test_setup_teardown(test_cryptoapi_sign, setup_cryptoapi_sign, - teardown_cryptoapi_sign), + cmocka_unit_test_setup_teardown(test_cryptoapi_sign, setup_xkey_provider, + teardown_xkey_provider), + cmocka_unit_test_setup_teardown(test_ssl_ctx_use_cryptoapicert, setup_xkey_provider, + teardown_xkey_provider), }; int ret = cmocka_run_group_tests_name("cryptoapi tests", tests, NULL, cleanup); From c20a15844829a186b4d5256b0e8d76b8eb074845 Mon Sep 17 00:00:00 2001 From: Selva Nair Date: Sat, 18 Mar 2023 10:13:30 -0400 Subject: [PATCH 064/229] Improve error message on short read from socks proxy Change-Id: Id00006bf8ea705d02eff2cbfba7d841e1cdb6ae1 Signed-off-by: Selva Nair Acked-by: Gert Doering Message-Id: <20230318141330.1315235-1-selva.nair@gmail.com> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26437.html Signed-off-by: Gert Doering (cherry picked from commit 172640189277c940439d24fd31a59b8faffd0b3e) --- src/openvpn/socks.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/openvpn/socks.c b/src/openvpn/socks.c index 6a672c25071..2cf0cc9f05c 100644 --- a/src/openvpn/socks.c +++ b/src/openvpn/socks.c @@ -357,11 +357,16 @@ recv_socks_reply(socket_descriptor_t sd, size = recv(sd, &c, 1, MSG_NOSIGNAL); /* error? */ - if (size != 1) + if (size < 0) { msg(D_LINK_ERRORS | M_ERRNO, "recv_socks_reply: TCP port read failed on recv()"); return false; } + else if (size == 0) + { + msg(D_LINK_ERRORS, "ERROR: recv_socks_reply: empty response from socks server"); + return false; + } if (len == 3) { From 047f772b84843344b6131e9e915472d14adcea2b Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Mon, 20 Mar 2023 20:58:20 +0100 Subject: [PATCH 065/229] dco-linux: remove M_ERRNO flag when printing netlink error message Netlink has its own error space and reports errors via the return value of its functions. For this reason remove the M_ERRNO flag when printing its errors. At the moment we get something like this: netlink reports error (-7): Invalid input data or parameter: Interrupted system call (errno=4) where the errno=4 (and its human readable representation) is a leftover from the previous recv() interrupted by a signal and it is totally unrelated to this netlink failure. Signed-off-by: Antonio Quartulli Acked-by: Gert Doering Message-Id: <20230320195820.6675-1-a@unstable.cc> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26452.html Signed-off-by: Gert Doering (cherry picked from commit 23903fd579353c9892415a750f17a9832a79cced) --- src/openvpn/dco_linux.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openvpn/dco_linux.c b/src/openvpn/dco_linux.c index e5cea3c7198..47961849bdd 100644 --- a/src/openvpn/dco_linux.c +++ b/src/openvpn/dco_linux.c @@ -153,7 +153,7 @@ ovpn_nl_recvmsgs(dco_context_t *dco, const char *prefix) default: if (ret) { - msg(M_NONFATAL|M_ERRNO, "%s: netlink reports error (%d): %s", prefix, ret, nl_geterror(-ret)); + msg(M_NONFATAL, "%s: netlink reports error (%d): %s", prefix, ret, nl_geterror(-ret)); } break; } From 92827ad84eb3a5b7ca70f3e7f34800d25790b10d Mon Sep 17 00:00:00 2001 From: Arne Schwabe Date: Mon, 20 Mar 2023 17:55:38 +0100 Subject: [PATCH 066/229] Improve description of compat-mode Explicitly say that the version specified is the one of the peer and not the version we try to emulate. Patch v2: Improve grammar. Change-Id: I3bd27a8d34d8cb4896a3b78508b7d16911571543 Change-Id: If4fb45b3426f5e0dbe6c87d5bd05681b9d733827 Signed-off-by: Arne Schwabe Acked-by: Frank Lichtenheld Message-Id: <20230320165538.902965-1-arne@rfc2549.org> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26445.html Signed-off-by: Gert Doering (cherry picked from commit daf66f4013d8facc085ea6cfaaf8a42f4d45a461) --- doc/man-sections/generic-options.rst | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/doc/man-sections/generic-options.rst b/doc/man-sections/generic-options.rst index c827651d62b..97e1b5aa610 100644 --- a/doc/man-sections/generic-options.rst +++ b/doc/man-sections/generic-options.rst @@ -53,10 +53,17 @@ which mode OpenVPN is configured as. need for /dev/urandom to be available. --compat-mode version - This option provides a way to alter the default of OpenVPN to be more - compatible with the version ``version`` specified. All of the changes - this option does can also be achieved using individual configuration - options. + This option provides a convenient way to alter the defaults of OpenVPN + to be more compatible with the version ``version`` specified. All of + the changes this option applies can also be achieved using individual + configuration options. + + The version specified with this option is the version of OpenVPN peer + OpenVPN should try to be compatible with. In general OpenVPN should be + compatible with the last two previous version without this option. E.g. + OpenVPN 2.6.0 should be compatible with 2.5.x and 2.4.x without this option. + However, there might be some edge cases that still require this option even + in these cases. Note: Using this option reverts defaults to no longer recommended values and should be avoided if possible. @@ -67,12 +74,15 @@ which mode OpenVPN is configured as. - 2.5.x or lower: ``--allow-compression asym`` is automatically added to the configuration if no other compression options are present. - 2.4.x or lower: The cipher in ``--cipher`` is appended to - ``--data-ciphers`` + ``--data-ciphers``. - 2.3.x or lower: ``--data-cipher-fallback`` is automatically added with - the same cipher as ``--cipher`` + the same cipher as ``--cipher``. - 2.3.6 or lower: ``--tls-version-min 1.0`` is added to the configuration when ``--tls-version-min`` is not explicitly set. + If not required, this is option should be avoided. Setting this option can + lower security or disable features like data-channel offloading. + --config file Load additional config options from ``file`` where each line corresponds to one command line option, but with the leading :code:`--` removed. From 8f503708ed954ff3ae43357bd9f59809581a1381 Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Tue, 21 Mar 2023 11:28:42 +0100 Subject: [PATCH 067/229] multi: don't call DCO APIs if DCO is disabled The agreement with the DCO submodule is that no API should be called if DCO is actually disabled. For this reason, every invocation must happen only after having checked that dco_enabled() returns true. Add missing checks before invoking dco_get_peer_stats_multi() Reported-by: Lev Stipakov Signed-off-by: Antonio Quartulli Acked-by: Lev Stipakov Message-Id: <20230321102842.10780-1-a@unstable.cc> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26458.html Signed-off-by: Gert Doering (cherry picked from commit 891c71db5e26291b19885b9a5ae5c72011b86658) --- src/openvpn/multi.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c index 933bf9bc3d6..1480bf477dd 100644 --- a/src/openvpn/multi.c +++ b/src/openvpn/multi.c @@ -549,7 +549,10 @@ multi_del_iroutes(struct multi_context *m, static void setenv_stats(struct multi_context *m, struct context *c) { - dco_get_peer_stats_multi(&m->top.c1.tuntap->dco, m); + if (dco_enabled(&m->top.options)) + { + dco_get_peer_stats_multi(&m->top.c1.tuntap->dco, m); + } setenv_counter(c->c2.es, "bytes_received", c->c2.link_read_bytes + c->c2.dco_read_bytes); setenv_counter(c->c2.es, "bytes_sent", c->c2.link_write_bytes + c->c2.dco_write_bytes); @@ -849,7 +852,10 @@ multi_print_status(struct multi_context *m, struct status_output *so, const int status_reset(so); - dco_get_peer_stats_multi(&m->top.c1.tuntap->dco, m); + if (dco_enabled(&m->top.options)) + { + dco_get_peer_stats_multi(&m->top.c1.tuntap->dco, m); + } if (version == 1) { From 3b967e7e05f679203ce26e027fcea5f7b4709eaa Mon Sep 17 00:00:00 2001 From: Heiko Hund Date: Fri, 10 Mar 2023 06:08:12 +0100 Subject: [PATCH 068/229] dns option: allow up to eight addresses per server This change allows configuration of more than one address per family for a DNS server. This way you can specify backup addresses in case a server is not reachable. During closer inspection of the various DNS backend in supported operation systems it turned out that our previous idea to have more than one DNS server applied in order of priority does not work in most cases. Thus it became important to be able to specify backup addresses. So instead of doing dns server 1 address 1.2.3.4 2001::1 dns server 2 address 5.6.7.8 2001::2 to specify a backup addresses, this is now done like so: dns server 1 address 1.2.3.4 2001::1 dns server 1 address 5.6.7.8 2001::2 or you can have all the addresses on one line if you like: dns server 1 address 1.2.3.4 2001::1 2001::2 5.6.7.8 This also saves some repeated options when (backup) servers share the same settings like "resolve-domains" compared to the originally intended way. The order in which addresses are given is retained for backends that support this sort of cross address family ordering. Change-Id: I9bd3d6d05da4e61a5fa05c0e455fc770b1fe186a Signed-off-by: Heiko Hund Acked-by: Arne Schwabe Message-Id: <20230310050814.67246-1-heiko@ist.eigentlich.net> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26386.html Signed-off-by: Gert Doering (cherry picked from commit 424ae5906388af8769ae448080fa3b7ec266e8d8) --- doc/man-sections/client-options.rst | 9 ++-- doc/man-sections/script-options.rst | 6 +-- src/openvpn/dns.c | 83 +++++++++++++++-------------- src/openvpn/dns.h | 18 ++++--- src/openvpn/options.c | 76 ++++++++++++++------------ 5 files changed, 103 insertions(+), 89 deletions(-) diff --git a/doc/man-sections/client-options.rst b/doc/man-sections/client-options.rst index 974cc9920f8..fe9ffa6abbc 100644 --- a/doc/man-sections/client-options.rst +++ b/doc/man-sections/client-options.rst @@ -168,7 +168,7 @@ configuration. :: dns search-domains domain [domain ...] - dns server n address addr[:port] [addr[:port]] + dns server n address addr[:port] [addr[:port] ...] dns server n resolve-domains|exclude-domains domain [domain ...] dns server n dnssec yes|optional|no dns server n transport DoH|DoT|plain @@ -186,9 +186,10 @@ configuration. lower numbers come first. DNS servers being pushed to a client replace already configured DNS servers with the same server id. - The ``address`` option configures the IPv4 and / or IPv6 address of - the DNS server. Optionally a port can be appended after a colon. IPv6 - addresses need to be enclosed in brackets if a port is appended. + The ``address`` option configures the IPv4 and / or IPv6 address(es) of + the DNS server. Up to eight addresses can be specified per DNS server. + Optionally a port can be appended after a colon. IPv6 addresses need to + be enclosed in brackets if a port is appended. The ``resolve-domains`` and ``exclude-domains`` options take one or more DNS domains which are explicitly resolved or explicitly not resolved diff --git a/doc/man-sections/script-options.rst b/doc/man-sections/script-options.rst index 3d2c7445373..d73231edc41 100644 --- a/doc/man-sections/script-options.rst +++ b/doc/man-sections/script-options.rst @@ -660,10 +660,8 @@ instances. :: dns_search_domain_{n} - dns_server_{n}_address4 - dns_server_{n}_port4 - dns_server_{n}_address6 - dns_server_{n}_port6 + dns_server_{n}_address_{m} + dns_server_{n}_port_{m} dns_server_{n}_resolve_domain_{m} dns_server_{n}_exclude_domain_{m} dns_server_{n}_dnssec diff --git a/src/openvpn/dns.c b/src/openvpn/dns.c index 9f2a7d5ecaf..b7808db14ea 100644 --- a/src/openvpn/dns.c +++ b/src/openvpn/dns.c @@ -64,7 +64,7 @@ dns_server_addr_parse(struct dns_server *server, const char *addr) char addrcopy[INET6_ADDRSTRLEN] = {0}; size_t copylen = 0; in_port_t port = 0; - int af; + sa_family_t af; char *first_colon = strchr(addr, ':'); char *last_colon = strrchr(addr, ':'); @@ -115,21 +115,26 @@ dns_server_addr_parse(struct dns_server *server, const char *addr) return false; } + if (server->addr_count >= SIZE(server->addr)) + { + return false; + } + if (ai->ai_family == AF_INET) { struct sockaddr_in *sin = (struct sockaddr_in *)ai->ai_addr; - server->addr4_defined = true; - server->addr4.s_addr = ntohl(sin->sin_addr.s_addr); - server->port4 = port; + server->addr[server->addr_count].in.a4.s_addr = ntohl(sin->sin_addr.s_addr); } else { struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ai->ai_addr; - server->addr6_defined = true; - server->addr6 = sin6->sin6_addr; - server->port6 = port; + server->addr[server->addr_count].in.a6 = sin6->sin6_addr; } + server->addr[server->addr_count].family = af; + server->addr[server->addr_count].port = port; + server->addr_count += 1; + freeaddrinfo(ai); return true; } @@ -197,7 +202,7 @@ dns_options_verify(int msglevel, const struct dns_options *o) o->servers ? o->servers : o->servers_prepull; while (server) { - if (!server->addr4_defined && !server->addr6_defined) + if (server->addr_count == 0) { msg(msglevel, "ERROR: dns server %ld does not have an address assigned", server->priority); return false; @@ -376,26 +381,23 @@ setenv_dns_options(const struct dns_options *o, struct env_set *es) for (i = 1, s = o->servers; s != NULL; i++, s = s->next) { - if (s->addr4_defined) - { - setenv_dns_option(es, "dns_server_%d_address4", i, -1, - print_in_addr_t(s->addr4.s_addr, 0, &gc)); - } - if (s->port4) - { - setenv_dns_option(es, "dns_server_%d_port4", i, -1, - print_in_port_t(s->port4, &gc)); - } - - if (s->addr6_defined) - { - setenv_dns_option(es, "dns_server_%d_address6", i, -1, - print_in6_addr(s->addr6, 0, &gc)); - } - if (s->port6) + for (j = 0; j < s->addr_count; ++j) { - setenv_dns_option(es, "dns_server_%d_port6", i, -1, - print_in_port_t(s->port6, &gc)); + if (s->addr[j].family == AF_INET) + { + setenv_dns_option(es, "dns_server_%d_address_%d", i, j + 1, + print_in_addr_t(s->addr[j].in.a4.s_addr, 0, &gc)); + } + else + { + setenv_dns_option(es, "dns_server_%d_address_%d", i, j + 1, + print_in6_addr(s->addr[j].in.a6, 0, &gc)); + } + if (s->addr[j].port) + { + setenv_dns_option(es, "dns_server_%d_port_%d", i, j + 1, + print_in_port_t(s->addr[j].port, &gc)); + } } if (s->domains) @@ -439,30 +441,29 @@ show_dns_options(const struct dns_options *o) { msg(D_SHOW_PARMS, " DNS server #%d:", i++); - if (server->addr4_defined) + for (int j = 0; j < server->addr_count; ++j) { - const char *addr = print_in_addr_t(server->addr4.s_addr, 0, &gc); - if (server->port4) + const char *addr; + const char *fmt_port; + if (server->addr[j].family == AF_INET) { - const char *port = print_in_port_t(server->port4, &gc); - msg(D_SHOW_PARMS, " address4 = %s:%s", addr, port); + addr = print_in_addr_t(server->addr[j].in.a4.s_addr, 0, &gc); + fmt_port = " address = %s:%s"; } else { - msg(D_SHOW_PARMS, " address4 = %s", addr); + addr = print_in6_addr(server->addr[j].in.a6, 0, &gc); + fmt_port = " address = [%s]:%s"; } - } - if (server->addr6_defined) - { - const char *addr = print_in6_addr(server->addr6, 0, &gc); - if (server->port6) + + if (server->addr[j].port) { - const char *port = print_in_port_t(server->port6, &gc); - msg(D_SHOW_PARMS, " address6 = [%s]:%s", addr, port); + const char *port = print_in_port_t(server->addr[j].port, &gc); + msg(D_SHOW_PARMS, fmt_port, addr, port); } else { - msg(D_SHOW_PARMS, " address6 = %s", addr); + msg(D_SHOW_PARMS, " address = %s", addr); } } diff --git a/src/openvpn/dns.h b/src/openvpn/dns.h index 03a894f28a8..162dec12ef1 100644 --- a/src/openvpn/dns.h +++ b/src/openvpn/dns.h @@ -52,15 +52,21 @@ struct dns_domain { const char *name; }; +struct dns_server_addr +{ + union { + struct in_addr a4; + struct in6_addr a6; + } in; + sa_family_t family; + in_port_t port; +}; + struct dns_server { struct dns_server *next; long priority; - bool addr4_defined; - bool addr6_defined; - struct in_addr addr4; - struct in6_addr addr6; - in_port_t port4; - in_port_t port6; + size_t addr_count; + struct dns_server_addr addr[8]; struct dns_domain *domains; enum dns_domain_type domain_type; enum dns_security dnssec; diff --git a/src/openvpn/options.c b/src/openvpn/options.c index 8f0e21940de..64a8250b297 100644 --- a/src/openvpn/options.c +++ b/src/openvpn/options.c @@ -512,7 +512,7 @@ static const char usage_message[] = " each filter is applied in the order of appearance.\n" "--dns server