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

fanotify: define struct members to hold response decision context

This patch adds a flag, FAN_INFO and an extensible buffer to provide
additional information about response decisions. The buffer contains
one or more headers defining the information type and the length of the
following information. The patch defines one additional information
type, FAN_RESPONSE_INFO_AUDIT_RULE, to audit a rule number. This will
allow for the creation of other information types in the future if other
users of the API identify different needs.

The kernel can be tested if it supports a given info type by supplying
the complete info extension but setting fd to FAN_NOFD. It will return
the expected size but not issue an audit record.

Suggested-by: Steve Grubb <sgrubb@redhat.com>
Link: https://lore.kernel.org/r/2745105.e9J7NaK4W3@x2
Suggested-by: Jan Kara <jack@suse.cz>
Link: https://lore.kernel.org/r/20201001101219.GE17860@quack2.suse.cz
Tested-by: Steve Grubb <sgrubb@redhat.com>
Acked-by: Steve Grubb <sgrubb@redhat.com>
Signed-off-by: Richard Guy Briggs <rgb@redhat.com>
Signed-off-by: Jan Kara <jack@suse.cz>
Message-Id: <10177cfcae5480926b7176321a28d9da6835b667.1675373475.git.rgb@redhat.com>

authored by

Richard Guy Briggs and committed by
Jan Kara
70529a19 2e0a5471

