Coverage Report

Created: 2025-06-13 06:36

/src/util-linux/lib/env.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * environ[] array cleanup code and getenv() wrappers
3
 *
4
 * No copyright is claimed.  This code is in the public domain; do with
5
 * it what you wish.
6
 */
7
#include <stdio.h>
8
#include <stdlib.h>
9
#include <string.h>
10
#ifdef HAVE_SYS_PRCTL_H
11
#include <sys/prctl.h>
12
#else
13
#define PR_GET_DUMPABLE 3
14
#endif
15
#if (!defined(HAVE_PRCTL) && defined(linux))
16
#include <sys/syscall.h>
17
#endif
18
#include <unistd.h>
19
#include <sys/types.h>
20
21
#include "env.h"
22
#include "strv.h"
23
#include "all-io.h"
24
25
#ifndef HAVE_ENVIRON_DECL
26
extern char **environ;
27
#endif
28
29
static char * const forbid[] = {
30
        "BASH_ENV=",    /* GNU creeping featurism strikes again... */
31
        "ENV=",
32
        "HOME=",
33
        "IFS=",
34
        "KRB_CONF=",
35
        "LD_",          /* anything with the LD_ prefix */
36
        "LIBPATH=",
37
        "MAIL=",
38
        "NLSPATH=",
39
        "PATH=",
40
        "SHELL=",
41
        "SHLIB_PATH=",
42
        (char *) 0
43
};
44
45
/* these are allowed, but with no slashes inside
46
   (to work around security problems in GNU gettext) */
47
static char * const noslash[] = {
48
        "LANG=",
49
        "LANGUAGE=",
50
        "LC_",          /* anything with the LC_ prefix */
51
        (char *) 0
52
};
53
54
55
struct ul_env_list {
56
  char  *name;
57
  char  *value;
58
59
  struct ul_env_list *next;
60
};
61
62
/*
63
 * Saves to the list and returns a pointer to the new head of the list.
64
 */
65
static struct ul_env_list *env_list_add( struct ul_env_list *ls0,
66
           const char *name, size_t namesz,
67
           const char *value, size_t valsz)
68
0
{
69
0
  struct ul_env_list *ls;
70
71
0
  ls = calloc(1, sizeof(struct ul_env_list) + namesz + valsz + 2);
72
0
  if (!ls)
73
0
    return ls0;
74
75
0
  ls->name = ((char *) ls) + sizeof(struct ul_env_list);
76
0
  ls->value = ls->name + namesz + 1;
77
78
0
  memcpy(ls->name, name, namesz);
79
0
  memcpy(ls->value, value, valsz);
80
81
0
  ls->next = ls0;
82
0
  return ls;
83
0
}
84
85
/*
86
 * Saves the @str (with the name=value string) to the @ls and returns a pointer
87
 * to the new head of the list.
88
 */
89
static struct ul_env_list *env_list_add_from_string(struct ul_env_list *ls,
90
                const char *str)
91
0
{
92
0
  size_t namesz = 0, valsz = 0;
93
0
  const char *val;
94
95
0
  if (!str || !*str)
96
0
    return ls;
97
98
0
  val = strchr(str, '=');
99
0
  if (!val)
100
0
    return NULL;
101
0
  namesz = val - str;
102
103
0
  val++;
104
0
  valsz = strlen(val);
105
106
0
  return env_list_add(ls, str, namesz, val, valsz);
107
0
}
108
109
/*
110
 * Saves the @name and @value to @ls0 and returns a pointer to the new head of
111
 * the list.
112
 */
113
struct ul_env_list *env_list_add_variable(
114
        struct ul_env_list *ls,
115
        const char *name, const char *value)
116
0
{
117
0
  if (!name || !*name)
118
0
    return ls;
119
120
0
  return env_list_add(ls, name, strlen(name), value, value ? strlen(value) : 0);
121
0
}
122
123
/*
124
 * Calls getenv() and adds the result to the list.
125
 */
126
struct ul_env_list *env_list_add_getenv(struct ul_env_list *ls,
127
        const char *name, const char *dflt)
128
0
{
129
0
  const char *val;
130
131
0
  if (!name)
132
0
    return ls;
133
134
0
  val = getenv(name);
135
0
  if (!val)
136
0
    val= dflt;
137
0
  if (val)
138
0
    ls = env_list_add_variable(ls, name, val);
139
0
  return ls;
140
0
}
141
142
/*
143
 * Calls getenv() for each name (comma-separated) in @str and adds the results
144
 * to the list.
145
 */
146
struct ul_env_list *env_list_add_getenvs(struct ul_env_list *ls, const char *str)
147
0
{
148
0
  char **all, **name;
149
150
0
  if (!str)
151
0
    return ls;
152
153
0
  all = strv_split(str, ",");
154
0
  if (!all)
155
0
    return ls;
156
157
0
  STRV_FOREACH(name, all)
158
0
    ls = env_list_add_getenv(ls, *name, NULL);
159
160
0
  strv_free(all);
161
0
  return ls;
162
0
}
163
164
/*
165
 * Use env_list_from_fd() to read environment from @fd.
166
 *
167
 * @fd must be /proc/<pid>/environ file.
168
*/
169
struct ul_env_list *env_list_from_fd(int fd)
170
0
{
171
0
  char *buf = NULL, *p;
172
0
  ssize_t rc = 0;
173
0
  struct ul_env_list *ls = NULL;
174
175
0
  errno = 0;
176
0
  if ((rc = read_all_alloc(fd, &buf)) < 1)
177
0
    return NULL;
178
0
  buf[rc] = '\0';
179
0
  p = buf;
180
181
0
  while (rc > 0) {
182
0
    ls = env_list_add_from_string(ls, p);
183
0
    p += strlen(p) + 1;
184
0
    rc -= strlen(p) + 1;
185
0
  }
186
187
0
  free(buf);
188
0
  return ls;
189
0
}
190
191
/*
192
 * Use setenv() for all stuff in @ls.
193
 */
