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