1 /*- 2 * Copyright (c) 2007 Sean C. Farley <scf@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer, 10 * without modification, immediately at the beginning of the file. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 31 #include "namespace.h" 32 #include <sys/types.h> 33 #include <errno.h> 34 #include <stdbool.h> 35 #include <stddef.h> 36 #include <stdlib.h> 37 #include <string.h> 38 #include <unistd.h> 39 #include "un-namespace.h" 40 41 42 static const char CorruptEnvFindMsg[] = "environment corrupt; unable to find "; 43 static const char CorruptEnvValueMsg[] = 44 "environment corrupt; missing value for "; 45 46 47 /* 48 * Standard environ. environ variable is exposed to entire process. 49 * 50 * origEnviron: Upon cleanup on unloading of library or failure, this 51 * allows environ to return to as it was before. 52 * environSize: Number of variables environ can hold. Can only 53 * increase. 54 * intEnviron: Internally-built environ. Exposed via environ during 55 * (re)builds of the environment. 56 */ 57 extern char **environ; 58 static char **origEnviron; 59 static char **intEnviron = NULL; 60 static int environSize = 0; 61 62 /* 63 * Array of environment variables built from environ. Each element records: 64 * name: Pointer to name=value string 65 * name length: Length of name not counting '=' character 66 * value: Pointer to value within same string as name 67 * value size: Size (not length) of space for value not counting the 68 * nul character 69 * active state: true/false value to signify whether variable is active. 70 * Useful since multiple variables with the same name can 71 * co-exist. At most, one variable can be active at any 72 * one time. 73 * putenv: Created from putenv() call. This memory must not be 74 * reused. 75 */ 76 static struct envVars { 77 size_t nameLen; 78 size_t valueSize; 79 char *name; 80 char *value; 81 bool active; 82 bool putenv; 83 } *envVars = NULL; 84 85 /* 86 * Environment array information. 87 * 88 * envActive: Number of active variables in array. 89 * envVarsSize: Size of array. 90 * envVarsTotal: Number of total variables in array (active or not). 91 */ 92 static int envActive = 0; 93 static int envVarsSize = 0; 94 static int envVarsTotal = 0; 95 96 97 /* Deinitialization of new environment. */ 98 static void __attribute__ ((destructor)) __clean_env_destructor(void); 99 100 101 /* 102 * A simple version of warnx() to avoid the bloat of including stdio in static 103 * binaries. 104 */ 105 static void 106 __env_warnx(const char *msg, const char *name, size_t nameLen) 107 { 108 static const char nl[] = "\n"; 109 static const char progSep[] = ": "; 110 111 _write(STDERR_FILENO, _getprogname(), strlen(_getprogname())); 112 _write(STDERR_FILENO, progSep, sizeof(progSep) - 1); 113 _write(STDERR_FILENO, msg, strlen(msg)); 114 _write(STDERR_FILENO, name, nameLen); 115 _write(STDERR_FILENO, nl, sizeof(nl) - 1); 116 117 return; 118 } 119 120 121 /* 122 * Inline strlen() for performance. Also, perform check for an equals sign. 123 * Cheaper here than peforming a strchr() later. 124 */ 125 static inline size_t 126 __strleneq(const char *str) 127 { 128 const char *s; 129 130 for (s = str; *s != '\0'; ++s) 131 if (*s == '=') 132 return (0); 133 134 return (s - str); 135 } 136 137 138 /* 139 * Comparison of an environment name=value to a name. 140 */ 141 static inline bool 142 strncmpeq(const char *nameValue, const char *name, size_t nameLen) 143 { 144 if (strncmp(nameValue, name, nameLen) == 0 && nameValue[nameLen] == '=') 145 return (true); 146 147 return (false); 148 } 149 150 151 /* 152 * Using environment, returns pointer to value associated with name, if any, 153 * else NULL. If the onlyActive flag is set to true, only variables that are 154 * active are returned else all are. 155 */ 156 static inline char * 157 __findenv(const char *name, size_t nameLen, int *envNdx, bool onlyActive) 158 { 159 int ndx; 160 161 /* 162 * Find environment variable from end of array (more likely to be 163 * active). A variable created by putenv is always active or it is not 164 * tracked in the array. 165 */ 166 for (ndx = *envNdx; ndx >= 0; ndx--) 167 if (envVars[ndx].putenv) { 168 if (strncmpeq(envVars[ndx].name, name, nameLen)) { 169 *envNdx = ndx; 170 return (envVars[ndx].name + nameLen + 171 sizeof ("=") - 1); 172 } 173 } else if ((!onlyActive || envVars[ndx].active) && 174 (envVars[ndx].nameLen == nameLen && 175 strncmpeq(envVars[ndx].name, name, nameLen))) { 176 *envNdx = ndx; 177 return (envVars[ndx].value); 178 } 179 180 return (NULL); 181 } 182 183 184 /* 185 * Using environ, returns pointer to value associated with name, if any, else 186 * NULL. Used on the original environ passed into the program. 187 */ 188 static char * 189 __findenv_environ(const char *name, size_t nameLen) 190 { 191 int envNdx; 192 193 /* Check for non-existant environment. */ 194 if (environ == NULL) 195 return (NULL); 196 197 /* Find variable within environ. */ 198 for (envNdx = 0; environ[envNdx] != NULL; envNdx++) 199 if (strncmpeq(environ[envNdx], name, nameLen)) 200 return (&(environ[envNdx][nameLen + sizeof("=") - 1])); 201 202 return (NULL); 203 } 204 205 206 /* 207 * Remove variable added by putenv() from variable tracking array. 208 */ 209 static void 210 __remove_putenv(int envNdx) 211 { 212 envVarsTotal--; 213 if (envVarsTotal > envNdx) 214 memmove(&(envVars[envNdx]), &(envVars[envNdx + 1]), 215 (envVarsTotal - envNdx) * sizeof (*envVars)); 216 memset(&(envVars[envVarsTotal]), 0, sizeof (*envVars)); 217 218 return; 219 } 220 221 222 /* 223 * Deallocate the environment built from environ as well as environ then set 224 * both to NULL. Eases debugging of memory leaks. 225 */ 226 static void 227 __clean_env(bool freeVars) 228 { 229 int envNdx; 230 231 /* Deallocate environment and environ if created by *env(). */ 232 if (envVars != NULL) { 233 for (envNdx = envVarsTotal - 1; envNdx >= 0; envNdx--) 234 /* Free variables or deactivate them. */ 235 if (envVars[envNdx].putenv) { 236 if (!freeVars) 237 __remove_putenv(envNdx); 238 } else { 239 if (freeVars) 240 free(envVars[envNdx].name); 241 else 242 envVars[envNdx].active = false; 243 } 244 if (freeVars) { 245 free(envVars); 246 envVars = NULL; 247 } else 248 envActive = 0; 249 250 /* Restore original environ if it has not updated by program. */ 251 if (origEnviron != NULL) { 252 if (environ == intEnviron) 253 environ = origEnviron; 254 free(intEnviron); 255 intEnviron = NULL; 256 environSize = 0; 257 } 258 } 259 260 return; 261 } 262 263 264 /* 265 * Using the environment, rebuild the environ array for use by other C library 266 * calls that depend upon it. 267 */ 268 static int 269 __rebuild_environ(int newEnvironSize) 270 { 271 char **tmpEnviron; 272 int envNdx; 273 int environNdx; 274 int tmpEnvironSize; 275 276 /* Resize environ. */ 277 if (newEnvironSize > environSize) { 278 tmpEnvironSize = newEnvironSize * 2; 279 tmpEnviron = realloc(intEnviron, sizeof (*intEnviron) * 280 (tmpEnvironSize + 1)); 281 if (tmpEnviron == NULL) 282 return (-1); 283 environSize = tmpEnvironSize; 284 intEnviron = tmpEnviron; 285 } 286 envActive = newEnvironSize; 287 288 /* Assign active variables to environ. */ 289 for (envNdx = envVarsTotal - 1, environNdx = 0; envNdx >= 0; envNdx--) 290 if (envVars[envNdx].active) 291 intEnviron[environNdx++] = envVars[envNdx].name; 292 intEnviron[environNdx] = NULL; 293 294 /* Always set environ which may have been replaced by program. */ 295 environ = intEnviron; 296 297 return (0); 298 } 299 300 301 /* 302 * Enlarge new environment. 303 */ 304 static inline bool 305 __enlarge_env(void) 306 { 307 int newEnvVarsSize; 308 struct envVars *tmpEnvVars; 309 310 envVarsTotal++; 311 if (envVarsTotal > envVarsSize) { 312 newEnvVarsSize = envVarsTotal * 2; 313 tmpEnvVars = realloc(envVars, sizeof (*envVars) * 314 newEnvVarsSize); 315 if (tmpEnvVars == NULL) { 316 envVarsTotal--; 317 return (false); 318 } 319 envVarsSize = newEnvVarsSize; 320 envVars = tmpEnvVars; 321 } 322 323 return (true); 324 } 325 326 327 /* 328 * Using environ, build an environment for use by standard C library calls. 329 */ 330 static int 331 __build_env(void) 332 { 333 char **env; 334 int activeNdx; 335 int envNdx; 336 int savedErrno; 337 size_t nameLen; 338 339 /* Check for non-existant environment. */ 340 if (environ == NULL || environ[0] == NULL) 341 return (0); 342 343 /* Count environment variables. */ 344 for (env = environ, envVarsTotal = 0; *env != NULL; env++) 345 envVarsTotal++; 346 envVarsSize = envVarsTotal * 2; 347 348 /* Create new environment. */ 349 envVars = calloc(1, sizeof (*envVars) * envVarsSize); 350 if (envVars == NULL) 351 goto Failure; 352 353 /* Copy environ values and keep track of them. */ 354 for (envNdx = envVarsTotal - 1; envNdx >= 0; envNdx--) { 355 envVars[envNdx].putenv = false; 356 envVars[envNdx].name = 357 strdup(environ[envVarsTotal - envNdx - 1]); 358 if (envVars[envNdx].name == NULL) 359 goto Failure; 360 envVars[envNdx].value = strchr(envVars[envNdx].name, '='); 361 if (envVars[envNdx].value != NULL) { 362 envVars[envNdx].value++; 363 envVars[envNdx].valueSize = 364 strlen(envVars[envNdx].value); 365 } else { 366 __env_warnx(CorruptEnvValueMsg, envVars[envNdx].name, 367 strlen(envVars[envNdx].name)); 368 errno = EFAULT; 369 goto Failure; 370 } 371 372 /* 373 * Find most current version of variable to make active. This 374 * will prevent multiple active variables from being created 375 * during this initialization phase. 376 */ 377 nameLen = envVars[envNdx].value - envVars[envNdx].name - 1; 378 envVars[envNdx].nameLen = nameLen; 379 activeNdx = envVarsTotal - 1; 380 if (__findenv(envVars[envNdx].name, nameLen, &activeNdx, 381 false) == NULL) { 382 __env_warnx(CorruptEnvFindMsg, envVars[envNdx].name, 383 nameLen); 384 errno = EFAULT; 385 goto Failure; 386 } 387 envVars[activeNdx].active = true; 388 } 389 390 /* Create a new environ. */ 391 origEnviron = environ; 392 environ = NULL; 393 if (__rebuild_environ(envVarsTotal) == 0) 394 return (0); 395 396 Failure: 397 savedErrno = errno; 398 __clean_env(true); 399 errno = savedErrno; 400 401 return (-1); 402 } 403 404 405 /* 406 * Destructor function with default argument to __clean_env(). 407 */ 408 static void 409 __clean_env_destructor(void) 410 { 411 __clean_env(true); 412 413 return; 414 } 415 416 417 /* 418 * Returns the value of a variable or NULL if none are found. 419 */ 420 char * 421 getenv(const char *name) 422 { 423 int envNdx; 424 size_t nameLen; 425 426 /* Check for malformed name. */ 427 if (name == NULL || (nameLen = __strleneq(name)) == 0) { 428 errno = EINVAL; 429 return (NULL); 430 } 431 432 /* 433 * Find environment variable via environ if no changes have been made 434 * via a *env() call or environ has been replaced by a running program, 435 * otherwise, use the rebuilt environment. 436 */ 437 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 * Set the value of a variable. Older settings are labeled as inactive. If an 448 * older setting has enough room to store the new value, it will be reused. No 449 * previous variables are ever freed here to avoid causing a segmentation fault 450 * in a user's code. 451 * 452 * The variables nameLen and valueLen are passed into here to allow the caller 453 * to calculate the length by means besides just strlen(). 454 */ 455 static int 456 __setenv(const char *name, size_t nameLen, const char *value, int overwrite) 457 { 458 bool reuse; 459 char *env; 460 int envNdx; 461 int newEnvActive; 462 size_t valueLen; 463 464 /* Find existing environment variable large enough to use. */ 465 envNdx = envVarsTotal - 1; 466 newEnvActive = envActive; 467 valueLen = strlen(value); 468 reuse = false; 469 if (__findenv(name, nameLen, &envNdx, false) != NULL) { 470 /* Deactivate entry if overwrite is allowed. */ 471 if (envVars[envNdx].active) { 472 if (overwrite == 0) 473 return (0); 474 envVars[envNdx].active = false; 475 newEnvActive--; 476 } 477 478 /* putenv() created variable cannot be reused. */ 479 if (envVars[envNdx].putenv) 480 __remove_putenv(envNdx); 481 482 /* Entry is large enough to reuse. */ 483 else if (envVars[envNdx].valueSize >= valueLen) 484 reuse = true; 485 } 486 487 /* Create new variable if none was found of sufficient size. */ 488 if (! reuse) { 489 /* Enlarge environment. */ 490 envNdx = envVarsTotal; 491 if (!__enlarge_env()) 492 return (-1); 493 494 /* Create environment entry. */ 495 envVars[envNdx].name = malloc(nameLen + sizeof ("=") + 496 valueLen); 497 if (envVars[envNdx].name == NULL) { 498 envVarsTotal--; 499 return (-1); 500 } 501 envVars[envNdx].nameLen = nameLen; 502 envVars[envNdx].valueSize = valueLen; 503 504 /* Save name of name/value pair. */ 505 env = stpcpy(envVars[envNdx].name, name); 506 if ((envVars[envNdx].name)[nameLen] != '=') 507 env = stpcpy(env, "="); 508 } 509 else 510 env = envVars[envNdx].value; 511 512 /* Save value of name/value pair. */ 513 strcpy(env, value); 514 envVars[envNdx].value = env; 515 envVars[envNdx].active = true; 516 newEnvActive++; 517 518 /* No need to rebuild environ if an active variable was reused. */ 519 if (reuse && newEnvActive == envActive) 520 return (0); 521 else 522 return (__rebuild_environ(newEnvActive)); 523 } 524 525 526 /* 527 * If the program attempts to replace the array of environment variables 528 * (environ) environ, then deactivate all variables and merge in the new list 529 * from environ. 530 */ 531 static int 532 __merge_environ(void) 533 { 534 char **env; 535 char *equals; 536 537 /* environ has been replaced. clean up everything. */ 538 if (envVarsTotal > 0 && environ != intEnviron) { 539 /* Deactivate all environment variables. */ 540 if (envActive > 0) { 541 origEnviron = NULL; 542 __clean_env(false); 543 } 544 545 /* 546 * Insert new environ into existing, yet deactivated, 547 * environment array. 548 */ 549 origEnviron = environ; 550 if (origEnviron != NULL) 551 for (env = origEnviron; *env != NULL; env++) { 552 if ((equals = strchr(*env, '=')) == NULL) { 553 __env_warnx(CorruptEnvValueMsg, *env, 554 strlen(*env)); 555 errno = EFAULT; 556 return (-1); 557 } 558 if (__setenv(*env, equals - *env, equals + 1, 559 1) == -1) 560 return (-1); 561 } 562 } 563 564 return (0); 565 } 566 567 568 /* 569 * The exposed setenv() that peforms a few tests before calling the function 570 * (__setenv()) that does the actual work of inserting a variable into the 571 * environment. 572 */ 573 int 574 setenv(const char *name, const char *value, int overwrite) 575 { 576 size_t nameLen; 577 578 /* Check for malformed name. */ 579 if (name == NULL || (nameLen = __strleneq(name)) == 0) { 580 errno = EINVAL; 581 return (-1); 582 } 583 584 /* Initialize environment. */ 585 if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1)) 586 return (-1); 587 588 return (__setenv(name, nameLen, value, overwrite)); 589 } 590 591 592 /* 593 * Insert a "name=value" string into the environment. Special settings must be 594 * made to keep setenv() from reusing this memory block and unsetenv() from 595 * allowing it to be tracked. 596 */ 597 int 598 putenv(char *string) 599 { 600 char *equals; 601 int envNdx; 602 int newEnvActive; 603 size_t nameLen; 604 605 /* Check for malformed argument. */ 606 if (string == NULL || (equals = strchr(string, '=')) == NULL || 607 (nameLen = equals - string) == 0) { 608 errno = EINVAL; 609 return (-1); 610 } 611 612 /* Initialize environment. */ 613 if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1)) 614 return (-1); 615 616 /* Deactivate previous environment variable. */ 617 envNdx = envVarsTotal - 1; 618 newEnvActive = envActive; 619 if (__findenv(string, nameLen, &envNdx, true) != NULL) { 620 /* Reuse previous putenv slot. */ 621 if (envVars[envNdx].putenv) { 622 envVars[envNdx].name = string; 623 return (__rebuild_environ(envActive)); 624 } else { 625 newEnvActive--; 626 envVars[envNdx].active = false; 627 } 628 } 629 630 /* Enlarge environment. */ 631 envNdx = envVarsTotal; 632 if (!__enlarge_env()) 633 return (-1); 634 635 /* Create environment entry. */ 636 envVars[envNdx].name = string; 637 envVars[envNdx].nameLen = -1; 638 envVars[envNdx].value = NULL; 639 envVars[envNdx].valueSize = -1; 640 envVars[envNdx].putenv = true; 641 envVars[envNdx].active = true; 642 newEnvActive++; 643 644 return (__rebuild_environ(newEnvActive)); 645 } 646 647 648 /* 649 * Unset variable with the same name by flagging it as inactive. No variable is 650 * ever freed. 651 */ 652 int 653 unsetenv(const char *name) 654 { 655 int envNdx; 656 size_t nameLen; 657 658 /* Check for malformed name. */ 659 if (name == NULL || (nameLen = __strleneq(name)) == 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 specified variable. */ 669 envNdx = envVarsTotal - 1; 670 if (__findenv(name, nameLen, &envNdx, true) != NULL) { 671 envVars[envNdx].active = false; 672 if (envVars[envNdx].putenv) 673 __remove_putenv(envNdx); 674 __rebuild_environ(envActive - 1); 675 } 676 677 return (0); 678 } 679