root/security/landlock/audit.c
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Landlock - Audit helpers
 *
 * Copyright © 2023-2025 Microsoft Corporation
 */

#include <kunit/test.h>
#include <linux/audit.h>
#include <linux/bitops.h>
#include <linux/lsm_audit.h>
#include <linux/pid.h>
#include <uapi/linux/landlock.h>

#include "access.h"
#include "audit.h"
#include "common.h"
#include "cred.h"
#include "domain.h"
#include "limits.h"
#include "ruleset.h"

static const char *const fs_access_strings[] = {
        [BIT_INDEX(LANDLOCK_ACCESS_FS_EXECUTE)] = "fs.execute",
        [BIT_INDEX(LANDLOCK_ACCESS_FS_WRITE_FILE)] = "fs.write_file",
        [BIT_INDEX(LANDLOCK_ACCESS_FS_READ_FILE)] = "fs.read_file",
        [BIT_INDEX(LANDLOCK_ACCESS_FS_READ_DIR)] = "fs.read_dir",
        [BIT_INDEX(LANDLOCK_ACCESS_FS_REMOVE_DIR)] = "fs.remove_dir",
        [BIT_INDEX(LANDLOCK_ACCESS_FS_REMOVE_FILE)] = "fs.remove_file",
        [BIT_INDEX(LANDLOCK_ACCESS_FS_MAKE_CHAR)] = "fs.make_char",
        [BIT_INDEX(LANDLOCK_ACCESS_FS_MAKE_DIR)] = "fs.make_dir",
        [BIT_INDEX(LANDLOCK_ACCESS_FS_MAKE_REG)] = "fs.make_reg",
        [BIT_INDEX(LANDLOCK_ACCESS_FS_MAKE_SOCK)] = "fs.make_sock",
        [BIT_INDEX(LANDLOCK_ACCESS_FS_MAKE_FIFO)] = "fs.make_fifo",
        [BIT_INDEX(LANDLOCK_ACCESS_FS_MAKE_BLOCK)] = "fs.make_block",
        [BIT_INDEX(LANDLOCK_ACCESS_FS_MAKE_SYM)] = "fs.make_sym",
        [BIT_INDEX(LANDLOCK_ACCESS_FS_REFER)] = "fs.refer",
        [BIT_INDEX(LANDLOCK_ACCESS_FS_TRUNCATE)] = "fs.truncate",
        [BIT_INDEX(LANDLOCK_ACCESS_FS_IOCTL_DEV)] = "fs.ioctl_dev",
        [BIT_INDEX(LANDLOCK_ACCESS_FS_RESOLVE_UNIX)] = "fs.resolve_unix",
};

static_assert(ARRAY_SIZE(fs_access_strings) == LANDLOCK_NUM_ACCESS_FS);

static const char *const net_access_strings[] = {
        [BIT_INDEX(LANDLOCK_ACCESS_NET_BIND_TCP)] = "net.bind_tcp",
        [BIT_INDEX(LANDLOCK_ACCESS_NET_CONNECT_TCP)] = "net.connect_tcp",
        [BIT_INDEX(LANDLOCK_ACCESS_NET_BIND_UDP)] = "net.bind_udp",
        [BIT_INDEX(LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP)] =
                "net.connect_send_udp",
};

static_assert(ARRAY_SIZE(net_access_strings) == LANDLOCK_NUM_ACCESS_NET);

static __attribute_const__ const char *
get_blocker(const enum landlock_request_type type,
            const unsigned long access_bit)
{
        switch (type) {
        case LANDLOCK_REQUEST_PTRACE:
                WARN_ON_ONCE(access_bit != -1);
                return "ptrace";

        case LANDLOCK_REQUEST_FS_CHANGE_TOPOLOGY:
                WARN_ON_ONCE(access_bit != -1);
                return "fs.change_topology";

        case LANDLOCK_REQUEST_FS_ACCESS:
                if (WARN_ON_ONCE(access_bit >= ARRAY_SIZE(fs_access_strings)))
                        return "unknown";
                return fs_access_strings[access_bit];

        case LANDLOCK_REQUEST_NET_ACCESS:
                if (WARN_ON_ONCE(access_bit >= ARRAY_SIZE(net_access_strings)))
                        return "unknown";
                return net_access_strings[access_bit];

        case LANDLOCK_REQUEST_SCOPE_ABSTRACT_UNIX_SOCKET:
                WARN_ON_ONCE(access_bit != -1);
                return "scope.abstract_unix_socket";

        case LANDLOCK_REQUEST_SCOPE_SIGNAL:
                WARN_ON_ONCE(access_bit != -1);
                return "scope.signal";
        }

        WARN_ON_ONCE(1);
        return "unknown";
}

