Coverage Report

Created: 2025-10-28 06:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/sudo/plugins/sudoers/env.c
Line
Count
Source
1
/*
2
 * SPDX-License-Identifier: ISC
3
 *
4
 * Copyright (c) 2000-2005, 2007-2023
5
 *  Todd C. Miller <Todd.Miller@sudo.ws>
6
 *
7
 * Permission to use, copy, modify, and distribute this software for any
8
 * purpose with or without fee is hereby granted, provided that the above
9
 * copyright notice and this permission notice appear in all copies.
10
 *
11
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18
 *
19
 * Sponsored in part by the Defense Advanced Research Projects
20
 * Agency (DARPA) and Air Force Research Laboratory, Air Force
21
 * Materiel Command, USAF, under agreement number F39502-99-1-0512.
22
 */
23
24
#include <config.h>
25
26
#include <stdio.h>
27
#include <stdlib.h>
28
#include <string.h>
29
#include <unistd.h>
30
#if defined(HAVE_STDINT_H)
31
# include <stdint.h>
32
#elif defined(HAVE_INTTYPES_H)
33
# include <inttypes.h>
34
#endif
35
#ifdef HAVE_LOGIN_CAP_H
36
# include <login_cap.h>
37
# ifndef LOGIN_SETENV
38
#  define LOGIN_SETENV  0
39
# endif
40
#endif /* HAVE_LOGIN_CAP_H */
41
#include <ctype.h>
42
#include <errno.h>
43
#include <limits.h>
44
#include <pwd.h>
45
46
#include <sudoers.h>
47
48
/*
49
 * Flags used in rebuild_env()
50
 */
51
#undef DID_TERM
52
#define DID_TERM  0x00000001
53
#undef DID_PATH
54
#define DID_PATH  0x00000002
55
#undef DID_HOME
56
#define DID_HOME  0x00000004
57
#undef DID_SHELL
58
#define DID_SHELL 0x00000008
59
#undef DID_LOGNAME
60
#define DID_LOGNAME 0x00000010
61
#undef DID_USER
62
#define DID_USER      0x00000020
63
#undef DID_LOGIN
64
#define DID_LOGIN     0x00000040
65
#undef DID_MAIL
66
#define DID_MAIL    0x00000080
67
#undef DID_MAX
68
#define DID_MAX     0x0000ffff
69
70
#undef KEPT_TERM
71
#define KEPT_TERM 0x00010000
72
#undef KEPT_PATH
73
#define KEPT_PATH 0x00020000
74
#undef KEPT_HOME
75
#define KEPT_HOME 0x00040000
76
#undef KEPT_SHELL
77
#define KEPT_SHELL  0x00080000
78
#undef KEPT_LOGNAME
79
24.7k
#define KEPT_LOGNAME  0x00100000
80
#undef KEPT_USER
81
24.7k
#define KEPT_USER     0x00200000
82
#undef KEPT_LOGIN
83
#define KEPT_LOGIN  0x00400000
84
#undef KEPT_MAIL
85
#define KEPT_MAIL 0x00800000
86
#undef KEPT_MAX
87
#define KEPT_MAX      0xffff0000
88
89
/*
90
 * AIX sets the LOGIN environment variable too.
91
 */
92
#ifdef _AIX
93
# define KEPT_USER_VARIABLES (KEPT_LOGIN|KEPT_LOGNAME|KEPT_USER)
94
#else
95
24.7k
# define KEPT_USER_VARIABLES (KEPT_LOGNAME|KEPT_USER)
96
#endif
97
98
/*
99
 * Functions to open, close and parse an environment file, either
100
 * a system file such as /etc/environment or one specified in sudoers.
101
 */
102
struct sudoers_env_file {
103
    void * (*open)(const char *);
104
    void   (*close)(void *);
105
    char * (*next)(void *, int *);
106
};
107
108
/*
109
 * State for a local environment file.
110
 */
111
struct env_file_local {
112
    FILE *fp;
113
    char *line;
114
    size_t linesize;
115
};
116
117
struct environment {
118
    char **envp;    /* pointer to the new environment */
119
    char **old_envp;    /* pointer the old environment we allocated */
120
    size_t env_size;    /* size of new_environ in char **'s */
121
    size_t env_len;   /* number of slots used, not counting NULL */
122
};
123
124
/*
125
 * Copy of the sudo-managed environment.
126
 */
127
static struct environment env;
128
129
/*
130
 * Default table of "bad" variables to remove from the environment.
131
 * XXX - how to omit TERMCAP if it starts with '/'?
132
 */
133
static const char *initial_badenv_table[] = {
134
    "IFS",
135
    "CDPATH",
136
    "LOCALDOMAIN",
137
    "RES_OPTIONS",
138
    "HOSTALIASES",
139
    "NLSPATH",
140
    "PATH_LOCALE",
141
    "LD_*",
142
    "_RLD*",
143
#ifdef __hpux
144
    "SHLIB_PATH",
145
#endif /* __hpux */
146
#ifdef _AIX
147
    "LDR_*",
148
    "LIBPATH",
149
    "AUTHSTATE",
150
#endif
151
#ifdef __APPLE__
152
    "DYLD_*",
153
#endif
154
#ifdef HAVE_KERB5
155
    "KRB5_CONFIG*",
156
    "KRB5_KTNAME",
157
#endif /* HAVE_KERB5 */
158
#ifdef HAVE_SECURID
159
    "VAR_ACE",
160
    "USR_ACE",
161
    "DLC_ACE",
162
#endif /* HAVE_SECURID */
163
    "TERMINFO",     /* terminfo, exclusive path to terminfo files */
164
    "TERMINFO_DIRS",    /* terminfo, path(s) to terminfo files */
165
    "TERMPATH",     /* termcap, path(s) to termcap files */
166
    "TERMCAP",      /* XXX - only if it starts with '/' */
167
    "ENV",      /* ksh, file to source before script runs */
168
    "BASH_ENV",     /* bash, file to source before script runs */
169
    "PS4",      /* bash, prefix for lines in xtrace mode */
170
    "GLOBIGNORE",   /* bash, globbing patterns to ignore */
171
    "BASHOPTS",     /* bash, initial "shopt -s" options */
172
    "SHELLOPTS",    /* bash, initial "set -o" options */
173
    "JAVA_TOOL_OPTIONS",  /* java, extra command line options */
174
    "PERLIO_DEBUG",   /* perl, debugging output file */
175
    "PERLLIB",      /* perl, search path for modules/includes */
176
    "PERL5LIB",     /* perl 5, search path for modules/includes */
177
    "PERL5OPT",     /* perl 5, extra command line options */
178
    "PERL5DB",      /* perl 5, command used to load debugger */
179
    "FPATH",      /* ksh, search path for functions */
180
    "NULLCMD",      /* zsh, command for null file redirection */
181
    "READNULLCMD",    /* zsh, command for null file redirection */
182
    "ZDOTDIR",      /* zsh, search path for dot files */
183
    "TMPPREFIX",    /* zsh, prefix for temporary files */
184
    "PYTHONHOME",   /* python, module search path */
185
    "PYTHONPATH",   /* python, search path */
186
    "PYTHONINSPECT",    /* python, allow inspection */
187
    "PYTHONUSERBASE",   /* python, per user site-packages directory */
188
    "RUBYLIB",      /* ruby, library load path */
189
    "RUBYOPT",      /* ruby, extra command line options */
190
    "*=()*",      /* bash functions */
191
    NULL
192
};
193
194
/*
195
 * Default table of variables to check for '%' and '/' characters.
196
 */
