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