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
__env_warnx(const char * msg,const char * name,size_t nameLen)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
__strleneq(const char * str)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
strncmpeq(const char * nameValue,const char * name,size_t nameLen)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 *
__findenv(const char * name,size_t nameLen,int * envNdx,bool onlyActive)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 *
__findenv_environ(const char * name,size_t nameLen)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
__remove_putenv(int envNdx)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
__clean_env(bool freeVars)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
__rebuild_environ(int newEnvironSize)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
__enlarge_env(void)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
__build_env(void)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
__clean_env_destructor(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 *
getenv(const char * name)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 *
secure_getenv(const char * name)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
__setenv(const char * name,size_t nameLen,const char * value,int overwrite)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
__merge_environ(void)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
setenv(const char * name,const char * value,int overwrite)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
putenv(char * string)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
unsetenv(const char * name)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
clearenv(void)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