Coverage Report

Created: 2026-06-10 06:37

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
23.9k
#define KEPT_LOGNAME  0x00100000
80
#undef KEPT_USER
81
23.9k
#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
23.9k
# 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
    "_JAVA_OPTIONS",    /* java, extra command line options (legacy) */
175
    "CLASSPATH",    /* java, class search path */
176
    "PERLIO_DEBUG",   /* perl, debugging output file */
177
    "PERLLIB",      /* perl, search path for modules/includes */
178
    "PERL5LIB",     /* perl 5, search path for modules/includes */
179
    "PERL5OPT",     /* perl 5, extra command line options */
180
    "PERL5DB",      /* perl 5, command used to load debugger */
181
    "FPATH",      /* ksh, search path for functions */
182
    "NULLCMD",      /* zsh, command for null file redirection */
183
    "READNULLCMD",    /* zsh, command for null file redirection */
184
    "ZDOTDIR",      /* zsh, search path for dot files */
185
    "TMPPREFIX",    /* zsh, prefix for temporary files */
186
    "PYTHONHOME",   /* python, module search path */
187
    "PYTHONPATH",   /* python, search path */
188
    "PYTHONINSPECT",    /* python, allow inspection */
189
    "PYTHONUSERBASE",   /* python, per user site-packages directory */
190
    "PYTHONSTARTUP",    /* python, interactive mode startup script */
191
    "RUBYLIB",      /* ruby, library load path */
192
    "RUBYOPT",      /* ruby, extra command line options */
193
    "NODE_OPTIONS",   /* node.js, extra command line options */
194
    "NODE_PATH",    /* node.js, module search path */
195
    "GIT_SSH_COMMAND",    /* git, custom SSH command */
196
    "GIT_CONFIG_*",   /* git, global configuration override */
197
    "GCONV_PATH",   /* glibc generic char set conversion iface */
198
    "*=()*",      /* bash functions */
199
    NULL
200
};
201
202
/*
203
 * Default table of variables to check for '%' and '/' characters.
204
 */
205
static const char *initial_checkenv_table[] = {
206
    "COLORTERM",
207
    "LANG",
208
    "LANGUAGE",
209
    "LC_*",
210
    "LINGUAS",
211
    "TERM",
212
    "TZ",
213
    NULL
214
};
215
216
/*
217
 * Default table of variables to preserve in the environment.
218
 */
219
static const char *initial_keepenv_table[] = {
220
    "COLORS",
221
    "DISPLAY",
222
    "HOSTNAME",
223
    "KRB5CCNAME",
224
    "LS_COLORS",
225
    "PATH",
226
    "PS1",
227
    "PS2",
228
    "XAUTHORITY",
229
    "XAUTHORIZATION",
230
    "XDG_CURRENT_DESKTOP",
231
    NULL
232
};
233
234
/*
235
 * Free our copy (or copies) of the environment.
236
 * This function is only safe to call after the command has executed.
237
 */
238
void
239
env_free(void)
240
23.8k
{
241
23.8k
    sudoers_gc_remove(GC_PTR, env.envp);
242
23.8k
    free(env.envp);
243
23.8k
    sudoers_gc_remove(GC_PTR, env.old_envp);
244
23.8k
    free(env.old_envp);
245
23.8k
    memset(&env, 0, sizeof(env));
246
23.8k
}
247
248
/*
249
 * Initialize env based on envp.
250
 */
251
bool
252
env_init(char * const envp[])
253
49.5k
{
254
49.5k
    char * const *ep;
255
49.5k
    size_t len;
256
49.5k
    debug_decl(env_init, SUDOERS_DEBUG_ENV);
257
258
49.5k
    if (envp == NULL) {
259
  /* Free the old envp we allocated, if any. */
260
25.6k
  sudoers_gc_remove(GC_PTR, env.old_envp);
261
25.6k
  free(env.old_envp);
262
263
  /*
264
   * Reset to initial state but keep a pointer to what we allocated
265
   * since it will be passed to execve(2).
266
   */
267
25.6k
  env.old_envp = env.envp;
268
25.6k
  env.envp = NULL;
269
25.6k
  env.env_size = 0;
270
25.6k
  env.env_len = 0;
271
25.6k
    } else {
272
  /* Make private copy of envp. */
273
860k
  for (ep = envp; *ep != NULL; ep++)
274
836k
      continue;
275
23.8k
  len = (size_t)(ep - envp);
276
277
23.8k
  env.env_len = len;
278
23.8k
  env.env_size = len + 1 + 128;
279
23.8k
  env.envp = reallocarray(NULL, env.env_size, sizeof(char *));
280
23.8k
  if (env.envp == NULL) {
281
0
      env.env_size = 0;
282
0
      env.env_len = 0;
283
0
      sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
284
0
      debug_return_bool(false);
285
0
  }
286
23.8k
  sudoers_gc_add(GC_PTR, env.envp);
287
#ifdef ENV_DEBUG
288
  memset(env.envp, 0, env.env_size * sizeof(char *));
289
#endif
290
23.8k
  memcpy(env.envp, envp, len * sizeof(char *));
291
23.8k
  env.envp[len] = NULL;
292
293
  /* Free the old envp we allocated, if any. */
294
23.8k
  sudoers_gc_remove(GC_PTR, env.old_envp);
295
23.8k
  free(env.old_envp);
296
23.8k
  env.old_envp = NULL;
297
23.8k
    }
298
299
49.5k
    debug_return_bool(true);
300
49.5k
}
301
302
/*
303
 * Getter for private copy of the environment.
304
 */
305
char **
306
env_get(void)
307
12.4k
{
308
12.4k
    return env.envp;
309
12.4k
}
310
311
/*
312
 * Swap the old and new copies of the environment.
313
 */
314
bool
315
env_swap_old(void)
316
400
{
317
400
    char **old_envp;
318
319
400
    if (env.old_envp == NULL)
320
178
  return false;
321
222
    old_envp = env.old_envp;
322
222
    env.old_envp = env.envp;
323
222
    env.envp = old_envp;
324
222
    return true;
325
400
}
326
327
/*
328
 * Similar to putenv(3) but operates on sudo's private copy of the
329
 * environment (not environ) and it always overwrites.  The dupcheck param
330
 * determines whether we need to verify that the variable is not already set.
331
 * Will only overwrite an existing variable if overwrite is set.
332
 * Does not include warnings or debugging to avoid recursive calls.
333
 */
