]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blobdiff - kernel/audit.c
sched/fair: Fix fault in reweight_entity
[mirror_ubuntu-jammy-kernel.git] / kernel / audit.c
index 121d37e700a62b53854c06199d9a89850ec39dd4..ec2ac151041c431889a3354bbcd65ffbc67b5b35 100644 (file)
@@ -125,7 +125,7 @@ static u32  audit_backlog_wait_time = AUDIT_BACKLOG_WAIT_TIME;
 /* The identity of the user shutting down the audit system. */
 static kuid_t          audit_sig_uid = INVALID_UID;
 static pid_t           audit_sig_pid = -1;
-static u32             audit_sig_sid;
+struct lsmblob audit_sig_lsm;
 
 /* Records can be lost in several ways:
    0) [suppressed in audit_alloc]
@@ -394,10 +394,11 @@ static int audit_log_config_change(char *function_name, u32 new, u32 old,
                return rc;
        audit_log_format(ab, "op=set %s=%u old=%u ", function_name, new, old);
        audit_log_session_info(ab);
-       rc = audit_log_task_context(ab);
+       rc = audit_log_task_context(ab, NULL);
        if (rc)
                allow_changes = 0; /* Something weird, deny request */
        audit_log_format(ab, " res=%d", allow_changes);
+       audit_log_lsm(NULL, false);
        audit_log_end(ab);
        return rc;
 }
@@ -541,20 +542,22 @@ static void kauditd_printk_skb(struct sk_buff *skb)
 /**
  * kauditd_rehold_skb - Handle a audit record send failure in the hold queue
  * @skb: audit record
+ * @error: error code (unused)
  *
  * Description:
  * This should only be used by the kauditd_thread when it fails to flush the
  * hold queue.
  */
-static void kauditd_rehold_skb(struct sk_buff *skb)
+static void kauditd_rehold_skb(struct sk_buff *skb, __always_unused int error)
 {
-       /* put the record back in the queue at the same place */
-       skb_queue_head(&audit_hold_queue, skb);
+       /* put the record back in the queue */
+       skb_queue_tail(&audit_hold_queue, skb);
 }
 
 /**
  * kauditd_hold_skb - Queue an audit record, waiting for auditd
  * @skb: audit record
+ * @error: error code
  *
  * Description:
  * Queue the audit record, waiting for an instance of auditd.  When this
@@ -564,19 +567,31 @@ static void kauditd_rehold_skb(struct sk_buff *skb)
  * and queue it, if we have room.  If we want to hold on to the record, but we
  * don't have room, record a record lost message.
  */
