Coverage Report

Created: 2026-01-09 06:36

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
25.6k
#define KEPT_LOGNAME  0x00100000
80
#undef KEPT_USER
81
25.6k
#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
25.6k
# 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.3k
{
233
25.3k
    sudoers_gc_remove(GC_PTR, env.envp);
234
25.3k
    free(env.envp);
235
25.3k
    sudoers_gc_remove(GC_PTR, env.old_envp);
236
25.3k
    free(env.old_envp);
237
25.3k
    memset(&env, 0, sizeof(env));
238
25.3k
}
239
240
/*
241
 * Initialize env based on envp.
242
 */
243
bool
244
env_init(char * const envp[])
245
52.6k
{
246
52.6k
    char * const *ep;
247
52.6k
    size_t len;
248
52.6k
    debug_decl(env_init, SUDOERS_DEBUG_ENV);
249
250
52.6k
    if (envp == NULL) {
251
  /* Free the old envp we allocated, if any. */
252
27.3k
  sudoers_gc_remove(GC_PTR, env.old_envp);
253
27.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
27.3k
  env.old_envp = env.envp;
260
27.3k
  env.envp = NULL;
261
27.3k
  env.env_size = 0;
262
27.3k
  env.env_len = 0;
263
27.3k
    } else {
264
  /* Make private copy of envp. */
265
912k
  for (ep = envp; *ep != NULL; ep++)
266
886k
      continue;
267
25.3k
  len = (size_t)(ep - envp);
268
269
25.3k
  env.env_len = len;
270
25.3k
  env.env_size = len + 1 + 128;
271
25.3k
  env.envp = reallocarray(NULL, env.env_size, sizeof(char *));
272
25.3k
  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.3k
  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.3k
  memcpy(env.envp, envp, len * sizeof(char *));
283
25.3k
  env.envp[len] = NULL;
284
285
  /* Free the old envp we allocated, if any. */
286
25.3k
  sudoers_gc_remove(GC_PTR, env.old_envp);
287
25.3k
  free(env.old_envp);
288
25.3k
  env.old_envp = NULL;
289
25.3k
    }
290
291
52.6k
    debug_return_bool(true);
292
52.6k
}
293
294
/*
295
 * Getter for private copy of the environment.
296
 */
297
char **
298
env_get(void)
299
13.3k
{
300
13.3k
    return env.envp;
301
13.3k
}
302
303
/*
304
 * Swap the old and new copies of the environment.
305
 */
306
bool
307
env_swap_old(void)
308
460
{
309
460
    char **old_envp;
310
311
460
    if (env.old_envp == NULL)
312
212
  return false;
313
248
    old_envp = env.old_envp;
314
248
    env.old_envp = env.envp;
315
248
    env.envp = old_envp;
316
248
    return true;
317
460
}
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
420k
{
329
420k
    char **ep;
330
420k
    const char *equal;
331
420k
    bool found = false;
332
333
    /* Some putenv(3) implementations check for NULL. */
334
420k
    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
420k
    equal = strchr(str, '=');
341
420k
    if (equal == NULL || equal == str) {
342
119
  errno = EINVAL;
343
119
  return -1;
344
119
    }
345
346
    /* Make sure there is room for the new entry plus a NULL. */
347
420k
    if (env.env_size > 2 && env.env_len > env.env_size - 2) {
348
126
  char **nenvp;
349
126
  size_t nsize;
350
351
126
  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
126
  nsize = env.env_size + 128;
358
126
  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
126
  sudoers_gc_remove(GC_PTR, env.envp);
365
126
  nenvp = reallocarray(env.envp, nsize, sizeof(char *));
366
126
  if (nenvp == NULL) {
367
0
      sudoers_gc_add(GC_PTR, env.envp);
368
0
      return -1;
369
0
  }
370
126
  sudoers_gc_add(GC_PTR, nenvp);
371
126
  env.envp = nenvp;
372
126
  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
126
    }
378
379
#ifdef ENV_DEBUG
380
    if (env.envp[env.env_len] != NULL) {
381
  errno = EINVAL;
382
  return -1;
383
    }
384
#endif
385
386
420k
    if (dupcheck) {
387
342k
  size_t len = (size_t)(equal - str) + 1;
388
6.67M
  for (ep = env.envp; *ep != NULL; ep++) {
389
6.37M
      if (strncmp(str, *ep, len) == 0) {
390
43.0k
    if (overwrite)
391
29.7k
        *ep = str;
392
43.0k
    found = true;
393
43.0k
    break;
394
43.0k
      }
395
6.37M
  }
396
  /* Prune out extra instances of the variable we just overwrote. */
397
342k
  if (found && overwrite) {
398
2.29M
      while (*++ep != NULL) {
399
2.26M
    if (strncmp(str, *ep, len) == 0) {
400
0
        char **cur = ep;
401
0
        while ((*cur = *(cur + 1)) != NULL)
402
0
      cur++;
403
0
        ep--;
404
0
    }
405
2.26M
      }
406
29.7k
      env.env_len = (size_t)(ep - env.envp);
407
29.7k
  }
408
342k
    }
409
410
420k
    if (!found) {
411
377k
  ep = env.envp + env.env_len;
412
377k
  env.env_len++;
413
377k
  *ep++ = str;
414
377k
  *ep = NULL;
415
377k
    }
416
420k
    return 0;
417
420k
}
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
420k
{
428
420k
    int ret;
429
420k
    debug_decl(sudo_putenv, SUDOERS_DEBUG_ENV);
430
431
420k
    sudo_debug_printf(SUDO_DEBUG_INFO, "sudo_putenv: %s", str);
432
433
420k
    ret = sudo_putenv_nodebug(str, dupcheck, overwrite);
434
420k
    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
119
    }
