OpenVPN
env_set.c
Go to the documentation of this file.
1/*
2 * OpenVPN -- An application to securely tunnel IP networks
3 * over a single TCP/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-2025 OpenVPN Technologies, Inc. <sales@openvpn.net>
9 * Copyright (C) 2014-2015 David Sommerseth <davids@redhat.com>
10 * Copyright (C) 2016-2025 David Sommerseth <davids@openvpn.net>
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License version 2
14 * as published by the Free Software Foundation.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program (see the file COPYING included with this
23 * distribution); if not, write to the Free Software Foundation, Inc.,
24 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 */
26
27#ifdef HAVE_CONFIG_H
28#include "config.h"
29#endif
30
31#include "syshead.h"
32
33#include "env_set.h"
34
35#include "run_command.h"
36#include "platform.h"
37
38/*
39 * Set environmental variable (int or string).
40 *
41 * On Posix, we use putenv for portability,
42 * and put up with its painful semantics
43 * that require all the support code below.
44 */
45
46/* General-purpose environmental variable set functions */
47
48static char *
49construct_name_value(const char *name, const char *value, struct gc_arena *gc)
50{
51 struct buffer out;
52
53 ASSERT(name);
54 if (!value)
55 {
56 value = "";
57 }
58 out = alloc_buf_gc(strlen(name) + strlen(value) + 2, gc);
59 buf_printf(&out, "%s=%s", name, value);
60 return BSTR(&out);
61}
62
63static bool
64env_string_equal(const char *s1, const char *s2)
65{
66 int c1, c2;
67 ASSERT(s1);
68 ASSERT(s2);
69
70 while (true)
71 {
72 c1 = *s1++;
73 c2 = *s2++;
74 if (c1 == '=')
75 {
76 c1 = 0;
77 }
78 if (c2 == '=')
79 {
80 c2 = 0;
81 }
82 if (!c1 && !c2)
83 {
84 return true;
85 }
86 if (c1 != c2)
87 {
88 break;
89 }
90 }
91 return false;
92}
93
94static bool
95remove_env_item(const char *str, const bool do_free, struct env_item **list)
96{
97 struct env_item *current, *prev;
98
99 ASSERT(str);
100 ASSERT(list);
101
102 for (current = *list, prev = NULL; current != NULL; current = current->next)
103 {
104 if (env_string_equal(current->string, str))
105 {
106 if (prev)
107 {
108 prev->next = current->next;
109 }
110 else
111 {
112 *list = current->next;
113 }
114 if (do_free)
115 {
116 secure_memzero(current->string, strlen(current->string));
117 free(current->string);
118 free(current);
119 }
120 return true;
121 }
122 prev = current;
123 }
124 return false;
125}
126
127static void
128add_env_item(char *str, const bool do_alloc, struct env_item **list, struct gc_arena *gc)
129{
130 struct env_item *item;
131
132 ASSERT(str);
133 ASSERT(list);
134
135 ALLOC_OBJ_GC(item, struct env_item, gc);
136 item->string = do_alloc ? string_alloc(str, gc) : str;
137 item->next = *list;
138 *list = item;
139}
140
141/* struct env_set functions */
142
143static bool
144env_set_del_nolock(struct env_set *es, const char *str)
145{
146 return remove_env_item(str, es->gc == NULL, &es->list);
147}
148
149static void
150env_set_add_nolock(struct env_set *es, const char *str)
151{
152 remove_env_item(str, es->gc == NULL, &es->list);
153 add_env_item((char *)str, true, &es->list, es->gc);
154}
155
156struct env_set *
158{
159 struct env_set *es;
161 es->list = NULL;
162 es->gc = gc;
163 return es;
164}
165
166void
168{
169 if (es && es->gc == NULL)
170 {
171 struct env_item *e = es->list;
172 while (e)
173 {
174 struct env_item *next = e->next;
175 free(e->string);
176 free(e);
177 e = next;
178 }
179 free(es);
180 }
181}
182
183bool
184env_set_del(struct env_set *es, const char *str)
185{
186 bool ret;
187 ASSERT(es);
188 ASSERT(str);
189 ret = env_set_del_nolock(es, str);
190 return ret;
191}
192
193void
194env_set_add(struct env_set *es, const char *str)
195{
196 ASSERT(es);
197 ASSERT(str);
199}
200
201const char *
202env_set_get(const struct env_set *es, const char *name)
203{
204 const struct env_item *item = es->list;
205 while (item && !env_string_equal(item->string, name))
206 {
207 item = item->next;
208 }
209 return item ? item->string : NULL;
210}
211
212void
213env_set_print(int msglevel, const struct env_set *es)
214{
215 if (check_debug_level(msglevel))
216 {
217 const struct env_item *e;
218 int i;
219
220 if (es)
221 {
222 e = es->list;
223 i = 0;
224
225 while (e)
226 {
228 {
229 msg(msglevel, "ENV [%d] '%s'", i, e->string);
230 }
231 ++i;
232 e = e->next;
233 }
234 }
235 }
236}
237
238void
239env_set_write_file(const char *path, const struct env_set *es)
240{
241 FILE *fp = platform_fopen(path, "w");
242 if (!fp)
243 {
244 msg(M_ERR, "could not write env set to '%s'", path);
245 return;
246 }
247
248 if (es)
249 {
250 const struct env_item *item = es->list;
251 while (item)
252 {
253 fputs(item->string, fp);
254 fputc('\n', fp);
255 item = item->next;
256 }
257 }
258
259 fclose(fp);
260}
261
262void
263env_set_inherit(struct env_set *es, const struct env_set *src)
264{
265 const struct env_item *e;
266
267 ASSERT(es);
268
269 if (src)
270 {
271 e = src->list;
272 while (e)
273 {
275 e = e->next;
276 }
277 }
278}
279
280
281/* add/modify/delete environmental strings */
282
283void
284setenv_counter(struct env_set *es, const char *name, counter_type value)
285{
286 char buf[64];
287 snprintf(buf, sizeof(buf), counter_format, value);
288 setenv_str(es, name, buf);
289}
290
291void
292setenv_int(struct env_set *es, const char *name, int value)
293{
294 char buf[64];
295 snprintf(buf, sizeof(buf), "%d", value);
296 setenv_str(es, name, buf);
297}
298
299void
300setenv_long_long(struct env_set *es, const char *name, long long value)
301{
302 char buf[64];
303 snprintf(buf, sizeof(buf), "%" PRIi64, (int64_t)value);
304 setenv_str(es, name, buf);
305}
306
307void
308setenv_str(struct env_set *es, const char *name, const char *value)
309{
310 setenv_str_ex(es, name, value, CC_NAME, 0, 0, CC_PRINT, 0, 0);
311}
312
313void
314setenv_str_safe(struct env_set *es, const char *name, const char *value)
315{
316 uint8_t b[64];
317 struct buffer buf;
318 buf_set_write(&buf, b, sizeof(b));
319 if (buf_printf(&buf, "OPENVPN_%s", name))
320 {
321 setenv_str(es, BSTR(&buf), value);
322 }
323 else
324 {
325 msg(M_WARN, "setenv_str_safe: name overflow");
326 }
327}
328
329void
330setenv_str_incr(struct env_set *es, const char *name, const char *value)
331{
332 unsigned int counter = 1;
333 const size_t tmpname_len = strlen(name) + 5; /* 3 digits counter max */
334 char *tmpname = gc_malloc(tmpname_len, true, NULL);
335 strcpy(tmpname, name);
336 while (NULL != env_set_get(es, tmpname) && counter < 1000)
337 {
338 ASSERT(snprintf(tmpname, tmpname_len, "%s_%u", name, counter));
339 counter++;
340 }
341 if (counter < 1000)
342 {
343 setenv_str(es, tmpname, value);
344 }
345 else
346 {
347 msg(D_TLS_DEBUG_MED, "Too many same-name env variables, ignoring: %s", name);
348 }
349 free(tmpname);
350}
351
352void
353setenv_del(struct env_set *es, const char *name)
354{
355 ASSERT(name);
356 setenv_str(es, name, NULL);
357}
358
359void
361 const char *name,
362 const char *value,
363 const unsigned int name_include,
364 const unsigned int name_exclude,
365 const char name_replace,
366 const unsigned int value_include,
367 const unsigned int value_exclude,
368 const char value_replace)
369{
370 struct gc_arena gc = gc_new();
371 const char *name_tmp;
372 const char *val_tmp = NULL;
373
374 ASSERT(name && strlen(name) > 1);
375
376 name_tmp = string_mod_const(name, name_include, name_exclude, name_replace, &gc);
377
378 if (value)
379 {
380 val_tmp = string_mod_const(value, value_include, value_exclude, value_replace, &gc);
381 }
382
383 ASSERT(es);
384
385 if (val_tmp)
386 {
387 const char *str = construct_name_value(name_tmp, val_tmp, &gc);
388 env_set_add(es, str);
389#if DEBUG_VERBOSE_SETENV
390 msg(M_INFO, "SETENV_ES '%s'", str);
391#endif
392 }
393 else
394 {
395 env_set_del(es, name_tmp);
396 }
397
398 gc_free(&gc);
399}
400
401/*
402 * Setenv functions that append an integer index to the name
403 */
404static const char *
405setenv_format_indexed_name(const char *name, const int i, struct gc_arena *gc)
406{
407 struct buffer out = alloc_buf_gc(strlen(name) + 16, gc);
408 if (i >= 0)
409 {
410 buf_printf(&out, "%s_%d", name, i);
411 }
412 else
413 {
414 buf_printf(&out, "%s", name);
415 }
416 return BSTR(&out);
417}
418
419void
420setenv_int_i(struct env_set *es, const char *name, const int value, const int i)
421{
422 struct gc_arena gc = gc_new();
423 const char *name_str = setenv_format_indexed_name(name, i, &gc);
424 setenv_int(es, name_str, value);
425 gc_free(&gc);
426}
427
428void
429setenv_str_i(struct env_set *es, const char *name, const char *value, const int i)
430{
431 struct gc_arena gc = gc_new();
432 const char *name_str = setenv_format_indexed_name(name, i, &gc);
433 setenv_str(es, name_str, value);
434 gc_free(&gc);
435}
436
437bool
438env_allowed(const char *str)
439{
440 return (script_security() >= SSEC_PW_ENV || !is_password_env_var(str));
441}
442
443/* Make arrays of strings */
444
445const char **
447 const bool check_allowed,
448 struct gc_arena *gc)
449{
450 char **ret = NULL;
451 struct env_item *e = NULL;
452 int i = 0, n = 0;
453
454 /* figure length of es */
455 if (es)
456 {
457 for (e = es->list; e != NULL; e = e->next)
458 {
459 ++n;
460 }
461 }
462
463 /* alloc return array */
464 ALLOC_ARRAY_CLEAR_GC(ret, char *, n+1, gc);
465
466 /* fill return array */
467 if (es)
468 {
469 i = 0;
470 for (e = es->list; e != NULL; e = e->next)
471 {
472 if (!check_allowed || env_allowed(e->string))
473 {
474 ASSERT(i < n);
475 ret[i++] = e->string;
476 }
477 }
478 }
479
480 ret[i] = NULL;
481 return (const char **)ret;
482}
bool buf_printf(struct buffer *buf, const char *format,...)
Definition buffer.c:240
void * gc_malloc(size_t size, bool clear, struct gc_arena *a)
Definition buffer.c:336
struct buffer alloc_buf_gc(size_t size, struct gc_arena *gc)
Definition buffer.c:88
const char * string_mod_const(const char *str, const unsigned int inclusive, const unsigned int exclusive, const char replace, struct gc_arena *gc)
Returns a copy of a string with certain classes of characters of it replaced with a specified charact...
Definition buffer.c:1091
char * string_alloc(const char *str, struct gc_arena *gc)
Definition buffer.c:649
#define BSTR(buf)
Definition buffer.h:129
#define ALLOC_ARRAY_CLEAR_GC(dptr, type, n, gc)
Definition buffer.h:1082
static void buf_set_write(struct buffer *buf, uint8_t *data, int size)
Definition buffer.h:331
static void secure_memzero(void *data, size_t len)
Securely zeroise memory.
Definition buffer.h:414
#define ALLOC_OBJ_CLEAR_GC(dptr, type, gc)
Definition buffer.h:1097
#define ALLOC_OBJ_GC(dptr, type, gc)
Definition buffer.h:1092
#define CC_NAME
alphanumeric plus underscore
Definition buffer.h:919
static void gc_free(struct gc_arena *a)
Definition buffer.h:1033
#define CC_PRINT
printable (>= 32, != 127)
Definition buffer.h:891
static struct gc_arena gc_new(void)
Definition buffer.h:1025
uint64_t counter_type
Definition common.h:30
#define counter_format
Definition common.h:31
void setenv_counter(struct env_set *es, const char *name, counter_type value)
Definition env_set.c:284
void env_set_print(int msglevel, const struct env_set *es)
Definition env_set.c:213
static bool env_set_del_nolock(struct env_set *es, const char *str)
Definition env_set.c:144
void env_set_destroy(struct env_set *es)
Definition env_set.c:167
void setenv_int(struct env_set *es, const char *name, int value)
Definition env_set.c:292
void setenv_int_i(struct env_set *es, const char *name, const int value, const int i)
Definition env_set.c:420
static const char * setenv_format_indexed_name(const char *name, const int i, struct gc_arena *gc)
Definition env_set.c:405
void setenv_str_i(struct env_set *es, const char *name, const char *value, const int i)
Definition env_set.c:429
static void env_set_add_nolock(struct env_set *es, const char *str)
Definition env_set.c:150
void env_set_write_file(const char *path, const struct env_set *es)
Write a struct env_set to a file.
Definition env_set.c:239
static char * construct_name_value(const char *name, const char *value, struct gc_arena *gc)
Definition env_set.c:49
void setenv_str(struct env_set *es, const char *name, const char *value)
Definition env_set.c:308
const char ** make_env_array(const struct env_set *es, const bool check_allowed, struct gc_arena *gc)
Definition env_set.c:446
void env_set_add(struct env_set *es, const char *str)
Definition env_set.c:194
static bool env_string_equal(const char *s1, const char *s2)
Definition env_set.c:64
static void add_env_item(char *str, const bool do_alloc, struct env_item **list, struct gc_arena *gc)
Definition env_set.c:128
void setenv_str_ex(struct env_set *es, const char *name, const char *value, const unsigned int name_include, const unsigned int name_exclude, const char name_replace, const unsigned int value_include, const unsigned int value_exclude, const char value_replace)
Definition env_set.c:360
const char * env_set_get(const struct env_set *es, const char *name)
Definition env_set.c:202
void env_set_inherit(struct env_set *es, const struct env_set *src)
Definition env_set.c:263
void setenv_str_incr(struct env_set *es, const char *name, const char *value)
Store the supplied name value pair in the env_set.
Definition env_set.c:330
void setenv_str_safe(struct env_set *es, const char *name, const char *value)
Definition env_set.c:314
struct env_set * env_set_create(struct gc_arena *gc)
Definition env_set.c:157
bool env_allowed(const char *str)
Definition env_set.c:438
bool env_set_del(struct env_set *es, const char *str)
Definition env_set.c:184
void setenv_long_long(struct env_set *es, const char *name, long long value)
Definition env_set.c:300
static bool remove_env_item(const char *str, const bool do_free, struct env_item **list)
Definition env_set.c:95
void setenv_del(struct env_set *es, const char *name)
Definition env_set.c:353
static bool is_password_env_var(const char *str)
Definition env_set.h:106
static bool env_safe_to_print(const char *str)
Definition env_set.h:113
#define D_TLS_DEBUG_MED
Definition errlevel.h:157
#define M_INFO
Definition errlevel.h:55
static bool check_debug_level(unsigned int level)
Definition error.h:220
#define M_ERR
Definition error.h:105
#define msg(flags,...)
Definition error.h:144
#define ASSERT(x)
Definition error.h:195
#define M_WARN
Definition error.h:91
FILE * platform_fopen(const char *path, const char *mode)
Definition platform.c:501
int script_security(void)
Definition run_command.c:43
#define SSEC_PW_ENV
Definition run_command.h:34
Wrapper structure for dynamically allocated memory.
Definition buffer.h:61
int len
Length in bytes of the actual content within the allocated memory.
Definition buffer.h:66
char * string
Definition env_set.h:38
struct env_item * next
Definition env_set.h:39
struct env_item * list
Definition env_set.h:44
struct gc_arena * gc
Definition env_set.h:43
Garbage collection arena used to keep track of dynamically allocated memory.
Definition buffer.h:117
struct env_set * es
struct gc_arena gc
Definition test_ssl.c:155