Coverage Report

Created: 2024-05-21 06:33

/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 "all-io.h"
23
24
#ifndef HAVE_ENVIRON_DECL
25
extern char **environ;
26
#endif
27
28
static char * const forbid[] = {
29
        "BASH_ENV=",    /* GNU creeping featurism strikes again... */
30
        "ENV=",
31
        "HOME=",
32
        "IFS=",
33
        "KRB_CONF=",
34
        "LD_",          /* anything with the LD_ prefix */
35
        "LIBPATH=",
36
        "MAIL=",
37
        "NLSPATH=",
38
        "PATH=",
39
        "SHELL=",
40
        "SHLIB_PATH=",
41
        (char *) 0
42
};
43
44
/* these are allowed, but with no slashes inside
45
   (to work around security problems in GNU gettext) */
46
static char * const noslash[] = {
47
        "LANG=",
48
        "LANGUAGE=",
49
        "LC_",          /* anything with the LC_ prefix */
50
        (char *) 0
51
};
52
53
54
struct ul_env_list {
55
  char *env;
56
  struct ul_env_list *next;
57
};
58
59
/*
60
 * Saves @name env.variable to @ls, returns pointer to the new head of the list.
61
 */
62
static struct ul_env_list *env_list_add(struct ul_env_list *ls0, const char *str)
63
0
{
64
0
  struct ul_env_list *ls;
65
0
  char *p;
66
0
  size_t sz = 0;
67
68
0
  if (!str || !*str)
69
0
    return ls0;
70
71
0
  sz = strlen(str) + 1;
72
0
  p = malloc(sizeof(struct ul_env_list) + sz);
73
0
  if (!p)
74
0
    return ls0;
75
76
0
  ls = (struct ul_env_list *) p;
77
0
  p += sizeof(struct ul_env_list);
78
0
  memcpy(p, str, sz);
79
0
  ls->env = p;
80
81
0
  ls->next = ls0;
82
0
  return ls;
83
0
}
84
85
/*
86
 * Use env_from_fd() to read environment from @fd.
87
 *
88
 * @fd must be /proc/<pid>/environ file.
89
*/
90
struct ul_env_list *env_from_fd(int fd)
91
0
{
92
0
  char *buf = NULL, *p;
93
0
  ssize_t rc = 0;
94
0
  struct ul_env_list *ls = NULL;
95
96
0
  if ((rc = read_all_alloc(fd, &buf)) < 1)
97
0
    return NULL;
98
0
  buf[rc] = '\0';
99
0
  p = buf;
100
101
0
  while (rc > 0) {
102
0
    ls = env_list_add(ls, p);
103
0
    p += strlen(p) + 1;
104
0
    rc -= strlen(p) + 1;
105
0
  }
106
107
0
  free(buf);
108
0
  return ls;
109
0
}
110
111
/*
112
 * Use setenv() for all stuff in @ls.
113
 *
114
 * It would be possible to use putenv(), but we want to keep @ls free()-able.
115
 */
116
int env_list_setenv(struct ul_env_list *ls)
117
0
{
118
0
  int rc = 0;
119
120
0
  while (ls && rc == 0) {
121
0
    if (ls->env) {
122
0
      char *val = strchr(ls->env, '=');
123
0
      if (!val)
124
0
        continue;
125
0
      *val = '\0';
126
0
      rc = setenv(ls->env, val + 1, 0);
127
0
      *val = '=';
128
0
    }
129
0
    ls = ls->next;
130
0
  }
131
0
  return rc;
132
0
}
133
134
void env_list_free(struct ul_env_list *ls)
135
0
{
136
0
  while (ls) {
137
0
    struct ul_env_list *x = ls;
138
0
    ls = ls->next;
139
0
    free(x);
140
0
  }
141
0
}
142
143
/*
144
 * Removes unwanted variables from environ[]. If @org is not NULL than stores
145
 * unwnated variables to the list.
146
 */
147
void __sanitize_env(struct ul_env_list **org)
148
0
{
149
0
        char **envp = environ;
150
0
        char * const *bad;
151
0
        char **cur;
152
0
        int last = 0;
153
154
0
        for (cur = envp; *cur; cur++)
155
0
                last++;
156
157
0
        for (cur = envp; *cur; cur++) {
158
0
                for (bad = forbid; *bad; bad++) {
159
0
                        if (strncmp(*cur, *bad, strlen(*bad)) == 0) {
160
0
        if (org)
161
0
          *org = env_list_add(*org, *cur);
162
0
                                last = remove_entry(envp, cur - envp, last);
163
0
                                cur--;
164
0
                                break;
165
0
                        }
166
0
                }
167
0
        }
168
169
0
        for (cur = envp; *cur; cur++) {
170
0
                for (bad = noslash; *bad; bad++) {
171
0
                        if (strncmp(*cur, *bad, strlen(*bad)) != 0)
172
0
                                continue;
173
0
                        if (!strchr(*cur, '/'))
174
0
                                continue;  /* OK */
175
0
      if (org)
176
0
        *org = env_list_add(*org, *cur);
177
0
                        last = remove_entry(envp, cur - envp, last);
178
0
                        cur--;
179
0
                        break;
180
0
                }
181
0
        }
182
0
}
183
184
void sanitize_env(void)
185
0
{
186
0
  __sanitize_env(NULL);
187
0
}
188
189
char *safe_getenv(const char *arg)
190
0
{
191
0
  if ((getuid() != geteuid()) || (getgid() != getegid()))
192
0
    return NULL;
193
0
#ifdef HAVE_PRCTL
194
0
  if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 0)
195
0
    return NULL;
196
#else
197
#if (defined(linux) && defined(SYS_prctl))
198
  if (syscall(SYS_prctl, PR_GET_DUMPABLE, 0, 0, 0, 0) == 0)
199
    return NULL;
200
#endif
201
#endif
202
0
#ifdef HAVE_SECURE_GETENV
203
0
return secure_getenv(arg);
204
#elif HAVE___SECURE_GETENV
205
  return __secure_getenv(arg);
206
#else
207
  return getenv(arg);
208
#endif
209
0
}
210
211
#ifdef TEST_PROGRAM
212
int main(void)
213
{
214
  char *const *bad;
215
  char copy[32];
216
  char *p;
217
  int retval = EXIT_SUCCESS;
218
  struct ul_env_list *removed = NULL;
219
220
  for (bad = forbid; *bad; bad++) {
221
    strcpy(copy, *bad);
222
    p = strchr(copy, '=');
223
    if (p)
224
      *p = '\0';
225
    setenv(copy, copy, 1);
226
  }
227
228
  /* removed */
229
  __sanitize_env(&removed);
230
231
  /* check removal */
232
  for (bad = forbid; *bad; bad++) {
233
    strcpy(copy, *bad);
234
    p = strchr(copy, '=');
235
    if (p)
236
      *p = '\0';
237
    p = getenv(copy);
238
    if (p) {
239
      warnx("%s was not removed", copy);
240
      retval = EXIT_FAILURE;
241
    }
242
  }
243
244
  /* restore removed */
245
  env_list_setenv(removed);
246
247
  /* check restore */
248
  for (bad = forbid; *bad; bad++) {
249
    strcpy(copy, *bad);
250
    p = strchr(copy, '=');
251
    if (p)
252
      *p = '\0';
253
    p = getenv(copy);
254
    if (!p) {
255
      warnx("%s was not restored", copy);
256
      retval = EXIT_FAILURE;
257
    }
258
  }
259
260
  env_list_free(removed);
261
262
  return retval;
263
}
264
#endif