1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (c) 2013 SUSE. All Rights Reserved.
4 *
5 * Started by Jan Kara <jack@suse.cz>
6 *
7 * DESCRIPTION
8 * Check that fanotify permission events work
9 */
10 #define _GNU_SOURCE
11 #include "config.h"
12
13 #include <stdio.h>
14 #include <unistd.h>
15 #include <sys/stat.h>
16 #include <sys/types.h>
17 #include <fcntl.h>
18 #include <sys/wait.h>
19 #include <errno.h>
20 #include <string.h>
21 #include <signal.h>
22 #include <sys/syscall.h>
23 #include <stdlib.h>
24 #include "tst_test.h"
25 #include "fanotify.h"
26
27 #if defined(HAVE_SYS_FANOTIFY_H)
28 #include <sys/fanotify.h>
29
30 #define EVENT_MAX 1024
31 /* size of the event structure, not counting name */
32 #define EVENT_SIZE (sizeof (struct fanotify_event_metadata))
33 /* reasonable guess as to size of 1024 events */
34 #define EVENT_BUF_LEN (EVENT_MAX * EVENT_SIZE)
35 /* Size large enough to hold a reasonable amount of expected event objects */
36 #define EVENT_SET_MAX 16
37
38 #define BUF_SIZE 256
39 #define TST_TOTAL 3
40 #define TEST_APP "fanotify_child"
41 #define MOUNT_PATH "fs_mnt"
42 #define FILE_EXEC_PATH MOUNT_PATH"/"TEST_APP
43
44 static char fname[BUF_SIZE];
45 static char buf[BUF_SIZE];
46 static volatile int fd_notify;
47
48 static pid_t child_pid;
49
50 static char event_buf[EVENT_BUF_LEN];
51 static int support_exec_events;
52
53 struct event {
54 unsigned long long mask;
55 unsigned int response;
56 };
57
58 /*
59 * Ensure to keep the first FAN_OPEN_EXEC_PERM test case before the first
60 * MARK_TYPE(FILESYSTEM) in order to allow for correct detection between
61 * exec events not supported and filesystem marks not supported.
62 */
63 static struct tcase {
64 const char *tname;
65 struct fanotify_mark_type mark;
66 unsigned long long mask;
67 int event_count;
68 struct event event_set[EVENT_SET_MAX];
69 } tcases[] = {
70 {
71 "inode mark, FAN_OPEN_PERM | FAN_ACCESS_PERM events",
72 INIT_FANOTIFY_MARK_TYPE(INODE),
73 FAN_OPEN_PERM | FAN_ACCESS_PERM, 3,
74 {
75 {FAN_OPEN_PERM, FAN_ALLOW},
76 {FAN_ACCESS_PERM, FAN_DENY},
77 {FAN_OPEN_PERM, FAN_DENY}
78 }
79 },
80 {
81 "inode mark, FAN_ACCESS_PERM | FAN_OPEN_EXEC_PERM events",
82 INIT_FANOTIFY_MARK_TYPE(INODE),
83 FAN_ACCESS_PERM | FAN_OPEN_EXEC_PERM, 2,
84 {
85 {FAN_ACCESS_PERM, FAN_DENY},
86 {FAN_OPEN_EXEC_PERM, FAN_DENY}
87 }
88 },
89 {
90 "mount mark, FAN_OPEN_PERM | FAN_ACCESS_PERM events",
91 INIT_FANOTIFY_MARK_TYPE(MOUNT),
92 FAN_OPEN_PERM | FAN_ACCESS_PERM, 3,
93 {
94 {FAN_OPEN_PERM, FAN_ALLOW},
95 {FAN_ACCESS_PERM, FAN_DENY},
96 {FAN_OPEN_PERM, FAN_DENY}
97 }
98 },
99 {
100 "mount mark, FAN_ACCESS_PERM | FAN_OPEN_EXEC_PERM events",
101 INIT_FANOTIFY_MARK_TYPE(MOUNT),
102 FAN_ACCESS_PERM | FAN_OPEN_EXEC_PERM, 2,
103 {
104 {FAN_ACCESS_PERM, FAN_DENY},
105 {FAN_OPEN_EXEC_PERM, FAN_DENY}
106 }
107 },
108 {
109 "filesystem mark, FAN_OPEN_PERM | FAN_ACCESS_PERM events",
110 INIT_FANOTIFY_MARK_TYPE(FILESYSTEM),
111 FAN_OPEN_PERM | FAN_ACCESS_PERM, 3,
112 {
113 {FAN_OPEN_PERM, FAN_ALLOW},
114 {FAN_ACCESS_PERM, FAN_DENY},
115 {FAN_OPEN_PERM, FAN_DENY}
116 }
117 },
118 {
119 "filesystem mark, FAN_ACCESS_PERM | FAN_OPEN_EXEC_PERM events",
120 INIT_FANOTIFY_MARK_TYPE(FILESYSTEM),
121 FAN_ACCESS_PERM | FAN_OPEN_EXEC_PERM, 2,
122 {
123 {FAN_ACCESS_PERM, FAN_DENY},
124 {FAN_OPEN_EXEC_PERM, FAN_DENY}
125 }
126 },
127 };
128
generate_events(void)129 static void generate_events(void)
130 {
131 int fd;
132 char *const argv[] = {FILE_EXEC_PATH, NULL};
133
134 /*
135 * Generate sequence of events
136 */
137 if ((fd = open(fname, O_RDWR | O_CREAT, 0700)) == -1)
138 exit(1);
139 if (write(fd, fname, 1) == -1)
140 exit(2);
141
142 lseek(fd, 0, SEEK_SET);
143 if (read(fd, buf, BUF_SIZE) != -1)
144 exit(3);
145
146 if (close(fd) == -1)
147 exit(4);
148
149 if (execve(FILE_EXEC_PATH, argv, environ) != -1)
150 exit(5);
151 }
152
child_handler(int tmp)153 static void child_handler(int tmp)
154 {
155 (void)tmp;
156 /*
157 * Close notification fd so that we cannot block while reading
158 * from it
159 */
160 close(fd_notify);
161 fd_notify = -1;
162 }
163
run_child(void)164 static void run_child(void)
165 {
166 struct sigaction child_action;
167
168 child_action.sa_handler = child_handler;
169 sigemptyset(&child_action.sa_mask);
170 child_action.sa_flags = SA_NOCLDSTOP;
171
172 if (sigaction(SIGCHLD, &child_action, NULL) < 0) {
173 tst_brk(TBROK | TERRNO,
174 "sigaction(SIGCHLD, &child_action, NULL) failed");
175 }
176
177 child_pid = SAFE_FORK();
178
179 if (child_pid == 0) {
180 /* Child will generate events now */
181 close(fd_notify);
182 generate_events();
183 exit(0);
184 }
185 }
186
check_child(void)187 static void check_child(void)
188 {
189 struct sigaction child_action;
190 int child_ret;
191
192 child_action.sa_handler = SIG_IGN;
193 sigemptyset(&child_action.sa_mask);
194 child_action.sa_flags = SA_NOCLDSTOP;
195 if (sigaction(SIGCHLD, &child_action, NULL) < 0) {
196 tst_brk(TBROK | TERRNO,
197 "sigaction(SIGCHLD, &child_action, NULL) failed");
198 }
199 SAFE_WAITPID(-1, &child_ret, 0);
200
201 if (WIFEXITED(child_ret) && WEXITSTATUS(child_ret) == 0)
202 tst_res(TPASS, "child exited correctly");
203 else
204 tst_res(TFAIL, "child %s", tst_strstatus(child_ret));
205 }
206
setup_mark(unsigned int n)207 static int setup_mark(unsigned int n)
208 {
209 unsigned int i = 0;
210 struct tcase *tc = &tcases[n];
211 struct fanotify_mark_type *mark = &tc->mark;
212 char *const files[] = {fname, FILE_EXEC_PATH};
213
214 tst_res(TINFO, "Test #%d: %s", n, tc->tname);
215 fd_notify = SAFE_FANOTIFY_INIT(FAN_CLASS_CONTENT, O_RDONLY);
216
217 for (; i < ARRAY_SIZE(files); i++) {
218 if (fanotify_mark(fd_notify, FAN_MARK_ADD | mark->flag,
219 tc->mask, AT_FDCWD, files[i]) < 0) {
220 if (errno == EINVAL &&
221 (tc->mask & FAN_OPEN_EXEC_PERM &&
222 !support_exec_events)) {
223 tst_res(TCONF,
224 "FAN_OPEN_EXEC_PERM not supported in "
225 "kernel?");
226 return -1;
227 } else if (errno == EINVAL &&
228 mark->flag == FAN_MARK_FILESYSTEM) {
229 tst_res(TCONF,
230 "FAN_MARK_FILESYSTEM not supported in "
231 "kernel?");
232 return -1;
233 } else if (errno == EINVAL) {
234 tst_brk(TCONF | TERRNO,
235 "CONFIG_FANOTIFY_ACCESS_PERMISSIONS "
236 "not configured in kernel?");
237 } else {
238 tst_brk(TBROK | TERRNO,
239 "fanotify_mark(%d, FAN_MARK_ADD | %s, "
240 "FAN_ACCESS_PERM | FAN_OPEN_PERM, "
241 "AT_FDCWD, %s) failed.",
242 fd_notify, mark->name, fname);
243 }
244 } else {
245 /*
246 * To distinguish between perm not supported, exec
247 * events not supported and filesystem mark not
248 * supported.
249 */
250 if (tc->mask & FAN_OPEN_EXEC_PERM)
251 support_exec_events = 1;
252 }
253 }
254
255 return 0;
256 }
257
test_fanotify(unsigned int n)258 static void test_fanotify(unsigned int n)
259 {
260 int ret, len = 0, i = 0, test_num = 0;
261 struct tcase *tc = &tcases[n];
262 struct event *event_set = tc->event_set;
263
264 if (setup_mark(n) != 0)
265 return;
266
267 run_child();
268
269 /*
270 * Process events
271 *
272 * tc->count + 1 is to accommodate for checking the child process
273 * return value
274 */
275 while (test_num < tc->event_count + 1 && fd_notify != -1) {
276 struct fanotify_event_metadata *event;
277
278 if (i == len) {
279 /* Get more events */
280 ret = read(fd_notify, event_buf + len,
281 EVENT_BUF_LEN - len);
282 if (fd_notify == -1)
283 break;
284 if (ret < 0) {
285 tst_brk(TBROK,
286 "read(%d, buf, %zu) failed",
287 fd_notify, EVENT_BUF_LEN);
288 }
289 len += ret;
290 }
291
292 event = (struct fanotify_event_metadata *)&event_buf[i];
293 /* Permission events cannot be merged, so the event mask
294 * reported should exactly match the event mask within the
295 * event set.
296 */
297 if (event->mask != event_set[test_num].mask) {
298 tst_res(TFAIL,
299 "got event: mask=%llx (expected %llx) "
300 "pid=%u fd=%d",
301 (unsigned long long)event->mask,
302 event_set[test_num].mask,
303 (unsigned)event->pid, event->fd);
304 } else if (event->pid != child_pid) {
305 tst_res(TFAIL,
306 "got event: mask=%llx pid=%u "
307 "(expected %u) fd=%d",
308 (unsigned long long)event->mask,
309 (unsigned)event->pid,
310 (unsigned)child_pid,
311 event->fd);
312 } else {
313 tst_res(TPASS,
314 "got event: mask=%llx pid=%u fd=%d",
315 (unsigned long long)event->mask,
316 (unsigned)event->pid, event->fd);
317 }
318
319 /* Write response to the permission event */
320 if (event_set[test_num].mask & LTP_ALL_PERM_EVENTS) {
321 struct fanotify_response resp;
322
323 resp.fd = event->fd;
324 resp.response = event_set[test_num].response;
325 SAFE_WRITE(1, fd_notify, &resp, sizeof(resp));
326 }
327
328 i += event->event_len;
329
330 if (event->fd != FAN_NOFD)
331 SAFE_CLOSE(event->fd);
332
333 test_num++;
334 }
335
336 for (; test_num < tc->event_count; test_num++) {
337 tst_res(TFAIL, "didn't get event: mask=%llx",
338 event_set[test_num].mask);
339
340 }
341
342 check_child();
343
344 if (fd_notify > 0)
345 SAFE_CLOSE(fd_notify);
346 }
347
setup(void)348 static void setup(void)
349 {
350 sprintf(fname, MOUNT_PATH"/fname_%d", getpid());
351 SAFE_FILE_PRINTF(fname, "1");
352
353 SAFE_CP(TEST_APP, FILE_EXEC_PATH);
354 }
355
cleanup(void)356 static void cleanup(void)
357 {
358 if (fd_notify > 0)
359 SAFE_CLOSE(fd_notify);
360 }
361
362 static const char *const resource_files[] = {
363 TEST_APP,
364 NULL
365 };
366
367 static struct tst_test test = {
368 .test = test_fanotify,
369 .tcnt = ARRAY_SIZE(tcases),
370 .setup = setup,
371 .cleanup = cleanup,
372 .forks_child = 1,
373 .needs_root = 1,
374 .mount_device = 1,
375 .mntpoint = MOUNT_PATH,
376 .resource_files = resource_files
377 };
378
379 #else
380 TST_TEST_TCONF("system doesn't have required fanotify support");
381 #endif
382