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