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, 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 int c1, c2;
66 ASSERT(s1);
67 ASSERT(s2);
68
69 while (true)
70 {
71 c1 = *s1++;
72 c2 = *s2++;
73 if (c1 == '=')
74 {
75 c1 = 0;
76 }
77 if (c2 == '=')
78 {
79 c2 = 0;
80 }
81 if (!c1 && !c2)
82 {
83 return true;
84 }
85 if (c1 != c2)
86 {
87 break;
88 }
89 }
90 return false;
91}
92
93static bool
94remove_env_item(const char *str, const bool do_free, struct env_item **list)
95{
96 struct env_item *current, *prev;
97
98 ASSERT(str);
99 ASSERT(list);
100
101 for (current = *list, prev = NULL; current != NULL; current = current->next)
102 {
103 if (env_string_equal(current->string, str))
104 {
105 if (prev)
106 {
107 prev->next = current->next;
108 }
109 else
110 {
111 *list = current->next;
112 }
113 if (do_free)
114 {
115 secure_memzero(current->string, strlen(current->string));
116 free(current->string);
117 free(current);
118 }
119 return true;
120 }
121 prev = current;
122 }
123 return false;
124}
125
126static void
127add_env_item(char *str, const bool do_alloc, struct env_item **list, struct gc_arena *gc)
128{
129 struct env_item *item;
130
131 ASSERT(str);
132 ASSERT(list);
133
134 ALLOC_OBJ_GC(item, struct env_item, gc);
135 item->string = do_alloc ? string_alloc(str, gc) : str;
136 item->next = *list;
137 *list = item;
138}
139
140/* struct env_set functions */
141
142static bool
143env_set_del_nolock(struct env_set *es, const char *str)
144{
145 return remove_env_item(str, es->gc == NULL, &es->list);
146}
147
148static void
149env_set_add_nolock(struct env_set *es, const char *str)
150{
151 remove_env_item(str, es->gc == NULL, &es->list);
152 add_env_item((char *)str, true, &es->list, es->gc);
153}
154
155struct env_set *
157{
158 struct env_set *es;
160 es->list = NULL;
161 es->gc = gc;
162 return es;
163}
164
165void
167{
168 if (es && es->gc == NULL)
169 {
170 struct env_item *e = es->list;
171 while (e)
172 {
173 struct env_item *next = e->next;
174 free(e->string);
175 free(e);
176 e = next;
177 }
178 free(es);
179 }
180}
181
182bool
183env_set_del(struct env_set *es, const char *str)
184{
185 bool ret;
186 ASSERT(es);
187 ASSERT(str);
188 ret = env_set_del_nolock(es, str);
189 return ret;
190}
191
192void
193env_set_add(struct env_set *es, const char *str)
194{
195 ASSERT(es);
196 ASSERT(str);
198}
199
200const char *
201env_set_get(const struct env_set *es, const char *name)
202{
203 const struct env_item *item = es->list;
204 while (item && !env_string_equal(item->string, name))
205 {
206 item = item->next;
207 }
208 return item ? item->string : NULL;
209}
210
211void
212env_set_print(int msglevel, const struct env_set *es)
213{
214 if (check_debug_level(msglevel))
215 {
216 const struct env_item *e;
217 int i;
218
219 if (es)
220 {
221 e = es->list;
222 i = 0;
223
224 while (e)
225 {
227 {
228 msg(msglevel, "ENV [%d] '%s'", i, e->string);
229 }
230 ++i;
231 e = e->next;
232 }
233 }
234 }
235}
236
237void
238env_set_write_file(const char *path, const struct env_set *es)
239{
240 FILE *fp = platform_fopen(path, "w");
241 if (!fp)
242 {
243 msg(M_ERR, "could not write env set to '%s'", path);
244 return;
245 }
246
247 if (es)
248 {
249 const struct env_item *item = es->list;
250 while (item)
251 {
252 fputs(item->string, fp);
253 fputc('\n', fp);
254 item = item->next;
255 }
256 }
257
258 fclose(fp);
259}
260
261void
262env_set_inherit(struct env_set *es, const struct env_set *src)
263{
264 const struct env_item *e;
265
266 ASSERT(es);
267
268 if (src)
269 {
270 e = src->list;
271 while (e)
272 {
274 e = e->next;
275 }
276 }
277}
278
279
280/* add/modify/delete environmental strings */
281
282void
283setenv_counter(struct env_set *es, const char *name, counter_type value)
284{
285 char buf[64];
286 snprintf(buf, sizeof(buf), counter_format, value);
287 setenv_str(es, name, buf);
288}
289
290void
291setenv_int(struct env_set *es, const char *name, int value)
292{
293 char buf[64];
294 snprintf(buf, sizeof(buf), "%d", value);
295 setenv_str(es, name, buf);
296}
297
298void
299setenv_long_long(struct env_set *es, const char *name, long long value)
300{
301 char buf[64];
302 snprintf(buf, sizeof(buf), "%" PRIi64, (int64_t)value);
303 setenv_str(es, name, buf);
304}
305
306void
307setenv_str(struct env_set *es, const char *name, const char *value)
308{
309 setenv_str_ex(es, name, value, CC_NAME, 0, 0, CC_PRINT, 0, 0);
310}
311
312void
313setenv_str_safe(struct env_set *es, const char *name, const char *value)
314{
315 uint8_t b[64];
316 struct buffer buf;
317 buf_set_write(&buf, b, sizeof(b));
318 if (buf_printf(&buf, "OPENVPN_%s", name))
319 {
320 setenv_str(es, BSTR(&buf), value);
321 }
322 else
323 {
324 msg(M_WARN, "setenv_str_safe: name overflow");
325 }
326}
327
328void
329setenv_str_incr(struct env_set *es, const char *name, const char *value)
330{
331 unsigned int counter = 1;
332 const size_t tmpname_len = strlen(name) + 5; /* 3 digits counter max */
333 char *tmpname = gc_malloc(tmpname_len, true, NULL);
334 strcpy(tmpname, name);
335 while (NULL != env_set_get(es, tmpname) && counter < 1000)
336 {
337 ASSERT(snprintf(tmpname, tmpname_len, "%s_%u", name, counter));
338 counter++;
339 }
340 if (counter < 1000)
341 {
342 setenv_str(es, tmpname, value);
343 }
344 else
345 {
346 msg(D_TLS_DEBUG_MED, "Too many same-name env variables, ignoring: %s", name);
347 }
348 free(tmpname);
349}
350
351void
352setenv_del(struct env_set *es, const char *name)
353{
354 ASSERT(name);
355 setenv_str(es, name, NULL);
356}
357
358void
359setenv_str_ex(struct env_set *es, const char *name, const char *value,
360 const unsigned int name_include, const unsigned int name_exclude,
361 const char name_replace, const unsigned int value_include,
362 const unsigned int value_exclude, const char value_replace)
363{
364 struct gc_arena gc = gc_new();
365 const char *name_tmp;
366 const char *val_tmp = NULL;
367
368 ASSERT(name && strlen(name) > 1);
369
370 name_tmp = string_mod_const(name, name_include, name_exclude, name_replace, &gc);
371
372 if (value)
373 {
374 val_tmp = string_mod_const(value, value_include, value_exclude, value_replace, &gc);
375 }
376
377 ASSERT(es);
378
379 if (val_tmp)
380 {
381 const char *str = construct_name_value(name_tmp, val_tmp, &gc);
382 env_set_add(es, str);
383#if DEBUG_VERBOSE_SETENV
384 msg(M_INFO, "SETENV_ES '%s'", str);
385#endif
386 }
387 else
388 {
389 env_set_del(es, name_tmp);
390 }
391
392 gc_free(&gc);
393}
394
395/*
396 * Setenv functions that append an integer index to the name
397 */
398static const char *
399setenv_format_indexed_name(const char *name, const int i, struct gc_arena *gc)
400{
401 struct buffer out = alloc_buf_gc(strlen(name) + 16, gc);
402 if (i >= 0)
403 {
404 buf_printf(&out, "%s_%d", name, i);
405 }
406 else
407 {
408 buf_printf(&out, "%s", name);
409 }
410 return BSTR(&out);
411}
412
413void
414setenv_int_i(struct env_set *es, const char *name, const int value, const int i)
415{
416 struct gc_arena gc = gc_new();
417 const char *name_str = setenv_format_indexed_name(name, i, &gc);
418 setenv_int(es, name_str, value);
419 gc_free(&gc);
420}
421
422void
423setenv_str_i(struct env_set *es, const char *name, const char *value, const int i)
424{
425 struct gc_arena gc = gc_new();
426 const char *name_str = setenv_format_indexed_name(name, i, &gc);
427 setenv_str(es, name_str, value);
428 gc_free(&gc);
429}
430
431bool
432env_allowed(const char *str)
433{
434 return (script_security() >= SSEC_PW_ENV || !is_password_env_var(str));
435}
436
437/* Make arrays of strings */
438
439const char **
440make_env_array(const struct env_set *es, const bool check_allowed, struct gc_arena *gc)
441{
442 char **ret = NULL;
443 struct env_item *e = NULL;
444 int i = 0, n = 0;
445
446 /* figure length of es */
447 if (es)
448 {
449 for (e = es->list; e != NULL; e = e->next)
450 {
451 ++n;
452 }
453 }
454
455 /* alloc return array */
456 ALLOC_ARRAY_CLEAR_GC(ret, char *, n + 1, gc);
457
458 /* fill return array */
459 if (es)
460 {
461 i = 0;
462 for (e = es->list; e != NULL; e = e->next)
463 {
464 if (!check_allowed || env_allowed(e->string))
465 {
466 ASSERT(i < n);
467 ret[i++] = e->string;
468 }
469 }
470 }
471
472 ret[i] = NULL;
473 return (const char **)ret;
474}
bool buf_printf(struct buffer *buf, const char *format,...)
Definition buffer.c:241
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:89
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:128
#define ALLOC_ARRAY_CLEAR_GC(dptr, type, n, gc)
Definition buffer.h:1064
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:1079
#define ALLOC_OBJ_GC(dptr, type, gc)
Definition buffer.h:1074
#define CC_NAME
alphanumeric plus underscore
Definition buffer.h:903
static void gc_free(struct gc_arena *a)
Definition buffer.h:1015
#define CC_PRINT
printable (>= 32, != 127)
Definition buffer.h:875
static struct gc_arena gc_new(void)
Definition buffer.h:1007
uint64_t counter_type
Definition common.h:29
#define counter_format
Definition common.h:30
void setenv_counter(struct env_set *es, const char *name, counter_type value)
Definition env_set.c:283
void env_set_print(int msglevel, const struct env_set *es)
Definition env_set.c:212
static bool env_set_del_nolock(struct env_set *es, const char *str)
Definition env_set.c:143
void env_set_destroy(struct env_set *es)
Definition env_set.c:166
void setenv_int(struct env_set *es, const char *name, int value)
Definition env_set.c:291
void setenv_int_i(struct env_set *es, const char *name, const int value, const int i)
Definition env_set.c:414
static const char * setenv_format_indexed_name(const char *name, const int i, struct gc_arena *gc)
Definition env_set.c:399
void setenv_str_i(struct env_set *es, const char *name, const char *value, const int i)
Definition env_set.c:423
static void env_set_add_nolock(struct env_set *es, const char *str)
Definition env_set.c:149
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:238
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:307
const char ** make_env_array(const struct env_set *es, const bool check_allowed, struct gc_arena *gc)
Definition env_set.c:440
void env_set_add(struct env_set *es, const char *str)
Definition env_set.c:193
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:127
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:359
const char * env_set_get(const struct env_set *es, const char *name)
Definition env_set.c:201
void env_set_inherit(struct env_set *es, const struct env_set *src)
Definition env_set.c:262
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:329
void setenv_str_safe(struct env_set *es, const char *name, const char *value)
Definition env_set.c:313
struct env_set * env_set_create(struct gc_arena *gc)
Definition env_set.c:156
bool env_allowed(const char *str)
Definition env_set.c:432
bool env_set_del(struct env_set *es, const char *str)
Definition env_set.c:183
void setenv_long_long(struct env_set *es, const char *name, long long value)
Definition env_set.c:299
static bool remove_env_item(const char *str, const bool do_free, struct env_item **list)
Definition env_set.c:94
void setenv_del(struct env_set *es, const char *name)
Definition env_set.c:352
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(unsigned int level)
Definition error.h:257
#define M_ERR
Definition error.h:104
#define msg(flags,...)
Definition error.h:150
#define ASSERT(x)
Definition error.h:217
#define M_WARN
Definition error.h:90
FILE * platform_fopen(const char *path, const char *mode)
Definition platform.c:504
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:60
int len
Length in bytes of the actual content within the allocated memory.
Definition buffer.h:65
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:116
struct env_set * es
struct gc_arena gc
Definition test_ssl.c:154