334
int
335
sudo_putenv_nodebug(char *str, bool dupcheck, bool overwrite)
336
381k
{
337
381k
    char **ep;
338
381k
    const char *equal;
339
381k
    bool found = false;
340
341
    /* Some putenv(3) implementations check for NULL. */
342
381k
    if (str == NULL) {
343
0
  errno = EINVAL;
344
0
  return -1;
345
0
    }
346
347
    /* The string must contain a '=' char but not start with one. */
348
381k
    equal = strchr(str, '=');
349
381k
    if (equal == NULL || equal == str) {
350
128
  errno = EINVAL;
351
128
  return -1;
352
128
    }
353
354
    /* Make sure there is room for the new entry plus a NULL. */
355
381k
    if (env.env_size > 2 && env.env_len > env.env_size - 2) {
356
129
  char **nenvp;
357
129
  size_t nsize;
358
359
129
  if (env.env_size > SIZE_MAX - 128) {
360
0
      sudo_warnx_nodebug(U_("internal error, %s overflow"),
361
0
    "sudo_putenv_nodebug");
362
0
      errno = EOVERFLOW;
363
0
      return -1;
364
0
  }
365
129
  nsize = env.env_size + 128;
366
129
  if (nsize > SIZE_MAX / sizeof(char *)) {
367
0
      sudo_warnx_nodebug(U_("internal error, %s overflow"),
368
0
    "sudo_putenv_nodebug");
369
0
      errno = EOVERFLOW;
370
0
      return -1;
371
0
  }
372
129
  sudoers_gc_remove(GC_PTR, env.envp);
373
129
  nenvp = reallocarray(env.envp, nsize, sizeof(char *));
374
129
  if (nenvp == NULL) {
375
0
      sudoers_gc_add(GC_PTR, env.envp);
376
0
      return -1;
377
0
  }
378
129
  sudoers_gc_add(GC_PTR, nenvp);
379
129
  env.envp = nenvp;
380
129
  env.env_size = nsize;
381
#ifdef ENV_DEBUG
382
  memset(env.envp + env.env_len, 0,
383
      (env.env_size - env.env_len) * sizeof(char *));
384
#endif
385
129
    }
386
387
#ifdef ENV_DEBUG
388
    if (env.envp[env.env_len] != NULL) {
389
  errno = EINVAL;
390
  return -1;
391
    }
392
#endif
393
394
381k
    if (dupcheck) {
395
308k
  size_t len = (size_t)(equal - str) + 1;
396
7.66M
  for (ep = env.envp; *ep != NULL; ep++) {
397
7.38M
      if (strncmp(str, *ep, len) == 0) {
398
29.2k
    if (overwrite)
399
16.7k
        *ep = str;
400
29.2k
    found = true;
401
29.2k
    break;
402
29.2k
      }
403
7.38M
  }
404
  /* Prune out extra instances of the variable we just overwrote. */
405
308k
  if (found && overwrite) {
406
1.22M
      while (*++ep != NULL) {
407
1.20M
    if (strncmp(str, *ep, len) == 0) {
408
0
        char **cur = ep;
409
0
        while ((*cur = *(cur + 1)) != NULL)
410
0
      cur++;
411
0
        ep--;
412
0
    }
413
1.20M
      }
414
16.7k
      env.env_len = (size_t)(ep - env.envp);
415
16.7k
  }
416
308k
    }
417
418
381k
    if (!found) {
419
351k
  ep = env.envp + env.env_len;
420
351k
  env.env_len++;
421
351k
  *ep++ = str;
422
351k
  *ep = NULL;
423
351k
    }
424
381k
    return 0;
425
381k
}
426
427
/*
428
 * Similar to putenv(3) but operates on sudo's private copy of the
429
 * environment (not environ) and it always overwrites.  The dupcheck param
430
 * determines whether we need to verify that the variable is not already set.
431
 * Will only overwrite an existing variable if overwrite is set.
432
 */
433
static int
434
sudo_putenv(char *str, bool dupcheck, bool overwrite)
435
381k
{
436
381k
    int ret;
437
381k
    debug_decl(sudo_putenv, SUDOERS_DEBUG_ENV);
438
439
381k
    sudo_debug_printf(SUDO_DEBUG_INFO, "sudo_putenv: %s", str);
440
441
381k
    ret = sudo_putenv_nodebug(str, dupcheck, overwrite);
442
381k
    if (ret == -1) {
443
#ifdef ENV_DEBUG
444
  if (env.envp[env.env_len] != NULL) {
445
      sudo_warnx("%s",
446
    U_("sudo_putenv: corrupted envp, length mismatch"));
447
  }
448
#endif
449
128
    }
450
381k
    debug_return_int(ret);
451
381k
}
452
453
/*
454
 * Similar to setenv(3) but operates on a private copy of the environment.
455
 * The dupcheck param determines whether we need to verify that the variable
456
 * is not already set.
457
 */
458
static int
459
sudo_setenv2(const char *var, const char *val, bool dupcheck, bool overwrite)
460
216k
{
461
216k
    char *estring;
462
216k
    size_t esize;
463
216k
    int ret = -1;
464
216k
    debug_decl(sudo_setenv2, SUDOERS_DEBUG_ENV);
465
466
216k
    esize = strlen(var) + 1 + strlen(val) + 1;
467
216k
    if ((estring = malloc(esize)) == NULL) {
468
0
  sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
469
0
      "unable to allocate memory");
470
0
  debug_return_int(-1);
471
0
    }
472
473
    /* Build environment string and insert it. */
474
216k
    if (strlcpy(estring, var, esize) >= esize ||
475
216k
  strlcat(estring, "=", esize) >= esize ||
476
216k
  strlcat(estring, val, esize) >= esize) {
477
478
0
  sudo_warnx(U_("internal error, %s overflow"), __func__);
479
0
  errno = EOVERFLOW;
480
216k
    } else {
481
216k
  ret = sudo_putenv(estring, dupcheck, overwrite);
482
216k
    }
483
216k
    if (ret == -1)
484
0
  free(estring);
485
216k
    else
486
216k
  sudoers_gc_add(GC_PTR, estring);
487
216k
    debug_return_int(ret);
488
216k
}
489
490
/*
491
 * Similar to setenv(3) but operates on a private copy of the environment.
492
 */
493
int
494
sudo_setenv(const char *var, const char *val, int overwrite)
495
0
{
496
0
    return sudo_setenv2(var, val, true, (bool)overwrite);
497
0
}
498
499
/*
500
 * Similar to unsetenv(3) but operates on a private copy of the environment.
501
 * Does not include warnings or debugging to avoid recursive calls.
502
 */
503
int
504
sudo_unsetenv_nodebug(const char *var)
505
0
{
506
0
    char **ep = env.envp;
507
0
    size_t len;
508
509
0
    if (ep == NULL || var == NULL || *var == '\0' || strchr(var, '=') != NULL) {
510
0
  errno = EINVAL;
511
0
  return -1;
512
0
    }
513
514
0
    len = strlen(var);
515
0
    while (*ep != NULL) {
516
0
  if (strncmp(var, *ep, len) == 0 && (*ep)[len] == '=') {
517
      /* Found it; shift remainder + NULL over by one. */
518
0
      char **cur = ep;
519
0
      while ((*cur = *(cur + 1)) != NULL)
520
0
    cur++;
521
0
      env.env_len--;
522
      /* Keep going, could be multiple instances of the var. */
523
0
  } else {
524
0
      ep++;
525
0
  }
526
0
    }
527
0
    return 0;
528
0
}
529
530
/*
531
 * Similar to unsetenv(3) but operates on a private copy of the environment.
532
 */
