mirror of
https://github.com/OpenVPN/openvpn.git
synced 2025-05-09 13:41:06 +08:00

Update performed by means of: dev-tools/update-copyright.sh Cc: David Sommerseth <davids@openvpn.net> Signed-off-by: Antonio Quartulli <a@unstable.cc> Acked-by: David Sommerseth <davids@openvpn.net> Message-Id: <20220125142456.18176-1-a@unstable.cc> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg23650.html Signed-off-by: Gert Doering <gert@greenie.muc.de>
460 lines
10 KiB
C
460 lines
10 KiB
C
/*
|
|
* OpenVPN -- An application to securely tunnel IP networks
|
|
* over a single TCP/UDP port, with support for SSL/TLS-based
|
|
* session authentication and key exchange,
|
|
* packet encryption, packet authentication, and
|
|
* packet compression.
|
|
*
|
|
* Copyright (C) 2002-2022 OpenVPN Technologies, Inc. <sales@openvpn.net>
|
|
* Copyright (C) 2014-2015 David Sommerseth <davids@redhat.com>
|
|
* Copyright (C) 2016-2022 David Sommerseth <davids@openvpn.net>
|
|
*
|
|
* 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 (see the file COPYING included with this
|
|
* distribution); if not, write to the Free Software Foundation, Inc.,
|
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#elif defined(_MSC_VER)
|
|
#include "config-msvc.h"
|
|
#endif
|
|
|
|
#include "syshead.h"
|
|
|
|
#include "env_set.h"
|
|
|
|
#include "run_command.h"
|
|
|
|
/*
|
|
* Set environmental variable (int or string).
|
|
*
|
|
* On Posix, we use putenv for portability,
|
|
* and put up with its painful semantics
|
|
* that require all the support code below.
|
|
*/
|
|
|
|
/* General-purpose environmental variable set functions */
|
|
|
|
static char *
|
|
construct_name_value(const char *name, const char *value, struct gc_arena *gc)
|
|
{
|
|
struct buffer out;
|
|
|
|
ASSERT(name);
|
|
if (!value)
|
|
{
|
|
value = "";
|
|
}
|
|
out = alloc_buf_gc(strlen(name) + strlen(value) + 2, gc);
|
|
buf_printf(&out, "%s=%s", name, value);
|
|
return BSTR(&out);
|
|
}
|
|
|
|
static bool
|
|
env_string_equal(const char *s1, const char *s2)
|
|
{
|
|
int c1, c2;
|
|
ASSERT(s1);
|
|
ASSERT(s2);
|
|
|
|
while (true)
|
|
{
|
|
c1 = *s1++;
|
|
c2 = *s2++;
|
|
if (c1 == '=')
|
|
{
|
|
c1 = 0;
|
|
}
|
|
if (c2 == '=')
|
|
{
|
|
c2 = 0;
|
|
}
|
|
if (!c1 && !c2)
|
|
{
|
|
return true;
|
|
}
|
|
if (c1 != c2)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool
|
|
remove_env_item(const char *str, const bool do_free, struct env_item **list)
|
|
{
|
|
struct env_item *current, *prev;
|
|
|
|
ASSERT(str);
|
|
ASSERT(list);
|
|
|
|
for (current = *list, prev = NULL; current != NULL; current = current->next)
|
|
{
|
|
if (env_string_equal(current->string, str))
|
|
{
|
|
if (prev)
|
|
{
|
|
prev->next = current->next;
|
|
}
|
|
else
|
|
{
|
|
*list = current->next;
|
|
}
|
|
if (do_free)
|
|
{
|
|
secure_memzero(current->string, strlen(current->string));
|
|
free(current->string);
|
|
free(current);
|
|
}
|
|
return true;
|
|
}
|
|
prev = current;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void
|
|
add_env_item(char *str, const bool do_alloc, struct env_item **list, struct gc_arena *gc)
|
|
{
|
|
struct env_item *item;
|
|
|
|
ASSERT(str);
|
|
ASSERT(list);
|
|
|
|
ALLOC_OBJ_GC(item, struct env_item, gc);
|
|
item->string = do_alloc ? string_alloc(str, gc) : str;
|
|
item->next = *list;
|
|
*list = item;
|
|
}
|
|
|
|
/* struct env_set functions */
|
|
|
|
static bool
|
|
env_set_del_nolock(struct env_set *es, const char *str)
|
|
{
|
|
return remove_env_item(str, es->gc == NULL, &es->list);
|
|
}
|
|
|
|
static void
|
|
env_set_add_nolock(struct env_set *es, const char *str)
|
|
{
|
|
remove_env_item(str, es->gc == NULL, &es->list);
|
|
add_env_item((char *)str, true, &es->list, es->gc);
|
|
}
|
|
|
|
struct env_set *
|
|
env_set_create(struct gc_arena *gc)
|
|
{
|
|
struct env_set *es;
|
|
ALLOC_OBJ_CLEAR_GC(es, struct env_set, gc);
|
|
es->list = NULL;
|
|
es->gc = gc;
|
|
return es;
|
|
}
|
|
|
|
void
|
|
env_set_destroy(struct env_set *es)
|
|
{
|
|
if (es && es->gc == NULL)
|
|
{
|
|
struct env_item *e = es->list;
|
|
while (e)
|
|
{
|
|
struct env_item *next = e->next;
|
|
free(e->string);
|
|
free(e);
|
|
e = next;
|
|
}
|
|
free(es);
|
|
}
|
|
}
|
|
|
|
bool
|
|
env_set_del(struct env_set *es, const char *str)
|
|
{
|
|
bool ret;
|
|
ASSERT(es);
|
|
ASSERT(str);
|
|
ret = env_set_del_nolock(es, str);
|
|
return ret;
|
|
}
|
|
|
|
void
|
|
env_set_add(struct env_set *es, const char *str)
|
|
{
|
|
ASSERT(es);
|
|
ASSERT(str);
|
|
env_set_add_nolock(es, str);
|
|
}
|
|
|
|
const char *
|
|
env_set_get(const struct env_set *es, const char *name)
|
|
{
|
|
const struct env_item *item = es->list;
|
|
while (item && !env_string_equal(item->string, name))
|
|
{
|
|
item = item->next;
|
|
}
|
|
return item ? item->string : NULL;
|
|
}
|
|
|
|
void
|
|
env_set_print(int msglevel, const struct env_set *es)
|
|
{
|
|
if (check_debug_level(msglevel))
|
|
{
|
|
const struct env_item *e;
|
|
int i;
|
|
|
|
if (es)
|
|
{
|
|
e = es->list;
|
|
i = 0;
|
|
|
|
while (e)
|
|
{
|
|
if (env_safe_to_print(e->string))
|
|
{
|
|
msg(msglevel, "ENV [%d] '%s'", i, e->string);
|
|
}
|
|
++i;
|
|
e = e->next;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
env_set_inherit(struct env_set *es, const struct env_set *src)
|
|
{
|
|
const struct env_item *e;
|
|
|
|
ASSERT(es);
|
|
|
|
if (src)
|
|
{
|
|
e = src->list;
|
|
while (e)
|
|
{
|
|
env_set_add_nolock(es, e->string);
|
|
e = e->next;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* add/modify/delete environmental strings */
|
|
|
|
void
|
|
setenv_counter(struct env_set *es, const char *name, counter_type value)
|
|
{
|
|
char buf[64];
|
|
openvpn_snprintf(buf, sizeof(buf), counter_format, value);
|
|
setenv_str(es, name, buf);
|
|
}
|
|
|
|
void
|
|
setenv_int(struct env_set *es, const char *name, int value)
|
|
{
|
|
char buf[64];
|
|
openvpn_snprintf(buf, sizeof(buf), "%d", value);
|
|
setenv_str(es, name, buf);
|
|
}
|
|
|
|
void
|
|
setenv_long_long(struct env_set *es, const char *name, long long value)
|
|
{
|
|
char buf[64];
|
|
openvpn_snprintf(buf, sizeof(buf), "%" PRIi64, (int64_t)value);
|
|
setenv_str(es, name, buf);
|
|
}
|
|
|
|
void
|
|
setenv_str(struct env_set *es, const char *name, const char *value)
|
|
{
|
|
setenv_str_ex(es, name, value, CC_NAME, 0, 0, CC_PRINT, 0, 0);
|
|
}
|
|
|
|
void
|
|
setenv_str_safe(struct env_set *es, const char *name, const char *value)
|
|
{
|
|
uint8_t b[64];
|
|
struct buffer buf;
|
|
buf_set_write(&buf, b, sizeof(b));
|
|
if (buf_printf(&buf, "OPENVPN_%s", name))
|
|
{
|
|
setenv_str(es, BSTR(&buf), value);
|
|
}
|
|
else
|
|
{
|
|
msg(M_WARN, "setenv_str_safe: name overflow");
|
|
}
|
|
}
|
|
|
|
void
|
|
setenv_str_incr(struct env_set *es, const char *name, const char *value)
|
|
{
|
|
unsigned int counter = 1;
|
|
const size_t tmpname_len = strlen(name) + 5; /* 3 digits counter max */
|
|
char *tmpname = gc_malloc(tmpname_len, true, NULL);
|
|
strcpy(tmpname, name);
|
|
while (NULL != env_set_get(es, tmpname) && counter < 1000)
|
|
{
|
|
ASSERT(openvpn_snprintf(tmpname, tmpname_len, "%s_%u", name, counter));
|
|
counter++;
|
|
}
|
|
if (counter < 1000)
|
|
{
|
|
setenv_str(es, tmpname, value);
|
|
}
|
|
else
|
|
{
|
|
msg(D_TLS_DEBUG_MED, "Too many same-name env variables, ignoring: %s", name);
|
|
}
|
|
free(tmpname);
|
|
}
|
|
|
|
void
|
|
setenv_del(struct env_set *es, const char *name)
|
|
{
|
|
ASSERT(name);
|
|
setenv_str(es, name, NULL);
|
|
}
|
|
|
|
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)
|
|
{
|
|
struct gc_arena gc = gc_new();
|
|
const char *name_tmp;
|
|
const char *val_tmp = NULL;
|
|
|
|
ASSERT(name && strlen(name) > 1);
|
|
|
|
name_tmp = string_mod_const(name, name_include, name_exclude, name_replace, &gc);
|
|
|
|
if (value)
|
|
{
|
|
val_tmp = string_mod_const(value, value_include, value_exclude, value_replace, &gc);
|
|
}
|
|
|
|
ASSERT(es);
|
|
|
|
if (val_tmp)
|
|
{
|
|
const char *str = construct_name_value(name_tmp, val_tmp, &gc);
|
|
env_set_add(es, str);
|
|
#if DEBUG_VERBOSE_SETENV
|
|
msg(M_INFO, "SETENV_ES '%s'", str);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
env_set_del(es, name_tmp);
|
|
}
|
|
|
|
gc_free(&gc);
|
|
}
|
|
|
|
/*
|
|
* Setenv functions that append an integer index to the name
|
|
*/
|
|
static const char *
|
|
setenv_format_indexed_name(const char *name, const int i, struct gc_arena *gc)
|
|
{
|
|
struct buffer out = alloc_buf_gc(strlen(name) + 16, gc);
|
|
if (i >= 0)
|
|
{
|
|
buf_printf(&out, "%s_%d", name, i);
|
|
}
|
|
else
|
|
{
|
|
buf_printf(&out, "%s", name);
|
|
}
|
|
return BSTR(&out);
|
|
}
|
|
|
|
void
|
|
setenv_int_i(struct env_set *es, const char *name, const int value, const int i)
|
|
{
|
|
struct gc_arena gc = gc_new();
|
|
const char *name_str = setenv_format_indexed_name(name, i, &gc);
|
|
setenv_int(es, name_str, value);
|
|
gc_free(&gc);
|
|
}
|
|
|
|
void
|
|
setenv_str_i(struct env_set *es, const char *name, const char *value, const int i)
|
|
{
|
|
struct gc_arena gc = gc_new();
|
|
const char *name_str = setenv_format_indexed_name(name, i, &gc);
|
|
setenv_str(es, name_str, value);
|
|
gc_free(&gc);
|
|
}
|
|
|
|
bool
|
|
env_allowed(const char *str)
|
|
{
|
|
return (script_security() >= SSEC_PW_ENV || !is_password_env_var(str));
|
|
}
|
|
|
|
/* Make arrays of strings */
|
|
|
|
const char **
|
|
make_env_array(const struct env_set *es,
|
|
const bool check_allowed,
|
|
struct gc_arena *gc)
|
|
{
|
|
char **ret = NULL;
|
|
struct env_item *e = NULL;
|
|
int i = 0, n = 0;
|
|
|
|
/* figure length of es */
|
|
if (es)
|
|
{
|
|
for (e = es->list; e != NULL; e = e->next)
|
|
{
|
|
++n;
|
|
}
|
|
}
|
|
|
|
/* alloc return array */
|
|
ALLOC_ARRAY_CLEAR_GC(ret, char *, n+1, gc);
|
|
|
|
/* fill return array */
|
|
if (es)
|
|
{
|
|
i = 0;
|
|
for (e = es->list; e != NULL; e = e->next)
|
|
{
|
|
if (!check_allowed || env_allowed(e->string))
|
|
{
|
|
ASSERT(i < n);
|
|
ret[i++] = e->string;
|
|
}
|
|
}
|
|
}
|
|
|
|
ret[i] = NULL;
|
|
return (const char **)ret;
|
|
}
|