+107 -23
+4 -1
fs/notify/fanotify/fanotify.c
··· 262 262 } 263 263 264 264 /* userspace responded, convert to something usable */ 265 - switch (event->response & ~FAN_AUDIT) { 265 + switch (event->response & FANOTIFY_RESPONSE_ACCESS) { 266 266 case FAN_ALLOW: 267 267 ret = 0; 268 268 break; ··· 563 563 564 564 pevent->fae.type = FANOTIFY_EVENT_TYPE_PATH_PERM; 565 565 pevent->response = 0; 566 + pevent->hdr.type = FAN_RESPONSE_INFO_NONE; 567 + pevent->hdr.pad = 0; 568 + pevent->hdr.len = 0; 566 569 pevent->state = FAN_EVENT_INIT; 567 570 pevent->path = *path; 568 571 path_get(path);
+4
fs/notify/fanotify/fanotify.h
··· 428 428 u32 response; /* userspace answer to the event */ 429 429 unsigned short state; /* state of the event */ 430 430 int fd; /* fd we passed to userspace for this event */ 431 + union { 432 + struct fanotify_response_info_header hdr; 433 + struct fanotify_response_info_audit_rule audit_rule; 434 + }; 431 435 }; 432 436 433 437 static inline struct fanotify_perm_event *
+65 -21
fs/notify/fanotify/fanotify_user.c
··· 283 283 return client_fd; 284 284 } 285 285 286 + static int process_access_response_info(const char __user *info, 287 + size_t info_len, 288 + struct fanotify_response_info_audit_rule *friar) 289 + { 290 + if (info_len != sizeof(*friar)) 291 + return -EINVAL; 292 + 293 + if (copy_from_user(friar, info, sizeof(*friar))) 294 + return -EFAULT; 295 + 296 + if (friar->hdr.type != FAN_RESPONSE_INFO_AUDIT_RULE) 297 + return -EINVAL; 298 + if (friar->hdr.pad != 0) 299 + return -EINVAL; 300 + if (friar->hdr.len != sizeof(*friar)) 301 + return -EINVAL; 302 + 303 + return info_len; 304 + } 305 + 286 306 /* 287 307 * Finish processing of permission event by setting it to ANSWERED state and 288 308 * drop group->notification_lock. 289 309 */ 290 310 static void finish_permission_event(struct fsnotify_group *group, 291 - struct fanotify_perm_event *event, 292 - u32 response) 311 + struct fanotify_perm_event *event, u32 response, 312 + struct fanotify_response_info_audit_rule *friar) 293 313 __releases(&group->notification_lock) 294 314 { 295 315 bool destroy = false; 296 316 297 317 assert_spin_locked(&group->notification_lock); 298 - event->response = response; 318 + event->response = response & ~FAN_INFO; 319 + if (response & FAN_INFO) 320 + memcpy(&event->audit_rule, friar, sizeof(*friar)); 321 + 299 322 if (event->state == FAN_EVENT_CANCELED) 300 323 destroy = true; 301 324 else ··· 329 306 } 330 307 331 308 static int process_access_response(struct fsnotify_group *group, 332 - struct fanotify_response *response_struct) 309 + struct fanotify_response *response_struct, 310 + const char __user *info, 311 + size_t info_len) 333 312 { 334 313 struct fanotify_perm_event *event; 335 314 int fd = response_struct->fd; 336 315 u32 response = response_struct->response; 316 + int ret = info_len; 317 + struct fanotify_response_info_audit_rule friar; 337 318 338 - pr_debug("%s: group=%p fd=%d response=%u\n", __func__, group, 339 - fd, response); 319 + pr_debug("%s: group=%p fd=%d response=%u buf=%p size=%zu\n", __func__, 320 + group, fd, response, info, info_len); 340 321 /* 341 322 * make sure the response is valid, if invalid we do nothing and either 342 323 * userspace can send a valid response or we will clean it up after the 343 324 * timeout 344 325 */ 345 - switch (response & ~FAN_AUDIT) { 326 + if (response & ~FANOTIFY_RESPONSE_VALID_MASK) 327 + return -EINVAL; 328 + 329 + switch (response & FANOTIFY_RESPONSE_ACCESS) { 346 330 case FAN_ALLOW: 347 331 case FAN_DENY: 348 332 break; ··· 357 327 return -EINVAL; 358 328 } 359 329 360 - if (fd < 0) 330 + if ((response & FAN_AUDIT) && !FAN_GROUP_FLAG(group, FAN_ENABLE_AUDIT)) 361 331 return -EINVAL; 362 332 363 - if ((response & FAN_AUDIT) && !FAN_GROUP_FLAG(group, FAN_ENABLE_AUDIT)) 333 + if (response & FAN_INFO) { 334 + ret = process_access_response_info(info, info_len, &friar); 335 + if (ret < 0) 336 + return ret; 337 + if (fd == FAN_NOFD) 338 + return ret; 339 + } else { 340 + ret = 0; 341 + } 342 + 343 + if (fd < 0) 364 344 return -EINVAL; 365 345 366 346 spin_lock(&group->notification_lock); ··· 380 340 continue; 381 341 382 342 list_del_init(&event->fae.fse.list); 383 - finish_permission_event(group, event, response); 343 + finish_permission_event(group, event, response, &friar); 384 344 wake_up(&group->fanotify_data.access_waitq); 385 - return 0; 345 + return ret; 386 346 } 387 347 spin_unlock(&group->notification_lock); 388 348 ··· 844 804 if (ret <= 0) { 845 805 spin_lock(&group->notification_lock); 846 806 finish_permission_event(group, 847 - FANOTIFY_PERM(event), FAN_DENY); 807 + FANOTIFY_PERM(event), FAN_DENY, NULL); 848 808 wake_up(&group->fanotify_data.access_waitq); 849 809 } else { 850 810 spin_lock(&group->notification_lock); ··· 867 827 868 828 static ssize_t fanotify_write(struct file *file, const char __user *buf, size_t count, loff_t *pos) 869 829 { 870 - struct fanotify_response response = { .fd = -1, .response = -1 }; 830 + struct fanotify_response response; 871 831 struct fsnotify_group *group; 872 832 int ret; 833 + const char __user *info_buf = buf + sizeof(struct fanotify_response); 834 + size_t info_len; 873 835 874 836 if (!IS_ENABLED(CONFIG_FANOTIFY_ACCESS_PERMISSIONS)) 875 837 return -EINVAL; 876 838 877 839 group = file->private_data; 878 840 841 + pr_debug("%s: group=%p count=%zu\n", __func__, group, count); 842 + 879 843 if (count < sizeof(response)) 880 844 return -EINVAL; 881 845 882 - count = sizeof(response); 883 - 884 - pr_debug("%s: group=%p count=%zu\n", __func__, group, count); 885 - 886 - if (copy_from_user(&response, buf, count)) 846 + if (copy_from_user(&response, buf, sizeof(response))) 887 847 return -EFAULT; 888 848 889 - ret = process_access_response(group, &response); 849 + info_len = count - sizeof(response); 850 + 851 + ret = process_access_response(group, &response, info_buf, info_len); 890 852 if (ret < 0) 891 853 count = ret; 854 + else 855 + count = sizeof(response) + ret; 892 856 893 857 return count; 894 858 } ··· 920 876 event = list_first_entry(&group->fanotify_data.access_list, 921 877 struct fanotify_perm_event, fae.fse.list); 922 878 list_del_init(&event->fae.fse.list); 923 - finish_permission_event(group, event, FAN_ALLOW); 879 + finish_permission_event(group, event, FAN_ALLOW, NULL); 924 880 spin_lock(&group->notification_lock); 925 881 } 926 882 ··· 937 893 fsnotify_destroy_event(group, fsn_event); 938 894 } else { 939 895 finish_permission_event(group, FANOTIFY_PERM(event), 940 - FAN_ALLOW); 896 + FAN_ALLOW, NULL); 941 897 } 942 898 spin_lock(&group->notification_lock); 943 899 }
+5
include/linux/fanotify.h
··· 122 122 #define ALL_FANOTIFY_EVENT_BITS (FANOTIFY_OUTGOING_EVENTS | \ 123 123 FANOTIFY_EVENT_FLAGS) 124 124 125 + /* These masks check for invalid bits in permission responses. */ 126 + #define FANOTIFY_RESPONSE_ACCESS (FAN_ALLOW | FAN_DENY) 127 + #define FANOTIFY_RESPONSE_FLAGS (FAN_AUDIT | FAN_INFO) 128 + #define FANOTIFY_RESPONSE_VALID_MASK (FANOTIFY_RESPONSE_ACCESS | FANOTIFY_RESPONSE_FLAGS) 129 + 125 130 /* Do not use these old uapi constants internally */ 126 131 #undef FAN_ALL_CLASS_BITS 127 132 #undef FAN_ALL_INIT_FLAGS
+29 -1
include/uapi/linux/fanotify.h
··· 188 188 __u32 error_count; 189 189 }; 190 190 191 + /* 192 + * User space may need to record additional information about its decision. 193 + * The extra information type records what kind of information is included. 194 + * The default is none. We also define an extra information buffer whose 195 + * size is determined by the extra information type. 196 + * 197 + * If the information type is Audit Rule, then the information following 198 + * is the rule number that triggered the user space decision that 199 + * requires auditing. 200 + */ 201 + 202 + #define FAN_RESPONSE_INFO_NONE 0 203 + #define FAN_RESPONSE_INFO_AUDIT_RULE 1 204 + 191 205 struct fanotify_response { 192 206 __s32 fd; 193 207 __u32 response; 194 208 }; 195 209 210 + struct fanotify_response_info_header { 211 + __u8 type; 212 + __u8 pad; 213 + __u16 len; 214 + }; 215 + 216 + struct fanotify_response_info_audit_rule { 217 + struct fanotify_response_info_header hdr; 218 + __u32 rule_number; 219 + __u32 subj_trust; 220 + __u32 obj_trust; 221 + }; 222 + 196 223 /* Legit userspace responses to a _PERM event */ 197 224 #define FAN_ALLOW 0x01 198 225 #define FAN_DENY 0x02 199 - #define FAN_AUDIT 0x10 /* Bit mask to create audit record for result */ 226 + #define FAN_AUDIT 0x10 /* Bitmask to create audit record for result */ 227 + #define FAN_INFO 0x20 /* Bitmask to indicate additional information */ 200 228 201 229 /* No fd set in event */ 202 230 #define FAN_NOFD -1