Attention is currently required from: plaisthos.
Hello plaisthos,
I'd like you to do a code review.
Please visit
http://gerrit.openvpn.net/c/openvpn/+/1773?usp=email
to review the following change.
Change subject: options: Factor out options_string code from options.c
......................................................................
options: Factor out options_string code from options.c
options.c is a huge, unwieldly file. So remove some
closely related code that has no direct dependencies
with the other functions in the file.
This time it is the functions related to the creation
and testing of the options string.
Change-Id: I6824058fc9c4efd48ceeb32d4164f97b2a243d85
Signed-off-by: Frank Lichtenheld <[email protected]>
---
M CMakeLists.txt
M src/openvpn/Makefile.am
M src/openvpn/init.c
M src/openvpn/occ.c
M src/openvpn/options.c
M src/openvpn/options.h
A src/openvpn/options_string.c
A src/openvpn/options_string.h
M src/openvpn/ssl.c
9 files changed, 532 insertions(+), 458 deletions(-)
git pull ssh://gerrit.openvpn.net:29418/openvpn refs/changes/73/1773/1
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b6054d6..3981bae 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -520,6 +520,8 @@
src/openvpn/options_parse.c
src/openvpn/options_show.c
src/openvpn/options_show.h
+ src/openvpn/options_string.c
+ src/openvpn/options_string.h
src/openvpn/otime.c
src/openvpn/otime.h
src/openvpn/ovpn_dco_win.h
diff --git a/src/openvpn/Makefile.am b/src/openvpn/Makefile.am
index 0fbd006..9778f32 100644
--- a/src/openvpn/Makefile.am
+++ b/src/openvpn/Makefile.am
@@ -109,6 +109,7 @@
openvpn.c openvpn.h \
options.c options.h \
options_show.c options_show.h \
+ options_string.c options_string.h \
options_util.c options_util.h \
options_parse.c \
otime.c otime.h \
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index 41fa557..7c4fce8 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -53,6 +53,7 @@
#include "mudp.h"
#include "dco.h"
#include "tun_afunix.h"
+#include "options_string.h"
#include "memdbg.h"
diff --git a/src/openvpn/occ.c b/src/openvpn/occ.c
index aa262a8..f25ae19 100644
--- a/src/openvpn/occ.c
+++ b/src/openvpn/occ.c
@@ -29,7 +29,7 @@
#include "occ.h"
#include "forward.h"
#include "memdbg.h"
-
+#include "options_string.h"
/*
* This random string identifies an OpenVPN
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index 8f5bc2a..6e314a1 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -3705,436 +3705,6 @@
}
/*
- * Build an options string to represent data channel encryption options.
- * This string must match exactly between peers. The keysize is checked
- * separately by read_key().
- *
- * The following options must match on both peers:
- *
- * Tunnel options:
- *
- * --dev tun|tap [unit number need not match]
- * --dev-type tun|tap
- * --link-mtu
- * --udp-mtu
- * --tun-mtu
- * --proto udp
- * --proto tcp-client [matched with --proto tcp-server
- * on the other end of the connection]
- * --proto tcp-server [matched with --proto tcp-client on
- * the other end of the connection]
- * --tun-ipv6
- * --ifconfig x y [matched with --ifconfig y x on
- * the other end of the connection]
- *
- * --comp-lzo
- * --compress alg
- * --fragment
- *
- * Crypto Options:
- *
- * --cipher
- * --auth
- * --secret
- *
- * SSL Options:
- *
- * --tls-auth
- * --tls-client [matched with --tls-server on
- * the other end of the connection]
- * --tls-server [matched with --tls-client on
- * the other end of the connection]
- */
-char *
-options_string(const struct options *o, const struct frame *frame, struct
tuntap *tt,
- openvpn_net_ctx_t *ctx, bool remote, struct gc_arena *gc)
-{
- struct buffer out = alloc_buf(OPTION_LINE_SIZE);
- bool tt_local = false;
-
- buf_printf(&out, "V4");
-
- /*
- * Tunnel Options
- */
-
- buf_printf(&out, ",dev-type %s", dev_type_string(o->dev, o->dev_type));
- /* the link-mtu that we send has only a meaning if have a fixed
- * cipher (p2p) or have a fallback cipher configured for older non
- * ncp clients. But not sending it will make even 2.4 complain
- * about it being missing. So still send it. */
- buf_printf(&out, ",link-mtu %u", (unsigned
int)calc_options_string_link_mtu(o, frame));
-
- if (o->ce.occ_mtu != 0)
- {
- buf_printf(&out, ",tun-mtu %d", o->ce.occ_mtu);
- }
- else
- {
- buf_printf(&out, ",tun-mtu %d", frame->tun_mtu);
- }
-
- buf_printf(&out, ",proto %s", proto_remote(o->ce.proto, remote));
-
- bool p2p_nopull = o->mode == MODE_POINT_TO_POINT && !PULL_DEFINED(o);
- /* send tun_ipv6 only in peer2peer mode - in client/server mode, it
- * is usually pushed by the server, triggering a non-helpful warning
- */
- if (o->ifconfig_ipv6_local && p2p_nopull)
- {
- buf_printf(&out, ",tun-ipv6");
- }
-
- /*
- * Try to get ifconfig parameters into the options string.
- * If tt is undefined, make a temporary instantiation.
- */
- if (!tt)
- {
- tt = init_tun(o->dev, o->dev_type, o->topology, o->ifconfig_local,
- o->ifconfig_remote_netmask, o->ifconfig_ipv6_local,
o->ifconfig_ipv6_netbits,
- o->ifconfig_ipv6_remote, NULL, NULL, false, NULL, ctx,
NULL);
- if (tt)
- {
- tt_local = true;
- }
- }
-
- if (tt && p2p_nopull)
- {
- const char *ios = ifconfig_options_string(tt, remote,
o->ifconfig_nowarn, gc);
- if (ios && strlen(ios))
- {
- buf_printf(&out, ",ifconfig %s", ios);
- }
- }
- if (tt_local)
- {
- free(tt);
- tt = NULL;
- }
-
-#ifdef USE_COMP
- if (o->comp.alg != COMP_ALG_UNDEF)
- {
- buf_printf(&out, ",comp-lzo"); /* for compatibility, this simply
indicates that compression
- context is active, not necessarily
LZO per-se */
- }
-#endif
-
-#ifdef ENABLE_FRAGMENT
- if (o->ce.fragment)
- {
- buf_printf(&out, ",mtu-dynamic");
- }
-#endif
-
-#define TLS_CLIENT (o->tls_client)
-#define TLS_SERVER (o->tls_server)
-
- /*
- * Key direction
- */
- {
- const char *kd = keydirection2ascii(o->key_direction, remote, false);
- if (kd)
- {
- buf_printf(&out, ",keydir %s", kd);
- }
- }
-
- /*
- * Crypto Options
- */
- if (o->shared_secret_file || TLS_CLIENT || TLS_SERVER)
- {
- struct key_type kt;
-
- ASSERT((o->shared_secret_file != NULL) + (TLS_CLIENT == true) +
(TLS_SERVER == true) <= 1);
-
- /* Skip resolving BF-CBC to allow SSL libraries without BF-CBC
- * to work here in the default configuration */
- const char *ciphername = o->ciphername;
- size_t keysize = 0;
-
- if (strcmp(o->ciphername, "BF-CBC") == 0)
- {
- init_key_type(&kt, "none", o->authname, true, false);
- keysize = 128;
- }
- else
- {
- init_key_type(&kt, o->ciphername, o->authname, true, false);
- ciphername = cipher_kt_name(kt.cipher);
- if (cipher_defined(o->ciphername))
- {
- keysize = cipher_kt_key_size(kt.cipher) * 8;
- }
- }
- /* Only announce the cipher to our peer if we are willing to
- * support it */
- if (p2p_nopull || tls_item_in_cipher_list(ciphername, o->ncp_ciphers))
- {
- buf_printf(&out, ",cipher %s", ciphername);
- }
- buf_printf(&out, ",auth %s", md_kt_name(kt.digest));
- buf_printf(&out, ",keysize %zu", keysize);
- if (o->shared_secret_file)
- {
- buf_printf(&out, ",secret");
- }
- }
-
- /*
- * SSL Options
- */
- {
- if (TLS_CLIENT || TLS_SERVER)
- {
- if (o->ce.tls_auth_file)
- {
- buf_printf(&out, ",tls-auth");
- }
- /* Not adding tls-crypt here, because we won't reach this code if
- * tls-auth/tls-crypt does not match. Removing tls-auth here would
- * break stuff, so leaving that in place. */
-
- buf_printf(&out, ",key-method %d", KEY_METHOD_2);
- }
-
- if (remote)
- {
- if (TLS_CLIENT)
- {
- buf_printf(&out, ",tls-server");
- }
- else if (TLS_SERVER)
- {
- buf_printf(&out, ",tls-client");
- }
- }
- else
- {
- if (TLS_CLIENT)
- {
- buf_printf(&out, ",tls-client");
- }
- else if (TLS_SERVER)
- {
- buf_printf(&out, ",tls-server");
- }
- }
- }
-
-#undef TLS_CLIENT
-#undef TLS_SERVER
-
- return BSTR(&out);
-}
-
-/*
- * Compare option strings for equality.
- * If the first two chars of the strings differ, it means that
- * we are looking at different versions of the options string,
- * therefore don't compare them and return true.
- */
-
-bool
-options_cmp_equal(char *actual, const char *expected)
-{
- return options_cmp_equal_safe(actual, expected, strlen(actual) + 1);
-}
-
-void
-options_warning(char *actual, const char *expected)
-{
- options_warning_safe(actual, expected, strlen(actual) + 1);
-}
-
-static const char *
-options_warning_extract_parm1(const char *option_string, struct gc_arena
*gc_ret)
-{
- struct gc_arena gc = gc_new();
- struct buffer b = string_alloc_buf(option_string, &gc);
- char *p = gc_malloc(OPTION_PARM_SIZE, false, &gc);
- const char *ret;
-
- buf_parse(&b, ' ', p, OPTION_PARM_SIZE);
- ret = string_alloc(p, gc_ret);
- gc_free(&gc);
- return ret;
-}
-
-static void
-options_warning_safe_scan2(const msglvl_t msglevel, const int delim,
- const bool report_inconsistent, const char *p1,
- const struct buffer *b2_src, const char *b1_name,
- const char *b2_name)
-{
- /* We will stop sending 'key-method', 'keydir', 'proto' and 'tls-auth' in
- * OCC in a future version (because it's not useful). To reduce questions
- * when interoperating, we no longer printing a warning about it.
- */
- if (strprefix(p1, "key-method ") || strprefix(p1, "keydir ") ||
strprefix(p1, "proto ")
- || streq(p1, "tls-auth") || strprefix(p1, "tun-ipv6") || strprefix(p1,
"cipher "))
- {
- return;
- }
-
- if (strlen(p1) > 0)
- {
- struct gc_arena gc = gc_new();
- struct buffer b2 = *b2_src;
- const char *p1_prefix = options_warning_extract_parm1(p1, &gc);
- char *p2 = gc_malloc(OPTION_PARM_SIZE, false, &gc);
-
- while (buf_parse(&b2, delim, p2, OPTION_PARM_SIZE))
- {
- if (strlen(p2))
- {
- const char *p2_prefix = options_warning_extract_parm1(p2, &gc);
-
- if (!strcmp(p1, p2))
- {
- goto done;
- }
- if (!strcmp(p1_prefix, p2_prefix))
- {
- if (report_inconsistent)
- {
- msg(msglevel, "WARNING: '%s' is used inconsistently,
%s='%s', %s='%s'",
- safe_print(p1_prefix, &gc), b1_name,
safe_print(p1, &gc), b2_name,
- safe_print(p2, &gc));
- }
- goto done;
- }
- }
- }
-
- msg(msglevel, "WARNING: '%s' is present in %s config but missing in %s
config, %s='%s'",
- safe_print(p1_prefix, &gc), b1_name, b2_name, b1_name,
safe_print(p1, &gc));
-
-done:
- gc_free(&gc);
- }
-}
-
-static void
-options_warning_safe_scan1(const msglvl_t msglevel, const int delim,
- const bool report_inconsistent, const struct buffer
*b1_src,
- const struct buffer *b2_src, const char *b1_name,
const char *b2_name)
-{
- struct gc_arena gc = gc_new();
- struct buffer b = *b1_src;
- char *p = gc_malloc(OPTION_PARM_SIZE, true, &gc);
-
- while (buf_parse(&b, delim, p, OPTION_PARM_SIZE))
- {
- options_warning_safe_scan2(msglevel, delim, report_inconsistent, p,
b2_src, b1_name,
- b2_name);
- }
-
- gc_free(&gc);
-}
-
-static void
-options_warning_safe_ml(const msglvl_t msglevel, char *actual, const char
*expected, size_t actual_n)
-{
- struct gc_arena gc = gc_new();
-
- if (actual_n > 0)
- {
- struct buffer local = alloc_buf_gc(OPTION_PARM_SIZE + 16, &gc);
- struct buffer remote = alloc_buf_gc(OPTION_PARM_SIZE + 16, &gc);
- actual[actual_n - 1] = 0;
-
- buf_printf(&local, "version %s", expected);
- buf_printf(&remote, "version %s", actual);
-
- options_warning_safe_scan1(msglevel, ',', true, &local, &remote,
"local", "remote");
-
- options_warning_safe_scan1(msglevel, ',', false, &remote, &local,
"remote", "local");
- }
-
- gc_free(&gc);
-}
-
-bool
-options_cmp_equal_safe(char *actual, const char *expected, size_t actual_n)
-{
- struct gc_arena gc = gc_new();
- bool ret = true;
-
- if (actual_n > 0)
- {
- actual[actual_n - 1] = 0;
- if (strncmp(actual, expected, 2))
- {
- msg(D_SHOW_OCC, "NOTE: Options consistency check may be skewed by
version differences");
- options_warning_safe_ml(D_SHOW_OCC, actual, expected, actual_n);
- }
- else
- {
- ret = !strcmp(actual, expected);
- }
- }
- gc_free(&gc);
- return ret;
-}
-
-void
-options_warning_safe(char *actual, const char *expected, size_t actual_n)
-{
- options_warning_safe_ml(D_SHOW_OCC, actual, expected, actual_n);
-}
-
-const char *
-options_string_version(const char *s, struct gc_arena *gc)
-{
- struct buffer out = alloc_buf_gc(4, gc);
- strncpynt((char *)BPTR(&out), s, 3);
- return BSTR(&out);
-}
-
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wsign-compare"
-#endif
-
-char *
-options_string_extract_option(const char *options_string, const char
*opt_name, struct gc_arena *gc)
-{
- char *ret = NULL;
- const size_t opt_name_len = strlen(opt_name);
-
- const char *p = options_string;
- while (p)
- {
- if (0 == strncmp(p, opt_name, opt_name_len) && strlen(p) >
(opt_name_len + 1)
- && p[opt_name_len] == ' ')
- {
- /* option found, extract value */
- const char *start = &p[opt_name_len + 1];
- const char *end = strchr(p, ',');
- size_t val_len = end ? end - start : strlen(start);
- ret = gc_malloc(val_len + 1, true, gc);
- memcpy(ret, start, val_len);
- break;
- }
- p = strchr(p, ',');
- if (p)
- {
- p++; /* skip delimiter */
- }
- }
- return ret;
-}
-
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-
-/*
* parse/print topology coding
*/
diff --git a/src/openvpn/options.h b/src/openvpn/options.h
index 85d16f9..d950a67 100644
--- a/src/openvpn/options.h
+++ b/src/openvpn/options.h
@@ -898,33 +898,6 @@
bool string_defined_equal(const char *s1, const char *s2);
-const char *options_string_version(const char *s, struct gc_arena *gc);
-
-char *options_string(const struct options *o, const struct frame *frame,
struct tuntap *tt,
- openvpn_net_ctx_t *ctx, bool remote, struct gc_arena *gc);
-
-bool options_cmp_equal_safe(char *actual, const char *expected, size_t
actual_n);
-
-void options_warning_safe(char *actual, const char *expected, size_t actual_n);
-
-bool options_cmp_equal(char *actual, const char *expected);
-
-void options_warning(char *actual, const char *expected);
-
-/**
- * Given an OpenVPN options string, extract the value of an option.
- *
- * @param options_string Zero-terminated, comma-separated options string
- * @param opt_name The name of the option to extract
- * @param gc The gc to allocate the return value
- *
- * @return gc-allocated value of option with name opt_name if option was found,
- * or NULL otherwise.
- */
-char *options_string_extract_option(const char *options_string, const char
*opt_name,
- struct gc_arena *gc);
-
-
void options_postprocess(struct options *options, struct env_set *es);
bool options_postprocess_pull(struct options *o, struct env_set *es);
diff --git a/src/openvpn/options_string.c b/src/openvpn/options_string.c
new file mode 100644
index 0000000..9c0313f
--- /dev/null
+++ b/src/openvpn/options_string.c
@@ -0,0 +1,470 @@
+/*
+ * 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) 2002-2026 OpenVPN Inc <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "syshead.h"
+
+#include "buffer.h"
+#include "crypto.h"
+#include "error.h"
+#include "mtu.h"
+#include "options.h"
+#include "socket_util.h"
+#include "ssl.h"
+#include "ssl_ncp.h"
+#include "tun.h"
+
+#include <string.h>
+
+/*
+ * Build an options string to represent data channel encryption options.
+ * This string must match exactly between peers. The keysize is checked
+ * separately by read_key().
+ *
+ * The following options must match on both peers:
+ *
+ * Tunnel options:
+ *
+ * --dev tun|tap [unit number need not match]
+ * --dev-type tun|tap
+ * --link-mtu
+ * --udp-mtu
+ * --tun-mtu
+ * --proto udp
+ * --proto tcp-client [matched with --proto tcp-server
+ * on the other end of the connection]
+ * --proto tcp-server [matched with --proto tcp-client on
+ * the other end of the connection]
+ * --tun-ipv6
+ * --ifconfig x y [matched with --ifconfig y x on
+ * the other end of the connection]
+ *
+ * --comp-lzo
+ * --compress alg
+ * --fragment
+ *
+ * Crypto Options:
+ *
+ * --cipher
+ * --auth
+ * --secret
+ *
+ * SSL Options:
+ *
+ * --tls-auth
+ * --tls-client [matched with --tls-server on
+ * the other end of the connection]
+ * --tls-server [matched with --tls-client on
+ * the other end of the connection]
+ */
+char *
+options_string(const struct options *o, const struct frame *frame, struct
tuntap *tt,
+ openvpn_net_ctx_t *ctx, bool remote, struct gc_arena *gc)
+{
+ struct buffer out = alloc_buf(OPTION_LINE_SIZE);
+ bool tt_local = false;
+
+ buf_printf(&out, "V4");
+
+ /*
+ * Tunnel Options
+ */
+
+ buf_printf(&out, ",dev-type %s", dev_type_string(o->dev, o->dev_type));
+ /* the link-mtu that we send has only a meaning if have a fixed
+ * cipher (p2p) or have a fallback cipher configured for older non
+ * ncp clients. But not sending it will make even 2.4 complain
+ * about it being missing. So still send it. */
+ buf_printf(&out, ",link-mtu %u", (unsigned
int)calc_options_string_link_mtu(o, frame));
+
+ if (o->ce.occ_mtu != 0)
+ {
+ buf_printf(&out, ",tun-mtu %d", o->ce.occ_mtu);
+ }
+ else
+ {
+ buf_printf(&out, ",tun-mtu %d", frame->tun_mtu);
+ }
+
+ buf_printf(&out, ",proto %s", proto_remote(o->ce.proto, remote));
+
+ bool p2p_nopull = o->mode == MODE_POINT_TO_POINT && !PULL_DEFINED(o);
+ /* send tun_ipv6 only in peer2peer mode - in client/server mode, it
+ * is usually pushed by the server, triggering a non-helpful warning
+ */
+ if (o->ifconfig_ipv6_local && p2p_nopull)
+ {
+ buf_printf(&out, ",tun-ipv6");
+ }
+
+ /*
+ * Try to get ifconfig parameters into the options string.
+ * If tt is undefined, make a temporary instantiation.
+ */
+ if (!tt)
+ {
+ tt = init_tun(o->dev, o->dev_type, o->topology, o->ifconfig_local,
+ o->ifconfig_remote_netmask, o->ifconfig_ipv6_local,
o->ifconfig_ipv6_netbits,
+ o->ifconfig_ipv6_remote, NULL, NULL, false, NULL, ctx,
NULL);
+ if (tt)
+ {
+ tt_local = true;
+ }
+ }
+
+ if (tt && p2p_nopull)
+ {
+ const char *ios = ifconfig_options_string(tt, remote,
o->ifconfig_nowarn, gc);
+ if (ios && strlen(ios))
+ {
+ buf_printf(&out, ",ifconfig %s", ios);
+ }
+ }
+ if (tt_local)
+ {
+ free(tt);
+ tt = NULL;
+ }
+
+#ifdef USE_COMP
+ if (o->comp.alg != COMP_ALG_UNDEF)
+ {
+ /* for compatibility, this simply indicates that compression
+ * context is active, not necessarily LZO per-se */
+ buf_printf(&out, ",comp-lzo");
+ }
+#endif
+
+#ifdef ENABLE_FRAGMENT
+ if (o->ce.fragment)
+ {
+ buf_printf(&out, ",mtu-dynamic");
+ }
+#endif
+
+#define TLS_CLIENT (o->tls_client)
+#define TLS_SERVER (o->tls_server)
+
+ /*
+ * Key direction
+ */
+ {
+ const char *kd = keydirection2ascii(o->key_direction, remote, false);
+ if (kd)
+ {
+ buf_printf(&out, ",keydir %s", kd);
+ }
+ }
+
+ /*
+ * Crypto Options
+ */
+ if (o->shared_secret_file || TLS_CLIENT || TLS_SERVER)
+ {
+ struct key_type kt;
+
+ ASSERT((o->shared_secret_file != NULL) + (TLS_CLIENT == true) +
(TLS_SERVER == true) <= 1);
+
+ /* Skip resolving BF-CBC to allow SSL libraries without BF-CBC
+ * to work here in the default configuration */
+ const char *ciphername = o->ciphername;
+ size_t keysize = 0;
+
+ if (strcmp(o->ciphername, "BF-CBC") == 0)
+ {
+ init_key_type(&kt, "none", o->authname, true, false);
+ keysize = 128;
+ }
+ else
+ {
+ init_key_type(&kt, o->ciphername, o->authname, true, false);
+ ciphername = cipher_kt_name(kt.cipher);
+ if (cipher_defined(o->ciphername))
+ {
+ keysize = cipher_kt_key_size(kt.cipher) * 8;
+ }
+ }
+ /* Only announce the cipher to our peer if we are willing to
+ * support it */
+ if (p2p_nopull || tls_item_in_cipher_list(ciphername, o->ncp_ciphers))
+ {
+ buf_printf(&out, ",cipher %s", ciphername);
+ }
+ buf_printf(&out, ",auth %s", md_kt_name(kt.digest));
+ buf_printf(&out, ",keysize %zu", keysize);
+ if (o->shared_secret_file)
+ {
+ buf_printf(&out, ",secret");
+ }
+ }
+
+ /*
+ * SSL Options
+ */
+ {
+ if (TLS_CLIENT || TLS_SERVER)
+ {
+ if (o->ce.tls_auth_file)
+ {
+ buf_printf(&out, ",tls-auth");
+ }
+ /* Not adding tls-crypt here, because we won't reach this code if
+ * tls-auth/tls-crypt does not match. Removing tls-auth here would
+ * break stuff, so leaving that in place. */
+
+ buf_printf(&out, ",key-method %d", KEY_METHOD_2);
+ }
+
+ if (remote)
+ {
+ if (TLS_CLIENT)
+ {
+ buf_printf(&out, ",tls-server");
+ }
+ else if (TLS_SERVER)
+ {
+ buf_printf(&out, ",tls-client");
+ }
+ }
+ else
+ {
+ if (TLS_CLIENT)
+ {
+ buf_printf(&out, ",tls-client");
+ }
+ else if (TLS_SERVER)
+ {
+ buf_printf(&out, ",tls-server");
+ }
+ }
+ }
+
+#undef TLS_CLIENT
+#undef TLS_SERVER
+
+ return BSTR(&out);
+}
+
+/*
+ * Compare option strings for equality.
+ * If the first two chars of the strings differ, it means that
+ * we are looking at different versions of the options string,
+ * therefore don't compare them and return true.
+ */
+
+static const char *
+options_warning_extract_parm1(const char *option_string, struct gc_arena
*gc_ret)
+{
+ struct gc_arena gc = gc_new();
+ struct buffer b = string_alloc_buf(option_string, &gc);
+ char *p = gc_malloc(OPTION_PARM_SIZE, false, &gc);
+ const char *ret;
+
+ buf_parse(&b, ' ', p, OPTION_PARM_SIZE);
+ ret = string_alloc(p, gc_ret);
+ gc_free(&gc);
+ return ret;
+}
+
+static void
+options_warning_safe_scan2(const msglvl_t msglevel, const int delim,
+ const bool report_inconsistent, const char *p1,
+ const struct buffer *b2_src, const char *b1_name,
+ const char *b2_name)
+{
+ /* We will stop sending 'key-method', 'keydir', 'proto' and 'tls-auth' in
+ * OCC in a future version (because it's not useful). To reduce questions
+ * when interoperating, we no longer printing a warning about it.
+ */
+ if (strprefix(p1, "key-method ") || strprefix(p1, "keydir ") ||
strprefix(p1, "proto ")
+ || streq(p1, "tls-auth") || strprefix(p1, "tun-ipv6") || strprefix(p1,
"cipher "))
+ {
+ return;
+ }
+
+ if (strlen(p1) > 0)
+ {
+ struct gc_arena gc = gc_new();
+ struct buffer b2 = *b2_src;
+ const char *p1_prefix = options_warning_extract_parm1(p1, &gc);
+ char *p2 = gc_malloc(OPTION_PARM_SIZE, false, &gc);
+
+ while (buf_parse(&b2, delim, p2, OPTION_PARM_SIZE))
+ {
+ if (strlen(p2))
+ {
+ const char *p2_prefix = options_warning_extract_parm1(p2, &gc);
+
+ if (!strcmp(p1, p2))
+ {
+ goto done;
+ }
+ if (!strcmp(p1_prefix, p2_prefix))
+ {
+ if (report_inconsistent)
+ {
+ msg(msglevel, "WARNING: '%s' is used inconsistently,
%s='%s', %s='%s'",
+ safe_print(p1_prefix, &gc), b1_name,
safe_print(p1, &gc), b2_name,
+ safe_print(p2, &gc));
+ }
+ goto done;
+ }
+ }
+ }
+
+ msg(msglevel, "WARNING: '%s' is present in %s config but missing in %s
config, %s='%s'",
+ safe_print(p1_prefix, &gc), b1_name, b2_name, b1_name,
safe_print(p1, &gc));
+
+done:
+ gc_free(&gc);
+ }
+}
+
+static void
+options_warning_safe_scan1(const msglvl_t msglevel, const int delim,
+ const bool report_inconsistent, const struct buffer
*b1_src,
+ const struct buffer *b2_src, const char *b1_name,
const char *b2_name)
+{
+ struct gc_arena gc = gc_new();
+ struct buffer b = *b1_src;
+ char *p = gc_malloc(OPTION_PARM_SIZE, true, &gc);
+
+ while (buf_parse(&b, delim, p, OPTION_PARM_SIZE))
+ {
+ options_warning_safe_scan2(msglevel, delim, report_inconsistent, p,
b2_src, b1_name,
+ b2_name);
+ }
+
+ gc_free(&gc);
+}
+
+static void
+options_warning_safe_ml(const msglvl_t msglevel, char *actual, const char
*expected, size_t actual_n)
+{
+ struct gc_arena gc = gc_new();
+
+ if (actual_n > 0)
+ {
+ struct buffer local = alloc_buf_gc(OPTION_PARM_SIZE + 16, &gc);
+ struct buffer remote = alloc_buf_gc(OPTION_PARM_SIZE + 16, &gc);
+ actual[actual_n - 1] = 0;
+
+ buf_printf(&local, "version %s", expected);
+ buf_printf(&remote, "version %s", actual);
+
+ options_warning_safe_scan1(msglevel, ',', true, &local, &remote,
"local", "remote");
+
+ options_warning_safe_scan1(msglevel, ',', false, &remote, &local,
"remote", "local");
+ }
+
+ gc_free(&gc);
+}
+
+bool
+options_cmp_equal_safe(char *actual, const char *expected, size_t actual_n)
+{
+ struct gc_arena gc = gc_new();
+ bool ret = true;
+
+ if (actual_n > 0)
+ {
+ actual[actual_n - 1] = 0;
+ if (strncmp(actual, expected, 2))
+ {
+ msg(D_SHOW_OCC, "NOTE: Options consistency check may be skewed by
version differences");
+ options_warning_safe_ml(D_SHOW_OCC, actual, expected, actual_n);
+ }
+ else
+ {
+ ret = !strcmp(actual, expected);
+ }
+ }
+ gc_free(&gc);
+ return ret;
+}
+
+bool
+options_cmp_equal(char *actual, const char *expected)
+{
+ return options_cmp_equal_safe(actual, expected, strlen(actual) + 1);
+}
+
+void
+options_warning_safe(char *actual, const char *expected, size_t actual_n)
+{
+ options_warning_safe_ml(D_SHOW_OCC, actual, expected, actual_n);
+}
+
+void
+options_warning(char *actual, const char *expected)
+{
+ options_warning_safe(actual, expected, strlen(actual) + 1);
+}
+
+const char *
+options_string_version(const char *s, struct gc_arena *gc)
+{
+ struct buffer out = alloc_buf_gc(4, gc);
+ strncpynt((char *)BPTR(&out), s, 3);
+ return BSTR(&out);
+}
+
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wsign-compare"
+#endif
+
+char *
+options_string_extract_option(const char *options_string, const char
*opt_name, struct gc_arena *gc)
+{
+ char *ret = NULL;
+ const size_t opt_name_len = strlen(opt_name);
+
+ const char *p = options_string;
+ while (p)
+ {
+ if (0 == strncmp(p, opt_name, opt_name_len) && strlen(p) >
(opt_name_len + 1)
+ && p[opt_name_len] == ' ')
+ {
+ /* option found, extract value */
+ const char *start = &p[opt_name_len + 1];
+ const char *end = strchr(p, ',');
+ size_t val_len = end ? end - start : strlen(start);
+ ret = gc_malloc(val_len + 1, true, gc);
+ memcpy(ret, start, val_len);
+ break;
+ }
+ p = strchr(p, ',');
+ if (p)
+ {
+ p++; /* skip delimiter */
+ }
+ }
+ return ret;
+}
+
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
diff --git a/src/openvpn/options_string.h b/src/openvpn/options_string.h
new file mode 100644
index 0000000..49a1cac
--- /dev/null
+++ b/src/openvpn/options_string.h
@@ -0,0 +1,56 @@
+/*
+ * 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) 2002-2026 OpenVPN Inc <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef OPTIONS_SHOW_H
+#define OPTIONS_SHOW_H
+
+#include "buffer.h"
+#include "options.h"
+
+const char *options_string_version(const char *s, struct gc_arena *gc);
+
+char *options_string(const struct options *o, const struct frame *frame,
struct tuntap *tt,
+ openvpn_net_ctx_t *ctx, bool remote, struct gc_arena *gc);
+
+bool options_cmp_equal_safe(char *actual, const char *expected, size_t
actual_n);
+
+void options_warning_safe(char *actual, const char *expected, size_t actual_n);
+
+bool options_cmp_equal(char *actual, const char *expected);
+
+void options_warning(char *actual, const char *expected);
+
+/**
+ * Given an OpenVPN options string, extract the value of an option.
+ *
+ * @param options_string Zero-terminated, comma-separated options string
+ * @param opt_name The name of the option to extract
+ * @param gc The gc to allocate the return value
+ *
+ * @return gc-allocated value of option with name opt_name if option was found,
+ * or NULL otherwise.
+ */
+char *options_string_extract_option(const char *options_string, const char
*opt_name,
+ struct gc_arena *gc);
+
+
+#endif
diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c
index 9cb38c6..fa4ae7f 100644
--- a/src/openvpn/ssl.c
+++ b/src/openvpn/ssl.c
@@ -62,6 +62,7 @@
#include "auth_token.h"
#include "mss.h"
#include "dco.h"
+#include "options_string.h"
#include "memdbg.h"
#include "openvpn.h"
--
To view, visit http://gerrit.openvpn.net/c/openvpn/+/1773?usp=email
To unsubscribe, or for help writing mail filters, visit
http://gerrit.openvpn.net/settings?usp=email
Gerrit-MessageType: newchange
Gerrit-Project: openvpn
Gerrit-Branch: master
Gerrit-Change-Id: I6824058fc9c4efd48ceeb32d4164f97b2a243d85
Gerrit-Change-Number: 1773
Gerrit-PatchSet: 1
Gerrit-Owner: flichtenheld <[email protected]>
Gerrit-Reviewer: plaisthos <[email protected]>
Gerrit-CC: openvpn-devel <[email protected]>
Gerrit-Attention: plaisthos <[email protected]>
_______________________________________________
Openvpn-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/openvpn-devel