442
420k
    debug_return_int(ret);
443
420k
}
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
232k
{
453
232k
    char *estring;
454
232k
    size_t esize;
455
232k
    int ret = -1;
456
232k
    debug_decl(sudo_setenv2, SUDOERS_DEBUG_ENV);
457
458
232k
    esize = strlen(var) + 1 + strlen(val) + 1;
459
232k
    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
232k
    if (strlcpy(estring, var, esize) >= esize ||
467
232k
  strlcat(estring, "=", esize) >= esize ||
468
232k
  strlcat(estring, val, esize) >= esize) {
469
470
0
  sudo_warnx(U_("internal error, %s overflow"), __func__);
471
0
  errno = EOVERFLOW;
472
232k
    } else {
473
232k
  ret = sudo_putenv(estring, dupcheck, overwrite);
474
232k
    }
475
232k
    if (ret == -1)
476
0
  free(estring);
477
232k
    else
478
232k
  sudoers_gc_add(GC_PTR, estring);
479
232k
    debug_return_int(ret);
480
232k
}
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.06M
{
585
1.06M
    struct list_member *cur;
586
1.06M
    bool is_logname = false;
587
1.06M
    debug_decl(matches_env_list, SUDOERS_DEBUG_ENV);
588
589
1.06M
    switch (*var) {
590
50.4k
    case 'L':
591
50.4k
  if (strncmp(var, "LOGNAME=", 8) == 0)
592
128
      is_logname = true;
593
#ifdef _AIX
594
  else if (strncmp(var, "LOGIN=", 6) == 0)
595
      is_logname = true;
596
#endif
597
50.4k
  break;
598
30.1k
    case 'U':
599
30.1k
  if (strncmp(var, "USER=", 5) == 0)
600
880
      is_logname = true;
601
30.1k
  break;
602
1.06M
    }
603
604
1.06M
    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
9.07k
  SLIST_FOREACH(cur, list, entries) {
610
9.07k
      if (matches_env_pattern(cur->value, "LOGNAME", full_match) ||
611
#ifdef _AIX
612
    matches_env_pattern(cur->value, "LOGIN", full_match) ||
613
#endif
614
9.07k
    matches_env_pattern(cur->value, "USER", full_match))
615
0
    debug_return_bool(true);
616
9.07k
  }
617
1.06M
    } else {
618
9.69M
  SLIST_FOREACH(cur, list, entries) {
619
9.69M
      if (matches_env_pattern(cur->value, var, full_match))
620
71.3k
    debug_return_bool(true);
621
9.69M
  }
622
1.06M
    }
623
991k
    debug_return_bool(false);
