aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndy Lutomirski <luto@kernel.org>2015-09-04 18:42:48 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2015-09-04 19:54:41 -0400
commit32ae976ed3b5ba39c9208ace41bcdf4157d21db3 (patch)
tree207faf9bc45d21e6beacad07431112442f262d99
parent58319057b7847667f0c9585b9de0e8932b0fdb08 (diff)
selftests/capabilities: Add tests for capability evolution
This test focuses on ambient capabilities. It requires either root or the ability to create user namespaces. Some of the test cases will be skipped for nonroot users. Signed-off-by: Andy Lutomirski <luto@kernel.org> Acked-by: Kees Cook <keescook@chromium.org> Cc: Christoph Lameter <cl@linux.com> # Original author Cc: Serge E. Hallyn <serge.hallyn@ubuntu.com> Cc: James Morris <james.l.morris@oracle.com> Cc: Shuah Khan <shuahkh@osg.samsung.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--tools/testing/selftests/capabilities/.gitignore2
-rw-r--r--tools/testing/selftests/capabilities/Makefile18
-rw-r--r--tools/testing/selftests/capabilities/test_execve.c427
-rw-r--r--tools/testing/selftests/capabilities/validate_cap.c73
4 files changed, 520 insertions, 0 deletions
diff --git a/tools/testing/selftests/capabilities/.gitignore b/tools/testing/selftests/capabilities/.gitignore
new file mode 100644
index 000000000000..b732dd0d4738
--- /dev/null
+++ b/tools/testing/selftests/capabilities/.gitignore
@@ -0,0 +1,2 @@
1test_execve
2validate_cap
diff --git a/tools/testing/selftests/capabilities/Makefile b/tools/testing/selftests/capabilities/Makefile
new file mode 100644
index 000000000000..8c8f0c1f0889
--- /dev/null
+++ b/tools/testing/selftests/capabilities/Makefile
@@ -0,0 +1,18 @@
1all:
2
3include ../lib.mk
4
5.PHONY: all clean
6
7TARGETS := validate_cap test_execve
8TEST_PROGS := test_execve
9
10CFLAGS := -O2 -g -std=gnu99 -Wall -lcap-ng
11
12all: $(TARGETS)
13
14clean:
15 $(RM) $(TARGETS)
16
17$(TARGETS): %: %.c
18 $(CC) -o $@ $(CFLAGS) $(EXTRA_CFLAGS) $^ -lrt -ldl
diff --git a/tools/testing/selftests/capabilities/test_execve.c b/tools/testing/selftests/capabilities/test_execve.c
new file mode 100644
index 000000000000..10a21a958aaf
--- /dev/null
+++ b/tools/testing/selftests/capabilities/test_execve.c
@@ -0,0 +1,427 @@
1#define _GNU_SOURCE
2
3#include <cap-ng.h>
4#include <err.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#ifndef PR_CAP_AMBIENT
22#define PR_CAP_AMBIENT 47
23# define PR_CAP_AMBIENT_IS_SET 1
24# define PR_CAP_AMBIENT_RAISE 2
25# define PR_CAP_AMBIENT_LOWER 3
26# define PR_CAP_AMBIENT_CLEAR_ALL 4
27#endif
28
29static int nerrs;
30
31static void vmaybe_write_file(bool enoent_ok, char *filename, char *fmt, va_list ap)
32{
33 char buf[4096];
34 int fd;
35 ssize_t written;
36 int buf_len;
37
38 buf_len = vsnprintf(buf, sizeof(buf), fmt, ap);
39 if (buf_len < 0) {
40 err(1, "vsnprintf failed");
41 }
42 if (buf_len >= sizeof(buf)) {
43 errx(1, "vsnprintf output truncated");
44 }
45
46 fd = open(filename, O_WRONLY);
47 if (fd < 0) {
48 if ((errno == ENOENT) && enoent_ok)
49 return;
50 err(1, "open of %s failed", filename);
51 }
52 written = write(fd, buf, buf_len);
53 if (written != buf_len) {
54 if (written >= 0) {
55 errx(1, "short write to %s", filename);
56 } else {
57 err(1, "write to %s failed", filename);
58 }
59 }
60 if (close(fd) != 0) {
61 err(1, "close of %s failed", filename);
62 }
63}
64
65static void maybe_write_file(char *filename, char *fmt, ...)
66{
67 va_list ap;
68
69 va_start(ap, fmt);
70 vmaybe_write_file(true, filename, fmt, ap);
71 va_end(ap);
72}
73
74static void write_file(char *filename, char *fmt, ...)
75{
76 va_list ap;
77
78 va_start(ap, fmt);
79 vmaybe_write_file(false, filename, fmt, ap);
80 va_end(ap);
81}
82
83static bool create_and_enter_ns(uid_t inner_uid)
84{
85 uid_t outer_uid;
86 gid_t outer_gid;
87 int i;
88 bool have_outer_privilege;
89
90 outer_uid = getuid();
91 outer_gid = getgid();
92
93 /*
94 * TODO: If we're already root, we could skip creating the userns.
95 */
96
97 if (unshare(CLONE_NEWNS) == 0) {
98 printf("[NOTE]\tUsing global UIDs for tests\n");
99 if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) != 0)
100 err(1, "PR_SET_KEEPCAPS");
101 if (setresuid(inner_uid, inner_uid, -1) != 0)
102 err(1, "setresuid");
103
104 // Re-enable effective caps
105 capng_get_caps_process();
106 for (i = 0; i < CAP_LAST_CAP; i++)
107 if (capng_have_capability(CAPNG_PERMITTED, i))
108 capng_update(CAPNG_ADD, CAPNG_EFFECTIVE, i);
109 if (capng_apply(CAPNG_SELECT_CAPS) != 0)
110 err(1, "capng_apply");
111
112 have_outer_privilege = true;
113 } else if (unshare(CLONE_NEWUSER | CLONE_NEWNS) == 0) {
114 printf("[NOTE]\tUsing a user namespace for tests\n");
115 maybe_write_file("/proc/self/setgroups", "deny");
116 write_file("/proc/self/uid_map", "%d %d 1", inner_uid, outer_uid);
117 write_file("/proc/self/gid_map", "0 %d 1", outer_gid);
118
119 have_outer_privilege = false;
120 } else {
121 errx(1, "must be root or be able to create a userns");
122 }
123
124 if (mount("none", "/", NULL, MS_REC | MS_PRIVATE, NULL) != 0)
125 err(1, "remount everything private");
126
127 return have_outer_privilege;
128}
129
130static void chdir_to_tmpfs(void)
131{
132 char cwd[PATH_MAX];
133 if (getcwd(cwd, sizeof(cwd)) != cwd)
134 err(1, "getcwd");
135
136 if (mount("private_tmp", ".", "tmpfs", 0, "mode=0777") != 0)
137 err(1, "mount private tmpfs");
138
139 if (chdir(cwd) != 0)
140 err(1, "chdir to private tmpfs");
141
142 if (umount2(".", MNT_DETACH) != 0)
143 err(1, "detach private tmpfs");
144}
145
146static void copy_fromat_to(int fromfd, const char *fromname, const char *toname)
147{
148 int from = openat(fromfd, fromname, O_RDONLY);
149 if (from == -1)
150 err(1, "open copy source");
151
152 int to = open(toname, O_CREAT | O_WRONLY | O_EXCL, 0700);
153
154 while (true) {
155 char buf[4096];
156 ssize_t sz = read(from, buf, sizeof(buf));
157 if (sz == 0)
158 break;
159 if (sz < 0)
160 err(1, "read");
161
162 if (write(to, buf, sz) != sz)
163 err(1, "write"); /* no short writes on tmpfs */
164 }
165
166 close(from);
167 close(to);
168}
169
170static 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 printf("[FAIL]\tChild died\n");
181 nerrs++;
182 } else if (WEXITSTATUS(status) != 0) {
183 printf("[FAIL]\tChild failed\n");
184 nerrs++;
185 } else {
186 printf("[OK]\tChild succeeded\n");
187 }
188
189 return false;
190 } else {
191 err(1, "fork");
192 }
193}
194
195static void exec_other_validate_cap(const char *name,
196 bool eff, bool perm, bool inh, bool ambient)
197{
198 execl(name, name, (eff ? "1" : "0"),
199 (perm ? "1" : "0"), (inh ? "1" : "0"), (ambient ? "1" : "0"),
200 NULL);
201 err(1, "execl");
202}
203
204static void exec_validate_cap(bool eff, bool perm, bool inh, bool ambient)
205{
206 exec_other_validate_cap("./validate_cap", eff, perm, inh, ambient);
207}
208
209static int do_tests(int uid, const char *our_path)
210{
211 bool have_outer_privilege = create_and_enter_ns(uid);
212
213 int ourpath_fd = open(our_path, O_RDONLY | O_DIRECTORY);
214 if (ourpath_fd == -1)
215 err(1, "open '%s'", our_path);
216
217 chdir_to_tmpfs();
218
219 copy_fromat_to(ourpath_fd, "validate_cap", "validate_cap");
220
221 if (have_outer_privilege) {
222 uid_t gid = getegid();
223
224 copy_fromat_to(ourpath_fd, "validate_cap",
225 "validate_cap_suidroot");
226 if (chown("validate_cap_suidroot", 0, -1) != 0)
227 err(1, "chown");
228 if (chmod("validate_cap_suidroot", S_ISUID | 0700) != 0)
229 err(1, "chmod");
230
231 copy_fromat_to(ourpath_fd, "validate_cap",
232 "validate_cap_suidnonroot");
233 if (chown("validate_cap_suidnonroot", uid + 1, -1) != 0)
234 err(1, "chown");
235 if (chmod("validate_cap_suidnonroot", S_ISUID | 0700) != 0)
236 err(1, "chmod");
237
238 copy_fromat_to(ourpath_fd, "validate_cap",
239 "validate_cap_sgidroot");
240 if (chown("validate_cap_sgidroot", -1, 0) != 0)
241 err(1, "chown");
242 if (chmod("validate_cap_sgidroot", S_ISGID | 0710) != 0)
243 err(1, "chmod");
244
245 copy_fromat_to(ourpath_fd, "validate_cap",
246 "validate_cap_sgidnonroot");
247 if (chown("validate_cap_sgidnonroot", -1, gid + 1) != 0)
248 err(1, "chown");
249 if (chmod("validate_cap_sgidnonroot", S_ISGID | 0710) != 0)
250 err(1, "chmod");
251}
252
253 capng_get_caps_process();
254
255 /* Make sure that i starts out clear */
256 capng_update(CAPNG_DROP, CAPNG_INHERITABLE, CAP_NET_BIND_SERVICE);
257 if (capng_apply(CAPNG_SELECT_CAPS) != 0)
258 err(1, "capng_apply");
259
260 if (uid == 0) {
261 printf("[RUN]\tRoot => ep\n");
262 if (fork_wait())
263 exec_validate_cap(true, true, false, false);
264 } else {
265 printf("[RUN]\tNon-root => no caps\n");
266 if (fork_wait())
267 exec_validate_cap(false, false, false, false);
268 }
269
270 printf("[OK]\tCheck cap_ambient manipulation rules\n");
271
272 /* We should not be able to add ambient caps yet. */
273 if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_NET_BIND_SERVICE, 0, 0, 0) != -1 || errno != EPERM) {
274 if (errno == EINVAL)
275 printf("[FAIL]\tPR_CAP_AMBIENT_RAISE isn't supported\n");
276 else
277 printf("[FAIL]\tPR_CAP_AMBIENT_RAISE should have failed eith EPERM on a non-inheritable cap\n");
278 return 1;
279 }
280 printf("[OK]\tPR_CAP_AMBIENT_RAISE failed on non-inheritable cap\n");
281
282 capng_update(CAPNG_ADD, CAPNG_INHERITABLE, CAP_NET_RAW);
283 capng_update(CAPNG_DROP, CAPNG_PERMITTED, CAP_NET_RAW);
284 capng_update(CAPNG_DROP, CAPNG_EFFECTIVE, CAP_NET_RAW);
285 if (capng_apply(CAPNG_SELECT_CAPS) != 0)
286 err(1, "capng_apply");
287 if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_NET_RAW, 0, 0, 0) != -1 || errno != EPERM) {
288 printf("[FAIL]\tPR_CAP_AMBIENT_RAISE should have failed on a non-permitted cap\n");
289 return 1;
290 }
291 printf("[OK]\tPR_CAP_AMBIENT_RAISE failed on non-permitted cap\n");
292
293 capng_update(CAPNG_ADD, CAPNG_INHERITABLE, CAP_NET_BIND_SERVICE);
294 if (capng_apply(CAPNG_SELECT_CAPS) != 0)
295 err(1, "capng_apply");
296 if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_NET_BIND_SERVICE, 0, 0, 0) != 0) {
297 printf("[FAIL]\tPR_CAP_AMBIENT_RAISE should have succeeded\n");
298 return 1;
299 }
300 printf("[OK]\tPR_CAP_AMBIENT_RAISE worked\n");
301
302 if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_NET_BIND_SERVICE, 0, 0, 0) != 1) {
303 printf("[FAIL]\tPR_CAP_AMBIENT_IS_SET is broken\n");
304 return 1;
305 }
306
307 if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0, 0) != 0)
308 err(1, "PR_CAP_AMBIENT_CLEAR_ALL");
309
310 if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_NET_BIND_SERVICE, 0, 0, 0) != 0) {
311 printf("[FAIL]\tPR_CAP_AMBIENT_CLEAR_ALL didn't work\n");
312 return 1;
313 }
314
315 if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_NET_BIND_SERVICE, 0, 0, 0) != 0)
316 err(1, "PR_CAP_AMBIENT_RAISE");
317
318 capng_update(CAPNG_DROP, CAPNG_INHERITABLE, CAP_NET_BIND_SERVICE);
319 if (capng_apply(CAPNG_SELECT_CAPS) != 0)
320 err(1, "capng_apply");
321
322 if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_NET_BIND_SERVICE, 0, 0, 0) != 0) {
323 printf("[FAIL]\tDropping I should have dropped A\n");
324 return 1;
325 }
326
327 printf("[OK]\tBasic manipulation appears to work\n");
328
329 capng_update(CAPNG_ADD, CAPNG_INHERITABLE, CAP_NET_BIND_SERVICE);
330 if (capng_apply(CAPNG_SELECT_CAPS) != 0)
331 err(1, "capng_apply");
332 if (uid == 0) {
333 printf("[RUN]\tRoot +i => eip\n");
334 if (fork_wait())
335 exec_validate_cap(true, true, true, false);
336 } else {
337 printf("[RUN]\tNon-root +i => i\n");
338 if (fork_wait())
339 exec_validate_cap(false, false, true, false);
340 }
341
342 if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_NET_BIND_SERVICE, 0, 0, 0) != 0)
343 err(1, "PR_CAP_AMBIENT_RAISE");
344
345 printf("[RUN]\tUID %d +ia => eipa\n", uid);
346 if (fork_wait())
347 exec_validate_cap(true, true, true, true);
348
349 /* The remaining tests need real privilege */
350
351 if (!have_outer_privilege) {
352 printf("[SKIP]\tSUID/SGID tests (needs privilege)\n");
353 goto done;
354 }
355
356 if (uid == 0) {
357 printf("[RUN]\tRoot +ia, suidroot => eipa\n");
358 if (fork_wait())
359 exec_other_validate_cap("./validate_cap_suidroot",
360 true, true, true, true);
361
362 printf("[RUN]\tRoot +ia, suidnonroot => ip\n");
363 if (fork_wait())
364 exec_other_validate_cap("./validate_cap_suidnonroot",
365 false, true, true, false);
366
367 printf("[RUN]\tRoot +ia, sgidroot => eipa\n");
368 if (fork_wait())
369 exec_other_validate_cap("./validate_cap_sgidroot",
370 true, true, true, true);
371
372 if (fork_wait()) {
373 printf("[RUN]\tRoot, gid != 0, +ia, sgidroot => eip\n");
374 if (setresgid(1, 1, 1) != 0)
375 err(1, "setresgid");
376 exec_other_validate_cap("./validate_cap_sgidroot",
377 true, true, true, false);
378 }
379
380 printf("[RUN]\tRoot +ia, sgidnonroot => eip\n");
381 if (fork_wait())
382 exec_other_validate_cap("./validate_cap_sgidnonroot",
383 true, true, true, false);
384 } else {
385 printf("[RUN]\tNon-root +ia, sgidnonroot => i\n");
386 exec_other_validate_cap("./validate_cap_sgidnonroot",
387 false, false, true, false);
388
389 if (fork_wait()) {
390 printf("[RUN]\tNon-root +ia, sgidroot => i\n");
391 if (setresgid(1, 1, 1) != 0)
392 err(1, "setresgid");
393 exec_other_validate_cap("./validate_cap_sgidroot",
394 false, false, true, false);
395 }
396 }
397
398done:
399 return nerrs ? 1 : 0;
400}
401
402int main(int argc, char **argv)
403{
404 char *tmp1, *tmp2, *our_path;
405
406 /* Find our path */
407 tmp1 = strdup(argv[0]);
408 if (!tmp1)
409 err(1, "strdup");
410 tmp2 = dirname(tmp1);
411 our_path = strdup(tmp2);
412 if (!our_path)
413 err(1, "strdup");
414 free(tmp1);
415
416 if (fork_wait()) {
417 printf("[RUN]\t+++ Tests with uid == 0 +++\n");
418 return do_tests(0, our_path);
419 }
420
421 if (fork_wait()) {
422 printf("[RUN]\t+++ Tests with uid != 0 +++\n");
423 return do_tests(1, our_path);
424 }
425
426 return nerrs ? 1 : 0;
427}
diff --git a/tools/testing/selftests/capabilities/validate_cap.c b/tools/testing/selftests/capabilities/validate_cap.c
new file mode 100644
index 000000000000..dd3c45f7b23c
--- /dev/null
+++ b/tools/testing/selftests/capabilities/validate_cap.c
@@ -0,0 +1,73 @@
1#include <cap-ng.h>
2#include <err.h>
3#include <linux/capability.h>
4#include <stdbool.h>
5#include <string.h>
6#include <stdio.h>
7#include <sys/prctl.h>
8#include <sys/auxv.h>
9
10#ifndef PR_CAP_AMBIENT
11#define PR_CAP_AMBIENT 47
12# define PR_CAP_AMBIENT_IS_SET 1
13# define PR_CAP_AMBIENT_RAISE 2
14# define PR_CAP_AMBIENT_LOWER 3
15# define PR_CAP_AMBIENT_CLEAR_ALL 4
16#endif
17
18#if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 19)
19# define HAVE_GETAUXVAL
20#endif
21
22static bool bool_arg(char **argv, int i)
23{
24 if (!strcmp(argv[i], "0"))
25 return false;
26 else if (!strcmp(argv[i], "1"))
27 return true;
28 else
29 errx(1, "wrong argv[%d]", i);
30}
31
32int main(int argc, char **argv)
33{
34 const char *atsec = "";
35
36 /*
37 * Be careful just in case a setgid or setcapped copy of this
38 * helper gets out.
39 */
40
41 if (argc != 5)
42 errx(1, "wrong argc");
43
44#ifdef HAVE_GETAUXVAL
45 if (getauxval(AT_SECURE))
46 atsec = " (AT_SECURE is set)";
47 else
48 atsec = " (AT_SECURE is not set)";
49#endif
50
51 capng_get_caps_process();
52
53 if (capng_have_capability(CAPNG_EFFECTIVE, CAP_NET_BIND_SERVICE) != bool_arg(argv, 1)) {
54 printf("[FAIL]\tWrong effective state%s\n", atsec);
55 return 1;
56 }
57 if (capng_have_capability(CAPNG_PERMITTED, CAP_NET_BIND_SERVICE) != bool_arg(argv, 2)) {
58 printf("[FAIL]\tWrong permitted state%s\n", atsec);
59 return 1;
60 }
61 if (capng_have_capability(CAPNG_INHERITABLE, CAP_NET_BIND_SERVICE) != bool_arg(argv, 3)) {
62 printf("[FAIL]\tWrong inheritable state%s\n", atsec);
63 return 1;
64 }
65
66 if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_NET_BIND_SERVICE, 0, 0, 0) != bool_arg(argv, 4)) {
67 printf("[FAIL]\tWrong ambient state%s\n", atsec);
68 return 1;
69 }
70
71 printf("[OK]\tCapabilities after execve were correct\n");
72 return 0;
73}