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