624
991k
}
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
18.6k
{
633
18.6k
    bool full_match;  /* unused */
634
18.6k
    debug_decl(matches_env_delete, SUDOERS_DEBUG_ENV);
635
636
    /* Skip anything listed in env_delete. */
637
18.6k
    debug_return_bool(matches_env_list(var, &def_env_delete, &full_match));
638
18.6k
}
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
17.3k
{
647
17.3k
    const char *cp;
648
17.3k
    char lastch;
649
17.3k
    debug_decl(tz_is_safe, SUDOERS_DEBUG_ENV);
650
651
    /* tzcode treats a value beginning with a ':' as a path. */
652
17.3k
    if (tzval[0] == ':')
653
196
  tzval++;
654
655
    /* Reject fully-qualified TZ that doesn't being with the zoneinfo dir. */
656
17.3k
    if (tzval[0] == '/') {
657
1.34k
#ifdef _PATH_ZONEINFO
658
1.34k
  if (strncmp(tzval, _PATH_ZONEINFO, sizeof(_PATH_ZONEINFO) - 1) != 0 ||
659
552
      tzval[sizeof(_PATH_ZONEINFO) - 1] != '/')
660
1.01k
      debug_return_bool(false);
661
#else
662
  /* Assume the worst. */
663
  debug_return_bool(false);
664
#endif
665
1.34k
    }
666
667
    /*
668
     * Make sure TZ only contains printable non-space characters
669
     * and does not contain a '..' path element.
670
     */
671
16.3k
    lastch = '/';
672
1.05M
    for (cp = tzval; *cp != '\0'; cp++) {
673
1.04M
  if (isspace((unsigned char)*cp) || !isprint((unsigned char)*cp))
674
660
      debug_return_bool(false);
675
1.04M
  if (lastch == '/' && cp[0] == '.' && cp[1] == '.' &&
676
692
      (cp[2] == '/' || cp[2] == '\0'))
677
444
      debug_return_bool(false);
678
1.04M
  lastch = *cp;
679
1.04M
    }
680
681
    /* Reject extra long TZ values (even if not a path). */
682
15.2k
    if ((size_t)(cp - tzval) >= PATH_MAX)
683
188
  debug_return_bool(false);
684
685
15.0k
    debug_return_bool(true);
686
15.0k
}
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
546k
{
696
546k
    int keepit = -1;
697
546k
    debug_decl(matches_env_check, SUDOERS_DEBUG_ENV);
698
699
    /* Skip anything listed in env_check that includes '/' or '%'. */
700
546k
    if (matches_env_list(var, &def_env_check, full_match)) {
701
29.9k
  if (strncmp(var, "TZ=", 3) == 0) {
702
      /* Special case for TZ */
703
17.3k
      keepit = tz_is_safe(var + 3);
704
17.3k
  } else {
705
12.6k
      const char *val = strchr(var, '=');
706
12.6k
      if (val != NULL)
707
12.6k
    keepit = !strpbrk(val + 1, "/%");
708
12.6k
  }
709
29.9k
    }
710
546k
    debug_return_int(keepit);
711
546k
}
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
497k
{
721
497k
    bool keepit = false;
722
497k
    debug_decl(matches_env_keep, SUDOERS_DEBUG_ENV);
723
724
    /* Preserve SHELL variable for "sudo -s". */
725
497k
    if (ISSET(ctx->mode, MODE_SHELL) && strncmp(var, "SHELL=", 6) == 0) {
726
220
  keepit = true;
727
497k
    } else if (matches_env_list(var, &def_env_keep, full_match)) {
728
41.3k
  keepit = true;
729
41.3k
    }
730
497k
    debug_return_bool(keepit);
731
497k
}
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
18.6k
{
740
18.6k
    int delete_it;
741
18.6k
    bool full_match = false;
742
18.6k
    debug_decl(env_should_delete, SUDOERS_DEBUG_ENV);
743
744
18.6k
    delete_it = matches_env_delete(var);
745
18.6k
    if (!delete_it)
746
18.6k
  delete_it = matches_env_check(var, &full_match) == false;
747
748
18.6k
    sudo_debug_printf(SUDO_DEBUG_INFO, "delete %s: %s",
749
18.6k
  var, delete_it ? "YES" : "NO");
750
18.6k
    debug_return_bool(delete_it);
751
18.6k
}
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
527k
{
760
527k
    int keepit;
761
527k
    bool full_match = false;
762
527k
    const char *cp;
763
527k
    debug_decl(env_should_keep, SUDOERS_DEBUG_ENV);
764
765
527k
    keepit = matches_env_check(var, &full_match);
766
527k
    if (keepit == -1)
767
497k
  keepit = matches_env_keep(ctx, var, &full_match);
768
769
    /* Skip bash functions unless we matched on the value as well as name. */
770
527k
    if (keepit && !full_match) {
771
69.0k
  if ((cp = strchr(var, '=')) != NULL) {
772
69.0k
      if (strncmp(cp, "=() ", 4) == 0)
773
488
    keepit = false;
774
69.0k
  }
775
69.0k
    }
776
527k
    sudo_debug_printf(SUDO_DEBUG_INFO, "keep %s: %s",
777
527k
  var, keepit == true ? "YES" : "NO");
778
527k
    debug_return_bool(keepit == true);
779
527k
}
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
65.0k
{
811
65.0k
    switch (*ep) {
812
13.4k
  case 'H':
813
13.4k
      if (strncmp(ep, "HOME=", 5) == 0)
814
0
    SET(*didvar, DID_HOME);
815
13.4k
      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
38.2k
  case 'P':
829
38.2k
      if (strncmp(ep, "PATH=", 5) == 0)
830
38.2k
    SET(*didvar, DID_PATH);
831
38.2k
      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
65.0k
    }
845
65.0k
}
846
847
84.7k
#define CHECK_PUTENV(a, b, c) do {                \
848
84.7k
    if (sudo_putenv((char *)(a), (b), (c)) == -1) {            \
849
0
  goto bad;                    \
850
0
    }                         \
851
84.7k
} while (0)
852
853
232k
#define CHECK_SETENV2(a, b, c, d) do {              \
854
232k
    if (sudo_setenv2((char *)(a), (b), (c), (d)) == -1) {          \
855
0
  goto bad;                    \
856
0
    }                         \
