1 /*-
2 * Copyright (c) 2015 John Baldwin <jhb@FreeBSD.org>
3 * Copyright (c) 2023 The FreeBSD Foundation
4 *
5 * This software was developed by Jake Freeland <jfree@FreeBSD.org>
6 * under sponsorship from the FreeBSD Foundation.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30 #include <sys/param.h>
31 #include <sys/capsicum.h>
32 #include <sys/cpuset.h>
33 #include <sys/ktrace.h>
34 #include <sys/mman.h>
35 #include <sys/socket.h>
36 #include <sys/syscall.h>
37 #include <sys/sysent.h>
38 #include <sys/time.h>
39 #include <sys/uio.h>
40 #include <sys/user.h>
41 #include <sys/wait.h>
42
43 #include <machine/sysarch.h>
44 #include <netinet/in.h>
45
46 #include <atf-c.h>
47 #include <capsicum_helpers.h>
48 #include <errno.h>
49 #include <fcntl.h>
50 #include <netdb.h>
51 #include <signal.h>
52 #include <sysdecode.h>
53
54 /*
55 * A variant of ATF_REQUIRE that is suitable for use in child
56 * processes. This only works if the parent process is tripped up by
57 * the early exit and fails some requirement itself.
58 */
59 #define CHILD_REQUIRE(exp) do { \
60 if (!(exp)) \
61 child_fail_require(__FILE__, __LINE__, \
62 #exp " not met\n"); \
63 } while (0)
64 #define CHILD_REQUIRE_EQ(actual, expected) do { \
65 __typeof__(expected) _e = expected; \
66 __typeof__(actual) _a = actual; \
67 if (_e != _a) \
68 child_fail_require(__FILE__, __LINE__, #actual \
69 " (%jd) == " #expected " (%jd) not met\n", \
70 (intmax_t)_a, (intmax_t)_e); \
71 } while (0)
72
73 static __dead2 void
child_fail_require(const char * file,int line,const char * fmt,...)74 child_fail_require(const char *file, int line, const char *fmt, ...)
75 {
76 va_list ap;
77 char buf[1024];
78
79 /* Use write() not fprintf() to avoid possible duplicate output. */
80 snprintf(buf, sizeof(buf), "%s:%d: ", file, line);
81 write(STDERR_FILENO, buf, strlen(buf));
82 va_start(ap, fmt);
83 vsnprintf(buf, sizeof(buf), fmt, ap);
84 write(STDERR_FILENO, buf, strlen(buf));
85 va_end(ap);
86
87 _exit(32);
88 }
89
90 static void *
xmalloc(size_t size)91 xmalloc(size_t size)
92 {
93 void *p;
94
95 p = malloc(size);
96 ATF_REQUIRE(p != NULL);
97 return (p);
98 }
99
100 /*
101 * Determine sysdecode ABI based on proc's ABI in sv_flags.
102 */
103 static enum sysdecode_abi
syscallabi(u_int sv_flags)104 syscallabi(u_int sv_flags)
105 {
106 switch (sv_flags & SV_ABI_MASK) {
107 case SV_ABI_FREEBSD:
108 return (SYSDECODE_ABI_FREEBSD);
109 case SV_ABI_LINUX:
110 #ifdef __LP64__
111 if ((sv_flags & SV_ILP32) != 0)
112 return (SYSDECODE_ABI_LINUX32);
113 #endif
114 return (SYSDECODE_ABI_LINUX);
115 }
116 return (SYSDECODE_ABI_UNKNOWN);
117 }
118
119 static int
trace_child(int cpid,int facility,int status)120 trace_child(int cpid, int facility, int status)
121 {
122 int error, fd;
123
124 ATF_REQUIRE((fd = open("ktrace.out",
125 O_RDONLY | O_CREAT | O_TRUNC, 0600)) != -1);
126 ATF_REQUIRE_MSG(ktrace("ktrace.out", KTROP_SET, facility, cpid) != -1,
127 "ktrace failed: %s", strerror(errno));
128 /* Notify child that we've starting tracing. */
129 ATF_REQUIRE(kill(cpid, SIGUSR1) != -1);
130 /* Wait for child to raise violation and exit. */
131 ATF_REQUIRE(waitpid(cpid, &error, 0) != -1);
132 ATF_REQUIRE(WIFEXITED(error));
133 ATF_REQUIRE_EQ(WEXITSTATUS(error), status);
134 return (fd);
135 }
136
137 /*
138 * Start tracing capability violations and notify child that it can execute.
139 * Return @numv capability violations from child in @v.
140 */
141 static void
cap_trace_child(pid_t cpid,struct ktr_cap_fail * v,int numv)142 cap_trace_child(pid_t cpid, struct ktr_cap_fail *v, int numv)
143 {
144 struct ktr_header header;
145 ssize_t n;
146 int fd;
147
148 fd = trace_child(cpid, KTRFAC_CAPFAIL, 0);
149
150 /* Read ktrace header and ensure violation occurred. */
151 for (int i = 0; i < numv; ++i) {
152 ATF_REQUIRE((n = read(fd, &header, sizeof(header))) != -1);
153 ATF_REQUIRE_EQ(n, sizeof(header));
154 ATF_REQUIRE_EQ(header.ktr_len, sizeof(*v));
155 ATF_REQUIRE_EQ(header.ktr_pid, cpid);
156 /* Read the capability violation. */
157 ATF_REQUIRE((n = read(fd, v + i,
158 sizeof(*v))) != -1);
159 ATF_REQUIRE_EQ(n, sizeof(*v));
160 }
161 ATF_REQUIRE(close(fd) != -1);
162 }
163
164 /*
165 * Test if ktrace will record an operation that is done with
166 * insufficient rights.
167 */
168 ATF_TC_WITHOUT_HEAD(ktrace__cap_not_capable);
ATF_TC_BODY(ktrace__cap_not_capable,tc)169 ATF_TC_BODY(ktrace__cap_not_capable, tc)
170 {
171 struct ktr_cap_fail violation;
172 cap_rights_t rights;
173 sigset_t set = { };
174 pid_t pid;
175 int error;
176
177 /* Block SIGUSR1 so child does not terminate. */
178 ATF_REQUIRE(sigaddset(&set, SIGUSR1) != -1);
179 ATF_REQUIRE(sigprocmask(SIG_BLOCK, &set, NULL) != -1);
180
181 ATF_REQUIRE((pid = fork()) != -1);
182 if (pid == 0) {
183 /* Limit fd rights to CAP_READ. */
184 cap_rights_init(&rights, CAP_READ);
185 CHILD_REQUIRE(caph_rights_limit(STDIN_FILENO, &rights) != -1);
186 CHILD_REQUIRE(caph_enter() != -1);
187 /* Wait until ktrace has started. */
188 CHILD_REQUIRE(sigwait(&set, &error) != -1);
189 CHILD_REQUIRE_EQ(error, SIGUSR1);
190 /* Write without CAP_WRITE. */
191 CHILD_REQUIRE(write(STDIN_FILENO, &pid, sizeof(pid)) == -1);
192 CHILD_REQUIRE_EQ(errno, ENOTCAPABLE);
193 exit(0);
194 }
195
196 cap_trace_child(pid, &violation, 1);
197 ATF_REQUIRE_EQ(violation.cap_type, CAPFAIL_NOTCAPABLE);
198 ATF_REQUIRE(cap_rights_is_set(&violation.cap_data.cap_needed,
199 CAP_WRITE));
200 }
201
202 /*
203 * Test if ktrace will record an attempt to increase rights.
204 */
205 ATF_TC_WITHOUT_HEAD(ktrace__cap_increase_rights);
ATF_TC_BODY(ktrace__cap_increase_rights,tc)206 ATF_TC_BODY(ktrace__cap_increase_rights, tc)
207 {
208 struct ktr_cap_fail violation;
209 cap_rights_t rights;
210 sigset_t set = { };
211 pid_t pid;
212 int error;
213
214 /* Block SIGUSR1 so child does not terminate. */
215 ATF_REQUIRE(sigaddset(&set, SIGUSR1) != -1);
216 ATF_REQUIRE(sigprocmask(SIG_BLOCK, &set, NULL) != -1);
217
218 ATF_REQUIRE((pid = fork()) != -1);
219 if (pid == 0) {
220 /* Limit fd rights to CAP_READ. */
221 cap_rights_init(&rights, CAP_READ);
222 CHILD_REQUIRE(caph_rights_limit(STDIN_FILENO, &rights) != -1);
223 CHILD_REQUIRE(caph_enter() != -1);
224 /* Wait until ktrace has started. */
225 CHILD_REQUIRE(sigwait(&set, &error) != -1);
226 CHILD_REQUIRE_EQ(error, SIGUSR1);
227 /* Increase fd rights to include CAP_WRITE. */
228 cap_rights_set(&rights, CAP_WRITE);
229 CHILD_REQUIRE(caph_rights_limit(STDIN_FILENO, &rights) == -1);
230 CHILD_REQUIRE_EQ(errno, ENOTCAPABLE);
231 exit(0);
232 }
233
234 cap_trace_child(pid, &violation, 1);
235 ATF_REQUIRE_EQ(violation.cap_type, CAPFAIL_INCREASE);
236 ATF_REQUIRE(cap_rights_is_set(&violation.cap_data.cap_needed,
237 CAP_WRITE));
238 }
239
240 /*
241 * Test if disallowed syscalls are reported as capability violations.
242 */
243 ATF_TC_WITHOUT_HEAD(ktrace__cap_syscall);
ATF_TC_BODY(ktrace__cap_syscall,tc)244 ATF_TC_BODY(ktrace__cap_syscall, tc)
245 {
246 struct kinfo_file kinf;
247 struct ktr_cap_fail violation[2];
248 sigset_t set = { };
249 pid_t pid;
250 int error;
251
252 /* Block SIGUSR1 so child does not terminate. */
253 ATF_REQUIRE(sigaddset(&set, SIGUSR1) != -1);
254 ATF_REQUIRE(sigprocmask(SIG_BLOCK, &set, NULL) != -1);
255
256 ATF_REQUIRE((pid = fork()) != -1);
257 if (pid == 0) {
258 /* Wait until ktrace has started. */
259 CHILD_REQUIRE(sigwait(&set, &error) != -1);
260 CHILD_REQUIRE_EQ(error, SIGUSR1);
261 /* chdir() is not permitted in capability mode. */
262 CHILD_REQUIRE(chdir(".") != -1);
263 kinf.kf_structsize = sizeof(struct kinfo_file);
264 /*
265 * fcntl() is permitted in capability mode,
266 * but the F_KINFO cmd is not.
267 */
268 CHILD_REQUIRE(fcntl(STDIN_FILENO, F_KINFO, &kinf) != -1);
269 exit(0);
270 }
271
272 cap_trace_child(pid, violation, 2);
273 ATF_REQUIRE_EQ(violation[0].cap_type, CAPFAIL_SYSCALL);
274 error = syscallabi(violation[0].cap_svflags);
275 ATF_REQUIRE_STREQ(sysdecode_syscallname(error, violation[0].cap_code),
276 "chdir");
277
278 ATF_REQUIRE_EQ(violation[1].cap_type, CAPFAIL_SYSCALL);
279 error = syscallabi(violation[1].cap_svflags);
280 ATF_REQUIRE_STREQ(sysdecode_syscallname(error, violation[1].cap_code),
281 "fcntl");
282 ATF_REQUIRE_EQ(violation[1].cap_data.cap_int, F_KINFO);
283 }
284
285 /*
286 * Test if sending a signal to another process is reported as
287 * a signal violation.
288 */
289 ATF_TC_WITHOUT_HEAD(ktrace__cap_signal);
ATF_TC_BODY(ktrace__cap_signal,tc)290 ATF_TC_BODY(ktrace__cap_signal, tc)
291 {
292 struct ktr_cap_fail violation;
293 sigset_t set = { };
294 pid_t pid;
295 int error;
296
297 /* Block SIGUSR1 so child does not terminate. */
298 ATF_REQUIRE(sigaddset(&set, SIGUSR1) != -1);
299 ATF_REQUIRE(sigprocmask(SIG_BLOCK, &set, NULL) != -1);
300
301 ATF_REQUIRE((pid = fork()) != -1);
302 if (pid == 0) {
303 /* Wait until ktrace has started. */
304 CHILD_REQUIRE(sigwait(&set, &error) != -1);
305 CHILD_REQUIRE_EQ(error, SIGUSR1);
306 /*
307 * Signals may only be sent to ourself. Sending signals
308 * to other processes is not allowed in capability mode.
309 */
310 CHILD_REQUIRE(kill(getppid(), SIGCONT) != -1);
311 exit(0);
312 }
313
314 cap_trace_child(pid, &violation, 1);
315 ATF_REQUIRE_EQ(violation.cap_type, CAPFAIL_SIGNAL);
316 error = syscallabi(violation.cap_svflags);
317 ATF_REQUIRE_STREQ(sysdecode_syscallname(error, violation.cap_code),
318 "kill");
319 ATF_REQUIRE_EQ(violation.cap_data.cap_int, SIGCONT);
320 }
321
322 /*
323 * Test if opening a socket with a restricted protocol is reported
324 * as a protocol violation.
325 */
326 ATF_TC(ktrace__cap_proto);
ATF_TC_HEAD(ktrace__cap_proto,tc)327 ATF_TC_HEAD(ktrace__cap_proto, tc)
328 {
329 atf_tc_set_md_var(tc, "require.user", "root");
330 }
ATF_TC_BODY(ktrace__cap_proto,tc)331 ATF_TC_BODY(ktrace__cap_proto, tc)
332 {
333 struct ktr_cap_fail violation;
334 sigset_t set = { };
335 pid_t pid;
336 int error;
337
338 /* Block SIGUSR1 so child does not terminate. */
339 ATF_REQUIRE(sigaddset(&set, SIGUSR1) != -1);
340 ATF_REQUIRE(sigprocmask(SIG_BLOCK, &set, NULL) != -1);
341
342 ATF_REQUIRE((pid = fork()) != -1);
343 if (pid == 0) {
344 /* Wait until ktrace has started. */
345 CHILD_REQUIRE(sigwait(&set, &error) != -1);
346 CHILD_REQUIRE_EQ(error, SIGUSR1);
347 /*
348 * Certain protocols may not be used in capability mode.
349 * ICMP's raw-protocol interface is not allowed.
350 */
351 CHILD_REQUIRE(close(socket(AF_INET, SOCK_RAW,
352 IPPROTO_ICMP)) != -1);
353 exit(0);
354 }
355
356 cap_trace_child(pid, &violation, 1);
357 ATF_REQUIRE_EQ(violation.cap_type, CAPFAIL_PROTO);
358 error = syscallabi(violation.cap_svflags);
359 ATF_REQUIRE_STREQ(sysdecode_syscallname(error, violation.cap_code),
360 "socket");
361 ATF_REQUIRE_EQ(violation.cap_data.cap_int, IPPROTO_ICMP);
362 }
363
364 /*
365 * Test if sending data to an address using a socket is
366 * reported as a sockaddr violation.
367 */
368 ATF_TC_WITHOUT_HEAD(ktrace__cap_sockaddr);
ATF_TC_BODY(ktrace__cap_sockaddr,tc)369 ATF_TC_BODY(ktrace__cap_sockaddr, tc)
370 {
371 struct sockaddr_in addr = { }, *saddr;
372 struct ktr_cap_fail violation;
373 sigset_t set = { };
374 pid_t pid;
375 int error, sfd;
376
377 /* Block SIGUSR1 so child does not terminate. */
378 ATF_REQUIRE(sigaddset(&set, SIGUSR1) != -1);
379 ATF_REQUIRE(sigprocmask(SIG_BLOCK, &set, NULL) != -1);
380
381 CHILD_REQUIRE((sfd = socket(AF_INET, SOCK_DGRAM,
382 IPPROTO_UDP)) != -1);
383 addr.sin_family = AF_INET;
384 addr.sin_port = htons(5000);
385 addr.sin_addr.s_addr = INADDR_ANY;
386 CHILD_REQUIRE(bind(sfd, (const struct sockaddr *)&addr,
387 sizeof(addr)) != -1);
388
389 ATF_REQUIRE((pid = fork()) != -1);
390 if (pid == 0) {
391 /* Wait until ktrace has started. */
392 CHILD_REQUIRE(sigwait(&set, &error) != -1);
393 CHILD_REQUIRE_EQ(error, SIGUSR1);
394 /*
395 * Sending data to an address is not permitted.
396 * In this case, sending data to @addr causes a
397 * violation.
398 */
399 CHILD_REQUIRE(sendto(sfd, NULL, 0, 0,
400 (const struct sockaddr *)&addr, sizeof(addr)) != -1);
401 exit(0);
402 }
403
404 cap_trace_child(pid, &violation, 1);
405 ATF_REQUIRE_EQ(violation.cap_type, CAPFAIL_SOCKADDR);
406 error = syscallabi(violation.cap_svflags);
407 ATF_REQUIRE_STREQ(sysdecode_syscallname(error, violation.cap_code),
408 "sendto");
409 saddr = (struct sockaddr_in *)&violation.cap_data.cap_sockaddr;
410 ATF_REQUIRE_EQ(saddr->sin_family, AF_INET);
411 ATF_REQUIRE_EQ(saddr->sin_port, htons(5000));
412 ATF_REQUIRE_EQ(saddr->sin_addr.s_addr, INADDR_ANY);
413 close(sfd);
414 }
415
416 /*
417 * Test if openat() with AT_FDCWD and absolute path are reported
418 * as namei violations.
419 */
420 ATF_TC_WITHOUT_HEAD(ktrace__cap_namei);
ATF_TC_BODY(ktrace__cap_namei,tc)421 ATF_TC_BODY(ktrace__cap_namei, tc)
422 {
423 struct ktr_cap_fail violation[2];
424 sigset_t set = { };
425 pid_t pid;
426 int error;
427
428 /* Block SIGUSR1 so child does not terminate. */
429 ATF_REQUIRE(sigaddset(&set, SIGUSR1) != -1);
430 ATF_REQUIRE(sigprocmask(SIG_BLOCK, &set, NULL) != -1);
431
432 ATF_REQUIRE((pid = fork()) != -1);
433 if (pid == 0) {
434 /* Wait until ktrace has started. */
435 CHILD_REQUIRE(sigwait(&set, &error) != -1);
436 CHILD_REQUIRE_EQ(error, SIGUSR1);
437 /*
438 * The AT_FDCWD file descriptor has not been opened
439 * and will be inaccessible in capability mode.
440 */
441 CHILD_REQUIRE(close(openat(AT_FDCWD, "ktrace.out",
442 O_RDONLY | O_CREAT)) != -1);
443 /*
444 * Absolute paths are inaccessible in capability mode.
445 */
446 CHILD_REQUIRE(close(openat(-1, "/", O_RDONLY)) != -1);
447 exit(0);
448 }
449
450 cap_trace_child(pid, violation, 2);
451 ATF_REQUIRE_EQ(violation[0].cap_type, CAPFAIL_NAMEI);
452 error = syscallabi(violation[0].cap_svflags);
453 ATF_REQUIRE_STREQ(sysdecode_syscallname(error, violation[0].cap_code),
454 "openat");
455 ATF_REQUIRE_STREQ(violation[0].cap_data.cap_path, "AT_FDCWD");
456
457 ATF_REQUIRE_EQ(violation[1].cap_type, CAPFAIL_NAMEI);
458 error = syscallabi(violation[1].cap_svflags);
459 ATF_REQUIRE_STREQ(sysdecode_syscallname(error, violation[1].cap_code),
460 "openat");
461 ATF_REQUIRE_STREQ(violation[1].cap_data.cap_path, "/");
462 }
463
464 /*
465 * Test if changing another process's cpu set is recorded as
466 * a cpuset violation.
467 */
468 ATF_TC_WITHOUT_HEAD(ktrace__cap_cpuset);
ATF_TC_BODY(ktrace__cap_cpuset,tc)469 ATF_TC_BODY(ktrace__cap_cpuset, tc)
470 {
471 struct ktr_cap_fail violation;
472 cpuset_t cpuset_mask = { };
473 sigset_t set = { };
474 pid_t pid;
475 int error;
476
477 /* Block SIGUSR1 so child does not terminate. */
478 ATF_REQUIRE(sigaddset(&set, SIGUSR1) != -1);
479 ATF_REQUIRE(sigprocmask(SIG_BLOCK, &set, NULL) != -1);
480
481 ATF_REQUIRE((pid = fork()) != -1);
482 if (pid == 0) {
483 /* Wait until ktrace has started. */
484 CHILD_REQUIRE(sigwait(&set, &error) != -1);
485 CHILD_REQUIRE_EQ(error, SIGUSR1);
486 /*
487 * Set cpu 0 affinity for parent process.
488 * Other process's cpu sets are restricted in capability
489 * mode, so this will raise a violation.
490 */
491 CPU_SET(0, &cpuset_mask);
492 CHILD_REQUIRE(cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID,
493 getppid(), sizeof(cpuset_mask), &cpuset_mask) != -1);
494 exit(0);
495 }
496
497 cap_trace_child(pid, &violation, 1);
498 ATF_REQUIRE_EQ(violation.cap_type, CAPFAIL_CPUSET);
499 error = syscallabi(violation.cap_svflags);
500 ATF_REQUIRE_STREQ(sysdecode_syscallname(error, violation.cap_code),
501 "cpuset_setaffinity");
502 }
503
504 ATF_TC_WITHOUT_HEAD(ktrace__cap_shm_open);
ATF_TC_BODY(ktrace__cap_shm_open,tc)505 ATF_TC_BODY(ktrace__cap_shm_open, tc)
506 {
507 struct ktr_cap_fail violation;
508 sigset_t set = { };
509 pid_t pid;
510 int error;
511
512 /* Block SIGUSR1 so child does not terminate. */
513 ATF_REQUIRE(sigaddset(&set, SIGUSR1) != -1);
514 ATF_REQUIRE(sigprocmask(SIG_BLOCK, &set, NULL) != -1);
515
516 ATF_REQUIRE((pid = fork()) != -1);
517 if (pid == 0) {
518 /* Wait until ktrace has started. */
519 CHILD_REQUIRE(sigwait(&set, &error) != -1);
520 CHILD_REQUIRE_EQ(error, SIGUSR1);
521
522 CHILD_REQUIRE(shm_open("/ktrace_shm", O_RDWR | O_CREAT,
523 0600) != -1);
524 CHILD_REQUIRE(shm_unlink("/ktrace_shm") != -1);
525 exit(0);
526 }
527
528 cap_trace_child(pid, &violation, 1);
529 ATF_REQUIRE_EQ(violation.cap_type, CAPFAIL_NAMEI);
530 error = syscallabi(violation.cap_svflags);
531 ATF_REQUIRE_STREQ(sysdecode_syscallname(error, violation.cap_code),
532 "shm_open2");
533 ATF_REQUIRE_STREQ(violation.cap_data.cap_path, "/ktrace_shm");
534 }
535
536 /*
537 * Make sure that ktrace is disabled upon exec of a setuid binary.
538 */
539 ATF_TC(ktrace__setuid_exec);
ATF_TC_HEAD(ktrace__setuid_exec,tc)540 ATF_TC_HEAD(ktrace__setuid_exec, tc)
541 {
542 atf_tc_set_md_var(tc, "require.user", "unprivileged");
543 }
ATF_TC_BODY(ktrace__setuid_exec,tc)544 ATF_TC_BODY(ktrace__setuid_exec, tc)
545 {
546 struct ktr_header header;
547 struct ktr_syscall *syscall;
548 sigset_t set = { };
549 off_t off, off1;
550 ssize_t n;
551 pid_t pid;
552 int error, fd;
553
554 /* Block SIGUSR1 so child does not terminate. */
555 ATF_REQUIRE(sigaddset(&set, SIGUSR1) != -1);
556 ATF_REQUIRE(sigprocmask(SIG_BLOCK, &set, NULL) != -1);
557
558 ATF_REQUIRE((pid = fork()) != -1);
559 if (pid == 0) {
560 /* Wait until ktrace has started. */
561 CHILD_REQUIRE(sigwait(&set, &error) != -1);
562 CHILD_REQUIRE_EQ(error, SIGUSR1);
563
564 execve("/usr/bin/su", (char *[]){ "su", "whoami", NULL }, NULL);
565 _exit(0);
566 }
567
568 fd = trace_child(pid, KTRFAC_SYSCALL, 1);
569
570 n = read(fd, &header, sizeof(header));
571 ATF_REQUIRE(n >= 0);
572 ATF_REQUIRE_EQ((size_t)n, sizeof(header));
573 ATF_REQUIRE_EQ(header.ktr_pid, pid);
574 ATF_REQUIRE(header.ktr_len >= (int)sizeof(*syscall));
575
576 syscall = xmalloc(header.ktr_len);
577 n = read(fd, syscall, header.ktr_len);
578 ATF_REQUIRE(n >= 0);
579 ATF_REQUIRE_EQ(n, header.ktr_len);
580 if (syscall->ktr_code == SYS_sigwait) {
581 free(syscall);
582
583 /* Skip the sigwait() syscall. */
584 n = read(fd, &header, sizeof(header));
585 ATF_REQUIRE(n >= 0);
586 ATF_REQUIRE_EQ((size_t)n, sizeof(header));
587 ATF_REQUIRE_EQ(header.ktr_pid, pid);
588 ATF_REQUIRE(header.ktr_len >= (int)sizeof(*syscall));
589
590 syscall = xmalloc(header.ktr_len);
591 n = read(fd, syscall, header.ktr_len);
592 ATF_REQUIRE(n >= 0);
593 ATF_REQUIRE_EQ(n, header.ktr_len);
594 }
595 ATF_REQUIRE_EQ(syscall->ktr_code, SYS_execve);
596 free(syscall);
597
598 /* su is setuid root, so this should have been the last entry. */
599 off = lseek(fd, 0, SEEK_CUR);
600 ATF_REQUIRE(off != -1);
601 off1 = lseek(fd, 0, SEEK_END);
602 ATF_REQUIRE(off1 != -1);
603 ATF_REQUIRE_EQ(off, off1);
604
605 ATF_REQUIRE(close(fd) == 0);
606 }
607
ATF_TP_ADD_TCS(tp)608 ATF_TP_ADD_TCS(tp)
609 {
610 ATF_TP_ADD_TC(tp, ktrace__cap_not_capable);
611 ATF_TP_ADD_TC(tp, ktrace__cap_increase_rights);
612 ATF_TP_ADD_TC(tp, ktrace__cap_syscall);
613 ATF_TP_ADD_TC(tp, ktrace__cap_signal);
614 ATF_TP_ADD_TC(tp, ktrace__cap_proto);
615 ATF_TP_ADD_TC(tp, ktrace__cap_sockaddr);
616 ATF_TP_ADD_TC(tp, ktrace__cap_namei);
617 ATF_TP_ADD_TC(tp, ktrace__cap_cpuset);
618 ATF_TP_ADD_TC(tp, ktrace__cap_shm_open);
619 ATF_TP_ADD_TC(tp, ktrace__setuid_exec);
620 return (atf_no_error());
621 }
622