1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Write in a pipe, wait, sandbox itself, test sandboxing, and wait again.
4 *
5 * Used by audit_exec.flags from audit_test.c
6 *
7 * Copyright © 2024-2025 Microsoft Corporation
8 */
9
10 #define _GNU_SOURCE
11 #include <fcntl.h>
12 #include <linux/landlock.h>
13 #include <linux/prctl.h>
14 #include <signal.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <sys/prctl.h>
18 #include <unistd.h>
19
20 #include "wrappers.h"
21
sync_with(int pipe_child,int pipe_parent)22 static int sync_with(int pipe_child, int pipe_parent)
23 {
24 char buf;
25
26 /* Signals that we are waiting. */
27 if (write(pipe_child, ".", 1) != 1) {
28 perror("Failed to write to first argument");
29 return 1;
30 }
31
32 /* Waits for the parent do its test. */
33 if (read(pipe_parent, &buf, 1) != 1) {
34 perror("Failed to write to the second argument");
35 return 1;
36 }
37
38 return 0;
39 }
40
main(int argc,char * argv[])41 int main(int argc, char *argv[])
42 {
43 const struct landlock_ruleset_attr layer2 = {
44 .handled_access_fs = LANDLOCK_ACCESS_FS_READ_DIR,
45 };
46 const struct landlock_ruleset_attr layer3 = {
47 .scoped = LANDLOCK_SCOPE_SIGNAL,
48 };
49 int err, pipe_child, pipe_parent, ruleset_fd;
50
51 /* The first argument must be the file descriptor number of a pipe. */
52 if (argc != 3) {
53 fprintf(stderr, "Wrong number of arguments (not two)\n");
54 return 1;
55 }
56
57 pipe_child = atoi(argv[1]);
58 pipe_parent = atoi(argv[2]);
59 /* PR_SET_NO_NEW_PRIVS already set by parent. */
60
61 /* First step to test parent's layer1. */
62 err = sync_with(pipe_child, pipe_parent);
63 if (err)
64 return err;
65
66 /* Tries to send a signal, denied by layer1. */
67 if (!kill(getppid(), 0)) {
68 fprintf(stderr, "Successfully sent a signal to the parent");
69 return 1;
70 }
71
72 /* Second step to test parent's layer1 and our layer2. */
73 err = sync_with(pipe_child, pipe_parent);
74 if (err)
75 return err;
76
77 ruleset_fd = landlock_create_ruleset(&layer2, sizeof(layer2), 0);
78 if (ruleset_fd < 0) {
79 perror("Failed to create the layer2 ruleset");
80 return 1;
81 }
82
83 if (landlock_restrict_self(ruleset_fd, 0)) {
84 perror("Failed to restrict self");
85 return 1;
86 }
87 close(ruleset_fd);
88
89 /* Tries to send a signal, denied by layer1. */
90 if (!kill(getppid(), 0)) {
91 fprintf(stderr, "Successfully sent a signal to the parent");
92 return 1;
93 }
94
95 /* Tries to open ., denied by layer2. */
96 if (open("/", O_RDONLY | O_DIRECTORY | O_CLOEXEC) >= 0) {
97 fprintf(stderr, "Successfully opened /");
98 return 1;
99 }
100
101 /* Third step to test our layer2 and layer3. */
102 err = sync_with(pipe_child, pipe_parent);
103 if (err)
104 return err;
105
106 ruleset_fd = landlock_create_ruleset(&layer3, sizeof(layer3), 0);
107 if (ruleset_fd < 0) {
108 perror("Failed to create the layer3 ruleset");
109 return 1;
110 }
111
112 if (landlock_restrict_self(ruleset_fd, 0)) {
113 perror("Failed to restrict self");
114 return 1;
115 }
116 close(ruleset_fd);
117
118 /* Tries to open ., denied by layer2. */
119 if (open("/", O_RDONLY | O_DIRECTORY | O_CLOEXEC) >= 0) {
120 fprintf(stderr, "Successfully opened /");
121 return 1;
122 }
123
124 /* Tries to send a signal, denied by layer3. */
125 if (!kill(getppid(), 0)) {
126 fprintf(stderr, "Successfully sent a signal to the parent");
127 return 1;
128 }
129
130 return 0;
131 }
132