/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2007-2009 Sean C. Farley <[email protected]>4* All rights reserved.5*6* Redistribution and use in source and binary forms, with or without7* modification, are permitted provided that the following conditions8* are met:9* 1. Redistributions of source code must retain the above copyright10* notice, this list of conditions and the following disclaimer,11* without modification, immediately at the beginning of the file.12* 2. Redistributions in binary form must reproduce the above copyright13* notice, this list of conditions and the following disclaimer in the14* documentation and/or other materials provided with the distribution.15*16* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR17* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES18* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.19* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,20* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT21* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,22* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY23* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT24* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF25* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.26*/2728#include "namespace.h"29#include <sys/types.h>30#include <ssp/ssp.h>31#include <errno.h>32#include <stdbool.h>33#include <stddef.h>34#include <stdlib.h>35#include <string.h>36#include <unistd.h>37#include "un-namespace.h"38#include "libc_private.h"3940static const char CorruptEnvFindMsg[] = "environment corrupt; unable to find ";41static const char CorruptEnvValueMsg[] =42"environment corrupt; missing value for ";434445/*46* Standard environ. environ variable is exposed to entire process.47*48* origEnviron: Upon cleanup on unloading of library or failure, this49* allows environ to return to as it was before.50* environSize: Number of variables environ can hold. Can only51* increase.52* intEnviron: Internally-built environ. Exposed via environ during53* (re)builds of the environment.54*/55static char **origEnviron;56static char **intEnviron = NULL;57static int environSize = 0;5859/*60* Array of environment variables built from environ. Each element records:61* name: Pointer to name=value string62* name length: Length of name not counting '=' character63* value: Pointer to value within same string as name64* value size: Size (not length) of space for value not counting the65* nul character66* active state: true/false value to signify whether variable is active.67* Useful since multiple variables with the same name can68* co-exist. At most, one variable can be active at any69* one time.70* putenv: Created from putenv() call. This memory must not be71* reused.72*/73static struct envVars {74size_t nameLen;75size_t valueSize;76char *name;77char *value;78bool active;79bool putenv;80} *envVars = NULL;8182/*83* Environment array information.84*85* envActive: Number of active variables in array.86* envVarsSize: Size of array.87* envVarsTotal: Number of total variables in array (active or not).88*/89static int envActive = 0;90static int envVarsSize = 0;91static int envVarsTotal = 0;929394/* Deinitialization of new environment. */95static void __attribute__ ((destructor)) __clean_env_destructor(void);969798/*99* A simple version of warnx() to avoid the bloat of including stdio in static100* binaries.101*/102static void103__env_warnx(const char *msg, const char *name, size_t nameLen)104{105static const char nl[] = "\n";106static const char progSep[] = ": ";107108_write(STDERR_FILENO, _getprogname(), strlen(_getprogname()));109_write(STDERR_FILENO, progSep, sizeof(progSep) - 1);110_write(STDERR_FILENO, msg, strlen(msg));111_write(STDERR_FILENO, name, nameLen);112_write(STDERR_FILENO, nl, sizeof(nl) - 1);113114return;115}116117118/*119* Inline strlen() for performance. Also, perform check for an equals sign.120* Cheaper here than performing a strchr() later.121*/122static inline size_t123__strleneq(const char *str)124{125const char *s;126127for (s = str; *s != '\0'; ++s)128if (*s == '=')129return (0);130131return (s - str);132}133134135/*136* Comparison of an environment name=value to a name.137*/138static inline bool139strncmpeq(const char *nameValue, const char *name, size_t nameLen)140{141if (strncmp(nameValue, name, nameLen) == 0 && nameValue[nameLen] == '=')142return (true);143144return (false);145}146147148/*149* Using environment, returns pointer to value associated with name, if any,150* else NULL. If the onlyActive flag is set to true, only variables that are151* active are returned else all are.152*/153static inline char *154__findenv(const char *name, size_t nameLen, int *envNdx, bool onlyActive)155{156int ndx;157158/*159* Find environment variable from end of array (more likely to be160* active). A variable created by putenv is always active, or it is not161* tracked in the array.162*/163for (ndx = *envNdx; ndx >= 0; ndx--)164if (envVars[ndx].putenv) {165if (strncmpeq(envVars[ndx].name, name, nameLen)) {166*envNdx = ndx;167return (envVars[ndx].name + nameLen +168sizeof ("=") - 1);169}170} else if ((!onlyActive || envVars[ndx].active) &&171(envVars[ndx].nameLen == nameLen &&172strncmpeq(envVars[ndx].name, name, nameLen))) {173*envNdx = ndx;174return (envVars[ndx].value);175}176177return (NULL);178}179180181/*182* Using environ, returns pointer to value associated with name, if any, else183* NULL. Used on the original environ passed into the program.184*/185static char *186__findenv_environ(const char *name, size_t nameLen)187{188int envNdx;189190/* Find variable within environ. */191for (envNdx = 0; environ[envNdx] != NULL; envNdx++)192if (strncmpeq(environ[envNdx], name, nameLen))193return (&(environ[envNdx][nameLen + sizeof("=") - 1]));194195return (NULL);196}197198199/*200* Remove variable added by putenv() from variable tracking array.201*/202static void203__remove_putenv(int envNdx)204{205envVarsTotal--;206if (envVarsTotal > envNdx)207memmove(&(envVars[envNdx]), &(envVars[envNdx + 1]),208(envVarsTotal - envNdx) * sizeof (*envVars));209memset(&(envVars[envVarsTotal]), 0, sizeof (*envVars));210211return;212}213214215/*216* Deallocate the environment built from environ as well as environ then set217* both to NULL. Eases debugging of memory leaks.218*/219static void220__clean_env(bool freeVars)221{222int envNdx;223224/* Deallocate environment and environ if created by *env(). */225if (envVars != NULL) {226for (envNdx = envVarsTotal - 1; envNdx >= 0; envNdx--)227/* Free variables or deactivate them. */228if (envVars[envNdx].putenv) {229if (!freeVars)230__remove_putenv(envNdx);231} else {232if (freeVars)233free(envVars[envNdx].name);234else235envVars[envNdx].active = false;236}237if (freeVars) {238free(envVars);239envVars = NULL;240} else241envActive = 0;242243/* Restore original environ if it has not updated by program. */244if (origEnviron != NULL) {245if (environ == intEnviron)246environ = origEnviron;247free(intEnviron);248intEnviron = NULL;249environSize = 0;250}251}252253return;254}255256257/*258* Using the environment, rebuild the environ array for use by other C library259* calls that depend upon it.260*/261static int262__rebuild_environ(int newEnvironSize)263{264char **tmpEnviron;265int envNdx;266int environNdx;267int tmpEnvironSize;268269/* Resize environ. */270if (newEnvironSize > environSize) {271tmpEnvironSize = newEnvironSize * 2;272tmpEnviron = reallocarray(intEnviron, tmpEnvironSize + 1,273sizeof(*intEnviron));274if (tmpEnviron == NULL)275return (-1);276environSize = tmpEnvironSize;277intEnviron = tmpEnviron;278}279envActive = newEnvironSize;280281/* Assign active variables to environ. */282for (envNdx = envVarsTotal - 1, environNdx = 0; envNdx >= 0; envNdx--)283if (envVars[envNdx].active)284intEnviron[environNdx++] = envVars[envNdx].name;285intEnviron[environNdx] = NULL;286287/* Always set environ which may have been replaced by program. */288environ = intEnviron;289290return (0);291}292293294/*295* Enlarge new environment.296*/297static inline bool298__enlarge_env(void)299{300int newEnvVarsSize;301struct envVars *tmpEnvVars;302303envVarsTotal++;304if (envVarsTotal > envVarsSize) {305newEnvVarsSize = envVarsTotal * 2;306tmpEnvVars = reallocarray(envVars, newEnvVarsSize,307sizeof(*envVars));308if (tmpEnvVars == NULL) {309envVarsTotal--;310return (false);311}312envVarsSize = newEnvVarsSize;313envVars = tmpEnvVars;314}315316return (true);317}318319320/*321* Using environ, build an environment for use by standard C library calls.322*/323static int324__build_env(void)325{326char **env;327int activeNdx;328int envNdx;329int savedErrno;330size_t nameLen;331332/* Check for non-existant environment. */333if (environ == NULL || environ[0] == NULL)334return (0);335336/* Count environment variables. */337for (env = environ, envVarsTotal = 0; *env != NULL; env++)338envVarsTotal++;339envVarsSize = envVarsTotal * 2;340341/* Create new environment. */342envVars = calloc(envVarsSize, sizeof(*envVars));343if (envVars == NULL)344goto Failure;345346/* Copy environ values and keep track of them. */347for (envNdx = envVarsTotal - 1; envNdx >= 0; envNdx--) {348envVars[envNdx].putenv = false;349envVars[envNdx].name =350strdup(environ[envVarsTotal - envNdx - 1]);351if (envVars[envNdx].name == NULL)352goto Failure;353envVars[envNdx].value = strchr(envVars[envNdx].name, '=');354if (envVars[envNdx].value != NULL) {355envVars[envNdx].value++;356envVars[envNdx].valueSize =357strlen(envVars[envNdx].value);358} else {359__env_warnx(CorruptEnvValueMsg, envVars[envNdx].name,360strlen(envVars[envNdx].name));361errno = EFAULT;362goto Failure;363}364365/*366* Find most current version of variable to make active. This367* will prevent multiple active variables from being created368* during this initialization phase.369*/370nameLen = envVars[envNdx].value - envVars[envNdx].name - 1;371envVars[envNdx].nameLen = nameLen;372activeNdx = envVarsTotal - 1;373if (__findenv(envVars[envNdx].name, nameLen, &activeNdx,374false) == NULL) {375__env_warnx(CorruptEnvFindMsg, envVars[envNdx].name,376nameLen);377errno = EFAULT;378goto Failure;379}380envVars[activeNdx].active = true;381}382383/* Create a new environ. */384origEnviron = environ;385environ = NULL;386if (__rebuild_environ(envVarsTotal) == 0)387return (0);388389Failure:390savedErrno = errno;391__clean_env(true);392errno = savedErrno;393394return (-1);395}396397398/*399* Destructor function with default argument to __clean_env().400*/401static void402__clean_env_destructor(void)403{404__clean_env(true);405406return;407}408409410/*411* Returns the value of a variable or NULL if none are found.412*/413char *414getenv(const char *name)415{416int envNdx;417size_t nameLen;418419/* Check for malformed name. */420if (name == NULL || (nameLen = __strleneq(name)) == 0) {421errno = EINVAL;422return (NULL);423}424425/*426* Variable search order:427* 1. Check for an empty environ. This allows an application to clear428* the environment.429* 2. Search the external environ array.430* 3. Search the internal environment.431*432* Since malloc() depends upon getenv(), getenv() must never cause the433* internal environment storage to be generated.434*/435if (environ == NULL || environ[0] == NULL)436return (NULL);437else if (envVars == NULL || environ != intEnviron)438return (__findenv_environ(name, nameLen));439else {440envNdx = envVarsTotal - 1;441return (__findenv(name, nameLen, &envNdx, true));442}443}444445446/*447* Like getenv(), but copies the value into the provided buffer.448*/449int450__ssp_real(getenv_r)(const char *name, char *buf, size_t len)451{452const char *val;453size_t nameLen;454int envNdx;455456if (name == NULL || (nameLen = __strleneq(name)) == 0) {457errno = EINVAL;458return (-1);459}460461if (environ == NULL || environ[0] == NULL) {462val = NULL;463} else if (envVars == NULL || environ != intEnviron) {464val = __findenv_environ(name, nameLen);465} else {466envNdx = envVarsTotal - 1;467val = __findenv(name, nameLen, &envNdx, true);468}469if (val == NULL) {470errno = ENOENT;471return (-1);472}473if (strlcpy(buf, val, len) >= len) {474errno = ERANGE;475return (-1);476}477return (0);478}479480481/*482* Runs getenv() unless the current process is tainted by uid or gid changes, in483* which case it will return NULL.484*/485char *486secure_getenv(const char *name)487{488if (issetugid())489return (NULL);490return (getenv(name));491}492493/*494* Set the value of a variable. Older settings are labeled as inactive. If an495* older setting has enough room to store the new value, it will be reused. No496* previous variables are ever freed here to avoid causing a segmentation fault497* in a user's code.498*499* The variables nameLen and valueLen are passed into here to allow the caller500* to calculate the length by means besides just strlen().501*/502static int503__setenv(const char *name, size_t nameLen, const char *value, int overwrite)504{505bool reuse;506char *env;507int envNdx;508int newEnvActive;509size_t valueLen;510511/* Find existing environment variable large enough to use. */512envNdx = envVarsTotal - 1;513newEnvActive = envActive;514valueLen = strlen(value);515reuse = false;516if (__findenv(name, nameLen, &envNdx, false) != NULL) {517/* Deactivate entry if overwrite is allowed. */518if (envVars[envNdx].active) {519if (overwrite == 0)520return (0);521envVars[envNdx].active = false;522newEnvActive--;523}524525/* putenv() created variable cannot be reused. */526if (envVars[envNdx].putenv)527__remove_putenv(envNdx);528529/* Entry is large enough to reuse. */530else if (envVars[envNdx].valueSize >= valueLen)531reuse = true;532}533534/* Create new variable if none was found of sufficient size. */535if (! reuse) {536/* Enlarge environment. */537envNdx = envVarsTotal;538if (!__enlarge_env())539return (-1);540541/* Create environment entry. */542envVars[envNdx].name = malloc(nameLen + sizeof ("=") +543valueLen);544if (envVars[envNdx].name == NULL) {545envVarsTotal--;546return (-1);547}548envVars[envNdx].nameLen = nameLen;549envVars[envNdx].valueSize = valueLen;550551/* Save name of name/value pair. */552env = stpncpy(envVars[envNdx].name, name, nameLen);553*env++ = '=';554}555else556env = envVars[envNdx].value;557558/* Save value of name/value pair. */559strcpy(env, value);560envVars[envNdx].value = env;561envVars[envNdx].active = true;562newEnvActive++;563564/* No need to rebuild environ if an active variable was reused. */565if (reuse && newEnvActive == envActive)566return (0);567else568return (__rebuild_environ(newEnvActive));569}570571572/*573* If the program attempts to replace the array of environment variables574* (environ) environ or sets the first varible to NULL, then deactivate all575* variables and merge in the new list from environ.576*/577static int578__merge_environ(void)579{580char **env;581char *equals;582583/*584* Internally-built environ has been replaced or cleared (detected by585* using the count of active variables against a NULL as the first value586* in environ). Clean up everything.587*/588if (intEnviron != NULL && (environ != intEnviron || (envActive > 0 &&589environ[0] == NULL))) {590/* Deactivate all environment variables. */591if (envActive > 0) {592origEnviron = NULL;593__clean_env(false);594}595596/*597* Insert new environ into existing, yet deactivated,598* environment array.599*/600origEnviron = environ;601if (origEnviron != NULL)602for (env = origEnviron; *env != NULL; env++) {603if ((equals = strchr(*env, '=')) == NULL) {604__env_warnx(CorruptEnvValueMsg, *env,605strlen(*env));606errno = EFAULT;607return (-1);608}609if (__setenv(*env, equals - *env, equals + 1,6101) == -1)611return (-1);612}613}614615return (0);616}617618619/*620* The exposed setenv() that performs a few tests before calling the function621* (__setenv()) that does the actual work of inserting a variable into the622* environment.623*/624int625setenv(const char *name, const char *value, int overwrite)626{627size_t nameLen;628629/* Check for malformed name. */630if (name == NULL || (nameLen = __strleneq(name)) == 0) {631errno = EINVAL;632return (-1);633}634635/* Initialize environment. */636if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1))637return (-1);638639return (__setenv(name, nameLen, value, overwrite));640}641642643/*644* Insert a "name=value" string into the environment. Special settings must be645* made to keep setenv() from reusing this memory block and unsetenv() from646* allowing it to be tracked.647*/648int649putenv(char *string)650{651char *equals;652int envNdx;653int newEnvActive;654size_t nameLen;655656/* Check for malformed argument. */657if (string == NULL || (equals = strchr(string, '=')) == NULL ||658(nameLen = equals - string) == 0) {659errno = EINVAL;660return (-1);661}662663/* Initialize environment. */664if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1))665return (-1);666667/* Deactivate previous environment variable. */668envNdx = envVarsTotal - 1;669newEnvActive = envActive;670if (__findenv(string, nameLen, &envNdx, true) != NULL) {671/* Reuse previous putenv slot. */672if (envVars[envNdx].putenv) {673envVars[envNdx].name = string;674return (__rebuild_environ(envActive));675} else {676newEnvActive--;677envVars[envNdx].active = false;678}679}680681/* Enlarge environment. */682envNdx = envVarsTotal;683if (!__enlarge_env())684return (-1);685686/* Create environment entry. */687envVars[envNdx].name = string;688envVars[envNdx].nameLen = -1;689envVars[envNdx].value = NULL;690envVars[envNdx].valueSize = -1;691envVars[envNdx].putenv = true;692envVars[envNdx].active = true;693newEnvActive++;694695return (__rebuild_environ(newEnvActive));696}697698699/*700* Unset variable with the same name by flagging it as inactive. No variable is701* ever freed.702*/703int704unsetenv(const char *name)705{706int envNdx;707size_t nameLen;708int newEnvActive;709710/* Check for malformed name. */711if (name == NULL || (nameLen = __strleneq(name)) == 0) {712errno = EINVAL;713return (-1);714}715716/* Initialize environment. */717if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1))718return (-1);719720/* Deactivate specified variable. */721/* Remove all occurrences. */722envNdx = envVarsTotal - 1;723newEnvActive = envActive;724while (__findenv(name, nameLen, &envNdx, true) != NULL) {725envVars[envNdx].active = false;726if (envVars[envNdx].putenv)727__remove_putenv(envNdx);728envNdx--;729newEnvActive--;730}731if (newEnvActive != envActive)732__rebuild_environ(newEnvActive);733734return (0);735}736737/*738* Unset all variable by flagging them as inactive. No variable is739* ever freed.740*/741int742clearenv(void)743{744int ndx;745746/* Initialize environment. */747if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1))748return (-1);749750/* Remove from the end to not shuffle memory too much. */751for (ndx = envVarsTotal - 1; ndx >= 0; ndx--) {752envVars[ndx].active = false;753if (envVars[ndx].putenv)754__remove_putenv(ndx);755}756757__rebuild_environ(0);758759return (0);760}761762763