1*49a6e16fSderaadt /* $OpenBSD: t_ptrace.c,v 1.5 2021/12/13 16:56:48 deraadt Exp $ */
2a545a52cSbluhm /* $NetBSD: t_ptrace.c,v 1.4 2018/05/14 12:44:40 kamil Exp $ */
3a545a52cSbluhm
4a545a52cSbluhm /*-
5a545a52cSbluhm * Copyright (c) 2016 The NetBSD Foundation, Inc.
6a545a52cSbluhm * All rights reserved.
7a545a52cSbluhm *
8a545a52cSbluhm * Redistribution and use in source and binary forms, with or without
9a545a52cSbluhm * modification, are permitted provided that the following conditions
10a545a52cSbluhm * are met:
11a545a52cSbluhm * 1. Redistributions of source code must retain the above copyright
12a545a52cSbluhm * notice, this list of conditions and the following disclaimer.
13a545a52cSbluhm * 2. Redistributions in binary form must reproduce the above copyright
14a545a52cSbluhm * notice, this list of conditions and the following disclaimer in the
15a545a52cSbluhm * documentation and/or other materials provided with the distribution.
16a545a52cSbluhm *
17a545a52cSbluhm * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
18a545a52cSbluhm * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19a545a52cSbluhm * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20a545a52cSbluhm * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
21a545a52cSbluhm * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22a545a52cSbluhm * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23a545a52cSbluhm * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24a545a52cSbluhm * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25a545a52cSbluhm * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26a545a52cSbluhm * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27a545a52cSbluhm * POSSIBILITY OF SUCH DAMAGE.
28a545a52cSbluhm */
29a545a52cSbluhm
30a545a52cSbluhm #include "macros.h"
31a545a52cSbluhm
32a545a52cSbluhm #include <sys/types.h>
33a545a52cSbluhm #include <sys/ptrace.h>
34a545a52cSbluhm #include <sys/stat.h>
35a545a52cSbluhm #include <sys/sysctl.h>
36a545a52cSbluhm #include <err.h>
37a545a52cSbluhm #include <errno.h>
38*49a6e16fSderaadt #include <limits.h>
39a545a52cSbluhm #include <unistd.h>
40a545a52cSbluhm
41a545a52cSbluhm #include "atf-c.h"
42a545a52cSbluhm
43a545a52cSbluhm #include "h_macros.h"
44a545a52cSbluhm
45a545a52cSbluhm /*
46a545a52cSbluhm * A child process cannot call atf functions and expect them to magically
47a545a52cSbluhm * work like in the parent.
48a545a52cSbluhm * The printf(3) messaging from a child will not work out of the box as well
49a545a52cSbluhm * without estabilishing a communication protocol with its parent. To not
50a545a52cSbluhm * overcomplicate the tests - do not log from a child and use err(3)/errx(3)
51a545a52cSbluhm * wrapped with FORKEE_ASSERT()/FORKEE_ASSERTX() as that is guaranteed to work.
52a545a52cSbluhm */
53a545a52cSbluhm #define FORKEE_ASSERTX(x) \
54a545a52cSbluhm do { \
55a545a52cSbluhm int ret = (x); \
56a545a52cSbluhm if (!ret) \
57a545a52cSbluhm errx(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: %s", \
58a545a52cSbluhm __FILE__, __LINE__, __func__, #x); \
59a545a52cSbluhm } while (0)
60a545a52cSbluhm
61a545a52cSbluhm #define FORKEE_ASSERT(x) \
62a545a52cSbluhm do { \
63a545a52cSbluhm int ret = (x); \
64a545a52cSbluhm if (!ret) \
65a545a52cSbluhm err(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: %s", \
66a545a52cSbluhm __FILE__, __LINE__, __func__, #x); \
67a545a52cSbluhm } while (0)
68a545a52cSbluhm
69a545a52cSbluhm ATF_TC(attach_pid0);
ATF_TC_HEAD(attach_pid0,tc)70a545a52cSbluhm ATF_TC_HEAD(attach_pid0, tc)
71a545a52cSbluhm {
72a545a52cSbluhm atf_tc_set_md_var(tc, "descr",
73a545a52cSbluhm "Assert that a debugger cannot attach to PID 0");
74a545a52cSbluhm }
75a545a52cSbluhm
ATF_TC_BODY(attach_pid0,tc)76a545a52cSbluhm ATF_TC_BODY(attach_pid0, tc)
77a545a52cSbluhm {
78a545a52cSbluhm errno = 0;
79a545a52cSbluhm ATF_REQUIRE_ERRNO(EPERM, ptrace(PT_ATTACH, 0, NULL, 0) == -1);
80a545a52cSbluhm }
81a545a52cSbluhm
82a545a52cSbluhm ATF_TC(attach_pid1);
ATF_TC_HEAD(attach_pid1,tc)83a545a52cSbluhm ATF_TC_HEAD(attach_pid1, tc)
84a545a52cSbluhm {
85a545a52cSbluhm atf_tc_set_md_var(tc, "descr",
86a545a52cSbluhm "Assert that a debugger cannot attach to PID 1 (as non-root)");
87a545a52cSbluhm
88a545a52cSbluhm atf_tc_set_md_var(tc, "require.user", "unprivileged");
89a545a52cSbluhm }
90a545a52cSbluhm
ATF_TC_BODY(attach_pid1,tc)91a545a52cSbluhm ATF_TC_BODY(attach_pid1, tc)
92a545a52cSbluhm {
93a545a52cSbluhm ATF_REQUIRE_ERRNO(EPERM, ptrace(PT_ATTACH, 1, NULL, 0) == -1);
94a545a52cSbluhm }
95a545a52cSbluhm
96a545a52cSbluhm ATF_TC(attach_pid1_securelevel);
ATF_TC_HEAD(attach_pid1_securelevel,tc)97a545a52cSbluhm ATF_TC_HEAD(attach_pid1_securelevel, tc)
98a545a52cSbluhm {
99a545a52cSbluhm atf_tc_set_md_var(tc, "descr",
100a545a52cSbluhm "Assert that a debugger cannot attach to PID 1 with "
101a545a52cSbluhm "securelevel >= 0 (as root)");
102a545a52cSbluhm
103a545a52cSbluhm atf_tc_set_md_var(tc, "require.user", "root");
104a545a52cSbluhm }
105a545a52cSbluhm
ATF_TC_BODY(attach_pid1_securelevel,tc)106a545a52cSbluhm ATF_TC_BODY(attach_pid1_securelevel, tc)
107a545a52cSbluhm {
108a545a52cSbluhm int level;
109a545a52cSbluhm size_t len = sizeof(level);
110a545a52cSbluhm
111a545a52cSbluhm ATF_REQUIRE(sysctlbyname("kern.securelevel", &level, &len, NULL, 0)
112a545a52cSbluhm != -1);
113a545a52cSbluhm
114a545a52cSbluhm if (level < 0) {
115a545a52cSbluhm atf_tc_skip("Test must be run with securelevel >= 0");
116a545a52cSbluhm }
117a545a52cSbluhm
118a545a52cSbluhm ATF_REQUIRE_ERRNO(EPERM, ptrace(PT_ATTACH, 1, NULL, 0) == -1);
119a545a52cSbluhm }
120a545a52cSbluhm
121a545a52cSbluhm ATF_TC(attach_self);
ATF_TC_HEAD(attach_self,tc)122a545a52cSbluhm ATF_TC_HEAD(attach_self, tc)
123a545a52cSbluhm {
124a545a52cSbluhm atf_tc_set_md_var(tc, "descr",
125a545a52cSbluhm "Assert that a debugger cannot attach to self (as it's nonsense)");
126a545a52cSbluhm }
127a545a52cSbluhm
ATF_TC_BODY(attach_self,tc)128a545a52cSbluhm ATF_TC_BODY(attach_self, tc)
129a545a52cSbluhm {
130a545a52cSbluhm ATF_REQUIRE_ERRNO(EINVAL, ptrace(PT_ATTACH, getpid(), NULL, 0) == -1);
131a545a52cSbluhm }
132a545a52cSbluhm
133a545a52cSbluhm ATF_TC(attach_chroot);
ATF_TC_HEAD(attach_chroot,tc)134a545a52cSbluhm ATF_TC_HEAD(attach_chroot, tc)
135a545a52cSbluhm {
136a545a52cSbluhm atf_tc_set_md_var(tc, "descr",
137a545a52cSbluhm "Assert that a debugger cannot trace another process unless the "
138a545a52cSbluhm "process's root directory is at or below the tracing process's "
139a545a52cSbluhm "root");
140a545a52cSbluhm
141a545a52cSbluhm atf_tc_set_md_var(tc, "require.user", "root");
142a545a52cSbluhm }
143a545a52cSbluhm
ATF_TC_BODY(attach_chroot,tc)144a545a52cSbluhm ATF_TC_BODY(attach_chroot, tc)
145a545a52cSbluhm {
146a545a52cSbluhm char buf[PATH_MAX];
147a545a52cSbluhm pid_t child;
148a545a52cSbluhm int fds_toparent[2], fds_fromparent[2];
149a545a52cSbluhm int rv;
150a545a52cSbluhm uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */
151a545a52cSbluhm
152a545a52cSbluhm (void)memset(buf, '\0', sizeof(buf));
153a545a52cSbluhm ATF_REQUIRE(getcwd(buf, sizeof(buf)) != NULL);
154a545a52cSbluhm (void)strlcat(buf, "/dir", sizeof(buf));
155a545a52cSbluhm
156a545a52cSbluhm ATF_REQUIRE(mkdir(buf, 0500) == 0);
157a545a52cSbluhm ATF_REQUIRE(chdir(buf) == 0);
158a545a52cSbluhm
159a545a52cSbluhm ATF_REQUIRE(pipe(fds_toparent) == 0);
160a545a52cSbluhm ATF_REQUIRE(pipe(fds_fromparent) == 0);
161a545a52cSbluhm child = atf_utils_fork();
162a545a52cSbluhm if (child == 0) {
163a545a52cSbluhm FORKEE_ASSERT(close(fds_toparent[0]) == 0);
164a545a52cSbluhm FORKEE_ASSERT(close(fds_fromparent[1]) == 0);
165a545a52cSbluhm
166a545a52cSbluhm FORKEE_ASSERT(chroot(buf) == 0);
167a545a52cSbluhm
168a545a52cSbluhm rv = write(fds_toparent[1], &msg, sizeof(msg));
169a545a52cSbluhm FORKEE_ASSERTX(rv == sizeof(msg));
170a545a52cSbluhm
1717496d4e5Sbluhm #ifdef __OpenBSD__
172e0b87bf8Smpi ATF_REQUIRE_ERRNO(EINVAL,
1737496d4e5Sbluhm #else
1747496d4e5Sbluhm ATF_REQUIRE_ERRNO(EPERM,
1757496d4e5Sbluhm #endif
176a545a52cSbluhm ptrace(PT_ATTACH, getppid(), NULL, 0) == -1);
177a545a52cSbluhm
178a545a52cSbluhm rv = read(fds_fromparent[0], &msg, sizeof(msg));
179a545a52cSbluhm FORKEE_ASSERTX(rv == sizeof(msg));
180a545a52cSbluhm
181a545a52cSbluhm _exit(0);
182a545a52cSbluhm }
183a545a52cSbluhm ATF_REQUIRE(close(fds_toparent[1]) == 0);
184a545a52cSbluhm ATF_REQUIRE(close(fds_fromparent[0]) == 0);
185a545a52cSbluhm
1864ae13c99Sbluhm printf("Waiting for chrooting of the child PID %d\n", child);
187a545a52cSbluhm rv = read(fds_toparent[0], &msg, sizeof(msg));
188a545a52cSbluhm ATF_REQUIRE(rv == sizeof(msg));
189a545a52cSbluhm
190a545a52cSbluhm printf("Child is ready, it will try to PT_ATTACH to parent\n");
191a545a52cSbluhm rv = write(fds_fromparent[1], &msg, sizeof(msg));
192a545a52cSbluhm ATF_REQUIRE(rv == sizeof(msg));
193a545a52cSbluhm
194a545a52cSbluhm printf("fds_fromparent is no longer needed - close it\n");
195a545a52cSbluhm ATF_REQUIRE(close(fds_fromparent[1]) == 0);
196a545a52cSbluhm
197a545a52cSbluhm printf("fds_toparent is no longer needed - close it\n");
198a545a52cSbluhm ATF_REQUIRE(close(fds_toparent[0]) == 0);
199a545a52cSbluhm }
200a545a52cSbluhm
201a545a52cSbluhm ATF_TC(traceme_twice);
ATF_TC_HEAD(traceme_twice,tc)202a545a52cSbluhm ATF_TC_HEAD(traceme_twice, tc)
203a545a52cSbluhm {
204a545a52cSbluhm atf_tc_set_md_var(tc, "descr",
205a545a52cSbluhm "Assert that a process cannot mark its parent a debugger twice");
206a545a52cSbluhm }
207a545a52cSbluhm
ATF_TC_BODY(traceme_twice,tc)208a545a52cSbluhm ATF_TC_BODY(traceme_twice, tc)
209a545a52cSbluhm {
210a545a52cSbluhm
2114ae13c99Sbluhm printf("Mark the parent process (PID %d) a debugger of PID %d\n",
212a545a52cSbluhm getppid(), getpid());
213a545a52cSbluhm ATF_REQUIRE(ptrace(PT_TRACE_ME, 0, NULL, 0) == 0);
214a545a52cSbluhm
2154ae13c99Sbluhm printf("Mark the parent process (PID %d) a debugger of PID %d again\n",
216a545a52cSbluhm getppid(), getpid());
217a545a52cSbluhm ATF_REQUIRE_ERRNO(EBUSY, ptrace(PT_TRACE_ME, 0, NULL, 0) == -1);
218a545a52cSbluhm }
219a545a52cSbluhm
ATF_TP_ADD_TCS(tp)220a545a52cSbluhm ATF_TP_ADD_TCS(tp)
221a545a52cSbluhm {
222a545a52cSbluhm setvbuf(stdout, NULL, _IONBF, 0);
223a545a52cSbluhm setvbuf(stderr, NULL, _IONBF, 0);
224a545a52cSbluhm ATF_TP_ADD_TC(tp, attach_pid0);
225a545a52cSbluhm ATF_TP_ADD_TC(tp, attach_pid1);
226a545a52cSbluhm ATF_TP_ADD_TC(tp, attach_pid1_securelevel);
227a545a52cSbluhm ATF_TP_ADD_TC(tp, attach_self);
228a545a52cSbluhm ATF_TP_ADD_TC(tp, attach_chroot);
229a545a52cSbluhm ATF_TP_ADD_TC(tp, traceme_twice);
230a545a52cSbluhm
231a545a52cSbluhm return atf_no_error();
232a545a52cSbluhm }
233