ptrace_test.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Landlock tests - Ptrace
  4. *
  5. * Copyright © 2017-2020 Mickaël Salaün <[email protected]>
  6. * Copyright © 2019-2020 ANSSI
  7. */
  8. #define _GNU_SOURCE
  9. #include <errno.h>
  10. #include <fcntl.h>
  11. #include <linux/landlock.h>
  12. #include <signal.h>
  13. #include <sys/prctl.h>
  14. #include <sys/ptrace.h>
  15. #include <sys/types.h>
  16. #include <sys/wait.h>
  17. #include <unistd.h>
  18. #include "common.h"
  19. /* Copied from security/yama/yama_lsm.c */
  20. #define YAMA_SCOPE_DISABLED 0
  21. #define YAMA_SCOPE_RELATIONAL 1
  22. #define YAMA_SCOPE_CAPABILITY 2
  23. #define YAMA_SCOPE_NO_ATTACH 3
  24. static void create_domain(struct __test_metadata *const _metadata)
  25. {
  26. int ruleset_fd;
  27. struct landlock_ruleset_attr ruleset_attr = {
  28. .handled_access_fs = LANDLOCK_ACCESS_FS_MAKE_BLOCK,
  29. };
  30. ruleset_fd =
  31. landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
  32. EXPECT_LE(0, ruleset_fd)
  33. {
  34. TH_LOG("Failed to create a ruleset: %s", strerror(errno));
  35. }
  36. EXPECT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0));
  37. EXPECT_EQ(0, landlock_restrict_self(ruleset_fd, 0));
  38. EXPECT_EQ(0, close(ruleset_fd));
  39. }
  40. static int test_ptrace_read(const pid_t pid)
  41. {
  42. static const char path_template[] = "/proc/%d/environ";
  43. char procenv_path[sizeof(path_template) + 10];
  44. int procenv_path_size, fd;
  45. procenv_path_size = snprintf(procenv_path, sizeof(procenv_path),
  46. path_template, pid);
  47. if (procenv_path_size >= sizeof(procenv_path))
  48. return E2BIG;
  49. fd = open(procenv_path, O_RDONLY | O_CLOEXEC);
  50. if (fd < 0)
  51. return errno;
  52. /*
  53. * Mixing error codes from close(2) and open(2) should not lead to any
  54. * (access type) confusion for this test.
  55. */
  56. if (close(fd) != 0)
  57. return errno;
  58. return 0;
  59. }
  60. static int get_yama_ptrace_scope(void)
  61. {
  62. int ret;
  63. char buf[2] = {};
  64. const int fd = open("/proc/sys/kernel/yama/ptrace_scope", O_RDONLY);
  65. if (fd < 0)
  66. return 0;
  67. if (read(fd, buf, 1) < 0) {
  68. close(fd);
  69. return -1;
  70. }
  71. ret = atoi(buf);
  72. close(fd);
  73. return ret;
  74. }
  75. /* clang-format off */
  76. FIXTURE(hierarchy) {};
  77. /* clang-format on */
  78. FIXTURE_VARIANT(hierarchy)
  79. {
  80. const bool domain_both;
  81. const bool domain_parent;
  82. const bool domain_child;
  83. };
  84. /*
  85. * Test multiple tracing combinations between a parent process P1 and a child
  86. * process P2.
  87. *
  88. * Yama's scoped ptrace is presumed disabled. If enabled, this optional
  89. * restriction is enforced in addition to any Landlock check, which means that
  90. * all P2 requests to trace P1 would be denied.
  91. */
  92. /*
  93. * No domain
  94. *
  95. * P1-. P1 -> P2 : allow
  96. * \ P2 -> P1 : allow
  97. * 'P2
  98. */
  99. /* clang-format off */
  100. FIXTURE_VARIANT_ADD(hierarchy, allow_without_domain) {
  101. /* clang-format on */
  102. .domain_both = false,
  103. .domain_parent = false,
  104. .domain_child = false,
  105. };
  106. /*
  107. * Child domain
  108. *
  109. * P1--. P1 -> P2 : allow
  110. * \ P2 -> P1 : deny
  111. * .'-----.
  112. * | P2 |
  113. * '------'
  114. */
  115. /* clang-format off */
  116. FIXTURE_VARIANT_ADD(hierarchy, allow_with_one_domain) {
  117. /* clang-format on */
  118. .domain_both = false,
  119. .domain_parent = false,
  120. .domain_child = true,
  121. };
  122. /*
  123. * Parent domain
  124. * .------.
  125. * | P1 --. P1 -> P2 : deny
  126. * '------' \ P2 -> P1 : allow
  127. * '
  128. * P2
  129. */
  130. /* clang-format off */
  131. FIXTURE_VARIANT_ADD(hierarchy, deny_with_parent_domain) {
  132. /* clang-format on */
  133. .domain_both = false,
  134. .domain_parent = true,
  135. .domain_child = false,
  136. };
  137. /*
  138. * Parent + child domain (siblings)
  139. * .------.
  140. * | P1 ---. P1 -> P2 : deny
  141. * '------' \ P2 -> P1 : deny
  142. * .---'--.
  143. * | P2 |
  144. * '------'
  145. */
  146. /* clang-format off */
  147. FIXTURE_VARIANT_ADD(hierarchy, deny_with_sibling_domain) {
  148. /* clang-format on */
  149. .domain_both = false,
  150. .domain_parent = true,
  151. .domain_child = true,
  152. };
  153. /*
  154. * Same domain (inherited)
  155. * .-------------.
  156. * | P1----. | P1 -> P2 : allow
  157. * | \ | P2 -> P1 : allow
  158. * | ' |
  159. * | P2 |
  160. * '-------------'
  161. */
  162. /* clang-format off */
  163. FIXTURE_VARIANT_ADD(hierarchy, allow_sibling_domain) {
  164. /* clang-format on */
  165. .domain_both = true,
  166. .domain_parent = false,
  167. .domain_child = false,
  168. };
  169. /*
  170. * Inherited + child domain
  171. * .-----------------.
  172. * | P1----. | P1 -> P2 : allow
  173. * | \ | P2 -> P1 : deny
  174. * | .-'----. |
  175. * | | P2 | |
  176. * | '------' |
  177. * '-----------------'
  178. */
  179. /* clang-format off */
  180. FIXTURE_VARIANT_ADD(hierarchy, allow_with_nested_domain) {
  181. /* clang-format on */
  182. .domain_both = true,
  183. .domain_parent = false,
  184. .domain_child = true,
  185. };
  186. /*
  187. * Inherited + parent domain
  188. * .-----------------.
  189. * |.------. | P1 -> P2 : deny
  190. * || P1 ----. | P2 -> P1 : allow
  191. * |'------' \ |
  192. * | ' |
  193. * | P2 |
  194. * '-----------------'
  195. */
  196. /* clang-format off */
  197. FIXTURE_VARIANT_ADD(hierarchy, deny_with_nested_and_parent_domain) {
  198. /* clang-format on */
  199. .domain_both = true,
  200. .domain_parent = true,
  201. .domain_child = false,
  202. };
  203. /*
  204. * Inherited + parent and child domain (siblings)
  205. * .-----------------.
  206. * | .------. | P1 -> P2 : deny
  207. * | | P1 . | P2 -> P1 : deny
  208. * | '------'\ |
  209. * | \ |
  210. * | .--'---. |
  211. * | | P2 | |
  212. * | '------' |
  213. * '-----------------'
  214. */
  215. /* clang-format off */
  216. FIXTURE_VARIANT_ADD(hierarchy, deny_with_forked_domain) {
  217. /* clang-format on */
  218. .domain_both = true,
  219. .domain_parent = true,
  220. .domain_child = true,
  221. };
  222. FIXTURE_SETUP(hierarchy)
  223. {
  224. }
  225. FIXTURE_TEARDOWN(hierarchy)
  226. {
  227. }
  228. /* Test PTRACE_TRACEME and PTRACE_ATTACH for parent and child. */
  229. TEST_F(hierarchy, trace)
  230. {
  231. pid_t child, parent;
  232. int status, err_proc_read;
  233. int pipe_child[2], pipe_parent[2];
  234. int yama_ptrace_scope;
  235. char buf_parent;
  236. long ret;
  237. bool can_read_child, can_trace_child, can_read_parent, can_trace_parent;
  238. yama_ptrace_scope = get_yama_ptrace_scope();
  239. ASSERT_LE(0, yama_ptrace_scope);
  240. if (yama_ptrace_scope > YAMA_SCOPE_DISABLED)
  241. TH_LOG("Incomplete tests due to Yama restrictions (scope %d)",
  242. yama_ptrace_scope);
  243. /*
  244. * can_read_child is true if a parent process can read its child
  245. * process, which is only the case when the parent process is not
  246. * isolated from the child with a dedicated Landlock domain.
  247. */
  248. can_read_child = !variant->domain_parent;
  249. /*
  250. * can_trace_child is true if a parent process can trace its child
  251. * process. This depends on two conditions:
  252. * - The parent process is not isolated from the child with a dedicated
  253. * Landlock domain.
  254. * - Yama allows tracing children (up to YAMA_SCOPE_RELATIONAL).
  255. */
  256. can_trace_child = can_read_child &&
  257. yama_ptrace_scope <= YAMA_SCOPE_RELATIONAL;
  258. /*
  259. * can_read_parent is true if a child process can read its parent
  260. * process, which is only the case when the child process is not
  261. * isolated from the parent with a dedicated Landlock domain.
  262. */
  263. can_read_parent = !variant->domain_child;
  264. /*
  265. * can_trace_parent is true if a child process can trace its parent
  266. * process. This depends on two conditions:
  267. * - The child process is not isolated from the parent with a dedicated
  268. * Landlock domain.
  269. * - Yama is disabled (YAMA_SCOPE_DISABLED).
  270. */
  271. can_trace_parent = can_read_parent &&
  272. yama_ptrace_scope <= YAMA_SCOPE_DISABLED;
  273. /*
  274. * Removes all effective and permitted capabilities to not interfere
  275. * with cap_ptrace_access_check() in case of PTRACE_MODE_FSCREDS.
  276. */
  277. drop_caps(_metadata);
  278. parent = getpid();
  279. ASSERT_EQ(0, pipe2(pipe_child, O_CLOEXEC));
  280. ASSERT_EQ(0, pipe2(pipe_parent, O_CLOEXEC));
  281. if (variant->domain_both) {
  282. create_domain(_metadata);
  283. if (!_metadata->passed)
  284. /* Aborts before forking. */
  285. return;
  286. }
  287. child = fork();
  288. ASSERT_LE(0, child);
  289. if (child == 0) {
  290. char buf_child;
  291. ASSERT_EQ(0, close(pipe_parent[1]));
  292. ASSERT_EQ(0, close(pipe_child[0]));
  293. if (variant->domain_child)
  294. create_domain(_metadata);
  295. /* Waits for the parent to be in a domain, if any. */
  296. ASSERT_EQ(1, read(pipe_parent[0], &buf_child, 1));
  297. /* Tests PTRACE_MODE_READ on the parent. */
  298. err_proc_read = test_ptrace_read(parent);
  299. if (can_read_parent) {
  300. EXPECT_EQ(0, err_proc_read);
  301. } else {
  302. EXPECT_EQ(EACCES, err_proc_read);
  303. }
  304. /* Tests PTRACE_ATTACH on the parent. */
  305. ret = ptrace(PTRACE_ATTACH, parent, NULL, 0);
  306. if (can_trace_parent) {
  307. EXPECT_EQ(0, ret);
  308. } else {
  309. EXPECT_EQ(-1, ret);
  310. EXPECT_EQ(EPERM, errno);
  311. }
  312. if (ret == 0) {
  313. ASSERT_EQ(parent, waitpid(parent, &status, 0));
  314. ASSERT_EQ(1, WIFSTOPPED(status));
  315. ASSERT_EQ(0, ptrace(PTRACE_DETACH, parent, NULL, 0));
  316. }
  317. /* Tests child PTRACE_TRACEME. */
  318. ret = ptrace(PTRACE_TRACEME);
  319. if (can_trace_child) {
  320. EXPECT_EQ(0, ret);
  321. } else {
  322. EXPECT_EQ(-1, ret);
  323. EXPECT_EQ(EPERM, errno);
  324. }
  325. /*
  326. * Signals that the PTRACE_ATTACH test is done and the
  327. * PTRACE_TRACEME test is ongoing.
  328. */
  329. ASSERT_EQ(1, write(pipe_child[1], ".", 1));
  330. if (can_trace_child) {
  331. ASSERT_EQ(0, raise(SIGSTOP));
  332. }
  333. /* Waits for the parent PTRACE_ATTACH test. */
  334. ASSERT_EQ(1, read(pipe_parent[0], &buf_child, 1));
  335. _exit(_metadata->passed ? EXIT_SUCCESS : EXIT_FAILURE);
  336. return;
  337. }
  338. ASSERT_EQ(0, close(pipe_child[1]));
  339. ASSERT_EQ(0, close(pipe_parent[0]));
  340. if (variant->domain_parent)
  341. create_domain(_metadata);
  342. /* Signals that the parent is in a domain, if any. */
  343. ASSERT_EQ(1, write(pipe_parent[1], ".", 1));
  344. /*
  345. * Waits for the child to test PTRACE_ATTACH on the parent and start
  346. * testing PTRACE_TRACEME.
  347. */
  348. ASSERT_EQ(1, read(pipe_child[0], &buf_parent, 1));
  349. /* Tests child PTRACE_TRACEME. */
  350. if (can_trace_child) {
  351. ASSERT_EQ(child, waitpid(child, &status, 0));
  352. ASSERT_EQ(1, WIFSTOPPED(status));
  353. ASSERT_EQ(0, ptrace(PTRACE_DETACH, child, NULL, 0));
  354. } else {
  355. /* The child should not be traced by the parent. */
  356. EXPECT_EQ(-1, ptrace(PTRACE_DETACH, child, NULL, 0));
  357. EXPECT_EQ(ESRCH, errno);
  358. }
  359. /* Tests PTRACE_MODE_READ on the child. */
  360. err_proc_read = test_ptrace_read(child);
  361. if (can_read_child) {
  362. EXPECT_EQ(0, err_proc_read);
  363. } else {
  364. EXPECT_EQ(EACCES, err_proc_read);
  365. }
  366. /* Tests PTRACE_ATTACH on the child. */
  367. ret = ptrace(PTRACE_ATTACH, child, NULL, 0);
  368. if (can_trace_child) {
  369. EXPECT_EQ(0, ret);
  370. } else {
  371. EXPECT_EQ(-1, ret);
  372. EXPECT_EQ(EPERM, errno);
  373. }
  374. if (ret == 0) {
  375. ASSERT_EQ(child, waitpid(child, &status, 0));
  376. ASSERT_EQ(1, WIFSTOPPED(status));
  377. ASSERT_EQ(0, ptrace(PTRACE_DETACH, child, NULL, 0));
  378. }
  379. /* Signals that the parent PTRACE_ATTACH test is done. */
  380. ASSERT_EQ(1, write(pipe_parent[1], ".", 1));
  381. ASSERT_EQ(child, waitpid(child, &status, 0));
  382. if (WIFSIGNALED(status) || !WIFEXITED(status) ||
  383. WEXITSTATUS(status) != EXIT_SUCCESS)
  384. _metadata->passed = 0;
  385. }
  386. TEST_HARNESS_MAIN