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
45static int fd_notify;
46
47static 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
104static 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, "Received result EPERM, as expected");
119			return;
120		}
121
122		tst_brk(TBROK | TERRNO,
123			"fanotify_init(0x%lx, O_RDONLY) failed",
124			tc->init_flags);
125	}
126
127	/* Attempt to place mark on object */
128	if (fanotify_mark(fd_notify, tc->mark_flags, tc->mark_mask, AT_FDCWD,
129				TEST_FILE) < 0) {
130		/*
131		 * Unprivileged users are only allowed to mark inodes and not
132		 * permitted to use access permissions
133		 */
134		if (errno == EPERM &&
135			(tc->mark_flags & DISALLOWED_MARK_FLAGS ||
136			 tc->mark_mask & FAN_ALL_PERM_EVENTS)) {
137			tst_res(TPASS, "Received result EPERM, as expected");
138			goto out;
139		}
140
141		tst_brk(TBROK | TERRNO,
142			"fanotify_mark(%d, %lx, %llx, AT_FDCWD, %s) "
143			"failed",
144			fd_notify,
145			tc->mark_flags,
146			tc->mark_mask,
147			TEST_FILE);
148	}
149
150	tst_res(TPASS,
151		"fanotify_init() and fanotify_mark() returned successfully, "
152		"as expected");
153
154out:
155	SAFE_CLOSE(fd_notify);
156}
157
158static void setup(void)
159{
160	int fd;
161	struct passwd *nobody;
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		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
185static void cleanup(void)
186{
187	if (fd_notify > 0)
188		SAFE_CLOSE(fd_notify);
189}
190
191static 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