Skip to content

Commit 58f5fbe

Browse files
amir73iljankara
authored andcommitted
fanotify: support watching filesystems and mounts inside userns
An unprivileged user is allowed to create an fanotify group and add inode marks, but not filesystem, mntns and mount marks. Add limited support for setting up filesystem, mntns and mount marks by an unprivileged user under the following conditions: 1. User has CAP_SYS_ADMIN in the user ns where the group was created 2.a. User has CAP_SYS_ADMIN in the user ns where the sb was created OR (in case setting up a mntns mark) 2.b. User has CAP_SYS_ADMIN in the user ns associated with the mntns Signed-off-by: Amir Goldstein <[email protected]> Signed-off-by: Jan Kara <[email protected]> Link: https://patch.msgid.link/[email protected]
1 parent 90d1238 commit 58f5fbe

File tree

4 files changed

+31
-15
lines changed

4 files changed

+31
-15
lines changed

fs/notify/fanotify/fanotify.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1009,6 +1009,7 @@ static int fanotify_handle_event(struct fsnotify_group *group, u32 mask,
10091009

10101010
static void fanotify_free_group_priv(struct fsnotify_group *group)
10111011
{
1012+
put_user_ns(group->user_ns);
10121013
kfree(group->fanotify_data.merge_hash);
10131014
if (group->fanotify_data.ucounts)
10141015
dec_ucount(group->fanotify_data.ucounts,

fs/notify/fanotify/fanotify_user.c

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1499,6 +1499,7 @@ static struct hlist_head *fanotify_alloc_merge_hash(void)
14991499
/* fanotify syscalls */
15001500
SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
15011501
{
1502+
struct user_namespace *user_ns = current_user_ns();
15021503
struct fsnotify_group *group;
15031504
int f_flags, fd;
15041505
unsigned int fid_mode = flags & FANOTIFY_FID_BITS;
@@ -1513,10 +1514,11 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
15131514
/*
15141515
* An unprivileged user can setup an fanotify group with
15151516
* limited functionality - an unprivileged group is limited to
1516-
* notification events with file handles and it cannot use
1517-
* unlimited queue/marks.
1517+
* notification events with file handles or mount ids and it
1518+
* cannot use unlimited queue/marks.
15181519
*/
1519-
if ((flags & FANOTIFY_ADMIN_INIT_FLAGS) || !fid_mode)
1520+
if ((flags & FANOTIFY_ADMIN_INIT_FLAGS) ||
1521+
!(flags & (FANOTIFY_FID_BITS | FAN_REPORT_MNT)))
15201522
return -EPERM;
15211523

15221524
/*
@@ -1595,8 +1597,7 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
15951597
}
15961598

15971599
/* Enforce groups limits per user in all containing user ns */
1598-
group->fanotify_data.ucounts = inc_ucount(current_user_ns(),
1599-
current_euid(),
1600+
group->fanotify_data.ucounts = inc_ucount(user_ns, current_euid(),
16001601
UCOUNT_FANOTIFY_GROUPS);
16011602
if (!group->fanotify_data.ucounts) {
16021603
fd = -EMFILE;
@@ -1605,6 +1606,7 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
16051606

16061607
group->fanotify_data.flags = flags | internal_flags;
16071608
group->memcg = get_mem_cgroup_from_mm(current->mm);
1609+
group->user_ns = get_user_ns(user_ns);
16081610

16091611
group->fanotify_data.merge_hash = fanotify_alloc_merge_hash();
16101612
if (!group->fanotify_data.merge_hash) {
@@ -1804,6 +1806,8 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
18041806
struct fsnotify_group *group;
18051807
struct path path;
18061808
struct fan_fsid __fsid, *fsid = NULL;
1809+
struct user_namespace *user_ns = NULL;
1810+
struct mnt_namespace *mntns;
18071811
u32 valid_mask = FANOTIFY_EVENTS | FANOTIFY_EVENT_FLAGS;
18081812
unsigned int mark_type = flags & FANOTIFY_MARK_TYPE_BITS;
18091813
unsigned int mark_cmd = flags & FANOTIFY_MARK_CMD_BITS;
@@ -1897,12 +1901,10 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
18971901
}
18981902

18991903
/*
1900-
* An unprivileged user is not allowed to setup mount nor filesystem
1901-
* marks. This also includes setting up such marks by a group that
1902-
* was initialized by an unprivileged user.
1904+
* A user is allowed to setup sb/mount/mntns marks only if it is
1905+
* capable in the user ns where the group was created.
19031906
*/
1904-
if ((!capable(CAP_SYS_ADMIN) ||
1905-
FAN_GROUP_FLAG(group, FANOTIFY_UNPRIV)) &&
1907+
if (!ns_capable(group->user_ns, CAP_SYS_ADMIN) &&
19061908
mark_type != FAN_MARK_INODE)
19071909
return -EPERM;
19081910

@@ -1986,18 +1988,31 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
19861988
fsid = &__fsid;
19871989
}
19881990

1989-
/* inode held in place by reference to path; group by fget on fd */
1991+
/*
1992+
* In addition to being capable in the user ns where group was created,
1993+
* the user also needs to be capable in the user ns associated with
1994+
* the filesystem or in the user ns associated with the mntns
1995+
* (when marking mntns).
1996+
*/
19901997
if (obj_type == FSNOTIFY_OBJ_TYPE_INODE) {
19911998
inode = path.dentry->d_inode;
19921999
obj = inode;
19932000
} else if (obj_type == FSNOTIFY_OBJ_TYPE_VFSMOUNT) {
2001+
user_ns = path.mnt->mnt_sb->s_user_ns;
19942002
obj = path.mnt;
19952003
} else if (obj_type == FSNOTIFY_OBJ_TYPE_SB) {
2004+
user_ns = path.mnt->mnt_sb->s_user_ns;
19962005
obj = path.mnt->mnt_sb;
19972006
} else if (obj_type == FSNOTIFY_OBJ_TYPE_MNTNS) {
1998-
obj = mnt_ns_from_dentry(path.dentry);
2007+
mntns = mnt_ns_from_dentry(path.dentry);
2008+
user_ns = mntns->user_ns;
2009+
obj = mntns;
19992010
}
20002011

2012+
ret = -EPERM;
2013+
if (user_ns && !ns_capable(user_ns, CAP_SYS_ADMIN))
2014+
goto path_put_and_out;
2015+
20012016
ret = -EINVAL;
20022017
if (!obj)
20032018
goto path_put_and_out;

include/linux/fanotify.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,7 @@
3838
FAN_REPORT_PIDFD | \
3939
FAN_REPORT_FD_ERROR | \
4040
FAN_UNLIMITED_QUEUE | \
41-
FAN_UNLIMITED_MARKS | \
42-
FAN_REPORT_MNT)
41+
FAN_UNLIMITED_MARKS)
4342

4443
/*
4544
* fanotify_init() flags that are allowed for user without CAP_SYS_ADMIN.
@@ -48,7 +47,7 @@
4847
* so one of the flags for reporting file handles is required.
4948
*/
5049
#define FANOTIFY_USER_INIT_FLAGS (FAN_CLASS_NOTIF | \
51-
FANOTIFY_FID_BITS | \
50+
FANOTIFY_FID_BITS | FAN_REPORT_MNT | \
5251
FAN_CLOEXEC | FAN_NONBLOCK)
5352

5453
#define FANOTIFY_INIT_FLAGS (FANOTIFY_ADMIN_INIT_FLAGS | \

include/linux/fsnotify_backend.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@ struct fsnotify_group {
250250
* full */
251251

252252
struct mem_cgroup *memcg; /* memcg to charge allocations */
253+
struct user_namespace *user_ns; /* user ns where group was created */
253254

254255
/* groups can define private fields here or use the void *private */
255256
union {

0 commit comments

Comments
 (0)