Merge commit 'upstream/1.7.0'
[debian/sudo] / env.c
1 /*
2  * Copyright (c) 2000-2005, 2007-2008
3  *      Todd C. Miller <Todd.Miller@courtesan.com>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  *
17  * Sponsored in part by the Defense Advanced Research Projects
18  * Agency (DARPA) and Air Force Research Laboratory, Air Force
19  * Materiel Command, USAF, under agreement number F39502-99-1-0512.
20  */
21
22 #include <config.h>
23
24 #include <sys/types.h>
25 #include <sys/param.h>
26 #include <sys/stat.h>
27 #include <stdio.h>
28 #ifdef STDC_HEADERS
29 # include <stdlib.h>
30 # include <stddef.h>
31 #else
32 # ifdef HAVE_STDLIB_H
33 #  include <stdlib.h>
34 # endif
35 #endif /* STDC_HEADERS */
36 #ifdef HAVE_STRING_H
37 # include <string.h>
38 #else
39 # ifdef HAVE_STRINGS_H
40 #  include <strings.h>
41 # endif
42 #endif /* HAVE_STRING_H */
43 #ifdef HAVE_UNISTD_H
44 # include <unistd.h>
45 #endif /* HAVE_UNISTD_H */
46 #include <pwd.h>
47
48 #include "sudo.h"
49
50 #ifndef lint
51 __unused static const char rcsid[] = "$Sudo: env.c,v 1.94 2008/11/09 14:13:12 millert Exp $";
52 #endif /* lint */
53
54 /*
55  * Flags used in rebuild_env()
56  */
57 #undef DID_TERM
58 #define DID_TERM        0x0001
59 #undef DID_PATH
60 #define DID_PATH        0x0002
61 #undef DID_HOME
62 #define DID_HOME        0x0004
63 #undef DID_SHELL
64 #define DID_SHELL       0x0008
65 #undef DID_LOGNAME
66 #define DID_LOGNAME     0x0010
67 #undef DID_USER
68 #define DID_USER        0x0020
69 #undef DID_USERNAME
70 #define DID_USERNAME    0x0040
71 #undef DID_MAX
72 #define DID_MAX         0x00ff
73
74 #undef KEPT_TERM
75 #define KEPT_TERM       0x0100
76 #undef KEPT_PATH
77 #define KEPT_PATH       0x0200
78 #undef KEPT_HOME
79 #define KEPT_HOME       0x0400
80 #undef KEPT_SHELL
81 #define KEPT_SHELL      0x0800
82 #undef KEPT_LOGNAME
83 #define KEPT_LOGNAME    0x1000
84 #undef KEPT_USER
85 #define KEPT_USER       0x2000
86 #undef KEPT_USERNAME
87 #define KEPT_USERNAME   0x4000
88 #undef KEPT_MAX
89 #define KEPT_MAX        0xff00
90
91 #undef VNULL
92 #define VNULL   (void *)NULL
93
94 struct environment {
95     char **envp;                /* pointer to the new environment */
96     size_t env_size;            /* size of new_environ in char **'s */
97     size_t env_len;             /* number of slots used, not counting NULL */
98 };
99
100 /*
101  * Prototypes
102  */
103 void rebuild_env                __P((int, int));
104 void sudo_setenv                __P((const char *, const char *, int));
105 void sudo_unsetenv              __P((const char *));
106 static void _sudo_setenv        __P((const char *, const char *, int));
107 static void insert_env          __P((char *, int, int));
108 static void sync_env            __P((void));
109
110 extern char **environ;          /* global environment */
111
112 /*
113  * Copy of the sudo-managed environment.
114  */
115 static struct environment env;
116
117 /*
118  * Default table of "bad" variables to remove from the environment.
119  * XXX - how to omit TERMCAP if it starts with '/'?
120  */
121 static const char *initial_badenv_table[] = {
122     "IFS",
123     "CDPATH",
124     "SHELLOPTS",
125     "PS4",
126     "LOCALDOMAIN",
127     "RES_OPTIONS",
128     "HOSTALIASES",
129     "NLSPATH",
130     "PATH_LOCALE",
131     "LD_*",
132     "_RLD*",
133 #ifdef __hpux
134     "SHLIB_PATH",
135 #endif /* __hpux */
136 #ifdef _AIX
137     "LDR_*",
138     "LIBPATH",
139     "AUTHSTATE",
140 #endif
141 #ifdef __APPLE__
142     "DYLD_*",
143 #endif
144 #ifdef HAVE_KERB4
145     "KRB_CONF*",
146     "KRBCONFDIR",
147     "KRBTKFILE",
148 #endif /* HAVE_KERB4 */
149 #ifdef HAVE_KERB5
150     "KRB5_CONFIG*",
151     "KRB5_KTNAME",
152 #endif /* HAVE_KERB5 */
153 #ifdef HAVE_SECURID
154     "VAR_ACE",
155     "USR_ACE",
156     "DLC_ACE",
157 #endif /* HAVE_SECURID */
158     "TERMINFO",                 /* terminfo, exclusive path to terminfo files */
159     "TERMINFO_DIRS",            /* terminfo, path(s) to terminfo files */
160     "TERMPATH",                 /* termcap, path(s) to termcap files */
161     "TERMCAP",                  /* XXX - only if it starts with '/' */
162     "ENV",                      /* ksh, file to source before script runs */
163     "BASH_ENV",                 /* bash, file to source before script runs */
164     "PS4",                      /* bash, prefix for lines in xtrace mode */
165     "GLOBIGNORE",               /* bash, globbing patterns to ignore */
166     "SHELLOPTS",                /* bash, extra command line options */
167     "JAVA_TOOL_OPTIONS",        /* java, extra command line options */
168     "PERLIO_DEBUG ",            /* perl, debugging output file */
169     "PERLLIB",                  /* perl, search path for modules/includes */
170     "PERL5LIB",                 /* perl 5, search path for modules/includes */
171     "PERL5OPT",                 /* perl 5, extra command line options */
172     "PERL5DB",                  /* perl 5, command used to load debugger */
173     "FPATH",                    /* ksh, search path for functions */
174     "NULLCMD",                  /* zsh, command for null file redirection */
175     "READNULLCMD",              /* zsh, command for null file redirection */
176     "ZDOTDIR",                  /* zsh, search path for dot files */
177     "TMPPREFIX",                /* zsh, prefix for temporary files */
178     "PYTHONHOME",               /* python, module search path */
179     "PYTHONPATH",               /* python, search path */
180     "PYTHONINSPECT",            /* python, allow inspection */
181     "RUBYLIB",                  /* ruby, library load path */
182     "RUBYOPT",                  /* ruby, extra command line options */
183     NULL
184 };
185
186 /*
187  * Default table of variables to check for '%' and '/' characters.
188  */
189 static const char *initial_checkenv_table[] = {
190     "COLORTERM",
191     "LANG",
192     "LANGUAGE",
193     "LC_*",
194     "LINGUAS",
195     "TERM",
196     NULL
197 };
198
199 /*
200  * Default table of variables to preserve in the environment.
201  */
202 static const char *initial_keepenv_table[] = {
203     "COLORS",
204     "DISPLAY",
205     "HOME",
206     "HOSTNAME",
207     "KRB5CCNAME",
208     "LS_COLORS",
209     "MAIL",
210     "PATH",
211     "PS1",
212     "PS2",
213     "TZ",
214     "XAUTHORITY",
215     "XAUTHORIZATION",
216     NULL
217 };
218
219 /*
220  * Syncronize our private copy of the environment with what is
221  * in environ.
222  */
223 static void
224 sync_env()
225 {
226     size_t evlen;
227     char **ep;
228
229     for (ep = environ; *ep != NULL; ep++)
230         continue;
231     evlen = ep - environ;
232     if (evlen + 1 > env.env_size) {
233         efree(env.envp);
234         env.env_size = evlen + 1 + 128;
235         env.envp = emalloc2(env.env_size, sizeof(char *));
236     }
237     memcpy(env.envp, environ, (evlen + 1) * sizeof(char *));
238     env.env_len = evlen;
239     environ = env.envp;
240 }
241
242 /*
243  * Similar to setenv(3) but operates on sudo's private copy of the environment
244  * and it always overwrites.  The dupcheck param determines whether we need
245  * to verify that the variable is not already set.
246  */
247 static void
248 _sudo_setenv(var, val, dupcheck)
249     const char *var;
250     const char *val;
251     int dupcheck;
252 {
253     char *estring;
254     size_t esize;
255
256     esize = strlen(var) + 1 + strlen(val) + 1;
257     estring = emalloc(esize);
258
259     /* Build environment string and insert it. */
260     if (strlcpy(estring, var, esize) >= esize ||
261         strlcat(estring, "=", esize) >= esize ||
262         strlcat(estring, val, esize) >= esize) {
263
264         errorx(1, "internal error, sudo_setenv() overflow");
265     }
266     insert_env(estring, dupcheck, FALSE);
267 }
268
269 #ifdef HAVE_LDAP
270 /*
271  * External version of sudo_setenv() that keeps things in sync with
272  * the environ pointer.
273  */
274 void
275 sudo_setenv(var, val, dupcheck)
276     const char *var;
277     const char *val;
278     int dupcheck;
279 {
280     char *estring;
281     size_t esize;
282
283     /* Make sure we are operating on the current environment. */
284     if (env.envp != environ)
285         sync_env();
286
287     esize = strlen(var) + 1 + strlen(val) + 1;
288     estring = emalloc(esize);
289
290     /* Build environment string and insert it. */
291     if (strlcpy(estring, var, esize) >= esize ||
292         strlcat(estring, "=", esize) >= esize ||
293         strlcat(estring, val, esize) >= esize) {
294
295         errorx(1, "internal error, sudo_setenv() overflow");
296     }
297     insert_env(estring, dupcheck, TRUE);
298 }
299 #endif /* HAVE_LDAP */
300
301 #if defined(HAVE_LDAP) || defined(HAVE_AIXAUTH)
302 /*
303  * Similar to unsetenv(3) but operates on sudo's private copy of the
304  * environment.
305  */
306 void
307 sudo_unsetenv(var)
308     const char *var;
309 {
310     char **nep;
311     size_t varlen;
312
313     /* Make sure we are operating on the current environment. */
314     if (env.envp != environ)
315         sync_env();
316
317     varlen = strlen(var);
318     for (nep = env.envp; *nep; nep++) {
319         if (strncmp(var, *nep, varlen) == 0 && (*nep)[varlen] == '=') {
320             /* Found it; move everything over by one and update len. */
321             memmove(nep, nep + 1,
322                 (env.env_len - (nep - env.envp)) * sizeof(char *));
323             env.env_len--;
324             return;
325         }
326     }
327 }
328 #endif /* HAVE_LDAP || HAVE_AIXAUTH */
329
330 /*
331  * Insert str into env.envp, assumes str has an '=' in it.
332  */
333 static void
334 insert_env(str, dupcheck, dosync)
335     char *str;
336     int dupcheck;
337     int dosync;
338 {
339     char **nep;
340     size_t varlen;
341
342     /* Make sure there is room for the new entry plus a NULL. */
343     if (env.env_len + 2 > env.env_size) {
344         env.env_size += 128;
345         env.envp = erealloc3(env.envp, env.env_size, sizeof(char *));
346         if (dosync)
347             environ = env.envp;
348     }
349
350     if (dupcheck) {
351             varlen = (strchr(str, '=') - str) + 1;
352
353             for (nep = env.envp; *nep; nep++) {
354                 if (strncmp(str, *nep, varlen) == 0) {
355                     if (dupcheck != -1)
356                         *nep = str;
357                     return;
358                 }
359             }
360     } else
361         nep = env.envp + env.env_len;
362
363     env.env_len++;
364     *nep++ = str;
365     *nep = NULL;
366 }
367
368 /*
369  * Check the env_delete blacklist.
370  * Returns TRUE if the variable was found, else false.
371  */
372 static int
373 matches_env_delete(var)
374     const char *var;
375 {
376     struct list_member *cur;
377     size_t len;
378     int iswild, match = FALSE;
379
380     /* Skip anything listed in env_delete. */
381     for (cur = def_env_delete; cur; cur = cur->next) {
382         len = strlen(cur->value);
383         /* Deal with '*' wildcard */
384         if (cur->value[len - 1] == '*') {
385             len--;
386             iswild = TRUE;
387         } else
388             iswild = FALSE;
389         if (strncmp(cur->value, var, len) == 0 &&
390             (iswild || var[len] == '=')) {
391             match = TRUE;
392             break;
393         }
394     }
395     return(match);
396 }
397
398 /*
399  * Apply the env_check list.
400  * Returns TRUE if the variable is allowed, FALSE if denied
401  * or -1 if no match.
402  */
403 static int
404 matches_env_check(var)
405     const char *var;
406 {
407     struct list_member *cur;
408     size_t len;
409     int iswild, keepit = -1;
410
411     for (cur = def_env_check; cur; cur = cur->next) {
412         len = strlen(cur->value);
413         /* Deal with '*' wildcard */
414         if (cur->value[len - 1] == '*') {
415             len--;
416             iswild = TRUE;
417         } else
418             iswild = FALSE;
419         if (strncmp(cur->value, var, len) == 0 &&
420             (iswild || var[len] == '=')) {
421             keepit = !strpbrk(var, "/%");
422             break;
423         }
424     }
425     return(keepit);
426 }
427
428 /*
429  * Check the env_keep list.
430  * Returns TRUE if the variable is allowed else FALSE.
431  */
432 static int
433 matches_env_keep(var)
434     const char *var;
435 {
436     struct list_member *cur;
437     size_t len;
438     int iswild, keepit = FALSE;
439
440     for (cur = def_env_keep; cur; cur = cur->next) {
441         len = strlen(cur->value);
442         /* Deal with '*' wildcard */
443         if (cur->value[len - 1] == '*') {
444             len--;
445             iswild = TRUE;
446         } else
447             iswild = FALSE;
448         if (strncmp(cur->value, var, len) == 0 &&
449             (iswild || var[len] == '=')) {
450             keepit = TRUE;
451             break;
452         }
453     }
454     return(keepit);
455 }
456
457 /*
458  * Build a new environment and ether clear potentially dangerous
459  * variables from the old one or start with a clean slate.
460  * Also adds sudo-specific variables (SUDO_*).
461  */
462 void
463 rebuild_env(sudo_mode, noexec)
464     int sudo_mode;
465     int noexec;
466 {
467     char **old_envp, **ep, *cp, *ps1;
468     char idbuf[MAX_UID_T_LEN];
469     unsigned int didvar;
470
471     /*
472      * Either clean out the environment or reset to a safe default.
473      */
474     ps1 = NULL;
475     didvar = 0;
476     env.env_len = 0;
477     env.env_size = 128;
478     old_envp = env.envp;
479     env.envp = emalloc2(env.env_size, sizeof(char *));
480     if (def_env_reset || ISSET(sudo_mode, MODE_LOGIN_SHELL)) {
481         /* Pull in vars we want to keep from the old environment. */
482         for (ep = environ; *ep; ep++) {
483             int keepit;
484
485             /* Skip variables with values beginning with () (bash functions) */
486             if ((cp = strchr(*ep, '=')) != NULL) {
487                 if (strncmp(cp, "=() ", 3) == 0)
488                     continue;
489             }
490
491             /*
492              * First check certain variables for '%' and '/' characters.
493              * If no match there, check the keep list.
494              * If nothing matched, we remove it from the environment.
495              */
496             keepit = matches_env_check(*ep);
497             if (keepit == -1)
498                 keepit = matches_env_keep(*ep);
499
500             if (!strncmp (*ep, "DISPLAY=",8)
501                 || !strncmp (*ep, "XAUTHORITY=", 11)
502                 || !strncmp (*ep, "XAUTHORIZATION=", 15)
503                 || !strncmp (*ep, "XAPPLRESDIR=", 12)
504                 || !strncmp (*ep, "XFILESEARCHPATH=", 16)
505                 || !strncmp (*ep, "XUSERFILESEARCHPATH=", 20)
506                 || !strncmp (*ep, "LANG=", 5)
507                 || !strncmp (*ep, "LANGUAGE=", 9)
508                 || !strncmp (*ep, "LC_", 3))
509               keepit = 1;
510
511             /* For SUDO_PS1 -> PS1 conversion. */
512             if (strncmp(*ep, "SUDO_PS1=", 8) == 0)
513                 ps1 = *ep + 5;
514
515             if (keepit) {
516                 /* Preserve variable. */
517                 switch (**ep) {
518                     case 'H':
519                         if (strncmp(*ep, "HOME=", 5) == 0)
520                             SET(didvar, DID_HOME);
521                         break;
522                     case 'L':
523                         if (strncmp(*ep, "LOGNAME=", 8) == 0)
524                             SET(didvar, DID_LOGNAME);
525                         break;
526                     case 'P':
527                         if (strncmp(*ep, "PATH=", 5) == 0)
528                             SET(didvar, DID_PATH);
529                         break;
530                     case 'S':
531                         if (strncmp(*ep, "SHELL=", 6) == 0)
532                             SET(didvar, DID_SHELL);
533                         break;
534                     case 'T':
535                         if (strncmp(*ep, "TERM=", 5) == 0)
536                             SET(didvar, DID_TERM);
537                         break;
538                     case 'U':
539                         if (strncmp(*ep, "USER=", 5) == 0)
540                             SET(didvar, DID_USER);
541                         if (strncmp(*ep, "USERNAME=", 5) == 0)
542                             SET(didvar, DID_USERNAME);
543                         break;
544                 }
545                 insert_env(*ep, FALSE, FALSE);
546             }
547         }
548         didvar |= didvar << 8;          /* convert DID_* to KEPT_* */
549
550         /*
551          * Add in defaults.  In -i mode these come from the runas user,
552          * otherwise they may be from the user's environment (depends
553          * on sudoers options).
554          */
555         if (ISSET(sudo_mode, MODE_LOGIN_SHELL)) {
556             _sudo_setenv("HOME", runas_pw->pw_dir, ISSET(didvar, DID_HOME));
557             _sudo_setenv("SHELL", runas_pw->pw_shell, ISSET(didvar, DID_SHELL));
558             _sudo_setenv("LOGNAME", runas_pw->pw_name,
559                 ISSET(didvar, DID_LOGNAME));
560             _sudo_setenv("USER", runas_pw->pw_name, ISSET(didvar, DID_USER));
561             _sudo_setenv("USERNAME", runas_pw->pw_name,
562                 ISSET(didvar, DID_USERNAME));
563         } else {
564             if (!ISSET(didvar, DID_HOME))
565                 _sudo_setenv("HOME", user_dir, FALSE);
566             if (!ISSET(didvar, DID_SHELL))
567                 _sudo_setenv("SHELL", sudo_user.pw->pw_shell, FALSE);
568             if (!ISSET(didvar, DID_LOGNAME))
569                 _sudo_setenv("LOGNAME", user_name, FALSE);
570             if (!ISSET(didvar, DID_USER))
571                 _sudo_setenv("USER", user_name, FALSE);
572             if (!ISSET(didvar, DID_USERNAME))
573                 _sudo_setenv("USERNAME", user_name, FALSE);
574         }
575     } else {
576         /*
577          * Copy environ entries as long as they don't match env_delete or
578          * env_check.
579          */
580         for (ep = environ; *ep; ep++) {
581             int okvar;
582
583             /* Skip variables with values beginning with () (bash functions) */
584             if ((cp = strchr(*ep, '=')) != NULL) {
585                 if (strncmp(cp, "=() ", 3) == 0)
586                     continue;
587             }
588
589             /*
590              * First check variables against the blacklist in env_delete.
591              * If no match there check for '%' and '/' characters.
592              */
593             okvar = matches_env_delete(*ep) != TRUE;
594             if (okvar)
595                 okvar = matches_env_check(*ep) != FALSE;
596
597             if (okvar) {
598                 if (strncmp(*ep, "SUDO_PS1=", 9) == 0)
599                     ps1 = *ep + 5;
600                 else if (strncmp(*ep, "PATH=", 5) == 0)
601                     SET(didvar, DID_PATH);
602                 else if (strncmp(*ep, "TERM=", 5) == 0)
603                     SET(didvar, DID_TERM);
604                 insert_env(*ep, FALSE, FALSE);
605             }
606         }
607     }
608     /* Replace the PATH envariable with a secure one? */
609     if (def_secure_path && !user_is_exempt()) {
610         _sudo_setenv("PATH", def_secure_path, TRUE);
611         SET(didvar, DID_PATH);
612     }
613
614     /* Set $USER, $LOGNAME and $USERNAME to target if "set_logname" is true. */
615     /* XXX - not needed for MODE_LOGIN_SHELL */
616     if (def_set_logname && runas_pw->pw_name) {
617         if (!ISSET(didvar, KEPT_LOGNAME))
618             _sudo_setenv("LOGNAME", runas_pw->pw_name, TRUE);
619         if (!ISSET(didvar, KEPT_USER))
620             _sudo_setenv("USER", runas_pw->pw_name, TRUE);
621         if (!ISSET(didvar, KEPT_USERNAME))
622             _sudo_setenv("USERNAME", runas_pw->pw_name, TRUE);
623     }
624
625     /* Set $HOME for `sudo -H'.  Only valid at PERM_FULL_RUNAS. */
626     /* XXX - not needed for MODE_LOGIN_SHELL */
627     if (runas_pw->pw_dir) {
628         if (ISSET(sudo_mode, MODE_RESET_HOME) ||
629             (ISSET(sudo_mode, MODE_RUN) && (def_always_set_home ||
630             (ISSET(sudo_mode, MODE_SHELL) && def_set_home))))
631             _sudo_setenv("HOME", runas_pw->pw_dir, TRUE);
632     }
633
634     /* Provide default values for $TERM and $PATH if they are not set. */
635     if (!ISSET(didvar, DID_TERM))
636         insert_env("TERM=unknown", FALSE, FALSE);
637     if (!ISSET(didvar, DID_PATH))
638         _sudo_setenv("PATH", _PATH_DEFPATH, FALSE);
639
640     /*
641      * Preload a noexec file?  For a list of LD_PRELOAD-alikes, see
642      * http://www.fortran-2000.com/ArnaudRecipes/sharedlib.html
643      * XXX - should prepend to original value, if any
644      */
645     if (noexec && def_noexec_file != NULL) {
646 #if defined(__darwin__) || defined(__APPLE__)
647         _sudo_setenv("DYLD_INSERT_LIBRARIES", def_noexec_file, TRUE);
648         _sudo_setenv("DYLD_FORCE_FLAT_NAMESPACE", "", TRUE);
649 #else
650 # if defined(__osf__) || defined(__sgi)
651         easprintf(&cp, "%s:DEFAULT", def_noexec_file);
652         _sudo_setenv("_RLD_LIST", cp, TRUE);
653         efree(cp);
654 # else
655 #  ifdef _AIX
656         _sudo_setenv("LDR_PRELOAD", def_noexec_file, TRUE);
657 #  else
658         _sudo_setenv("LD_PRELOAD", def_noexec_file, TRUE);
659 #  endif /* _AIX */
660 # endif /* __osf__ || __sgi */
661 #endif /* __darwin__ || __APPLE__ */
662     }
663
664     /* Set PS1 if SUDO_PS1 is set. */
665     if (ps1 != NULL)
666         insert_env(ps1, TRUE, FALSE);
667
668     /* Add the SUDO_COMMAND envariable (cmnd + args). */
669     if (user_args) {
670         easprintf(&cp, "%s %s", user_cmnd, user_args);
671         _sudo_setenv("SUDO_COMMAND", cp, TRUE);
672         efree(cp);
673     } else
674         _sudo_setenv("SUDO_COMMAND", user_cmnd, TRUE);
675
676     /* Add the SUDO_USER, SUDO_UID, SUDO_GID environment variables. */
677     _sudo_setenv("SUDO_USER", user_name, TRUE);
678     snprintf(idbuf, sizeof(idbuf), "%lu", (unsigned long) user_uid);
679     _sudo_setenv("SUDO_UID", idbuf, TRUE);
680     snprintf(idbuf, sizeof(idbuf), "%lu", (unsigned long) user_gid);
681     _sudo_setenv("SUDO_GID", idbuf, TRUE);
682
683     /* Install new environment. */
684     environ = env.envp;
685     efree(old_envp);
686 }
687
688 void
689 insert_env_vars(env_vars)
690     struct list_member *env_vars;
691 {
692     struct list_member *cur;
693
694     if (env_vars == NULL)
695         return;
696
697     /* Make sure we are operating on the current environment. */
698     if (env.envp != environ)
699         sync_env();
700
701     /* Add user-specified environment variables. */
702     for (cur = env_vars; cur != NULL; cur = cur->next)
703         insert_env(cur->value, TRUE, TRUE);
704 }
705
706 /*
707  * Validate the list of environment variables passed in on the command
708  * line against env_delete, env_check, and env_keep.
709  * Calls log_error() if any specified variables are not allowed.
710  */
711 void
712 validate_env_vars(env_vars)
713     struct list_member *env_vars;
714 {
715     struct list_member *var;
716     char *eq, *bad = NULL;
717     size_t len, blen = 0, bsize = 0;
718     int okvar;
719
720     for (var = env_vars; var != NULL; var = var->next) {
721         if (def_secure_path && !user_is_exempt() &&
722             strncmp(var->value, "PATH=", 5) == 0) {
723             okvar = FALSE;
724         } else if (def_env_reset) {
725             okvar = matches_env_check(var->value);
726             if (okvar == -1)
727                 okvar = matches_env_keep(var->value);
728         } else {
729             okvar = matches_env_delete(var->value) == FALSE;
730             if (okvar == FALSE)
731                 okvar = matches_env_check(var->value) != FALSE;
732         }
733         if (okvar == FALSE) {
734             /* Not allowed, add to error string, allocating as needed. */
735             if ((eq = strchr(var->value, '=')) != NULL)
736                 *eq = '\0';
737             len = strlen(var->value) + 2;
738             if (blen + len >= bsize) {
739                 do {
740                     bsize += 1024;
741                 } while (blen + len >= bsize);
742                 bad = erealloc(bad, bsize);
743                 bad[blen] = '\0';
744             }
745             strlcat(bad, var->value, bsize);
746             strlcat(bad, ", ", bsize);
747             blen += len;
748             if (eq != NULL)
749                 *eq = '=';
750         }
751     }
752     if (bad != NULL) {
753         bad[blen - 2] = '\0';           /* remove trailing ", " */
754         log_error(NO_MAIL,
755             "sorry, you are not allowed to set the following environment variables: %s", bad);
756         /* NOTREACHED */
757         efree(bad);
758     }
759 }
760
761 /*
762  * Read in /etc/environment ala AIX and Linux.
763  * Lines are in the form of NAME=VALUE
764  * Invalid lines, blank lines, or lines consisting solely of a comment
765  * character are skipped.
766  */
767 void
768 read_env_file(path, replace)
769     const char *path;
770     int replace;
771 {
772     FILE *fp;
773     char *cp;
774
775     if ((fp = fopen(path, "r")) == NULL)
776         return;
777
778     /* Make sure we are operating on the current environment. */
779     if (env.envp != environ)
780         sync_env();
781
782     while ((cp = sudo_parseln(fp)) != NULL) {
783         /* Skip blank or comment lines */
784         if (*cp == '\0')
785             continue;
786
787         /* Must be of the form name=value */
788         if (strchr(cp, '=') == NULL)
789             continue;
790
791         insert_env(estrdup(cp), replace ? TRUE : -1, TRUE);
792     }
793     fclose(fp);
794 }
795
796 void
797 init_envtables()
798 {
799     struct list_member *cur;
800     const char **p;
801
802     /* Fill in the "env_delete" list. */
803     for (p = initial_badenv_table; *p; p++) {
804         cur = emalloc(sizeof(struct list_member));
805         cur->value = estrdup(*p);
806         cur->next = def_env_delete;
807         def_env_delete = cur;
808     }
809
810     /* Fill in the "env_check" list. */
811     for (p = initial_checkenv_table; *p; p++) {
812         cur = emalloc(sizeof(struct list_member));
813         cur->value = estrdup(*p);
814         cur->next = def_env_check;
815         def_env_check = cur;
816     }
817
818     /* Fill in the "env_keep" list. */
819     for (p = initial_keepenv_table; *p; p++) {
820         cur = emalloc(sizeof(struct list_member));
821         cur->value = estrdup(*p);
822         cur->next = def_env_keep;
823         def_env_keep = cur;
824     }
825 }