197
static const char *initial_checkenv_table[] = {
198
    "COLORTERM",
199
    "LANG",
200
    "LANGUAGE",
201
    "LC_*",
202
    "LINGUAS",
203
    "TERM",
204
    "TZ",
205
    NULL
206
};
207
208
/*
209
 * Default table of variables to preserve in the environment.
210
 */
211
static const char *initial_keepenv_table[] = {
212
    "COLORS",
213
    "DISPLAY",
214
    "HOSTNAME",
215
    "KRB5CCNAME",
216
    "LS_COLORS",
217
    "PATH",
218
    "PS1",
219
    "PS2",
220
    "XAUTHORITY",
221
    "XAUTHORIZATION",
222
    "XDG_CURRENT_DESKTOP",
223
    NULL
224
};
225
226
/*
227
 * Free our copy (or copies) of the environment.
228
 * This function is only safe to call after the command has executed.
229
 */
230
void
231
env_free(void)
232
25.1k
{
233
25.1k
    sudoers_gc_remove(GC_PTR, env.envp);
234
25.1k
    free(env.envp);
235
25.1k
    sudoers_gc_remove(GC_PTR, env.old_envp);
236
25.1k
    free(env.old_envp);
237
25.1k
    memset(&env, 0, sizeof(env));
238
25.1k
}
239
240
/*
241
 * Initialize env based on envp.
242
 */
243
bool
244
env_init(char * const envp[])
245
51.4k
{
246
51.4k
    char * const *ep;
247
51.4k
    size_t len;
248
51.4k
    debug_decl(env_init, SUDOERS_DEBUG_ENV);
249
250
51.4k
    if (envp == NULL) {
251
  /* Free the old envp we allocated, if any. */
252
26.3k
  sudoers_gc_remove(GC_PTR, env.old_envp);
253
26.3k
  free(env.old_envp);
254
255
  /*
256
   * Reset to initial state but keep a pointer to what we allocated
257
   * since it will be passed to execve(2).
258
   */
259
26.3k
  env.old_envp = env.envp;
260
26.3k
  env.envp = NULL;
261
26.3k
  env.env_size = 0;
262
26.3k
  env.env_len = 0;
263
26.3k
    } else {
264
  /* Make private copy of envp. */
265
954k
  for (ep = envp; *ep != NULL; ep++)
266
928k
      continue;
267
25.1k
  len = (size_t)(ep - envp);
268
269
25.1k
  env.env_len = len;
270
25.1k
  env.env_size = len + 1 + 128;
271
25.1k
  env.envp = reallocarray(NULL, env.env_size, sizeof(char *));
272
25.1k
  if (env.envp == NULL) {
273
0
      env.env_size = 0;
274
0
      env.env_len = 0;
275
0
      sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
276
0
      debug_return_bool(false);
277
0
  }
278
25.1k
  sudoers_gc_add(GC_PTR, env.envp);
279
#ifdef ENV_DEBUG
280
  memset(env.envp, 0, env.env_size * sizeof(char *));
281
#endif
282
25.1k
  memcpy(env.envp, envp, len * sizeof(char *));
283
25.1k
  env.envp[len] = NULL;
284
285
  /* Free the old envp we allocated, if any. */
286
25.1k
  sudoers_gc_remove(GC_PTR, env.old_envp);
287
25.1k
  free(env.old_envp);
288
25.1k
  env.old_envp = NULL;
289
25.1k
    }
290
291
51.4k
    debug_return_bool(true);
292
51.4k
}
293
294
/*
295
 * Getter for private copy of the environment.
296
 */
297
char **
298
env_get(void)
299
13.0k
{
300
13.0k
    return env.envp;
301
13.0k
}
302
303
/*
304
 * Swap the old and new copies of the environment.
305
 */
306
bool
307
env_swap_old(void)
308
586
{
309
586
    char **old_envp;
310
311
586
    if (env.old_envp == NULL)
312
274
  return false;
313
312
    old_envp = env.old_envp;
314
312
    env.old_envp = env.envp;
315
312
    env.envp = old_envp;
316
312
    return true;
317
586
}
318
319
/*
320
 * Similar to putenv(3) but operates on sudo's private copy of the
321
 * environment (not environ) and it always overwrites.  The dupcheck param
322
 * determines whether we need to verify that the variable is not already set.
323
 * Will only overwrite an existing variable if overwrite is set.
324
 * Does not include warnings or debugging to avoid recursive calls.
325
 */
326
int
327
sudo_putenv_nodebug(char *str, bool dupcheck, bool overwrite)
328
389k
{
329
389k
    char **ep;
330
389k
    const char *equal;
331
389k
    bool found = false;
332
333
    /* Some putenv(3) implementations check for NULL. */
334
389k
    if (str == NULL) {
335
0
  errno = EINVAL;
336
0
  return -1;
337
0
    }
338
339
    /* The string must contain a '=' char but not start with one. */
340
389k
    equal = strchr(str, '=');
341
389k
    if (equal == NULL || equal == str) {
342
109
  errno = EINVAL;
343
109
  return -1;
344
109
    }
345
346
    /* Make sure there is room for the new entry plus a NULL. */
347
389k
    if (env.env_size > 2 && env.env_len > env.env_size - 2) {
348
91
  char **nenvp;
349
91
  size_t nsize;
350
351
91
  if (env.env_size > SIZE_MAX - 128) {
352
0
      sudo_warnx_nodebug(U_("internal error, %s overflow"),
353
0
    "sudo_putenv_nodebug");
354
0
      errno = EOVERFLOW;
355
0
      return -1;
356
0
  }
357
91
  nsize = env.env_size + 128;
358
91
  if (nsize > SIZE_MAX / sizeof(char *)) {
359
0
      sudo_warnx_nodebug(U_("internal error, %s overflow"),
360
0
    "sudo_putenv_nodebug");
361
0
      errno = EOVERFLOW;
362
0
      return -1;
363
0
  }
364
91
  sudoers_gc_remove(GC_PTR, env.envp);
365
91
  nenvp = reallocarray(env.envp, nsize, sizeof(char *));
366
91
  if (nenvp == NULL) {
367
0
      sudoers_gc_add(GC_PTR, env.envp);
368
0
      return -1;
369
0
  }
370
91
  sudoers_gc_add(GC_PTR, nenvp);
371
91
  env.envp = nenvp;
372
91
  env.env_size = nsize;
373
#ifdef ENV_DEBUG
374
  memset(env.envp + env.env_len, 0,
375
      (env.env_size - env.env_len) * sizeof(char *));
376
#endif
377
91
    }
378
379
#ifdef ENV_DEBUG
380
    if (env.envp[env.env_len] != NULL) {
381
  errno = EINVAL;
382
  return -1;
383
    }
384
#endif
385
386
389k
    if (dupcheck) {
387
313k
  size_t len = (size_t)(equal - str) + 1;
388
5.50M
  for (ep = env.envp; *ep != NULL; ep++) {
389
5.21M
      if (strncmp(str, *ep, len) == 0) {
390
23.1k
    if (overwrite)
391
10.4k
        *ep = str;
392
23.1k
    found = true;
393
23.1k
    break;
394
23.1k
      }
395
5.21M
  }
396
  /* Prune out extra instances of the variable we just overwrote. */
397
313k
  if (found && overwrite) {
398
99.3k
      while (*++ep != NULL) {
399
88.9k
    if (strncmp(str, *ep, len) == 0) {
400
20
        char **cur = ep;
401
538
        while ((*cur = *(cur + 1)) != NULL)
402
518
      cur++;
403
20
        ep--;
404
20
    }
405
88.9k
      }
406
10.4k
      env.env_len = (size_t)(ep - env.envp);
407
10.4k
  }
408
313k
    }
409
410
389k
    if (!found) {
411
366k
  ep = env.envp + env.env_len;
412
366k
  env.env_len++;
413
366k
  *ep++ = str;
414
366k
  *ep = NULL;
415
366k
    }
416
389k
    return 0;
417
389k
}
418
419
/*
420
 * Similar to putenv(3) but operates on sudo's private copy of the
421
 * environment (not environ) and it always overwrites.  The dupcheck param
422
 * determines whether we need to verify that the variable is not already set.
423
 * Will only overwrite an existing variable if overwrite is set.
424
 */
