2 * SPDX-License-Identifier: ISC
4 * Copyright (c) 2010, 2012-2016 Todd C. Miller <Todd.Miller@sudo.ws>
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 * This is an open source non-commercial project. Dear PVS-Studio, please check it.
21 * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
26 #include <sys/types.h>
32 #endif /* HAVE_STRING_H */
35 #endif /* HAVE_STRINGS_H */
39 #include "sudo_plugin.h"
42 extern char **environ; /* global environment pointer */
43 static char **priv_environ; /* private environment pointer */
46 * NOTE: we don't use dlsym() to find the libc getenv()
47 * since this may allocate memory on some systems (glibc)
48 * which leads to a hang if malloc() calls getenv (jemalloc).
51 getenv_unhooked(const char *name)
53 char **ep, *val = NULL;
56 /* For BSD compatibility, treat '=' in name like end of string. */
57 while (name[namelen] != '\0' && name[namelen] != '=')
59 for (ep = environ; *ep != NULL; ep++) {
60 if (strncmp(*ep, name, namelen) == 0 && (*ep)[namelen] == '=') {
61 val = *ep + namelen + 1;
69 getenv(const char *name)
73 switch (process_hooks_getenv(name, &val)) {
74 case SUDO_HOOK_RET_STOP:
76 case SUDO_HOOK_RET_ERROR:
79 return getenv_unhooked(name);
84 rpl_putenv(PUTENV_CONST char *string)
90 /* Look for existing entry. */
91 len = (strchr(string, '=') - string) + 1;
92 for (ep = environ; *ep != NULL; ep++) {
93 if (strncmp(string, *ep, len) == 0) {
99 /* Prune out duplicate variables. */
101 while (*ep != NULL) {
102 if (strncmp(string, *ep, len) == 0) {
104 while ((*cur = *(cur + 1)) != NULL)
112 /* Append at the end if not already found. */
114 size_t env_len = (size_t)(ep - environ);
115 char **envp = reallocarray(priv_environ, env_len + 2, sizeof(char *));
118 if (environ != priv_environ)
119 memcpy(envp, environ, env_len * sizeof(char *));
120 envp[env_len++] = (char *)string;
121 envp[env_len] = NULL;
122 priv_environ = environ = envp;
127 typedef int (*sudo_fn_putenv_t)(PUTENV_CONST char *);
130 putenv_unhooked(PUTENV_CONST char *string)
134 fn = (sudo_fn_putenv_t)sudo_dso_findsym(SUDO_DSO_NEXT, "putenv");
137 return rpl_putenv(string);
141 putenv(PUTENV_CONST char *string)
143 switch (process_hooks_putenv((char *)string)) {
144 case SUDO_HOOK_RET_STOP:
146 case SUDO_HOOK_RET_ERROR:
149 return putenv_unhooked(string);
154 rpl_setenv(const char *var, const char *val, int overwrite)
160 if (!var || *var == '\0') {
166 * POSIX says a var name with '=' is an error but BSD
167 * just ignores the '=' and anything after it.
169 for (src = var; *src != '\0' && *src != '='; src++)
171 esize = (size_t)(src - var) + 2;
173 esize += strlen(val); /* glibc treats a NULL val as "" */
176 /* Allocate and fill in envstr. */
177 if ((envstr = malloc(esize)) == NULL)
179 for (src = var, dst = envstr; *src != '\0' && *src != '=';)
183 for (src = val; *src != '\0';)
188 if (!overwrite && getenv(var) != NULL) {
192 if (rpl_putenv(envstr) == -1) {
199 typedef int (*sudo_fn_setenv_t)(const char *, const char *, int);
202 setenv_unhooked(const char *var, const char *val, int overwrite)
206 fn = (sudo_fn_setenv_t)sudo_dso_findsym(SUDO_DSO_NEXT, "setenv");
208 return fn(var, val, overwrite);
209 return rpl_setenv(var, val, overwrite);
213 setenv(const char *var, const char *val, int overwrite)
215 switch (process_hooks_setenv(var, val, overwrite)) {
216 case SUDO_HOOK_RET_STOP:
218 case SUDO_HOOK_RET_ERROR:
221 return setenv_unhooked(var, val, overwrite);
226 rpl_unsetenv(const char *var)
231 if (var == NULL || *var == '\0' || strchr(var, '=') != NULL) {
237 while (*ep != NULL) {
238 if (strncmp(var, *ep, len) == 0 && (*ep)[len] == '=') {
239 /* Found it; shift remainder + NULL over by one. */
241 while ((*cur = *(cur + 1)) != NULL)
243 /* Keep going, could be multiple instances of the var. */
252 typedef void (*sudo_fn_unsetenv_t)(const char *);
254 typedef int (*sudo_fn_unsetenv_t)(const char *);
258 unsetenv_unhooked(const char *var)
261 sudo_fn_unsetenv_t fn;
263 fn = (sudo_fn_unsetenv_t)sudo_dso_findsym(SUDO_DSO_NEXT, "unsetenv");
265 # ifdef UNSETENV_VOID
271 ret = rpl_unsetenv(var);
281 unsetenv(const char *var)
285 switch (process_hooks_unsetenv(var)) {
286 case SUDO_HOOK_RET_STOP:
289 case SUDO_HOOK_RET_ERROR:
293 ret = unsetenv_unhooked(var);
296 #ifndef UNSETENV_VOID