857
232k
} 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.9k
{
868
25.9k
    char **ep, *cp, *ps1;
869
25.9k
    char idbuf[STRLEN_MAX_UNSIGNED(uid_t) + 1];
870
25.9k
    unsigned int didvar;
871
25.9k
    bool reset_home = false;
872
25.9k
    int len;
873
25.9k
    debug_decl(rebuild_env, SUDOERS_DEBUG_ENV);
874
875
    /*
876
     * Either clean out the environment or reset to a safe default.
877
     */
878
25.9k
    ps1 = NULL;
879
25.9k
    didvar = 0;
880
25.9k
    env.env_len = 0;
881
25.9k
    env.env_size = 128;
882
25.9k
    sudoers_gc_remove(GC_PTR, env.old_envp);
883
25.9k
    free(env.old_envp);
884
25.9k
    env.old_envp = env.envp;
885
25.9k
    env.envp = reallocarray(NULL, env.env_size, sizeof(char *));
886
25.9k
    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.9k
    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.9k
    env.envp[0] = NULL;
897
25.9k
#endif
898
899
    /* Reset HOME based on target user if configured to. */
900
25.9k
    if (ISSET(ctx->mode, MODE_RUN)) {
901
12.2k
  if (def_always_set_home ||
902
12.2k
      ISSET(ctx->mode, MODE_RESET_HOME | MODE_LOGIN_SHELL) || 
903
11.9k
      (ISSET(ctx->mode, MODE_SHELL) && def_set_home))
904
308
      reset_home = true;
905
12.2k
    }
906
907
25.9k
    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
25.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
24.8k
#ifdef _PATH_ENVIRONMENT
927
      /* Insert system-wide environment variables. */
928
24.8k
      if (!read_env_file(ctx, _PATH_ENVIRONMENT, true, false))
929
0
    sudo_warn("%s", _PATH_ENVIRONMENT);
930
24.8k
#endif
931
49.6k
      for (ep = env.envp; *ep; ep++)
932
24.8k
    env_update_didvar(*ep, &didvar);
933
24.8k
  }
934
935
  /* Pull in vars we want to keep from the old environment. */
936
25.0k
  if (env.old_envp != NULL) {
937
482k
      for (ep = env.old_envp; *ep; ep++) {
938
469k
    bool keepit;
939
940
    /*
941
     * Look up the variable in the env_check and env_keep lists.
942
     */
943
469k
    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
469k
    if (strncmp(*ep, "SUDO_PS1=", 9) == 0)
950
0
        ps1 = *ep + 5;
951
952
469k
    if (keepit) {
953
        /* Preserve variable. */
954
40.2k
        CHECK_PUTENV(*ep, true, false);
955
40.2k
        env_update_didvar(*ep, &didvar);
956
40.2k
    }
957
469k
      }
958
13.4k
  }
959
25.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
25.0k
  if (ISSET(ctx->mode, MODE_LOGIN_SHELL)) {
967
252
      CHECK_SETENV2("SHELL", ctx->runas.pw->pw_shell,
968
252
    ISSET(didvar, DID_SHELL), true);
969
252
      SET(didvar, DID_SHELL);
970
#ifdef _AIX
971
      CHECK_SETENV2("LOGIN", ctx->runas.pw->pw_name,
972
    ISSET(didvar, DID_LOGIN), true);
973
#endif
974
252
      CHECK_SETENV2("LOGNAME", ctx->runas.pw->pw_name,
975
252
    ISSET(didvar, DID_LOGNAME), true);
976
252
      CHECK_SETENV2("USER", ctx->runas.pw->pw_name,
977
252
    ISSET(didvar, DID_USER), true);
978
24.8k
  } else {
979
      /* We will set LOGNAME later in the def_set_logname case. */
980
24.8k
      if (!def_set_logname) {
981
#ifdef _AIX
982
    if (!ISSET(didvar, DID_LOGIN))
983
        CHECK_SETENV2("LOGIN", ctx->user.name, false, true);
984
#endif
985
0
    if (!ISSET(didvar, DID_LOGNAME))
986
0
        CHECK_SETENV2("LOGNAME", ctx->user.name, false, true);
987
0
    if (!ISSET(didvar, DID_USER))
988
0
        CHECK_SETENV2("USER", ctx->user.name, false, true);
989
0
      }
990
24.8k
  }
991
992
  /* If we didn't keep HOME, reset it based on target user. */
993
25.0k
  if (!ISSET(didvar, KEPT_HOME))
994
25.0k
      reset_home = true;
995
996
  /*
997
   * Set MAIL to target user in -i mode or if MAIL is not preserved
998
   * from user's environment.
999
   */
1000
25.0k
  if (ISSET(ctx->mode, MODE_LOGIN_SHELL) || !ISSET(didvar, KEPT_MAIL)) {
1001
25.0k
      if (_PATH_MAILDIR[sizeof(_PATH_MAILDIR) - 2] == '/') {
1002
0
    len = asprintf(&cp, "MAIL=%s%s", _PATH_MAILDIR,
1003
0
        ctx->runas.pw->pw_name);
1004
25.0k
      } else {
1005
25.0k
    len = asprintf(&cp, "MAIL=%s/%s", _PATH_MAILDIR,
1006
25.0k
        ctx->runas.pw->pw_name);
1007
25.0k
      }
1008
25.0k
      if (len == -1)
1009
0
        goto bad;
1010
25.0k
      if (sudo_putenv(cp, ISSET(didvar, DID_MAIL), true) == -1) {
1011
0
    free(cp);
1012
0
    goto bad;
1013
0
      }
1014
25.0k
      sudoers_gc_add(GC_PTR, cp);
1015
25.0k
  }
