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