#include <config.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#include <errno.h>
#include "sudo.h"
#include "sudo_plugin.h"
#include "sudo_dso.h"
extern char **environ;
static char **priv_environ;
char *
getenv_unhooked(const char *name)
{
char **ep, *val = NULL;
size_t namelen = 0;
while (name[namelen] != '\0' && name[namelen] != '=')
namelen++;
for (ep = environ; *ep != NULL; ep++) {
if (strncmp(*ep, name, namelen) == 0 && (*ep)[namelen] == '=') {
val = *ep + namelen + 1;
break;
}
}
return val;
}
__dso_public char *
getenv(const char *name)
{
char *val = NULL;
switch (process_hooks_getenv(name, &val)) {
case SUDO_HOOK_RET_STOP:
return val;
case SUDO_HOOK_RET_ERROR:
return NULL;
default:
return getenv_unhooked(name);
}
}
static int
rpl_putenv(PUTENV_CONST char *string)
{
char **ep;
size_t len;
bool found = false;
len = (strchr(string, '=') - string) + 1;
for (ep = environ; *ep != NULL; ep++) {
if (strncmp(string, *ep, len) == 0) {
*ep = (char *)string;
found = true;
break;
}
}
if (found) {
while (*ep != NULL) {
if (strncmp(string, *ep, len) == 0) {
char **cur = ep;
while ((*cur = *(cur + 1)) != NULL)
cur++;
} else {
ep++;
}
}
}
if (!found) {
size_t env_len = (size_t)(ep - environ);
char **envp = reallocarray(priv_environ, env_len + 2, sizeof(char *));
if (envp == NULL)
return -1;
if (environ != priv_environ)
memcpy(envp, environ, env_len * sizeof(char *));
envp[env_len++] = (char *)string;
envp[env_len] = NULL;
priv_environ = environ = envp;
}
return 0;
}
typedef int (*sudo_fn_putenv_t)(PUTENV_CONST char *);
static int
putenv_unhooked(PUTENV_CONST char *string)
{
sudo_fn_putenv_t fn;
fn = (sudo_fn_putenv_t)sudo_dso_findsym(SUDO_DSO_NEXT, "putenv");
if (fn != NULL)
return fn(string);
return rpl_putenv(string);
}
__dso_public int
putenv(PUTENV_CONST char *string)
{
switch (process_hooks_putenv((char *)string)) {
case SUDO_HOOK_RET_STOP:
return 0;
case SUDO_HOOK_RET_ERROR:
return -1;
default:
return putenv_unhooked(string);
}
}
static int
rpl_setenv(const char *var, const char *val, int overwrite)
{
char *envstr, *dst;
const char *src;
size_t esize;
if (!var || *var == '\0') {
errno = EINVAL;
return -1;
}
for (src = var; *src != '\0' && *src != '='; src++)
;
esize = (size_t)(src - var) + 2;
if (val) {
esize += strlen(val);
}
if ((envstr = malloc(esize)) == NULL)
return -1;
for (src = var, dst = envstr; *src != '\0' && *src != '=';)
*dst++ = *src++;
*dst++ = '=';
if (val) {
for (src = val; *src != '\0';)
*dst++ = *src++;
}
*dst = '\0';
if (!overwrite && getenv(var) != NULL) {
free(envstr);
return 0;
}
if (rpl_putenv(envstr) == -1) {
free(envstr);
return -1;
}
return 0;
}
typedef int (*sudo_fn_setenv_t)(const char *, const char *, int);
static int
setenv_unhooked(const char *var, const char *val, int overwrite)
{
sudo_fn_setenv_t fn;
fn = (sudo_fn_setenv_t)sudo_dso_findsym(SUDO_DSO_NEXT, "setenv");
if (fn != NULL)
return fn(var, val, overwrite);
return rpl_setenv(var, val, overwrite);
}
__dso_public int
setenv(const char *var, const char *val, int overwrite)
{
switch (process_hooks_setenv(var, val, overwrite)) {
case SUDO_HOOK_RET_STOP:
return 0;
case SUDO_HOOK_RET_ERROR:
return -1;
default:
return setenv_unhooked(var, val, overwrite);
}
}
static int
rpl_unsetenv(const char *var)
{
char **ep = environ;
size_t len;
if (var == NULL || *var == '\0' || strchr(var, '=') != NULL) {
errno = EINVAL;
return -1;
}
len = strlen(var);
while (*ep != NULL) {
if (strncmp(var, *ep, len) == 0 && (*ep)[len] == '=') {
char **cur = ep;
while ((*cur = *(cur + 1)) != NULL)
cur++;
} else {
ep++;
}
}
return 0;
}
#ifdef UNSETENV_VOID
typedef void (*sudo_fn_unsetenv_t)(const char *);
#else
typedef int (*sudo_fn_unsetenv_t)(const char *);
#endif
static int
unsetenv_unhooked(const char *var)
{
int rval = 0;
sudo_fn_unsetenv_t fn;
fn = (sudo_fn_unsetenv_t)sudo_dso_findsym(SUDO_DSO_NEXT, "unsetenv");
if (fn != NULL) {
# ifdef UNSETENV_VOID
fn(var);
# else
rval = fn(var);
# endif
} else {
rval = rpl_unsetenv(var);
}
return rval;
}
#ifdef UNSETENV_VOID
__dso_public void
#else
__dso_public int
#endif
unsetenv(const char *var)
{
int rval;
switch (process_hooks_unsetenv(var)) {
case SUDO_HOOK_RET_STOP:
rval = 0;
break;
case SUDO_HOOK_RET_ERROR:
rval = -1;
break;
default:
rval = unsetenv_unhooked(var);
break;
}
#ifndef UNSETENV_VOID
return rval;
#endif
}