425
static int
426
sudo_putenv(char *str, bool dupcheck, bool overwrite)
427
389k
{
428
389k
    int ret;
429
389k
    debug_decl(sudo_putenv, SUDOERS_DEBUG_ENV);
430
431
389k
    sudo_debug_printf(SUDO_DEBUG_INFO, "sudo_putenv: %s", str);
432
433
389k
    ret = sudo_putenv_nodebug(str, dupcheck, overwrite);
434
389k
    if (ret == -1) {
435
#ifdef ENV_DEBUG
436
  if (env.envp[env.env_len] != NULL) {
437
      sudo_warnx("%s",
438
    U_("sudo_putenv: corrupted envp, length mismatch"));
439
  }
440
#endif
441
109
    }
442
389k
    debug_return_int(ret);
443
389k
}
444
445
/*
446
 * Similar to setenv(3) but operates on a private copy of the environment.
447
 * The dupcheck param determines whether we need to verify that the variable
448
 * is not already set.
449
 */
450
static int
451
sudo_setenv2(const char *var, const char *val, bool dupcheck, bool overwrite)
452
224k
{
453
224k
    char *estring;
454
224k
    size_t esize;
455
224k
    int ret = -1;
456
224k
    debug_decl(sudo_setenv2, SUDOERS_DEBUG_ENV);
457
458
224k
    esize = strlen(var) + 1 + strlen(val) + 1;
459
224k
    if ((estring = malloc(esize)) == NULL) {
460
0
  sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
461
0
      "unable to allocate memory");
462
0
  debug_return_int(-1);
463
0
    }
464
465
    /* Build environment string and insert it. */
466
224k
    if (strlcpy(estring, var, esize) >= esize ||
467
224k
  strlcat(estring, "=", esize) >= esize ||
468
224k
  strlcat(estring, val, esize) >= esize) {
469
470
0
  sudo_warnx(U_("internal error, %s overflow"), __func__);
471
0
  errno = EOVERFLOW;
472
224k
    } else {
473
224k
  ret = sudo_putenv(estring, dupcheck, overwrite);
474
224k
    }
475
224k
    if (ret == -1)
476
0
  free(estring);
477
224k
    else
478
224k
  sudoers_gc_add(GC_PTR, estring);
479
224k
    debug_return_int(ret);
480
224k
}
481
482
/*
483
 * Similar to setenv(3) but operates on a private copy of the environment.
484
 */
485
int
486
sudo_setenv(const char *var, const char *val, int overwrite)
487
0
{
488
0
    return sudo_setenv2(var, val, true, (bool)overwrite);
489
0
}
490
491
/*
492
 * Similar to unsetenv(3) but operates on a private copy of the environment.
493
 * Does not include warnings or debugging to avoid recursive calls.
494
 */
495
int
496
sudo_unsetenv_nodebug(const char *var)
497
0
{
498
0
    char **ep = env.envp;
499
0
    size_t len;
500
501
0
    if (ep == NULL || var == NULL || *var == '\0' || strchr(var, '=') != NULL) {
502
0
  errno = EINVAL;
503
0
  return -1;
504
0
    }
505
506
0
    len = strlen(var);
507
0
    while (*ep != NULL) {
508
0
  if (strncmp(var, *ep, len) == 0 && (*ep)[len] == '=') {
509
      /* Found it; shift remainder + NULL over by one. */
510
0
      char **cur = ep;
511
0
      while ((*cur = *(cur + 1)) != NULL)
512
0
    cur++;
513
0
      env.env_len--;
514
      /* Keep going, could be multiple instances of the var. */
515
0
  } else {
516
0
      ep++;
517
0
  }
518
0
    }
519
0
    return 0;
520
0
}
521
522
/*
523
 * Similar to unsetenv(3) but operates on a private copy of the environment.
524
 */
525
int
526
sudo_unsetenv(const char *name)
527
0
{
528
0
    int ret;
529
0
    debug_decl(sudo_unsetenv, SUDOERS_DEBUG_ENV);
530
531
0
    sudo_debug_printf(SUDO_DEBUG_INFO, "sudo_unsetenv: %s", name);
532
533
0
    ret = sudo_unsetenv_nodebug(name);
534
535
0
    debug_return_int(ret);
536
0
}
537
538
/*
539
 * Similar to getenv(3) but operates on a private copy of the environment.
540
 * Does not include warnings or debugging to avoid recursive calls.
541
 */
542
char *
543
sudo_getenv_nodebug(const char *name)
544
0
{
545
0
    char **ep, *val = NULL;
546
0
    size_t namelen = 0;
547
548
0
    if (env.env_len != 0) {
549
  /* For BSD compatibility, treat '=' in name like end of string. */
550
0
  while (name[namelen] != '\0' && name[namelen] != '=')
551
0
      namelen++;
552
0
  for (ep = env.envp; *ep != NULL; ep++) {
553
0
      if (strncmp(*ep, name, namelen) == 0 && (*ep)[namelen] == '=') {
554
0
    val = *ep + namelen + 1;
555
0
    break;
556
0
      }
557
0
  }
558
0
    }
559
0
    return val;
560
0
}
561
562
/*
563
 * Similar to getenv(3) but operates on a private copy of the environment.
564
 */
565
char *
566
sudo_getenv(const char *name)
567
0
{
568
0
    char *val;
569
0
    debug_decl(sudo_getenv, SUDOERS_DEBUG_ENV);
570
571
0
    sudo_debug_printf(SUDO_DEBUG_INFO, "sudo_getenv: %s", name);
572
573
0
    val = sudo_getenv_nodebug(name);
574
575
0
    debug_return_str(val);
576
0
}
577
578
/*
579
 * Check for var against patterns in the specified environment list.
580
 * Returns true if the variable was found, else false.
581
 */
582
static bool
583
matches_env_list(const char *var, struct list_members *list, bool *full_match)
584
1.03M
{
585
1.03M
    struct list_member *cur;
586
1.03M
    bool is_logname = false;
587
1.03M
    debug_decl(matches_env_list, SUDOERS_DEBUG_ENV);
588
589
1.03M
    switch (*var) {
590
32.2k
    case 'L':
591
32.2k
  if (strncmp(var, "LOGNAME=", 8) == 0)
592
184
      is_logname = true;
593
#ifdef _AIX
594
  else if (strncmp(var, "LOGIN=", 6) == 0)
595
      is_logname = true;
596
#endif
597
32.2k
  break;
598
28.3k
    case 'U':
599
28.3k
  if (strncmp(var, "USER=", 5) == 0)
600
1.04k
      is_logname = true;
601
28.3k
  break;
602
1.03M
    }
603
604
1.03M
    if (is_logname) {
605
  /*
606
   * We treat LOGIN, LOGNAME and USER specially.
607
   * If one is preserved/deleted we want to preserve/delete them all.
608
   */
609
11.0k
  SLIST_FOREACH(cur, list, entries) {
610
11.0k
      if (matches_env_pattern(cur->value, "LOGNAME", full_match) ||
611
#ifdef _AIX
612
    matches_env_pattern(cur->value, "LOGIN", full_match) ||
613
#endif
614
11.0k
    matches_env_pattern(cur->value, "USER", full_match))
615
0
    debug_return_bool(true);
616
11.0k
  }
617
1.03M
    } else {
618
9.76M
  SLIST_FOREACH(cur, list, entries) {
619
9.76M
      if (matches_env_pattern(cur->value, var, full_match))
620
48.3k
    debug_return_bool(true);
621
9.76M
  }
622
1.03M
    }
623
989k
    debug_return_bool(false);
624
989k
}
625
626
/*
627
 * Check the env_delete blocklist.
628
 * Returns true if the variable was found, else false.
629
 */