-static void kauditd_hold_skb(struct sk_buff *skb)
+static void kauditd_hold_skb(struct sk_buff *skb, int error)
 {
        /* at this point it is uncertain if we will ever send this to auditd so
         * try to send the message via printk before we go any further */
        kauditd_printk_skb(skb);
 
        /* can we just silently drop the message? */
-       if (!audit_default) {
-               kfree_skb(skb);
-               return;
+       if (!audit_default)
+               goto drop;
+
+       /* the hold queue is only for when the daemon goes away completely,
+        * not -EAGAIN failures; if we are in a -EAGAIN state requeue the
+        * record on the retry queue unless it's full, in which case drop it
+        */
+       if (error == -EAGAIN) {
+               if (!audit_backlog_limit ||
+                   skb_queue_len(&audit_retry_queue) < audit_backlog_limit) {
+                       skb_queue_tail(&audit_retry_queue, skb);
+                       return;
+               }
+               audit_log_lost("kauditd retry queue overflow");
+               goto drop;
        }
 
-       /* if we have room, queue the message */
+       /* if we have room in the hold queue, queue the message */
        if (!audit_backlog_limit ||
            skb_queue_len(&audit_hold_queue) < audit_backlog_limit) {
                skb_queue_tail(&audit_hold_queue, skb);
@@ -585,24 +600,32 @@ static void kauditd_hold_skb(struct sk_buff *skb)
 
        /* we have no other options - drop the message */
        audit_log_lost("kauditd hold queue overflow");
+drop:
        kfree_skb(skb);
 }
 
 /**
  * kauditd_retry_skb - Queue an audit record, attempt to send again to auditd
  * @skb: audit record
+ * @error: error code (unused)
  *
  * Description:
  * Not as serious as kauditd_hold_skb() as we still have a connected auditd,
  * but for some reason we are having problems sending it audit records so
  * queue the given record and attempt to resend.
  */
-static void kauditd_retry_skb(struct sk_buff *skb)
+static void kauditd_retry_skb(struct sk_buff *skb, __always_unused int error)
 {
-       /* NOTE: because records should only live in the retry queue for a
-        * short period of time, before either being sent or moved to the hold
-        * queue, we don't currently enforce a limit on this queue */
-       skb_queue_tail(&audit_retry_queue, skb);
+       if (!audit_backlog_limit ||
+           skb_queue_len(&audit_retry_queue) < audit_backlog_limit) {
+               skb_queue_tail(&audit_retry_queue, skb);
+               return;
+       }
+
+       /* we have to drop the record, send it via printk as a last effort */
+       kauditd_printk_skb(skb);
+       audit_log_lost("kauditd retry queue overflow");
+       kfree_skb(skb);
 }
 
 /**
@@ -640,7 +663,7 @@ static void auditd_reset(const struct auditd_connection *ac)
        /* flush the retry queue to the hold queue, but don't touch the main
         * queue since we need to process that normally for multicast */
        while ((skb = skb_dequeue(&audit_retry_queue)))
-               kauditd_hold_skb(skb);
+               kauditd_hold_skb(skb, -ECONNREFUSED);
 }
 
 /**
@@ -714,16 +737,18 @@ static int kauditd_send_queue(struct sock *sk, u32 portid,
                              struct sk_buff_head *queue,
                              unsigned int retry_limit,
                              void (*skb_hook)(struct sk_buff *skb),
-                             void (*err_hook)(struct sk_buff *skb))
+                             void (*err_hook)(struct sk_buff *skb, int error))
 {
        int rc = 0;
-       struct sk_buff *skb;
-       static unsigned int failed = 0;
+       struct sk_buff *skb = NULL;
+       struct sk_buff *skb_tail;
+       unsigned int failed = 0;
 
        /* NOTE: kauditd_thread takes care of all our locking, we just use
         *       the netlink info passed to us (e.g. sk and portid) */
 
-       while ((skb = skb_dequeue(queue))) {
+       skb_tail = skb_peek_tail(queue);
+       while ((skb != skb_tail) && (skb = skb_dequeue(queue))) {
                /* call the skb_hook for each skb we touch */
                if (skb_hook)
                        (*skb_hook)(skb);
@@ -731,36 +756,34 @@ static int kauditd_send_queue(struct sock *sk, u32 portid,
                /* can we send to anyone via unicast? */
                if (!sk) {
                        if (err_hook)
-                               (*err_hook)(skb);
+                               (*err_hook)(skb, -ECONNREFUSED);
                        continue;
                }
 
+retry:
                /* grab an extra skb reference in case of error */
                skb_get(skb);
                rc = netlink_unicast(sk, skb, portid, 0);
                if (rc < 0) {
-                       /* fatal failure for our queue flush attempt? */
+                       /* send failed - try a few times unless fatal error */
                        if (++failed >= retry_limit ||
                            rc == -ECONNREFUSED || rc == -EPERM) {
-                               /* yes - error processing for the queue */
                                sk = NULL;
                                if (err_hook)
-                                       (*err_hook)(skb);
-                               if (!skb_hook)
-                                       goto out;
-                               /* keep processing with the skb_hook */
+                                       (*err_hook)(skb, rc);
+                               if (rc == -EAGAIN)
+                                       rc = 0;
+                               /* continue to drain the queue */
                                continue;
                        } else
-                               /* no - requeue to preserve ordering */
-                               skb_queue_head(queue, skb);
+                               goto retry;
                } else {
-                       /* it worked - drop the extra reference and continue */
+                       /* skb sent - drop the extra reference and continue */
                        consume_skb(skb);
                        failed = 0;
                }
        }
 
-out:
        return (rc >= 0 ? 0 : rc);
 }
 
@@ -1069,13 +1092,31 @@ static void audit_log_common_recv_msg(struct audit_context *context,
                return;
        audit_log_format(*ab, "pid=%d uid=%u ", pid, uid);
        audit_log_session_info(*ab);
-       audit_log_task_context(*ab);
+       audit_log_task_context(*ab, NULL);
 }
 
 static inline void audit_log_user_recv_msg(struct audit_buffer **ab,
                                           u16 msg_type)
 {
-       audit_log_common_recv_msg(NULL, ab, msg_type);
+       struct audit_context *context;
+
+       if (!lsm_multiple_contexts()) {
+               audit_log_common_recv_msg(NULL, ab, msg_type);
+               return;
+       }
+
+       context = audit_context();
+       if (context) {
+               if (!context->in_syscall)
+                       audit_stamp_context(context);
+               audit_log_common_recv_msg(context, ab, msg_type);
+               return;
+       }
+
+       audit_alloc(current);
+       context = audit_context();
+
+       audit_log_common_recv_msg(context, ab, msg_type);
 }
 
 int is_audit_feature_set(int i)
@@ -1190,8 +1231,6 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
        struct audit_buffer     *ab;
        u16                     msg_type = nlh->nlmsg_type;
        struct audit_sig_info   *sig_data;
-       char                    *ctx = NULL;
-       u32                     len;
 
        err = audit_netlink_ok(skb, msg_type);
        if (err)
@@ -1373,6 +1412,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
                                audit_log_n_untrustedstring(ab, str, data_len);
                        }
                        audit_log_end(ab);
+                       audit_log_lsm(NULL, false);
                }
                break;
        case AUDIT_ADD_RULE:
