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