1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Landlock tests - Common user space base
4 *
5 * Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net>
6 * Copyright © 2019-2020 ANSSI
7 */
8
9 #define _GNU_SOURCE
10 #include <errno.h>
11 #include <fcntl.h>
12 #include <linux/landlock.h>
13 #include <string.h>
14 #include <sys/prctl.h>
15 #include <sys/socket.h>
16 #include <sys/types.h>
17
18 #include "common.h"
19
20 #ifndef O_PATH
21 #define O_PATH 010000000
22 #endif
23
TEST(inconsistent_attr)24 TEST(inconsistent_attr) {
25 const long page_size = sysconf(_SC_PAGESIZE);
26 char *const buf = malloc(page_size + 1);
27 struct landlock_ruleset_attr *const ruleset_attr = (void *)buf;
28
29 ASSERT_NE(NULL, buf);
30
31 /* Checks copy_from_user(). */
32 ASSERT_EQ(-1, landlock_create_ruleset(ruleset_attr, 0, 0));
33 /* The size if less than sizeof(struct landlock_attr_enforce). */
34 ASSERT_EQ(EINVAL, errno);
35 ASSERT_EQ(-1, landlock_create_ruleset(ruleset_attr, 1, 0));
36 ASSERT_EQ(EINVAL, errno);
37
38 ASSERT_EQ(-1, landlock_create_ruleset(NULL, 1, 0));
39 /* The size if less than sizeof(struct landlock_attr_enforce). */
40 ASSERT_EQ(EFAULT, errno);
41
42 ASSERT_EQ(-1, landlock_create_ruleset(NULL,
43 sizeof(struct landlock_ruleset_attr), 0));
44 ASSERT_EQ(EFAULT, errno);
45
46 ASSERT_EQ(-1, landlock_create_ruleset(ruleset_attr, page_size + 1, 0));
47 ASSERT_EQ(E2BIG, errno);
48
49 ASSERT_EQ(-1, landlock_create_ruleset(ruleset_attr,
50 sizeof(struct landlock_ruleset_attr), 0));
51 ASSERT_EQ(ENOMSG, errno);
52 ASSERT_EQ(-1, landlock_create_ruleset(ruleset_attr, page_size, 0));
53 ASSERT_EQ(ENOMSG, errno);
54
55 /* Checks non-zero value. */
56 buf[page_size - 2] = '.';
57 ASSERT_EQ(-1, landlock_create_ruleset(ruleset_attr, page_size, 0));
58 ASSERT_EQ(E2BIG, errno);
59
60 ASSERT_EQ(-1, landlock_create_ruleset(ruleset_attr, page_size + 1, 0));
61 ASSERT_EQ(E2BIG, errno);
62
63 free(buf);
64 }
65
TEST(abi_version)66 TEST(abi_version) {
67 const struct landlock_ruleset_attr ruleset_attr = {
68 .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE,
69 };
70 ASSERT_EQ(1, landlock_create_ruleset(NULL, 0,
71 LANDLOCK_CREATE_RULESET_VERSION));
72
73 ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 0,
74 LANDLOCK_CREATE_RULESET_VERSION));
75 ASSERT_EQ(EINVAL, errno);
76
77 ASSERT_EQ(-1, landlock_create_ruleset(NULL, sizeof(ruleset_attr),
78 LANDLOCK_CREATE_RULESET_VERSION));
79 ASSERT_EQ(EINVAL, errno);
80
81 ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr,
82 sizeof(ruleset_attr),
83 LANDLOCK_CREATE_RULESET_VERSION));
84 ASSERT_EQ(EINVAL, errno);
85
86 ASSERT_EQ(-1, landlock_create_ruleset(NULL, 0,
87 LANDLOCK_CREATE_RULESET_VERSION | 1 << 31));
88 ASSERT_EQ(EINVAL, errno);
89 }
90
TEST(inval_create_ruleset_flags)91 TEST(inval_create_ruleset_flags) {
92 const int last_flag = LANDLOCK_CREATE_RULESET_VERSION;
93 const int invalid_flag = last_flag << 1;
94 const struct landlock_ruleset_attr ruleset_attr = {
95 .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE,
96 };
97
98 ASSERT_EQ(-1, landlock_create_ruleset(NULL, 0, invalid_flag));
99 ASSERT_EQ(EINVAL, errno);
100
101 ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 0, invalid_flag));
102 ASSERT_EQ(EINVAL, errno);
103
104 ASSERT_EQ(-1, landlock_create_ruleset(NULL, sizeof(ruleset_attr),
105 invalid_flag));
106 ASSERT_EQ(EINVAL, errno);
107
108 ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr,
109 sizeof(ruleset_attr), invalid_flag));
110 ASSERT_EQ(EINVAL, errno);
111 }
112
TEST(empty_path_beneath_attr)113 TEST(empty_path_beneath_attr) {
114 const struct landlock_ruleset_attr ruleset_attr = {
115 .handled_access_fs = LANDLOCK_ACCESS_FS_EXECUTE,
116 };
117 const int ruleset_fd = landlock_create_ruleset(&ruleset_attr,
118 sizeof(ruleset_attr), 0);
119
120 ASSERT_LE(0, ruleset_fd);
121
122 /* Similar to struct landlock_path_beneath_attr.parent_fd = 0 */
123 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
124 NULL, 0));
125 ASSERT_EQ(EFAULT, errno);
126 ASSERT_EQ(0, close(ruleset_fd));
127 }
128
TEST(inval_fd_enforce)129 TEST(inval_fd_enforce) {
130 ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0));
131
132 ASSERT_EQ(-1, landlock_restrict_self(-1, 0));
133 ASSERT_EQ(EBADF, errno);
134 }
135
TEST(unpriv_enforce_without_no_new_privs)136 TEST(unpriv_enforce_without_no_new_privs) {
137 int err;
138
139 drop_caps(_metadata);
140 err = landlock_restrict_self(-1, 0);
141 ASSERT_EQ(EPERM, errno);
142 ASSERT_EQ(err, -1);
143 }
144
TEST(ruleset_fd_io)145 TEST(ruleset_fd_io)
146 {
147 struct landlock_ruleset_attr ruleset_attr = {
148 .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE,
149 };
150 int ruleset_fd;
151 char buf;
152
153 drop_caps(_metadata);
154 ruleset_fd = landlock_create_ruleset(&ruleset_attr,
155 sizeof(ruleset_attr), 0);
156 ASSERT_LE(0, ruleset_fd);
157
158 ASSERT_EQ(-1, write(ruleset_fd, ".", 1));
159 ASSERT_EQ(EINVAL, errno);
160 ASSERT_EQ(-1, read(ruleset_fd, &buf, 1));
161 ASSERT_EQ(EINVAL, errno);
162
163 ASSERT_EQ(0, close(ruleset_fd));
164 }
165
166 /* Tests enforcement of a ruleset FD transferred through a UNIX socket. */
TEST(ruleset_fd_transfer)167 TEST(ruleset_fd_transfer)
168 {
169 struct landlock_ruleset_attr ruleset_attr = {
170 .handled_access_fs = LANDLOCK_ACCESS_FS_READ_DIR,
171 };
172 struct landlock_path_beneath_attr path_beneath_attr = {
173 .allowed_access = LANDLOCK_ACCESS_FS_READ_DIR,
174 };
175 int ruleset_fd_tx, dir_fd;
176 union {
177 /* Aligned ancillary data buffer. */
178 char buf[CMSG_SPACE(sizeof(ruleset_fd_tx))];
179 struct cmsghdr _align;
180 } cmsg_tx = {};
181 char data_tx = '.';
182 struct iovec io = {
183 .iov_base = &data_tx,
184 .iov_len = sizeof(data_tx),
185 };
186 struct msghdr msg = {
187 .msg_iov = &io,
188 .msg_iovlen = 1,
189 .msg_control = &cmsg_tx.buf,
190 .msg_controllen = sizeof(cmsg_tx.buf),
191 };
192 struct cmsghdr *cmsg;
193 int socket_fds[2];
194 pid_t child;
195 int status;
196
197 drop_caps(_metadata);
198
199 /* Creates a test ruleset with a simple rule. */
200 ruleset_fd_tx = landlock_create_ruleset(&ruleset_attr,
201 sizeof(ruleset_attr), 0);
202 ASSERT_LE(0, ruleset_fd_tx);
203 path_beneath_attr.parent_fd = open("/tmp", O_PATH | O_NOFOLLOW |
204 O_DIRECTORY | O_CLOEXEC);
205 ASSERT_LE(0, path_beneath_attr.parent_fd);
206 ASSERT_EQ(0, landlock_add_rule(ruleset_fd_tx, LANDLOCK_RULE_PATH_BENEATH,
207 &path_beneath_attr, 0));
208 ASSERT_EQ(0, close(path_beneath_attr.parent_fd));
209
210 cmsg = CMSG_FIRSTHDR(&msg);
211 ASSERT_NE(NULL, cmsg);
212 cmsg->cmsg_len = CMSG_LEN(sizeof(ruleset_fd_tx));
213 cmsg->cmsg_level = SOL_SOCKET;
214 cmsg->cmsg_type = SCM_RIGHTS;
215 memcpy(CMSG_DATA(cmsg), &ruleset_fd_tx, sizeof(ruleset_fd_tx));
216
217 /* Sends the ruleset FD over a socketpair and then close it. */
218 ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, socket_fds));
219 ASSERT_EQ(sizeof(data_tx), sendmsg(socket_fds[0], &msg, 0));
220 ASSERT_EQ(0, close(socket_fds[0]));
221 ASSERT_EQ(0, close(ruleset_fd_tx));
222
223 child = fork();
224 ASSERT_LE(0, child);
225 if (child == 0) {
226 int ruleset_fd_rx;
227
228 *(char *)msg.msg_iov->iov_base = '\0';
229 ASSERT_EQ(sizeof(data_tx), recvmsg(socket_fds[1], &msg, MSG_CMSG_CLOEXEC));
230 ASSERT_EQ('.', *(char *)msg.msg_iov->iov_base);
231 ASSERT_EQ(0, close(socket_fds[1]));
232 cmsg = CMSG_FIRSTHDR(&msg);
233 ASSERT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(ruleset_fd_tx)));
234 memcpy(&ruleset_fd_rx, CMSG_DATA(cmsg), sizeof(ruleset_fd_tx));
235
236 /* Enforces the received ruleset on the child. */
237 ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0));
238 ASSERT_EQ(0, landlock_restrict_self(ruleset_fd_rx, 0));
239 ASSERT_EQ(0, close(ruleset_fd_rx));
240
241 /* Checks that the ruleset enforcement. */
242 ASSERT_EQ(-1, open("/", O_RDONLY | O_DIRECTORY | O_CLOEXEC));
243 ASSERT_EQ(EACCES, errno);
244 dir_fd = open("/tmp", O_RDONLY | O_DIRECTORY | O_CLOEXEC);
245 ASSERT_LE(0, dir_fd);
246 ASSERT_EQ(0, close(dir_fd));
247 _exit(_metadata->passed ? EXIT_SUCCESS : EXIT_FAILURE);
248 return;
249 }
250
251 ASSERT_EQ(0, close(socket_fds[1]));
252
253 /* Checks that the parent is unrestricted. */
254 dir_fd = open("/", O_RDONLY | O_DIRECTORY | O_CLOEXEC);
255 ASSERT_LE(0, dir_fd);
256 ASSERT_EQ(0, close(dir_fd));
257 dir_fd = open("/tmp", O_RDONLY | O_DIRECTORY | O_CLOEXEC);
258 ASSERT_LE(0, dir_fd);
259 ASSERT_EQ(0, close(dir_fd));
260
261 ASSERT_EQ(child, waitpid(child, &status, 0));
262 ASSERT_EQ(1, WIFEXITED(status));
263 ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
264 }
265
266 TEST_HARNESS_MAIN
267