630
static bool
631
matches_env_delete(const char *var)
632
24.1k
{
633
24.1k
    bool full_match;  /* unused */
634
24.1k
    debug_decl(matches_env_delete, SUDOERS_DEBUG_ENV);
635
636
    /* Skip anything listed in env_delete. */
637
24.1k
    debug_return_bool(matches_env_list(var, &def_env_delete, &full_match));
638
24.1k
}
639
640
/*
641
 * Verify the TZ environment variable is safe.
642
 * On many systems it is possible to set this to a pathname.
643
 */
644
static bool
645
tz_is_safe(const char *tzval)
646
5.37k
{
647
5.37k
    const char *cp;
648
5.37k
    char lastch;
649
5.37k
    debug_decl(tz_is_safe, SUDOERS_DEBUG_ENV);
650
651
    /* tzcode treats a value beginning with a ':' as a path. */
652
5.37k
    if (tzval[0] == ':')
653
220
  tzval++;
654
655
    /* Reject fully-qualified TZ that doesn't being with the zoneinfo dir. */
656
5.37k
    if (tzval[0] == '/') {
657
1.28k
#ifdef _PATH_ZONEINFO
658
1.28k
  if (strncmp(tzval, _PATH_ZONEINFO, sizeof(_PATH_ZONEINFO) - 1) != 0 ||
659
476
      tzval[sizeof(_PATH_ZONEINFO) - 1] != '/')
660
1.08k
      debug_return_bool(false);
661
#else
662
  /* Assume the worst. */
663
  debug_return_bool(false);
664
#endif
665
1.28k
    }
666
667
    /*
668
     * Make sure TZ only contains printable non-space characters
669
     * and does not contain a '..' path element.
670
     */
671
4.28k
    lastch = '/';
672
814k
    for (cp = tzval; *cp != '\0'; cp++) {
673
811k
  if (isspace((unsigned char)*cp) || !isprint((unsigned char)*cp))
674
560
      debug_return_bool(false);
675
810k
  if (lastch == '/' && cp[0] == '.' && cp[1] == '.' &&
676
736
      (cp[2] == '/' || cp[2] == '\0'))
677
476
      debug_return_bool(false);
678
810k
  lastch = *cp;
679
810k
    }
680
681
    /* Reject extra long TZ values (even if not a path). */
682
3.25k
    if ((size_t)(cp - tzval) >= PATH_MAX)
683
188
  debug_return_bool(false);
684
685
3.06k
    debug_return_bool(true);
686
3.06k
}
687
688
/*
689
 * Apply the env_check list.
690
 * Returns true if the variable is allowed, false if denied
691
 * or -1 if no match.
692
 */
693
static int
694
matches_env_check(const char *var, bool *full_match)
695
523k
{
696
523k
    int keepit = -1;
697
523k
    debug_decl(matches_env_check, SUDOERS_DEBUG_ENV);
698
699
    /* Skip anything listed in env_check that includes '/' or '%'. */
700
523k
    if (matches_env_list(var, &def_env_check, full_match)) {
701
8.99k
  if (strncmp(var, "TZ=", 3) == 0) {
702
      /* Special case for TZ */
703
5.37k
      keepit = tz_is_safe(var + 3);
704
5.37k
  } else {
705
3.62k
      const char *val = strchr(var, '=');
706
3.62k
      if (val != NULL)
707
3.62k
    keepit = !strpbrk(val + 1, "/%");
708
3.62k
  }
709
8.99k
    }
710
523k
    debug_return_int(keepit);
711
523k
}
712
713
/*
714
 * Check the env_keep list.
715
 * Returns true if the variable is allowed else false.
716
 */
717
static bool
718
matches_env_keep(const struct sudoers_context *ctx, const char *var,
719
    bool *full_match)
720
490k
{
721
490k
    bool keepit = false;
722
490k
    debug_decl(matches_env_keep, SUDOERS_DEBUG_ENV);
723
724
    /* Preserve SHELL variable for "sudo -s". */
725
490k
    if (ISSET(ctx->mode, MODE_SHELL) && strncmp(var, "SHELL=", 6) == 0) {
726
200
  keepit = true;
727
490k
    } else if (matches_env_list(var, &def_env_keep, full_match)) {
728
39.3k
  keepit = true;
729
39.3k
    }
730
490k
    debug_return_bool(keepit);
731
490k
}
732
733
/*
734
 * Look up var in the env_delete and env_check.
735
 * Returns true if we should delete the variable, else false.
736
 */
737
static bool
738
env_should_delete(const char *var)
739
24.1k
{
740
24.1k
    int delete_it;
741
24.1k
    bool full_match = false;
742
24.1k
    debug_decl(env_should_delete, SUDOERS_DEBUG_ENV);
743
744
24.1k
    delete_it = matches_env_delete(var);
745
24.1k
    if (!delete_it)
746
24.1k
  delete_it = matches_env_check(var, &full_match) == false;
747
748
24.1k
    sudo_debug_printf(SUDO_DEBUG_INFO, "delete %s: %s",
749
24.1k
  var, delete_it ? "YES" : "NO");
750
24.1k
    debug_return_bool(delete_it);
751
24.1k
}
752
753
/*
754
 * Lookup var in the env_check and env_keep lists.
755
 * Returns true if the variable is allowed else false.
756
 */
757
static bool
758
env_should_keep(const struct sudoers_context *ctx, const char *var)
759
499k
{
760
499k
    int keepit;
761
499k
    bool full_match = false;
762
499k
    const char *cp;
763
499k
    debug_decl(env_should_keep, SUDOERS_DEBUG_ENV);
764
765
499k
    keepit = matches_env_check(var, &full_match);
766
499k
    if (keepit == -1)
767
490k
  keepit = matches_env_keep(ctx, var, &full_match);
768
769
    /* Skip bash functions unless we matched on the value as well as name. */
770
499k
    if (keepit && !full_match) {
771
46.2k
  if ((cp = strchr(var, '=')) != NULL) {
772
46.2k
      if (strncmp(cp, "=() ", 4) == 0)
773
196
    keepit = false;
774
46.2k
  }
775
46.2k
    }
776
499k
    sudo_debug_printf(SUDO_DEBUG_INFO, "keep %s: %s",
777
499k
  var, keepit == true ? "YES" : "NO");
778
499k
    debug_return_bool(keepit == true);
779
499k
}
780
781
#ifdef HAVE_PAM
782
/*
783
 * Merge another environment with our private copy.
784
 * Only overwrite an existing variable if it is not
785
 * being preserved from the user's environment.
786
 * Returns true on success or false on failure.
787
 */
