Linux 6.9-rc1
[linux-2.6-microblaze.git] / kernel / auditsc.c
index 3a8c9d7..6f0d6fb 100644 (file)
@@ -64,6 +64,7 @@
 #include <uapi/linux/limits.h>
 #include <uapi/linux/netfilter/nf_tables.h>
 #include <uapi/linux/openat2.h> // struct open_how
+#include <uapi/linux/fanotify.h>
 
 #include "audit.h"
 
@@ -142,6 +143,8 @@ static const struct audit_nfcfgop_tab audit_nfcfgs[] = {
        { AUDIT_NFT_OP_OBJ_RESET,               "nft_reset_obj"            },
        { AUDIT_NFT_OP_FLOWTABLE_REGISTER,      "nft_register_flowtable"   },
        { AUDIT_NFT_OP_FLOWTABLE_UNREGISTER,    "nft_unregister_flowtable" },
+       { AUDIT_NFT_OP_SETELEM_RESET,           "nft_reset_setelem"        },
+       { AUDIT_NFT_OP_RULE_RESET,              "nft_reset_rule"           },
        { AUDIT_NFT_OP_INVALID,                 "nft_invalid"              },
 };
 
@@ -806,30 +809,53 @@ static int audit_in_mask(const struct audit_krule *rule, unsigned long val)
 }
 
 /**
- * audit_filter_uring - apply filters to an io_uring operation
+ * __audit_filter_op - common filter helper for operations (syscall/uring/etc)
  * @tsk: associated task
  * @ctx: audit context
+ * @list: audit filter list
+ * @name: audit_name (can be NULL)
+ * @op: current syscall/uring_op
+ *
+ * Run the udit filters specified in @list against @tsk using @ctx,
+ * @name, and @op, as necessary; the caller is responsible for ensuring
+ * that the call is made while the RCU read lock is held. The @name
+ * parameter can be NULL, but all others must be specified.
+ * Returns 1/true if the filter finds a match, 0/false if none are found.
  */
-static void audit_filter_uring(struct task_struct *tsk,
-                              struct audit_context *ctx)
+static int __audit_filter_op(struct task_struct *tsk,
+                          struct audit_context *ctx,
+                          struct list_head *list,
+                          struct audit_names *name,
+                          unsigned long op)
 {
        struct audit_entry *e;
        enum audit_state state;
 
+       list_for_each_entry_rcu(e, list, list) {
+               if (audit_in_mask(&e->rule, op) &&
+                   audit_filter_rules(tsk, &e->rule, ctx, name,
+                                      &state, false)) {
+                       ctx->current_state = state;
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+/**
+ * audit_filter_uring - apply filters to an io_uring operation
+ * @tsk: associated task
+ * @ctx: audit context
+ */
+static void audit_filter_uring(struct task_struct *tsk,
+                              struct audit_context *ctx)
+{
        if (auditd_test_task(tsk))
                return;
 
        rcu_read_lock();
-       list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_URING_EXIT],
-                               list) {
-               if (audit_in_mask(&e->rule, ctx->uring_op) &&
-                   audit_filter_rules(tsk, &e->rule, ctx, NULL, &state,
-                                      false)) {
-                       rcu_read_unlock();
-                       ctx->current_state = state;
-                       return;
-               }
-       }
+       __audit_filter_op(tsk, ctx, &audit_filter_list[AUDIT_FILTER_URING_EXIT],
+                       NULL, ctx->uring_op);
        rcu_read_unlock();
 }
 
@@ -841,24 +867,13 @@ static void audit_filter_uring(struct task_struct *tsk,
 static void audit_filter_syscall(struct task_struct *tsk,
                                 struct audit_context *ctx)
 {
-       struct audit_entry *e;
-       enum audit_state state;
-
        if (auditd_test_task(tsk))
                return;
 
        rcu_read_lock();
-       list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_EXIT], list) {
-               if (audit_in_mask(&e->rule, ctx->major) &&
-                   audit_filter_rules(tsk, &e->rule, ctx, NULL,
-                                      &state, false)) {
-                       rcu_read_unlock();
-                       ctx->current_state = state;
-                       return;
-               }
-       }
+       __audit_filter_op(tsk, ctx, &audit_filter_list[AUDIT_FILTER_EXIT],
+                       NULL, ctx->major);
        rcu_read_unlock();
-       return;
 }
 
 /*
@@ -867,20 +882,12 @@ static void audit_filter_syscall(struct task_struct *tsk,
  */
 static int audit_filter_inode_name(struct task_struct *tsk,
                                   struct audit_names *n,
-                                  struct audit_context *ctx) {
+                                  struct audit_context *ctx)
+{
        int h = audit_hash_ino((u32)n->ino);
        struct list_head *list = &audit_inode_hash[h];
-       struct audit_entry *e;
-       enum audit_state state;
 
-       list_for_each_entry_rcu(e, list, list) {
-               if (audit_in_mask(&e->rule, ctx->major) &&
-                   audit_filter_rules(tsk, &e->rule, ctx, n, &state, false)) {
-                       ctx->current_state = state;
-                       return 1;
-               }
-       }
-       return 0;
+       return __audit_filter_op(tsk, ctx, list, n, ctx->major);
 }
 
 /* At syscall exit time, this filter is called if any audit_names have been
@@ -965,7 +972,7 @@ static void audit_reset_context(struct audit_context *ctx)
        if (!ctx)
                return;
 
-       /* if ctx is non-null, reset the "ctx->state" regardless */
+       /* if ctx is non-null, reset the "ctx->context" regardless */
        ctx->context = AUDIT_CTX_UNUSED;
        if (ctx->dummy)
                return;
