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 } 726