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 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 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 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 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 * 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 * 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 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 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 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 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 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 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 * 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 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 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 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 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 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