@@ -1439,29 +1479,34 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
                kfree(new);
                break;
        }
-       case AUDIT_SIGNAL_INFO:
-               len = 0;
-               if (audit_sig_sid) {
-                       err = security_secid_to_secctx(audit_sig_sid, &ctx, &len);
+       case AUDIT_SIGNAL_INFO: {
+               struct lsmcontext context = { };
+               int len = 0;
+
+               if (lsmblob_is_set(&audit_sig_lsm)) {
+                       err = security_secid_to_secctx(&audit_sig_lsm,
+                                                      &context, LSMBLOB_FIRST);
                        if (err)
                                return err;
                }
-               sig_data = kmalloc(sizeof(*sig_data) + len, GFP_KERNEL);
+               sig_data = kmalloc(sizeof(*sig_data) + context.len, GFP_KERNEL);
                if (!sig_data) {
-                       if (audit_sig_sid)
-                               security_release_secctx(ctx, len);
+                       if (lsmblob_is_set(&audit_sig_lsm))
+                               security_release_secctx(&context);
                        return -ENOMEM;
                }
                sig_data->uid = from_kuid(&init_user_ns, audit_sig_uid);
                sig_data->pid = audit_sig_pid;
-               if (audit_sig_sid) {
-                       memcpy(sig_data->ctx, ctx, len);
-                       security_release_secctx(ctx, len);
+               if (lsmblob_is_set(&audit_sig_lsm)) {
+                       len = context.len;
+                       memcpy(sig_data->ctx, context.context, len);
+                       security_release_secctx(&context);
                }
                audit_send_reply(skb, seq, AUDIT_SIGNAL_INFO, 0, 0,
                                 sig_data, sizeof(*sig_data) + len);
                kfree(sig_data);
                break;
+       }
        case AUDIT_TTY_GET: {
                struct audit_tty_status s;
                unsigned int t;
@@ -1542,6 +1587,20 @@ static void audit_receive(struct sk_buff  *skb)
                nlh = nlmsg_next(nlh, &len);
        }
        audit_ctl_unlock();
+
+       /* can't block with the ctrl lock, so penalize the sender now */
+       if (audit_backlog_limit &&
+           (skb_queue_len(&audit_queue) > audit_backlog_limit)) {
+               DECLARE_WAITQUEUE(wait, current);
+
+               /* wake kauditd to try and flush the queue */
+               wake_up_interruptible(&kauditd_wait);
+
+               add_wait_queue_exclusive(&audit_backlog_wait, &wait);
+               set_current_state(TASK_UNINTERRUPTIBLE);
+               schedule_timeout(audit_backlog_wait_time);
+               remove_wait_queue(&audit_backlog_wait, &wait);
+       }
 }
 
 /* Log information about who is connecting to the audit multicast socket */
@@ -1568,7 +1627,7 @@ static void audit_log_multicast(int group, const char *op, int err)
                         tty ? tty_name(tty) : "(none)",
                         audit_get_sessionid(current));
        audit_put_tty(tty);
