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-2026 OpenVPN Technologies, Inc. <sales@openvpn.net>
9 * Copyright (C) 2014-2015 David Sommerseth <davids@redhat.com>
10 * Copyright (C) 2016-2026 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, see <https://www.gnu.org/licenses/>.
24 */
25
26#ifdef HAVE_CONFIG_H
27#include "config.h"
28#endif
29
30#include "syshead.h"
31
32#include "env_set.h"
33
34#include "run_command.h"
35#include "platform.h"
36
37/*
38 * Set environmental variable (int or string).
39 *
40 * On Posix, we use putenv for portability,
41 * and put up with its painful semantics
42 * that require all the support code below.
43 */
44
45/* General-purpose environmental variable set functions */
46
47static char *
48construct_name_value(const char *name, const char *value, struct gc_arena *gc)
49{
50 struct buffer out;
51
52 ASSERT(name);
53 if (!value)
54 {
55 value = "";
56 }
57 out = alloc_buf_gc(strlen(name) + strlen(value) + 2, gc);
58 buf_printf(&out, "%s=%s", name, value);
59 return BSTR(&out);
60}
61
62static bool
63env_string_equal(const char *s1, const char *s2)
64{
65 ASSERT(s1);
66 ASSERT(s2);
67
68 while (true)
69 {
70 int c1 = *s1++;
71 int c2 = *s2++;
72 if (c1 == '=')
73 {
74 c1 = 0;
75 }
76 if (c2 == '=')
77 {
78 c2 = 0;
79 }
80 if (!c1 && !c2)
81 {
82 return true;
83 }
84 if (c1 != c2)
85 {
86 break;
87 }
88 }
89 return false;
90}
91
92static bool
93remove_env_item(const char *str, const bool do_free, struct env_item **list)
94{
95 struct env_item *current, *prev;
96
97 ASSERT(str);
98 ASSERT(list);
99
100 for (current = *list, prev = NULL; current != NULL; current = current->next)
101 {
102 if (env_string_equal(current->string, str))
103 {
104 if (prev)
105 {
106 prev->next = current->next;
107 }
108 else
109 {
110 *list = current->next;
111 }
112 if (do_free)
113 {
114 secure_memzero(current->string, strlen(current->string));
115 free(current->string);
116 free(current);
117 }
118 return true;
119 }
120 prev = current;
121 }
122 return false;
123}
124
125static void
126add_env_item(char *str, const bool do_alloc, struct env_item **list, struct gc_arena *gc)
127{
128 struct env_item *item;
129
130 ASSERT(str);
131 ASSERT(list);
132
133 ALLOC_OBJ_GC(item, struct env_item, gc);
134 item->string = do_alloc ? string_alloc(str, gc) : str;
135 item->next = *list;
136 *list = item;
137}
138
139/* struct env_set functions */
140
141static bool
142env_set_del_nolock(struct env_set *es, const char *str)
143{
144 return remove_env_item(str, es->gc == NULL, &es->list);
145}
146
147static void
148env_set_add_nolock(struct env_set *es, const char *str)
149{
150 remove_env_item(str, es->gc == NULL, &es->list);
151 add_env_item((char *)str, true, &es->list, es->gc);
152}
153
154struct env_set *
156{
157 struct env_set *es;
158 ALLOC_OBJ_CLEAR_GC(es, struct env_set, gc);
159 es->list = NULL;
160 es->gc = gc;
161 return es;
162}
163
164void
166{
167 if (es && es->gc == NULL)
168 {
169 struct env_item *e = es->list;
170 while (e)
171 {
172 struct env_item *next = e->next;
173 free(e->string);
174 free(e);
175 e = next;
176 }
177 free(es);
178 }
179}
180
181bool
182env_set_del(struct env_set *es, const char *str)
183{
184 bool ret;
185 ASSERT(es);
186 ASSERT(str);
187 ret = env_set_del_nolock(es, str);
188 return ret;
189}
190
191void
192env_set_add(struct env_set *es, const char *str)
193{
194 ASSERT(es);
195 ASSERT(str);
196 env_set_add_nolock(es, str);
197}
198
199const char *
200env_set_get(const struct env_set *es, const char *name)
201{
202 const struct env_item *item = es->list;
203 while (item && !env_string_equal(item->string, name))
204 {
205 item = item->next;
206 }
207 return item ? item->string : NULL;
208}
209
210void
211env_set_print(msglvl_t msglevel, const struct env_set *es)
212{
213 if (check_debug_level(msglevel))
214 {
215 const struct env_item *e;
216
217 if (es)
218 {
219 int i = 0;
220 e = es->list;
221
222 while (e)
223 {
225 {
226 msg(msglevel, "ENV [%d] '%s'", i, e->string);
227 }
228 ++i;
229 e = e->next;
230 }
231 }
232 }
233}
234
235void
236env_set_write_file(const char *path, const struct env_set *es)
237{
238 FILE *fp = platform_fopen(path, "w");
239 if (!fp)
240 {
241 msg(M_ERR, "could not write env set to '%s'", path);
242 return;
243 }
244
245 if (es)
246 {
247 const struct env_item *item = es->list;
248 while (item)
249 {
250 fputs(item->string, fp);
251 fputc('\n', fp);
252 item = item->next;
253 }
254 }
255
256 fclose(fp);
257}
258
259void
260env_set_inherit(struct env_set *es, const struct env_set *src)
261{
262 const struct env_item *e;
263
264 ASSERT(es);
265
266 if (src)
267 {
268 e = src->list;
269 while (e)
270 {
272 e = e->next;
273 }
274 }
275}
276
277
278/* add/modify/delete environmental strings */
279
280void
281setenv_counter(struct env_set *es, const char *name, counter_type value)
282{
283 char buf[64];
284 snprintf(buf, sizeof(buf), counter_format, value);
285 setenv_str(es, name, buf);
286}
287
288void
289setenv_int(struct env_set *es, const char *name, int value)
290{
291 char buf[64];
292 snprintf(buf, sizeof(buf), "%d", value);
293 setenv_str(es, name, buf);
294}
295
296void
297setenv_long_long(struct env_set *es, const char *name, long long value)
298{
299 char buf[64];
300 snprintf(buf, sizeof(buf), "%" PRIi64, (int64_t)value);
301 setenv_str(es, name, buf);
302}
303
304void
305setenv_str(struct env_set *es, const char *name, const char *value)
306{
307 setenv_str_ex(es, name, value, CC_NAME, 0, 0, CC_PRINT, 0, 0);
308}
309
310void
311setenv_str_safe(struct env_set *es, const char *name, const char *value)
312{
313 uint8_t b[64];
314 struct buffer buf;
315 buf_set_write(&buf, b, sizeof(b));
316 if (buf_printf(&buf, "OPENVPN_%s", name))
317 {
318 setenv_str(es, BSTR(&buf), value);
319 }
320 else
321 {
322 msg(M_WARN, "setenv_str_safe: name overflow");
323 }
324}
325
326void
327setenv_str_incr(struct env_set *es, const char *name, const char *value)
328{
329 unsigned int counter = 1;
330 const size_t tmpname_len = strlen(name) + 5; /* 3 digits counter max */
331 char *tmpname = gc_malloc(tmpname_len, true, NULL);
332 strcpy(tmpname, name);
333 while (NULL != env_set_get(es, tmpname) && counter < 1000)
334 {
336 counter++;
337 }
338 if (counter < 1000)
339 {
340 setenv_str(es, tmpname, value);
341 }
342 else
343 {
344 msg(D_TLS_DEBUG_MED, "Too many same-name env variables, ignoring: %s", name);
345 }
346 free(tmpname);
347}
348
349void
350setenv_del(struct env_set *es, const char *name)
351{
352 ASSERT(name);
353 setenv_str(es, name, NULL);
354}
355
356void
357setenv_str_ex(struct env_set *es, const char *name, const char *value,
358 const unsigned int name_include, const unsigned int name_exclude,
359 const char name_replace, const unsigned int value_include,
360 const unsigned int value_exclude, const char value_replace)
361{
362 struct gc_arena gc = gc_new();
363 const char *name_tmp;
364 const char *val_tmp = NULL;
365
366 ASSERT(name && strlen(name) > 1);
367
368 name_tmp = string_mod_const(name, name_include, name_exclude, name_replace, &gc);
369
370 if (value)
371 {
372 val_tmp = string_mod_const(value, value_include, value_exclude, value_replace, &gc);
373 }
374
375 ASSERT(es);
376
377 if (val_tmp)
378 {
379 const char *str = construct_name_value(name_tmp, val_tmp, &gc);
380 env_set_add(es, str);
381#if DEBUG_VERBOSE_SETENV
382 msg(M_INFO, "SETENV_ES '%s'", str);
383#endif
384 }
385 else
386 {
387 env_set_del(es, name_tmp);
388 }
389
390 gc_free(&gc);
391}
392
393/*
394 * Setenv functions that append an integer index to the name
395 */
396static const char *
397setenv_format_indexed_name(const char *name, const int i, struct gc_arena *gc)
398{
399 struct buffer out = alloc_buf_gc(strlen(name) + 16, gc);
400 if (i >= 0)
401 {
402 buf_printf(&out, "%s_%d", name, i);
403 }
404 else
405 {
406 buf_printf(&out, "%s", name);
407 }
408 return BSTR(&out);
409}
410
411void
412setenv_int_i(struct env_set *es, const char *name, const int value, const int i)
413{
414 struct gc_arena gc = gc_new();
415 const char *name_str = setenv_format_indexed_name(name, i, &gc);
416 setenv_int(es, name_str, value);
417 gc_free(&gc);
418}
419
420void
421setenv_str_i(struct env_set *es, const char *name, const char *value, const int i)
422{
423 struct gc_arena gc = gc_new();
424 const char *name_str = setenv_format_indexed_name(name, i, &gc);
425 setenv_str(es, name_str, value);
426 gc_free(&gc);
427}
428
429bool
430env_allowed(const char *str)
431{
432 return (script_security() >= SSEC_PW_ENV || !is_password_env_var(str));
433}
434
435/* Make arrays of strings */
436
437const char **
438make_env_array(const struct env_set *es, const bool check_allowed, struct gc_arena *gc)
439{
440 char **ret = NULL;
441 struct env_item *e = NULL;
442 int i = 0, n = 0;
443
444 /* figure length of es */
445 if (es)
446 {
447 for (e = es->list; e != NULL; e = e->next)
448 {
449 ++n;
450 }
451 }
452
453 /* alloc return array */
454 ALLOC_ARRAY_CLEAR_GC(ret, char *, n + 1, gc);
455
456 /* fill return array */
457 if (es)
458 {
459 i = 0;
460 for (e = es->list; e != NULL; e = e->next)
461 {
462 if (!check_allowed || env_allowed(e->string))
463 {
464 ASSERT(i < n);
465 ret[i++] = e->string;
466 }
467 }
468 }
469
470 ret[i] = NULL;
471 return (const char **)ret;
472}
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
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:1055
bool checked_snprintf(char *str, size_t size, const char *format,...)
Like snprintf() but returns an boolean.
Definition buffer.c:1121
char * string_alloc(const char *str, struct gc_arena *gc)
Duplicate a string, allocating memory under garbage collection.
Definition buffer.c:616
#define BSTR(buf)
Return the buffer content pointer cast to char *.
Definition buffer.h:157
#define ALLOC_ARRAY_CLEAR_GC(dptr, type, n, gc)
Allocate and zero-initialise a garbage-collected array of n elements.
Definition buffer.h:2093
static void buf_set_write(struct buffer *buf, uint8_t *data, int size)
Initialise a buffer with an externally provided writable memory region.
Definition buffer.h:658
static void secure_memzero(void *data, size_t len)
Securely zeroise memory.
Definition buffer.h:769
#define ALLOC_OBJ_CLEAR_GC(dptr, type, gc)
Allocate and zero-initialise a garbage-collected object of the given type.
Definition buffer.h:2134
#define ALLOC_OBJ_GC(dptr, type, gc)
Allocate a garbage-collected object of the given type (uninitialised).
Definition buffer.h:2122
#define CC_NAME
alphanumeric plus underscore
Definition buffer.h:1736
static void gc_free(struct gc_arena *a)
Free all allocations in a garbage collection arena.
Definition buffer.h:1976
#define CC_PRINT
printable (>= 32, != 127)
Definition buffer.h:1708
static struct gc_arena gc_new(void)
Allocate and return a new, empty garbage collection arena.
Definition buffer.h:1960
uint64_t counter_type
Definition common.h:31
#define counter_format
Definition common.h:32
void setenv_counter(struct env_set *es, const char *name, counter_type value)
Definition env_set.c:281
static bool env_set_del_nolock(struct env_set *es, const char *str)
Definition env_set.c:142
void env_set_destroy(struct env_set *es)
Definition env_set.c:165
void setenv_int(struct env_set *es, const char *name, int value)
Definition env_set.c:289
void setenv_int_i(struct env_set *es, const char *name, const int value, const int i)
Definition env_set.c:412
static const char * setenv_format_indexed_name(const char *name, const int i, struct gc_arena *gc)
Definition env_set.c:397
void env_set_print(msglvl_t msglevel, const struct env_set *es)
Definition env_set.c:211
void setenv_str_i(struct env_set *es, const char *name, const char *value, const int i)
Definition env_set.c:421
static void env_set_add_nolock(struct env_set *es, const char *str)
Definition env_set.c:148
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:236
static char * construct_name_value(const char *name, const char *value, struct gc_arena *gc)
Definition env_set.c:48
void setenv_str(struct env_set *es, const char *name, const char *value)
Definition env_set.c:305
const char ** make_env_array(const struct env_set *es, const bool check_allowed, struct gc_arena *gc)
Definition env_set.c:438
void env_set_add(struct env_set *es, const char *str)
Definition env_set.c:192
static bool env_string_equal(const char *s1, const char *s2)
Definition env_set.c:63
static void add_env_item(char *str, const bool do_alloc, struct env_item **list, struct gc_arena *gc)
Definition env_set.c:126
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:357
const char * env_set_get(const struct env_set *es, const char *name)
Definition env_set.c:200
void env_set_inherit(struct env_set *es, const struct env_set *src)
Definition env_set.c:260
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:327
void setenv_str_safe(struct env_set *es, const char *name, const char *value)
Definition env_set.c:311
struct env_set * env_set_create(struct gc_arena *gc)
Definition env_set.c:155
bool env_allowed(const char *str)
Definition env_set.c:430
bool env_set_del(struct env_set *es, const char *str)
Definition env_set.c:182
void setenv_long_long(struct env_set *es, const char *name, long long value)
Definition env_set.c:297
static bool remove_env_item(const char *str, const bool do_free, struct env_item **list)
Definition env_set.c:93
void setenv_del(struct env_set *es, const char *name)
Definition env_set.c:350
static bool is_password_env_var(const char *str)
Definition env_set.h:102
static bool env_safe_to_print(const char *str)
Definition env_set.h:109
#define D_TLS_DEBUG_MED
Definition errlevel.h:156
#define M_INFO
Definition errlevel.h:54
static bool check_debug_level(msglvl_t level)
Definition error.h:253
#define M_ERR
Definition error.h:106
#define msg(flags,...)
Definition error.h:152
unsigned int msglvl_t
Definition error.h:77
#define ASSERT(x)
Definition error.h:221
#define M_WARN
Definition error.h:92
FILE * platform_fopen(const char *path, const char *mode)
Definition platform.c:500
int script_security(void)
Definition run_command.c:42
#define SSEC_PW_ENV
allow calling of built-in programs and user-defined scripts that may receive a password as an environ...
Definition run_command.h:38
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
char * string
Definition env_set.h:38
struct env_item * next
Definition env_set.h:39
struct env_item * list
Definition env_set.h:45
struct gc_arena * gc
Definition env_set.h:44
Garbage collection arena used to keep track of dynamically allocated memory.
Definition buffer.h:127
struct gc_arena gc
Definition test_ssl.c:122