533
int
534
sudo_unsetenv(const char *name)
535
0
{
536
0
    int ret;
537
0
    debug_decl(sudo_unsetenv, SUDOERS_DEBUG_ENV);
538
539
0
    sudo_debug_printf(SUDO_DEBUG_INFO, "sudo_unsetenv: %s", name);
540
541
0
    ret = sudo_unsetenv_nodebug(name);
542
543
0
    debug_return_int(ret);
544
0
}
545
546
/*
547
 * Similar to getenv(3) but operates on a private copy of the environment.
548
 * Does not include warnings or debugging to avoid recursive calls.
549
 */
550
char *
551
sudo_getenv_nodebug(const char *name)
552
0
{
553
0
    char **ep, *val = NULL;
554
0
    size_t namelen = 0;
555
556
0
    if (env.env_len != 0) {
557
  /* For BSD compatibility, treat '=' in name like end of string. */
558
0
  while (name[namelen] != '\0' && name[namelen] != '=')
559
0
      namelen++;
560
0
  for (ep = env.envp; *ep != NULL; ep++) {
561
0
      if (strncmp(*ep, name, namelen) == 0 && (*ep)[namelen] == '=') {
562
0
    val = *ep + namelen + 1;
563
0
    break;
564
0
      }
565
0
  }
566
0
    }
567
0
    return val;
568
0
}
569
570
/*
571
 * Similar to getenv(3) but operates on a private copy of the environment.
572
 */
573
char *
574
sudo_getenv(const char *name)
575
0
{
576
0
    char *val;
577
0
    debug_decl(sudo_getenv, SUDOERS_DEBUG_ENV);
578
579
0
    sudo_debug_printf(SUDO_DEBUG_INFO, "sudo_getenv: %s", name);
580
581
0
    val = sudo_getenv_nodebug(name);
582
583
0
    debug_return_str(val);
584
0
}
585
586
/*
587
 * Check for var against patterns in the specified environment list.
588
 * Returns true if the variable was found, else false.
589
 */
590
static bool
591
matches_env_list(const char *var, struct list_members *list, bool *full_match)
592
957k
{
593
957k
    struct list_member *cur;
594
957k
    bool is_logname = false;
595
957k
    debug_decl(matches_env_list, SUDOERS_DEBUG_ENV);
596
597
957k
    switch (*var) {
598
33.0k
    case 'L':
599
33.0k
  if (strncmp(var, "LOGNAME=", 8) == 0)
600
536
      is_logname = true;
601
#ifdef _AIX
602
  else if (strncmp(var, "LOGIN=", 6) == 0)
603
      is_logname = true;
604
#endif
605
33.0k
  break;
606
27.7k
    case 'U':
607
27.7k
  if (strncmp(var, "USER=", 5) == 0)
608
912
      is_logname = true;
609
27.7k
  break;
610
957k
    }
611
612
957k
    if (is_logname) {
613
  /*
614
   * We treat LOGIN, LOGNAME and USER specially.
615
   * If one is preserved/deleted we want to preserve/delete them all.
616
   */
617
13.0k
  SLIST_FOREACH(cur, list, entries) {
618
13.0k
      if (matches_env_pattern(cur->value, "LOGNAME", full_match) ||
619
#ifdef _AIX
620
    matches_env_pattern(cur->value, "LOGIN", full_match) ||
621
#endif
622
13.0k
    matches_env_pattern(cur->value, "USER", full_match))
623
0
    debug_return_bool(true);
624
13.0k
  }
625
956k
    } else {
626
8.98M
  SLIST_FOREACH(cur, list, entries) {
627
8.98M
      if (matches_env_pattern(cur->value, var, full_match))
628
49.2k
    debug_return_bool(true);
629
8.98M
  }
630
956k
    }
631
908k
    debug_return_bool(false);
632
908k
}
633
634
/*
635
 * Check the env_delete blocklist.
636
 * Returns true if the variable was found, else false.
637
 */
638
static bool
639
matches_env_delete(const char *var)
640
16.9k
{
641
16.9k
    bool full_match;  /* unused */
642
16.9k
    debug_decl(matches_env_delete, SUDOERS_DEBUG_ENV);
643
644
    /* Skip anything listed in env_delete. */
645
16.9k
    debug_return_bool(matches_env_list(var, &def_env_delete, &full_match));
646
16.9k
}
647
648
/*
649
 * Verify the TZ environment variable is safe.
650
 * On many systems it is possible to set this to a pathname.
651
 */
652
static bool
653
tz_is_safe(const char *tzval)
654
6.71k
{
655
6.71k
    const char *cp;
656
6.71k
    char lastch;
657
6.71k
    debug_decl(tz_is_safe, SUDOERS_DEBUG_ENV);
658
659
    /* tzcode treats a value beginning with a ':' as a path. */
660
6.71k
    if (tzval[0] == ':')
661
392
  tzval++;
662
663
    /* Reject fully-qualified TZ that doesn't being with the zoneinfo dir. */
664
6.71k
    if (tzval[0] == '/') {
665
1.90k
#ifdef _PATH_ZONEINFO
666
1.90k
  if (strncmp(tzval, _PATH_ZONEINFO, sizeof(_PATH_ZONEINFO) - 1) != 0 ||
667
776
      tzval[sizeof(_PATH_ZONEINFO) - 1] != '/')
668
1.45k
      debug_return_bool(false);
669
#else
670
  /* Assume the worst. */
671
  debug_return_bool(false);
672
#endif
673
1.90k
    }
674
675
    /*
676
     * Make sure TZ only contains printable non-space characters
677
     * and does not contain a '..' path element.
678
     */
679
5.26k
    lastch = '/';
680
1.20M
    for (cp = tzval; *cp != '\0'; cp++) {
681
1.20M
  if (isspace((unsigned char)*cp) || !isprint((unsigned char)*cp))
682
960
      debug_return_bool(false);
683
1.19M
  if (lastch == '/' && cp[0] == '.' && cp[1] == '.' &&
684
732
      (cp[2] == '/' || cp[2] == '\0'))
685
380
      debug_return_bool(false);
686
1.19M
  lastch = *cp;
687
1.19M
    }
688
689
    /* Reject extra long TZ values (even if not a path). */
690
3.92k
    if ((size_t)(cp - tzval) >= PATH_MAX)
691
188
  debug_return_bool(false);
692
693
3.73k
    debug_return_bool(true);
694
3.73k
}
695
696
/*
697
 * Apply the env_check list.
698
 * Returns true if the variable is allowed, false if denied
699
 * or -1 if no match.
700
 */
