Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/testing/selftests/capabilities/test_execve.c
26285 views
1
// SPDX-License-Identifier: GPL-2.0
2
#define _GNU_SOURCE
3
4
#include <cap-ng.h>
5
#include <linux/capability.h>
6
#include <stdbool.h>
7
#include <string.h>
8
#include <stdio.h>
9
#include <fcntl.h>
10
#include <errno.h>
11
#include <stdarg.h>
12
#include <sched.h>
13
#include <sys/mount.h>
14
#include <limits.h>
15
#include <libgen.h>
16
#include <malloc.h>
17
#include <sys/wait.h>
18
#include <sys/prctl.h>
19
#include <sys/stat.h>
20
21
#include "../kselftest.h"
22
23
static int nerrs;
24
static pid_t mpid; /* main() pid is used to avoid duplicate test counts */
25
26
static void vmaybe_write_file(bool enoent_ok, char *filename, char *fmt, va_list ap)
27
{
28
char buf[4096];
29
int fd;
30
ssize_t written;
31
int buf_len;
32
33
buf_len = vsnprintf(buf, sizeof(buf), fmt, ap);
34
if (buf_len < 0)
35
ksft_exit_fail_msg("vsnprintf failed - %s\n", strerror(errno));
36
37
if (buf_len >= sizeof(buf))
38
ksft_exit_fail_msg("vsnprintf output truncated\n");
39
40
41
fd = open(filename, O_WRONLY);
42
if (fd < 0) {
43
if ((errno == ENOENT) && enoent_ok)
44
return;
45
ksft_exit_fail_msg("open of %s failed - %s\n",
46
filename, strerror(errno));
47
}
48
written = write(fd, buf, buf_len);
49
if (written != buf_len) {
50
if (written >= 0) {
51
ksft_exit_fail_msg("short write to %s\n", filename);
52
} else {
53
ksft_exit_fail_msg("write to %s failed - %s\n",
54
filename, strerror(errno));
55
}
56
}
57
if (close(fd) != 0) {
58
ksft_exit_fail_msg("close of %s failed - %s\n",
59
filename, strerror(errno));
60
}
61
}
62
63
static void maybe_write_file(char *filename, char *fmt, ...)
64
{
65
va_list ap;
66
67
va_start(ap, fmt);
68
vmaybe_write_file(true, filename, fmt, ap);
69
va_end(ap);
70
}
71
72
static void write_file(char *filename, char *fmt, ...)
73
{
74
va_list ap;
75
76
va_start(ap, fmt);
77
vmaybe_write_file(false, filename, fmt, ap);
78
va_end(ap);
79
}
80
81
static bool create_and_enter_ns(uid_t inner_uid)
82
{
83
uid_t outer_uid;
84
gid_t outer_gid;
85
int i, ret;
86
bool have_outer_privilege;
87
88
outer_uid = getuid();
89
outer_gid = getgid();
90
91
if (outer_uid == 0 && unshare(CLONE_NEWNS) == 0) {
92
ksft_print_msg("[NOTE]\tUsing global UIDs for tests\n");
93
if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) != 0)
94
ksft_exit_fail_msg("PR_SET_KEEPCAPS - %s\n",
95
strerror(errno));
96
if (setresuid(inner_uid, inner_uid, -1) != 0)
97
ksft_exit_fail_msg("setresuid - %s\n", strerror(errno));
98
99
// Re-enable effective caps
100
ret = capng_get_caps_process();
101
if (ret == -1)
102
ksft_exit_fail_msg("capng_get_caps_process failed\n");
103
104
for (i = 0; i < CAP_LAST_CAP; i++)
105
if (capng_have_capability(CAPNG_PERMITTED, i))
106
capng_update(CAPNG_ADD, CAPNG_EFFECTIVE, i);
107
if (capng_apply(CAPNG_SELECT_CAPS) != 0)
108
ksft_exit_fail_msg(
109
"capng_apply - %s\n", strerror(errno));
110
111
have_outer_privilege = true;
112
} else if (unshare(CLONE_NEWUSER | CLONE_NEWNS) == 0) {
113
ksft_print_msg("[NOTE]\tUsing a user namespace for tests\n");
114
maybe_write_file("/proc/self/setgroups", "deny");
115
write_file("/proc/self/uid_map", "%d %d 1", inner_uid, outer_uid);
116
write_file("/proc/self/gid_map", "0 %d 1", outer_gid);
117
118
have_outer_privilege = false;
119
} else {
120
ksft_exit_skip("must be root or be able to create a userns\n");
121
}
122
123
if (mount("none", "/", NULL, MS_REC | MS_PRIVATE, NULL) != 0)
124
ksft_exit_fail_msg("remount everything private - %s\n",
125
strerror(errno));
126
127
return have_outer_privilege;
128
}
129
130
static void chdir_to_tmpfs(void)
131
{
132
char cwd[PATH_MAX];
133
if (getcwd(cwd, sizeof(cwd)) != cwd)
134
ksft_exit_fail_msg("getcwd - %s\n", strerror(errno));
135
136
if (mount("private_tmp", ".", "tmpfs", 0, "mode=0777") != 0)
137
ksft_exit_fail_msg("mount private tmpfs - %s\n",
138
strerror(errno));
139
140
if (chdir(cwd) != 0)
141
ksft_exit_fail_msg("chdir to private tmpfs - %s\n",
142
strerror(errno));
143
}
144
145
static void copy_fromat_to(int fromfd, const char *fromname, const char *toname)
146
{
147
int from = openat(fromfd, fromname, O_RDONLY);
148
if (from == -1)
149
ksft_exit_fail_msg("open copy source - %s\n", strerror(errno));
150
151
int to = open(toname, O_CREAT | O_WRONLY | O_EXCL, 0700);
152
153
while (true) {
154
char buf[4096];
155
ssize_t sz = read(from, buf, sizeof(buf));
156
if (sz == 0)
157
break;
158
if (sz < 0)
159
ksft_exit_fail_msg("read - %s\n", strerror(errno));
160
161
if (write(to, buf, sz) != sz)
162
/* no short writes on tmpfs */
163
ksft_exit_fail_msg("write - %s\n", strerror(errno));
164
}
165
166
close(from);
167
close(to);
168
}
169
170
static bool fork_wait(void)
171
{
172
pid_t child = fork();
173
if (child == 0) {
174
nerrs = 0;
175
return true;
176
} else if (child > 0) {
177
int status;
178
if (waitpid(child, &status, 0) != child ||
179
!WIFEXITED(status)) {
180
ksft_print_msg("Child died\n");
181
nerrs++;
182
} else if (WEXITSTATUS(status) != 0) {
183
ksft_print_msg("Child failed\n");
184
nerrs++;
185
} else {
186
/* don't print this message for mpid */
187
if (getpid() != mpid)
188
ksft_test_result_pass("Passed\n");
189
}
190
return false;
191
} else {
192
ksft_exit_fail_msg("fork - %s\n", strerror(errno));
193
return false;
194
}
195
}
196
197
static void exec_other_validate_cap(const char *name,
198
bool eff, bool perm, bool inh, bool ambient)
199
{
200
execl(name, name, (eff ? "1" : "0"),
201
(perm ? "1" : "0"), (inh ? "1" : "0"), (ambient ? "1" : "0"),
202
NULL);
203
ksft_exit_fail_msg("execl - %s\n", strerror(errno));
204
}
205
206
static void exec_validate_cap(bool eff, bool perm, bool inh, bool ambient)
207
{
208
exec_other_validate_cap("./validate_cap", eff, perm, inh, ambient);
209
}
210
211
static int do_tests(int uid, const char *our_path)
212
{
213
int ret;
214
bool have_outer_privilege = create_and_enter_ns(uid);
215
216
int ourpath_fd = open(our_path, O_RDONLY | O_DIRECTORY);
217
if (ourpath_fd == -1)
218
ksft_exit_fail_msg("open '%s' - %s\n",
219
our_path, strerror(errno));
220
221
chdir_to_tmpfs();
222
223
copy_fromat_to(ourpath_fd, "validate_cap", "validate_cap");
224
225
if (have_outer_privilege) {
226
uid_t gid = getegid();
227
228
copy_fromat_to(ourpath_fd, "validate_cap",
229
"validate_cap_suidroot");
230
if (chown("validate_cap_suidroot", 0, -1) != 0)
231
ksft_exit_fail_msg("chown - %s\n", strerror(errno));
232
if (chmod("validate_cap_suidroot", S_ISUID | 0700) != 0)
233
ksft_exit_fail_msg("chmod - %s\n", strerror(errno));
234
235
copy_fromat_to(ourpath_fd, "validate_cap",
236
"validate_cap_suidnonroot");
237
if (chown("validate_cap_suidnonroot", uid + 1, -1) != 0)
238
ksft_exit_fail_msg("chown - %s\n", strerror(errno));
239
if (chmod("validate_cap_suidnonroot", S_ISUID | 0700) != 0)
240
ksft_exit_fail_msg("chmod - %s\n", strerror(errno));
241
242
copy_fromat_to(ourpath_fd, "validate_cap",
243
"validate_cap_sgidroot");
244
if (chown("validate_cap_sgidroot", -1, 0) != 0)
245
ksft_exit_fail_msg("chown - %s\n", strerror(errno));
246
if (chmod("validate_cap_sgidroot", S_ISGID | 0710) != 0)
247
ksft_exit_fail_msg("chmod - %s\n", strerror(errno));
248
249
copy_fromat_to(ourpath_fd, "validate_cap",
250
"validate_cap_sgidnonroot");
251
if (chown("validate_cap_sgidnonroot", -1, gid + 1) != 0)
252
ksft_exit_fail_msg("chown - %s\n", strerror(errno));
253
if (chmod("validate_cap_sgidnonroot", S_ISGID | 0710) != 0)
254
ksft_exit_fail_msg("chmod - %s\n", strerror(errno));
255
}
256
257
ret = capng_get_caps_process();
258
if (ret == -1)
259
ksft_exit_fail_msg("capng_get_caps_process failed\n");
260
261
/* Make sure that i starts out clear */
262
capng_update(CAPNG_DROP, CAPNG_INHERITABLE, CAP_NET_BIND_SERVICE);
263
if (capng_apply(CAPNG_SELECT_CAPS) != 0)
264
ksft_exit_fail_msg("capng_apply - %s\n", strerror(errno));
265
266
if (uid == 0) {
267
ksft_print_msg("[RUN]\tRoot => ep\n");
268
if (fork_wait())
269
exec_validate_cap(true, true, false, false);
270
} else {
271
ksft_print_msg("[RUN]\tNon-root => no caps\n");
272
if (fork_wait())
273
exec_validate_cap(false, false, false, false);
274
}
275
276
ksft_print_msg("Check cap_ambient manipulation rules\n");
277
278
/* We should not be able to add ambient caps yet. */
279
if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_NET_BIND_SERVICE, 0, 0, 0) != -1 || errno != EPERM) {
280
if (errno == EINVAL)
281
ksft_test_result_fail(
282
"PR_CAP_AMBIENT_RAISE isn't supported\n");
283
else
284
ksft_test_result_fail(
285
"PR_CAP_AMBIENT_RAISE should have failed eith EPERM on a non-inheritable cap\n");
286
return 1;
287
}
288
ksft_test_result_pass(
289
"PR_CAP_AMBIENT_RAISE failed on non-inheritable cap\n");
290
291
capng_update(CAPNG_ADD, CAPNG_INHERITABLE, CAP_NET_RAW);
292
capng_update(CAPNG_DROP, CAPNG_PERMITTED, CAP_NET_RAW);
293
capng_update(CAPNG_DROP, CAPNG_EFFECTIVE, CAP_NET_RAW);
294
if (capng_apply(CAPNG_SELECT_CAPS) != 0)
295
ksft_exit_fail_msg("capng_apply - %s\n", strerror(errno));
296
if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_NET_RAW, 0, 0, 0) != -1 || errno != EPERM) {
297
ksft_test_result_fail(
298
"PR_CAP_AMBIENT_RAISE should have failed on a non-permitted cap\n");
299
return 1;
300
}
301
ksft_test_result_pass(
302
"PR_CAP_AMBIENT_RAISE failed on non-permitted cap\n");
303
304
capng_update(CAPNG_ADD, CAPNG_INHERITABLE, CAP_NET_BIND_SERVICE);
305
if (capng_apply(CAPNG_SELECT_CAPS) != 0)
306
ksft_exit_fail_msg("capng_apply - %s\n", strerror(errno));
307
if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_NET_BIND_SERVICE, 0, 0, 0) != 0) {
308
ksft_test_result_fail(
309
"PR_CAP_AMBIENT_RAISE should have succeeded\n");
310
return 1;
311
}
312
ksft_test_result_pass("PR_CAP_AMBIENT_RAISE worked\n");
313
314
if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_NET_BIND_SERVICE, 0, 0, 0) != 1) {
315
ksft_test_result_fail("PR_CAP_AMBIENT_IS_SET is broken\n");
316
return 1;
317
}
318
319
if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0, 0) != 0)
320
ksft_exit_fail_msg("PR_CAP_AMBIENT_CLEAR_ALL - %s\n",
321
strerror(errno));
322
323
if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_NET_BIND_SERVICE, 0, 0, 0) != 0) {
324
ksft_test_result_fail(
325
"PR_CAP_AMBIENT_CLEAR_ALL didn't work\n");
326
return 1;
327
}
328
329
if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_NET_BIND_SERVICE, 0, 0, 0) != 0)
330
ksft_exit_fail_msg("PR_CAP_AMBIENT_RAISE - %s\n",
331
strerror(errno));
332
333
capng_update(CAPNG_DROP, CAPNG_INHERITABLE, CAP_NET_BIND_SERVICE);
334
if (capng_apply(CAPNG_SELECT_CAPS) != 0)
335
ksft_exit_fail_msg("capng_apply - %s\n", strerror(errno));
336
337
if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_NET_BIND_SERVICE, 0, 0, 0) != 0) {
338
ksft_test_result_fail("Dropping I should have dropped A\n");
339
return 1;
340
}
341
342
ksft_test_result_pass("Basic manipulation appears to work\n");
343
344
capng_update(CAPNG_ADD, CAPNG_INHERITABLE, CAP_NET_BIND_SERVICE);
345
if (capng_apply(CAPNG_SELECT_CAPS) != 0)
346
ksft_exit_fail_msg("capng_apply - %s\n", strerror(errno));
347
if (uid == 0) {
348
ksft_print_msg("[RUN]\tRoot +i => eip\n");
349
if (fork_wait())
350
exec_validate_cap(true, true, true, false);
351
} else {
352
ksft_print_msg("[RUN]\tNon-root +i => i\n");
353
if (fork_wait())
354
exec_validate_cap(false, false, true, false);
355
}
356
357
if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_NET_BIND_SERVICE, 0, 0, 0) != 0)
358
ksft_exit_fail_msg("PR_CAP_AMBIENT_RAISE - %s\n",
359
strerror(errno));
360
361
ksft_print_msg("[RUN]\tUID %d +ia => eipa\n", uid);
362
if (fork_wait())
363
exec_validate_cap(true, true, true, true);
364
365
/* The remaining tests need real privilege */
366
367
if (!have_outer_privilege) {
368
ksft_test_result_skip("SUID/SGID tests (needs privilege)\n");
369
goto done;
370
}
371
372
if (uid == 0) {
373
ksft_print_msg("[RUN]\tRoot +ia, suidroot => eipa\n");
374
if (fork_wait())
375
exec_other_validate_cap("./validate_cap_suidroot",
376
true, true, true, true);
377
378
ksft_print_msg("[RUN]\tRoot +ia, suidnonroot => ip\n");
379
if (fork_wait())
380
exec_other_validate_cap("./validate_cap_suidnonroot",
381
false, true, true, false);
382
383
ksft_print_msg("[RUN]\tRoot +ia, sgidroot => eipa\n");
384
if (fork_wait())
385
exec_other_validate_cap("./validate_cap_sgidroot",
386
true, true, true, true);
387
388
if (fork_wait()) {
389
ksft_print_msg(
390
"[RUN]\tRoot, gid != 0, +ia, sgidroot => eip\n");
391
if (setresgid(1, 1, 1) != 0)
392
ksft_exit_fail_msg("setresgid - %s\n",
393
strerror(errno));
394
exec_other_validate_cap("./validate_cap_sgidroot",
395
true, true, true, false);
396
}
397
398
ksft_print_msg("[RUN]\tRoot +ia, sgidnonroot => eip\n");
399
if (fork_wait())
400
exec_other_validate_cap("./validate_cap_sgidnonroot",
401
true, true, true, false);
402
} else {
403
ksft_print_msg("[RUN]\tNon-root +ia, sgidnonroot => i\n");
404
if (fork_wait())
405
exec_other_validate_cap("./validate_cap_sgidnonroot",
406
false, false, true, false);
407
408
if (fork_wait()) {
409
ksft_print_msg("[RUN]\tNon-root +ia, sgidroot => i\n");
410
if (setresgid(1, 1, 1) != 0)
411
ksft_exit_fail_msg("setresgid - %s\n",
412
strerror(errno));
413
exec_other_validate_cap("./validate_cap_sgidroot",
414
false, false, true, false);
415
}
416
}
417
418
done:
419
ksft_print_cnts();
420
return nerrs ? 1 : 0;
421
}
422
423
int main(int argc, char **argv)
424
{
425
char *tmp1, *tmp2, *our_path;
426
427
/* Find our path */
428
tmp1 = strdup(argv[0]);
429
if (!tmp1)
430
ksft_exit_fail_msg("strdup - %s\n", strerror(errno));
431
tmp2 = dirname(tmp1);
432
our_path = strdup(tmp2);
433
if (!our_path)
434
ksft_exit_fail_msg("strdup - %s\n", strerror(errno));
435
free(tmp1);
436
437
mpid = getpid();
438
439
if (fork_wait()) {
440
ksft_print_header();
441
ksft_set_plan(12);
442
ksft_print_msg("[RUN]\t+++ Tests with uid == 0 +++\n");
443
return do_tests(0, our_path);
444
}
445
446
ksft_print_msg("==================================================\n");
447
448
if (fork_wait()) {
449
ksft_print_header();
450
ksft_set_plan(9);
451
ksft_print_msg("[RUN]\t+++ Tests with uid != 0 +++\n");
452
return do_tests(1, our_path);
453
}
454
455
return nerrs ? 1 : 0;
456
}
457
458