static void log_blockers(struct audit_buffer *const ab,
                         const enum landlock_request_type type,
                         const access_mask_t access)
{
        const unsigned long access_mask = access;
        unsigned long access_bit;
        bool is_first = true;

        for_each_set_bit(access_bit, &access_mask, BITS_PER_TYPE(access)) {
                audit_log_format(ab, "%s%s", is_first ? "" : ",",
                                 get_blocker(type, access_bit));
                is_first = false;
        }
        if (is_first)
                audit_log_format(ab, "%s", get_blocker(type, -1));
}

static void log_domain(struct landlock_hierarchy *const hierarchy)
{
        struct audit_buffer *ab;

        /* Ignores already logged domains.  */
        if (READ_ONCE(hierarchy->log_status) == LANDLOCK_LOG_RECORDED)
                return;

        /* Uses consistent allocation flags wrt common_lsm_audit(). */
        ab = audit_log_start(audit_context(), GFP_ATOMIC | __GFP_NOWARN,
                             AUDIT_LANDLOCK_DOMAIN);
        if (!ab)
                return;

        WARN_ON_ONCE(hierarchy->id == 0);
        audit_log_format(
                ab,
                "domain=%llx status=allocated mode=enforcing pid=%d uid=%u exe=",
                hierarchy->id, pid_nr(hierarchy->details->pid),
                hierarchy->details->uid);
        audit_log_untrustedstring(ab, hierarchy->details->exe_path);
        audit_log_format(ab, " comm=");
        audit_log_untrustedstring(ab, hierarchy->details->comm);
        audit_log_end(ab);

        /*
         * There may be race condition leading to logging of the same domain
         * several times but that is OK.
         */
        WRITE_ONCE(hierarchy->log_status, LANDLOCK_LOG_RECORDED);
}

static struct landlock_hierarchy *
get_hierarchy(const struct landlock_ruleset *const domain, const size_t layer)
{
        struct landlock_hierarchy *hierarchy = domain->hierarchy;
        ssize_t i;

        if (WARN_ON_ONCE(layer >= domain->num_layers))
                return hierarchy;

        for (i = domain->num_layers - 1; i > layer; i--) {
                if (WARN_ON_ONCE(!hierarchy->parent))
                        break;

                hierarchy = hierarchy->parent;
        }

        return hierarchy;
}

#ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST

static void test_get_hierarchy(struct kunit *const test)
{
        struct landlock_hierarchy dom0_hierarchy = {
                .id = 10,
        };
        struct landlock_hierarchy dom1_hierarchy = {
                .parent = &dom0_hierarchy,
                .id = 20,
        };
        struct landlock_hierarchy dom2_hierarchy = {
                .parent = &dom1_hierarchy,
                .id = 30,
        };
        struct landlock_ruleset dom2 = {
                .hierarchy = &dom2_hierarchy,
                .num_layers = 3,
        };

        KUNIT_EXPECT_EQ(test, 10, get_hierarchy(&dom2, 0)->id);
        KUNIT_EXPECT_EQ(test, 20, get_hierarchy(&dom2, 1)->id);
        KUNIT_EXPECT_EQ(test, 30, get_hierarchy(&dom2, 2)->id);
        /* KUNIT_EXPECT_EQ(test, 30, get_hierarchy(&dom2, -1)->id); */
}

#endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */

/* Get the youngest layer that denied the access_request. */
static size_t get_denied_layer(const struct landlock_ruleset *const domain,
                               access_mask_t *const access_request,
                               const struct layer_masks *masks)
{
        for (ssize_t i = ARRAY_SIZE(masks->layers) - 1; i >= 0; i--) {
                if (masks->layers[i].access & *access_request) {
                        *access_request &= masks->layers[i].access;
                        return i;
                }
        }

        /* Not found - fall back to default values */
        *access_request = 0;
        return domain->num_layers - 1;
}

#ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST

static void test_get_denied_layer(struct kunit *const test)
{
        const struct landlock_ruleset dom = {
                .num_layers = 5,
        };
        const struct layer_masks masks = {
                .layers[0].access = LANDLOCK_ACCESS_FS_EXECUTE |
                                    LANDLOCK_ACCESS_FS_READ_DIR,
                .layers[1].access = LANDLOCK_ACCESS_FS_READ_FILE |
                                    LANDLOCK_ACCESS_FS_READ_DIR,
                .layers[2].access = LANDLOCK_ACCESS_FS_REMOVE_DIR,
        };
        access_mask_t access;

        access = LANDLOCK_ACCESS_FS_EXECUTE;
        KUNIT_EXPECT_EQ(test, 0, get_denied_layer(&dom, &access, &masks));
        KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_EXECUTE);

        access = LANDLOCK_ACCESS_FS_READ_FILE;
        KUNIT_EXPECT_EQ(test, 1, get_denied_layer(&dom, &access, &masks));
        KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_READ_FILE);

        access = LANDLOCK_ACCESS_FS_READ_DIR;
        KUNIT_EXPECT_EQ(test, 1, get_denied_layer(&dom, &access, &masks));
        KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_READ_DIR);

        access = LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_READ_DIR;
        KUNIT_EXPECT_EQ(test, 1, get_denied_layer(&dom, &access, &masks));
        KUNIT_EXPECT_EQ(test, access,
                        LANDLOCK_ACCESS_FS_READ_FILE |
                                LANDLOCK_ACCESS_FS_READ_DIR);

        access = LANDLOCK_ACCESS_FS_EXECUTE | LANDLOCK_ACCESS_FS_READ_DIR;
        KUNIT_EXPECT_EQ(test, 1, get_denied_layer(&dom, &access, &masks));
        KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_READ_DIR);

        access = LANDLOCK_ACCESS_FS_WRITE_FILE;
        KUNIT_EXPECT_EQ(test, 4, get_denied_layer(&dom, &access, &masks));
        KUNIT_EXPECT_EQ(test, access, 0);
}

#endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */

static size_t
get_layer_from_deny_masks(access_mask_t *const access_request,
                          const access_mask_t all_existing_optional_access,
                          const deny_masks_t deny_masks,
                          optional_access_t quiet_optional_accesses,
                          bool *quiet)
{
        const unsigned long access_opt = all_existing_optional_access;
        const unsigned long access_req = *access_request;
        access_mask_t missing = 0;
        size_t youngest_layer = 0;
        size_t access_index = 0;
        unsigned long access_bit;
        bool should_quiet = false;

        /* This will require change with new object types. */
        WARN_ON_ONCE(access_opt != _LANDLOCK_ACCESS_FS_OPTIONAL);

        for_each_set_bit(access_bit, &access_opt,
                         BITS_PER_TYPE(access_mask_t)) {
                if (access_req & BIT(access_bit)) {
                        const size_t layer =
                                (deny_masks >>
                                 (access_index *
                                  HWEIGHT(LANDLOCK_MAX_NUM_LAYERS - 1))) &
                                (LANDLOCK_MAX_NUM_LAYERS - 1);
                        const bool layer_has_quiet =
                                !!(quiet_optional_accesses & BIT(access_index));

                        if (layer > youngest_layer) {
                                youngest_layer = layer;
                                missing = BIT(access_bit);
                                should_quiet = layer_has_quiet;
                        } else if (layer == youngest_layer) {
                                missing |= BIT(access_bit);
                                /*
                                 * Whether the layer has rules with quiet flag
                                 * covering the file accessed does not depend on
                                 * the access, and so the following
                                 * WARN_ON_ONCE() should not fail.
                                 */
                                WARN_ON_ONCE(should_quiet && !layer_has_quiet);
                                should_quiet = layer_has_quiet;
                        }
                }
                access_index++;
        }

        *access_request = missing;
        *quiet = should_quiet;
        return youngest_layer;
}

#ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST

static void test_get_layer_from_deny_masks(struct kunit *const test)
{
        deny_masks_t deny_mask;
        access_mask_t access;
        optional_access_t quiet_optional_accesses;
        bool quiet;

        /* truncate:0 ioctl_dev:2 */
        deny_mask = 0x20;
        quiet_optional_accesses = 0;

        access = LANDLOCK_ACCESS_FS_TRUNCATE;
        KUNIT_EXPECT_EQ(test, 0,
                        get_layer_from_deny_masks(
                                &access, _LANDLOCK_ACCESS_FS_OPTIONAL,
                                deny_mask, quiet_optional_accesses, &quiet));
        KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE);
        KUNIT_EXPECT_EQ(test, quiet, false);

        access = LANDLOCK_ACCESS_FS_IOCTL_DEV;
        KUNIT_EXPECT_EQ(test, 2,
                        get_layer_from_deny_masks(
                                &access, _LANDLOCK_ACCESS_FS_OPTIONAL,
                                deny_mask, quiet_optional_accesses, &quiet));
        KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_IOCTL_DEV);
        KUNIT_EXPECT_EQ(test, quiet, false);

        access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV;
        KUNIT_EXPECT_EQ(test, 2,
                        get_layer_from_deny_masks(
                                &access, _LANDLOCK_ACCESS_FS_OPTIONAL,
                                deny_mask, quiet_optional_accesses, &quiet));
        KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_IOCTL_DEV);
        KUNIT_EXPECT_EQ(test, quiet, false);

        /* layer denying truncate: quiet, ioctl: not quiet */
        quiet_optional_accesses = 0b01;

        access = LANDLOCK_ACCESS_FS_TRUNCATE;
        KUNIT_EXPECT_EQ(test, 0,
                        get_layer_from_deny_masks(
                                &access, _LANDLOCK_ACCESS_FS_OPTIONAL,
                                deny_mask, quiet_optional_accesses, &quiet));
        KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE);
        KUNIT_EXPECT_EQ(test, quiet, true);

        access = LANDLOCK_ACCESS_FS_IOCTL_DEV;
        KUNIT_EXPECT_EQ(test, 2,
                        get_layer_from_deny_masks(
                                &access, _LANDLOCK_ACCESS_FS_OPTIONAL,
                                deny_mask, quiet_optional_accesses, &quiet));
        KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_IOCTL_DEV);
        KUNIT_EXPECT_EQ(test, quiet, false);

        access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV;
        KUNIT_EXPECT_EQ(test, 2,
                        get_layer_from_deny_masks(
                                &access, _LANDLOCK_ACCESS_FS_OPTIONAL,
                                deny_mask, quiet_optional_accesses, &quiet));
        KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_IOCTL_DEV);
        KUNIT_EXPECT_EQ(test, quiet, false);

        /* Reverse order - truncate:2 ioctl_dev:0 */
        deny_mask = 0x02;
        quiet_optional_accesses = 0;

        access = LANDLOCK_ACCESS_FS_TRUNCATE;
        KUNIT_EXPECT_EQ(test, 2,
                        get_layer_from_deny_masks(
                                &access, _LANDLOCK_ACCESS_FS_OPTIONAL,
                                deny_mask, quiet_optional_accesses, &quiet));
        KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE);
        KUNIT_EXPECT_EQ(test, quiet, false);

        access = LANDLOCK_ACCESS_FS_IOCTL_DEV;
        KUNIT_EXPECT_EQ(test, 0,
                        get_layer_from_deny_masks(
                                &access, _LANDLOCK_ACCESS_FS_OPTIONAL,
                                deny_mask, quiet_optional_accesses, &quiet));
        KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_IOCTL_DEV);
        KUNIT_EXPECT_EQ(test, quiet, false);

        access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV;
        KUNIT_EXPECT_EQ(test, 2,
                        get_layer_from_deny_masks(
                                &access, _LANDLOCK_ACCESS_FS_OPTIONAL,
                                deny_mask, quiet_optional_accesses, &quiet));
        KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE);
        KUNIT_EXPECT_EQ(test, quiet, false);

        /* layer denying truncate: quiet, ioctl: not quiet */
        quiet_optional_accesses = 0b01;

        access = LANDLOCK_ACCESS_FS_TRUNCATE;
        KUNIT_EXPECT_EQ(test, 2,
                        get_layer_from_deny_masks(
                                &access, _LANDLOCK_ACCESS_FS_OPTIONAL,
                                deny_mask, quiet_optional_accesses, &quiet));
        KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE);
        KUNIT_EXPECT_EQ(test, quiet, true);

        access = LANDLOCK_ACCESS_FS_IOCTL_DEV;
        KUNIT_EXPECT_EQ(test, 0,
                        get_layer_from_deny_masks(
                                &access, _LANDLOCK_ACCESS_FS_OPTIONAL,
                                deny_mask, quiet_optional_accesses, &quiet));
        KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_IOCTL_DEV);
        KUNIT_EXPECT_EQ(test, quiet, false);

        access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV;
        KUNIT_EXPECT_EQ(test, 2,
                        get_layer_from_deny_masks(
                                &access, _LANDLOCK_ACCESS_FS_OPTIONAL,
                                deny_mask, quiet_optional_accesses, &quiet));
        KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE);
        KUNIT_EXPECT_EQ(test, quiet, true);

        /* layer denying truncate: not quiet, ioctl: quiet */
        quiet_optional_accesses = 0b10;

        access = LANDLOCK_ACCESS_FS_TRUNCATE;
        KUNIT_EXPECT_EQ(test, 2,
                        get_layer_from_deny_masks(
                                &access, _LANDLOCK_ACCESS_FS_OPTIONAL,
                                deny_mask, quiet_optional_accesses, &quiet));
        KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE);
        KUNIT_EXPECT_EQ(test, quiet, false);

        access = LANDLOCK_ACCESS_FS_IOCTL_DEV;
        KUNIT_EXPECT_EQ(test, 0,
                        get_layer_from_deny_masks(
                                &access, _LANDLOCK_ACCESS_FS_OPTIONAL,
                                deny_mask, quiet_optional_accesses, &quiet));
        KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_IOCTL_DEV);
        KUNIT_EXPECT_EQ(test, quiet, true);

        access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV;
        KUNIT_EXPECT_EQ(test, 2,
                        get_layer_from_deny_masks(
                                &access, _LANDLOCK_ACCESS_FS_OPTIONAL,
                                deny_mask, quiet_optional_accesses, &quiet));
        KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE);
        KUNIT_EXPECT_EQ(test, quiet, false);

        /* truncate:15 ioctl_dev:15 */
        deny_mask = 0xff;
        quiet_optional_accesses = 0;

        access = LANDLOCK_ACCESS_FS_TRUNCATE;
        KUNIT_EXPECT_EQ(test, 15,
                        get_layer_from_deny_masks(
                                &access, _LANDLOCK_ACCESS_FS_OPTIONAL,
                                deny_mask, quiet_optional_accesses, &quiet));
        KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE);
        KUNIT_EXPECT_EQ(test, quiet, false);

        access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV;
        KUNIT_EXPECT_EQ(test, 15,
                        get_layer_from_deny_masks(
                                &access, _LANDLOCK_ACCESS_FS_OPTIONAL,
                                deny_mask, quiet_optional_accesses, &quiet));
        KUNIT_EXPECT_EQ(test, access,
                        LANDLOCK_ACCESS_FS_TRUNCATE |
                                LANDLOCK_ACCESS_FS_IOCTL_DEV);
        KUNIT_EXPECT_EQ(test, quiet, false);

        /* Both quiet (same layer so quietness must be the same) */
        quiet_optional_accesses = 0b11;

        access = LANDLOCK_ACCESS_FS_TRUNCATE;
        KUNIT_EXPECT_EQ(test, 15,
                        get_layer_from_deny_masks(
                                &access, _LANDLOCK_ACCESS_FS_OPTIONAL,
                                deny_mask, quiet_optional_accesses, &quiet));
        KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE);
        KUNIT_EXPECT_EQ(test, quiet, true);

        access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV;
        KUNIT_EXPECT_EQ(test, 15,
                        get_layer_from_deny_masks(
                                &access, _LANDLOCK_ACCESS_FS_OPTIONAL,
                                deny_mask, quiet_optional_accesses, &quiet));
        KUNIT_EXPECT_EQ(test, access,
                        LANDLOCK_ACCESS_FS_TRUNCATE |
                                LANDLOCK_ACCESS_FS_IOCTL_DEV);
        KUNIT_EXPECT_EQ(test, quiet, true);
}

#endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */

static bool is_valid_request(const struct landlock_request *const request)
{
        if (WARN_ON_ONCE(request->layer_plus_one > LANDLOCK_MAX_NUM_LAYERS))
                return false;

        if (WARN_ON_ONCE(!(!!request->layer_plus_one ^ !!request->access)))
                return false;

        if (request->access) {
                if (WARN_ON_ONCE(!(!!request->layer_masks ^
                                   !!request->all_existing_optional_access)))
                        return false;
        } else {
                if (WARN_ON_ONCE(request->layer_masks ||
                                 request->all_existing_optional_access))
                        return false;
        }

        if (request->deny_masks) {
                if (WARN_ON_ONCE(!request->all_existing_optional_access))
                        return false;
                static_assert(sizeof(request->all_existing_optional_access) ==
                              sizeof(u32));
                if (WARN_ON_ONCE(
                            request->quiet_optional_accesses >=
                            BIT(hweight32(
                                    request->all_existing_optional_access))))
                        return false;
        }

        return true;
}

static access_mask_t
pick_access_mask_for_request_type(const enum landlock_request_type type,
                                  const struct access_masks access_masks)
{
        switch (type) {
        case LANDLOCK_REQUEST_FS_ACCESS:
                return access_masks.fs;
        case LANDLOCK_REQUEST_NET_ACCESS:
                return access_masks.net;
        default:
                WARN_ONCE(1, "Invalid request type %d passed to %s", type,
                          __func__);
                return 0;
        }
}

