diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
index 4ade3f9df337..b70de273eedb 100644
--- a/fs/notify/fanotify/fanotify_user.c
+++ b/fs/notify/fanotify/fanotify_user.c
@@ -397,9 +397,21 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
metadata.vers = FANOTIFY_METADATA_VERSION;
metadata.reserved = 0;
metadata.mask = event->mask & FANOTIFY_OUTGOING_EVENTS;
- metadata.pid = pid_vnr(event->pid);
+ /*
+ * An unprivileged event listener does not get an open file descriptor
+ * in the event nor another generating process pid. If the event was
+ * generated by the unprivileged process itself, self pid is reported.
+ * We may relax this in the future by checking calling process access
+ * permissions to the object.
+ */
+ if (!FAN_GROUP_FLAG(group, FANOTIFY_UNPRIV) ||
+ task_tgid(current) == event->pid)
+ metadata.pid = pid_vnr(event->pid);
+ else
+ metadata.pid = 0;
- if (path && path->mnt && path->dentry) {
+ if (!FAN_GROUP_FLAG(group, FANOTIFY_UNPRIV) &&
+ path && path->mnt && path->dentry) {
fd = create_fd(group, path, &f);
if (fd < 0)
return fd;@@ -995,12 +1007,29 @@ 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);
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
+ if (!capable(CAP_SYS_ADMIN)) {
+ /*
+ * An unprivileged user can setup an unprivileged listener with
+ * limited functionality - an unprivileged event listener cannot
+ * request permission events, cannot set mount/filesystem marks
+ * and cannot request unlimited queue/marks.
+ */
+ if ((flags & ~FANOTIFY_UNPRIV_INIT_FLAGS) ||
+ class != FAN_CLASS_NOTIF)
+ return -EPERM;
+
+ /*
+ * We set the internal flag FANOTIFY_UNPRIV on the group, so we
+ * know that we need to limit setting mount/filesystem marks on
+ * this group and avoid providing pid and open fd in the event.
+ */
+ internal_flags |= FANOTIFY_UNPRIV;
+ }
#ifdef CONFIG_AUDITSYSCALL
if (flags & ~(FANOTIFY_INIT_FLAGS | FAN_ENABLE_AUDIT))@@ -1051,7 +1080,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->overflow_event = fanotify_alloc_overflow_event();
@@ -1247,6 +1276,15 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
goto fput_and_out;
group = f.file->private_data;
+ /*
+ * An unprivileged event listener is not allowed to watch a mount
+ * point nor a filesystem.
+ */
+ ret = -EPERM;
+ if (FAN_GROUP_FLAG(group, FANOTIFY_UNPRIV) &&
+ mark_type != FAN_MARK_INODE)
+ goto fput_and_out;
+
/*
* group->priority == FS_PRIO_0 == FAN_CLASS_NOTIF. These are not
* allowed to set permissions events.
@@ -1379,6 +1417,7 @@ SYSCALL32_DEFINE6(fanotify_mark,
*/
static int __init fanotify_user_setup(void)
{
+ BUILD_BUG_ON(FANOTIFY_INIT_FLAGS & FANOTIFY_INTERNAL_FLAGS);
BUILD_BUG_ON(HWEIGHT32(FANOTIFY_INIT_FLAGS) != 10);
BUILD_BUG_ON(HWEIGHT32(FANOTIFY_MARK_FLAGS) != 9);
diff --git a/fs/notify/fdinfo.c b/fs/notify/fdinfo.c
index f0d6b54be412..57f0d5d9f934 100644
--- a/fs/notify/fdinfo.c
+++ b/fs/notify/fdinfo.c
@@ -144,7 +144,8 @@ 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.f_flags);
+ group->fanotify_data.flags & FANOTIFY_INIT_FLAGS,
+ group->fanotify_data.f_flags);
show_fdinfo(m, f, fanotify_fdinfo);
}
diff --git a/include/linux/fanotify.h b/include/linux/fanotify.h
index 031a97d8369a..a573c1028c14 100644
--- a/include/linux/fanotify.h
+++ b/include/linux/fanotify.h
@@ -28,6 +28,22 @@ extern struct ctl_table fanotify_table[]; /* for sysctl */
FAN_CLOEXEC | FAN_NONBLOCK | \
FAN_UNLIMITED_QUEUE | FAN_UNLIMITED_MARKS)
+/* Internal flags */
+#define FANOTIFY_UNPRIV 0x80000000
+#define FANOTIFY_INTERNAL_FLAGS (FANOTIFY_UNPRIV)
+
+/*
+ * fanotify_init() flags allowed for unprivileged listener.
+ * FAN_CLASS_NOTIF in this mask is purely semantic because it is zero,
+ * but it is the only class we allow for unprivileged listener.
+ * Since unprivileged listener does not provide file descriptors in events,
+ * reporting file handles makes sense, but it is not a must.
+ * FAN_REPORT_TID does not make sense for unprivileged listener, which uses
+ * event->pid only to filter out events generated by listener process itself.
+ */
+#define FANOTIFY_UNPRIV_INIT_FLAGS (FAN_CLOEXEC | FAN_NONBLOCK | \
+ FAN_CLASS_NOTIF | FANOTIFY_FID_BITS)
+
#define FANOTIFY_MARK_TYPE_BITS (FAN_MARK_INODE | FAN_MARK_MOUNT | \
FAN_MARK_FILESYSTEM)
--
2.25.1