• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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