788
bool
789
env_merge(const struct sudoers_context *ctx, char * const envp[])
790
{
791
    char * const *ep;
792
    bool ret = true;
793
    debug_decl(env_merge, SUDOERS_DEBUG_ENV);
794
795
    for (ep = envp; *ep != NULL; ep++) {
796
  /* XXX - avoid checking value here, should only check name */
797
  bool overwrite = def_env_reset ? !env_should_keep(ctx, *ep) : env_should_delete(*ep);
798
  if (sudo_putenv(*ep, true, overwrite) == -1) {
799
      /* XXX cannot undo on failure */
800
      ret = false;
801
      break;
802
  }
803
    }
804
    debug_return_bool(ret);
805
}
806
#endif /* HAVE_PAM */
807
808
static void
809
env_update_didvar(const char *ep, unsigned int *didvar)
810
62.2k
{
811
62.2k
    switch (*ep) {
812
12.8k
  case 'H':
813
12.8k
      if (strncmp(ep, "HOME=", 5) == 0)
814
0
    SET(*didvar, DID_HOME);
815
12.8k
      break;
816
0
  case 'L':
817
#ifdef _AIX
818
      if (strncmp(ep, "LOGIN=", 8) == 0)
819
    SET(*didvar, DID_LOGIN);
820
#endif
821
0
      if (strncmp(ep, "LOGNAME=", 8) == 0)
822
0
    SET(*didvar, DID_LOGNAME);
823
0
      break;
824
0
  case 'M':
825
0
      if (strncmp(ep, "MAIL=", 5) == 0)
826
0
    SET(*didvar, DID_MAIL);
827
0
      break;
828
36.5k
  case 'P':
829
36.5k
      if (strncmp(ep, "PATH=", 5) == 0)
830
36.5k
    SET(*didvar, DID_PATH);
831
36.5k
      break;
832
0
  case 'S':
833
0
      if (strncmp(ep, "SHELL=", 6) == 0)
834
0
    SET(*didvar, DID_SHELL);
835
0
      break;
836
0
  case 'T':
837
0
      if (strncmp(ep, "TERM=", 5) == 0)
838
0
    SET(*didvar, DID_TERM);
839
0
      break;
840
0
  case 'U':
841
0
      if (strncmp(ep, "USER=", 5) == 0)
842
0
    SET(*didvar, DID_USER);
843
0
      break;
844
62.2k
    }
845
62.2k
}
846
847
87.7k
#define CHECK_PUTENV(a, b, c) do {                \
848
87.7k
    if (sudo_putenv((char *)(a), (b), (c)) == -1) {            \
849
0
  goto bad;                    \
850
0
    }                         \
851
87.7k
} while (0)
852
853
224k
#define CHECK_SETENV2(a, b, c, d) do {              \
854
224k
    if (sudo_setenv2((char *)(a), (b), (c), (d)) == -1) {          \
855
0
  goto bad;                    \
856
0
    }                         \
857
224k
} while (0)
858
859
/*
860
 * Build a new environment and either clear potentially dangerous
861
 * variables from the old one or start with a clean slate.
862
 * Also adds sudo-specific variables (SUDO_*).
863
 * Returns true on success or false on failure.
864
 */
865
bool
866
rebuild_env(const struct sudoers_context *ctx)
867
25.0k
{
868
25.0k
    char **ep, *cp, *ps1;
869
25.0k
    char idbuf[STRLEN_MAX_UNSIGNED(uid_t) + 1];
870
25.0k
    unsigned int didvar;
871
25.0k
    bool reset_home = false;
872
25.0k
    int len;
873
25.0k
    debug_decl(rebuild_env, SUDOERS_DEBUG_ENV);
874
875
    /*
876
     * Either clean out the environment or reset to a safe default.
877
     */
878
25.0k
    ps1 = NULL;
879
25.0k
    didvar = 0;
880
25.0k
    env.env_len = 0;
881
25.0k
    env.env_size = 128;
882
25.0k
    sudoers_gc_remove(GC_PTR, env.old_envp);
883
25.0k
    free(env.old_envp);
884
25.0k
    env.old_envp = env.envp;
885
25.0k
    env.envp = reallocarray(NULL, env.env_size, sizeof(char *));
886
25.0k
    if (env.envp == NULL) {
887
0
  sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
888
0
      "unable to allocate memory");
889
0
  env.env_size = 0;
890
0
  goto bad;
891
0
    }
892
25.0k
    sudoers_gc_add(GC_PTR, env.envp);
893
#ifdef ENV_DEBUG
894
    memset(env.envp, 0, env.env_size * sizeof(char *));
895
#else
896
25.0k
    env.envp[0] = NULL;
897
25.0k
#endif
898
899
    /* Reset HOME based on target user if configured to. */
900
25.0k
    if (ISSET(ctx->mode, MODE_RUN)) {
901
11.8k
  if (def_always_set_home ||
902
11.8k
      ISSET(ctx->mode, MODE_RESET_HOME | MODE_LOGIN_SHELL) || 
903
11.5k
      (ISSET(ctx->mode, MODE_SHELL) && def_set_home))
904
343
      reset_home = true;
905
11.8k
    }
