fanotify: fix permission model of unprivileged group
authorAmir Goldstein <amir73il@gmail.com>
Mon, 24 May 2021 13:53:21 +0000 (16:53 +0300)
committerJan Kara <jack@suse.cz>
Tue, 25 May 2021 10:21:14 +0000 (12:21 +0200)
Reporting event->pid should depend on the privileges of the user that
initialized the group, not the privileges of the user reading the
events.

Use an internal group flag FANOTIFY_UNPRIV to record the fact that the
group was initialized by an unprivileged user.

To be on the safe side, the premissions to setup filesystem and mount
marks now require that both the user that initialized the group and
the user setting up the mark have CAP_SYS_ADMIN.

Link: https://lore.kernel.org/linux-fsdevel/CAOQ4uxiA77_P5vtv7e83g0+9d7B5W9ZTE4GfQEYbWmfT1rA=VA@mail.gmail.com/
Fixes: 7cea2a3c505e ("fanotify: support limited functionality for unprivileged users")
Cc: <Stable@vger.kernel.org> # v5.12+
Link: https://lore.kernel.org/r/20210524135321.2190062-1-amir73il@gmail.com
Reviewed-by: Matthew Bobrowski <repnop@google.com>
Acked-by: Christian Brauner <christian.brauner@ubuntu.com>
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Jan Kara <jack@suse.cz>
fs/notify/fanotify/fanotify_user.c
fs/notify/fdinfo.c
include/linux/fanotify.h

index 71fefb3..be5b6d2 100644 (file)
@@ -424,11 +424,18 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
         * events generated by the listener process itself, without disclosing
         * the pids of other processes.
         */
-       if (!capable(CAP_SYS_ADMIN) &&
+       if (FAN_GROUP_FLAG(group, FANOTIFY_UNPRIV) &&
            task_tgid(current) != event->pid)
                metadata.pid = 0;
 
-       if (path && path->mnt && path->dentry) {
+       /*
+        * For now, fid mode is required for an unprivileged listener and
+        * fid mode does not report fd in events.  Keep this check anyway
+        * for safety in case fid mode requirement is relaxed in the future
+        * to allow unprivileged listener to get events with no fd and no fid.
+        */
+       if (!FAN_GROUP_FLAG(group, FANOTIFY_UNPRIV) &&
+           path && path->mnt && path->dentry) {
                fd = create_fd(group, path, &f);
                if (fd < 0)
                        return fd;
@@ -1040,6 +1047,7 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
        int f_flags, fd;
        unsigned int fid_mode = flags & FANOTIFY_FID_BITS;
        unsigned int class = flags & FANOTIFY_CLASS_BITS;
+       unsigned int internal_flags = 0;
 
        pr_debug("%s: flags=%x event_f_flags=%x\n",
                 __func__, flags, event_f_flags);
@@ -1053,6 +1061,13 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
                 */
                if ((flags & FANOTIFY_ADMIN_INIT_FLAGS) || !fid_mode)
                        return -EPERM;
+
+               /*
+                * Setting the internal flag FANOTIFY_UNPRIV on the group
+                * prevents setting mount/filesystem marks on this group and
+                * prevents reporting pid and open fd in events.
+                */
+               internal_flags |= FANOTIFY_UNPRIV;
        }
 
 #ifdef CONFIG_AUDITSYSCALL
@@ -1105,7 +1120,7 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
                goto out_destroy_group;
        }
 
-       group->fanotify_data.flags = flags;
+       group->fanotify_data.flags = flags | internal_flags;
        group->memcg = get_mem_cgroup_from_mm(current->mm);
 
        group->fanotify_data.merge_hash = fanotify_alloc_merge_hash();
@@ -1305,11 +1320,13 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
        group = f.file->private_data;
 
        /*
-        * An unprivileged user is not allowed to watch a mount point nor
-        * a filesystem.
+        * An unprivileged user is not allowed to setup mount nor filesystem
+        * marks.  This also includes setting up such marks by a group that
+        * was initialized by an unprivileged user.
         */
        ret = -EPERM;
-       if (!capable(CAP_SYS_ADMIN) &&
+       if ((!capable(CAP_SYS_ADMIN) ||
+            FAN_GROUP_FLAG(group, FANOTIFY_UNPRIV)) &&
            mark_type != FAN_MARK_INODE)
                goto fput_and_out;
 
@@ -1460,6 +1477,7 @@ static int __init fanotify_user_setup(void)
        max_marks = clamp(max_marks, FANOTIFY_OLD_DEFAULT_MAX_MARKS,
                                     FANOTIFY_DEFAULT_MAX_USER_MARKS);
 
+       BUILD_BUG_ON(FANOTIFY_INIT_FLAGS & FANOTIFY_INTERNAL_GROUP_FLAGS);
        BUILD_BUG_ON(HWEIGHT32(FANOTIFY_INIT_FLAGS) != 10);
        BUILD_BUG_ON(HWEIGHT32(FANOTIFY_MARK_FLAGS) != 9);
 
index a712b2a..57f0d5d 100644 (file)
@@ -144,7 +144,7 @@ void fanotify_show_fdinfo(struct seq_file *m, struct file *f)
        struct fsnotify_group *group = f->private_data;
 
        seq_printf(m, "fanotify flags:%x event-flags:%x\n",
-                  group->fanotify_data.flags,
+                  group->fanotify_data.flags & FANOTIFY_INIT_FLAGS,
                   group->fanotify_data.f_flags);
 
        show_fdinfo(m, f, fanotify_fdinfo);
index bad41bc..a16dbec 100644 (file)
@@ -51,6 +51,10 @@ extern struct ctl_table fanotify_table[]; /* for sysctl */
 #define FANOTIFY_INIT_FLAGS    (FANOTIFY_ADMIN_INIT_FLAGS | \
                                 FANOTIFY_USER_INIT_FLAGS)
 
+/* Internal group flags */
+#define FANOTIFY_UNPRIV                0x80000000
+#define FANOTIFY_INTERNAL_GROUP_FLAGS  (FANOTIFY_UNPRIV)
+
 #define FANOTIFY_MARK_TYPE_BITS        (FAN_MARK_INODE | FAN_MARK_MOUNT | \
                                 FAN_MARK_FILESYSTEM)