Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

fanotify: fix permission model of unprivileged group

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>

authored by

Amir Goldstein and committed by
Jan Kara
a8b98c80 b577750e

+29 -7
+24 -6
fs/notify/fanotify/fanotify_user.c
··· 424 424 * events generated by the listener process itself, without disclosing 425 425 * the pids of other processes. 426 426 */ 427 - if (!capable(CAP_SYS_ADMIN) && 427 + if (FAN_GROUP_FLAG(group, FANOTIFY_UNPRIV) && 428 428 task_tgid(current) != event->pid) 429 429 metadata.pid = 0; 430 430 431 - if (path && path->mnt && path->dentry) { 431 + /* 432 + * For now, fid mode is required for an unprivileged listener and 433 + * fid mode does not report fd in events. Keep this check anyway 434 + * for safety in case fid mode requirement is relaxed in the future 435 + * to allow unprivileged listener to get events with no fd and no fid. 436 + */ 437 + if (!FAN_GROUP_FLAG(group, FANOTIFY_UNPRIV) && 438 + path && path->mnt && path->dentry) { 432 439 fd = create_fd(group, path, &f); 433 440 if (fd < 0) 434 441 return fd; ··· 1047 1040 int f_flags, fd; 1048 1041 unsigned int fid_mode = flags & FANOTIFY_FID_BITS; 1049 1042 unsigned int class = flags & FANOTIFY_CLASS_BITS; 1043 + unsigned int internal_flags = 0; 1050 1044 1051 1045 pr_debug("%s: flags=%x event_f_flags=%x\n", 1052 1046 __func__, flags, event_f_flags); ··· 1061 1053 */ 1062 1054 if ((flags & FANOTIFY_ADMIN_INIT_FLAGS) || !fid_mode) 1063 1055 return -EPERM; 1056 + 1057 + /* 1058 + * Setting the internal flag FANOTIFY_UNPRIV on the group 1059 + * prevents setting mount/filesystem marks on this group and 1060 + * prevents reporting pid and open fd in events. 1061 + */ 1062 + internal_flags |= FANOTIFY_UNPRIV; 1064 1063 } 1065 1064 1066 1065 #ifdef CONFIG_AUDITSYSCALL ··· 1120 1105 goto out_destroy_group; 1121 1106 } 1122 1107 1123 - group->fanotify_data.flags = flags; 1108 + group->fanotify_data.flags = flags | internal_flags; 1124 1109 group->memcg = get_mem_cgroup_from_mm(current->mm); 1125 1110 1126 1111 group->fanotify_data.merge_hash = fanotify_alloc_merge_hash(); ··· 1320 1305 group = f.file->private_data; 1321 1306 1322 1307 /* 1323 - * An unprivileged user is not allowed to watch a mount point nor 1324 - * a filesystem. 1308 + * An unprivileged user is not allowed to setup mount nor filesystem 1309 + * marks. This also includes setting up such marks by a group that 1310 + * was initialized by an unprivileged user. 1325 1311 */ 1326 1312 ret = -EPERM; 1327 - if (!capable(CAP_SYS_ADMIN) && 1313 + if ((!capable(CAP_SYS_ADMIN) || 1314 + FAN_GROUP_FLAG(group, FANOTIFY_UNPRIV)) && 1328 1315 mark_type != FAN_MARK_INODE) 1329 1316 goto fput_and_out; 1330 1317 ··· 1477 1460 max_marks = clamp(max_marks, FANOTIFY_OLD_DEFAULT_MAX_MARKS, 1478 1461 FANOTIFY_DEFAULT_MAX_USER_MARKS); 1479 1462 1463 + BUILD_BUG_ON(FANOTIFY_INIT_FLAGS & FANOTIFY_INTERNAL_GROUP_FLAGS); 1480 1464 BUILD_BUG_ON(HWEIGHT32(FANOTIFY_INIT_FLAGS) != 10); 1481 1465 BUILD_BUG_ON(HWEIGHT32(FANOTIFY_MARK_FLAGS) != 9); 1482 1466
+1 -1
fs/notify/fdinfo.c
··· 144 144 struct fsnotify_group *group = f->private_data; 145 145 146 146 seq_printf(m, "fanotify flags:%x event-flags:%x\n", 147 - group->fanotify_data.flags, 147 + group->fanotify_data.flags & FANOTIFY_INIT_FLAGS, 148 148 group->fanotify_data.f_flags); 149 149 150 150 show_fdinfo(m, f, fanotify_fdinfo);
+4
include/linux/fanotify.h
··· 51 51 #define FANOTIFY_INIT_FLAGS (FANOTIFY_ADMIN_INIT_FLAGS | \ 52 52 FANOTIFY_USER_INIT_FLAGS) 53 53 54 + /* Internal group flags */ 55 + #define FANOTIFY_UNPRIV 0x80000000 56 + #define FANOTIFY_INTERNAL_GROUP_FLAGS (FANOTIFY_UNPRIV) 57 + 54 58 #define FANOTIFY_MARK_TYPE_BITS (FAN_MARK_INODE | FAN_MARK_MOUNT | \ 55 59 FAN_MARK_FILESYSTEM) 56 60