#include "namespace.h"
#include <sys/types.h>
#include <ssp/ssp.h>
#include <errno.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "un-namespace.h"
#include "libc_private.h"
static const char CorruptEnvFindMsg[] = "environment corrupt; unable to find ";
static const char CorruptEnvValueMsg[] =
"environment corrupt; missing value for ";
static char **origEnviron;
static char **intEnviron = NULL;
static int environSize = 0;
static struct envVars {
size_t nameLen;
size_t valueSize;
char *name;
char *value;
bool active;
bool putenv;
} *envVars = NULL;
static int envActive = 0;
static int envVarsSize = 0;
static int envVarsTotal = 0;
static void __attribute__ ((destructor)) __clean_env_destructor(void);
static void
__env_warnx(const char *msg, const char *name, size_t nameLen)
{
static const char nl[] = "\n";
static const char progSep[] = ": ";
_write(STDERR_FILENO, _getprogname(), strlen(_getprogname()));
_write(STDERR_FILENO, progSep, sizeof(progSep) - 1);
_write(STDERR_FILENO, msg, strlen(msg));
_write(STDERR_FILENO, name, nameLen);
_write(STDERR_FILENO, nl, sizeof(nl) - 1);
return;
}
static inline size_t
__strleneq(const char *str)
{
const char *s;
for (s = str; *s != '\0'; ++s)
if (*s == '=')
return (0);
return (s - str);
}
static inline bool
strncmpeq(const char *nameValue, const char *name, size_t nameLen)
{
if (strncmp(nameValue, name, nameLen) == 0 && nameValue[nameLen] == '=')
return (true);
return (false);
}
static inline char *
__findenv(const char *name, size_t nameLen, int *envNdx, bool onlyActive)
{
int ndx;
for (ndx = *envNdx; ndx >= 0; ndx--)
if (envVars[ndx].putenv) {
if (strncmpeq(envVars[ndx].name, name, nameLen)) {
*envNdx = ndx;
return (envVars[ndx].name + nameLen +
sizeof ("=") - 1);
}
} else if ((!onlyActive || envVars[ndx].active) &&
(envVars[ndx].nameLen == nameLen &&
strncmpeq(envVars[ndx].name, name, nameLen))) {
*envNdx = ndx;
return (envVars[ndx].value);
}
return (NULL);
}
static char *
__findenv_environ(const char *name, size_t nameLen)
{
int envNdx;
for (envNdx = 0; environ[envNdx] != NULL; envNdx++)
if (strncmpeq(environ[envNdx], name, nameLen))
return (&(environ[envNdx][nameLen + sizeof("=") - 1]));
return (NULL);
}
static void
__remove_putenv(int envNdx)
{
envVarsTotal--;
if (envVarsTotal > envNdx)
memmove(&(envVars[envNdx]), &(envVars[envNdx + 1]),
(envVarsTotal - envNdx) * sizeof (*envVars));
memset(&(envVars[envVarsTotal]), 0, sizeof (*envVars));
return;
}
static void
__clean_env(bool freeVars)
{
int envNdx;
if (envVars != NULL) {
for (envNdx = envVarsTotal - 1; envNdx >= 0; envNdx--)
if (envVars[envNdx].putenv) {
if (!freeVars)
__remove_putenv(envNdx);
} else {
if (freeVars)
free(envVars[envNdx].name);
else
envVars[envNdx].active = false;
}
if (freeVars) {
free(envVars);
envVars = NULL;
} else
envActive = 0;
if (origEnviron != NULL) {
if (environ == intEnviron)
environ = origEnviron;
free(intEnviron);
intEnviron = NULL;
environSize = 0;
}
}
return;
}
static int
__rebuild_environ(int newEnvironSize)
{
char **tmpEnviron;
int envNdx;
int environNdx;
int tmpEnvironSize;
if (newEnvironSize > environSize) {
tmpEnvironSize = newEnvironSize * 2;
tmpEnviron = reallocarray(intEnviron, tmpEnvironSize + 1,
sizeof(*intEnviron));
if (tmpEnviron == NULL)
return (-1);
environSize = tmpEnvironSize;
intEnviron = tmpEnviron;
}
envActive = newEnvironSize;
for (envNdx = envVarsTotal - 1, environNdx = 0; envNdx >= 0; envNdx--)
if (envVars[envNdx].active)
intEnviron[environNdx++] = envVars[envNdx].name;
intEnviron[environNdx] = NULL;
environ = intEnviron;
return (0);
}
static inline bool
__enlarge_env(void)
{
int newEnvVarsSize;
struct envVars *tmpEnvVars;
envVarsTotal++;
if (envVarsTotal > envVarsSize) {
newEnvVarsSize = envVarsTotal * 2;
tmpEnvVars = reallocarray(envVars, newEnvVarsSize,
sizeof(*envVars));
if (tmpEnvVars == NULL) {
envVarsTotal--;
return (false);
}
envVarsSize = newEnvVarsSize;
envVars = tmpEnvVars;
}
return (true);
}
static int
__build_env(void)
{
char **env;
int activeNdx;
int envNdx;
int savedErrno;
size_t nameLen;
if (environ == NULL || environ[0] == NULL)
return (0);
for (env = environ, envVarsTotal = 0; *env != NULL; env++)
envVarsTotal++;
envVarsSize = envVarsTotal * 2;
envVars = calloc(envVarsSize, sizeof(*envVars));
if (envVars == NULL)
goto Failure;
for (envNdx = envVarsTotal - 1; envNdx >= 0; envNdx--) {
envVars[envNdx].putenv = false;
envVars[envNdx].name =
strdup(environ[envVarsTotal - envNdx - 1]);
if (envVars[envNdx].name == NULL)
goto Failure;
envVars[envNdx].value = strchr(envVars[envNdx].name, '=');
if (envVars[envNdx].value != NULL) {
envVars[envNdx].value++;
envVars[envNdx].valueSize =
strlen(envVars[envNdx].value);
} else {
__env_warnx(CorruptEnvValueMsg, envVars[envNdx].name,
strlen(envVars[envNdx].name));
errno = EFAULT;
goto Failure;
}
nameLen = envVars[envNdx].value - envVars[envNdx].name - 1;
envVars[envNdx].nameLen = nameLen;
activeNdx = envVarsTotal - 1;
if (__findenv(envVars[envNdx].name, nameLen, &activeNdx,
false) == NULL) {
__env_warnx(CorruptEnvFindMsg, envVars[envNdx].name,
nameLen);
errno = EFAULT;
goto Failure;
}
envVars[activeNdx].active = true;
}
origEnviron = environ;
environ = NULL;
if (__rebuild_environ(envVarsTotal) == 0)
return (0);
Failure:
savedErrno = errno;
__clean_env(true);
errno = savedErrno;
return (-1);
}
static void
__clean_env_destructor(void)
{
__clean_env(true);
return;
}
char *
getenv(const char *name)
{
int envNdx;
size_t nameLen;
if (name == NULL || (nameLen = __strleneq(name)) == 0) {
errno = EINVAL;
return (NULL);
}
if (environ == NULL || environ[0] == NULL)
return (NULL);
else if (envVars == NULL || environ != intEnviron)
return (__findenv_environ(name, nameLen));
else {
envNdx = envVarsTotal - 1;
return (__findenv(name, nameLen, &envNdx, true));
}
}
int
__ssp_real(getenv_r)(const char *name, char *buf, size_t len)
{
const char *val;
size_t nameLen;
int envNdx;
if (name == NULL || (nameLen = __strleneq(name)) == 0) {
errno = EINVAL;
return (-1);
}
if (environ == NULL || environ[0] == NULL) {
val = NULL;
} else if (envVars == NULL || environ != intEnviron) {
val = __findenv_environ(name, nameLen);
} else {
envNdx = envVarsTotal - 1;
val = __findenv(name, nameLen, &envNdx, true);
}
if (val == NULL) {
errno = ENOENT;
return (-1);
}
if (strlcpy(buf, val, len) >= len) {
errno = ERANGE;
return (-1);
}
return (0);
}
char *
secure_getenv(const char *name)
{
if (issetugid())
return (NULL);
return (getenv(name));
}
static int
__setenv(const char *name, size_t nameLen, const char *value, int overwrite)
{
bool reuse;
char *env;
int envNdx;
int newEnvActive;
size_t valueLen;
envNdx = envVarsTotal - 1;
newEnvActive = envActive;
valueLen = strlen(value);
reuse = false;
if (__findenv(name, nameLen, &envNdx, false) != NULL) {
if (envVars[envNdx].active) {
if (overwrite == 0)
return (0);
envVars[envNdx].active = false;
newEnvActive--;
}
if (envVars[envNdx].putenv)
__remove_putenv(envNdx);
else if (envVars[envNdx].valueSize >= valueLen)
reuse = true;
}
if (! reuse) {
envNdx = envVarsTotal;
if (!__enlarge_env())
return (-1);
envVars[envNdx].name = malloc(nameLen + sizeof ("=") +
valueLen);
if (envVars[envNdx].name == NULL) {
envVarsTotal--;
return (-1);
}
envVars[envNdx].nameLen = nameLen;
envVars[envNdx].valueSize = valueLen;
env = stpncpy(envVars[envNdx].name, name, nameLen);
*env++ = '=';
}
else
env = envVars[envNdx].value;
strcpy(env, value);
envVars[envNdx].value = env;
envVars[envNdx].active = true;
newEnvActive++;
if (reuse && newEnvActive == envActive)
return (0);
else
return (__rebuild_environ(newEnvActive));
}
static int
__merge_environ(void)
{
char **env;
char *equals;
if (intEnviron != NULL && (environ != intEnviron || (envActive > 0 &&
environ[0] == NULL))) {
if (envActive > 0) {
origEnviron = NULL;
__clean_env(false);
}
origEnviron = environ;
if (origEnviron != NULL)
for (env = origEnviron; *env != NULL; env++) {
if ((equals = strchr(*env, '=')) == NULL) {
__env_warnx(CorruptEnvValueMsg, *env,
strlen(*env));
errno = EFAULT;
return (-1);
}
if (__setenv(*env, equals - *env, equals + 1,
1) == -1)
return (-1);
}
}
return (0);
}
int
setenv(const char *name, const char *value, int overwrite)
{
size_t nameLen;
if (name == NULL || (nameLen = __strleneq(name)) == 0) {
errno = EINVAL;
return (-1);
}
if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1))
return (-1);
return (__setenv(name, nameLen, value, overwrite));
}
int
putenv(char *string)
{
char *equals;
int envNdx;
int newEnvActive;
size_t nameLen;
if (string == NULL || (equals = strchr(string, '=')) == NULL ||
(nameLen = equals - string) == 0) {
errno = EINVAL;
return (-1);
}
if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1))
return (-1);
envNdx = envVarsTotal - 1;
newEnvActive = envActive;
if (__findenv(string, nameLen, &envNdx, true) != NULL) {
if (envVars[envNdx].putenv) {
envVars[envNdx].name = string;
return (__rebuild_environ(envActive));
} else {
newEnvActive--;
envVars[envNdx].active = false;
}
}
envNdx = envVarsTotal;
if (!__enlarge_env())
return (-1);
envVars[envNdx].name = string;
envVars[envNdx].nameLen = -1;
envVars[envNdx].value = NULL;
envVars[envNdx].valueSize = -1;
envVars[envNdx].putenv = true;
envVars[envNdx].active = true;
newEnvActive++;
return (__rebuild_environ(newEnvActive));
}
int
unsetenv(const char *name)
{
int envNdx;
size_t nameLen;
int newEnvActive;
if (name == NULL || (nameLen = __strleneq(name)) == 0) {
errno = EINVAL;
return (-1);
}
if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1))
return (-1);
envNdx = envVarsTotal - 1;
newEnvActive = envActive;
while (__findenv(name, nameLen, &envNdx, true) != NULL) {
envVars[envNdx].active = false;
if (envVars[envNdx].putenv)
__remove_putenv(envNdx);
envNdx--;
newEnvActive--;
}
if (newEnvActive != envActive)
__rebuild_environ(newEnvActive);
return (0);
}
int
clearenv(void)
{
int ndx;
if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1))
return (-1);
for (ndx = envVarsTotal - 1; ndx >= 0; ndx--) {
envVars[ndx].active = false;
if (envVars[ndx].putenv)
__remove_putenv(ndx);
}
__rebuild_environ(0);
return (0);
}