@@ -1002,7 +1009,7 @@ static void audit_reset_context(struct audit_context *ctx)
        kfree(ctx->sockaddr);
        ctx->sockaddr = NULL;
        ctx->sockaddr_len = 0;
-       ctx->pid = ctx->ppid = 0;
+       ctx->ppid = 0;
        ctx->uid = ctx->euid = ctx->suid = ctx->fsuid = KUIDT_INIT(0);
        ctx->gid = ctx->egid = ctx->sgid = ctx->fsgid = KGIDT_INIT(0);
        ctx->personality = 0;
@@ -1016,7 +1023,6 @@ static void audit_reset_context(struct audit_context *ctx)
        WARN_ON(!list_empty(&ctx->killed_trees));
        audit_free_module(ctx);
        ctx->fds[0] = -1;
-       audit_proctitle_free(ctx);
        ctx->type = 0; /* reset last for audit_free_*() */
 }
 
@@ -1061,7 +1067,8 @@ int audit_alloc(struct task_struct *tsk)
                return 0;
        }
 
-       if (!(context = audit_alloc_context(state))) {
+       context = audit_alloc_context(state);
+       if (!context) {
                kfree(key);
                audit_log_lost("out of memory in audit_alloc");
                return -ENOMEM;
@@ -1073,35 +1080,11 @@ int audit_alloc(struct task_struct *tsk)
        return 0;
 }
 
-/**
- * audit_alloc_kernel - allocate an audit_context for a kernel task
- * @tsk: the kernel task
- *
- * Similar to the audit_alloc() function, but intended for kernel private
- * threads.  Returns zero on success, negative values on failure.
- */
-int audit_alloc_kernel(struct task_struct *tsk)
-{
-       /*
-        * At the moment we are just going to call into audit_alloc() to
-        * simplify the code, but there two things to keep in mind with this
-        * approach:
-        *
-        * 1. Filtering internal kernel tasks is a bit laughable in almost all
-        * cases, but there is at least one case where there is a benefit:
-        * the '-a task,never' case allows the admin to effectively disable
-        * task auditing at runtime.
-        *
-        * 2. The {set,clear}_task_syscall_work() ops likely have zero effect
-        * on these internal kernel tasks, but they probably don't hurt either.
-        */
-       return audit_alloc(tsk);
-}
-
 static inline void audit_free_context(struct audit_context *context)
 {
        /* resetting is extra work, but it is likely just noise */
        audit_reset_context(context);
+       audit_proctitle_free(context);
        free_tree_refs(context);
        kfree(context->filterkey);
        kfree(context);
@@ -1316,15 +1299,11 @@ out:
 static void audit_log_cap(struct audit_buffer *ab, char *prefix,
                          kernel_cap_t *cap)
 {
-       int i;
-
        if (cap_isclear(*cap)) {
                audit_log_format(ab, " %s=0", prefix);
                return;
        }
-       audit_log_format(ab, " %s=", prefix);
-       CAP_FOR_EACH_U32(i)
-               audit_log_format(ab, "%08x", cap->cap[CAP_LAST_U32 - i]);
+       audit_log_format(ab, " %s=%016llx", prefix, cap->val);
 }
 
 static void audit_log_fcaps(struct audit_buffer *ab, struct audit_names *name)
@@ -1858,7 +1837,7 @@ void __audit_free(struct task_struct *tsk)
 
        /* We are called either by do_exit() or the fork() error handling code;
         * in the former case tsk == current and in the latter tsk is a
-        * random task_struct that doesn't doesn't have any meaningful data we
+        * random task_struct that doesn't have any meaningful data we
         * need to log via audit_log_exit().
         */
        if (tsk == current && !context->dummy) {
@@ -1965,6 +1944,7 @@ void __audit_uring_exit(int success, long code)
                goto out;
        }
 
+       audit_return_fixup(ctx, success, code);
        if (ctx->context == AUDIT_CTX_SYSCALL) {
                /*
                 * NOTE: See the note in __audit_uring_entry() about the case
@@ -2006,7 +1986,6 @@ void __audit_uring_exit(int success, long code)
        audit_filter_inodes(current, ctx);
        if (ctx->current_state != AUDIT_STATE_RECORD)
                goto out;
-       audit_return_fixup(ctx, success, code);
        audit_log_exit();
 
 out:
@@ -2090,13 +2069,13 @@ void __audit_syscall_exit(int success, long return_code)
        if (!list_empty(&context->killed_trees))
                audit_kill_trees(context);
 
+       audit_return_fixup(context, success, return_code);
        /* run through both filters to ensure we set the filterkey properly */
        audit_filter_syscall(current, context);
        audit_filter_inodes(current, context);
-       if (context->current_state < AUDIT_STATE_RECORD)
+       if (context->current_state != AUDIT_STATE_RECORD)
                goto out;
 
-       audit_return_fixup(context, success, return_code);
        audit_log_exit();
 
 out:
@@ -2149,7 +2128,7 @@ retry:
        d = dentry;
        rcu_read_lock();
        seq = read_seqbegin(&rename_lock);
-       for(;;) {
+       for (;;) {
                struct inode *inode = d_backing_inode(d);
 
                if (inode && unlikely(inode->i_fsnotify_marks)) {
@@ -2233,7 +2212,7 @@ __audit_reusename(const __user char *uptr)
                if (!n->name)
                        continue;
                if (n->name->uptr == uptr) {
-                       n->name->refcnt++;
+                       atomic_inc(&n->name->refcnt);
                        return n->name;
                }
        }
@@ -2262,7 +2241,7 @@ void __audit_getname(struct filename *name)
        n->name = name;
        n->name_len = AUDIT_NAME_FULL;
        name->aname = n;
-       name->refcnt++;
+       atomic_inc(&name->refcnt);
 }
 
 static inline int audit_copy_fcaps(struct audit_names *name,
@@ -2274,7 +2253,7 @@ static inline int audit_copy_fcaps(struct audit_names *name,
        if (!dentry)
                return 0;
 
-       rc = get_vfs_caps_from_disk(&init_user_ns, dentry, &caps);
+       rc = get_vfs_caps_from_disk(&nop_mnt_idmap, dentry, &caps);
        if (rc)
                return rc;
 
@@ -2394,7 +2373,7 @@ out_alloc:
                return;
        if (name) {
                n->name = name;
-               name->refcnt++;
+               atomic_inc(&name->refcnt);
        }
 
 out:
@@ -2481,6 +2460,8 @@ void __audit_inode_child(struct inode *parent,
                }
        }
 
+       cond_resched();
+
        /* is there a matching child entry? */
        list_for_each_entry(n, &context->names_list, list) {
                /* can only match entries that have a name */
@@ -2519,7 +2500,7 @@ void __audit_inode_child(struct inode *parent,
                if (found_parent) {
                        found_child->name = found_parent->name;
                        found_child->name_len = AUDIT_NAME_FULL;
-                       found_child->name->refcnt++;
+                       atomic_inc(&found_child->name->refcnt);
                }
        }
 
@@ -2829,7 +2810,7 @@ int __audit_log_bprm_fcaps(struct linux_binprm *bprm,
        ax->d.next = context->aux;
        context->aux = (void *)ax;
 
-       get_vfs_caps_from_disk(&init_user_ns,
+       get_vfs_caps_from_disk(&nop_mnt_idmap,
                               bprm->file->f_path.dentry, &vcaps);
 
        ax->fcap.permitted = vcaps.permitted;
@@ -2899,10 +2880,21 @@ void __audit_log_kern_module(char *name)
        context->type = AUDIT_KERN_MODULE;
 }
 
-void __audit_fanotify(unsigned int response)
+void __audit_fanotify(u32 response, struct fanotify_response_info_audit_rule *friar)
 {
-       audit_log(audit_context(), GFP_KERNEL,
-               AUDIT_FANOTIFY, "resp=%u", response);
+       /* {subj,obj}_trust values are {0,1,2}: no,yes,unknown */
+       switch (friar->hdr.type) {
+       case FAN_RESPONSE_INFO_NONE:
+               audit_log(audit_context(), GFP_KERNEL, AUDIT_FANOTIFY,
+                         "resp=%u fan_type=%u fan_info=0 subj_trust=2 obj_trust=2",
+                         response, FAN_RESPONSE_INFO_NONE);
+               break;
+       case FAN_RESPONSE_INFO_AUDIT_RULE:
+               audit_log(audit_context(), GFP_KERNEL, AUDIT_FANOTIFY,
+                         "resp=%u fan_type=%u fan_info=%X subj_trust=%u obj_trust=%u",
+                         response, friar->hdr.type, friar->rule_number,
+                         friar->subj_trust, friar->obj_trust);
+       }
 }
 
 void __audit_tk_injoffset(struct timespec64 offset)