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