1016
25.0k
    } else {
1017
  /*
1018
   * Copy environ entries as long as they don't match env_delete or
1019
   * env_check.
1020
   */
1021
868
  if (env.old_envp != NULL) {
1022
19.1k
      for (ep = env.old_envp; *ep; ep++) {
1023
    /* Add variable unless it matches a blocklist. */
1024
18.6k
    if (!env_should_delete(*ep)) {
1025
18.6k
        if (strncmp(*ep, "SUDO_PS1=", 9) == 0)
1026
0
      ps1 = *ep + 5;
1027
18.6k
        else if (strncmp(*ep, "SHELL=", 6) == 0)
1028
0
      SET(didvar, DID_SHELL);
1029
18.6k
        else if (strncmp(*ep, "PATH=", 5) == 0)
1030
532
      SET(didvar, DID_PATH);
1031
18.0k
        else if (strncmp(*ep, "TERM=", 5) == 0)
1032
0
      SET(didvar, DID_TERM);
1033
18.6k
        CHECK_PUTENV(*ep, true, false);
1034
18.6k
    }
1035
18.6k
      }
1036
532
  }
1037
868
    }
1038
    /* Replace the PATH envariable with a secure one? */
1039
25.9k
    if (def_secure_path && !user_is_exempt(ctx)) {
1040
0
  CHECK_SETENV2("PATH", def_secure_path, true, true);
1041
0
  SET(didvar, DID_PATH);
1042
0
    }
1043
1044
    /*
1045
     * Set LOGIN, LOGNAME, and USER to target if "set_logname" is not
1046
     * disabled.  We skip this if we are running a login shell (because
1047
     * they have already been set).
1048
     */
1049
25.9k
    if (def_set_logname && !ISSET(ctx->mode, MODE_LOGIN_SHELL)) {
1050
25.6k
  if ((didvar & KEPT_USER_VARIABLES) == 0) {
1051
      /* Nothing preserved, set them all. */
1052
#ifdef _AIX
1053
      CHECK_SETENV2("LOGIN", ctx->runas.pw->pw_name, true, true);
1054
#endif
1055
25.6k
      CHECK_SETENV2("LOGNAME", ctx->runas.pw->pw_name, true, true);
1056
25.6k
      CHECK_SETENV2("USER", ctx->runas.pw->pw_name, true, true);
1057
25.6k
  } else if ((didvar & KEPT_USER_VARIABLES) != KEPT_USER_VARIABLES) {
1058
      /*
1059
       * Preserved some of LOGIN, LOGNAME, USER but not all.
1060
       * Make the unset ones match so we don't end up with some
1061
       * set to the invoking user and others set to the runas user.
1062
       */
1063
0
      if (ISSET(didvar, KEPT_LOGNAME))
1064
0
    cp = sudo_getenv("LOGNAME");
1065
#ifdef _AIX
1066
      else if (ISSET(didvar, KEPT_LOGIN))
1067
    cp = sudo_getenv("LOGIN");
1068
#endif
1069
0
      else if (ISSET(didvar, KEPT_USER))
1070
0
    cp = sudo_getenv("USER");
1071
0
      else
1072
0
    cp = NULL;
1073
0
      if (cp != NULL) {
1074
#ifdef _AIX
1075
    if (!ISSET(didvar, KEPT_LOGIN))
1076
        CHECK_SETENV2("LOGIN", cp, true, true);
1077
#endif
1078
0
    if (!ISSET(didvar, KEPT_LOGNAME))
1079
0
        CHECK_SETENV2("LOGNAME", cp, true, true);
1080
0
    if (!ISSET(didvar, KEPT_USER))
1081
0
        CHECK_SETENV2("USER", cp, true, true);
1082
0
      }
1083
0
  }
1084
25.6k
    }
1085
1086
    /* Set $HOME to target user if not preserving user's value. */
1087
25.9k
    if (reset_home)
1088
25.0k
  CHECK_SETENV2("HOME", ctx->runas.pw->pw_dir, true, true);
1089
1090
    /* Provide default values for $SHELL, $TERM and $PATH if not set. */
1091
25.9k
    if (!ISSET(didvar, DID_SHELL))
1092
25.6k
  CHECK_SETENV2("SHELL", ctx->runas.pw->pw_shell, false, false);
1093
25.9k
    if (!ISSET(didvar, DID_TERM))
1094
25.9k
  CHECK_PUTENV("TERM=unknown", false, false);
1095
25.9k
    if (!ISSET(didvar, DID_PATH))
1096
444
  CHECK_SETENV2("PATH", _PATH_STDPATH, false, true);
1097
1098
    /* Set PS1 if SUDO_PS1 is set. */
1099
25.9k
    if (ps1 != NULL)
1100
0
  CHECK_PUTENV(ps1, true, true);
1101
1102
    /* Add the SUDO_COMMAND envariable (cmnd + args). */
