Imported Upstream version 1.6.8p12
[debian/sudo] / env.c
1 /*
2  * Copyright (c) 2000-2004 Todd C. Miller <Todd.Miller@courtesan.com>
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  *
16  * Sponsored in part by the Defense Advanced Research Projects
17  * Agency (DARPA) and Air Force Research Laboratory, Air Force
18  * Materiel Command, USAF, under agreement number F39502-99-1-0512.
19  */
20
21 #include "config.h"
22
23 #include <sys/types.h>
24 #include <sys/param.h>
25 #include <sys/stat.h>
26 #include <stdio.h>
27 #ifdef STDC_HEADERS
28 # include <stdlib.h>
29 # include <stddef.h>
30 #else
31 # ifdef HAVE_STDLIB_H
32 #  include <stdlib.h>
33 # endif
34 #endif /* STDC_HEADERS */
35 #ifdef HAVE_STRING_H
36 # include <string.h>
37 #else
38 # ifdef HAVE_STRINGS_H
39 #  include <strings.h>
40 # endif
41 #endif /* HAVE_STRING_H */
42 #ifdef HAVE_UNISTD_H
43 # include <unistd.h>
44 #endif /* HAVE_UNISTD_H */
45 #ifdef HAVE_ERR_H
46 # include <err.h>
47 #else
48 # include "emul/err.h"
49 #endif /* HAVE_ERR_H */
50 #include <pwd.h>
51
52 #include "sudo.h"
53
54 #ifndef lint
55 static const char rcsid[] = "$Sudo: env.c,v 1.42 2004/09/08 15:57:49 millert Exp $";
56 #endif /* lint */
57
58 /*
59  * Flags used in rebuild_env()
60  */
61 #undef DID_TERM
62 #define DID_TERM        0x01
63 #undef DID_PATH
64 #define DID_PATH        0x02
65 #undef DID_HOME
66 #define DID_HOME        0x04
67 #undef DID_SHELL
68 #define DID_SHELL       0x08
69 #undef DID_LOGNAME
70 #define DID_LOGNAME     0x10
71 #undef DID_USER
72 #define DID_USER        0x20
73
74 #undef VNULL
75 #define VNULL   (VOID *)NULL
76
77 /*
78  * Prototypes
79  */
80 char **rebuild_env              __P((char **, int, int));
81 char **zero_env                 __P((char **));
82 static void insert_env          __P((char *, int));
83 static char *format_env         __P((char *, ...));
84
85 /*
86  * Default table of "bad" variables to remove from the environment.
87  * XXX - how to omit TERMCAP if it starts with '/'?
88  */
89 static const char *initial_badenv_table[] = {
90     "IFS",
91     "CDPATH",
92     "LOCALDOMAIN",
93     "RES_OPTIONS",
94     "HOSTALIASES",
95     "NLSPATH",
96     "PATH_LOCALE",
97     "LD_*",
98     "_RLD*",
99 #ifdef __hpux
100     "SHLIB_PATH",
101 #endif /* __hpux */
102 #ifdef _AIX
103     "LIBPATH",
104 #endif /* _AIX */
105 #ifdef __APPLE__
106     "DYLD_*",
107 #endif
108 #ifdef HAVE_KERB4
109     "KRB_CONF*",
110     "KRBCONFDIR",
111     "KRBTKFILE",
112 #endif /* HAVE_KERB4 */
113 #ifdef HAVE_KERB5
114     "KRB5_CONFIG*",
115 #endif /* HAVE_KERB5 */
116 #ifdef HAVE_SECURID
117     "VAR_ACE",
118     "USR_ACE",
119     "DLC_ACE",
120 #endif /* HAVE_SECURID */
121     "TERMINFO",
122     "TERMINFO_DIRS",
123     "TERMPATH",
124     "TERMCAP",                  /* XXX - only if it starts with '/' */
125     "ENV",
126     "BASH_ENV",
127     "PS4",
128     "SHELLOPTS",
129     "JAVA_TOOL_OPTIONS",
130     "PERLLIB",
131     "PERL5LIB",
132     "PERL5OPT",
133     NULL
134 };
135
136 /*
137  * Default table of variables to check for '%' and '/' characters.
138  */
139 static const char *initial_checkenv_table[] = {
140     "LC_*",
141     "LANG",
142     "LANGUAGE",
143     NULL
144 };
145
146 static char **new_environ;      /* Modified copy of the environment */
147 static size_t env_size;         /* size of new_environ in char **'s */
148 static size_t env_len;          /* number of slots used, not counting NULL */
149
150 /*
151  * Zero out environment and replace with a minimal set of KRB5CCNAME
152  * USER, LOGNAME, HOME, TZ, PATH (XXX - should just set path to default)
153  * May set user_path, user_shell, and/or user_prompt as side effects.
154  */
155 char **
156 zero_env(envp)
157     char **envp;
158 {
159     static char *newenv[9];
160     char **ep, **nep = newenv;
161     char **ne_last = &newenv[(sizeof(newenv) / sizeof(newenv[0])) - 1];
162     extern char *prev_user;
163
164     for (ep = envp; *ep; ep++) {
165         switch (**ep) {
166             case 'H':
167                 if (strncmp("HOME=", *ep, 5) == 0)
168                     break;
169                 continue;
170             case 'K':
171                 if (strncmp("KRB5CCNAME=", *ep, 11) == 0)
172                     break;
173                 continue;
174             case 'L':
175                 if (strncmp("LOGNAME=", *ep, 8) == 0)
176                     break;
177                 continue;
178             case 'P':
179                 if (strncmp("PATH=", *ep, 5) == 0) {
180                     user_path = *ep + 5;
181                     /* XXX - set to sane default instead of user's? */
182                     break;
183                 }
184                 continue;
185             case 'S':
186                 if (strncmp("SHELL=", *ep, 6) == 0)
187                     user_shell = *ep + 6;
188                 else if (!user_prompt && strncmp("SUDO_PROMPT=", *ep, 12) == 0)
189                     user_prompt = *ep + 12;
190                 else if (strncmp("SUDO_USER=", *ep, 10) == 0)
191                     prev_user = *ep + 10;
192                 continue;
193             case 'T':
194                 if (strncmp("TZ=", *ep, 3) == 0)
195                     break;
196                 continue;
197             case 'U':
198                 if (strncmp("USER=", *ep, 5) == 0)
199                     break;
200                 continue;
201             default:
202                 continue;
203         }
204
205         /* Deal with multiply defined variables (take first instance) */
206         for (nep = newenv; *nep; nep++) {
207             if (**nep == **ep)
208                 break;
209         }
210         if (*nep == NULL) {
211             if (nep < ne_last)
212                 *nep++ = *ep;
213             else
214                 errx(1, "internal error, attempt to write outside newenv");
215         }
216     }
217
218 #ifdef HAVE_LDAP
219     /*
220      * Prevent OpenLDAP from reading any user dotfiles
221      * or files in the current directory.
222      *
223      */      
224     if (nep < ne_last)
225         *nep++ = "LDAPNOINIT=1";
226     else
227         errx(1, "internal error, attempt to write outside newenv");
228 #endif
229
230     return(&newenv[0]);
231 }
232
233 /*
234  * Given a variable and value, allocate and format an environment string.
235  */
236 static char *
237 #ifdef __STDC__
238 format_env(char *var, ...)
239 #else
240 format_env(var, va_alist)
241     char *var;
242     va_dcl
243 #endif
244 {
245     char *estring;
246     char *val;
247     size_t esize;
248     va_list ap;
249
250 #ifdef __STDC__
251     va_start(ap, var);
252 #else
253     va_start(ap);
254 #endif
255     esize = strlen(var) + 2;
256     while ((val = va_arg(ap, char *)) != NULL)
257         esize += strlen(val);
258     va_end(ap);
259     estring = (char *) emalloc(esize);
260
261     /* Store variable name and the '=' separator.  */
262     if (strlcpy(estring, var, esize) >= esize ||
263         strlcat(estring, "=", esize) >= esize) {
264
265         errx(1, "internal error, format_env() overflow");
266     }
267
268     /* Now store the variable's value (if any) */
269 #ifdef __STDC__
270     va_start(ap, var);
271 #else
272     va_start(ap);
273 #endif
274     while ((val = va_arg(ap, char *)) != NULL) {
275         if (strlcat(estring, val, esize) >= esize)
276             errx(1, "internal error, format_env() overflow");
277     }
278     va_end(ap);
279
280     return(estring);
281 }
282
283 /*
284  * Insert str into new_environ, assumes str has an '=' in it.
285  * NOTE: no other routines may modify new_environ, env_size, or env_len.
286  */
287 static void
288 insert_env(str, dupcheck)
289     char *str;
290     int dupcheck;
291 {
292     char **nep;
293     size_t varlen;
294
295     /* Make sure there is room for the new entry plus a NULL. */
296     if (env_len + 2 > env_size) {
297         env_size += 128;
298         new_environ = erealloc3(new_environ, env_size, sizeof(char *));
299     }
300
301     if (dupcheck) {
302             varlen = (strchr(str, '=') - str) + 1;
303
304             for (nep = new_environ; *nep; nep++) {
305                 if (strncmp(str, *nep, varlen) == 0) {
306                     *nep = str;
307                     return;
308                 }
309             }
310     } else
311         nep = &new_environ[env_len];
312
313     env_len++;
314     *nep++ = str;
315     *nep = NULL;
316 }
317
318 /*
319  * Build a new environment and ether clear potentially dangerous
320  * variables from the old one or start with a clean slate.
321  * Also adds sudo-specific variables (SUDO_*).
322  */
323 char **
324 rebuild_env(envp, sudo_mode, noexec)
325     char **envp;
326     int sudo_mode;
327     int noexec;
328 {
329     char **ep, *cp, *ps1;
330     int okvar, iswild, didvar;
331     size_t len;
332     struct list_member *cur;
333
334     /*
335      * Either clean out the environment or reset to a safe default.
336      */
337     ps1 = NULL;
338     didvar = 0;
339     if (def_env_reset) {
340         int keepit;
341
342         /* Pull in vars we want to keep from the old environment. */
343         for (ep = envp; *ep; ep++) {
344             keepit = 0;
345
346             /* Skip variables with values beginning with () (bash functions) */
347             if ((cp = strchr(*ep, '=')) != NULL) {
348                 if (strncmp(cp, "=() ", 3) == 0)
349                     continue;
350             }
351
352             for (cur = def_env_keep; cur; cur = cur->next) {
353                 len = strlen(cur->value);
354                 /* Deal with '*' wildcard */
355                 if (cur->value[len - 1] == '*') {
356                     len--;
357                     iswild = 1;
358                 } else
359                     iswild = 0;
360                 if (strncmp(cur->value, *ep, len) == 0 &&
361                     (iswild || (*ep)[len] == '=')) {
362                     /* We always preserve TERM, no special treatment needed. */
363                     if (strncmp(*ep, "TERM=", 5) != 0)
364                         keepit = 1;
365                     break;
366                 }
367             }
368
369             /* For SUDO_PS1 -> PS1 conversion. */
370             if (strncmp(*ep, "SUDO_PS1=", 8) == 0)
371                 ps1 = *ep + 5;
372
373             if (keepit) {
374                 /* Preserve variable. */
375                 switch (**ep) {
376                     case 'H':
377                         if (strncmp(*ep, "HOME=", 5) == 0)
378                             SET(didvar, DID_HOME);
379                         break;
380                     case 'S':
381                         if (strncmp(*ep, "SHELL=", 6) == 0)
382                             SET(didvar, DID_SHELL);
383                         break;
384                     case 'L':
385                         if (strncmp(*ep, "LOGNAME=", 8) == 0)
386                             SET(didvar, DID_LOGNAME);
387                         break;
388                     case 'U':
389                         if (strncmp(*ep, "USER=", 5) == 0)
390                             SET(didvar, DID_USER);
391                         break;
392                 }
393                 insert_env(*ep, 0);
394             } else {
395                 /* Preserve TERM and PATH, ignore anything else. */
396                 if (!ISSET(didvar, DID_TERM) && strncmp(*ep, "TERM=", 5) == 0) {
397                     insert_env(*ep, 0);
398                     SET(didvar, DID_TERM);
399                 } else if (!ISSET(didvar, DID_PATH) && strncmp(*ep, "PATH=", 5) == 0) {
400                     insert_env(*ep, 0);
401                     SET(didvar, DID_PATH);
402                 }
403             }
404         }
405
406         /*
407          * Add in defaults.  In -i mode these come from the runas user,
408          * otherwise they may be from the user's environment (depends
409          * on sudoers options).
410          */
411         if (ISSET(sudo_mode, MODE_LOGIN_SHELL)) {
412             insert_env(format_env("HOME", runas_pw->pw_dir, VNULL), 0);
413             insert_env(format_env("SHELL", runas_pw->pw_shell, VNULL), 0);
414             insert_env(format_env("LOGNAME", runas_pw->pw_name, VNULL), 0);
415             insert_env(format_env("USER", runas_pw->pw_name, VNULL), 0);
416         } else {
417             if (!ISSET(didvar, DID_HOME))
418                 insert_env(format_env("HOME", user_dir, VNULL), 0);
419             if (!ISSET(didvar, DID_SHELL))
420                 insert_env(format_env("SHELL", sudo_user.pw->pw_shell, VNULL), 0);
421             if (!ISSET(didvar, DID_LOGNAME))
422                 insert_env(format_env("LOGNAME", user_name, VNULL), 0);
423             if (!ISSET(didvar, DID_USER))
424                 insert_env(format_env("USER", user_name, VNULL), 0);
425         }
426     } else {
427         /*
428          * Copy envp entries as long as they don't match env_delete or
429          * env_check.
430          */
431         for (ep = envp; *ep; ep++) {
432             okvar = 1;
433
434             /* Skip variables with values beginning with () (bash functions) */
435             if ((cp = strchr(*ep, '=')) != NULL) {
436                 if (strncmp(cp, "=() ", 3) == 0)
437                     continue;
438             }
439
440             /* Skip anything listed in env_delete. */
441             for (cur = def_env_delete; cur && okvar; cur = cur->next) {
442                 len = strlen(cur->value);
443                 /* Deal with '*' wildcard */
444                 if (cur->value[len - 1] == '*') {
445                     len--;
446                     iswild = 1;
447                 } else
448                     iswild = 0;
449                 if (strncmp(cur->value, *ep, len) == 0 &&
450                     (iswild || (*ep)[len] == '=')) {
451                     okvar = 0;
452                 }
453             }
454
455             /* Check certain variables for '%' and '/' characters. */
456             for (cur = def_env_check; cur && okvar; cur = cur->next) {
457                 len = strlen(cur->value);
458                 /* Deal with '*' wildcard */
459                 if (cur->value[len - 1] == '*') {
460                     len--;
461                     iswild = 1;
462                 } else
463                     iswild = 0;
464                 if (strncmp(cur->value, *ep, len) == 0 &&
465                     (iswild || (*ep)[len] == '=') &&
466                     strpbrk(*ep, "/%")) {
467                     okvar = 0;
468                 }
469             }
470
471             if (okvar) {
472                 if (strncmp(*ep, "SUDO_PS1=", 9) == 0)
473                     ps1 = *ep + 5;
474                 else if (strncmp(*ep, "PATH=", 5) == 0)
475                     SET(didvar, DID_PATH);
476                 else if (strncmp(*ep, "TERM=", 5) == 0)
477                     SET(didvar, DID_TERM);
478                 insert_env(*ep, 0);
479             }
480         }
481     }
482     /* Provide default values for $TERM and $PATH if they are not set. */
483     if (!ISSET(didvar, DID_TERM))
484         insert_env("TERM=unknown", 0);
485     if (!ISSET(didvar, DID_PATH))
486         insert_env(format_env("PATH", _PATH_DEFPATH, VNULL), 0);
487
488 #ifdef SECURE_PATH
489     /* Replace the PATH envariable with a secure one. */
490     insert_env(format_env("PATH", SECURE_PATH, VNULL), 1);
491 #endif
492
493     /* Set $USER and $LOGNAME to target if "set_logname" is true. */
494     if (def_set_logname && runas_pw->pw_name) {
495         insert_env(format_env("LOGNAME", runas_pw->pw_name, VNULL), 1);
496         insert_env(format_env("USER", runas_pw->pw_name, VNULL), 1);
497     }
498
499     /* Set $HOME for `sudo -H'.  Only valid at PERM_FULL_RUNAS. */
500     if (ISSET(sudo_mode, MODE_RESET_HOME) && runas_pw->pw_dir)
501         insert_env(format_env("HOME", runas_pw->pw_dir, VNULL), 1);
502
503     /*
504      * Preload a noexec file?  For a list of LD_PRELOAD-alikes, see
505      * http://www.fortran-2000.com/ArnaudRecipes/sharedlib.html
506      * XXX - should prepend to original value, if any
507      */
508     if (noexec && def_noexec_file != NULL) {
509 #if defined(__darwin__) || defined(__APPLE__)
510         insert_env(format_env("DYLD_INSERT_LIBRARIES", def_noexec_file, VNULL), 1);
511         insert_env(format_env("DYLD_FORCE_FLAT_NAMESPACE", VNULL), 1);
512 #else
513 # if defined(__osf__) || defined(__sgi)
514         insert_env(format_env("_RLD_LIST", def_noexec_file, ":DEFAULT", VNULL), 1);
515 # else
516         insert_env(format_env("LD_PRELOAD", def_noexec_file, VNULL), 1);
517 # endif
518 #endif
519     }
520
521     /* Set PS1 if SUDO_PS1 is set. */
522     if (ps1)
523         insert_env(ps1, 1);
524
525     /* Add the SUDO_COMMAND envariable (cmnd + args). */
526     if (user_args)
527         insert_env(format_env("SUDO_COMMAND", user_cmnd, " ", user_args, VNULL), 1);
528     else
529         insert_env(format_env("SUDO_COMMAND", user_cmnd, VNULL), 1);
530
531     /* Add the SUDO_USER, SUDO_UID, SUDO_GID environment variables. */
532     insert_env(format_env("SUDO_USER", user_name, VNULL), 1);
533     easprintf(&cp, "SUDO_UID=%lu", (unsigned long) user_uid);
534     insert_env(cp, 1);
535     easprintf(&cp, "SUDO_GID=%lu", (unsigned long) user_gid);
536     insert_env(cp, 1);
537
538     return(new_environ);
539 }
540
541 void
542 init_envtables()
543 {
544     struct list_member *cur;
545     const char **p;
546
547     /* Fill in "env_delete" variable. */
548     for (p = initial_badenv_table; *p; p++) {
549         cur = emalloc(sizeof(struct list_member));
550         cur->value = estrdup(*p);
551         cur->next = def_env_delete;
552         def_env_delete = cur;
553     }
554
555     /* Fill in "env_check" variable. */
556     for (p = initial_checkenv_table; *p; p++) {
557         cur = emalloc(sizeof(struct list_member));
558         cur->value = estrdup(*p);
559         cur->next = def_env_check;
560         def_env_check = cur;
561     }
562 }