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