1103
25.9k
    if (ctx->user.cmnd_args) {
1104
  /*
1105
   * We limit ctx->user.cmnd_args to 4096 bytes to avoid an execve(2)
1106
   * failure for very long argument vectors.  The command's environment
1107
   * also counts against the ARG_MAX limit.
1108
   */
1109
1.35k
  len = asprintf(&cp, "SUDO_COMMAND=%s %.*s", ctx->user.cmnd, 4096,
1110
1.35k
      ctx->user.cmnd_args);
1111
1.35k
  if (len == -1)
1112
0
      goto bad;
1113
1.35k
  if (sudo_putenv(cp, true, true) == -1) {
1114
0
      free(cp);
1115
0
      goto bad;
1116
0
  }
1117
1.35k
  sudoers_gc_add(GC_PTR, cp);
1118
24.5k
    } else {
1119
24.5k
  CHECK_SETENV2("SUDO_COMMAND", ctx->user.cmnd, true, true);
1120
24.5k
    }
1121
1122
    /* Add the SUDO_{USER,UID,GID,HOME,TTY} environment variables. */
1123
25.9k
    CHECK_SETENV2("SUDO_USER", ctx->user.name, true, true);
1124
25.9k
    (void)snprintf(idbuf, sizeof(idbuf), "%u", (unsigned int) ctx->user.uid);
1125
25.9k
    CHECK_SETENV2("SUDO_UID", idbuf, true, true);
1126
25.9k
    (void)snprintf(idbuf, sizeof(idbuf), "%u", (unsigned int) ctx->user.gid);
1127
25.9k
    CHECK_SETENV2("SUDO_GID", idbuf, true, true);
1128
25.9k
    CHECK_SETENV2("SUDO_HOME", ctx->user.pw->pw_dir, true, true);
1129
25.9k
    if (ctx->user.ttypath != NULL)
1130
691
  CHECK_SETENV2("SUDO_TTY", ctx->user.ttypath, true, true);
1131
1132
25.9k
    debug_return_bool(true);
1133
1134
0
bad:
1135
0
    sudo_warn("%s", U_("unable to rebuild the environment"));
1136
0
    debug_return_bool(false);
1137
0
}
1138
1139
/*
1140
 * Insert all environment variables in envp into the private copy
1141
 * of the environment.
1142
 * Returns true on success or false on failure.
1143
 */
1144
bool
1145
insert_env_vars(char * const envp[])
1146
5.59k
{
1147
5.59k
    char * const *ep;
1148
5.59k
    bool ret = true;
1149
5.59k
    debug_decl(insert_env_vars, SUDOERS_DEBUG_ENV);
1150
1151
    /* Add user-specified environment variables. */
1152
5.59k
    if (envp != NULL) {
1153
54.0k
  for (ep = envp; *ep != NULL; ep++) {
1154
      /* XXX - no undo on failure */
1155
52.4k
      if (sudo_putenv(*ep, true, true) == -1) {
1156
119
    ret = false;
1157
119
    break;
1158
119
      }
1159
52.4k
  }
1160
1.77k
    }
1161
5.59k
    debug_return_bool(ret);
1162
5.59k
}
1163
1164
/*
1165
 * Validate the list of environment variables passed in on the command
1166
 * line against env_delete, env_check, and env_keep.
1167
 * Calls log_warning() if any specified variables are not allowed.
1168
 * Returns true if allowed, else false.
1169
 */
