audit: filter PATH records keyed on filesystem magic
authorRichard Guy Briggs <rgb@redhat.com>
Wed, 23 Aug 2017 11:03:39 +0000 (07:03 -0400)
committerPaul Moore <paul@paul-moore.com>
Fri, 10 Nov 2017 21:08:56 +0000 (16:08 -0500)
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>
include/uapi/linux/audit.h
kernel/auditfilter.c
kernel/auditsc.c

index 0714a66f0e0cd6018758d991163b1de1f6326dd2..be711341938edba0ccf9a40f285b94c317511c3b 100644 (file)
 #define AUDIT_FILTER_WATCH     0x03    /* Apply rule to file system watches */
 #define AUDIT_FILTER_EXIT      0x04    /* Apply rule at syscall exit */
 #define AUDIT_FILTER_TYPE      0x05    /* Apply rule at audit_log_start */
+#define AUDIT_FILTER_FS                0x06    /* Apply rule at __audit_inode_child */
 
-#define AUDIT_NR_FILTERS       6
+#define AUDIT_NR_FILTERS       7
 
 #define AUDIT_FILTER_PREPEND   0x10    /* Prepend to front of list */
 
 #define AUDIT_OBJ_LEV_HIGH     23
 #define AUDIT_LOGINUID_SET     24
 #define AUDIT_SESSIONID        25      /* Session ID */
+#define AUDIT_FSTYPE   26      /* FileSystem Type */
 
                                /* These are ONLY useful when checking
                                 * at syscall exit time (AUDIT_AT_EXIT). */
@@ -335,13 +337,15 @@ enum {
 #define AUDIT_FEATURE_BITMAP_EXCLUDE_EXTEND    0x00000008
 #define AUDIT_FEATURE_BITMAP_SESSIONID_FILTER  0x00000010
 #define AUDIT_FEATURE_BITMAP_LOST_RESET                0x00000020
+#define AUDIT_FEATURE_BITMAP_FILTER_FS         0x00000040
 
 #define AUDIT_FEATURE_BITMAP_ALL (AUDIT_FEATURE_BITMAP_BACKLOG_LIMIT | \
                                  AUDIT_FEATURE_BITMAP_BACKLOG_WAIT_TIME | \
                                  AUDIT_FEATURE_BITMAP_EXECUTABLE_PATH | \
                                  AUDIT_FEATURE_BITMAP_EXCLUDE_EXTEND | \
                                  AUDIT_FEATURE_BITMAP_SESSIONID_FILTER | \
-                                 AUDIT_FEATURE_BITMAP_LOST_RESET)
+                                 AUDIT_FEATURE_BITMAP_LOST_RESET | \
+                                 AUDIT_FEATURE_BITMAP_FILTER_FS)
 
 /* deprecated: AUDIT_VERSION_* */
 #define AUDIT_VERSION_LATEST           AUDIT_FEATURE_BITMAP_ALL
index 0b0aa5854dac1ed41959f9383af95d4097ad4646..4a1758adb22249fcdc11ffba75263367ccde8936 100644 (file)
@@ -56,7 +56,8 @@ struct list_head audit_filter_list[AUDIT_NR_FILTERS] = {
        LIST_HEAD_INIT(audit_filter_list[3]),
        LIST_HEAD_INIT(audit_filter_list[4]),
        LIST_HEAD_INIT(audit_filter_list[5]),
-#if AUDIT_NR_FILTERS != 6
+       LIST_HEAD_INIT(audit_filter_list[6]),
+#if AUDIT_NR_FILTERS != 7
 #error Fix audit_filter_list initialiser
 #endif
 };
@@ -67,6 +68,7 @@ static struct list_head audit_rules_list[AUDIT_NR_FILTERS] = {
        LIST_HEAD_INIT(audit_rules_list[3]),
        LIST_HEAD_INIT(audit_rules_list[4]),
        LIST_HEAD_INIT(audit_rules_list[5]),
+       LIST_HEAD_INIT(audit_rules_list[6]),
 };
 
 DEFINE_MUTEX(audit_filter_mutex);
