2 * Copyright (c) 2010, 2012 Todd C. Miller <Todd.Miller@courtesan.com>
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.
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.
19 #include <sys/types.h>
29 #endif /* STDC_HEADERS */
32 #endif /* HAVE_STRING_H */
35 #endif /* HAVE_STRINGS_H */
36 #if defined(HAVE_MALLOC_H) && !defined(STDC_HEADERS)
38 #endif /* HAVE_MALLOC_H && !STDC_HEADERS */
43 # include "compat/dlfcn.h"
47 #include "sudo_plugin.h"
49 extern char **environ; /* global environment pointer */
50 static char **priv_environ; /* private environment pointer */
53 rpl_getenv(const char *name)
55 char **ep, *val = NULL;
58 /* For BSD compatibility, treat '=' in name like end of string. */
59 while (name[namelen] != '\0' && name[namelen] != '=')
61 for (ep = environ; *ep != NULL; ep++) {
62 if (strncmp(*ep, name, namelen) == 0 && (*ep)[namelen] == '=') {
63 val = *ep + namelen + 1;
70 typedef char * (*sudo_fn_getenv_t)(const char *);
73 getenv_unhooked(const char *name)
75 #if defined(HAVE_DLOPEN) && defined(RTLD_NEXT)
78 fn = (sudo_fn_getenv_t)dlsym(RTLD_NEXT, "getenv");
81 #endif /* HAVE_DLOPEN && RTLD_NEXT */
82 return rpl_getenv(name);
86 getenv(const char *name)
90 switch (process_hooks_getenv(name, &val)) {
91 case SUDO_HOOK_RET_STOP:
93 case SUDO_HOOK_RET_ERROR:
96 return getenv_unhooked(name);
101 rpl_putenv(PUTENV_CONST char *string)
107 /* Look for existing entry. */
108 len = (strchr(string, '=') - string) + 1;
109 for (ep = environ; *ep != NULL; ep++) {
110 if (strncmp(string, *ep, len) == 0) {
111 *ep = (char *)string;
116 /* Prune out duplicate variables. */
118 while (*ep != NULL) {
119 if (strncmp(string, *ep, len) == 0) {
121 while ((*cur = *(cur + 1)) != NULL)
129 /* Append at the end if not already found. */
131 size_t env_len = (size_t)(ep - environ);
132 char **envp = erealloc3(priv_environ, env_len + 2, sizeof(char *));
133 if (environ != priv_environ)
134 memcpy(envp, environ, env_len * sizeof(char *));
135 envp[env_len++] = (char *)string;
136 envp[env_len] = NULL;
137 priv_environ = environ = envp;
142 typedef int (*sudo_fn_putenv_t)(PUTENV_CONST char *);
145 putenv_unhooked(PUTENV_CONST char *string)
147 #if defined(HAVE_DLOPEN) && defined(RTLD_NEXT)
150 fn = (sudo_fn_putenv_t)dlsym(RTLD_NEXT, "putenv");
153 #endif /* HAVE_DLOPEN && RTLD_NEXT */
154 return rpl_putenv(string);
158 putenv(PUTENV_CONST char *string)
160 switch (process_hooks_putenv((char *)string)) {
161 case SUDO_HOOK_RET_STOP:
163 case SUDO_HOOK_RET_ERROR:
166 return putenv_unhooked(string);
171 rpl_setenv(const char *var, const char *val, int overwrite)
177 if (!var || *var == '\0') {
183 * POSIX says a var name with '=' is an error but BSD
184 * just ignores the '=' and anything after it.
186 for (src = var; *src != '\0' && *src != '='; src++)
188 esize = (size_t)(src - var) + 2;
190 esize += strlen(val); /* glibc treats a NULL val as "" */
193 /* Allocate and fill in envstr. */
194 if ((envstr = malloc(esize)) == NULL)
196 for (src = var, dst = envstr; *src != '\0' && *src != '=';)
200 for (src = val; *src != '\0';)
205 if (!overwrite && getenv(var) != NULL) {
209 return rpl_putenv(envstr);
212 typedef int (*sudo_fn_setenv_t)(const char *, const char *, int);
215 setenv_unhooked(const char *var, const char *val, int overwrite)
217 #if defined(HAVE_SETENV) && defined(HAVE_DLOPEN) && defined(RTLD_NEXT)
220 fn = (sudo_fn_setenv_t)dlsym(RTLD_NEXT, "setenv");
222 return fn(var, val, overwrite);
223 #endif /* HAVE_SETENV && HAVE_DLOPEN && RTLD_NEXT */
224 return rpl_setenv(var, val, overwrite);
228 setenv(const char *var, const char *val, int overwrite)
230 switch (process_hooks_setenv(var, val, overwrite)) {
231 case SUDO_HOOK_RET_STOP:
233 case SUDO_HOOK_RET_ERROR:
236 return setenv_unhooked(var, val, overwrite);
241 rpl_unsetenv(const char *var)
246 if (var == NULL || *var == '\0' || strchr(var, '=') != NULL) {
252 while (*ep != NULL) {
253 if (strncmp(var, *ep, len) == 0 && (*ep)[len] == '=') {
254 /* Found it; shift remainder + NULL over by one. */
256 while ((*cur = *(cur + 1)) != NULL)
258 /* Keep going, could be multiple instances of the var. */
267 typedef void (*sudo_fn_unsetenv_t)(const char *);
269 typedef int (*sudo_fn_unsetenv_t)(const char *);
273 unsetenv_unhooked(const char *var)
276 #if defined(HAVE_UNSETENV) && defined(HAVE_DLOPEN) && defined(RTLD_NEXT)
277 sudo_fn_unsetenv_t fn;
279 fn = (sudo_fn_unsetenv_t)dlsym(RTLD_NEXT, "unsetenv");
281 # ifdef UNSETENV_VOID
287 #endif /* HAVE_UNSETENV && HAVE_DLOPEN && RTLD_NEXT */
289 rval = rpl_unsetenv(var);
299 unsetenv(const char *var)
303 switch (process_hooks_unsetenv(var)) {
304 case SUDO_HOOK_RET_STOP:
307 case SUDO_HOOK_RET_ERROR:
311 rval = unsetenv_unhooked(var);
314 #ifndef UNSETENV_VOID