68b97b25 |
/*
* 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)
{ |