/**
 * landlock_log_denial - Create audit records related to a denial
 *
 * @subject: The Landlock subject's credential denying an action.
 * @request: Detail of the user space request.
 */
void landlock_log_denial(const struct landlock_cred_security *const subject,
                         const struct landlock_request *const request)
{
        struct audit_buffer *ab;
        struct landlock_hierarchy *youngest_denied;
        size_t youngest_layer;
        access_mask_t missing;
        bool object_quiet_flag = false, quiet_applicable_to_access = false;

        if (WARN_ON_ONCE(!subject || !subject->domain ||
                         !subject->domain->hierarchy || !request))
                return;

        if (!is_valid_request(request))
                return;

        missing = request->access;
        if (missing) {
                /* Gets the nearest domain that denies the request. */
                if (request->layer_masks) {
                        youngest_layer = get_denied_layer(subject->domain,
                                                          &missing,
                                                          request->layer_masks);
                        object_quiet_flag =
                                request->layer_masks->layers[youngest_layer]
                                        .quiet;
                } else {
                        youngest_layer = get_layer_from_deny_masks(
                                &missing, _LANDLOCK_ACCESS_FS_OPTIONAL,
                                request->deny_masks,
                                request->quiet_optional_accesses,
                                &object_quiet_flag);
                }
                youngest_denied =
                        get_hierarchy(subject->domain, youngest_layer);
        } else {
                youngest_layer = request->layer_plus_one - 1;
                youngest_denied =
                        get_hierarchy(subject->domain, youngest_layer);
        }

        if (READ_ONCE(youngest_denied->log_status) == LANDLOCK_LOG_DISABLED)
                return;

        /*
         * Consistently keeps track of the number of denied access requests
         * even if audit is currently disabled, or if audit rules currently
         * exclude this record type, or if landlock_restrict_self(2)'s flags
         * quiet logs.
         */
        atomic64_inc(&youngest_denied->num_denials);

        if (!audit_enabled)
                return;

        /* Checks if the current exec was restricting itself. */
        if (subject->domain_exec & BIT(youngest_layer)) {
                /* Ignores denials for the same execution. */
                if (!youngest_denied->log_same_exec)
                        return;
        } else {
                /* Ignores denials after a new execution. */
                if (!youngest_denied->log_new_exec)
                        return;
        }

        /*
         * Checks if the object is marked quiet by the layer that denied the
         * request.  If it's a different layer that marked it as quiet, but that
         * layer is not the one that denied the request, we should still audit
         * log the denial.
         */
        if (object_quiet_flag) {
                /*
                 * We now check if the denied requests are all covered by the
                 * layer's quiet access bits.
                 */
                const access_mask_t quiet_mask =
                        pick_access_mask_for_request_type(
                                request->type, youngest_denied->quiet_masks);

                quiet_applicable_to_access = (quiet_mask & missing) == missing;
        } else {
                /*
                 * Either the object is not quiet, or this is a scope request.
                 * We check request->type to distinguish between the two cases.
                 */
                const access_mask_t quiet_mask =
                        youngest_denied->quiet_masks.scope;

                switch (request->type) {
                case LANDLOCK_REQUEST_SCOPE_SIGNAL:
                        quiet_applicable_to_access =
                                !!(quiet_mask & LANDLOCK_SCOPE_SIGNAL);
                        break;
                case LANDLOCK_REQUEST_SCOPE_ABSTRACT_UNIX_SOCKET:
                        quiet_applicable_to_access =
                                !!(quiet_mask &
                                   LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET);
                        break;
                /*
                 * Leave LANDLOCK_REQUEST_PTRACE and
                 * LANDLOCK_REQUEST_FS_CHANGE_TOPOLOGY unhandled for now - they
                 * are never quiet.
                 */
                default:
                        break;
                }
        }

        if (quiet_applicable_to_access)
                return;

        /* Uses consistent allocation flags wrt common_lsm_audit(). */
        ab = audit_log_start(audit_context(), GFP_ATOMIC | __GFP_NOWARN,
                             AUDIT_LANDLOCK_ACCESS);
        if (!ab)
                return;

        audit_log_format(ab, "domain=%llx blockers=", youngest_denied->id);
        log_blockers(ab, request->type, missing);
        audit_log_lsm_data(ab, &request->audit);
        audit_log_end(ab);

        /* Logs this domain the first time it shows in log. */
        log_domain(youngest_denied);
}