906
907
25.0k
    if (def_env_reset || ISSET(ctx->mode, MODE_LOGIN_SHELL)) {
908
  /*
909
   * If starting with a fresh environment, initialize it based on
910
   * /etc/environment or login.conf.  For "sudo -i" we want those
911
   * variables to override the invoking user's environment, so we
912
   * defer reading them until later.
913
   */
914
24.0k
  if (!ISSET(ctx->mode, MODE_LOGIN_SHELL)) {
915
#ifdef HAVE_LOGIN_CAP_H
916
      /* Insert login class environment variables. */
917
      if (ctx->runas.class) {
918
    login_cap_t *lc = login_getclass(ctx->runas.class);
919
    if (lc != NULL) {
920
        setusercontext(lc, ctx->runas.pw,
921
      ctx->runas.pw->pw_uid, LOGIN_SETPATH|LOGIN_SETENV);
922
        login_close(lc);
923
    }
924
      }
925
#endif /* HAVE_LOGIN_CAP_H */
926
23.7k
#ifdef _PATH_ENVIRONMENT
927
      /* Insert system-wide environment variables. */
928
23.7k
      if (!read_env_file(ctx, _PATH_ENVIRONMENT, true, false))
929
0
    sudo_warn("%s", _PATH_ENVIRONMENT);
930
23.7k
#endif
931
47.4k
      for (ep = env.envp; *ep; ep++)
932
23.7k
    env_update_didvar(*ep, &didvar);
933
23.7k
  }
934
935
  /* Pull in vars we want to keep from the old environment. */
936
24.0k
  if (env.old_envp != NULL) {
937
488k
      for (ep = env.old_envp; *ep; ep++) {
938
475k
    bool keepit;
939
940
    /*
941
     * Look up the variable in the env_check and env_keep lists.
942
     */
943
475k
    keepit = env_should_keep(ctx, *ep);
944
945
    /*
946
     * Do SUDO_PS1 -> PS1 conversion.
947
     * This must happen *after* env_should_keep() is called.
948
     */
949
475k
    if (strncmp(*ep, "SUDO_PS1=", 9) == 0)
950
0
        ps1 = *ep + 5;
951
952
475k
    if (keepit) {
953
        /* Preserve variable. */
954
38.5k
        CHECK_PUTENV(*ep, true, false);
955
38.5k
        env_update_didvar(*ep, &didvar);
956
38.5k
    }
957
475k
      }
958
12.8k
  }
959
24.0k
  didvar |= didvar << 16;   /* convert DID_* to KEPT_* */
960
961
  /*
962
   * Add in defaults.  In -i mode these come from the runas user,
963
   * otherwise they may be from the user's environment (depends
964
   * on sudoers options).
965
   */
966
24.0k
  if (ISSET(ctx->mode, MODE_LOGIN_SHELL)) {
967
315
      CHECK_SETENV2("SHELL", ctx->runas.pw->pw_shell,
968
315
    ISSET(didvar, DID_SHELL), true);
969
#ifdef _AIX
970
      CHECK_SETENV2("LOGIN", ctx->runas.pw->pw_name,
971
    ISSET(didvar, DID_LOGIN), true);
972
#endif
973
315
      CHECK_SETENV2("LOGNAME", ctx->runas.pw->pw_name,
974
315
    ISSET(didvar, DID_LOGNAME), true);
975
315
      CHECK_SETENV2("USER", ctx->runas.pw->pw_name,
976
315
    ISSET(didvar, DID_USER), true);
977
23.7k
  } else {
978
      /* We will set LOGNAME later in the def_set_logname case. */
979
23.7k
      if (!def_set_logname) {
980
#ifdef _AIX
981
    if (!ISSET(didvar, DID_LOGIN))
982
        CHECK_SETENV2("LOGIN", ctx->user.name, false, true);
983
#endif
984
0
    if (!ISSET(didvar, DID_LOGNAME))
985
0
        CHECK_SETENV2("LOGNAME", ctx->user.name, false, true);
986
0
    if (!ISSET(didvar, DID_USER))
987
0
        CHECK_SETENV2("USER", ctx->user.name, false, true);
988
0
      }
989
23.7k
  }
990
991
  /* If we didn't keep HOME, reset it based on target user. */
992
24.0k
  if (!ISSET(didvar, KEPT_HOME))
993
24.0k
      reset_home = true;
994
995
  /*
996
   * Set MAIL to target user in -i mode or if MAIL is not preserved
997
   * from user's environment.
998
   */
999
24.0k
  if (ISSET(ctx->mode, MODE_LOGIN_SHELL) || !ISSET(didvar, KEPT_MAIL)) {
1000
24.0k
      if (_PATH_MAILDIR[sizeof(_PATH_MAILDIR) - 2] == '/') {
1001
0
    len = asprintf(&cp, "MAIL=%s%s", _PATH_MAILDIR,
1002
0
        ctx->runas.pw->pw_name);
1003
24.0k
      } else {
1004
24.0k
    len = asprintf(&cp, "MAIL=%s/%s", _PATH_MAILDIR,
1005
24.0k
        ctx->runas.pw->pw_name);
1006
24.0k
      }
1007
24.0k
      if (len == -1)
1008
0
        goto bad;
1009
24.0k
      if (sudo_putenv(cp, ISSET(didvar, DID_MAIL), true) == -1) {
1010
0
    free(cp);
1011
0
    goto bad;
1012
0
      }
1013
24.0k
      sudoers_gc_add(GC_PTR, cp);
1014
24.0k
  }
1015
24.0k
    } else {
1016
  /*
1017
   * Copy environ entries as long as they don't match env_delete or
1018
   * env_check.
1019
   */
1020
1.07k
  if (env.old_envp != NULL) {
1021
24.7k
      for (ep = env.old_envp; *ep; ep++) {
1022
    /* Add variable unless it matches a blocklist. */
1023
24.1k
    if (!env_should_delete(*ep)) {
1024
24.1k
        if (strncmp(*ep, "SUDO_PS1=", 9) == 0)
1025
0
      ps1 = *ep + 5;
1026
24.1k
        else if (strncmp(*ep, "SHELL=", 6) == 0)
1027
0
      SET(didvar, DID_SHELL);
1028
24.1k
        else if (strncmp(*ep, "PATH=", 5) == 0)
1029
652
      SET(didvar, DID_PATH);
1030
23.4k
        else if (strncmp(*ep, "TERM=", 5) == 0)
1031
0
      SET(didvar, DID_TERM);
1032
24.1k
        CHECK_PUTENV(*ep, true, false);
1033
24.1k
    }
1034
24.1k
      }
1035
652
  }
1036
1.07k
    }
1037
    /* Replace the PATH envariable with a secure one? */
1038
25.0k
    if (def_secure_path && !user_is_exempt(ctx)) {
1039
0
  CHECK_SETENV2("PATH", def_secure_path, true, true);
1040
0
  SET(didvar, DID_PATH);
1041
0
    }
1042
1043
    /*
1044
     * Set LOGIN, LOGNAME, and USER to target if "set_logname" is not
1045
     * disabled.  We skip this if we are running a login shell (because
1046
     * they have already been set).
1047
     */
1048
25.0k
    if (def_set_logname && !ISSET(ctx->mode, MODE_LOGIN_SHELL)) {
1049
24.7k
  if ((didvar & KEPT_USER_VARIABLES) == 0) {
1050
      /* Nothing preserved, set them all. */
1051
#ifdef _AIX
1052
      CHECK_SETENV2("LOGIN", ctx->runas.pw->pw_name, true, true);
1053
#endif
1054
24.7k
      CHECK_SETENV2("LOGNAME", ctx->runas.pw->pw_name, true, true);
1055
24.7k
      CHECK_SETENV2("USER", ctx->runas.pw->pw_name, true, true);
1056
24.7k
  } else if ((didvar & KEPT_USER_VARIABLES) != KEPT_USER_VARIABLES) {
1057
      /*
1058
       * Preserved some of LOGIN, LOGNAME, USER but not all.
1059
       * Make the unset ones match so we don't end up with some
1060
       * set to the invoking user and others set to the runas user.
1061
       */
1062
0
      if (ISSET(didvar, KEPT_LOGNAME))
1063
0
    cp = sudo_getenv("LOGNAME");
1064
#ifdef _AIX
1065
      else if (ISSET(didvar, KEPT_LOGIN))
1066
    cp = sudo_getenv("LOGIN");
1067
#endif
1068
0
      else if (ISSET(didvar, KEPT_USER))
1069
0
    cp = sudo_getenv("USER");
1070
0
      else
1071
0
    cp = NULL;
1072
0
      if (cp != NULL) {
1073
#ifdef _AIX
1074
    if (!ISSET(didvar, KEPT_LOGIN))
1075
        CHECK_SETENV2("LOGIN", cp, true, true);
1076
#endif
1077
0
    if (!ISSET(didvar, KEPT_LOGNAME))
1078
0
        CHECK_SETENV2("LOGNAME", cp, true, true);
1079
0
    if (!ISSET(didvar, KEPT_USER))
1080
0
        CHECK_SETENV2("USER", cp, true, true);
1081
0
      }
1082
0
  }
1083
24.7k
    }
1084
1085
    /* Set $HOME to target user if not preserving user's value. */
1086
25.0k
    if (reset_home)
1087
24.0k
  CHECK_SETENV2("HOME", ctx->runas.pw->pw_dir, true, true);
1088
1089
    /* Provide default values for $SHELL, $TERM and $PATH if not set. */
1090
25.0k
    if (!ISSET(didvar, DID_SHELL))
1091
25.0k
  CHECK_SETENV2("SHELL", ctx->runas.pw->pw_shell, false, false);
1092
25.0k
    if (!ISSET(didvar, DID_TERM))
1093
25.0k
  CHECK_PUTENV("TERM=unknown", false, false);
1094
25.0k
    if (!ISSET(didvar, DID_PATH))
1095
561
  CHECK_SETENV2("PATH", _PATH_STDPATH, false, true);
1096
1097
    /* Set PS1 if SUDO_PS1 is set. */
1098
25.0k
    if (ps1 != NULL)
1099
0
  CHECK_PUTENV(ps1, true, true);
1100
1101
    /* Add the SUDO_COMMAND envariable (cmnd + args). */