1170
bool
1171
validate_env_vars(const struct sudoers_context *ctx, char * const env_vars[])
1172
6.72k
{
1173
6.72k
    char * const *ep;
1174
6.72k
    char errbuf[4096];
1175
6.72k
    char *errpos = errbuf;
1176
6.72k
    bool okvar, ret = true;
1177
6.72k
    debug_decl(validate_env_vars, SUDOERS_DEBUG_ENV);
1178
1179
6.72k
    if (env_vars == NULL)
1180
3.61k
  debug_return_bool(true); /* nothing to do */
1181
1182
    /* Add user-specified environment variables. */
1183
541k
    for (ep = env_vars; *ep != NULL; ep++) {
1184
537k
  char *eq = strchr(*ep, '=');
1185
537k
  if (eq == NULL || eq == *ep) {
1186
      /* Must be in the form var=val. */
1187
479k
      okvar = false;
1188
479k
  } else if (def_secure_path && !user_is_exempt(ctx) &&
1189
0
      strncmp(*ep, "PATH=", 5) == 0) {
1190
0
      okvar = false;
1191
58.7k
  } else if (def_env_reset) {
1192
58.7k
      okvar = env_should_keep(ctx, *ep);
1193
58.7k
  } else {
1194
0
      okvar = !env_should_delete(*ep);
1195
0
  }
1196
537k
  if (okvar == false) {
1197
      /* Not allowed, append to error buffer if space remains. */
1198
509k
      if (errpos < &errbuf[sizeof(errbuf)]) {
1199
20.5k
    const size_t varlen = strcspn(*ep, "=");
1200
20.5k
    const size_t errsize = sizeof(errbuf) - (size_t)(errpos - errbuf);
1201
20.5k
    int len = snprintf(errpos, errsize, "%s%.*s",
1202
20.5k
        errpos != errbuf ? ", " : "", (int)varlen, *ep);
1203
20.5k
    if (len >= ssizeof(errbuf) - (errpos - errbuf)) {
1204
76
        memcpy(&errbuf[sizeof(errbuf) - 4], "...", 4);
1205
76
        errpos = &errbuf[sizeof(errbuf)];
1206
20.4k
    } else {
1207
20.4k
        errpos += len;
1208
20.4k
    }
1209
20.5k
      }
1210
509k
  }
1211
537k
    }
1212
3.11k
    if (errpos != errbuf) {
1213
  /* XXX - audit? */
1214
1.88k
  log_warningx(ctx, 0,
1215
1.88k
      N_("sorry, you are not allowed to set the following environment variables: %s"), errbuf);
1216
1.88k
  ret = false;
1217
1.88k
    }
1218
3.11k
    debug_return_bool(ret);
1219
3.11k
}
1220
1221
static void *
1222
env_file_open_local(const char *path)
1223
36.1k
{
1224
36.1k
    struct env_file_local *efl;
1225
36.1k
    debug_decl(env_file_open_local, SUDOERS_DEBUG_ENV);
1226
1227
36.1k
    efl = calloc(1, sizeof(*efl));
1228
36.1k
    if (efl != NULL) {
1229
36.1k
  if ((efl->fp = fopen(path, "r")) == NULL) {
1230
0
      if (errno != ENOENT) {
1231
0
    free(efl);
1232
0
    efl = NULL;
1233
0
      }
1234
0
  }
1235
36.1k
    }
1236
36.1k
    debug_return_ptr(efl);
1237
36.1k
}
1238
1239
static void
1240
env_file_close_local(void *cookie)
1241
36.1k
{
1242
36.1k
    struct env_file_local *efl = cookie;
1243
36.1k
    debug_decl(env_file_close_local, SUDOERS_DEBUG_ENV);
1244
1245
36.1k
    if (efl != NULL) {
1246
36.1k
  if (efl->fp != NULL)
1247
36.1k
      fclose(efl->fp);
1248
36.1k
  free(efl->line);
1249
36.1k
  free(efl);
1250
36.1k
    }
1251
36.1k
    debug_return;
1252
36.1k
}
1253
1254
/*
1255
 * Parse /etc/environment lines ala AIX and Linux.
1256
 * Lines may be in either of three formats:
1257
 *  NAME=VALUE
1258
 *  NAME="VALUE"
1259
 *  NAME='VALUE'
1260
 * with an optional "export" prefix so the shell can source the file.
1261
 * Invalid lines, blank lines, or lines consisting solely of a comment
1262
 * character are skipped.
1263
 */
1264
static char *
1265
env_file_next_local(void *cookie, int *errnum)
1266
61.0k
{
1267
61.0k
    struct env_file_local *efl = cookie;
1268
61.0k
    char *var, *val, *ret = NULL;
1269
61.0k
    size_t var_len, val_len;
1270
61.0k
    debug_decl(env_file_next_local, SUDOERS_DEBUG_ENV);
1271
1272
61.0k
    *errnum = 0;
1273
61.0k
    if (efl->fp == NULL)
1274
0
  debug_return_ptr(NULL);
1275
1276
61.0k
    for (;;) {
1277
61.0k
  if (sudo_parseln(&efl->line, &efl->linesize, NULL, efl->fp, PARSELN_CONT_IGN) == -1) {
1278
36.1k
      if (!feof(efl->fp))
1279
0
    *errnum = errno;
1280
36.1k
      break;
1281
36.1k
  }
1282
1283
  /* Skip blank or comment lines */
1284
24.9k
  if (*(var = efl->line) == '\0')
1285
0
      continue;
1286
1287
  /* Skip optional "export " */
1288
24.9k
  if (strncmp(var, "export", 6) == 0 && isspace((unsigned char) var[6])) {
1289
0
      var += 7;
1290
0
      while (isspace((unsigned char) *var)) {
1291
0
    var++;
1292
0
      }
1293
0
  }
1294
1295
  /* Must be of the form name=["']value['"] */
1296
124k
  for (val = var; *val != '\0' && *val != '='; val++)
1297
99.7k
      continue;
1298
24.9k
  if (var == val || *val != '=')
1299
0
      continue;
1300
24.9k
  var_len = (size_t)(val - var);
1301
24.9k
  val_len = strlen(++val);
1302
1303
  /* Strip leading and trailing single/double quotes */
1304
24.9k
  if ((val[0] == '\'' || val[0] == '\"') && val_len > 1 && val[0] == val[val_len - 1]) {
1305
24.9k
      val[val_len - 1] = '\0';
1306
24.9k
      val++;
1307
24.9k
      val_len -= 2;
1308
24.9k
  }
1309
1310
24.9k
  if ((ret = malloc(var_len + 1 + val_len + 1)) == NULL) {
1311
0
      *errnum = errno;
1312
0
      sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
1313
0
    "unable to allocate memory");
1314
24.9k
  } else {
1315
24.9k
      memcpy(ret, var, var_len + 1); /* includes '=' */
1316
24.9k
      memcpy(ret + var_len + 1, val, val_len + 1); /* includes NUL */
1317
24.9k
      sudoers_gc_add(GC_PTR, ret);
1318
24.9k
  }
1319
24.9k
  break;
1320
24.9k
    }
