OpenVPN
options_string.c
Go to the documentation of this file.
1/*
2 * OpenVPN -- An application to securely tunnel IP networks
3 * over a single UDP port, with support for SSL/TLS-based
4 * session authentication and key exchange,
5 * packet encryption, packet authentication, and
6 * packet compression.
7 *
8 * Copyright (C) 2002-2026 OpenVPN Inc <sales@openvpn.net>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2
12 * as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, see <https://www.gnu.org/licenses/>.
21 */
22
23#ifdef HAVE_CONFIG_H
24#include "config.h"
25#endif
26
27#include "syshead.h"
28
29#include "buffer.h"
30#include "crypto.h"
31#include "error.h"
32#include "mtu.h"
33#include "options.h"
34#include "socket_util.h"
35#include "ssl.h"
36#include "ssl_ncp.h"
37#include "tun.h"
38
39#include <string.h>
40
41char *
42options_string(const struct options *o, const struct frame *frame, struct tuntap *tt,
43 openvpn_net_ctx_t *ctx, bool remote, struct gc_arena *gc)
44{
45 struct buffer out = alloc_buf(OPTION_LINE_SIZE);
46 bool tt_local = false;
47
48 buf_printf(&out, "V4");
49
50 /*
51 * Tunnel Options
52 */
53
54 buf_printf(&out, ",dev-type %s", dev_type_string(o->dev, o->dev_type));
55 /* the link-mtu that we send has only a meaning if have a fixed
56 * cipher (p2p) or have a fallback cipher configured for older non
57 * ncp clients. But not sending it will make even 2.4 complain
58 * about it being missing. So still send it. */
59 buf_printf(&out, ",link-mtu %u", (unsigned int)calc_options_string_link_mtu(o, frame));
60
61 if (o->ce.occ_mtu != 0)
62 {
63 buf_printf(&out, ",tun-mtu %d", o->ce.occ_mtu);
64 }
65 else
66 {
67 buf_printf(&out, ",tun-mtu %d", frame->tun_mtu);
68 }
69
70 buf_printf(&out, ",proto %s", proto_remote(o->ce.proto, remote));
71
72 bool p2p_nopull = o->mode == MODE_POINT_TO_POINT && !PULL_DEFINED(o);
73 /* send tun_ipv6 only in peer2peer mode - in client/server mode, it
74 * is usually pushed by the server, triggering a non-helpful warning
75 */
76 if (o->ifconfig_ipv6_local && p2p_nopull)
77 {
78 buf_printf(&out, ",tun-ipv6");
79 }
80
81 /*
82 * Try to get ifconfig parameters into the options string.
83 * If tt is undefined, make a temporary instantiation.
84 */
85 if (!tt)
86 {
87 tt = init_tun(o->dev, o->dev_type, o->topology, o->ifconfig_local,
88 o->ifconfig_remote_netmask, o->ifconfig_ipv6_local, o->ifconfig_ipv6_netbits,
89 o->ifconfig_ipv6_remote, NULL, NULL, false, NULL, ctx, NULL);
90 if (tt)
91 {
92 tt_local = true;
93 }
94 }
95
96 if (tt && p2p_nopull)
97 {
98 const char *ios = ifconfig_options_string(tt, remote, o->ifconfig_nowarn, gc);
99 if (ios && strlen(ios))
100 {
101 buf_printf(&out, ",ifconfig %s", ios);
102 }
103 }
104 if (tt_local)
105 {
106 free(tt);
107 tt = NULL;
108 }
109
110#ifdef USE_COMP
111 if (o->comp.alg != COMP_ALG_UNDEF)
112 {
113 /* for compatibility, this simply indicates that compression
114 * context is active, not necessarily LZO per-se */
115 buf_printf(&out, ",comp-lzo");
116 }
117#endif
118
119#ifdef ENABLE_FRAGMENT
120 if (o->ce.fragment)
121 {
122 buf_printf(&out, ",mtu-dynamic");
123 }
124#endif
125
126#define TLS_CLIENT (o->tls_client)
127#define TLS_SERVER (o->tls_server)
128
129 /*
130 * Key direction
131 */
132 {
133 const char *kd = keydirection2ascii(o->key_direction, remote, false);
134 if (kd)
135 {
136 buf_printf(&out, ",keydir %s", kd);
137 }
138 }
139
140 /*
141 * Crypto Options
142 */
143 if (o->shared_secret_file || TLS_CLIENT || TLS_SERVER)
144 {
145 struct key_type kt;
146
147 ASSERT((o->shared_secret_file != NULL) + (TLS_CLIENT == true) + (TLS_SERVER == true) <= 1);
148
149 /* Skip resolving BF-CBC to allow SSL libraries without BF-CBC
150 * to work here in the default configuration */
151 const char *ciphername = o->ciphername;
152 size_t keysize = 0;
153
154 if (strcmp(o->ciphername, "BF-CBC") == 0)
155 {
156 init_key_type(&kt, "none", o->authname, true, false);
157 keysize = 128;
158 }
159 else
160 {
161 init_key_type(&kt, o->ciphername, o->authname, true, false);
162 ciphername = cipher_kt_name(kt.cipher);
164 {
165 keysize = cipher_kt_key_size(kt.cipher) * 8;
166 }
167 }
168 /* Only announce the cipher to our peer if we are willing to
169 * support it */
170 if (p2p_nopull || tls_item_in_cipher_list(ciphername, o->ncp_ciphers))
171 {
172 buf_printf(&out, ",cipher %s", ciphername);
173 }
174 buf_printf(&out, ",auth %s", md_kt_name(kt.digest));
175 buf_printf(&out, ",keysize %zu", keysize);
176 if (o->shared_secret_file)
177 {
178 buf_printf(&out, ",secret");
179 }
180 }
181
182 /*
183 * SSL Options
184 */
185 {
186 if (TLS_CLIENT || TLS_SERVER)
187 {
188 if (o->ce.tls_auth_file)
189 {
190 buf_printf(&out, ",tls-auth");
191 }
192 /* Not adding tls-crypt here, because we won't reach this code if
193 * tls-auth/tls-crypt does not match. Removing tls-auth here would
194 * break stuff, so leaving that in place. */
195
196 buf_printf(&out, ",key-method %d", KEY_METHOD_2);
197 }
198
199 if (remote)
200 {
201 if (TLS_CLIENT)
202 {
203 buf_printf(&out, ",tls-server");
204 }
205 else if (TLS_SERVER)
206 {
207 buf_printf(&out, ",tls-client");
208 }
209 }
210 else
211 {
212 if (TLS_CLIENT)
213 {
214 buf_printf(&out, ",tls-client");
215 }
216 else if (TLS_SERVER)
217 {
218 buf_printf(&out, ",tls-server");
219 }
220 }
221 }
222
223#undef TLS_CLIENT
224#undef TLS_SERVER
225
226 return BSTR(&out);
227}
228
236static const char *
237options_warning_extract_parm1(const char *option_string, struct gc_arena *gc_ret)
238{
239 struct gc_arena gc = gc_new();
241 char *p = gc_malloc(OPTION_PARM_SIZE, false, &gc);
242 const char *ret;
243
244 buf_parse(&b, ' ', p, OPTION_PARM_SIZE);
246 gc_free(&gc);
247 return ret;
248}
249
250static void
251options_warning_safe_scan2(const msglvl_t msglevel, const int delim,
252 const bool report_inconsistent, const char *p1,
253 const struct buffer *b2_src, const char *b1_name,
254 const char *b2_name)
255{
256 /* We will stop sending 'key-method', 'keydir', 'proto' and 'tls-auth' in
257 * OCC in a future version (because it's not useful). To reduce questions
258 * when interoperating, we no longer printing a warning about it.
259 */
260 if (strprefix(p1, "key-method ") || strprefix(p1, "keydir ") || strprefix(p1, "proto ")
261 || streq(p1, "tls-auth") || strprefix(p1, "tun-ipv6") || strprefix(p1, "cipher "))
262 {
263 return;
264 }
265
266 if (strlen(p1) > 0)
267 {
268 struct gc_arena gc = gc_new();
269 struct buffer b2 = *b2_src;
271 char *p2 = gc_malloc(OPTION_PARM_SIZE, false, &gc);
272
274 {
275 if (strlen(p2))
276 {
278
279 if (!strcmp(p1, p2))
280 {
281 goto done;
282 }
284 {
286 {
287 msg(msglevel, "WARNING: '%s' is used inconsistently, %s='%s', %s='%s'",
289 safe_print(p2, &gc));
290 }
291 goto done;
292 }
293 }
294 }
295
296 msg(msglevel, "WARNING: '%s' is present in %s config but missing in %s config, %s='%s'",
298
299done:
300 gc_free(&gc);
301 }
302}
303
304static void
305options_warning_safe_scan1(const msglvl_t msglevel, const int delim,
306 const bool report_inconsistent, const struct buffer *b1_src,
307 const struct buffer *b2_src, const char *b1_name, const char *b2_name)
308{
309 struct gc_arena gc = gc_new();
310 struct buffer b = *b1_src;
311 char *p = gc_malloc(OPTION_PARM_SIZE, true, &gc);
312
313 while (buf_parse(&b, delim, p, OPTION_PARM_SIZE))
314 {
316 b2_name);
317 }
318
319 gc_free(&gc);
320}
321
322static void
323options_warning_safe_ml(const msglvl_t msglevel, char *actual, const char *expected, size_t actual_n)
324{
325 struct gc_arena gc = gc_new();
326
327 if (actual_n > 0)
328 {
329 struct buffer local = alloc_buf_gc(OPTION_PARM_SIZE + 16, &gc);
330 struct buffer remote = alloc_buf_gc(OPTION_PARM_SIZE + 16, &gc);
331 actual[actual_n - 1] = 0;
332
333 buf_printf(&local, "version %s", expected);
334 buf_printf(&remote, "version %s", actual);
335
336 options_warning_safe_scan1(msglevel, ',', true, &local, &remote, "local", "remote");
337
338 options_warning_safe_scan1(msglevel, ',', false, &remote, &local, "remote", "local");
339 }
340
341 gc_free(&gc);
342}
343
344bool
345options_cmp_equal_safe(char *actual, const char *expected, size_t actual_n)
346{
347 struct gc_arena gc = gc_new();
348 bool ret = true;
349
350 if (actual_n > 0)
351 {
352 actual[actual_n - 1] = 0;
353 if (strncmp(actual, expected, 2))
354 {
355 msg(D_SHOW_OCC, "NOTE: Options consistency check may be skewed by version differences");
356 options_warning_safe_ml(D_SHOW_OCC, actual, expected, actual_n);
357 }
358 else
359 {
360 ret = !strcmp(actual, expected);
361 }
362 }
363 gc_free(&gc);
364 return ret;
365}
366
367bool
368options_cmp_equal(char *actual, const char *expected)
369{
370 return options_cmp_equal_safe(actual, expected, strlen(actual) + 1);
371}
372
373void
374options_warning_safe(char *actual, const char *expected, size_t actual_n)
375{
376 options_warning_safe_ml(D_SHOW_OCC, actual, expected, actual_n);
377}
378
379void
380options_warning(char *actual, const char *expected)
381{
382 options_warning_safe(actual, expected, strlen(actual) + 1);
383}
384
385const char *
386options_string_version(const char *s, struct gc_arena *gc)
387{
388 struct buffer out = alloc_buf_gc(4, gc);
389 strncpynt((char *)BPTR(&out), s, 3);
390 return BSTR(&out);
391}
392
393#if defined(__GNUC__) || defined(__clang__)
394#pragma GCC diagnostic push
395#pragma GCC diagnostic ignored "-Wsign-compare"
396#endif
397
398char *
400{
401 char *ret = NULL;
402 const size_t opt_name_len = strlen(opt_name);
403
404 const char *p = options_string;
405 while (p)
406 {
407 if (0 == strncmp(p, opt_name, opt_name_len) && strlen(p) > (opt_name_len + 1)
408 && p[opt_name_len] == ' ')
409 {
410 /* option found, extract value */
411 const char *start = &p[opt_name_len + 1];
412 const char *end = strchr(p, ',');
413 size_t val_len = end ? end - start : strlen(start);
414 ret = gc_malloc(val_len + 1, true, gc);
415 memcpy(ret, start, val_len);
416 break;
417 }
418 p = strchr(p, ',');
419 if (p)
420 {
421 p++; /* skip delimiter */
422 }
423 }
424 return ret;
425}
426
427#if defined(__GNUC__) || defined(__clang__)
428#pragma GCC diagnostic pop
429#endif
bool buf_printf(struct buffer *buf, const char *format,...)
printf-style append to a buffer with overflow check.
Definition buffer.c:226
void * gc_malloc(size_t size, bool clear, struct gc_arena *a)
Allocate memory and, optionally, zero it.
Definition buffer.c:318
struct buffer alloc_buf_gc(size_t size, struct gc_arena *gc)
Allocate a buffer of the given size under garbage collection.
Definition buffer.c:77
struct buffer alloc_buf(size_t size)
Allocate a buffer of the given size.
Definition buffer.c:60
bool buf_parse(struct buffer *buf, const int delim, char *line, const int size)
Extract the next token from a buffer, delimited by a given character.
Definition buffer.c:775
char * string_alloc(const char *str, struct gc_arena *gc)
Duplicate a string, allocating memory under garbage collection.
Definition buffer.c:616
struct buffer string_alloc_buf(const char *str, struct gc_arena *gc)
Allocate a buffer containing a copy of the given string.
Definition buffer.c:707
Buffer management functions and garbage collection.
#define BSTR(buf)
Return the buffer content pointer cast to char *.
Definition buffer.h:151
#define BPTR(buf)
Return a pointer to the start of the buffer content.
Definition buffer.h:139
static void strncpynt(char *dest, const char *src, size_t maxlen)
Like strncpy() but always null-terminates the destination.
Definition buffer.h:646
static void gc_free(struct gc_arena *a)
Free all allocations in a garbage collection arena.
Definition buffer.h:1912
static bool strprefix(const char *str, const char *prefix)
Return true iff str starts with prefix.
Definition buffer.h:1764
static struct gc_arena gc_new(void)
Allocate and return a new, empty garbage collection arena.
Definition buffer.h:1896
#define COMP_ALG_UNDEF
Definition comp.h:54
void init_key_type(struct key_type *kt, const char *ciphername, const char *authname, bool tls_mode, bool warn)
Initialize a key_type structure with.
Definition crypto.c:875
const char * keydirection2ascii(int kd, bool remote, bool humanreadable)
Definition crypto.c:1655
Data Channel Cryptography Module.
static bool cipher_defined(const char *ciphername)
Checks if the cipher is defined and is not the null (none) cipher.
const char * md_kt_name(const char *mdname)
Retrieve a string describing the digest digest (e.g.
const char * cipher_kt_name(const char *ciphername)
Retrieve a normalised string describing the cipher (e.g.
unsigned int cipher_kt_key_size(const char *ciphername)
Returns the size of keys used by the cipher, in bytes.
#define D_SHOW_OCC
Definition errlevel.h:150
const char * safe_print(const char *str, struct gc_arena *gc)
Definition misc.c:547
size_t calc_options_string_link_mtu(const struct options *o, const struct frame *frame)
Calculate the link-mtu to advertise to our peer.
Definition mtu.c:155
void * openvpn_net_ctx_t
Definition networking.h:38
#define msg(flags,...)
Definition error.h:152
unsigned int msglvl_t
Definition error.h:77
#define ASSERT(x)
Definition error.h:219
#define MODE_POINT_TO_POINT
Definition options.h:264
#define streq(x, y)
Definition options.h:726
#define OPTION_PARM_SIZE
Definition options.h:56
#define PULL_DEFINED(opt)
Definition options.h:766
#define OPTION_LINE_SIZE
Definition options.h:57
bool options_cmp_equal(char *actual, const char *expected)
#define TLS_CLIENT
static const char * options_warning_extract_parm1(const char *option_string, struct gc_arena *gc_ret)
Compare option strings for equality.
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)
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)
static void options_warning_safe_ml(const msglvl_t msglevel, char *actual, const char *expected, size_t actual_n)
char * options_string_extract_option(const char *options_string, const char *opt_name, struct gc_arena *gc)
Given an OpenVPN options string, extract the value of an option.
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)
void options_warning(char *actual, const char *expected)
const char * options_string_version(const char *s, struct gc_arena *gc)
#define TLS_SERVER
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)
Build an options string to represent data channel encryption options.
const char * proto_remote(int proto, bool remote)
Control Channel SSL/Data channel negotiation module.
#define KEY_METHOD_2
Definition ssl.h:122
bool tls_item_in_cipher_list(const char *item, const char *list)
Return true iff item is present in the colon-separated zero-terminated cipher list.
Definition ssl_ncp.c:197
Control Channel SSL/Data dynamic negotiation Module This file is split from ssl.h to be able to unit ...
Wrapper structure for dynamically allocated memory.
Definition buffer.h:71
int len
Length in bytes of the actual content within the allocated memory.
Definition buffer.h:76
const char * tls_auth_file
Definition options.h:167
Packet geometry parameters.
Definition mtu.h:113
int tun_mtu
the (user) configured tun-mtu.
Definition mtu.h:147
Garbage collection arena used to keep track of dynamically allocated memory.
Definition buffer.h:127
const char * cipher
const name of the cipher
Definition crypto.h:142
const char * digest
Message digest static parameters.
Definition crypto.h:143
const char * authname
Definition options.h:581
struct connection_entry ce
Definition options.h:294
const char * ncp_ciphers
Definition options.h:580
const char * ciphername
Definition options.h:575
const char * shared_secret_file
Definition options.h:571
Definition tun.h:181
struct gc_arena gc
Definition test_ssl.c:122
struct tuntap * init_tun(const char *dev, const char *dev_type, int topology, const char *ifconfig_local_parm, const char *ifconfig_remote_netmask_parm, const char *ifconfig_ipv6_local_parm, int ifconfig_ipv6_netbits_parm, const char *ifconfig_ipv6_remote_parm, struct addrinfo *local_public, struct addrinfo *remote_public, const bool strict_warn, struct env_set *es, openvpn_net_ctx_t *ctx, struct tuntap *tt)
Definition tun.c:830
const char * dev_type_string(const char *dev, const char *dev_type)
Definition tun.c:540
const char * ifconfig_options_string(const struct tuntap *tt, bool remote, bool disable, struct gc_arena *gc)
Definition tun.c:694