Pull fsnotify updates from Jan Kara: "Support for auditing decisions regarding fanotify permission events" * tag 'fsnotify_for_v6.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs: fanotify,audit: Allow audit to use the full permission event response fanotify: define struct members to hold response decision context fanotify: Ensure consistent variable type for response
Unified · Split
| @@ -262,7 +262,7 @@ static int fanotify_get_response(struct fsnotify_group *group, · 261 unmodified lines | ||
| 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; |
| @@ -273,7 +273,8 @@ static int fanotify_get_response(struct fsnotify_group *group, · 4 unmodified lines | ||
| 273 | 273 | |
| 274 | 274 | /* Check if the response should be audited */ |
| 275 | 275 | if (event->response & FAN_AUDIT) |
| 276 | − audit_fanotify(event->response & ~FAN_AUDIT); | |
| 276 | + audit_fanotify(event->response & ~FAN_AUDIT, | |
| 277 | + &event->audit_rule); | |
| 277 | 278 | |
| 278 | 279 | pr_debug("%s: group=%p event=%p about to return ret=%d\n", __func__, |
| 279 | 280 | group, event, ret); |
| @@ -563,6 +564,9 @@ static struct fanotify_event *fanotify_alloc_perm_event(const struct path *path, · 283 unmodified lines | ||
| 563 | 564 | |
| 564 | 565 | pevent->fae.type = FANOTIFY_EVENT_TYPE_PATH_PERM; |
| 565 | 566 | pevent->response = 0; |
| 567 | + pevent->hdr.type = FAN_RESPONSE_INFO_NONE; | |
| 568 | + pevent->hdr.pad = 0; | |
| 569 | + pevent->hdr.len = 0; | |
| 566 | 570 | pevent->state = FAN_EVENT_INIT; |
| 567 | 571 | pevent->path = *path; |
| 568 | 572 | path_get(path); |
| @@ -425,9 +425,13 @@ FANOTIFY_PE(struct fanotify_event *event) · 424 unmodified lines | ||
| 425 | 425 | struct fanotify_perm_event { |
| 426 | 426 | struct fanotify_event fae; |
| 427 | 427 | struct path path; |
| 428 | − unsigned short response; /* userspace answer to the event */ | |
| 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 * |
| @@ -283,19 +283,42 @@ static int create_fd(struct fsnotify_group *group, const struct path *path, · 282 unmodified lines | ||
| 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 | − unsigned int 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 |
| @@ -306,20 +329,27 @@ static void finish_permission_event(struct fsnotify_group *group, · 4 unmodified lines | ||
| 306 | 329 | } |
| 307 | 330 | |
| 308 | 331 | static int process_access_response(struct fsnotify_group *group, |
| 309 | − struct fanotify_response *response_struct) | |
| 332 | + struct fanotify_response *response_struct, | |
| 333 | + const char __user *info, | |
| 334 | + size_t info_len) | |
| 310 | 335 | { |
| 311 | 336 | struct fanotify_perm_event *event; |
| 312 | 337 | int fd = response_struct->fd; |
| 313 | − int response = response_struct->response; | |
| 338 | + u32 response = response_struct->response; | |
| 339 | + int ret = info_len; | |
| 340 | + struct fanotify_response_info_audit_rule friar; | |
| 314 | 341 | |
| 315 | − pr_debug("%s: group=%p fd=%d response=%d\n", __func__, group, | |
| 316 | − fd, response); | |
| 342 | + pr_debug("%s: group=%p fd=%d response=%u buf=%p size=%zu\n", __func__, | |
| 343 | + group, fd, response, info, info_len); | |
| 317 | 344 | /* |
| 318 | 345 | * make sure the response is valid, if invalid we do nothing and either |
| 319 | 346 | * userspace can send a valid response or we will clean it up after the |
| 320 | 347 | * timeout |
| 321 | 348 | */ |
| 322 | − switch (response & ~FAN_AUDIT) { | |
| 349 | + if (response & ~FANOTIFY_RESPONSE_VALID_MASK) | |
| 350 | + return -EINVAL; | |
| 351 | + | |
| 352 | + switch (response & FANOTIFY_RESPONSE_ACCESS) { | |
| 323 | 353 | case FAN_ALLOW: |
| 324 | 354 | case FAN_DENY: |
| 325 | 355 | break; |
| @@ -327,10 +357,20 @@ static int process_access_response(struct fsnotify_group *group, · 1 unmodified line | ||
| 327 | 357 | return -EINVAL; |
| 328 | 358 | } |
| 329 | 359 | |
| 330 | − if (fd < 0) | |
| 360 | + if ((response & FAN_AUDIT) && !FAN_GROUP_FLAG(group, FAN_ENABLE_AUDIT)) | |
| 331 | 361 | return -EINVAL; |
| 332 | 362 | |
| 333 | − if ((response & FAN_AUDIT) && !FAN_GROUP_FLAG(group, FAN_ENABLE_AUDIT)) | |
| 363 | + if (response & FAN_INFO) { | |
| 364 | + ret = process_access_response_info(info, info_len, &friar); | |
| 365 | + if (ret < 0) | |
| 366 | + return ret; | |
| 367 | + if (fd == FAN_NOFD) | |
| 368 | + return ret; | |
| 369 | + } else { | |
| 370 | + ret = 0; | |
| 371 | + } | |
| 372 | + | |
| 373 | + if (fd < 0) | |
| 334 | 374 | return -EINVAL; |
| 335 | 375 | |
| 336 | 376 | spin_lock(&group->notification_lock); |
| @@ -340,9 +380,9 @@ static int process_access_response(struct fsnotify_group *group, · 3 unmodified lines | ||
| 340 | 380 | continue; |
| 341 | 381 | |
| 342 | 382 | list_del_init(&event->fae.fse.list); |
| 343 | − finish_permission_event(group, event, response); | |
| 383 | + finish_permission_event(group, event, response, &friar); | |
| 344 | 384 | wake_up(&group->fanotify_data.access_waitq); |
| 345 | − return 0; | |
| 385 | + return ret; | |
| 346 | 386 | } |
| 347 | 387 | spin_unlock(&group->notification_lock); |
| 348 | 388 | |
| @@ -804,7 +844,7 @@ static ssize_t fanotify_read(struct file *file, char __user *buf, · 455 unmodified lines | ||
| 804 | 844 | if (ret <= 0) { |
| 805 | 845 | spin_lock(&group->notification_lock); |
| 806 | 846 | finish_permission_event(group, |
| 807 | − FANOTIFY_PERM(event), FAN_DENY); | |
| 847 | + FANOTIFY_PERM(event), FAN_DENY, NULL); | |
| 808 | 848 | wake_up(&group->fanotify_data.access_waitq); |
| 809 | 849 | } else { |
| 810 | 850 | spin_lock(&group->notification_lock); |
| @@ -827,28 +867,32 @@ static ssize_t fanotify_read(struct file *file, char __user *buf, · 16 unmodified lines | ||
| 827 | 867 | |
| 828 | 868 | static ssize_t fanotify_write(struct file *file, const char __user *buf, size_t count, loff_t *pos) |
| 829 | 869 | { |
| 830 | − struct fanotify_response response = { .fd = -1, .response = -1 }; | |
| 870 | + struct fanotify_response response; | |
| 831 | 871 | struct fsnotify_group *group; |
| 832 | 872 | int ret; |
| 873 | + const char __user *info_buf = buf + sizeof(struct fanotify_response); | |
| 874 | + size_t info_len; | |
| 833 | 875 | |
| 834 | 876 | if (!IS_ENABLED(CONFIG_FANOTIFY_ACCESS_PERMISSIONS)) |
| 835 | 877 | return -EINVAL; |
| 836 | 878 | |
| 837 | 879 | group = file->private_data; |
| 838 | 880 | |
| 881 | + pr_debug("%s: group=%p count=%zu\n", __func__, group, count); | |
| 882 | + | |
| 839 | 883 | if (count < sizeof(response)) |
| 840 | 884 | return -EINVAL; |
| 841 | 885 | |
| 842 | − count = sizeof(response); | |
| 843 | − | |
| 844 | − pr_debug("%s: group=%p count=%zu\n", __func__, group, count); | |
| 845 | − | |
| 846 | − if (copy_from_user(&response, buf, count)) | |
| 886 | + if (copy_from_user(&response, buf, sizeof(response))) | |
| 847 | 887 | return -EFAULT; |
| 848 | 888 | |
| 849 | − ret = process_access_response(group, &response); | |
| 889 | + info_len = count - sizeof(response); | |
| 890 | + | |
| 891 | + ret = process_access_response(group, &response, info_buf, info_len); | |
| 850 | 892 | if (ret < 0) |
| 851 | 893 | count = ret; |
| 894 | + else | |
| 895 | + count = sizeof(response) + ret; | |
| 852 | 896 | |
| 853 | 897 | return count; |
| 854 | 898 | } |
| @@ -876,7 +920,7 @@ static int fanotify_release(struct inode *ignored, struct file *file) · 21 unmodified lines | ||
| 876 | 920 | event = list_first_entry(&group->fanotify_data.access_list, |
| 877 | 921 | struct fanotify_perm_event, fae.fse.list); |
| 878 | 922 | list_del_init(&event->fae.fse.list); |
| 879 | − finish_permission_event(group, event, FAN_ALLOW); | |
| 923 | + finish_permission_event(group, event, FAN_ALLOW, NULL); | |
| 880 | 924 | spin_lock(&group->notification_lock); |
| 881 | 925 | } |
| 882 | 926 | |
| @@ -893,7 +937,7 @@ static int fanotify_release(struct inode *ignored, struct file *file) · 10 unmodified lines | ||
| 893 | 937 | fsnotify_destroy_event(group, fsn_event); |
| 894 | 938 | } else { |
| 895 | 939 | finish_permission_event(group, FANOTIFY_PERM(event), |
| 896 | − FAN_ALLOW); | |
| 940 | + FAN_ALLOW, NULL); | |
| 897 | 941 | } |
| 898 | 942 | spin_lock(&group->notification_lock); |
| 899 | 943 | } |
| @@ -14,6 +14,7 @@ · 13 unmodified lines | ||
| 14 | 14 | #include <linux/audit_arch.h> |
| 15 | 15 | #include <uapi/linux/audit.h> |
| 16 | 16 | #include <uapi/linux/netfilter/nf_tables.h> |
| 17 | +#include <uapi/linux/fanotify.h> | |
| 17 | 18 | |
| 18 | 19 | #define AUDIT_INO_UNSET ((unsigned long)-1) |
| 19 | 20 | #define AUDIT_DEV_UNSET ((dev_t)-1) |
| @@ -416,7 +417,7 @@ extern void __audit_log_capset(const struct cred *new, const struct cred *old); · 396 unmodified lines | ||
| 416 | 417 | extern void __audit_mmap_fd(int fd, int flags); |
| 417 | 418 | extern void __audit_openat2_how(struct open_how *how); |
| 418 | 419 | extern void __audit_log_kern_module(char *name); |
| 419 | −extern void __audit_fanotify(unsigned int response); | |
| 420 | +extern void __audit_fanotify(u32 response, struct fanotify_response_info_audit_rule *friar); | |
| 420 | 421 | extern void __audit_tk_injoffset(struct timespec64 offset); |
| 421 | 422 | extern void __audit_ntp_log(const struct audit_ntp_data *ad); |
| 422 | 423 | extern void __audit_log_nfcfg(const char *name, u8 af, unsigned int nentries, |
| @@ -523,10 +524,10 @@ static inline void audit_log_kern_module(char *name) · 100 unmodified lines | ||
| 523 | 524 | __audit_log_kern_module(name); |
| 524 | 525 | } |
| 525 | 526 | |
| 526 | −static inline void audit_fanotify(unsigned int response) | |
| 527 | +static inline void audit_fanotify(u32 response, struct fanotify_response_info_audit_rule *friar) | |
| 527 | 528 | { |
| 528 | 529 | if (!audit_dummy_context()) |
| 529 | − __audit_fanotify(response); | |
| 530 | + __audit_fanotify(response, friar); | |
| 530 | 531 | } |
| 531 | 532 | |
| 532 | 533 | static inline void audit_tk_injoffset(struct timespec64 offset) |
| @@ -679,7 +680,7 @@ static inline void audit_log_kern_module(char *name) · 146 unmodified lines | ||
| 679 | 680 | { |
| 680 | 681 | } |
| 681 | 682 | |
| 682 | −static inline void audit_fanotify(unsigned int response) | |
| 683 | +static inline void audit_fanotify(u32 response, struct fanotify_response_info_audit_rule *friar) | |
| 683 | 684 | { } |
| 684 | 685 | |
| 685 | 686 | static inline void audit_tk_injoffset(struct timespec64 offset) |
| @@ -122,6 +122,11 @@ · 121 unmodified lines | ||
| 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 |
| @@ -188,15 +188,43 @@ struct fanotify_event_info_error { · 187 unmodified lines | ||
| 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 |
| @@ -64,6 +64,7 @@ · 63 unmodified lines | ||
| 64 | 64 | #include <uapi/linux/limits.h> |
| 65 | 65 | #include <uapi/linux/netfilter/nf_tables.h> |
| 66 | 66 | #include <uapi/linux/openat2.h> // struct open_how |
| 67 | +#include <uapi/linux/fanotify.h> | |
| 67 | 68 | |
| 68 | 69 | #include "audit.h" |
| 69 | 70 | |
| @@ -2877,10 +2878,21 @@ void __audit_log_kern_module(char *name) · 2807 unmodified lines | ||
| 2877 | 2878 | context->type = AUDIT_KERN_MODULE; |
| 2878 | 2879 | } |
| 2879 | 2880 | |
| 2880 | −void __audit_fanotify(unsigned int response) | |
| 2881 | +void __audit_fanotify(u32 response, struct fanotify_response_info_audit_rule *friar) | |
| 2881 | 2882 | { |
| 2882 | − audit_log(audit_context(), GFP_KERNEL, | |
| 2883 | − AUDIT_FANOTIFY, "resp=%u", response); | |
| 2883 | + /* {subj,obj}_trust values are {0,1,2}: no,yes,unknown */ | |
| 2884 | + switch (friar->hdr.type) { | |
| 2885 | + case FAN_RESPONSE_INFO_NONE: | |
| 2886 | + audit_log(audit_context(), GFP_KERNEL, AUDIT_FANOTIFY, | |
| 2887 | + "resp=%u fan_type=%u fan_info=0 subj_trust=2 obj_trust=2", | |
| 2888 | + response, FAN_RESPONSE_INFO_NONE); | |
| 2889 | + break; | |
| 2890 | + case FAN_RESPONSE_INFO_AUDIT_RULE: | |
| 2891 | + audit_log(audit_context(), GFP_KERNEL, AUDIT_FANOTIFY, | |
| 2892 | + "resp=%u fan_type=%u fan_info=%X subj_trust=%u obj_trust=%u", | |
| 2893 | + response, friar->hdr.type, friar->rule_number, | |
| 2894 | + friar->subj_trust, friar->obj_trust); | |
| 2895 | + } | |
| 2884 | 2896 | } |
| 2885 | 2897 | |
| 2886 | 2898 | void __audit_tk_injoffset(struct timespec64 offset) |