1/*-
2 * Copyright (c) 2007-2009 Sean C. Farley <scf@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer,
10 *    without modification, immediately at the beginning of the file.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: stable/11/lib/libc/stdlib/getenv.c 316613 2017-04-07 16:08:04Z pfg $");
29
30
31#include "namespace.h"
32#include <sys/types.h>
33#include <errno.h>
34#include <stdbool.h>
35#include <stddef.h>
36#include <stdlib.h>
37#include <string.h>
38#include <unistd.h>
39#include "un-namespace.h"
40
41
42static const char CorruptEnvFindMsg[] = "environment corrupt; unable to find ";
43static const char CorruptEnvValueMsg[] =
44    "environment corrupt; missing value for ";
45
46
47/*
48 * Standard environ.  environ variable is exposed to entire process.
49 *
50 *	origEnviron:	Upon cleanup on unloading of library or failure, this
51 *			allows environ to return to as it was before.
52 *	environSize:	Number of variables environ can hold.  Can only
53 *			increase.
54 *	intEnviron:	Internally-built environ.  Exposed via environ during
55 *			(re)builds of the environment.
56 */
57extern char **environ;
58static char **origEnviron;
59static char **intEnviron = NULL;
60static int environSize = 0;
61
62/*
63 * Array of environment variables built from environ.  Each element records:
64 *	name:		Pointer to name=value string
65 *	name length:	Length of name not counting '=' character
66 *	value:		Pointer to value within same string as name
67 *	value size:	Size (not length) of space for value not counting the
68 *			nul character
69 *	active state:	true/false value to signify whether variable is active.
70 *			Useful since multiple variables with the same name can
71 *			co-exist.  At most, one variable can be active at any
72 *			one time.
73 *	putenv:		Created from putenv() call.  This memory must not be
74 *			reused.
75 */
76static struct envVars {
77	size_t nameLen;
78	size_t valueSize;
79	char *name;
80	char *value;
81	bool active;
82	bool putenv;
83} *envVars = NULL;
84
85/*
86 * Environment array information.
87 *
88 *	envActive:	Number of active variables in array.
89 *	envVarsSize:	Size of array.
90 *	envVarsTotal:	Number of total variables in array (active or not).
91 */
92static int envActive = 0;
93static int envVarsSize = 0;
94static int envVarsTotal = 0;
95
96
97/* Deinitialization of new environment. */
98static void __attribute__ ((destructor)) __clean_env_destructor(void);
99
100
101/*
102 * A simple version of warnx() to avoid the bloat of including stdio in static
103 * binaries.
104 */
105static void
106__env_warnx(const char *msg, const char *name, size_t nameLen)
107{
108	static const char nl[] = "\n";
109	static const char progSep[] = ": ";
110
111	_write(STDERR_FILENO, _getprogname(), strlen(_getprogname()));
112	_write(STDERR_FILENO, progSep, sizeof(progSep) - 1);
113	_write(STDERR_FILENO, msg, strlen(msg));
114	_write(STDERR_FILENO, name, nameLen);
115	_write(STDERR_FILENO, nl, sizeof(nl) - 1);
116
117	return;
118}
119
120
121/*
122 * Inline strlen() for performance.  Also, perform check for an equals sign.
123 * Cheaper here than peforming a strchr() later.
124 */
125static inline size_t
126__strleneq(const char *str)
127{
128	const char *s;
129
130	for (s = str; *s != '\0'; ++s)
131		if (*s == '=')
132			return (0);
133
134	return (s - str);
135}
136
137
138/*
139 * Comparison of an environment name=value to a name.
140 */
141static inline bool
142strncmpeq(const char *nameValue, const char *name, size_t nameLen)
143{
144	if (strncmp(nameValue, name, nameLen) == 0 && nameValue[nameLen] == '=')
145		return (true);
146
147	return (false);
148}
149
150
151/*
152 * Using environment, returns pointer to value associated with name, if any,
153 * else NULL.  If the onlyActive flag is set to true, only variables that are
154 * active are returned else all are.
155 */
156static inline char *
157__findenv(const char *name, size_t nameLen, int *envNdx, bool onlyActive)
158{
159	int ndx;
160
161	/*
162	 * Find environment variable from end of array (more likely to be
163	 * active).  A variable created by putenv is always active, or it is not
164	 * tracked in the array.
165	 */
166	for (ndx = *envNdx; ndx >= 0; ndx--)
167		if (envVars[ndx].putenv) {
168			if (strncmpeq(envVars[ndx].name, name, nameLen)) {
169				*envNdx = ndx;
170				return (envVars[ndx].name + nameLen +
171				    sizeof ("=") - 1);
172			}
173		} else if ((!onlyActive || envVars[ndx].active) &&
174		    (envVars[ndx].nameLen == nameLen &&
175		    strncmpeq(envVars[ndx].name, name, nameLen))) {
176			*envNdx = ndx;
177			return (envVars[ndx].value);
178		}
179
180	return (NULL);
181}
182
183
184/*
185 * Using environ, returns pointer to value associated with name, if any, else
186 * NULL.  Used on the original environ passed into the program.
187 */
188static char *
189__findenv_environ(const char *name, size_t nameLen)
190{
191	int envNdx;
192
193	/* Find variable within environ. */
194	for (envNdx = 0; environ[envNdx] != NULL; envNdx++)
195		if (strncmpeq(environ[envNdx], name, nameLen))
196			return (&(environ[envNdx][nameLen + sizeof("=") - 1]));
197
198	return (NULL);
199}
200
201
202/*
203 * Remove variable added by putenv() from variable tracking array.
204 */
205static void
206__remove_putenv(int envNdx)
207{
208	envVarsTotal--;
209	if (envVarsTotal > envNdx)
210		memmove(&(envVars[envNdx]), &(envVars[envNdx + 1]),
211		    (envVarsTotal - envNdx) * sizeof (*envVars));
212	memset(&(envVars[envVarsTotal]), 0, sizeof (*envVars));
213
214	return;
215}
216
217
218/*
219 * Deallocate the environment built from environ as well as environ then set
220 * both to NULL.  Eases debugging of memory leaks.
221 */
222static void
223__clean_env(bool freeVars)
224{
225	int envNdx;
226
227	/* Deallocate environment and environ if created by *env(). */
228	if (envVars != NULL) {
229		for (envNdx = envVarsTotal - 1; envNdx >= 0; envNdx--)
230			/* Free variables or deactivate them. */
231			if (envVars[envNdx].putenv) {
232				if (!freeVars)
233					__remove_putenv(envNdx);
234			} else {
235				if (freeVars)
236					free(envVars[envNdx].name);
237				else
238					envVars[envNdx].active = false;
239			}
240		if (freeVars) {
241			free(envVars);
242			envVars = NULL;
243		} else
244			envActive = 0;
245
246		/* Restore original environ if it has not updated by program. */
247		if (origEnviron != NULL) {
248			if (environ == intEnviron)
249				environ = origEnviron;
250			free(intEnviron);
251			intEnviron = NULL;
252			environSize = 0;
253		}
254	}
255
256	return;
257}
258
259
260/*
261 * Using the environment, rebuild the environ array for use by other C library
262 * calls that depend upon it.
263 */
264static int
265__rebuild_environ(int newEnvironSize)
266{
267	char **tmpEnviron;
268	int envNdx;
269	int environNdx;
270	int tmpEnvironSize;
271
272	/* Resize environ. */
273	if (newEnvironSize > environSize) {
274		tmpEnvironSize = newEnvironSize * 2;
275		tmpEnviron = reallocarray(intEnviron, tmpEnvironSize + 1,
276		    sizeof(*intEnviron));
277		if (tmpEnviron == NULL)
278			return (-1);
279		environSize = tmpEnvironSize;
280		intEnviron = tmpEnviron;
281	}
282	envActive = newEnvironSize;
283
284	/* Assign active variables to environ. */
285	for (envNdx = envVarsTotal - 1, environNdx = 0; envNdx >= 0; envNdx--)
286		if (envVars[envNdx].active)
287			intEnviron[environNdx++] = envVars[envNdx].name;
288	intEnviron[environNdx] = NULL;
289
290	/* Always set environ which may have been replaced by program. */
291	environ = intEnviron;
292
293	return (0);
294}
295
296
297/*
298 * Enlarge new environment.
299 */
300static inline bool
301__enlarge_env(void)
302{
303	int newEnvVarsSize;
304	struct envVars *tmpEnvVars;
305
306	envVarsTotal++;
307	if (envVarsTotal > envVarsSize) {
308		newEnvVarsSize = envVarsTotal * 2;
309		tmpEnvVars = reallocarray(envVars, newEnvVarsSize,
310		    sizeof(*envVars));
311		if (tmpEnvVars == NULL) {
312			envVarsTotal--;
313			return (false);
314		}
315		envVarsSize = newEnvVarsSize;
316		envVars = tmpEnvVars;
317	}
318
319	return (true);
320}
321
322
323/*
324 * Using environ, build an environment for use by standard C library calls.
325 */
326static int
327__build_env(void)
328{
329	char **env;
330	int activeNdx;
331	int envNdx;
332	int savedErrno;
333	size_t nameLen;
334
335	/* Check for non-existant environment. */
336	if (environ == NULL || environ[0] == NULL)
337		return (0);
338
339	/* Count environment variables. */
340	for (env = environ, envVarsTotal = 0; *env != NULL; env++)
341		envVarsTotal++;
342	envVarsSize = envVarsTotal * 2;
343
344	/* Create new environment. */
345	envVars = calloc(envVarsSize, sizeof(*envVars));
346	if (envVars == NULL)
347		goto Failure;
348
349	/* Copy environ values and keep track of them. */
350	for (envNdx = envVarsTotal - 1; envNdx >= 0; envNdx--) {
351		envVars[envNdx].putenv = false;
352		envVars[envNdx].name =
353		    strdup(environ[envVarsTotal - envNdx - 1]);
354		if (envVars[envNdx].name == NULL)
355			goto Failure;
356		envVars[envNdx].value = strchr(envVars[envNdx].name, '=');
357		if (envVars[envNdx].value != NULL) {
358			envVars[envNdx].value++;
359			envVars[envNdx].valueSize =
360			    strlen(envVars[envNdx].value);
361		} else {
362			__env_warnx(CorruptEnvValueMsg, envVars[envNdx].name,
363			    strlen(envVars[envNdx].name));
364			errno = EFAULT;
365			goto Failure;
366		}
367
368		/*
369		 * Find most current version of variable to make active.  This
370		 * will prevent multiple active variables from being created
371		 * during this initialization phase.
372		 */
373		nameLen = envVars[envNdx].value - envVars[envNdx].name - 1;
374		envVars[envNdx].nameLen = nameLen;
375		activeNdx = envVarsTotal - 1;
376		if (__findenv(envVars[envNdx].name, nameLen, &activeNdx,
377		    false) == NULL) {
378			__env_warnx(CorruptEnvFindMsg, envVars[envNdx].name,
379			    nameLen);
380			errno = EFAULT;
381			goto Failure;
382		}
383		envVars[activeNdx].active = true;
384	}
385
386	/* Create a new environ. */
387	origEnviron = environ;
388	environ = NULL;
389	if (__rebuild_environ(envVarsTotal) == 0)
390		return (0);
391
392Failure:
393	savedErrno = errno;
394	__clean_env(true);
395	errno = savedErrno;
396
397	return (-1);
398}
399
400
401/*
402 * Destructor function with default argument to __clean_env().
403 */
404static void
405__clean_env_destructor(void)
406{
407	__clean_env(true);
408
409	return;
410}
411
412
413/*
414 * Returns the value of a variable or NULL if none are found.
415 */
416char *
417getenv(const char *name)
418{
419	int envNdx;
420	size_t nameLen;
421
422	/* Check for malformed name. */
423	if (name == NULL || (nameLen = __strleneq(name)) == 0) {
424		errno = EINVAL;
425		return (NULL);
426	}
427
428	/*
429	 * Variable search order:
430	 * 1. Check for an empty environ.  This allows an application to clear
431	 *    the environment.
432	 * 2. Search the external environ array.
433	 * 3. Search the internal environment.
434	 *
435	 * Since malloc() depends upon getenv(), getenv() must never cause the
436	 * internal environment storage to be generated.
437	 */
438	if (environ == NULL || environ[0] == NULL)
439		return (NULL);
440	else if (envVars == NULL || environ != intEnviron)
441		return (__findenv_environ(name, nameLen));
442	else {
443		envNdx = envVarsTotal - 1;
444		return (__findenv(name, nameLen, &envNdx, true));
445	}
446}
447
448
449/*
450 * Set the value of a variable.  Older settings are labeled as inactive.  If an
451 * older setting has enough room to store the new value, it will be reused.  No
452 * previous variables are ever freed here to avoid causing a segmentation fault
453 * in a user's code.
454 *
455 * The variables nameLen and valueLen are passed into here to allow the caller
456 * to calculate the length by means besides just strlen().
457 */
458static int
459__setenv(const char *name, size_t nameLen, const char *value, int overwrite)
460{
461	bool reuse;
462	char *env;
463	int envNdx;
464	int newEnvActive;
465	size_t valueLen;
466
467	/* Find existing environment variable large enough to use. */
468	envNdx = envVarsTotal - 1;
469	newEnvActive = envActive;
470	valueLen = strlen(value);
471	reuse = false;
472	if (__findenv(name, nameLen, &envNdx, false) != NULL) {
473		/* Deactivate entry if overwrite is allowed. */
474		if (envVars[envNdx].active) {
475			if (overwrite == 0)
476				return (0);
477			envVars[envNdx].active = false;
478			newEnvActive--;
479		}
480
481		/* putenv() created variable cannot be reused. */
482		if (envVars[envNdx].putenv)
483			__remove_putenv(envNdx);
484
485		/* Entry is large enough to reuse. */
486		else if (envVars[envNdx].valueSize >= valueLen)
487			reuse = true;
488	}
489
490	/* Create new variable if none was found of sufficient size. */
491	if (! reuse) {
492		/* Enlarge environment. */
493		envNdx = envVarsTotal;
494		if (!__enlarge_env())
495			return (-1);
496
497		/* Create environment entry. */
498		envVars[envNdx].name = malloc(nameLen + sizeof ("=") +
499		    valueLen);
500		if (envVars[envNdx].name == NULL) {
501			envVarsTotal--;
502			return (-1);
503		}
504		envVars[envNdx].nameLen = nameLen;
505		envVars[envNdx].valueSize = valueLen;
506
507		/* Save name of name/value pair. */
508		env = stpncpy(envVars[envNdx].name, name, nameLen);
509		*env++ = '=';
510	}
511	else
512		env = envVars[envNdx].value;
513
514	/* Save value of name/value pair. */
515	strcpy(env, value);
516	envVars[envNdx].value = env;
517	envVars[envNdx].active = true;
518	newEnvActive++;
519
520	/* No need to rebuild environ if an active variable was reused. */
521	if (reuse && newEnvActive == envActive)
522		return (0);
523	else
524		return (__rebuild_environ(newEnvActive));
525}
526
527
528/*
529 * If the program attempts to replace the array of environment variables
530 * (environ) environ or sets the first varible to NULL, then deactivate all
531 * variables and merge in the new list from environ.
532 */
533static int
534__merge_environ(void)
535{
536	char **env;
537	char *equals;
538
539	/*
540	 * Internally-built environ has been replaced or cleared (detected by
541	 * using the count of active variables against a NULL as the first value
542	 * in environ).  Clean up everything.
543	 */
544	if (intEnviron != NULL && (environ != intEnviron || (envActive > 0 &&
545	    environ[0] == NULL))) {
546		/* Deactivate all environment variables. */
547		if (envActive > 0) {
548			origEnviron = NULL;
549			__clean_env(false);
550		}
551
552		/*
553		 * Insert new environ into existing, yet deactivated,
554		 * environment array.
555		 */
556		origEnviron = environ;
557		if (origEnviron != NULL)
558			for (env = origEnviron; *env != NULL; env++) {
559				if ((equals = strchr(*env, '=')) == NULL) {
560					__env_warnx(CorruptEnvValueMsg, *env,
561					    strlen(*env));
562					errno = EFAULT;
563					return (-1);
564				}
565				if (__setenv(*env, equals - *env, equals + 1,
566				    1) == -1)
567					return (-1);
568			}
569	}
570
571	return (0);
572}
573
574
575/*
576 * The exposed setenv() that peforms a few tests before calling the function
577 * (__setenv()) that does the actual work of inserting a variable into the
578 * environment.
579 */
580int
581setenv(const char *name, const char *value, int overwrite)
582{
583	size_t nameLen;
584
585	/* Check for malformed name. */
586	if (name == NULL || (nameLen = __strleneq(name)) == 0) {
587		errno = EINVAL;
588		return (-1);
589	}
590
591	/* Initialize environment. */
592	if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1))
593		return (-1);
594
595	return (__setenv(name, nameLen, value, overwrite));
596}
597
598
599/*
600 * Insert a "name=value" string into the environment.  Special settings must be
601 * made to keep setenv() from reusing this memory block and unsetenv() from
602 * allowing it to be tracked.
603 */
604int
605putenv(char *string)
606{
607	char *equals;
608	int envNdx;
609	int newEnvActive;
610	size_t nameLen;
611
612	/* Check for malformed argument. */
613	if (string == NULL || (equals = strchr(string, '=')) == NULL ||
614	    (nameLen = equals - string) == 0) {
615		errno = EINVAL;
616		return (-1);
617	}
618
619	/* Initialize environment. */
620	if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1))
621		return (-1);
622
623	/* Deactivate previous environment variable. */
624	envNdx = envVarsTotal - 1;
625	newEnvActive = envActive;
626	if (__findenv(string, nameLen, &envNdx, true) != NULL) {
627		/* Reuse previous putenv slot. */
628		if (envVars[envNdx].putenv) {
629			envVars[envNdx].name = string;
630			return (__rebuild_environ(envActive));
631		} else {
632			newEnvActive--;
633			envVars[envNdx].active = false;
634		}
635	}
636
637	/* Enlarge environment. */
638	envNdx = envVarsTotal;
639	if (!__enlarge_env())
640		return (-1);
641
642	/* Create environment entry. */
643	envVars[envNdx].name = string;
644	envVars[envNdx].nameLen = -1;
645	envVars[envNdx].value = NULL;
646	envVars[envNdx].valueSize = -1;
647	envVars[envNdx].putenv = true;
648	envVars[envNdx].active = true;
649	newEnvActive++;
650
651	return (__rebuild_environ(newEnvActive));
652}
653
654
655/*
656 * Unset variable with the same name by flagging it as inactive.  No variable is
657 * ever freed.
658 */
659int
660unsetenv(const char *name)
661{
662	int envNdx;
663	size_t nameLen;
664	int newEnvActive;
665
666	/* Check for malformed name. */
667	if (name == NULL || (nameLen = __strleneq(name)) == 0) {
668		errno = EINVAL;
669		return (-1);
670	}
671
672	/* Initialize environment. */
673	if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1))
674		return (-1);
675
676	/* Deactivate specified variable. */
677	/* Remove all occurrences. */
678	envNdx = envVarsTotal - 1;
679	newEnvActive = envActive;
680	while (__findenv(name, nameLen, &envNdx, true) != NULL) {
681		envVars[envNdx].active = false;
682		if (envVars[envNdx].putenv)
683			__remove_putenv(envNdx);
684		envNdx--;
685		newEnvActive--;
686	}
687	if (newEnvActive != envActive)
688		__rebuild_environ(newEnvActive);
689
690	return (0);
691}
692