1
2
3
4
5
6
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
24TEST(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
32 ASSERT_EQ(-1, landlock_create_ruleset(ruleset_attr, 0, 0));
33
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
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
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
66TEST(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
91TEST(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
113TEST(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
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
129TEST(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
136TEST(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
145TEST(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
167TEST(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
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
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
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
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
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
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
266TEST_HARNESS_MAIN
267