]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libc/stdlib/getenv.c
Update to bmake-20240108
[FreeBSD/FreeBSD.git] / lib / libc / stdlib / getenv.c
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 }