/**
 * landlock_log_drop_domain - Create an audit record on domain deallocation
 *
 * @hierarchy: The domain's hierarchy being deallocated.
 *
 * Only domains which previously appeared in the audit logs are logged again.
 * This is useful to know when a domain will never show again in the audit log.
 *
 * Called in a work queue scheduled by landlock_put_ruleset_deferred() called
 * by hook_cred_free().
 */
void landlock_log_drop_domain(const struct landlock_hierarchy *const hierarchy)
{
        struct audit_buffer *ab;

        if (WARN_ON_ONCE(!hierarchy))
                return;

        if (!audit_enabled)
                return;

        /* Ignores domains that were not logged.  */
        if (READ_ONCE(hierarchy->log_status) != LANDLOCK_LOG_RECORDED)
                return;

        /*
         * If logging of domain allocation succeeded, warns about failure to log
         * domain deallocation to highlight unbalanced domain lifetime logs.
         */
        ab = audit_log_start(audit_context(), GFP_KERNEL,
                             AUDIT_LANDLOCK_DOMAIN);
        if (!ab)
                return;

        audit_log_format(ab, "domain=%llx status=deallocated denials=%llu",
                         hierarchy->id, atomic64_read(&hierarchy->num_denials));
        audit_log_end(ab);
}

#ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST

static struct kunit_case test_cases[] = {
        /* clang-format off */
        KUNIT_CASE(test_get_hierarchy),
        KUNIT_CASE(test_get_denied_layer),
        KUNIT_CASE(test_get_layer_from_deny_masks),
        {}
        /* clang-format on */
};

static struct kunit_suite test_suite = {
        .name = "landlock_audit",
        .test_cases = test_cases,
};

kunit_test_suite(test_suite);

#endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */