xref: /freebsd/lib/libc/stdlib/getenv.c (revision 559a218c9b257775fb249b67945fe4a05b7a6b9f)
12966d28cSSean Farley /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3d915a14eSPedro F. Giffuni  *
46da7f71cSSean Farley  * Copyright (c) 2007-2009 Sean C. Farley <scf@FreeBSD.org>
52966d28cSSean Farley  * All rights reserved.
658f0484fSRodney W. Grimes  *
758f0484fSRodney W. Grimes  * Redistribution and use in source and binary forms, with or without
858f0484fSRodney W. Grimes  * modification, are permitted provided that the following conditions
958f0484fSRodney W. Grimes  * are met:
1058f0484fSRodney W. Grimes  * 1. Redistributions of source code must retain the above copyright
112966d28cSSean Farley  *    notice, this list of conditions and the following disclaimer,
122966d28cSSean Farley  *    without modification, immediately at the beginning of the file.
1358f0484fSRodney W. Grimes  * 2. Redistributions in binary form must reproduce the above copyright
1458f0484fSRodney W. Grimes  *    notice, this list of conditions and the following disclaimer in the
1558f0484fSRodney W. Grimes  *    documentation and/or other materials provided with the distribution.
1658f0484fSRodney W. Grimes  *
172966d28cSSean Farley  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
182966d28cSSean Farley  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
192966d28cSSean Farley  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
202966d28cSSean Farley  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
212966d28cSSean Farley  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
222966d28cSSean Farley  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
232966d28cSSean Farley  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
242966d28cSSean Farley  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
252966d28cSSean Farley  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
262966d28cSSean Farley  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2758f0484fSRodney W. Grimes  */
282966d28cSSean Farley 
297f08f0ddSSean Farley #include "namespace.h"
307f08f0ddSSean Farley #include <sys/types.h>
317f08f0ddSSean Farley #include <errno.h>
327f08f0ddSSean Farley #include <stdbool.h>
337f08f0ddSSean Farley #include <stddef.h>
347f08f0ddSSean Farley #include <stdlib.h>
357f08f0ddSSean Farley #include <string.h>
367f08f0ddSSean Farley #include <unistd.h>
377f08f0ddSSean Farley #include "un-namespace.h"
380c6f0c0dSKonstantin Belousov #include "libc_private.h"
397f08f0ddSSean Farley 
407f08f0ddSSean Farley static const char CorruptEnvFindMsg[] = "environment corrupt; unable to find ";
419bab2367SSean Farley static const char CorruptEnvValueMsg[] =
427f08f0ddSSean Farley     "environment corrupt; missing value for ";
439bab2367SSean Farley 
449bab2367SSean Farley 
45283e0c0eSDavid Greenman /*
462966d28cSSean Farley  * Standard environ.  environ variable is exposed to entire process.
47283e0c0eSDavid Greenman  *
482966d28cSSean Farley  *	origEnviron:	Upon cleanup on unloading of library or failure, this
492966d28cSSean Farley  *			allows environ to return to as it was before.
502966d28cSSean Farley  *	environSize:	Number of variables environ can hold.  Can only
512966d28cSSean Farley  *			increase.
529bab2367SSean Farley  *	intEnviron:	Internally-built environ.  Exposed via environ during
539bab2367SSean Farley  *			(re)builds of the environment.
54283e0c0eSDavid Greenman  */
552966d28cSSean Farley static char **origEnviron;
569bab2367SSean Farley static char **intEnviron = NULL;
572966d28cSSean Farley static int environSize = 0;
58283e0c0eSDavid Greenman 
592966d28cSSean Farley /*
602966d28cSSean Farley  * Array of environment variables built from environ.  Each element records:
612966d28cSSean Farley  *	name:		Pointer to name=value string
622966d28cSSean Farley  *	name length:	Length of name not counting '=' character
632966d28cSSean Farley  *	value:		Pointer to value within same string as name
642966d28cSSean Farley  *	value size:	Size (not length) of space for value not counting the
652966d28cSSean Farley  *			nul character
662966d28cSSean Farley  *	active state:	true/false value to signify whether variable is active.
672966d28cSSean Farley  *			Useful since multiple variables with the same name can
682966d28cSSean Farley  *			co-exist.  At most, one variable can be active at any
692966d28cSSean Farley  *			one time.
702966d28cSSean Farley  *	putenv:		Created from putenv() call.  This memory must not be
712966d28cSSean Farley  *			reused.
722966d28cSSean Farley  */
732966d28cSSean Farley static struct envVars {
742966d28cSSean Farley 	size_t nameLen;
752966d28cSSean Farley 	size_t valueSize;
762966d28cSSean Farley 	char *name;
772966d28cSSean Farley 	char *value;
782966d28cSSean Farley 	bool active;
792966d28cSSean Farley 	bool putenv;
802966d28cSSean Farley } *envVars = NULL;
812966d28cSSean Farley 
822966d28cSSean Farley /*
832966d28cSSean Farley  * Environment array information.
842966d28cSSean Farley  *
852966d28cSSean Farley  *	envActive:	Number of active variables in array.
862966d28cSSean Farley  *	envVarsSize:	Size of array.
872966d28cSSean Farley  *	envVarsTotal:	Number of total variables in array (active or not).
882966d28cSSean Farley  */
892966d28cSSean Farley static int envActive = 0;
902966d28cSSean Farley static int envVarsSize = 0;
912966d28cSSean Farley static int envVarsTotal = 0;
922966d28cSSean Farley 
932966d28cSSean Farley 
942966d28cSSean Farley /* Deinitialization of new environment. */
959bab2367SSean Farley static void __attribute__ ((destructor)) __clean_env_destructor(void);
962966d28cSSean Farley 
972966d28cSSean Farley 
982966d28cSSean Farley /*
997f08f0ddSSean Farley  * A simple version of warnx() to avoid the bloat of including stdio in static
1007f08f0ddSSean Farley  * binaries.
1017f08f0ddSSean Farley  */
1027f08f0ddSSean Farley static void
__env_warnx(const char * msg,const char * name,size_t nameLen)1037f08f0ddSSean Farley __env_warnx(const char *msg, const char *name, size_t nameLen)
1047f08f0ddSSean Farley {
1057f08f0ddSSean Farley 	static const char nl[] = "\n";
1067f08f0ddSSean Farley 	static const char progSep[] = ": ";
1077f08f0ddSSean Farley 
1087f08f0ddSSean Farley 	_write(STDERR_FILENO, _getprogname(), strlen(_getprogname()));
1097f08f0ddSSean Farley 	_write(STDERR_FILENO, progSep, sizeof(progSep) - 1);
1107f08f0ddSSean Farley 	_write(STDERR_FILENO, msg, strlen(msg));
1117f08f0ddSSean Farley 	_write(STDERR_FILENO, name, nameLen);
1127f08f0ddSSean Farley 	_write(STDERR_FILENO, nl, sizeof(nl) - 1);
1137f08f0ddSSean Farley 
1147f08f0ddSSean Farley 	return;
1157f08f0ddSSean Farley }
1167f08f0ddSSean Farley 
1177f08f0ddSSean Farley 
1187f08f0ddSSean Farley /*
1192966d28cSSean Farley  * Inline strlen() for performance.  Also, perform check for an equals sign.
1208dcf5860SGordon Bergling  * Cheaper here than performing a strchr() later.
1212966d28cSSean Farley  */
1222966d28cSSean Farley static inline size_t
__strleneq(const char * str)1232966d28cSSean Farley __strleneq(const char *str)
1242966d28cSSean Farley {
1252966d28cSSean Farley 	const char *s;
1262966d28cSSean Farley 
1272966d28cSSean Farley 	for (s = str; *s != '\0'; ++s)
1282966d28cSSean Farley 		if (*s == '=')
1292966d28cSSean Farley 			return (0);
1302966d28cSSean Farley 
1312966d28cSSean Farley 	return (s - str);
132283e0c0eSDavid Greenman }
1332966d28cSSean Farley 
1342966d28cSSean Farley 
1352966d28cSSean Farley /*
1362966d28cSSean Farley  * Comparison of an environment name=value to a name.
1372966d28cSSean Farley  */
1382966d28cSSean Farley static inline bool
strncmpeq(const char * nameValue,const char * name,size_t nameLen)1392966d28cSSean Farley strncmpeq(const char *nameValue, const char *name, size_t nameLen)
1402966d28cSSean Farley {
1412966d28cSSean Farley 	if (strncmp(nameValue, name, nameLen) == 0 && nameValue[nameLen] == '=')
1422966d28cSSean Farley 		return (true);
1432966d28cSSean Farley 
1442966d28cSSean Farley 	return (false);
145283e0c0eSDavid Greenman }
1462966d28cSSean Farley 
1472966d28cSSean Farley 
1482966d28cSSean Farley /*
1492966d28cSSean Farley  * Using environment, returns pointer to value associated with name, if any,
1502966d28cSSean Farley  * else NULL.  If the onlyActive flag is set to true, only variables that are
1512966d28cSSean Farley  * active are returned else all are.
1522966d28cSSean Farley  */
1532966d28cSSean Farley static inline char *
__findenv(const char * name,size_t nameLen,int * envNdx,bool onlyActive)1542966d28cSSean Farley __findenv(const char *name, size_t nameLen, int *envNdx, bool onlyActive)
1552966d28cSSean Farley {
1562966d28cSSean Farley 	int ndx;
1572966d28cSSean Farley 
1582966d28cSSean Farley 	/*
1592966d28cSSean Farley 	 * Find environment variable from end of array (more likely to be
1606da7f71cSSean Farley 	 * active).  A variable created by putenv is always active, or it is not
1612966d28cSSean Farley 	 * tracked in the array.
1622966d28cSSean Farley 	 */
1632966d28cSSean Farley 	for (ndx = *envNdx; ndx >= 0; ndx--)
1642966d28cSSean Farley 		if (envVars[ndx].putenv) {
1652966d28cSSean Farley 			if (strncmpeq(envVars[ndx].name, name, nameLen)) {
1662966d28cSSean Farley 				*envNdx = ndx;
1672966d28cSSean Farley 				return (envVars[ndx].name + nameLen +
1682966d28cSSean Farley 				    sizeof ("=") - 1);
1692966d28cSSean Farley 			}
1702966d28cSSean Farley 		} else if ((!onlyActive || envVars[ndx].active) &&
1712966d28cSSean Farley 		    (envVars[ndx].nameLen == nameLen &&
1722966d28cSSean Farley 		    strncmpeq(envVars[ndx].name, name, nameLen))) {
1732966d28cSSean Farley 			*envNdx = ndx;
1742966d28cSSean Farley 			return (envVars[ndx].value);
1752966d28cSSean Farley 		}
1762966d28cSSean Farley 
177283e0c0eSDavid Greenman 	return (NULL);
178283e0c0eSDavid Greenman }
17958f0484fSRodney W. Grimes 
1802966d28cSSean Farley 
1812966d28cSSean Farley /*
1822966d28cSSean Farley  * Using environ, returns pointer to value associated with name, if any, else
1832966d28cSSean Farley  * NULL.  Used on the original environ passed into the program.
1842966d28cSSean Farley  */
1852966d28cSSean Farley static char *
__findenv_environ(const char * name,size_t nameLen)1862966d28cSSean Farley __findenv_environ(const char *name, size_t nameLen)
1872966d28cSSean Farley {
1882966d28cSSean Farley 	int envNdx;
1892966d28cSSean Farley 
1902966d28cSSean Farley 	/* Find variable within environ. */
1912966d28cSSean Farley 	for (envNdx = 0; environ[envNdx] != NULL; envNdx++)
1922966d28cSSean Farley 		if (strncmpeq(environ[envNdx], name, nameLen))
1932966d28cSSean Farley 			return (&(environ[envNdx][nameLen + sizeof("=") - 1]));
1942966d28cSSean Farley 
1952966d28cSSean Farley 	return (NULL);
1962966d28cSSean Farley }
1972966d28cSSean Farley 
1982966d28cSSean Farley 
1992966d28cSSean Farley /*
2009bab2367SSean Farley  * Remove variable added by putenv() from variable tracking array.
2019bab2367SSean Farley  */
2029bab2367SSean Farley static void
__remove_putenv(int envNdx)2039bab2367SSean Farley __remove_putenv(int envNdx)
2049bab2367SSean Farley {
2059bab2367SSean Farley 	envVarsTotal--;
2069bab2367SSean Farley 	if (envVarsTotal > envNdx)
2079bab2367SSean Farley 		memmove(&(envVars[envNdx]), &(envVars[envNdx + 1]),
2089bab2367SSean Farley 		    (envVarsTotal - envNdx) * sizeof (*envVars));
2099bab2367SSean Farley 	memset(&(envVars[envVarsTotal]), 0, sizeof (*envVars));
2109bab2367SSean Farley 
2119bab2367SSean Farley 	return;
2129bab2367SSean Farley }
2139bab2367SSean Farley 
2149bab2367SSean Farley 
2159bab2367SSean Farley /*
2169bab2367SSean Farley  * Deallocate the environment built from environ as well as environ then set
2179bab2367SSean Farley  * both to NULL.  Eases debugging of memory leaks.
2189bab2367SSean Farley  */
2199bab2367SSean Farley static void
__clean_env(bool freeVars)2209bab2367SSean Farley __clean_env(bool freeVars)
2219bab2367SSean Farley {
2229bab2367SSean Farley 	int envNdx;
2239bab2367SSean Farley 
2249bab2367SSean Farley 	/* Deallocate environment and environ if created by *env(). */
2259bab2367SSean Farley 	if (envVars != NULL) {
2269bab2367SSean Farley 		for (envNdx = envVarsTotal - 1; envNdx >= 0; envNdx--)
2279bab2367SSean Farley 			/* Free variables or deactivate them. */
2289bab2367SSean Farley 			if (envVars[envNdx].putenv) {
2299bab2367SSean Farley 				if (!freeVars)
2309bab2367SSean Farley 					__remove_putenv(envNdx);
2319bab2367SSean Farley 			} else {
2329bab2367SSean Farley 				if (freeVars)
2339bab2367SSean Farley 					free(envVars[envNdx].name);
2349bab2367SSean Farley 				else
2359bab2367SSean Farley 					envVars[envNdx].active = false;
2369bab2367SSean Farley 			}
2379bab2367SSean Farley 		if (freeVars) {
2389bab2367SSean Farley 			free(envVars);
2399bab2367SSean Farley 			envVars = NULL;
2409bab2367SSean Farley 		} else
2419bab2367SSean Farley 			envActive = 0;
2429bab2367SSean Farley 
2439bab2367SSean Farley 		/* Restore original environ if it has not updated by program. */
2449bab2367SSean Farley 		if (origEnviron != NULL) {
2459bab2367SSean Farley 			if (environ == intEnviron)
2469bab2367SSean Farley 				environ = origEnviron;
2479bab2367SSean Farley 			free(intEnviron);
2489bab2367SSean Farley 			intEnviron = NULL;
2499bab2367SSean Farley 			environSize = 0;
2509bab2367SSean Farley 		}
2519bab2367SSean Farley 	}
2529bab2367SSean Farley 
2539bab2367SSean Farley 	return;
2549bab2367SSean Farley }
2559bab2367SSean Farley 
2569bab2367SSean Farley 
2579bab2367SSean Farley /*
2582966d28cSSean Farley  * Using the environment, rebuild the environ array for use by other C library
2592966d28cSSean Farley  * calls that depend upon it.
2602966d28cSSean Farley  */
2612966d28cSSean Farley static int
__rebuild_environ(int newEnvironSize)2622966d28cSSean Farley __rebuild_environ(int newEnvironSize)
2632966d28cSSean Farley {
2642966d28cSSean Farley 	char **tmpEnviron;
2652966d28cSSean Farley 	int envNdx;
2662966d28cSSean Farley 	int environNdx;
2672966d28cSSean Farley 	int tmpEnvironSize;
2682966d28cSSean Farley 
2692966d28cSSean Farley 	/* Resize environ. */
2702966d28cSSean Farley 	if (newEnvironSize > environSize) {
2712966d28cSSean Farley 		tmpEnvironSize = newEnvironSize * 2;
2729f36610fSPedro F. Giffuni 		tmpEnviron = reallocarray(intEnviron, tmpEnvironSize + 1,
2739f36610fSPedro F. Giffuni 		    sizeof(*intEnviron));
2742966d28cSSean Farley 		if (tmpEnviron == NULL)
2752966d28cSSean Farley 			return (-1);
2762966d28cSSean Farley 		environSize = tmpEnvironSize;
2779bab2367SSean Farley 		intEnviron = tmpEnviron;
2782966d28cSSean Farley 	}
2792966d28cSSean Farley 	envActive = newEnvironSize;
2802966d28cSSean Farley 
2812966d28cSSean Farley 	/* Assign active variables to environ. */
2822966d28cSSean Farley 	for (envNdx = envVarsTotal - 1, environNdx = 0; envNdx >= 0; envNdx--)
2832966d28cSSean Farley 		if (envVars[envNdx].active)
2849bab2367SSean Farley 			intEnviron[environNdx++] = envVars[envNdx].name;
2859bab2367SSean Farley 	intEnviron[environNdx] = NULL;
2869bab2367SSean Farley 
2879bab2367SSean Farley 	/* Always set environ which may have been replaced by program. */
2889bab2367SSean Farley 	environ = intEnviron;
2892966d28cSSean Farley 
2902966d28cSSean Farley 	return (0);
2912966d28cSSean Farley }
2922966d28cSSean Farley 
2932966d28cSSean Farley 
2942966d28cSSean Farley /*
2952966d28cSSean Farley  * Enlarge new environment.
2962966d28cSSean Farley  */
2972966d28cSSean Farley static inline bool
__enlarge_env(void)2982966d28cSSean Farley __enlarge_env(void)
2992966d28cSSean Farley {
3002966d28cSSean Farley 	int newEnvVarsSize;
3012966d28cSSean Farley 	struct envVars *tmpEnvVars;
3022966d28cSSean Farley 
3032966d28cSSean Farley 	envVarsTotal++;
3042966d28cSSean Farley 	if (envVarsTotal > envVarsSize) {
3052966d28cSSean Farley 		newEnvVarsSize = envVarsTotal * 2;
3069f36610fSPedro F. Giffuni 		tmpEnvVars = reallocarray(envVars, newEnvVarsSize,
3079f36610fSPedro F. Giffuni 		    sizeof(*envVars));
3082966d28cSSean Farley 		if (tmpEnvVars == NULL) {
3092966d28cSSean Farley 			envVarsTotal--;
3102966d28cSSean Farley 			return (false);
3112966d28cSSean Farley 		}
3122966d28cSSean Farley 		envVarsSize = newEnvVarsSize;
3132966d28cSSean Farley 		envVars = tmpEnvVars;
3142966d28cSSean Farley 	}
3152966d28cSSean Farley 
3162966d28cSSean Farley 	return (true);
3172966d28cSSean Farley }
3182966d28cSSean Farley 
3192966d28cSSean Farley 
3202966d28cSSean Farley /*
3212966d28cSSean Farley  * Using environ, build an environment for use by standard C library calls.
3222966d28cSSean Farley  */
3232966d28cSSean Farley static int
__build_env(void)3242966d28cSSean Farley __build_env(void)
3252966d28cSSean Farley {
3262966d28cSSean Farley 	char **env;
3272966d28cSSean Farley 	int activeNdx;
3282966d28cSSean Farley 	int envNdx;
3292966d28cSSean Farley 	int savedErrno;
3302966d28cSSean Farley 	size_t nameLen;
3312966d28cSSean Farley 
3322966d28cSSean Farley 	/* Check for non-existant environment. */
3339bab2367SSean Farley 	if (environ == NULL || environ[0] == NULL)
3342966d28cSSean Farley 		return (0);
3352966d28cSSean Farley 
3362966d28cSSean Farley 	/* Count environment variables. */
3372966d28cSSean Farley 	for (env = environ, envVarsTotal = 0; *env != NULL; env++)
3382966d28cSSean Farley 		envVarsTotal++;
3392966d28cSSean Farley 	envVarsSize = envVarsTotal * 2;
3402966d28cSSean Farley 
3412966d28cSSean Farley 	/* Create new environment. */
342e449183cSPedro F. Giffuni 	envVars = calloc(envVarsSize, sizeof(*envVars));
3432966d28cSSean Farley 	if (envVars == NULL)
3442966d28cSSean Farley 		goto Failure;
3452966d28cSSean Farley 
3462966d28cSSean Farley 	/* Copy environ values and keep track of them. */
3472966d28cSSean Farley 	for (envNdx = envVarsTotal - 1; envNdx >= 0; envNdx--) {
3482966d28cSSean Farley 		envVars[envNdx].putenv = false;
3492966d28cSSean Farley 		envVars[envNdx].name =
3502966d28cSSean Farley 		    strdup(environ[envVarsTotal - envNdx - 1]);
3512966d28cSSean Farley 		if (envVars[envNdx].name == NULL)
3522966d28cSSean Farley 			goto Failure;
35356a3273eSBrian Feldman 		envVars[envNdx].value = strchr(envVars[envNdx].name, '=');
35456a3273eSBrian Feldman 		if (envVars[envNdx].value != NULL) {
35556a3273eSBrian Feldman 			envVars[envNdx].value++;
35656a3273eSBrian Feldman 			envVars[envNdx].valueSize =
35756a3273eSBrian Feldman 			    strlen(envVars[envNdx].value);
35856a3273eSBrian Feldman 		} else {
35956a3273eSBrian Feldman 			__env_warnx(CorruptEnvValueMsg, envVars[envNdx].name,
36056a3273eSBrian Feldman 			    strlen(envVars[envNdx].name));
361f959b43fSSean Farley 			errno = EFAULT;
362f959b43fSSean Farley 			goto Failure;
36356a3273eSBrian Feldman 		}
36456a3273eSBrian Feldman 
36558f0484fSRodney W. Grimes 		/*
3662966d28cSSean Farley 		 * Find most current version of variable to make active.  This
3672966d28cSSean Farley 		 * will prevent multiple active variables from being created
3682966d28cSSean Farley 		 * during this initialization phase.
3692966d28cSSean Farley 		 */
3702966d28cSSean Farley 		nameLen = envVars[envNdx].value - envVars[envNdx].name - 1;
3712966d28cSSean Farley 		envVars[envNdx].nameLen = nameLen;
3722966d28cSSean Farley 		activeNdx = envVarsTotal - 1;
3732966d28cSSean Farley 		if (__findenv(envVars[envNdx].name, nameLen, &activeNdx,
3742966d28cSSean Farley 		    false) == NULL) {
3757f08f0ddSSean Farley 			__env_warnx(CorruptEnvFindMsg, envVars[envNdx].name,
3767f08f0ddSSean Farley 			    nameLen);
377f959b43fSSean Farley 			errno = EFAULT;
378f959b43fSSean Farley 			goto Failure;
3792966d28cSSean Farley 		}
3802966d28cSSean Farley 		envVars[activeNdx].active = true;
3812966d28cSSean Farley 	}
3822966d28cSSean Farley 
3832966d28cSSean Farley 	/* Create a new environ. */
3842966d28cSSean Farley 	origEnviron = environ;
3852966d28cSSean Farley 	environ = NULL;
3869bab2367SSean Farley 	if (__rebuild_environ(envVarsTotal) == 0)
3879bab2367SSean Farley 		return (0);
3882966d28cSSean Farley 
3892966d28cSSean Farley Failure:
3902966d28cSSean Farley 	savedErrno = errno;
3919bab2367SSean Farley 	__clean_env(true);
3922966d28cSSean Farley 	errno = savedErrno;
3932966d28cSSean Farley 
3942966d28cSSean Farley 	return (-1);
3952966d28cSSean Farley }
3962966d28cSSean Farley 
3972966d28cSSean Farley 
3982966d28cSSean Farley /*
3999bab2367SSean Farley  * Destructor function with default argument to __clean_env().
4002966d28cSSean Farley  */
4012966d28cSSean Farley static void
__clean_env_destructor(void)4029bab2367SSean Farley __clean_env_destructor(void)
4032966d28cSSean Farley {
4049bab2367SSean Farley 	__clean_env(true);
4052966d28cSSean Farley 
4062966d28cSSean Farley 	return;
4072966d28cSSean Farley }
4082966d28cSSean Farley 
4092966d28cSSean Farley 
4102966d28cSSean Farley /*
4112966d28cSSean Farley  * Returns the value of a variable or NULL if none are found.
41258f0484fSRodney W. Grimes  */
41358f0484fSRodney W. Grimes char *
getenv(const char * name)4142966d28cSSean Farley getenv(const char *name)
41558f0484fSRodney W. Grimes {
4162966d28cSSean Farley 	int envNdx;
4172966d28cSSean Farley 	size_t nameLen;
41858f0484fSRodney W. Grimes 
4192966d28cSSean Farley 	/* Check for malformed name. */
4202966d28cSSean Farley 	if (name == NULL || (nameLen = __strleneq(name)) == 0) {
4212966d28cSSean Farley 		errno = EINVAL;
4222966d28cSSean Farley 		return (NULL);
4232966d28cSSean Farley 	}
4242966d28cSSean Farley 
4259bab2367SSean Farley 	/*
4266da7f71cSSean Farley 	 * Variable search order:
4276da7f71cSSean Farley 	 * 1. Check for an empty environ.  This allows an application to clear
4286da7f71cSSean Farley 	 *    the environment.
4296da7f71cSSean Farley 	 * 2. Search the external environ array.
4306da7f71cSSean Farley 	 * 3. Search the internal environment.
43156a3273eSBrian Feldman 	 *
4326da7f71cSSean Farley 	 * Since malloc() depends upon getenv(), getenv() must never cause the
4336da7f71cSSean Farley 	 * internal environment storage to be generated.
4349bab2367SSean Farley 	 */
43556a3273eSBrian Feldman 	if (environ == NULL || environ[0] == NULL)
43620f492f0SBrian Feldman 		return (NULL);
43756a3273eSBrian Feldman 	else if (envVars == NULL || environ != intEnviron)
43856a3273eSBrian Feldman 		return (__findenv_environ(name, nameLen));
43956a3273eSBrian Feldman 	else {
4402966d28cSSean Farley 		envNdx = envVarsTotal - 1;
4412966d28cSSean Farley 		return (__findenv(name, nameLen, &envNdx, true));
4422966d28cSSean Farley 	}
44356a3273eSBrian Feldman }
4442966d28cSSean Farley 
4452966d28cSSean Farley 
4462966d28cSSean Farley /*
447adeca214Slucy  * Runs getenv() unless the current process is tainted by uid or gid changes, in
448adeca214Slucy  * which case it will return NULL.
449adeca214Slucy  */
450adeca214Slucy char *
secure_getenv(const char * name)451adeca214Slucy secure_getenv(const char *name)
452adeca214Slucy {
453adeca214Slucy 	if (issetugid())
45472f501d0SWarner Losh 		return (NULL);
45572f501d0SWarner Losh 	return (getenv(name));
456adeca214Slucy }
457adeca214Slucy 
458adeca214Slucy /*
4592966d28cSSean Farley  * Set the value of a variable.  Older settings are labeled as inactive.  If an
4602966d28cSSean Farley  * older setting has enough room to store the new value, it will be reused.  No
4612966d28cSSean Farley  * previous variables are ever freed here to avoid causing a segmentation fault
4622966d28cSSean Farley  * in a user's code.
4639bab2367SSean Farley  *
4649bab2367SSean Farley  * The variables nameLen and valueLen are passed into here to allow the caller
4659bab2367SSean Farley  * to calculate the length by means besides just strlen().
4662966d28cSSean Farley  */
4679bab2367SSean Farley static int
__setenv(const char * name,size_t nameLen,const char * value,int overwrite)4689bab2367SSean Farley __setenv(const char *name, size_t nameLen, const char *value, int overwrite)
4692966d28cSSean Farley {
4702966d28cSSean Farley 	bool reuse;
4712966d28cSSean Farley 	char *env;
4722966d28cSSean Farley 	int envNdx;
4732966d28cSSean Farley 	int newEnvActive;
4742966d28cSSean Farley 	size_t valueLen;
4752966d28cSSean Farley 
4762966d28cSSean Farley 	/* Find existing environment variable large enough to use. */
4772966d28cSSean Farley 	envNdx = envVarsTotal - 1;
4782966d28cSSean Farley 	newEnvActive = envActive;
4792966d28cSSean Farley 	valueLen = strlen(value);
4802966d28cSSean Farley 	reuse = false;
4812966d28cSSean Farley 	if (__findenv(name, nameLen, &envNdx, false) != NULL) {
4822966d28cSSean Farley 		/* Deactivate entry if overwrite is allowed. */
4832966d28cSSean Farley 		if (envVars[envNdx].active) {
4842966d28cSSean Farley 			if (overwrite == 0)
4852966d28cSSean Farley 				return (0);
4862966d28cSSean Farley 			envVars[envNdx].active = false;
4872966d28cSSean Farley 			newEnvActive--;
4882966d28cSSean Farley 		}
4892966d28cSSean Farley 
4902966d28cSSean Farley 		/* putenv() created variable cannot be reused. */
4912966d28cSSean Farley 		if (envVars[envNdx].putenv)
4922966d28cSSean Farley 			__remove_putenv(envNdx);
4932966d28cSSean Farley 
4942966d28cSSean Farley 		/* Entry is large enough to reuse. */
4952966d28cSSean Farley 		else if (envVars[envNdx].valueSize >= valueLen)
4962966d28cSSean Farley 			reuse = true;
4972966d28cSSean Farley 	}
4982966d28cSSean Farley 
4992966d28cSSean Farley 	/* Create new variable if none was found of sufficient size. */
5002966d28cSSean Farley 	if (! reuse) {
5012966d28cSSean Farley 		/* Enlarge environment. */
5022966d28cSSean Farley 		envNdx = envVarsTotal;
5032966d28cSSean Farley 		if (!__enlarge_env())
5042966d28cSSean Farley 			return (-1);
5052966d28cSSean Farley 
5062966d28cSSean Farley 		/* Create environment entry. */
5072966d28cSSean Farley 		envVars[envNdx].name = malloc(nameLen + sizeof ("=") +
5082966d28cSSean Farley 		    valueLen);
5092966d28cSSean Farley 		if (envVars[envNdx].name == NULL) {
5102966d28cSSean Farley 			envVarsTotal--;
5112966d28cSSean Farley 			return (-1);
5122966d28cSSean Farley 		}
5132966d28cSSean Farley 		envVars[envNdx].nameLen = nameLen;
5142966d28cSSean Farley 		envVars[envNdx].valueSize = valueLen;
5152966d28cSSean Farley 
5162966d28cSSean Farley 		/* Save name of name/value pair. */
517e488ee55SAndriy Gapon 		env = stpncpy(envVars[envNdx].name, name, nameLen);
51822423fd8SAndriy Gapon 		*env++ = '=';
5192966d28cSSean Farley 	}
5202966d28cSSean Farley 	else
5212966d28cSSean Farley 		env = envVars[envNdx].value;
5222966d28cSSean Farley 
5232966d28cSSean Farley 	/* Save value of name/value pair. */
5242966d28cSSean Farley 	strcpy(env, value);
5252966d28cSSean Farley 	envVars[envNdx].value = env;
5262966d28cSSean Farley 	envVars[envNdx].active = true;
5272966d28cSSean Farley 	newEnvActive++;
5282966d28cSSean Farley 
52921c37696SSean Farley 	/* No need to rebuild environ if an active variable was reused. */
53021c37696SSean Farley 	if (reuse && newEnvActive == envActive)
5312966d28cSSean Farley 		return (0);
5322966d28cSSean Farley 	else
5332966d28cSSean Farley 		return (__rebuild_environ(newEnvActive));
5342966d28cSSean Farley }
5352966d28cSSean Farley 
5362966d28cSSean Farley 
5372966d28cSSean Farley /*
5389bab2367SSean Farley  * If the program attempts to replace the array of environment variables
5393522c38bSSean Farley  * (environ) environ or sets the first varible to NULL, then deactivate all
5403522c38bSSean Farley  * variables and merge in the new list from environ.
5419bab2367SSean Farley  */
5429bab2367SSean Farley static int
__merge_environ(void)5439bab2367SSean Farley __merge_environ(void)
5449bab2367SSean Farley {
5459bab2367SSean Farley 	char **env;
5469bab2367SSean Farley 	char *equals;
5479bab2367SSean Farley 
5483522c38bSSean Farley 	/*
549ee2889cbSSean Farley 	 * Internally-built environ has been replaced or cleared (detected by
550ee2889cbSSean Farley 	 * using the count of active variables against a NULL as the first value
551ee2889cbSSean Farley 	 * in environ).  Clean up everything.
5523522c38bSSean Farley 	 */
553ee2889cbSSean Farley 	if (intEnviron != NULL && (environ != intEnviron || (envActive > 0 &&
554ee2889cbSSean Farley 	    environ[0] == NULL))) {
5559bab2367SSean Farley 		/* Deactivate all environment variables. */
5569bab2367SSean Farley 		if (envActive > 0) {
5579bab2367SSean Farley 			origEnviron = NULL;
5589bab2367SSean Farley 			__clean_env(false);
5599bab2367SSean Farley 		}
5609bab2367SSean Farley 
5619bab2367SSean Farley 		/*
5629bab2367SSean Farley 		 * Insert new environ into existing, yet deactivated,
5639bab2367SSean Farley 		 * environment array.
5649bab2367SSean Farley 		 */
5659bab2367SSean Farley 		origEnviron = environ;
5669bab2367SSean Farley 		if (origEnviron != NULL)
5679bab2367SSean Farley 			for (env = origEnviron; *env != NULL; env++) {
5689bab2367SSean Farley 				if ((equals = strchr(*env, '=')) == NULL) {
5697f08f0ddSSean Farley 					__env_warnx(CorruptEnvValueMsg, *env,
5707f08f0ddSSean Farley 					    strlen(*env));
571f959b43fSSean Farley 					errno = EFAULT;
572f959b43fSSean Farley 					return (-1);
5739bab2367SSean Farley 				}
5749bab2367SSean Farley 				if (__setenv(*env, equals - *env, equals + 1,
5759bab2367SSean Farley 				    1) == -1)
5769bab2367SSean Farley 					return (-1);
5779bab2367SSean Farley 			}
5789bab2367SSean Farley 	}
5799bab2367SSean Farley 
5809bab2367SSean Farley 	return (0);
5819bab2367SSean Farley }
5829bab2367SSean Farley 
5839bab2367SSean Farley 
5849bab2367SSean Farley /*
5858dcf5860SGordon Bergling  * The exposed setenv() that performs a few tests before calling the function
5869bab2367SSean Farley  * (__setenv()) that does the actual work of inserting a variable into the
5879bab2367SSean Farley  * environment.
5889bab2367SSean Farley  */
5899bab2367SSean Farley int
setenv(const char * name,const char * value,int overwrite)5909bab2367SSean Farley setenv(const char *name, const char *value, int overwrite)
5919bab2367SSean Farley {
5929bab2367SSean Farley 	size_t nameLen;
5939bab2367SSean Farley 
5949bab2367SSean Farley 	/* Check for malformed name. */
5959bab2367SSean Farley 	if (name == NULL || (nameLen = __strleneq(name)) == 0) {
5969bab2367SSean Farley 		errno = EINVAL;
5979bab2367SSean Farley 		return (-1);
5989bab2367SSean Farley 	}
5999bab2367SSean Farley 
6009bab2367SSean Farley 	/* Initialize environment. */
6019bab2367SSean Farley 	if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1))
6029bab2367SSean Farley 		return (-1);
6039bab2367SSean Farley 
6049bab2367SSean Farley 	return (__setenv(name, nameLen, value, overwrite));
6059bab2367SSean Farley }
6069bab2367SSean Farley 
6079bab2367SSean Farley 
6089bab2367SSean Farley /*
6099bab2367SSean Farley  * Insert a "name=value" string into the environment.  Special settings must be
6102966d28cSSean Farley  * made to keep setenv() from reusing this memory block and unsetenv() from
6112966d28cSSean Farley  * allowing it to be tracked.
6122966d28cSSean Farley  */
6132966d28cSSean Farley int
putenv(char * string)6142966d28cSSean Farley putenv(char *string)
6152966d28cSSean Farley {
6162966d28cSSean Farley 	char *equals;
6172966d28cSSean Farley 	int envNdx;
6182966d28cSSean Farley 	int newEnvActive;
6192966d28cSSean Farley 	size_t nameLen;
6202966d28cSSean Farley 
6212966d28cSSean Farley 	/* Check for malformed argument. */
6222966d28cSSean Farley 	if (string == NULL || (equals = strchr(string, '=')) == NULL ||
6232966d28cSSean Farley 	    (nameLen = equals - string) == 0) {
6242966d28cSSean Farley 		errno = EINVAL;
6252966d28cSSean Farley 		return (-1);
6262966d28cSSean Farley 	}
6272966d28cSSean Farley 
6282966d28cSSean Farley 	/* Initialize environment. */
6299bab2367SSean Farley 	if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1))
6302966d28cSSean Farley 		return (-1);
6312966d28cSSean Farley 
6322966d28cSSean Farley 	/* Deactivate previous environment variable. */
6332966d28cSSean Farley 	envNdx = envVarsTotal - 1;
6342966d28cSSean Farley 	newEnvActive = envActive;
6352966d28cSSean Farley 	if (__findenv(string, nameLen, &envNdx, true) != NULL) {
6362966d28cSSean Farley 		/* Reuse previous putenv slot. */
6372966d28cSSean Farley 		if (envVars[envNdx].putenv) {
6382966d28cSSean Farley 			envVars[envNdx].name = string;
6392966d28cSSean Farley 			return (__rebuild_environ(envActive));
6402966d28cSSean Farley 		} else {
6412966d28cSSean Farley 			newEnvActive--;
6422966d28cSSean Farley 			envVars[envNdx].active = false;
6432966d28cSSean Farley 		}
6442966d28cSSean Farley 	}
6452966d28cSSean Farley 
6462966d28cSSean Farley 	/* Enlarge environment. */
6472966d28cSSean Farley 	envNdx = envVarsTotal;
6482966d28cSSean Farley 	if (!__enlarge_env())
6492966d28cSSean Farley 		return (-1);
6502966d28cSSean Farley 
6512966d28cSSean Farley 	/* Create environment entry. */
6522966d28cSSean Farley 	envVars[envNdx].name = string;
6532966d28cSSean Farley 	envVars[envNdx].nameLen = -1;
6542966d28cSSean Farley 	envVars[envNdx].value = NULL;
6552966d28cSSean Farley 	envVars[envNdx].valueSize = -1;
6562966d28cSSean Farley 	envVars[envNdx].putenv = true;
6572966d28cSSean Farley 	envVars[envNdx].active = true;
6582966d28cSSean Farley 	newEnvActive++;
6592966d28cSSean Farley 
6602966d28cSSean Farley 	return (__rebuild_environ(newEnvActive));
6612966d28cSSean Farley }
6622966d28cSSean Farley 
6632966d28cSSean Farley 
6642966d28cSSean Farley /*
6652966d28cSSean Farley  * Unset variable with the same name by flagging it as inactive.  No variable is
6662966d28cSSean Farley  * ever freed.
6672966d28cSSean Farley  */
6682966d28cSSean Farley int
unsetenv(const char * name)6692966d28cSSean Farley unsetenv(const char *name)
6702966d28cSSean Farley {
6712966d28cSSean Farley 	int envNdx;
6722966d28cSSean Farley 	size_t nameLen;
673542e1349SAndrey A. Chernov 	int newEnvActive;
6742966d28cSSean Farley 
6752966d28cSSean Farley 	/* Check for malformed name. */
6762966d28cSSean Farley 	if (name == NULL || (nameLen = __strleneq(name)) == 0) {
6772966d28cSSean Farley 		errno = EINVAL;
6782966d28cSSean Farley 		return (-1);
6792966d28cSSean Farley 	}
6802966d28cSSean Farley 
6812966d28cSSean Farley 	/* Initialize environment. */
6829bab2367SSean Farley 	if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1))
6832966d28cSSean Farley 		return (-1);
6842966d28cSSean Farley 
6852966d28cSSean Farley 	/* Deactivate specified variable. */
6862de80aeaSAndrey A. Chernov 	/* Remove all occurrences. */
687542e1349SAndrey A. Chernov 	envNdx = envVarsTotal - 1;
688542e1349SAndrey A. Chernov 	newEnvActive = envActive;
6892de80aeaSAndrey A. Chernov 	while (__findenv(name, nameLen, &envNdx, true) != NULL) {
6902966d28cSSean Farley 		envVars[envNdx].active = false;
6912966d28cSSean Farley 		if (envVars[envNdx].putenv)
6922966d28cSSean Farley 			__remove_putenv(envNdx);
693542e1349SAndrey A. Chernov 		envNdx--;
694542e1349SAndrey A. Chernov 		newEnvActive--;
6952966d28cSSean Farley 	}
696542e1349SAndrey A. Chernov 	if (newEnvActive != envActive)
697542e1349SAndrey A. Chernov 		__rebuild_environ(newEnvActive);
6982966d28cSSean Farley 
6992966d28cSSean Farley 	return (0);
70058f0484fSRodney W. Grimes }
701597b0267SMariusz Zaborski 
702597b0267SMariusz Zaborski /*
703597b0267SMariusz Zaborski  * Unset all variable by flagging them as inactive.  No variable is
704597b0267SMariusz Zaborski  * ever freed.
705597b0267SMariusz Zaborski  */
706597b0267SMariusz Zaborski int
clearenv(void)707597b0267SMariusz Zaborski clearenv(void)
708597b0267SMariusz Zaborski {
709597b0267SMariusz Zaborski 	int ndx;
710597b0267SMariusz Zaborski 
711597b0267SMariusz Zaborski 	/* Initialize environment. */
712597b0267SMariusz Zaborski 	if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1))
713597b0267SMariusz Zaborski 		return (-1);
714597b0267SMariusz Zaborski 
715597b0267SMariusz Zaborski 	/* Remove from the end to not shuffle memory too much. */
716597b0267SMariusz Zaborski 	for (ndx = envVarsTotal - 1; ndx >= 0; ndx--) {
717597b0267SMariusz Zaborski 		envVars[ndx].active = false;
718597b0267SMariusz Zaborski 		if (envVars[ndx].putenv)
719597b0267SMariusz Zaborski 			__remove_putenv(ndx);
720597b0267SMariusz Zaborski 	}
721597b0267SMariusz Zaborski 
722597b0267SMariusz Zaborski 	__rebuild_environ(0);
723597b0267SMariusz Zaborski 
724597b0267SMariusz Zaborski 	return (0);
725597b0267SMariusz Zaborski }
726