1102
25.0k
    if (ctx->user.cmnd_args) {
1103
  /*
1104
   * We limit ctx->user.cmnd_args to 4096 bytes to avoid an execve(2)
1105
   * failure for very long argument vectors.  The command's environment
1106
   * also counts against the ARG_MAX limit.
1107
   */
1108
1.78k
  len = asprintf(&cp, "SUDO_COMMAND=%s %.*s", ctx->user.cmnd, 4096,
1109
1.78k
      ctx->user.cmnd_args);
1110
1.78k
  if (len == -1)
1111
0
      goto bad;
1112
1.78k
  if (sudo_putenv(cp, true, true) == -1) {
1113
0
      free(cp);
1114
0
      goto bad;
1115
0
  }
1116
1.78k
  sudoers_gc_add(GC_PTR, cp);
1117
23.3k
    } else {
1118
23.3k
  CHECK_SETENV2("SUDO_COMMAND", ctx->user.cmnd, true, true);
1119
23.3k
    }
1120
1121
    /* Add the SUDO_{USER,UID,GID,HOME,TTY} environment variables. */
1122
25.0k
    CHECK_SETENV2("SUDO_USER", ctx->user.name, true, true);
1123
25.0k
    (void)snprintf(idbuf, sizeof(idbuf), "%u", (unsigned int) ctx->user.uid);
1124
25.0k
    CHECK_SETENV2("SUDO_UID", idbuf, true, true);
1125
25.0k
    (void)snprintf(idbuf, sizeof(idbuf), "%u", (unsigned int) ctx->user.gid);
1126
25.0k
    CHECK_SETENV2("SUDO_GID", idbuf, true, true);
1127
25.0k
    CHECK_SETENV2("SUDO_HOME", ctx->user.pw->pw_dir, true, true);
1128
25.0k
    if (ctx->user.ttypath != NULL)
1129
566
  CHECK_SETENV2("SUDO_TTY", ctx->user.ttypath, true, true);
1130
1131
25.0k
    debug_return_bool(true);
1132
1133
0
bad:
1134
0
    sudo_warn("%s", U_("unable to rebuild the environment"));
1135
0
    debug_return_bool(false);
1136
0
}
1137
1138
/*
1139
 * Insert all environment variables in envp into the private copy
1140
 * of the environment.
1141
 * Returns true on success or false on failure.
1142
 */
1143
bool
1144
insert_env_vars(char * const envp[])
1145
5.80k
{
1146
5.80k
    char * const *ep;
1147
5.80k
    bool ret = true;
1148
5.80k
    debug_decl(insert_env_vars, SUDOERS_DEBUG_ENV);
1149
1150
    /* Add user-specified environment variables. */
1151
5.80k
    if (envp != NULL) {
1152
29.1k
  for (ep = envp; *ep != NULL; ep++) {
1153
      /* XXX - no undo on failure */
1154
27.5k
      if (sudo_putenv(*ep, true, true) == -1) {
1155
109
    ret = false;
1156
109
    break;
1157
109
      }
1158
27.5k
  }
1159
1.73k
    }
1160
5.80k
    debug_return_bool(ret);
1161
5.80k
}
1162
1163
/*
1164
 * Validate the list of environment variables passed in on the command
1165
 * line against env_delete, env_check, and env_keep.
1166
 * Calls log_warning() if any specified variables are not allowed.
1167
 * Returns true if allowed, else false.
1168
 */
1169
bool
1170
validate_env_vars(const struct sudoers_context *ctx, char * const env_vars[])
1171
6.55k
{
1172
6.55k
    char * const *ep;
1173
6.55k
    char errbuf[4096];
1174
6.55k
    char *errpos = errbuf;
1175
6.55k
    bool okvar, ret = true;
1176
6.55k
    debug_decl(validate_env_vars, SUDOERS_DEBUG_ENV);
1177
1178
6.55k
    if (env_vars == NULL)
1179
3.85k
  debug_return_bool(true); /* nothing to do */
1180
1181
    /* Add user-specified environment variables. */
1182
505k
    for (ep = env_vars; *ep != NULL; ep++) {
1183
502k
  char *eq = strchr(*ep, '=');
1184
502k
  if (eq == NULL || eq == *ep) {
1185
      /* Must be in the form var=val. */
1186
478k
      okvar = false;
1187
478k
  } else if (def_secure_path && !user_is_exempt(ctx) &&
1188
0
      strncmp(*ep, "PATH=", 5) == 0) {
1189
0
      okvar = false;
1190
23.6k
  } else if (def_env_reset) {
1191
23.6k
      okvar = env_should_keep(ctx, *ep);
1192
23.6k
  } else {
1193
0
      okvar = !env_should_delete(*ep);
1194
0
  }
1195
502k
  if (okvar == false) {
1196
      /* Not allowed, append to error buffer if space remains. */
1197
494k
      if (errpos < &errbuf[sizeof(errbuf)]) {
1198
15.1k
    const size_t varlen = strcspn(*ep, "=");
1199
15.1k
    const size_t errsize = sizeof(errbuf) - (size_t)(errpos - errbuf);
1200
15.1k
    int len = snprintf(errpos, errsize, "%s%.*s",
1201
15.1k
        errpos != errbuf ? ", " : "", (int)varlen, *ep);
1202
15.1k
    if (len >= ssizeof(errbuf) - (errpos - errbuf)) {
1203
48
        memcpy(&errbuf[sizeof(errbuf) - 4], "...", 4);
1204
48
        errpos = &errbuf[sizeof(errbuf)];
1205
15.1k
    } else {
1206
15.1k
        errpos += len;
1207
15.1k
    }
1208
15.1k
      }
1209
494k
  }
1210
502k
    }
1211
2.69k
    if (errpos != errbuf) {
1212
  /* XXX - audit? */
1213
1.70k
  log_warningx(ctx, 0,
1214
1.70k
      N_("sorry, you are not allowed to set the following environment variables: %s"), errbuf);
1215
1.70k
  ret = false;
1216
1.70k
    }
1217
2.69k
    debug_return_bool(ret);
1218
2.69k
}
1219
1220
static void *
1221
env_file_open_local(const char *path)
1222
35.4k
{
1223
35.4k
    struct env_file_local *efl;
1224
35.4k
    debug_decl(env_file_open_local, SUDOERS_DEBUG_ENV);
1225
1226
35.4k
    efl = calloc(1, sizeof(*efl));
1227
35.4k
    if (efl != NULL) {
1228
35.4k
  if ((efl->fp = fopen(path, "r")) == NULL) {
1229
0
      if (errno != ENOENT) {
1230
0
    free(efl);
1231
0
    efl = NULL;
1232
0
      }
1233
0
  }
1234
35.4k
    }
1235
35.4k
    debug_return_ptr(efl);
1236
35.4k
}
1237
1238
static void
1239
env_file_close_local(void *cookie)
1240
35.4k
{
1241
35.4k
    struct env_file_local *efl = cookie;
1242
35.4k
    debug_decl(env_file_close_local, SUDOERS_DEBUG_ENV);
1243
1244
35.4k
    if (efl != NULL) {
1245
35.4k
  if (efl->fp != NULL)
1246
35.4k
      fclose(efl->fp);
1247
35.4k
  free(efl->line);
1248
35.4k
  free(efl);
1249
35.4k
    }
1250
35.4k
    debug_return;
1251
35.4k
}
1252
1253
/*
1254
 * Parse /etc/environment lines ala AIX and Linux.
1255
 * Lines may be in either of three formats:
1256
 *  NAME=VALUE
1257
 *  NAME="VALUE"
1258
 *  NAME='VALUE'
1259
 * with an optional "export" prefix so the shell can source the file.
1260
 * Invalid lines, blank lines, or lines consisting solely of a comment
1261
 * character are skipped.
1262
 */
