1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 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 } 730