701
static int
702
matches_env_check(const char *var, bool *full_match)
703
484k
{
704
484k
    int keepit = -1;
705
484k
    debug_decl(matches_env_check, SUDOERS_DEBUG_ENV);
706
707
    /* Skip anything listed in env_check that includes '/' or '%'. */
708
484k
    if (matches_env_list(var, &def_env_check, full_match)) {
709
10.6k
  if (strncmp(var, "TZ=", 3) == 0) {
710
      /* Special case for TZ */
711
6.71k
      keepit = tz_is_safe(var + 3);
712
6.71k
  } else {
713
3.93k
      const char *val = strchr(var, '=');
714
3.93k
      if (val != NULL)
715
3.93k
    keepit = !strpbrk(val + 1, "/%");
716
3.93k
  }
717
10.6k
    }
718
484k
    debug_return_int(keepit);
719
484k
}
720
721
/*
722
 * Check the env_keep list.
723
 * Returns true if the variable is allowed else false.
724
 */
725
static bool
726
matches_env_keep(const struct sudoers_context *ctx, const char *var,
727
    bool *full_match)
728
456k
{
729
456k
    bool keepit = false;
730
456k
    debug_decl(matches_env_keep, SUDOERS_DEBUG_ENV);
731
732
    /* Preserve SHELL variable for "sudo -s". */
733
456k
    if (ISSET(ctx->mode, MODE_SHELL) && strncmp(var, "SHELL=", 6) == 0) {
734
432
  keepit = true;
735
456k
    } else if (matches_env_list(var, &def_env_keep, full_match)) {
736
38.5k
  keepit = true;
737
38.5k
    }
738
456k
    debug_return_bool(keepit);
739
456k
}
740
741
/*
742
 * Look up var in the env_delete and env_check.
743
 * Returns true if we should delete the variable, else false.
744
 */
745
static bool
746
env_should_delete(const char *var)
747
16.9k
{
748
16.9k
    int delete_it;
749
16.9k
    bool full_match = false;
750
16.9k
    debug_decl(env_should_delete, SUDOERS_DEBUG_ENV);
751
752
16.9k
    delete_it = matches_env_delete(var);
753
16.9k
    if (!delete_it)
754
16.9k
  delete_it = matches_env_check(var, &full_match) == false;
755
756
16.9k
    sudo_debug_printf(SUDO_DEBUG_INFO, "delete %s: %s",
757
16.9k
  var, delete_it ? "YES" : "NO");
758
16.9k
    debug_return_bool(delete_it);
759
16.9k
}
760
761
/*
762
 * Lookup var in the env_check and env_keep lists.
763
 * Returns true if the variable is allowed else false.
764
 */
765
static bool
766
env_should_keep(const struct sudoers_context *ctx, const char *var)
767
467k
{
768
467k
    int keepit;
769
467k
    bool full_match = false;
770
467k
    const char *cp;
771
467k
    debug_decl(env_should_keep, SUDOERS_DEBUG_ENV);
772
773
467k
    keepit = matches_env_check(var, &full_match);
774
467k
    if (keepit == -1)
775
456k
  keepit = matches_env_keep(ctx, var, &full_match);
776
777
    /* Skip bash functions unless we matched on the value as well as name. */
778
467k
    if (keepit && !full_match) {
779
46.6k
  if ((cp = strchr(var, '=')) != NULL) {
780
46.6k
      if (strncmp(cp, "=() ", 4) == 0)
781
284
    keepit = false;
782
46.6k
  }
783
46.6k
    }
784
467k
    sudo_debug_printf(SUDO_DEBUG_INFO, "keep %s: %s",
785
467k
  var, keepit == true ? "YES" : "NO");
786
467k
    debug_return_bool(keepit == true);
787
467k
}
788
789
#ifdef HAVE_PAM
790
/*
791
 * Merge another environment with our private copy.
792
 * Only overwrite an existing variable if it is not
793
 * being preserved from the user's environment.
794
 * Returns true on success or false on failure.
795
 */
796
bool
797
env_merge(const struct sudoers_context *ctx, char * const envp[])
798
{
799
    char * const *ep;
800
    bool ret = true;
801
    debug_decl(env_merge, SUDOERS_DEBUG_ENV);
802
803
    for (ep = envp; *ep != NULL; ep++) {
804
  /* XXX - avoid checking value here, should only check name */
805
  bool overwrite = def_env_reset ? !env_should_keep(ctx, *ep) : env_should_delete(*ep);
806
  if (sudo_putenv(*ep, true, overwrite) == -1) {
807
      /* XXX cannot undo on failure */
808
      ret = false;
809
      break;
810
  }
811
    }
812
    debug_return_bool(ret);
813
}
814
#endif /* HAVE_PAM */
815
816
static void
817
env_update_didvar(const char *ep, unsigned int *didvar)
818
60.8k
{
819
60.8k
    switch (*ep) {
820
12.5k
  case 'H':
821
12.5k
      if (strncmp(ep, "HOME=", 5) == 0)
822
0
    SET(*didvar, DID_HOME);
823
12.5k
      break;
824
0
  case 'L':
825
#ifdef _AIX
826
      if (strncmp(ep, "LOGIN=", 6) == 0)
827
    SET(*didvar, DID_LOGIN);
828
#endif
829
0
      if (strncmp(ep, "LOGNAME=", 8) == 0)
830
0
    SET(*didvar, DID_LOGNAME);
831
0
      break;
832
0
  case 'M':
833
0
      if (strncmp(ep, "MAIL=", 5) == 0)
834
0
    SET(*didvar, DID_MAIL);
835
0
      break;
836
35.7k
  case 'P':
837
35.7k
      if (strncmp(ep, "PATH=", 5) == 0)
838
35.7k
    SET(*didvar, DID_PATH);
839
35.7k
      break;
840
0
  case 'S':
841
0
      if (strncmp(ep, "SHELL=", 6) == 0)
842
0
    SET(*didvar, DID_SHELL);
843
0
      break;
844
0
  case 'T':
845
0
      if (strncmp(ep, "TERM=", 5) == 0)
846
0
    SET(*didvar, DID_TERM);
847
0
      break;
848
0
  case 'U':
849
0
      if (strncmp(ep, "USER=", 5) == 0)
850
0
    SET(*didvar, DID_USER);
851
0
      break;
852
60.8k
    }
853
60.8k
}
854
855
78.7k
#define CHECK_PUTENV(a, b, c) do {                \
856
78.7k
    if (sudo_putenv((char *)(a), (b), (c)) == -1) {            \
857
0
  goto bad;                    \
858
0
    }                         \
859
78.7k
} while (0)
860
861
216k
#define CHECK_SETENV2(a, b, c, d) do {              \
862
216k
    if (sudo_setenv2((char *)(a), (b), (c), (d)) == -1) {          \
863
0
  goto bad;                    \
864
0
    }                         \
865
216k
} while (0)
866
867
/*
868
 * Build a new environment and either clear potentially dangerous
869
 * variables from the old one or start with a clean slate.
870
 * Also adds sudo-specific variables (SUDO_*).
871
 * Returns true on success or false on failure.
872
 */
