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