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 <ssp/ssp.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 * Like getenv(), but copies the value into the provided buffer. 449 */ 450 int 451 __ssp_real(getenv_r)(const char *name, char *buf, size_t len) 452 { 453 const char *val; 454 size_t nameLen; 455 int envNdx; 456 457 if (name == NULL || (nameLen = __strleneq(name)) == 0) { 458 errno = EINVAL; 459 return (-1); 460 } 461 462 if (environ == NULL || environ[0] == NULL) { 463 val = NULL; 464 } else if (envVars == NULL || environ != intEnviron) { 465 val = __findenv_environ(name, nameLen); 466 } else { 467 envNdx = envVarsTotal - 1; 468 val = __findenv(name, nameLen, &envNdx, true); 469 } 470 if (val == NULL) { 471 errno = ENOENT; 472 return (-1); 473 } 474 if (strlcpy(buf, val, len) >= len) { 475 errno = ERANGE; 476 return (-1); 477 } 478 return (0); 479 } 480 481 482 /* 483 * Runs getenv() unless the current process is tainted by uid or gid changes, in 484 * which case it will return NULL. 485 */ 486 char * 487 secure_getenv(const char *name) 488 { 489 if (issetugid()) 490 return (NULL); 491 return (getenv(name)); 492 } 493 494 /* 495 * Set the value of a variable. Older settings are labeled as inactive. If an 496 * older setting has enough room to store the new value, it will be reused. No 497 * previous variables are ever freed here to avoid causing a segmentation fault 498 * in a user's code. 499 * 500 * The variables nameLen and valueLen are passed into here to allow the caller 501 * to calculate the length by means besides just strlen(). 502 */ 503 static int 504 __setenv(const char *name, size_t nameLen, const char *value, int overwrite) 505 { 506 bool reuse; 507 char *env; 508 int envNdx; 509 int newEnvActive; 510 size_t valueLen; 511 512 /* Find existing environment variable large enough to use. */ 513 envNdx = envVarsTotal - 1; 514 newEnvActive = envActive; 515 valueLen = strlen(value); 516 reuse = false; 517 if (__findenv(name, nameLen, &envNdx, false) != NULL) { 518 /* Deactivate entry if overwrite is allowed. */ 519 if (envVars[envNdx].active) { 520 if (overwrite == 0) 521 return (0); 522 envVars[envNdx].active = false; 523 newEnvActive--; 524 } 525 526 /* putenv() created variable cannot be reused. */ 527 if (envVars[envNdx].putenv) 528 __remove_putenv(envNdx); 529 530 /* Entry is large enough to reuse. */ 531 else if (envVars[envNdx].valueSize >= valueLen) 532 reuse = true; 533 } 534 535 /* Create new variable if none was found of sufficient size. */ 536 if (! reuse) { 537 /* Enlarge environment. */ 538 envNdx = envVarsTotal; 539 if (!__enlarge_env()) 540 return (-1); 541 542 /* Create environment entry. */ 543 envVars[envNdx].name = malloc(nameLen + sizeof ("=") + 544 valueLen); 545 if (envVars[envNdx].name == NULL) { 546 envVarsTotal--; 547 return (-1); 548 } 549 envVars[envNdx].nameLen = nameLen; 550 envVars[envNdx].valueSize = valueLen; 551 552 /* Save name of name/value pair. */ 553 env = stpncpy(envVars[envNdx].name, name, nameLen); 554 *env++ = '='; 555 } 556 else 557 env = envVars[envNdx].value; 558 559 /* Save value of name/value pair. */ 560 strcpy(env, value); 561 envVars[envNdx].value = env; 562 envVars[envNdx].active = true; 563 newEnvActive++; 564 565 /* No need to rebuild environ if an active variable was reused. */ 566 if (reuse && newEnvActive == envActive) 567 return (0); 568 else 569 return (__rebuild_environ(newEnvActive)); 570 } 571 572 573 /* 574 * If the program attempts to replace the array of environment variables 575 * (environ) environ or sets the first varible to NULL, then deactivate all 576 * variables and merge in the new list from environ. 577 */ 578 static int 579 __merge_environ(void) 580 { 581 char **env; 582 char *equals; 583 584 /* 585 * Internally-built environ has been replaced or cleared (detected by 586 * using the count of active variables against a NULL as the first value 587 * in environ). Clean up everything. 588 */ 589 if (intEnviron != NULL && (environ != intEnviron || (envActive > 0 && 590 environ[0] == NULL))) { 591 /* Deactivate all environment variables. */ 592 if (envActive > 0) { 593 origEnviron = NULL; 594 __clean_env(false); 595 } 596 597 /* 598 * Insert new environ into existing, yet deactivated, 599 * environment array. 600 */ 601 origEnviron = environ; 602 if (origEnviron != NULL) 603 for (env = origEnviron; *env != NULL; env++) { 604 if ((equals = strchr(*env, '=')) == NULL) { 605 __env_warnx(CorruptEnvValueMsg, *env, 606 strlen(*env)); 607 errno = EFAULT; 608 return (-1); 609 } 610 if (__setenv(*env, equals - *env, equals + 1, 611 1) == -1) 612 return (-1); 613 } 614 } 615 616 return (0); 617 } 618 619 620 /* 621 * The exposed setenv() that performs a few tests before calling the function 622 * (__setenv()) that does the actual work of inserting a variable into the 623 * environment. 624 */ 625 int 626 setenv(const char *name, const char *value, int overwrite) 627 { 628 size_t nameLen; 629 630 /* Check for malformed name. */ 631 if (name == NULL || (nameLen = __strleneq(name)) == 0) { 632 errno = EINVAL; 633 return (-1); 634 } 635 636 /* Initialize environment. */ 637 if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1)) 638 return (-1); 639 640 return (__setenv(name, nameLen, value, overwrite)); 641 } 642 643 644 /* 645 * Insert a "name=value" string into the environment. Special settings must be 646 * made to keep setenv() from reusing this memory block and unsetenv() from 647 * allowing it to be tracked. 648 */ 649 int 650 putenv(char *string) 651 { 652 char *equals; 653 int envNdx; 654 int newEnvActive; 655 size_t nameLen; 656 657 /* Check for malformed argument. */ 658 if (string == NULL || (equals = strchr(string, '=')) == NULL || 659 (nameLen = equals - string) == 0) { 660 errno = EINVAL; 661 return (-1); 662 } 663 664 /* Initialize environment. */ 665 if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1)) 666 return (-1); 667 668 /* Deactivate previous environment variable. */ 669 envNdx = envVarsTotal - 1; 670 newEnvActive = envActive; 671 if (__findenv(string, nameLen, &envNdx, true) != NULL) { 672 /* Reuse previous putenv slot. */ 673 if (envVars[envNdx].putenv) { 674 envVars[envNdx].name = string; 675 return (__rebuild_environ(envActive)); 676 } else { 677 newEnvActive--; 678 envVars[envNdx].active = false; 679 } 680 } 681 682 /* Enlarge environment. */ 683 envNdx = envVarsTotal; 684 if (!__enlarge_env()) 685 return (-1); 686 687 /* Create environment entry. */ 688 envVars[envNdx].name = string; 689 envVars[envNdx].nameLen = -1; 690 envVars[envNdx].value = NULL; 691 envVars[envNdx].valueSize = -1; 692 envVars[envNdx].putenv = true; 693 envVars[envNdx].active = true; 694 newEnvActive++; 695 696 return (__rebuild_environ(newEnvActive)); 697 } 698 699 700 /* 701 * Unset variable with the same name by flagging it as inactive. No variable is 702 * ever freed. 703 */ 704 int 705 unsetenv(const char *name) 706 { 707 int envNdx; 708 size_t nameLen; 709 int newEnvActive; 710 711 /* Check for malformed name. */ 712 if (name == NULL || (nameLen = __strleneq(name)) == 0) { 713 errno = EINVAL; 714 return (-1); 715 } 716 717 /* Initialize environment. */ 718 if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1)) 719 return (-1); 720 721 /* Deactivate specified variable. */ 722 /* Remove all occurrences. */ 723 envNdx = envVarsTotal - 1; 724 newEnvActive = envActive; 725 while (__findenv(name, nameLen, &envNdx, true) != NULL) { 726 envVars[envNdx].active = false; 727 if (envVars[envNdx].putenv) 728 __remove_putenv(envNdx); 729 envNdx--; 730 newEnvActive--; 731 } 732 if (newEnvActive != envActive) 733 __rebuild_environ(newEnvActive); 734 735 return (0); 736 } 737 738 /* 739 * Unset all variable by flagging them as inactive. No variable is 740 * ever freed. 741 */ 742 int 743 clearenv(void) 744 { 745 int ndx; 746 747 /* Initialize environment. */ 748 if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1)) 749 return (-1); 750 751 /* Remove from the end to not shuffle memory too much. */ 752 for (ndx = envVarsTotal - 1; ndx >= 0; ndx--) { 753 envVars[ndx].active = false; 754 if (envVars[ndx].putenv) 755 __remove_putenv(ndx); 756 } 757 758 __rebuild_environ(0); 759 760 return (0); 761 } 762