873
bool
874
rebuild_env(const struct sudoers_context *ctx)
875
24.2k
{
876
24.2k
    char **ep, *cp, *ps1;
877
24.2k
    char idbuf[STRLEN_MAX_UNSIGNED(uid_t) + 1];
878
24.2k
    unsigned int didvar;
879
24.2k
    bool reset_home = false;
880
24.2k
    int len;
881
24.2k
    debug_decl(rebuild_env, SUDOERS_DEBUG_ENV);
882
883
    /*
884
     * Either clean out the environment or reset to a safe default.
885
     */
886
24.2k
    ps1 = NULL;
887
24.2k
    didvar = 0;
888
24.2k
    env.env_len = 0;
889
24.2k
    env.env_size = 128;
890
24.2k
    sudoers_gc_remove(GC_PTR, env.old_envp);
891
24.2k
    free(env.old_envp);
892
24.2k
    env.old_envp = env.envp;
893
24.2k
    env.envp = reallocarray(NULL, env.env_size, sizeof(char *));
894
24.2k
    if (env.envp == NULL) {
895
0
  sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
896
0
      "unable to allocate memory");
897
0
  env.env_size = 0;
898
0
  goto bad;
899
0
    }
900
24.2k
    sudoers_gc_add(GC_PTR, env.envp);
901
#ifdef ENV_DEBUG
902
    memset(env.envp, 0, env.env_size * sizeof(char *));
903
#else
904
24.2k
    env.envp[0] = NULL;
905
24.2k
#endif
906
907
    /* Reset HOME based on target user if configured to. */
908
24.2k
    if (ISSET(ctx->mode, MODE_RUN)) {
909
11.4k
  if (def_always_set_home ||
910
11.4k
      ISSET(ctx->mode, MODE_RESET_HOME | MODE_LOGIN_SHELL) || 
911
11.1k
      (ISSET(ctx->mode, MODE_SHELL) && def_set_home))
912
322
      reset_home = true;
913
11.4k
    }
914
915
24.2k
    if (def_env_reset || ISSET(ctx->mode, MODE_LOGIN_SHELL)) {
916
  /*
917
   * If starting with a fresh environment, initialize it based on
918
   * /etc/environment or login.conf.  For "sudo -i" we want those
919
   * variables to override the invoking user's environment, so we
920
   * defer reading them until later.
921
   */
922
23.4k
  if (!ISSET(ctx->mode, MODE_LOGIN_SHELL)) {
923
#ifdef HAVE_LOGIN_CAP_H
924
      /* Insert login class environment variables. */
925
      if (ctx->runas.class) {
926
    login_cap_t *lc = login_getclass(ctx->runas.class);
927
    if (lc != NULL) {
928
        setusercontext(lc, ctx->runas.pw,
929
      ctx->runas.pw->pw_uid, LOGIN_SETPATH|LOGIN_SETENV);
930
        login_close(lc);
931
    }
932
      }
933
#endif /* HAVE_LOGIN_CAP_H */
934
23.2k
#ifdef _PATH_ENVIRONMENT
935
      /* Insert system-wide environment variables. */
936
23.2k
      if (!read_env_file(ctx, _PATH_ENVIRONMENT, true, false))
937
0
    sudo_warn("%s", _PATH_ENVIRONMENT);
938
23.2k
#endif
939
46.4k
      for (ep = env.envp; *ep; ep++)
940
23.2k
    env_update_didvar(*ep, &didvar);
941
23.2k
  }
942
943
  /* Pull in vars we want to keep from the old environment. */
944
23.4k
  if (env.old_envp != NULL) {
945
451k
      for (ep = env.old_envp; *ep; ep++) {
946
438k
    bool keepit;
947
948
    /*
949
     * Look up the variable in the env_check and env_keep lists.
950
     */
951
438k
    keepit = env_should_keep(ctx, *ep);
952
953
    /*
954
     * Do SUDO_PS1 -> PS1 conversion.
955
     * This must happen *after* env_should_keep() is called.
956
     */
957
438k
    if (strncmp(*ep, "SUDO_PS1=", 9) == 0)
958
0
        ps1 = *ep + 5;
959
960
438k
    if (keepit) {
961
        /* Preserve variable. */
962
37.6k
        CHECK_PUTENV(*ep, true, false);
963
37.6k
        env_update_didvar(*ep, &didvar);
964
37.6k
    }
965
438k
      }
966
12.5k
  }
967
23.4k
  didvar |= didvar << 16;   /* convert DID_* to KEPT_* */
968
969
  /*
970
   * Add in defaults.  In -i mode these come from the runas user,
971
   * otherwise they may be from the user's environment (depends
972
   * on sudoers options).
973
   */
974
23.4k
  if (ISSET(ctx->mode, MODE_LOGIN_SHELL)) {
975
231
      CHECK_SETENV2("SHELL", ctx->runas.pw->pw_shell,
976
231
    ISSET(didvar, DID_SHELL), true);
977
231
      SET(didvar, DID_SHELL);
978
#ifdef _AIX
979
      CHECK_SETENV2("LOGIN", ctx->runas.pw->pw_name,
980
    ISSET(didvar, DID_LOGIN), true);
981
#endif
982
231
      CHECK_SETENV2("LOGNAME", ctx->runas.pw->pw_name,
983
231
    ISSET(didvar, DID_LOGNAME), true);
984
231
      CHECK_SETENV2("USER", ctx->runas.pw->pw_name,
985
231
    ISSET(didvar, DID_USER), true);
986
23.2k
  } else {
987
      /* We will set LOGNAME later in the def_set_logname case. */
988
23.2k
      if (!def_set_logname) {
989
#ifdef _AIX
990
    if (!ISSET(didvar, DID_LOGIN))
991
        CHECK_SETENV2("LOGIN", ctx->user.name, false, true);
992
#endif
993
0
    if (!ISSET(didvar, DID_LOGNAME))
994
0
        CHECK_SETENV2("LOGNAME", ctx->user.name, false, true);
995
0
    if (!ISSET(didvar, DID_USER))
996
0
        CHECK_SETENV2("USER", ctx->user.name, false, true);
997
0
      }
998
23.2k
  }
999
1000
  /* If we didn't keep HOME, reset it based on target user. */
1001
23.4k
  if (!ISSET(didvar, KEPT_HOME))
1002
23.4k
      reset_home = true;
1003
1004
  /*
1005
   * Set MAIL to target user in -i mode or if MAIL is not preserved
1006
   * from user's environment.
1007
   */
1008
23.4k
  if (ISSET(ctx->mode, MODE_LOGIN_SHELL) || !ISSET(didvar, KEPT_MAIL)) {
1009
23.4k
      if (_PATH_MAILDIR[sizeof(_PATH_MAILDIR) - 2] == '/') {
1010
0
    len = asprintf(&cp, "MAIL=%s%s", _PATH_MAILDIR,
1011
0
        ctx->runas.pw->pw_name);
1012
23.4k
      } else {
1013
23.4k
    len = asprintf(&cp, "MAIL=%s/%s", _PATH_MAILDIR,
1014
23.4k
        ctx->runas.pw->pw_name);
1015
23.4k
      }
1016
23.4k
      if (len == -1)
1017
0
        goto bad;
1018
23.4k
      if (sudo_putenv(cp, ISSET(didvar, DID_MAIL), true) == -1) {
1019
0
    free(cp);
1020
0
    goto bad;
1021
0
      }
1022
23.4k
      sudoers_gc_add(GC_PTR, cp);
1023
23.4k
  }
1024
23.4k
    } else {
1025
  /*
1026
   * Copy environ entries as long as they don't match env_delete or
1027
   * env_check.
1028
   */
1029
772
  if (env.old_envp != NULL) {
1030
17.4k
      for (ep = env.old_envp; *ep; ep++) {
1031
    /* Add variable unless it matches a blocklist. */
1032
16.9k
    if (!env_should_delete(*ep)) {
1033
16.9k
        if (strncmp(*ep, "SUDO_PS1=", 9) == 0)
1034
0
      ps1 = *ep + 5;
1035
16.9k
        else if (strncmp(*ep, "SHELL=", 6) == 0)
1036
0
      SET(didvar, DID_SHELL);
1037
16.9k
        else if (strncmp(*ep, "PATH=", 5) == 0)
1038
484
      SET(didvar, DID_PATH);
1039
16.4k
        else if (strncmp(*ep, "TERM=", 5) == 0)
1040
0
      SET(didvar, DID_TERM);
1041
16.9k
        CHECK_PUTENV(*ep, true, false);
1042
16.9k
    }
1043
16.9k
      }
1044
484
  }
1045
772
    }
