]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libc/stdlib/getenv.c
zfs: merge openzfs/zfs@a9d6b0690
[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 <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 }