@@ -263,6 +265,7 @@ static inline struct audit_entry *audit_to_entry_common(struct audit_rule_data *
 #endif
        case AUDIT_FILTER_USER:
        case AUDIT_FILTER_TYPE:
+       case AUDIT_FILTER_FS:
                ;
        }
        if (unlikely(rule->action == AUDIT_POSSIBLE)) {
@@ -338,6 +341,21 @@ static int audit_field_valid(struct audit_entry *entry, struct audit_field *f)
                    entry->rule.listnr != AUDIT_FILTER_USER)
                        return -EINVAL;
                break;
+       case AUDIT_FSTYPE:
+               if (entry->rule.listnr != AUDIT_FILTER_FS)
+                       return -EINVAL;
+               break;
+       }
+
+       switch(entry->rule.listnr) {
+       case AUDIT_FILTER_FS:
+               switch(f->type) {
+               case AUDIT_FSTYPE:
+               case AUDIT_FILTERKEY:
+                       break;
+               default:
+                       return -EINVAL;
+               }
        }
 
        switch(f->type) {
@@ -391,6 +409,7 @@ static int audit_field_valid(struct audit_entry *entry, struct audit_field *f)
                        return -EINVAL;
        /* FALL THROUGH */
        case AUDIT_ARCH:
+       case AUDIT_FSTYPE:
                if (f->op != Audit_not_equal && f->op != Audit_equal)
                        return -EINVAL;
                break;
@@ -910,10 +929,13 @@ static inline int audit_add_rule(struct audit_entry *entry)
 #ifdef CONFIG_AUDITSYSCALL
        int dont_count = 0;
 
-       /* If either of these, don't count towards total */
-       if (entry->rule.listnr == AUDIT_FILTER_USER ||
-               entry->rule.listnr == AUDIT_FILTER_TYPE)
+       /* If any of these, don't count towards total */
+       switch(entry->rule.listnr) {
+       case AUDIT_FILTER_USER:
+       case AUDIT_FILTER_TYPE:
+       case AUDIT_FILTER_FS:
                dont_count = 1;
+       }
 #endif
 
        mutex_lock(&audit_filter_mutex);
@@ -989,10 +1011,13 @@ int audit_del_rule(struct audit_entry *entry)
 #ifdef CONFIG_AUDITSYSCALL
        int dont_count = 0;
 
-       /* If either of these, don't count towards total */
-       if (entry->rule.listnr == AUDIT_FILTER_USER ||
-               entry->rule.listnr == AUDIT_FILTER_TYPE)
+       /* If any of these, don't count towards total */
+       switch(entry->rule.listnr) {
+       case AUDIT_FILTER_USER:
+       case AUDIT_FILTER_TYPE:
+       case AUDIT_FILTER_FS:
                dont_count = 1;
+       }
 #endif
 
        mutex_lock(&audit_filter_mutex);
index aac1a41f82bd78d3fed70a048abf348d4b07b941..c9bb29e173351327919c1735e80c78d4295013c4 100644 (file)
@@ -1869,10 +1869,33 @@ void __audit_inode_child(struct inode *parent,
        struct inode *inode = d_backing_inode(dentry);
        const char *dname = dentry->d_name.name;
        struct audit_names *n, *found_parent = NULL, *found_child = NULL;
+       struct audit_entry *e;
+       struct list_head *list = &audit_filter_list[AUDIT_FILTER_FS];
+       int i;
 
        if (!context->in_syscall)
                return;
 
+       rcu_read_lock();
+       if (!list_empty(list)) {
+               list_for_each_entry_rcu(e, list, list) {
+                       for (i = 0; i < e->rule.field_count; i++) {
+                               struct audit_field *f = &e->rule.fields[i];
+
+                               if (f->type == AUDIT_FSTYPE) {
+                                       if (audit_comparator(parent->i_sb->s_magic,
+                                           f->op, f->val)) {
+                                               if (e->rule.action == AUDIT_NEVER) {
+                                                       rcu_read_unlock();
+                                                       return;
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+       rcu_read_unlock();
+
        if (inode)
                handle_one(inode);