1046
    /* Replace the PATH envariable with a secure one? */
1047
24.2k
    if (def_secure_path && !user_is_exempt(ctx)) {
1048
0
  CHECK_SETENV2("PATH", def_secure_path, true, true);
1049
0
  SET(didvar, DID_PATH);
1050
0
    }
1051
1052
    /*
1053
     * Set LOGIN, LOGNAME, and USER to target if "set_logname" is not
1054
     * disabled.  We skip this if we are running a login shell (because
1055
     * they have already been set).
1056
     */
1057
24.2k
    if (def_set_logname && !ISSET(ctx->mode, MODE_LOGIN_SHELL)) {
1058
23.9k
  if ((didvar & KEPT_USER_VARIABLES) == 0) {
1059
      /* Nothing preserved, set them all. */
1060
#ifdef _AIX
1061
      CHECK_SETENV2("LOGIN", ctx->runas.pw->pw_name, true, true);
1062
#endif
1063
23.9k
      CHECK_SETENV2("LOGNAME", ctx->runas.pw->pw_name, true, true);
1064
23.9k
      CHECK_SETENV2("USER", ctx->runas.pw->pw_name, true, true);
1065
23.9k
  } else if ((didvar & KEPT_USER_VARIABLES) != KEPT_USER_VARIABLES) {
1066
      /*
1067
       * Preserved some of LOGIN, LOGNAME, USER but not all.
1068
       * Make the unset ones match so we don't end up with some
1069
       * set to the invoking user and others set to the runas user.
1070
       */
1071
0
      if (ISSET(didvar, KEPT_LOGNAME))
1072
0
    cp = sudo_getenv("LOGNAME");
1073
#ifdef _AIX
1074
      else if (ISSET(didvar, KEPT_LOGIN))
1075
    cp = sudo_getenv("LOGIN");
1076
#endif
1077
0
      else if (ISSET(didvar, KEPT_USER))
1078
0
    cp = sudo_getenv("USER");
1079
0
      else
1080
0
    cp = NULL;
1081
0
      if (cp != NULL) {
1082
#ifdef _AIX
1083
    if (!ISSET(didvar, KEPT_LOGIN))
1084
        CHECK_SETENV2("LOGIN", cp, true, true);
1085
#endif
1086
0
    if (!ISSET(didvar, KEPT_LOGNAME))
1087
0
        CHECK_SETENV2("LOGNAME", cp, true, true);
1088
0
    if (!ISSET(didvar, KEPT_USER))
1089
0
        CHECK_SETENV2("USER", cp, true, true);
1090
0
      }
1091
0
  }
1092
23.9k
    }
1093
1094
    /* Set $HOME to target user if not preserving user's value. */
1095
24.2k
    if (reset_home)
1096
23.4k
  CHECK_SETENV2("HOME", ctx->runas.pw->pw_dir, true, true);
1097
1098
    /* Provide default values for $SHELL, $TERM and $PATH if not set. */
1099
24.2k
    if (!ISSET(didvar, DID_SHELL))
1100
23.9k
  CHECK_SETENV2("SHELL", ctx->runas.pw->pw_shell, false, false);
1101
24.2k
    if (!ISSET(didvar, DID_TERM))
1102
24.2k
  CHECK_PUTENV("TERM=unknown", false, false);
1103
24.2k
    if (!ISSET(didvar, DID_PATH))
1104
387
  CHECK_SETENV2("PATH", _PATH_STDPATH, false, true);
1105
1106
    /* Set PS1 if SUDO_PS1 is set. */
1107
24.2k
    if (ps1 != NULL)
1108
0
  CHECK_PUTENV(ps1, true, true);
1109
1110
    /* Add the SUDO_COMMAND envariable (cmnd + args). */
1111
24.2k
    if (ctx->user.cmnd_args) {
1112
  /*
1113
   * We limit ctx->user.cmnd_args to 4096 bytes to avoid an execve(2)
1114
   * failure for very long argument vectors.  The command's environment
1115
   * also counts against the ARG_MAX limit.
1116
   */
1117
1.49k
  len = asprintf(&cp, "SUDO_COMMAND=%s %.*s", ctx->user.cmnd, 4096,
1118
1.49k
      ctx->user.cmnd_args);
1119
1.49k
  if (len == -1)
1120
0
      goto bad;
1121
1.49k
  if (sudo_putenv(cp, true, true) == -1) {
1122
0
      free(cp);
1123
0
      goto bad;
1124
0
  }
1125
1.49k
  sudoers_gc_add(GC_PTR, cp);
1126
22.7k
    } else {
1127
22.7k
  CHECK_SETENV2("SUDO_COMMAND", ctx->user.cmnd, true, true);
1128
22.7k
    }
1129
1130
    /* Add the SUDO_{USER,UID,GID,HOME,TTY} environment variables. */
1131
24.2k
    CHECK_SETENV2("SUDO_USER", ctx->user.name, true, true);
1132
24.2k
    (void)snprintf(idbuf, sizeof(idbuf), "%u", (unsigned int) ctx->user.uid);
1133
24.2k
    CHECK_SETENV2("SUDO_UID", idbuf, true, true);
1134
24.2k
    (void)snprintf(idbuf, sizeof(idbuf), "%u", (unsigned int) ctx->user.gid);
1135
24.2k
    CHECK_SETENV2("SUDO_GID", idbuf, true, true);
