]>
Commit | Line | Data |
---|---|---|
1 | /* Copyright (C) 2012-2019 Free Software Foundation, Inc. | |
2 | This file is part of the GNU C Library. | |
3 | ||
4 | The GNU C Library is free software; you can redistribute it and/or | |
5 | modify it under the terms of the GNU Lesser General Public | |
6 | License as published by the Free Software Foundation; either | |
7 | version 2.1 of the License, or (at your option) any later version. | |
8 | ||
9 | The GNU C Library is distributed in the hope that it will be useful, | |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
12 | Lesser General Public License for more details. | |
13 | ||
14 | You should have received a copy of the GNU Lesser General Public | |
15 | License along with the GNU C Library; if not, see | |
16 | <https://www.gnu.org/licenses/>. */ | |
17 | ||
18 | /* Verify that tunables correctly filter out unsafe environment variables like | |
19 | MALLOC_CHECK_ and MALLOC_MMAP_THRESHOLD_ but also retain | |
20 | MALLOC_MMAP_THRESHOLD_ in an unprivileged child. */ | |
21 | ||
22 | #include <errno.h> | |
23 | #include <fcntl.h> | |
24 | #include <stdlib.h> | |
25 | #include <stdint.h> | |
26 | #include <stdio.h> | |
27 | #include <string.h> | |
28 | #include <sys/stat.h> | |
29 | #include <sys/wait.h> | |
30 | #include <unistd.h> | |
31 | ||
32 | #include <support/support.h> | |
33 | #include <support/test-driver.h> | |
34 | ||
35 | static char SETGID_CHILD[] = "setgid-child"; | |
36 | #define CHILD_STATUS 42 | |
37 | ||
38 | /* Return a GID which is not our current GID, but is present in the | |
39 | supplementary group list. */ | |
40 | static gid_t | |
41 | choose_gid (void) | |
42 | { | |
43 | const int count = 64; | |
44 | gid_t groups[count]; | |
45 | int ret = getgroups (count, groups); | |
46 | if (ret < 0) | |
47 | { | |
48 | printf ("getgroups: %m\n"); | |
49 | exit (1); | |
50 | } | |
51 | gid_t current = getgid (); | |
52 | for (int i = 0; i < ret; ++i) | |
53 | { | |
54 | if (groups[i] != current) | |
55 | return groups[i]; | |
56 | } | |
57 | return 0; | |
58 | } | |
59 | ||
60 | /* Spawn and execute a program and verify that it returns the CHILD_STATUS. */ | |
61 | static pid_t | |
62 | do_execve (char **args) | |
63 | { | |
64 | pid_t kid = vfork (); | |
65 | ||
66 | if (kid < 0) | |
67 | { | |
68 | printf ("vfork: %m\n"); | |
69 | return -1; | |
70 | } | |
71 | ||
72 | if (kid == 0) | |
73 | { | |
74 | /* Child process. */ | |
75 | execve (args[0], args, environ); | |
76 | _exit (-errno); | |
77 | } | |
78 | ||
79 | if (kid < 0) | |
80 | return 1; | |
81 | ||
82 | int status; | |
83 | ||
84 | if (waitpid (kid, &status, 0) < 0) | |
85 | { | |
86 | printf ("waitpid: %m\n"); | |
87 | return 1; | |
88 | } | |
89 | ||
90 | if (WEXITSTATUS (status) == EXIT_UNSUPPORTED) | |
91 | return EXIT_UNSUPPORTED; | |
92 | ||
93 | if (!WIFEXITED (status) || WEXITSTATUS (status) != CHILD_STATUS) | |
94 | { | |
95 | printf ("Unexpected exit status %d from child process\n", | |
96 | WEXITSTATUS (status)); | |
97 | return 1; | |
98 | } | |
99 | return 0; | |
100 | } | |
101 | ||
102 | /* Copies the executable into a restricted directory, so that we can | |
103 | safely make it SGID with the TARGET group ID. Then runs the | |
104 | executable. */ | |
105 | static int | |
106 | run_executable_sgid (gid_t target) | |
107 | { | |
108 | char *dirname = xasprintf ("%s/tst-tunables-setuid.%jd", | |
109 | test_dir, (intmax_t) getpid ()); | |
110 | char *execname = xasprintf ("%s/bin", dirname); | |
111 | int infd = -1; | |
112 | int outfd = -1; | |
113 | int ret = 0; | |
114 | if (mkdir (dirname, 0700) < 0) | |
115 | { | |
116 | printf ("mkdir: %m\n"); | |
117 | goto err; | |
118 | } | |
119 | infd = open ("/proc/self/exe", O_RDONLY); | |
120 | if (infd < 0) | |
121 | { | |
122 | printf ("open (/proc/self/exe): %m\n"); | |
123 | goto err; | |
124 | } | |
125 | outfd = open (execname, O_WRONLY | O_CREAT | O_EXCL, 0700); | |
126 | if (outfd < 0) | |
127 | { | |
128 | printf ("open (%s): %m\n", execname); | |
129 | goto err; | |
130 | } | |
131 | char buf[4096]; | |
132 | for (;;) | |
133 | { | |
134 | ssize_t rdcount = read (infd, buf, sizeof (buf)); | |
135 | if (rdcount < 0) | |
136 | { | |
137 | printf ("read: %m\n"); | |
138 | goto err; | |
139 | } | |
140 | if (rdcount == 0) | |
141 | break; | |
142 | char *p = buf; | |
143 | char *end = buf + rdcount; | |
144 | while (p != end) | |
145 | { | |
146 | ssize_t wrcount = write (outfd, buf, end - p); | |
147 | if (wrcount == 0) | |
148 | errno = ENOSPC; | |
149 | if (wrcount <= 0) | |
150 | { | |
151 | printf ("write: %m\n"); | |
152 | goto err; | |
153 | } | |
154 | p += wrcount; | |
155 | } | |
156 | } | |
157 | if (fchown (outfd, getuid (), target) < 0) | |
158 | { | |
159 | printf ("fchown (%s): %m\n", execname); | |
160 | goto err; | |
161 | } | |
162 | if (fchmod (outfd, 02750) < 0) | |
163 | { | |
164 | printf ("fchmod (%s): %m\n", execname); | |
165 | goto err; | |
166 | } | |
167 | if (close (outfd) < 0) | |
168 | { | |
169 | printf ("close (outfd): %m\n"); | |
170 | goto err; | |
171 | } | |
172 | if (close (infd) < 0) | |
173 | { | |
174 | printf ("close (infd): %m\n"); | |
175 | goto err; | |
176 | } | |
177 | ||
178 | char *args[] = {execname, SETGID_CHILD, NULL}; | |
179 | ||
180 | ret = do_execve (args); | |
181 | ||
182 | err: | |
183 | if (outfd >= 0) | |
184 | close (outfd); | |
185 | if (infd >= 0) | |
186 | close (infd); | |
187 | if (execname) | |
188 | { | |
189 | unlink (execname); | |
190 | free (execname); | |
191 | } | |
192 | if (dirname) | |
193 | { | |
194 | rmdir (dirname); | |
195 | free (dirname); | |
196 | } | |
197 | return ret; | |
198 | } | |
199 | ||
200 | #ifndef test_child | |
201 | static int | |
202 | test_child (void) | |
203 | { | |
204 | if (getenv ("MALLOC_CHECK_") != NULL) | |
205 | { | |
206 | printf ("MALLOC_CHECK_ is still set\n"); | |
207 | return 1; | |
208 | } | |
209 | ||
210 | if (getenv ("MALLOC_MMAP_THRESHOLD_") == NULL) | |
211 | { | |
212 | printf ("MALLOC_MMAP_THRESHOLD_ lost\n"); | |
213 | return 1; | |
214 | } | |
215 | ||
216 | if (getenv ("LD_HWCAP_MASK") != NULL) | |
217 | { | |
218 | printf ("LD_HWCAP_MASK still set\n"); | |
219 | return 1; | |
220 | } | |
221 | ||
222 | return 0; | |
223 | } | |
224 | #endif | |
225 | ||
226 | #ifndef test_parent | |
227 | static int | |
228 | test_parent (void) | |
229 | { | |
230 | if (getenv ("MALLOC_CHECK_") == NULL) | |
231 | { | |
232 | printf ("MALLOC_CHECK_ lost\n"); | |
233 | return 1; | |
234 | } | |
235 | ||
236 | if (getenv ("MALLOC_MMAP_THRESHOLD_") == NULL) | |
237 | { | |
238 | printf ("MALLOC_MMAP_THRESHOLD_ lost\n"); | |
239 | return 1; | |
240 | } | |
241 | ||
242 | if (getenv ("LD_HWCAP_MASK") == NULL) | |
243 | { | |
244 | printf ("LD_HWCAP_MASK lost\n"); | |
245 | return 1; | |
246 | } | |
247 | ||
248 | return 0; | |
249 | } | |
250 | #endif | |
251 | ||
252 | static int | |
253 | do_test (int argc, char **argv) | |
254 | { | |
255 | /* Setgid child process. */ | |
256 | if (argc == 2 && strcmp (argv[1], SETGID_CHILD) == 0) | |
257 | { | |
258 | if (getgid () == getegid ()) | |
259 | { | |
260 | /* This can happen if the file system is mounted nosuid. */ | |
261 | fprintf (stderr, "SGID failed: GID and EGID match (%jd)\n", | |
262 | (intmax_t) getgid ()); | |
263 | exit (EXIT_UNSUPPORTED); | |
264 | } | |
265 | ||
266 | int ret = test_child (); | |
267 | ||
268 | if (ret != 0) | |
269 | exit (1); | |
270 | ||
271 | exit (CHILD_STATUS); | |
272 | } | |
273 | else | |
274 | { | |
275 | if (test_parent () != 0) | |
276 | exit (1); | |
277 | ||
278 | /* Try running a setgid program. */ | |
279 | gid_t target = choose_gid (); | |
280 | if (target == 0) | |
281 | { | |
282 | fprintf (stderr, | |
283 | "Could not find a suitable GID for user %jd, skipping test\n", | |
284 | (intmax_t) getuid ()); | |
285 | exit (0); | |
286 | } | |
287 | ||
288 | return run_executable_sgid (target); | |
289 | } | |
290 | ||
291 | /* Something went wrong and our argv was corrupted. */ | |
292 | _exit (1); | |
293 | } | |
294 | ||
295 | #define TEST_FUNCTION_ARGV do_test | |
296 | #include <support/test-driver.c> |