1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (c) 2018 Matthew Bobrowski. All Rights Reserved.
4 *
5 * Started by Matthew Bobrowski <mbobrowski@mbobrowski.org>
6 */
7
8 /*\
9 * [Description]
10 * This set of tests is to ensure that the unprivileged listener feature of
11 * fanotify is functioning as expected. The objective this test case file
12 * is to validate whether any forbidden flags that are passed by an
13 * unprivileged user return the correct error result.
14 */
15
16 #define _GNU_SOURCE
17 #include "config.h"
18
19 #include <pwd.h>
20 #include <stdio.h>
21 #include <errno.h>
22 #include "tst_test.h"
23
24 #ifdef HAVE_SYS_FANOTIFY_H
25 #include "fanotify.h"
26
27 /*
28 * This is a set of intialization flags that are not permitted to be used by an
29 * unprivileged user. Thus, if supplied, either EPERM or EINVAL should be
30 * returned to the calling process respectively.
31 */
32 #define DISALLOWED_INIT_FLAGS (FAN_UNLIMITED_QUEUE | FAN_UNLIMITED_MARKS | \
33 FAN_CLASS_CONTENT | FAN_CLASS_PRE_CONTENT | \
34 FAN_REPORT_TID)
35
36 /*
37 * This is a set of mark flags that are not permitted to be used with an
38 * unprivileged listener.
39 */
40 #define DISALLOWED_MARK_FLAGS (FAN_MARK_MOUNT | FAN_MARK_FILESYSTEM)
41
42 #define MOUNT_PATH "fs_mnt"
43 #define TEST_FILE MOUNT_PATH "/testfile"
44
45 static int fd_notify;
46
47 static struct test_case_t {
48 const char *name;
49 unsigned long init_flags;
50 unsigned long mark_flags;
51 unsigned long long mark_mask;
52 } test_cases[] = {
53 {
54 "init_flags: missing FAN_REPORT_FID",
55 FAN_CLASS_NOTIF,
56 0, 0
57 },
58 {
59 "init_flags: FAN_CLASS_CONTENT",
60 FANOTIFY_REQUIRED_USER_INIT_FLAGS | FAN_CLASS_CONTENT,
61 0, 0
62 },
63 {
64 "init_flags: FAN_CLASS_PRE_CONTENT",
65 FANOTIFY_REQUIRED_USER_INIT_FLAGS | FAN_CLASS_PRE_CONTENT,
66 0, 0
67 },
68 {
69 "init_flags: FAN_UNLIMITED_QUEUE",
70 FANOTIFY_REQUIRED_USER_INIT_FLAGS | FAN_UNLIMITED_QUEUE,
71 0, 0
72 },
73 {
74 "init_flags: FAN_UNLIMITED_MARKS",
75 FANOTIFY_REQUIRED_USER_INIT_FLAGS | FAN_UNLIMITED_MARKS,
76 0, 0
77 },
78 {
79 "init_flags: FAN_REPORT_TID",
80 FANOTIFY_REQUIRED_USER_INIT_FLAGS | FAN_REPORT_TID,
81 0, 0
82 },
83 {
84 "init_flags: FAN_CLASS_NOTIF, "
85 "mark_flags: FAN_MARK_ADD | FAN_MARK_MOUNT",
86 FANOTIFY_REQUIRED_USER_INIT_FLAGS | FAN_CLASS_NOTIF,
87 FAN_MARK_ADD | FAN_MARK_MOUNT, FAN_ALL_EVENTS
88 },
89 {
90 "init_flags: FAN_CLASS_NOTIF, "
91 "mark_flags: FAN_MARK_ADD | FAN_MARK_FILESYSTEM",
92 FANOTIFY_REQUIRED_USER_INIT_FLAGS | FAN_CLASS_NOTIF,
93 FAN_MARK_ADD | FAN_MARK_FILESYSTEM, FAN_ALL_EVENTS
94 },
95 {
96 "init_flags: FAN_CLASS_NOTIF, "
97 "mark_flags: FAN_MARK_ADD, "
98 "mark_mask: FAN_ALL_EVENTS",
99 FANOTIFY_REQUIRED_USER_INIT_FLAGS | FAN_CLASS_NOTIF,
100 FAN_MARK_ADD, FAN_ALL_EVENTS
101 }
102 };
103
test_fanotify(unsigned int n)104 static void test_fanotify(unsigned int n)
105 {
106 struct test_case_t *tc = &test_cases[n];
107
108 tst_res(TINFO, "Test #%d %s", n, tc->name);
109
110 /* Initialize fanotify */
111 fd_notify = fanotify_init(tc->init_flags, O_RDONLY);
112
113 if (fd_notify < 0) {
114 if (errno == EPERM &&
115 ((tc->init_flags & DISALLOWED_INIT_FLAGS) ||
116 (tc->init_flags & FANOTIFY_REQUIRED_USER_INIT_FLAGS) !=
117 FANOTIFY_REQUIRED_USER_INIT_FLAGS)) {
118 tst_res(TPASS,
119 "Received result EPERM, as expected");
120 return;
121 } else {
122 tst_brk(TBROK | TERRNO,
123 "fanotify_init(0x%lx, O_RDONLY) failed",
124 tc->init_flags);
125 }
126 }
127
128 /* Attempt to place mark on object */
129 if (fanotify_mark(fd_notify, tc->mark_flags, tc->mark_mask, AT_FDCWD,
130 TEST_FILE) < 0) {
131 /*
132 * Unprivileged users are only allowed to mark inodes and not
133 * permitted to use access permissions
134 */
135 if (errno == EPERM &&
136 (tc->mark_flags & DISALLOWED_MARK_FLAGS ||
137 tc->mark_mask & FAN_ALL_PERM_EVENTS)) {
138 tst_res(TPASS, "Received result EPERM, as expected");
139 goto out;
140 }
141
142 tst_brk(TBROK | TERRNO,
143 "fanotify_mark(%d, %lx, %llx, AT_FDCWD, %s) "
144 "failed",
145 fd_notify,
146 tc->mark_flags,
147 tc->mark_mask,
148 TEST_FILE);
149 }
150
151 tst_res(TPASS,
152 "fanotify_init() and fanotify_mark() returned successfully, "
153 "as expected");
154
155 out:
156 SAFE_CLOSE(fd_notify);
157 }
158
setup(void)159 static void setup(void)
160 {
161 int fd;
162
163 SAFE_TOUCH(TEST_FILE, 0666, NULL);
164
165 /* Check for kernel fanotify support */
166 REQUIRE_FANOTIFY_INIT_FLAGS_SUPPORTED_ON_FS(FAN_REPORT_FID, TEST_FILE);
167
168 /* Relinquish privileged user */
169 if (geteuid() == 0) {
170 tst_res(TINFO,
171 "Running as privileged user, revoking permissions.");
172 struct passwd *nobody = SAFE_GETPWNAM("nobody");
173 SAFE_SETUID(nobody->pw_uid);
174 }
175
176 /* Check for unprivileged fanotify support */
177 fd = fanotify_init(FANOTIFY_REQUIRED_USER_INIT_FLAGS, O_RDONLY);
178 if (fd < 0) {
179 tst_brk(TCONF,
180 "unprivileged fanotify not supported by kernel?");
181 }
182 SAFE_CLOSE(fd);
183 }
184
cleanup(void)185 static void cleanup(void)
186 {
187 if (fd_notify > 0)
188 SAFE_CLOSE(fd_notify);
189 }
190
191 static struct tst_test test = {
192 .test = test_fanotify,
193 .tcnt = ARRAY_SIZE(test_cases),
194 .setup = setup,
195 .cleanup = cleanup,
196 .needs_root = 1,
197 .mount_device = 1,
198 .mntpoint = MOUNT_PATH,
199 };
200
201 #else
202 TST_TEST_TCONF("system doesn't have required fanotify support");
203 #endif
204