Skip to content

Commit

Permalink
fanotify: fix permission model of unprivileged group
Browse files Browse the repository at this point in the history
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: 7cea2a3 ("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>
  • Loading branch information
Amir Goldstein authored and Jan Kara committed May 25, 2021
1 parent b577750 commit a8b98c8
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 7 deletions.
30 changes: 24 additions & 6 deletions fs/notify/fanotify/fanotify_user.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand All @@ -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
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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);

Expand Down
2 changes: 1 addition & 1 deletion fs/notify/fdinfo.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 4 additions & 0 deletions include/linux/fanotify.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down

0 comments on commit a8b98c8

Please sign in to comment.