-       audit_log_task_context(ab); /* subj= */
+       audit_log_task_context(ab, NULL); /* subj= */
        audit_log_format(ab, " comm=");
        audit_log_untrustedstring(ab, get_task_comm(comm, current));
        audit_log_d_path_exe(ab, current->mm); /* exe= */
@@ -1609,7 +1668,8 @@ static int __net_init audit_net_init(struct net *net)
                audit_panic("cannot initialize netlink socket in namespace");
                return -ENOMEM;
        }
-       aunet->sk->sk_sndtimeo = MAX_SCHEDULE_TIMEOUT;
+       /* limit the timeout in case auditd is blocked/stopped */
+       aunet->sk->sk_sndtimeo = HZ / 10;
 
        return 0;
 }
@@ -1825,7 +1885,9 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask,
         *    task_tgid_vnr() since auditd_pid is set in audit_receive_msg()
         *    using a PID anchored in the caller's namespace
         * 2. generator holding the audit_cmd_mutex - we don't want to block
-        *    while holding the mutex */
+        *    while holding the mutex, although we do penalize the sender
+        *    later in audit_receive() when it is safe to block
+        */
        if (!(auditd_test_task(current) || audit_ctl_owner_current())) {
                long stime = audit_backlog_wait_time;
 
@@ -1868,6 +1930,11 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask,
        /* cancel dummy context to enable supporting records */
        if (ctx)
                ctx->dummy = 0;
+       if (type == AUDIT_MAC_TASK_CONTEXTS && ab->ctx &&
+           ab->ctx->serial == 0) {
+               audit_stamp_context(ab->ctx);
+               audit_get_stamp(ab->ctx, &t, &serial);
+       }
        audit_log_format(ab, "audit(%llu.%03lu:%u): ",
                         (unsigned long long)t.tv_sec, t.tv_nsec/1000000, serial);
 
@@ -2125,34 +2192,103 @@ void audit_log_key(struct audit_buffer *ab, char *key)
                audit_log_format(ab, "(null)");
 }
 