1263
static char *
1264
env_file_next_local(void *cookie, int *errnum)
1265
59.3k
{
1266
59.3k
    struct env_file_local *efl = cookie;
1267
59.3k
    char *var, *val, *ret = NULL;
1268
59.3k
    size_t var_len, val_len;
1269
59.3k
    debug_decl(env_file_next_local, SUDOERS_DEBUG_ENV);
1270
1271
59.3k
    *errnum = 0;
1272
59.3k
    if (efl->fp == NULL)
1273
0
  debug_return_ptr(NULL);
1274
1275
59.3k
    for (;;) {
1276
59.3k
  if (sudo_parseln(&efl->line, &efl->linesize, NULL, efl->fp, PARSELN_CONT_IGN) == -1) {
1277
35.4k
      if (!feof(efl->fp))
1278
0
    *errnum = errno;
1279
35.4k
      break;
1280
35.4k
  }
1281
1282
  /* Skip blank or comment lines */
1283
23.8k
  if (*(var = efl->line) == '\0')
1284
0
      continue;
1285
1286
  /* Skip optional "export " */
1287
23.8k
  if (strncmp(var, "export", 6) == 0 && isspace((unsigned char) var[6])) {
1288
0
      var += 7;
1289
0
      while (isspace((unsigned char) *var)) {
1290
0
    var++;
1291
0
      }
1292
0
  }
1293
1294
  /* Must be of the form name=["']value['"] */
1295
119k
  for (val = var; *val != '\0' && *val != '='; val++)
1296
95.4k
      continue;
1297
23.8k
  if (var == val || *val != '=')
1298
0
      continue;
1299
23.8k
  var_len = (size_t)(val - var);
1300
23.8k
  val_len = strlen(++val);
1301
1302
  /* Strip leading and trailing single/double quotes */
1303
23.8k
  if ((val[0] == '\'' || val[0] == '\"') && val_len > 1 && val[0] == val[val_len - 1]) {
1304
23.8k
      val[val_len - 1] = '\0';
1305
23.8k
      val++;
1306
23.8k
      val_len -= 2;
1307
23.8k
  }
1308
1309
23.8k
  if ((ret = malloc(var_len + 1 + val_len + 1)) == NULL) {
1310
0
      *errnum = errno;
1311
0
      sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
1312
0
    "unable to allocate memory");
1313
23.8k
  } else {
1314
23.8k
      memcpy(ret, var, var_len + 1); /* includes '=' */
1315
23.8k
      memcpy(ret + var_len + 1, val, val_len + 1); /* includes NUL */
1316
23.8k
      sudoers_gc_add(GC_PTR, ret);
1317
23.8k
  }
1318
23.8k
  break;
1319
23.8k
    }
1320
59.3k
    debug_return_str(ret);
1321
59.3k
}
1322
1323
static struct sudoers_env_file env_file_sudoers = {
1324
    env_file_open_local,
1325
    env_file_close_local,
1326
    env_file_next_local
1327
};
1328
1329
static struct sudoers_env_file env_file_system = {
1330
    env_file_open_local,
1331
    env_file_close_local,
1332
    env_file_next_local
1333
};
1334
1335
void
1336
register_env_file(void * (*ef_open)(const char *), void (*ef_close)(void *),
1337
    char * (*ef_next)(void *, int *), bool sys)
1338
0
{
1339
0
    struct sudoers_env_file *ef = sys ? &env_file_system : &env_file_sudoers;
1340
1341
0
    ef->open = ef_open;
1342
0
    ef->close = ef_close;
1343
0
    ef->next = ef_next;
1344
0
}
1345
1346
bool
1347
read_env_file(const struct sudoers_context *ctx, const char *path,
1348
    bool overwrite, bool restricted)
1349
35.4k
{
1350
35.4k
    struct sudoers_env_file *ef;
1351
35.4k
    bool ret = true;
1352
35.4k
    char *envstr;
1353
35.4k
    void *cookie;
1354
35.4k
    int errnum;
1355
35.4k
    debug_decl(read_env_file, SUDOERS_DEBUG_ENV);
1356
1357
    /*
1358
     * The environment file may be handled differently depending on
1359
     * whether it is specified in sudoers or the system.
1360
     */
1361
35.4k
    if (path == def_env_file || path == def_restricted_env_file)
1362
11.6k
  ef = &env_file_sudoers;
1363
23.8k
    else
1364
23.8k
  ef = &env_file_system;
1365
1366
35.4k
    cookie = ef->open(path);
1367
35.4k
    if (cookie == NULL)
1368
0
  debug_return_bool(false);
1369
1370
59.3k
    for (;;) {
1371
  /* Keep reading until EOF or error. */
1372
59.3k
  if ((envstr = ef->next(cookie, &errnum)) == NULL) {
1373
35.4k
      if (errnum != 0)
1374
0
    ret = false;
1375
35.4k
      break;
1376
35.4k
  }
1377
1378
  /*
1379
   * If the env file is restricted, apply env_check and env_keep
1380
   * when env_reset is set or env_delete when it is not.
1381
   */
1382
23.8k
  if (restricted) {
1383
0
      if (def_env_reset ? !env_should_keep(ctx, envstr) : env_should_delete(envstr)) {
1384
0
    free(envstr);
1385
0
    continue;
1386
0
      }
1387
0
  }
1388
23.8k
  if (sudo_putenv(envstr, true, overwrite) == -1) {
1389
      /* XXX - no undo on failure */
1390
0
      ret = false;
1391
0
      break;
1392
0
  }
1393
23.8k
    }
1394
35.4k
    ef->close(cookie);
1395
1396
35.4k
    debug_return_bool(ret);
1397
35.4k
}
1398
1399
bool
1400
init_envtables(void)
1401
30.8k
{
1402
30.8k
    struct list_member *cur;
1403
30.8k
    const char **p;
1404
30.8k
    debug_decl(init_envtables, SUDOERS_DEBUG_ENV);
1405
1406
    /* Fill in the "env_delete" list. */
1407
1.17M
    for (p = initial_badenv_table; *p; p++) {
1408
1.14M
  cur = calloc(1, sizeof(struct list_member));
1409
1.14M
  if (cur == NULL || (cur->value = strdup(*p)) == NULL) {
1410
0
      sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
1411
0
    "unable to allocate memory");
1412
0
      free(cur);
1413
0
      debug_return_bool(false);
1414
0
  }
1415
1.14M
  SLIST_INSERT_HEAD(&def_env_delete, cur, entries);
1416
1.14M
    }
1417
1418
    /* Fill in the "env_check" list. */
1419
247k
    for (p = initial_checkenv_table; *p; p++) {
1420
216k
  cur = calloc(1, sizeof(struct list_member));
1421
216k
  if (cur == NULL || (cur->value = strdup(*p)) == NULL) {
1422
0
      sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
1423
0
    "unable to allocate memory");
1424
0
      free(cur);
1425
0
      debug_return_bool(false);
1426
0
  }
1427
216k
  SLIST_INSERT_HEAD(&def_env_check, cur, entries);
1428
216k
    }
1429
1430
    /* Fill in the "env_keep" list. */
1431
370k
    for (p = initial_keepenv_table; *p; p++) {
1432
339k
  cur = calloc(1, sizeof(struct list_member));
1433
339k
  if (cur == NULL || (cur->value = strdup(*p)) == NULL) {
1434
0
      sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
1435
0
    "unable to allocate memory");
1436
0
      free(cur);
1437
0
      debug_return_bool(false);
1438
0
  }
1439
339k
  SLIST_INSERT_HEAD(&def_env_keep, cur, entries);
1440
339k
    }
1441
30.8k
    debug_return_bool(true);
1442
30.8k
}