Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sudo-project
GitHub Repository: sudo-project/sudo
Path: blob/main/src/env_hooks.c
1532 views
1
/*
2
* SPDX-License-Identifier: ISC
3
*
4
* Copyright (c) 2010, 2012-2016 Todd C. Miller <[email protected]>
5
*
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.
9
*
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.
17
*/
18
19
#include <config.h>
20
21
#include <stdlib.h>
22
#include <string.h>
23
#include <errno.h>
24
25
#include <sudo.h>
26
#include <sudo_plugin.h>
27
#include <sudo_dso.h>
28
29
extern char **environ; /* global environment pointer */
30
static char **priv_environ; /* private environment pointer */
31
32
/*
33
* NOTE: we don't use dlsym() to find the libc getenv()
34
* since this may allocate memory on some systems (glibc)
35
* which leads to a hang if malloc() calls getenv (jemalloc).
36
*/
37
char *
38
getenv_unhooked(const char *name)
39
{
40
char **ep, *val = NULL;
41
size_t namelen = 0;
42
43
/* For BSD compatibility, treat '=' in name like end of string. */
44
while (name[namelen] != '\0' && name[namelen] != '=')
45
namelen++;
46
for (ep = environ; *ep != NULL; ep++) {
47
if (strncmp(*ep, name, namelen) == 0 && (*ep)[namelen] == '=') {
48
val = *ep + namelen + 1;
49
break;
50
}
51
}
52
return val;
53
}
54
55
sudo_dso_public char *getenv(const char *name);
56
57
char *
58
getenv(const char *name)
59
{
60
char *val = NULL;
61
62
switch (process_hooks_getenv(name, &val)) {
63
case SUDO_HOOK_RET_STOP:
64
return val;
65
case SUDO_HOOK_RET_ERROR:
66
return NULL;
67
default:
68
return getenv_unhooked(name);
69
}
70
}
71
72
static int
73
rpl_putenv(PUTENV_CONST char *string)
74
{
75
char **ep;
76
const char *equal;
77
size_t len;
78
bool found = false;
79
80
/* Some putenv(3) implementations check for NULL. */
81
if (string == NULL) {
82
errno = EINVAL;
83
return -1;
84
}
85
86
/* The string must contain a '=' char but not start with one. */
87
equal = strchr(string, '=');
88
if (equal == NULL || equal == string) {
89
errno = EINVAL;
90
return -1;
91
}
92
93
/* Look for existing entry. */
94
len = (size_t)(equal - string) + 1;
95
for (ep = environ; *ep != NULL; ep++) {
96
if (strncmp(string, *ep, len) == 0) {
97
*ep = (char *)string;
98
found = true;
99
break;
100
}
101
}
102
/* Prune out duplicate variables. */
103
if (found) {
104
while (*ep != NULL) {
105
if (strncmp(string, *ep, len) == 0) {
106
char **cur = ep;
107
while ((*cur = *(cur + 1)) != NULL)
108
cur++;
109
} else {
110
ep++;
111
}
112
}
113
}
114
115
/* Append at the end if not already found. */
116
if (!found) {
117
size_t env_len = (size_t)(ep - environ);
118
char **envp = reallocarray(priv_environ, env_len + 2, sizeof(char *));
119
if (envp == NULL)
120
return -1;
121
if (environ != priv_environ)
122
memcpy(envp, environ, env_len * sizeof(char *));
123
envp[env_len++] = (char *)string;
124
envp[env_len] = NULL;
125
priv_environ = environ = envp;
126
}
127
return 0;
128
}
129
130
typedef int (*sudo_fn_putenv_t)(PUTENV_CONST char *);
131
132
static int
133
putenv_unhooked(PUTENV_CONST char *string)
134
{
135
sudo_fn_putenv_t fn;
136
137
fn = (sudo_fn_putenv_t)sudo_dso_findsym(SUDO_DSO_NEXT, "putenv");
138
if (fn != NULL)
139
return fn(string);
140
return rpl_putenv(string);
141
}
142
143
sudo_dso_public int putenv(PUTENV_CONST char *string);
144
145
int
146
putenv(PUTENV_CONST char *string)
147
{
148
switch (process_hooks_putenv((char *)string)) {
149
case SUDO_HOOK_RET_STOP:
150
return 0;
151
case SUDO_HOOK_RET_ERROR:
152
return -1;
153
default:
154
return putenv_unhooked(string);
155
}
156
}
157
158
static int
159
rpl_setenv(const char *var, const char *val, int overwrite)
160
{
161
char *envstr, *dst;
162
const char *src;
163
size_t esize;
164
165
if (!var || *var == '\0') {
166
errno = EINVAL;
167
return -1;
168
}
169
170
/*
171
* POSIX says a var name with '=' is an error but BSD
172
* just ignores the '=' and anything after it.
173
*/
174
for (src = var; *src != '\0' && *src != '='; src++)
175
continue;
176
esize = (size_t)(src - var) + 2;
177
if (val) {
178
esize += strlen(val); /* glibc treats a NULL val as "" */
179
}
180
181
/* Allocate and fill in envstr. */
182
if ((envstr = malloc(esize)) == NULL)
183
return -1;
184
for (src = var, dst = envstr; *src != '\0' && *src != '=';)
185
*dst++ = *src++;
186
*dst++ = '=';
187
if (val) {
188
for (src = val; *src != '\0';)
189
*dst++ = *src++;
190
}
191
*dst = '\0';
192
193
if (!overwrite && getenv(var) != NULL) {
194
free(envstr);
195
return 0;
196
}
197
if (rpl_putenv(envstr) == -1) {
198
free(envstr);
199
return -1;
200
}
201
return 0;
202
}
203
204
typedef int (*sudo_fn_setenv_t)(const char *, const char *, int);
205
206
static int
207
setenv_unhooked(const char *var, const char *val, int overwrite)
208
{
209
sudo_fn_setenv_t fn;
210
211
fn = (sudo_fn_setenv_t)sudo_dso_findsym(SUDO_DSO_NEXT, "setenv");
212
if (fn != NULL)
213
return fn(var, val, overwrite);
214
return rpl_setenv(var, val, overwrite);
215
}
216
217
sudo_dso_public int setenv(const char *var, const char *val, int overwrite);
218
219
int
220
setenv(const char *var, const char *val, int overwrite)
221
{
222
switch (process_hooks_setenv(var, val, overwrite)) {
223
case SUDO_HOOK_RET_STOP:
224
return 0;
225
case SUDO_HOOK_RET_ERROR:
226
return -1;
227
default:
228
return setenv_unhooked(var, val, overwrite);
229
}
230
}
231
232
static int
233
rpl_unsetenv(const char *var)
234
{
235
char **ep = environ;
236
size_t len;
237
238
if (var == NULL || *var == '\0' || strchr(var, '=') != NULL) {
239
errno = EINVAL;
240
return -1;
241
}
242
243
len = strlen(var);
244
while (*ep != NULL) {
245
if (strncmp(var, *ep, len) == 0 && (*ep)[len] == '=') {
246
/* Found it; shift remainder + NULL over by one. */
247
char **cur = ep;
248
while ((*cur = *(cur + 1)) != NULL)
249
cur++;
250
/* Keep going, could be multiple instances of the var. */
251
} else {
252
ep++;
253
}
254
}
255
return 0;
256
}
257
258
#ifdef UNSETENV_VOID
259
typedef void (*sudo_fn_unsetenv_t)(const char *);
260
#else
261
typedef int (*sudo_fn_unsetenv_t)(const char *);
262
#endif
263
264
static int
265
unsetenv_unhooked(const char *var)
266
{
267
int ret = 0;
268
sudo_fn_unsetenv_t fn;
269
270
fn = (sudo_fn_unsetenv_t)sudo_dso_findsym(SUDO_DSO_NEXT, "unsetenv");
271
if (fn != NULL) {
272
# ifdef UNSETENV_VOID
273
fn(var);
274
# else
275
ret = fn(var);
276
# endif
277
} else {
278
ret = rpl_unsetenv(var);
279
}
280
return ret;
281
}
282
283
#ifdef UNSETENV_VOID
284
# define UNSETENV_RTYPE void
285
#else
286
# define UNSETENV_RTYPE int
287
#endif
288
289
sudo_dso_public UNSETENV_RTYPE unsetenv(const char *var);
290
291
UNSETENV_RTYPE
292
unsetenv(const char *var)
293
{
294
int ret;
295
296
switch (process_hooks_unsetenv(var)) {
297
case SUDO_HOOK_RET_STOP:
298
ret = 0;
299
break;
300
case SUDO_HOOK_RET_ERROR:
301
ret = -1;
302
break;
303
default:
304
ret = unsetenv_unhooked(var);
305
break;
306
}
307
#ifndef UNSETENV_VOID
308
return ret;
309
#endif
310
}
311
312