1321
61.0k
    debug_return_str(ret);
1322
61.0k
}
1323
1324
static struct sudoers_env_file env_file_sudoers = {
1325
    env_file_open_local,
1326
    env_file_close_local,
1327
    env_file_next_local
1328
};
1329
1330
static struct sudoers_env_file env_file_system = {
1331
    env_file_open_local,
1332
    env_file_close_local,
1333
    env_file_next_local
1334
};
1335
1336
void
1337
register_env_file(void * (*ef_open)(const char *), void (*ef_close)(void *),
1338
    char * (*ef_next)(void *, int *), bool sys)
1339
0
{
1340
0
    struct sudoers_env_file *ef = sys ? &env_file_system : &env_file_sudoers;
1341
1342
0
    ef->open = ef_open;
1343
0
    ef->close = ef_close;
1344
0
    ef->next = ef_next;
1345
0
}
1346
1347
bool
1348
read_env_file(const struct sudoers_context *ctx, const char *path,
1349
    bool overwrite, bool restricted)
1350
36.1k
{
1351
36.1k
    struct sudoers_env_file *ef;
1352
36.1k
    bool ret = true;
1353
36.1k
    char *envstr;
1354
36.1k
    void *cookie;
1355
36.1k
    int errnum;
1356
36.1k
    debug_decl(read_env_file, SUDOERS_DEBUG_ENV);
1357
1358
    /*
1359
     * The environment file may be handled differently depending on
1360
     * whether it is specified in sudoers or the system.
1361
     */
1362
36.1k
    if (path == def_env_file || path == def_restricted_env_file)
1363
11.1k
  ef = &env_file_sudoers;
1364
24.9k
    else
1365
24.9k
  ef = &env_file_system;
1366
1367
36.1k
    cookie = ef->open(path);
1368
36.1k
    if (cookie == NULL)
1369
0
  debug_return_bool(false);
1370
1371
61.0k
    for (;;) {
1372
  /* Keep reading until EOF or error. */
1373
61.0k
  if ((envstr = ef->next(cookie, &errnum)) == NULL) {
1374
36.1k
      if (errnum != 0)
1375
0
    ret = false;
1376
36.1k
      break;
1377
36.1k
  }
1378
1379
  /*
1380
   * If the env file is restricted, apply env_check and env_keep
1381
   * when env_reset is set or env_delete when it is not.
1382
   */
1383
24.9k
  if (restricted) {
1384
0
      if (def_env_reset ? !env_should_keep(ctx, envstr) : env_should_delete(envstr)) {
1385
0
    free(envstr);
1386
0
    continue;
1387
0
      }
1388
0
  }
1389
24.9k
  if (sudo_putenv(envstr, true, overwrite) == -1) {
1390
      /* XXX - no undo on failure */
1391
0
      ret = false;
1392
0
      break;
1393
0
  }
1394
24.9k
    }
1395
36.1k
    ef->close(cookie);
1396
1397
36.1k
    debug_return_bool(ret);
1398
36.1k
}
1399
1400
bool
1401
init_envtables(void)
1402
31.2k
{
1403
31.2k
    struct list_member *cur;
1404
31.2k
    const char **p;
1405
31.2k
    debug_decl(init_envtables, SUDOERS_DEBUG_ENV);
1406
1407
    /* Fill in the "env_delete" list. */
1408
1.18M
    for (p = initial_badenv_table; *p; p++) {
1409
1.15M
  cur = calloc(1, sizeof(struct list_member));
1410
1.15M
  if (cur == NULL || (cur->value = strdup(*p)) == NULL) {
1411
0
      sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
1412
0
    "unable to allocate memory");
1413
0
      free(cur);
1414
0
      debug_return_bool(false);
1415
0
  }
1416
1.15M
  SLIST_INSERT_HEAD(&def_env_delete, cur, entries);
1417
1.15M
    }
1418
1419
    /* Fill in the "env_check" list. */
1420
249k
    for (p = initial_checkenv_table; *p; p++) {
1421
218k
  cur = calloc(1, sizeof(struct list_member));
1422
218k
  if (cur == NULL || (cur->value = strdup(*p)) == NULL) {
1423
0
      sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
1424
0
    "unable to allocate memory");
1425
0
      free(cur);
1426
0
      debug_return_bool(false);
1427
0
  }
1428
218k
  SLIST_INSERT_HEAD(&def_env_check, cur, entries);
1429
218k
    }
1430
1431
    /* Fill in the "env_keep" list. */
1432
374k
    for (p = initial_keepenv_table; *p; p++) {
1433
343k
  cur = calloc(1, sizeof(struct list_member));
1434
343k
  if (cur == NULL || (cur->value = strdup(*p)) == NULL) {
1435
0
      sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
1436
0
    "unable to allocate memory");
1437
0
      free(cur);
1438
0
      debug_return_bool(false);
1439
0
  }
1440
343k
  SLIST_INSERT_HEAD(&def_env_keep, cur, entries);
1441
343k
    }
1442
31.2k
    debug_return_bool(true);
1443
31.2k
}