diff options
| author | Richard Guy Briggs <rgb@redhat.com> | 2017-08-23 07:03:39 -0400 |
|---|---|---|
| committer | Paul Moore <paul@paul-moore.com> | 2017-11-10 16:08:56 -0500 |
| commit | 42d5e37654e4cdb9fb2e2f3ab30045fee35c42d8 (patch) | |
| tree | 5fa06c6189f135561616208016519bb61c309b1b /kernel | |
| parent | f7b53637c090bd8ce2dc74ad0f3aa1898aff2524 (diff) | |
audit: filter PATH records keyed on filesystem magic
Tracefs or debugfs were causing hundreds to thousands of PATH records to
be associated with the init_module and finit_module SYSCALL records on a
few modules when the following rule was in place for startup:
-a always,exit -F arch=x86_64 -S init_module -F key=mod-load
Provide a method to ignore these large number of PATH records from
overwhelming the logs if they are not of interest. Introduce a new
filter list "AUDIT_FILTER_FS", with a new field type AUDIT_FSTYPE,
which keys off the filesystem 4-octet hexadecimal magic identifier to
filter specific filesystem PATH records.
An example rule would look like:
-a never,filesystem -F fstype=0x74726163 -F key=ignore_tracefs
-a never,filesystem -F fstype=0x64626720 -F key=ignore_debugfs
Arguably the better way to address this issue is to disable tracefs and
debugfs on boot from production systems.
See: https://github.com/linux-audit/audit-kernel/issues/16
See: https://github.com/linux-audit/audit-userspace/issues/8
Test case: https://github.com/linux-audit/audit-testsuite/issues/42
Signed-off-by: Richard Guy Briggs <rgb@redhat.com>
[PM: fixed the whitespace damage in kernel/auditsc.c]
Signed-off-by: Paul Moore <paul@paul-moore.com>
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/auditfilter.c | 39 | ||||
| -rw-r--r-- | kernel/auditsc.c | 23 |
2 files changed, 55 insertions, 7 deletions
diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index 0b0aa5854dac..4a1758adb222 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c | |||
| @@ -56,7 +56,8 @@ struct list_head audit_filter_list[AUDIT_NR_FILTERS] = { | |||
| 56 | LIST_HEAD_INIT(audit_filter_list[3]), | 56 | LIST_HEAD_INIT(audit_filter_list[3]), |
| 57 | LIST_HEAD_INIT(audit_filter_list[4]), | 57 | LIST_HEAD_INIT(audit_filter_list[4]), |
| 58 | LIST_HEAD_INIT(audit_filter_list[5]), | 58 | LIST_HEAD_INIT(audit_filter_list[5]), |
| 59 | #if AUDIT_NR_FILTERS != 6 | 59 | LIST_HEAD_INIT(audit_filter_list[6]), |
| 60 | #if AUDIT_NR_FILTERS != 7 | ||
| 60 | #error Fix audit_filter_list initialiser | 61 | #error Fix audit_filter_list initialiser |
| 61 | #endif | 62 | #endif |
| 62 | }; | 63 | }; |
| @@ -67,6 +68,7 @@ static struct list_head audit_rules_list[AUDIT_NR_FILTERS] = { | |||
| 67 | LIST_HEAD_INIT(audit_rules_list[3]), | 68 | LIST_HEAD_INIT(audit_rules_list[3]), |
| 68 | LIST_HEAD_INIT(audit_rules_list[4]), | 69 | LIST_HEAD_INIT(audit_rules_list[4]), |
| 69 | LIST_HEAD_INIT(audit_rules_list[5]), | 70 | LIST_HEAD_INIT(audit_rules_list[5]), |
| 71 | LIST_HEAD_INIT(audit_rules_list[6]), | ||
| 70 | }; | 72 | }; |
| 71 | 73 | ||
| 72 | DEFINE_MUTEX(audit_filter_mutex); | 74 | DEFINE_MUTEX(audit_filter_mutex); |
| @@ -263,6 +265,7 @@ static inline struct audit_entry *audit_to_entry_common(struct audit_rule_data * | |||
| 263 | #endif | 265 | #endif |
| 264 | case AUDIT_FILTER_USER: | 266 | case AUDIT_FILTER_USER: |
| 265 | case AUDIT_FILTER_TYPE: | 267 | case AUDIT_FILTER_TYPE: |
| 268 | case AUDIT_FILTER_FS: | ||
| 266 | ; | 269 | ; |
| 267 | } | 270 | } |
| 268 | if (unlikely(rule->action == AUDIT_POSSIBLE)) { | 271 | if (unlikely(rule->action == AUDIT_POSSIBLE)) { |
| @@ -338,6 +341,21 @@ static int audit_field_valid(struct audit_entry *entry, struct audit_field *f) | |||
| 338 | entry->rule.listnr != AUDIT_FILTER_USER) | 341 | entry->rule.listnr != AUDIT_FILTER_USER) |
| 339 | return -EINVAL; | 342 | return -EINVAL; |
| 340 | break; | 343 | break; |
| 344 | case AUDIT_FSTYPE: | ||
| 345 | if (entry->rule.listnr != AUDIT_FILTER_FS) | ||
| 346 | return -EINVAL; | ||
| 347 | break; | ||
| 348 | } | ||
| 349 | |||
| 350 | switch(entry->rule.listnr) { | ||
| 351 | case AUDIT_FILTER_FS: | ||
| 352 | switch(f->type) { | ||
| 353 | case AUDIT_FSTYPE: | ||
| 354 | case AUDIT_FILTERKEY: | ||
| 355 | break; | ||
| 356 | default: | ||
| 357 | return -EINVAL; | ||
| 358 | } | ||
| 341 | } | 359 | } |
| 342 | 360 | ||
| 343 | switch(f->type) { | 361 | switch(f->type) { |
| @@ -391,6 +409,7 @@ static int audit_field_valid(struct audit_entry *entry, struct audit_field *f) | |||
| 391 | return -EINVAL; | 409 | return -EINVAL; |
| 392 | /* FALL THROUGH */ | 410 | /* FALL THROUGH */ |
| 393 | case AUDIT_ARCH: | 411 | case AUDIT_ARCH: |
| 412 | case AUDIT_FSTYPE: | ||
| 394 | if (f->op != Audit_not_equal && f->op != Audit_equal) | 413 | if (f->op != Audit_not_equal && f->op != Audit_equal) |
| 395 | return -EINVAL; | 414 | return -EINVAL; |
| 396 | break; | 415 | break; |
| @@ -910,10 +929,13 @@ static inline int audit_add_rule(struct audit_entry *entry) | |||
| 910 | #ifdef CONFIG_AUDITSYSCALL | 929 | #ifdef CONFIG_AUDITSYSCALL |
| 911 | int dont_count = 0; | 930 | int dont_count = 0; |
| 912 | 931 | ||
| 913 | /* If either of these, don't count towards total */ | 932 | /* If any of these, don't count towards total */ |
| 914 | if (entry->rule.listnr == AUDIT_FILTER_USER || | 933 | switch(entry->rule.listnr) { |
| 915 | entry->rule.listnr == AUDIT_FILTER_TYPE) | 934 | case AUDIT_FILTER_USER: |
| 935 | case AUDIT_FILTER_TYPE: | ||
| 936 | case AUDIT_FILTER_FS: | ||
| 916 | dont_count = 1; | 937 | dont_count = 1; |
| 938 | } | ||
| 917 | #endif | 939 | #endif |
| 918 | 940 | ||
| 919 | mutex_lock(&audit_filter_mutex); | 941 | mutex_lock(&audit_filter_mutex); |
| @@ -989,10 +1011,13 @@ int audit_del_rule(struct audit_entry *entry) | |||
| 989 | #ifdef CONFIG_AUDITSYSCALL | 1011 | #ifdef CONFIG_AUDITSYSCALL |
| 990 | int dont_count = 0; | 1012 | int dont_count = 0; |
| 991 | 1013 | ||
| 992 | /* If either of these, don't count towards total */ | 1014 | /* If any of these, don't count towards total */ |
| 993 | if (entry->rule.listnr == AUDIT_FILTER_USER || | 1015 | switch(entry->rule.listnr) { |
| 994 | entry->rule.listnr == AUDIT_FILTER_TYPE) | 1016 | case AUDIT_FILTER_USER: |
| 1017 | case AUDIT_FILTER_TYPE: | ||
| 1018 | case AUDIT_FILTER_FS: | ||
| 995 | dont_count = 1; | 1019 | dont_count = 1; |
| 1020 | } | ||
| 996 | #endif | 1021 | #endif |
| 997 | 1022 | ||
| 998 | mutex_lock(&audit_filter_mutex); | 1023 | mutex_lock(&audit_filter_mutex); |
diff --git a/kernel/auditsc.c b/kernel/auditsc.c index aac1a41f82bd..c9bb29e17335 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c | |||
| @@ -1869,10 +1869,33 @@ void __audit_inode_child(struct inode *parent, | |||
| 1869 | struct inode *inode = d_backing_inode(dentry); | 1869 | struct inode *inode = d_backing_inode(dentry); |
| 1870 | const char *dname = dentry->d_name.name; | 1870 | const char *dname = dentry->d_name.name; |
| 1871 | struct audit_names *n, *found_parent = NULL, *found_child = NULL; | 1871 | struct audit_names *n, *found_parent = NULL, *found_child = NULL; |
| 1872 | struct audit_entry *e; | ||
| 1873 | struct list_head *list = &audit_filter_list[AUDIT_FILTER_FS]; | ||
| 1874 | int i; | ||
| 1872 | 1875 | ||
| 1873 | if (!context->in_syscall) | 1876 | if (!context->in_syscall) |
| 1874 | return; | 1877 | return; |
| 1875 | 1878 | ||
| 1879 | rcu_read_lock(); | ||
| 1880 | if (!list_empty(list)) { | ||
| 1881 | list_for_each_entry_rcu(e, list, list) { | ||
| 1882 | for (i = 0; i < e->rule.field_count; i++) { | ||
| 1883 | struct audit_field *f = &e->rule.fields[i]; | ||
| 1884 | |||
| 1885 | if (f->type == AUDIT_FSTYPE) { | ||
| 1886 | if (audit_comparator(parent->i_sb->s_magic, | ||
| 1887 | f->op, f->val)) { | ||
| 1888 | if (e->rule.action == AUDIT_NEVER) { | ||
| 1889 | rcu_read_unlock(); | ||
| 1890 | return; | ||
| 1891 | } | ||
| 1892 | } | ||
| 1893 | } | ||
| 1894 | } | ||
| 1895 | } | ||
| 1896 | } | ||
| 1897 | rcu_read_unlock(); | ||
| 1898 | |||
| 1876 | if (inode) | 1899 | if (inode) |
| 1877 | handle_one(inode); | 1900 | handle_one(inode); |
| 1878 | 1901 | ||