-int audit_log_task_context(struct audit_buffer *ab)
+int audit_log_task_context(struct audit_buffer *ab, struct lsmblob *blob)
 {
-       char *ctx = NULL;
-       unsigned len;
+       int i;
        int error;
-       u32 sid;
+       struct lsmblob localblob;
+       struct lsmcontext lsmdata;
 
-       security_task_getsecid_subj(current, &sid);
-       if (!sid)
+       /*
+        * If there is more than one security module that has a
+        * subject "context" it's necessary to put the subject data
+        * into a separate record to maintain compatibility.
+        */
+       if (lsm_multiple_contexts()) {
+               audit_log_format(ab, " subj=?");
                return 0;
+       }
 
-       error = security_secid_to_secctx(sid, &ctx, &len);
-       if (error) {
-               if (error != -EINVAL)
-                       goto error_path;
-               return 0;
+       if (blob == NULL) {
+               security_task_getsecid_subj(current, &localblob);
+               if (!lsmblob_is_set(&localblob)) {
+                       audit_log_format(ab, " subj=?");
+                       return 0;
+               }
+               blob = &localblob;
        }
 
-       audit_log_format(ab, " subj=%s", ctx);
-       security_release_secctx(ctx, len);
-       return 0;
+       for (i = 0; i < LSMBLOB_ENTRIES; i++) {
+               if (blob->secid[i] == 0)
+                       continue;
+               error = security_secid_to_secctx(blob, &lsmdata, i);
+               if (error && error != -EINVAL) {
+                       audit_panic("error in audit_log_task_context");
+                       return error;
+               }
+
+               audit_log_format(ab, " subj=%s", lsmdata.context);
+               security_release_secctx(&lsmdata);
+               break;
+       }
 
-error_path:
-       audit_panic("error in audit_log_task_context");
-       return error;
+       return 0;
 }
 EXPORT_SYMBOL(audit_log_task_context);
 
+int audit_log_object_context(struct audit_buffer *ab,
+                                   struct lsmblob *blob)
+{
+       int i;
+       int error;
+       bool sep = false;
+       struct lsmcontext lsmdata;
+       struct audit_buffer *lsmab = NULL;
+       struct audit_context *context = NULL;
+
+       /*
+        * If there is more than one security module that has a
+        * object "context" it's necessary to put the object data
+        * into a separate record to maintain compatibility.
+        */
+       if (lsm_multiple_contexts()) {
+               audit_log_format(ab, " obj=?");
+               context = ab->ctx;
+               if (context)
+                       lsmab = audit_log_start(context, GFP_KERNEL,
+                                               AUDIT_MAC_OBJ_CONTEXTS);
+       }
+
+       for (i = 0; i < LSMBLOB_ENTRIES; i++) {
+               if (blob->secid[i] == 0)
+                       continue;
+               error = security_secid_to_secctx(blob, &lsmdata, i);
+               if (error && error != -EINVAL) {
+                       audit_panic("error in audit_log_object_context");
+                       return error;
+               }
+
+               if (context) {
+                       audit_log_format(lsmab, "%sobj_%s=%s",
+                                        sep ? " " : "",
+                                        security_lsm_slot_name(i),
+                                        lsmdata.context);
+                       sep = true;
+               } else
+                       audit_log_format(ab, " obj=%s", lsmdata.context);
+
+               security_release_secctx(&lsmdata);
+               if (!context)
+                       break;
+       }
+
+       if (context)
+               audit_log_end(lsmab);
+
+       return 0;
+}
+EXPORT_SYMBOL(audit_log_object_context);
+
 void audit_log_d_path_exe(struct audit_buffer *ab,
                          struct mm_struct *mm)
 {
@@ -2221,7 +2357,7 @@ void audit_log_task_info(struct audit_buffer *ab)
        audit_log_format(ab, " comm=");
        audit_log_untrustedstring(ab, get_task_comm(comm, current));
        audit_log_d_path_exe(ab, current->mm);
-       audit_log_task_context(ab);
+       audit_log_task_context(ab, NULL);
 }
 EXPORT_SYMBOL(audit_log_task_info);
 
@@ -2279,6 +2415,7 @@ static void audit_log_set_loginuid(kuid_t koldloginuid, kuid_t kloginuid,
        if (!audit_enabled)
                return;
 
+       audit_stamp_context(audit_context());
        ab = audit_log_start(audit_context(), GFP_KERNEL, AUDIT_LOGIN);
        if (!ab)
                return;
@@ -2289,11 +2426,12 @@ static void audit_log_set_loginuid(kuid_t koldloginuid, kuid_t kloginuid,
        tty = audit_get_tty();
 
        audit_log_format(ab, "pid=%d uid=%u", task_tgid_nr(current), uid);
-       audit_log_task_context(ab);
+       audit_log_task_context(ab, NULL);
        audit_log_format(ab, " old-auid=%u auid=%u tty=%s old-ses=%u ses=%u res=%d",
                         oldloginuid, loginuid, tty ? tty_name(tty) : "(none)",
                         oldsessionid, sessionid, !rc);
        audit_put_tty(tty);
+       audit_log_lsm(NULL, true);
        audit_log_end(ab);
 }
 
@@ -2353,7 +2491,7 @@ int audit_signal_info(int sig, struct task_struct *t)
                        audit_sig_uid = auid;
                else
                        audit_sig_uid = uid;
-               security_task_getsecid_subj(current, &audit_sig_sid);
+               security_task_getsecid_subj(current, &audit_sig_lsm);
        }
 
        return audit_signal_info_syscall(t);