Imported Debian patch 1.6.8p7-1.3
[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     "SHELLOPTS",
93     "PS4",
94     "LOCALDOMAIN",
95     "RES_OPTIONS",
96     "HOSTALIASES",
97     "NLSPATH",
98     "PATH_LOCALE",
99     "LD_*",
100     "_RLD*",
101 #ifdef __hpux
102     "SHLIB_PATH",
103 #endif /* __hpux */
104 #ifdef _AIX
105     "LIBPATH",
106 #endif /* _AIX */
107 #ifdef __APPLE__
108     "DYLD_*",
109 #endif
110 #ifdef HAVE_KERB4
111     "KRB_CONF*",
112     "KRBCONFDIR",
113     "KRBTKFILE",
114 #endif /* HAVE_KERB4 */
115 #ifdef HAVE_KERB5
116     "KRB5_CONFIG*",
117 #endif /* HAVE_KERB5 */
118 #ifdef HAVE_SECURID
119     "VAR_ACE",
120     "USR_ACE",
121     "DLC_ACE",
122 #endif /* HAVE_SECURID */
123     "TERMINFO",
124     "TERMINFO_DIRS",
125     "TERMPATH",
126     "TERMCAP",                  /* XXX - only if it starts with '/' */
127     "ENV",
128     "BASH_ENV",
129     NULL
130 };
131
132 /*
133  * Default table of variables to check for '%' and '/' characters.
134  */
135 static const char *initial_checkenv_table[] = {
136     "LC_*",
137     "LANG",
138     "LANGUAGE",
139     "TERM",
140     NULL
141 };
142
143 static char **new_environ;      /* Modified copy of the environment */
144 static size_t env_size;         /* size of new_environ in char **'s */
145 static size_t env_len;          /* number of slots used, not counting NULL */
146
147 /*
148  * Zero out environment and replace with a minimal set of KRB5CCNAME
149  * USER, LOGNAME, HOME, TZ, PATH (XXX - should just set path to default)
150  * May set user_path, user_shell, and/or user_prompt as side effects.
151  */
152 char **
153 zero_env(envp)
154     char **envp;
155 {
156     static char *newenv[9];
157     char **ep, **nep = newenv;
158     char **ne_last = &newenv[(sizeof(newenv) / sizeof(newenv[0])) - 1];
159     extern char *prev_user;
160
161     for (ep = envp; *ep; ep++) {
162         switch (**ep) {
163             case 'H':
164                 if (strncmp("HOME=", *ep, 5) == 0)
165                     break;
166                 continue;
167             case 'K':
168                 if (strncmp("KRB5CCNAME=", *ep, 11) == 0)
169                     break;
170                 continue;
171             case 'L':
172                 if (strncmp("LOGNAME=", *ep, 8) == 0)
173                     break;
174                 continue;
175             case 'P':
176                 if (strncmp("PATH=", *ep, 5) == 0) {
177                     user_path = *ep + 5;
178                     /* XXX - set to sane default instead of user's? */
179                     break;
180                 }
181                 continue;
182             case 'S':
183                 if (strncmp("SHELL=", *ep, 6) == 0)
184                     user_shell = *ep + 6;
185                 else if (!user_prompt && strncmp("SUDO_PROMPT=", *ep, 12) == 0)
186                     user_prompt = *ep + 12;
187                 else if (strncmp("SUDO_USER=", *ep, 10) == 0)
188                     prev_user = *ep + 10;
189                 continue;
190             case 'T':
191                 if (strncmp("TZ=", *ep, 3) == 0)
192                     break;
193                 continue;
194             case 'U':
195                 if (strncmp("USER=", *ep, 5) == 0)
196                     break;
197                 continue;
198             default:
199                 continue;
200         }
201
202         /* Deal with multiply defined variables (take first instance) */
203         for (nep = newenv; *nep; nep++) {
204             if (**nep == **ep)
205                 break;
206         }
207         if (*nep == NULL) {
208             if (nep < ne_last)
209                 *nep++ = *ep;
210             else
211                 errx(1, "internal error, attempt to write outside newenv");
212         }
213     }
214
215 #ifdef HAVE_LDAP
216     /*
217      * Prevent OpenLDAP from reading any user dotfiles
218      * or files in the current directory.
219      *
220      */      
221     if (nep < ne_last)
222         *nep++ = "LDAPNOINIT=1";
223     else
224         errx(1, "internal error, attempt to write outside newenv");
225 #endif
226
227     return(&newenv[0]);
228 }
229
230 /*
231  * Given a variable and value, allocate and format an environment string.
232  */
233 static char *
234 #ifdef __STDC__
235 format_env(char *var, ...)
236 #else
237 format_env(var, va_alist)
238     char *var;
239     va_dcl
240 #endif
241 {
242     char *estring;
243     char *val;
244     size_t esize;
245     va_list ap;
246
247 #ifdef __STDC__
248     va_start(ap, var);
249 #else
250     va_start(ap);
251 #endif
252     esize = strlen(var) + 2;
253     while ((val = va_arg(ap, char *)) != NULL)
254         esize += strlen(val);
255     va_end(ap);
256     estring = (char *) emalloc(esize);
257
258     /* Store variable name and the '=' separator.  */
259     if (strlcpy(estring, var, esize) >= esize ||
260         strlcat(estring, "=", esize) >= esize) {
261
262         errx(1, "internal error, format_env() overflow");
263     }
264
265     /* Now store the variable's value (if any) */
266 #ifdef __STDC__
267     va_start(ap, var);
268 #else
269     va_start(ap);
270 #endif
271     while ((val = va_arg(ap, char *)) != NULL) {
272         if (strlcat(estring, val, esize) >= esize)
273             errx(1, "internal error, format_env() overflow");
274     }
275     va_end(ap);
276
277     return(estring);
278 }
279
280 /*
281  * Insert str into new_environ, assumes str has an '=' in it.
282  * NOTE: no other routines may modify new_environ, env_size, or env_len.
283  */
284 static void
285 insert_env(str, dupcheck)
286     char *str;
287     int dupcheck;
288 {
289     char **nep;
290     size_t varlen;
291
292     /* Make sure there is room for the new entry plus a NULL. */
293     if (env_len + 2 > env_size) {
294         env_size += 128;
295         new_environ = erealloc3(new_environ, env_size, sizeof(char *));
296     }
297
298     if (dupcheck) {
299             varlen = (strchr(str, '=') - str) + 1;
300
301             for (nep = new_environ; *nep; nep++) {
302                 if (strncmp(str, *nep, varlen) == 0) {
303                     *nep = str;
304                     return;
305                 }
306             }
307     } else
308         nep = &new_environ[env_len];
309
310     env_len++;
311     *nep++ = str;
312     *nep = NULL;
313 }
314
315 /*
316  * Build a new environment and ether clear potentially dangerous
317  * variables from the old one or start with a clean slate.
318  * Also adds sudo-specific variables (SUDO_*).
319  */
320 char **
321 rebuild_env(envp, sudo_mode, noexec)
322     char **envp;
323     int sudo_mode;
324     int noexec;
325 {
326     char **ep, *cp, *ps1;
327     int okvar, iswild, didvar;
328     size_t len;
329     struct list_member *cur;
330
331     /*
332      * Either clean out the environment or reset to a safe default.
333      */
334     ps1 = NULL;
335     didvar = 0;
336     if (def_env_reset) {
337         int keepit;
338
339         /* Pull in vars we want to keep from the old environment. */
340         for (ep = envp; *ep; ep++) {
341             keepit = 0;
342
343             /* Skip variables with values beginning with () (bash functions) */
344             if ((cp = strchr(*ep, '=')) != NULL) {
345                 if (strncmp(cp, "=() ", 3) == 0)
346                     continue;
347             }
348
349             for (cur = def_env_keep; cur; cur = cur->next) {
350                 len = strlen(cur->value);
351                 /* Deal with '*' wildcard */
352                 if (cur->value[len - 1] == '*') {
353                     len--;
354                     iswild = 1;
355                 } else
356                     iswild = 0;
357                 if (strncmp(cur->value, *ep, len) == 0 &&
358                     (iswild || (*ep)[len] == '=')) {
359                     /* We always preserve TERM, no special treatment needed. */
360                     if (strncmp(*ep, "TERM=", 5) != 0)
361                         keepit = 1;
362                     break;
363                 }
364             }
365
366             /* For SUDO_PS1 -> PS1 conversion. */
367             if (strncmp(*ep, "SUDO_PS1=", 8) == 0)
368                 ps1 = *ep + 5;
369
370             if (keepit) {
371                 /* Preserve variable. */
372                 switch (**ep) {
373                     case 'H':
374                         if (strncmp(*ep, "HOME=", 5) == 0)
375                             SET(didvar, DID_HOME);
376                         break;
377                     case 'S':
378                         if (strncmp(*ep, "SHELL=", 6) == 0)
379                             SET(didvar, DID_SHELL);
380                         break;
381                     case 'L':
382                         if (strncmp(*ep, "LOGNAME=", 8) == 0)
383                             SET(didvar, DID_LOGNAME);
384                         break;
385                     case 'U':
386                         if (strncmp(*ep, "USER=", 5) == 0)
387                             SET(didvar, DID_USER);
388                         break;
389                 }
390                 insert_env(*ep, 0);
391             } else {
392                 /* Preserve TERM and PATH, ignore anything else. */
393                 if (!ISSET(didvar, DID_TERM) && strncmp(*ep, "TERM=", 5) == 0) {
394                     insert_env(*ep, 0);
395                     SET(didvar, DID_TERM);
396                 } else if (!ISSET(didvar, DID_PATH) && strncmp(*ep, "PATH=", 5) == 0) {
397                     insert_env(*ep, 0);
398                     SET(didvar, DID_PATH);
399                 }
400             }
401         }
402
403         /*
404          * Add in defaults.  In -i mode these come from the runas user,
405          * otherwise they may be from the user's environment (depends
406          * on sudoers options).
407          */
408         if (ISSET(sudo_mode, MODE_LOGIN_SHELL)) {
409             insert_env(format_env("HOME", runas_pw->pw_dir, VNULL), 0);
410             insert_env(format_env("SHELL", runas_pw->pw_shell, VNULL), 0);
411             insert_env(format_env("LOGNAME", runas_pw->pw_name, VNULL), 0);
412             insert_env(format_env("USER", runas_pw->pw_name, VNULL), 0);
413         } else {
414             if (!ISSET(didvar, DID_HOME))
415                 insert_env(format_env("HOME", user_dir, VNULL), 0);
416             if (!ISSET(didvar, DID_SHELL))
417                 insert_env(format_env("SHELL", sudo_user.pw->pw_shell, VNULL), 0);
418             if (!ISSET(didvar, DID_LOGNAME))
419                 insert_env(format_env("LOGNAME", user_name, VNULL), 0);
420             if (!ISSET(didvar, DID_USER))
421                 insert_env(format_env("USER", user_name, VNULL), 0);
422         }
423     } else {
424         /*
425          * Copy envp entries as long as they don't match env_delete or
426          * env_check.
427          */
428         for (ep = envp; *ep; ep++) {
429             okvar = 0;
430
431             /* Skip variables with values beginning with () (bash functions) */
432             if ((cp = strchr(*ep, '=')) != NULL) {
433                 if (strncmp(cp, "=() ", 3) == 0)
434                     continue;
435             }
436
437             /* Skip anything listed in env_delete. */
438 #if 0
439             for (cur = def_env_delete; cur && okvar; cur = cur->next) {
440                 len = strlen(cur->value);
441                 /* Deal with '*' wildcard */
442                 if (cur->value[len - 1] == '*') {
443                     len--;
444                     iswild = 1;
445                 } else
446                     iswild = 0;
447                 if (strncmp(cur->value, *ep, len) == 0 &&
448                     (iswild || (*ep)[len] == '=')) {
449                     okvar = 0;
450                 }
451             }
452 #endif
453
454             /* Check certain variables for '%' and '/' characters. */
455             for (cur = def_env_check; cur; cur = cur->next) {
456                 len = strlen(cur->value);
457                 /* Deal with '*' wildcard */
458                 if (cur->value[len - 1] == '*') {
459                     len--;
460                     iswild = 1;
461                 } else
462                     iswild = 0;
463                 if (strncmp(cur->value, *ep, len) == 0 &&
464                     (iswild || (*ep)[len] == '=') &&
465                     strpbrk(*ep, "/%") == NULL) {
466                     okvar = 1;
467                 }
468             }
469
470             if (okvar) {
471                 if (strncmp(*ep, "SUDO_PS1=", 9) == 0)
472                     ps1 = *ep + 5;
473                 else if (strncmp(*ep, "PATH=", 5) == 0)
474                     SET(didvar, DID_PATH);
475                 else if (strncmp(*ep, "TERM=", 5) == 0)
476                     SET(didvar, DID_TERM);
477                 insert_env(*ep, 0);
478             }
479         }
480     }
481     /* Provide default values for $TERM and $PATH if they are not set. */
482     if (!ISSET(didvar, DID_TERM))
483         insert_env("TERM=unknown", 0);
484     if (!ISSET(didvar, DID_PATH))
485         insert_env(format_env("PATH", _PATH_DEFPATH, VNULL), 0);
486
487 #ifdef SECURE_PATH
488     /* Replace the PATH envariable with a secure one. */
489     insert_env(format_env("PATH", SECURE_PATH, VNULL), 1);
490 #endif
491
492     /* Set $USER and $LOGNAME to target if "set_logname" is true. */
493     if (def_set_logname && runas_pw->pw_name) {
494         insert_env(format_env("LOGNAME", runas_pw->pw_name, VNULL), 1);
495         insert_env(format_env("USER", runas_pw->pw_name, VNULL), 1);
496     }
497
498     /* Set $HOME for `sudo -H'.  Only valid at PERM_FULL_RUNAS. */
499     if (ISSET(sudo_mode, MODE_RESET_HOME) && runas_pw->pw_dir)
500         insert_env(format_env("HOME", runas_pw->pw_dir, VNULL), 1);
501
502     /*
503      * Preload a noexec file?  For a list of LD_PRELOAD-alikes, see
504      * http://www.fortran-2000.com/ArnaudRecipes/sharedlib.html
505      * XXX - should prepend to original value, if any
506      */
507     if (noexec && def_noexec_file != NULL) {
508 #if defined(__darwin__) || defined(__APPLE__)
509         insert_env(format_env("DYLD_INSERT_LIBRARIES", def_noexec_file, VNULL), 1);
510         insert_env(format_env("DYLD_FORCE_FLAT_NAMESPACE", VNULL), 1);
511 #else
512 # if defined(__osf__) || defined(__sgi)
513         insert_env(format_env("_RLD_LIST", def_noexec_file, ":DEFAULT", VNULL), 1);
514 # else
515         insert_env(format_env("LD_PRELOAD", def_noexec_file, VNULL), 1);
516 # endif
517 #endif
518     }
519
520     /* Set PS1 if SUDO_PS1 is set. */
521     if (ps1)
522         insert_env(ps1, 1);
523
524     /* Add the SUDO_COMMAND envariable (cmnd + args). */
525     if (user_args)
526         insert_env(format_env("SUDO_COMMAND", user_cmnd, " ", user_args, VNULL), 1);
527     else
528         insert_env(format_env("SUDO_COMMAND", user_cmnd, VNULL), 1);
529
530     /* Add the SUDO_USER, SUDO_UID, SUDO_GID environment variables. */
531     insert_env(format_env("SUDO_USER", user_name, VNULL), 1);
532     easprintf(&cp, "SUDO_UID=%lu", (unsigned long) user_uid);
533     insert_env(cp, 1);
534     easprintf(&cp, "SUDO_GID=%lu", (unsigned long) user_gid);
535     insert_env(cp, 1);
536
537     return(new_environ);
538 }
539
540 void
541 init_envtables()
542 {
543     struct list_member *cur;
544     const char **p;
545
546     /* Fill in "env_delete" variable. */
547     for (p = initial_badenv_table; *p; p++) {
548         cur = emalloc(sizeof(struct list_member));
549         cur->value = estrdup(*p);
550         cur->next = def_env_delete;
551         def_env_delete = cur;
552     }
553
554     /* Fill in "env_check" variable. */
555     for (p = initial_checkenv_table; *p; p++) {
556         cur = emalloc(sizeof(struct list_member));
557         cur->value = estrdup(*p);
558         cur->next = def_env_check;
559         def_env_check = cur;
560     }
561 }