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