194
int env_list_setenv(struct ul_env_list *ls, int overwrite)
195
0
{
196
0
  int rc = 0;
197
198
0
  while (ls && rc == 0) {
199
0
    if (ls->name && ls->value)
200
0
      rc = setenv(ls->name, ls->value, overwrite);
201
0
    ls = ls->next;
202
0
  }
203
0
  return rc;
204
0
}
205
206
void env_list_free(struct ul_env_list *ls)
207
0
{
208
0
  while (ls) {
209
0
    struct ul_env_list *x = ls;
210
0
    ls = ls->next;
211
0
    free(x);
212
0
  }
213
0
}
214
215
/*
216
 * Removes unwanted variables from environ[]. If @org is not NULL than stores
217
 * unwnated variables to the list.
218
 */
219
void __sanitize_env(struct ul_env_list **org)
220
0
{
221
0
        char **envp = environ;
222
0
        char * const *bad;
223
0
        char **cur;
224
0
        int last = 0;
225
226
0
        for (cur = envp; *cur; cur++)
227
0
                last++;
228
229
0
        for (cur = envp; *cur; cur++) {
230
0
                for (bad = forbid; *bad; bad++) {
231
0
                        if (strncmp(*cur, *bad, strlen(*bad)) == 0) {
232
0
        if (org)
233
0
          *org = env_list_add_from_string(*org, *cur);
234
0
                                last = ul_remove_entry(envp, cur - envp, last);
235
0
                                cur--;
236
0
                                break;
237
0
                        }
238
0
                }
239
0
        }
240
241
0
        for (cur = envp; *cur; cur++) {
242
0
                for (bad = noslash; *bad; bad++) {
243
0
                        if (strncmp(*cur, *bad, strlen(*bad)) != 0)
244
0
                                continue;
245
0
                        if (!strchr(*cur, '/'))
246
0
                                continue;  /* OK */
247
0
      if (org)
248
0
        *org = env_list_add_from_string(*org, *cur);
249
0
                        last = ul_remove_entry(envp, cur - envp, last);
250
0
                        cur--;
251
0
                        break;
252
0
                }
253
0
        }
254
0
}
255
256
void sanitize_env(void)
257
0
{
258
0
  __sanitize_env(NULL);
259
0
}
260
261
char *safe_getenv(const char *arg)
262
0
{
263
0
  if (is_privileged_execution())
264
0
    return NULL;
265
0
#ifdef HAVE_PRCTL
266
0
  if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 0)
267
0
    return NULL;
268
#else
269
#if (defined(linux) && defined(SYS_prctl))
270
  if (syscall(SYS_prctl, PR_GET_DUMPABLE, 0, 0, 0, 0) == 0)
271
    return NULL;
272
#endif
273
#endif
274
0
#ifdef HAVE_SECURE_GETENV
275
0
return secure_getenv(arg);
276
#elif HAVE___SECURE_GETENV
277
  return __secure_getenv(arg);
278
#else
279
  return getenv(arg);
280
#endif
281
0
}
282
283
#ifdef TEST_PROGRAM
284
int main(void)
285
{
286
  char *const *bad;
287
  char copy[32];
288
  char *p;
289
  int retval = EXIT_SUCCESS;
290
  struct ul_env_list *removed = NULL;
291
292
  /* define env. */
293
  for (bad = forbid; *bad; bad++) {
294
    strcpy(copy, *bad);
295
    p = strchr(copy, '=');
296
    if (p)
297
      *p = '\0';
298
    setenv(copy, "abc", 1);
299
  }
300
301
  /* removed */
302
  __sanitize_env(&removed);
303
304
  /* check removal */
305
  for (bad = forbid; *bad; bad++) {
306
    strcpy(copy, *bad);
307
    p = strchr(copy, '=');
308
    if (p)
309
      *p = '\0';
310
    p = getenv(copy);
311
    if (p) {
312
      warnx("%s was not removed", copy);
313
      retval = EXIT_FAILURE;
314
    }
315
  }
316
317
  /* restore removed */
318
  env_list_setenv(removed, 0);
319
320
  /* check restore */
321
  for (bad = forbid; *bad; bad++) {
322
    strcpy(copy, *bad);
323
    p = strchr(copy, '=');
324
    if (p)
325
      *p = '\0';
326
    p = getenv(copy);
327
    if (!p) {
328
      warnx("%s was not restored", copy);
329
      retval = EXIT_FAILURE;
330
    }
331
  }
332
333
  env_list_free(removed);
334
335
  return retval;
336
}
337
#endif