1136
24.2k
    CHECK_SETENV2("SUDO_HOME", ctx->user.pw->pw_dir, true, true);
1137
24.2k
    if (ctx->user.ttypath != NULL)
1138
711
  CHECK_SETENV2("SUDO_TTY", ctx->user.ttypath, true, true);
1139
1140
24.2k
    debug_return_bool(true);
1141
1142
0
bad:
1143
0
    sudo_warn("%s", U_("unable to rebuild the environment"));
1144
0
    debug_return_bool(false);
1145
0
}
1146
1147
/*
1148
 * Insert all environment variables in envp into the private copy
1149
 * of the environment.
1150
 * Returns true on success or false on failure.
1151
 */
1152
bool
1153
insert_env_vars(char * const envp[])
1154
5.17k
{
1155
5.17k
    char * const *ep;
1156
5.17k
    bool ret = true;
1157
5.17k
    debug_decl(insert_env_vars, SUDOERS_DEBUG_ENV);
1158
1159
    /* Add user-specified environment variables. */
1160
5.17k
    if (envp != NULL) {
1161
38.9k
  for (ep = envp; *ep != NULL; ep++) {
1162
      /* XXX - no undo on failure */
1163
37.5k
      if (sudo_putenv(*ep, true, true) == -1) {
1164
128
    ret = false;
1165
128
    break;
1166
128
      }
1167
37.5k
  }
1168
1.48k
    }
1169
5.17k
    debug_return_bool(ret);
1170
5.17k
}
1171
1172
/*
1173
 * Validate the list of environment variables passed in on the command
1174
 * line against env_delete, env_check, and env_keep.
1175
 * Calls log_warning() if any specified variables are not allowed.
1176
 * Returns true if allowed, else false.
1177
 */
1178
bool
1179
validate_env_vars(const struct sudoers_context *ctx, char * const env_vars[])
1180
6.28k
{
1181
6.28k
    char * const *ep;
1182
6.28k
    char errbuf[4096];
1183
6.28k
    char *errpos = errbuf;
1184
6.28k
    bool okvar, ret = true;
1185
6.28k
    debug_decl(validate_env_vars, SUDOERS_DEBUG_ENV);
1186
1187
6.28k
    if (env_vars == NULL)
1188
3.51k
  debug_return_bool(true); /* nothing to do */
1189
1190
    /* Add user-specified environment variables. */
1191
510k
    for (ep = env_vars; *ep != NULL; ep++) {
1192
508k
  char *eq = strchr(*ep, '=');
1193
508k
  if (eq == NULL || eq == *ep) {
1194
      /* Must be in the form var=val. */
1195
479k
      okvar = false;
1196
479k
  } else if (def_secure_path && !user_is_exempt(ctx) &&
1197
0
      strncmp(*ep, "PATH=", 5) == 0) {
1198
0
      okvar = false;
1199
28.7k
  } else if (def_env_reset) {
1200
28.7k
      okvar = env_should_keep(ctx, *ep);
1201
28.7k
  } else {
1202
0
      okvar = !env_should_delete(*ep);
1203
0
  }
1204
508k
  if (okvar == false) {
1205
      /* Not allowed, append to error buffer if space remains. */
1206
499k
      if (errpos < &errbuf[sizeof(errbuf)]) {
1207
19.5k
    const size_t varlen = strcspn(*ep, "=");
1208
19.5k
    const size_t errsize = sizeof(errbuf) - (size_t)(errpos - errbuf);
1209
19.5k
    int len = snprintf(errpos, errsize, "%s%.*s",
1210
19.5k
        errpos != errbuf ? ", " : "", (int)varlen, *ep);
1211
19.5k
    if (len >= ssizeof(errbuf) - (errpos - errbuf)) {
1212
52
        memcpy(&errbuf[sizeof(errbuf) - 4], "...", 4);
1213
52
        errpos = &errbuf[sizeof(errbuf)];
1214
19.5k
    } else {
1215
19.5k
        errpos += len;
1216
19.5k
    }
1217
19.5k
      }
1218
499k
  }
1219
508k
    }
1220
2.76k
    if (errpos != errbuf) {
1221
  /* XXX - audit? */
1222
1.75k
  log_warningx(ctx, 0,
1223
1.75k
      N_("sorry, you are not allowed to set the following environment variables: %s"), errbuf);
1224
1.75k
  ret = false;
1225
1.75k
    }
1226
2.76k
    debug_return_bool(ret);
1227
2.76k
}
1228
1229
static void *
1230
env_file_open_local(const char *path)
1231
33.6k
{
1232
33.6k
    struct env_file_local *efl;
1233
33.6k
    debug_decl(env_file_open_local, SUDOERS_DEBUG_ENV);
1234
1235
33.6k
    efl = calloc(1, sizeof(*efl));
1236
33.6k
    if (efl != NULL) {
1237
33.6k
  if ((efl->fp = fopen(path, "r")) == NULL) {
1238
0
      if (errno != ENOENT) {
1239
0
    free(efl);
1240
0
    efl = NULL;
1241
0
      }
1242
0
  }
1243
33.6k
    }
1244
33.6k
    debug_return_ptr(efl);
1245
33.6k
}
1246
1247
static void
1248
env_file_close_local(void *cookie)
1249
33.6k
{
1250
33.6k
    struct env_file_local *efl = cookie;
1251
33.6k
    debug_decl(env_file_close_local, SUDOERS_DEBUG_ENV);
1252
1253
33.6k
    if (efl != NULL) {
1254
33.6k
  if (efl->fp != NULL)
1255
33.6k
      fclose(efl->fp);
1256
33.6k
  free(efl->line);
1257
33.6k
  free(efl);
1258
33.6k
    }
1259
33.6k
    debug_return;
1260
33.6k
}
1261
1262
/*
1263
 * Parse /etc/environment lines ala AIX and Linux.
1264
 * Lines may be in either of three formats:
1265
 *  NAME=VALUE
1266
 *  NAME="VALUE"
1267
 *  NAME='VALUE'
1268
 * with an optional "export" prefix so the shell can source the file.
1269
 * Invalid lines, blank lines, or lines consisting solely of a comment
1270
 * character are skipped.
1271
 */
1272
static char *
1273
env_file_next_local(void *cookie, int *errnum)
1274
57.0k
{
1275
57.0k
    struct env_file_local *efl = cookie;
1276
57.0k
    char *var, *val, *ret = NULL;
1277
57.0k
    size_t var_len, val_len;
1278
57.0k
    debug_decl(env_file_next_local, SUDOERS_DEBUG_ENV);
1279
1280
57.0k
    *errnum = 0;
1281
57.0k
    if (efl->fp == NULL)
1282
0
  debug_return_ptr(NULL);
1283
1284
57.0k
    for (;;) {
1285
57.0k
  if (sudo_parseln(&efl->line, &efl->linesize, NULL, efl->fp, PARSELN_CONT_IGN) == -1) {
1286
33.6k
      if (!feof(efl->fp))
1287
0
    *errnum = errno;
1288
33.6k
      break;
1289
33.6k
  }
1290
1291
  /* Skip blank or comment lines */
1292
23.3k
  if (*(var = efl->line) == '\0')
1293
0
      continue;
1294
1295
  /* Skip optional "export " */
1296
23.3k
  if (strncmp(var, "export", 6) == 0 && isspace((unsigned char) var[6])) {
1297
0
      var += 7;
1298
0
      while (isspace((unsigned char) *var)) {
1299
0
    var++;
1300
0
      }
1301
0
  }
1302
1303
  /* Must be of the form name=["']value['"] */
1304
116k
  for (val = var; *val != '\0' && *val != '='; val++)
1305
93.3k
      continue;
1306
23.3k
  if (var == val || *val != '=')
1307
0
      continue;
1308
23.3k
  var_len = (size_t)(val - var);
1309
23.3k
  val_len = strlen(++val);
1310
1311
  /* Strip leading and trailing single/double quotes */
1312
23.3k
  if ((val[0] == '\'' || val[0] == '\"') && val_len > 1 && val[0] == val[val_len - 1]) {
1313
23.3k
      val[val_len - 1] = '\0';
1314
23.3k
      val++;
1315
23.3k
      val_len -= 2;
1316
23.3k
  }
1317
1318
23.3k
  if ((ret = malloc(var_len + 1 + val_len + 1)) == NULL) {
1319
0
      *errnum = errno;
1320
0
      sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
1321
0
    "unable to allocate memory");
1322
23.3k
  } else {
1323
23.3k
      memcpy(ret, var, var_len + 1); /* includes '=' */
1324
23.3k
      memcpy(ret + var_len + 1, val, val_len + 1); /* includes NUL */
1325
23.3k
      sudoers_gc_add(GC_PTR, ret);
1326
23.3k
  }
1327
23.3k
  break;
1328
23.3k
    }
1329
57.0k
    debug_return_str(ret);
1330
57.0k
}
1331
1332
static struct sudoers_env_file env_file_sudoers = {
1333
    env_file_open_local,
1334
    env_file_close_local,
1335
    env_file_next_local
1336
};
1337
1338
static struct sudoers_env_file env_file_system = {
1339
    env_file_open_local,
1340
    env_file_close_local,
1341
    env_file_next_local
1342
};
1343
1344
void
1345
register_env_file(void * (*ef_open)(const char *), void (*ef_close)(void *),
1346
    char * (*ef_next)(void *, int *), bool sys)
1347
0
{
1348
0
    struct sudoers_env_file *ef = sys ? &env_file_system : &env_file_sudoers;
1349
1350
0
    ef->open = ef_open;
1351
0
    ef->close = ef_close;
1352
0
    ef->next = ef_next;
1353
0
}
1354
1355
bool
1356
read_env_file(const struct sudoers_context *ctx, const char *path,
1357
    bool overwrite, bool restricted)
1358
33.6k
{
1359
33.6k
    struct sudoers_env_file *ef;
1360
33.6k
    bool ret = true;
1361
33.6k
    char *envstr;
1362
33.6k
    void *cookie;
1363
33.6k
    int errnum;
1364
33.6k
    debug_decl(read_env_file, SUDOERS_DEBUG_ENV);
1365
1366
    /*
1367
     * The environment file may be handled differently depending on
1368
     * whether it is specified in sudoers or the system.
1369
     */
1370
33.6k
    if (path == def_env_file || path == def_restricted_env_file)
1371
10.3k
  ef = &env_file_sudoers;
1372
23.3k
    else
1373
23.3k
  ef = &env_file_system;
1374
1375
33.6k
    cookie = ef->open(path);
1376
33.6k
    if (cookie == NULL)
1377
0
  debug_return_bool(false);
1378
1379
57.0k
    for (;;) {
1380
  /* Keep reading until EOF or error. */
1381
57.0k
  if ((envstr = ef->next(cookie, &errnum)) == NULL) {
1382
33.6k
      if (errnum != 0)
1383
0
    ret = false;
1384
33.6k
      break;
1385
33.6k
  }
1386
1387
  /*
1388
   * If the env file is restricted, apply env_check and env_keep
1389
   * when env_reset is set or env_delete when it is not.
1390
   */
1391
23.3k
  if (restricted) {
1392
0
      if (def_env_reset ? !env_should_keep(ctx, envstr) : env_should_delete(envstr)) {
1393
0
    free(envstr);
1394
0
    continue;
1395
0
      }
1396
0
  }
1397
23.3k
  if (sudo_putenv(envstr, true, overwrite) == -1) {
1398
      /* XXX - no undo on failure */
1399
0
      ret = false;
1400
0
      break;
1401
0
  }
1402
23.3k
    }
1403
33.6k
    ef->close(cookie);
1404
1405
33.6k
    debug_return_bool(ret);
1406
33.6k
}
1407
1408
bool
1409
init_envtables(void)
1410
29.3k
{
1411
29.3k
    struct list_member *cur;
1412
29.3k
    const char **p;
1413
29.3k
    debug_decl(init_envtables, SUDOERS_DEBUG_ENV);
1414
1415
    /* Fill in the "env_delete" list. */
1416
1.35M
    for (p = initial_badenv_table; *p; p++) {
1417
1.32M
  cur = calloc(1, sizeof(struct list_member));
1418
1.32M
  if (cur == NULL || (cur->value = strdup(*p)) == NULL) {
1419
0
      sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
1420
0
    "unable to allocate memory");
1421
0
      free(cur);
1422
0
      debug_return_bool(false);
1423
0
  }
1424
1.32M
  SLIST_INSERT_HEAD(&def_env_delete, cur, entries);
1425
1.32M
    }
1426
1427
    /* Fill in the "env_check" list. */
1428
235k
    for (p = initial_checkenv_table; *p; p++) {
1429
205k
  cur = calloc(1, sizeof(struct list_member));
1430
205k
  if (cur == NULL || (cur->value = strdup(*p)) == NULL) {
1431
0
      sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
1432
0
    "unable to allocate memory");
1433
0
      free(cur);
1434
0
      debug_return_bool(false);
1435
0
  }
1436
205k
  SLIST_INSERT_HEAD(&def_env_check, cur, entries);
1437
205k
    }
1438
1439
    /* Fill in the "env_keep" list. */
1440
352k
    for (p = initial_keepenv_table; *p; p++) {
1441
323k
  cur = calloc(1, sizeof(struct list_member));
1442
323k
  if (cur == NULL || (cur->value = strdup(*p)) == NULL) {
1443
0
      sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
1444
0
    "unable to allocate memory");
1445
0
      free(cur);
1446
0
      debug_return_bool(false);
1447
0
  }
1448
323k
  SLIST_INSERT_HEAD(&def_env_keep, cur, entries);
1449
323k
    }
1450
29.3k
    debug_return_bool(true);
1451
29.3k
}