Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/lib/libc/stdlib/getenv.c
39530 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2007-2009 Sean C. Farley <[email protected]>
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
763