[RFC PATCH 02/29] lsm: split the init code out into lsm_init.c
John Johansen
john.johansen at canonical.com
Tue Apr 15 22:01:58 UTC 2025
On 4/9/25 11:49, Paul Moore wrote:
> Continue to pull code out of security/security.c to help improve
> readability by pulling all of the LSM framework initialization
> code out into a new file.
>
> No code changes.
>
> Signed-off-by: Paul Moore <paul at paul-moore.com>
Reviewed-by: John Johansen <john.johansen at canonical.com>
> ---
> include/linux/lsm_hooks.h | 3 +-
> security/Makefile | 2 +-
> security/lsm.h | 22 ++
> security/lsm_init.c | 537 ++++++++++++++++++++++++++++++++++
> security/security.c | 591 +++-----------------------------------
> 5 files changed, 595 insertions(+), 560 deletions(-)
> create mode 100644 security/lsm.h
> create mode 100644 security/lsm_init.c
>
> diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
> index 090d1d3e19fe..eeb4bfd60b79 100644
> --- a/include/linux/lsm_hooks.h
> +++ b/include/linux/lsm_hooks.h
> @@ -167,11 +167,10 @@ struct lsm_info {
> __used __section(".early_lsm_info.init") \
> __aligned(sizeof(unsigned long))
>
> +
> /* DO NOT tamper with these variables outside of the LSM framework */
> extern char *lsm_names;
> extern struct lsm_static_calls_table static_calls_table __ro_after_init;
> -extern struct lsm_info __start_lsm_info[], __end_lsm_info[];
> -extern struct lsm_info __start_early_lsm_info[], __end_early_lsm_info[];
>
> /**
> * lsm_get_xattr_slot - Return the next available slot and increment the index
> diff --git a/security/Makefile b/security/Makefile
> index 14d87847bce8..4601230ba442 100644
> --- a/security/Makefile
> +++ b/security/Makefile
> @@ -11,7 +11,7 @@ obj-$(CONFIG_SECURITY) += lsm_syscalls.o
> obj-$(CONFIG_MMU) += min_addr.o
>
> # Object file lists
> -obj-$(CONFIG_SECURITY) += security.o lsm_notifier.o
> +obj-$(CONFIG_SECURITY) += security.o lsm_notifier.o lsm_init.o
> obj-$(CONFIG_SECURITYFS) += inode.o
> obj-$(CONFIG_SECURITY_SELINUX) += selinux/
> obj-$(CONFIG_SECURITY_SMACK) += smack/
> diff --git a/security/lsm.h b/security/lsm.h
> new file mode 100644
> index 000000000000..0e1731bad4a7
> --- /dev/null
> +++ b/security/lsm.h
> @@ -0,0 +1,22 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * LSM functions
> + */
> +
> +#ifndef _LSM_H_
> +#define _LSM_H_
> +
> +#include <linux/lsm_hooks.h>
> +
> +/* LSM blob configuration */
> +extern struct lsm_blob_sizes blob_sizes;
> +
> +/* LSM blob caches */
> +extern struct kmem_cache *lsm_file_cache;
> +extern struct kmem_cache *lsm_inode_cache;
> +
> +/* LSM blob allocators */
> +int lsm_cred_alloc(struct cred *cred, gfp_t gfp);
> +int lsm_task_alloc(struct task_struct *task);
> +
> +#endif /* _LSM_H_ */
> diff --git a/security/lsm_init.c b/security/lsm_init.c
> new file mode 100644
> index 000000000000..70e7d4207dae
> --- /dev/null
> +++ b/security/lsm_init.c
> @@ -0,0 +1,537 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * LSM initialization functions
> + */
> +
> +#define pr_fmt(fmt) "LSM: " fmt
> +
> +#include <linux/init.h>
> +#include <linux/lsm_hooks.h>
> +
> +#include "lsm.h"
> +
> +char *lsm_names;
> +
> +/* Pointers to LSM sections defined in include/asm-generic/vmlinux.lds.h */
> +extern struct lsm_info __start_lsm_info[], __end_lsm_info[];
> +extern struct lsm_info __start_early_lsm_info[], __end_early_lsm_info[];
> +
> +/* Boot-time LSM user choice */
> +static __initconst const char *const builtin_lsm_order = CONFIG_LSM;
> +static __initdata const char *chosen_lsm_order;
> +static __initdata const char *chosen_major_lsm;
> +
> +/* Ordered list of LSMs to initialize. */
> +static __initdata struct lsm_info *ordered_lsms[MAX_LSM_COUNT + 1];
> +static __initdata struct lsm_info *exclusive;
> +
> +static __initdata bool debug;
> +#define init_debug(...) \
> + do { \
> + if (debug) \
> + pr_info(__VA_ARGS__); \
> + } while (0)
> +
> +static int lsm_append(const char *new, char **result);
> +
> +/* Save user chosen LSM */
> +static int __init choose_major_lsm(char *str)
> +{
> + chosen_major_lsm = str;
> + return 1;
> +}
> +__setup("security=", choose_major_lsm);
> +
> +/* Explicitly choose LSM initialization order. */
> +static int __init choose_lsm_order(char *str)
> +{
> + chosen_lsm_order = str;
> + return 1;
> +}
> +__setup("lsm=", choose_lsm_order);
> +
> +/* Enable LSM order debugging. */
> +static int __init enable_debug(char *str)
> +{
> + debug = true;
> + return 1;
> +}
> +__setup("lsm.debug", enable_debug);
> +
> +/* Mark an LSM's enabled flag. */
> +static int lsm_enabled_true __initdata = 1;
> +static int lsm_enabled_false __initdata = 0;
> +static void __init set_enabled(struct lsm_info *lsm, bool enabled)
> +{
> + /*
> + * When an LSM hasn't configured an enable variable, we can use
> + * a hard-coded location for storing the default enabled state.
> + */
> + if (!lsm->enabled) {
> + if (enabled)
> + lsm->enabled = &lsm_enabled_true;
> + else
> + lsm->enabled = &lsm_enabled_false;
> + } else if (lsm->enabled == &lsm_enabled_true) {
> + if (!enabled)
> + lsm->enabled = &lsm_enabled_false;
> + } else if (lsm->enabled == &lsm_enabled_false) {
> + if (enabled)
> + lsm->enabled = &lsm_enabled_true;
> + } else {
> + *lsm->enabled = enabled;
> + }
> +}
> +
> +static inline bool is_enabled(struct lsm_info *lsm)
> +{
> + if (!lsm->enabled)
> + return false;
> +
> + return *lsm->enabled;
> +}
> +
> +/* Is an LSM already listed in the ordered LSMs list? */
> +static bool __init exists_ordered_lsm(struct lsm_info *lsm)
> +{
> + struct lsm_info **check;
> +
> + for (check = ordered_lsms; *check; check++)
> + if (*check == lsm)
> + return true;
> +
> + return false;
> +}
> +
> +/* Append an LSM to the list of ordered LSMs to initialize. */
> +static int last_lsm __initdata;
> +static void __init append_ordered_lsm(struct lsm_info *lsm, const char *from)
> +{
> + /* Ignore duplicate selections. */
> + if (exists_ordered_lsm(lsm))
> + return;
> +
> + if (WARN(last_lsm == MAX_LSM_COUNT, "%s: out of LSM static calls!?\n", from))
> + return;
> +
> + /* Enable this LSM, if it is not already set. */
> + if (!lsm->enabled)
> + lsm->enabled = &lsm_enabled_true;
> + ordered_lsms[last_lsm++] = lsm;
> +
> + init_debug("%s ordered: %s (%s)\n", from, lsm->name,
> + is_enabled(lsm) ? "enabled" : "disabled");
> +}
> +
> +/* Is an LSM allowed to be initialized? */
> +static bool __init lsm_allowed(struct lsm_info *lsm)
> +{
> + /* Skip if the LSM is disabled. */
> + if (!is_enabled(lsm))
> + return false;
> +
> + /* Not allowed if another exclusive LSM already initialized. */
> + if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && exclusive) {
> + init_debug("exclusive disabled: %s\n", lsm->name);
> + return false;
> + }
> +
> + return true;
> +}
> +
> +static void __init lsm_set_blob_size(int *need, int *lbs)
> +{
> + int offset;
> +
> + if (*need <= 0)
> + return;
> +
> + offset = ALIGN(*lbs, sizeof(void *));
> + *lbs = offset + *need;
> + *need = offset;
> +}
> +
> +static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed)
> +{
> + if (!needed)
> + return;
> +
> + lsm_set_blob_size(&needed->lbs_cred, &blob_sizes.lbs_cred);
> + lsm_set_blob_size(&needed->lbs_file, &blob_sizes.lbs_file);
> + lsm_set_blob_size(&needed->lbs_ib, &blob_sizes.lbs_ib);
> + /*
> + * The inode blob gets an rcu_head in addition to
> + * what the modules might need.
> + */
> + if (needed->lbs_inode && blob_sizes.lbs_inode == 0)
> + blob_sizes.lbs_inode = sizeof(struct rcu_head);
> + lsm_set_blob_size(&needed->lbs_inode, &blob_sizes.lbs_inode);
> + lsm_set_blob_size(&needed->lbs_ipc, &blob_sizes.lbs_ipc);
> + lsm_set_blob_size(&needed->lbs_key, &blob_sizes.lbs_key);
> + lsm_set_blob_size(&needed->lbs_msg_msg, &blob_sizes.lbs_msg_msg);
> + lsm_set_blob_size(&needed->lbs_perf_event, &blob_sizes.lbs_perf_event);
> + lsm_set_blob_size(&needed->lbs_sock, &blob_sizes.lbs_sock);
> + lsm_set_blob_size(&needed->lbs_superblock, &blob_sizes.lbs_superblock);
> + lsm_set_blob_size(&needed->lbs_task, &blob_sizes.lbs_task);
> + lsm_set_blob_size(&needed->lbs_tun_dev, &blob_sizes.lbs_tun_dev);
> + lsm_set_blob_size(&needed->lbs_xattr_count,
> + &blob_sizes.lbs_xattr_count);
> + lsm_set_blob_size(&needed->lbs_bdev, &blob_sizes.lbs_bdev);
> +}
> +
> +/* Prepare LSM for initialization. */
> +static void __init prepare_lsm(struct lsm_info *lsm)
> +{
> + int enabled = lsm_allowed(lsm);
> +
> + /* Record enablement (to handle any following exclusive LSMs). */
> + set_enabled(lsm, enabled);
> +
> + /* If enabled, do pre-initialization work. */
> + if (enabled) {
> + if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && !exclusive) {
> + exclusive = lsm;
> + init_debug("exclusive chosen: %s\n", lsm->name);
> + }
> +
> + lsm_set_blob_sizes(lsm->blobs);
> + }
> +}
> +
> +/* Initialize a given LSM, if it is enabled. */
> +static void __init initialize_lsm(struct lsm_info *lsm)
> +{
> + if (is_enabled(lsm)) {
> + int ret;
> +
> + init_debug("initializing %s\n", lsm->name);
> + ret = lsm->init();
> + WARN(ret, "%s failed to initialize: %d\n", lsm->name, ret);
> + }
> +}
> +
> +/*
> + * Current index to use while initializing the lsm id list.
> + */
> +u32 lsm_active_cnt __ro_after_init;
> +const struct lsm_id *lsm_idlist[MAX_LSM_COUNT];
> +
> +/* Populate ordered LSMs list from comma-separated LSM name list. */
> +static void __init ordered_lsm_parse(const char *order, const char *origin)
> +{
> + struct lsm_info *lsm;
> + char *sep, *name, *next;
> +
> + /* LSM_ORDER_FIRST is always first. */
> + for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
> + if (lsm->order == LSM_ORDER_FIRST)
> + append_ordered_lsm(lsm, " first");
> + }
> +
> + /* Process "security=", if given. */
> + if (chosen_major_lsm) {
> + struct lsm_info *major;
> +
> + /*
> + * To match the original "security=" behavior, this
> + * explicitly does NOT fallback to another Legacy Major
> + * if the selected one was separately disabled: disable
> + * all non-matching Legacy Major LSMs.
> + */
> + for (major = __start_lsm_info; major < __end_lsm_info;
> + major++) {
> + if ((major->flags & LSM_FLAG_LEGACY_MAJOR) &&
> + strcmp(major->name, chosen_major_lsm) != 0) {
> + set_enabled(major, false);
> + init_debug("security=%s disabled: %s (only one legacy major LSM)\n",
> + chosen_major_lsm, major->name);
> + }
> + }
> + }
> +
> + sep = kstrdup(order, GFP_KERNEL);
> + next = sep;
> + /* Walk the list, looking for matching LSMs. */
> + while ((name = strsep(&next, ",")) != NULL) {
> + bool found = false;
> +
> + for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
> + if (strcmp(lsm->name, name) == 0) {
> + if (lsm->order == LSM_ORDER_MUTABLE)
> + append_ordered_lsm(lsm, origin);
> + found = true;
> + }
> + }
> +
> + if (!found)
> + init_debug("%s ignored: %s (not built into kernel)\n",
> + origin, name);
> + }
> +
> + /* Process "security=", if given. */
> + if (chosen_major_lsm) {
> + for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
> + if (exists_ordered_lsm(lsm))
> + continue;
> + if (strcmp(lsm->name, chosen_major_lsm) == 0)
> + append_ordered_lsm(lsm, "security=");
> + }
> + }
> +
> + /* LSM_ORDER_LAST is always last. */
> + for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
> + if (lsm->order == LSM_ORDER_LAST)
> + append_ordered_lsm(lsm, " last");
> + }
> +
> + /* Disable all LSMs not in the ordered list. */
> + for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
> + if (exists_ordered_lsm(lsm))
> + continue;
> + set_enabled(lsm, false);
> + init_debug("%s skipped: %s (not in requested order)\n",
> + origin, lsm->name);
> + }
> +
> + kfree(sep);
> +}
> +
> +static void __init report_lsm_order(void)
> +{
> + struct lsm_info **lsm, *early;
> + int first = 0;
> +
> + pr_info("initializing lsm=");
> +
> + /* Report each enabled LSM name, comma separated. */
> + for (early = __start_early_lsm_info;
> + early < __end_early_lsm_info; early++)
> + if (is_enabled(early))
> + pr_cont("%s%s", first++ == 0 ? "" : ",", early->name);
> + for (lsm = ordered_lsms; *lsm; lsm++)
> + if (is_enabled(*lsm))
> + pr_cont("%s%s", first++ == 0 ? "" : ",", (*lsm)->name);
> +
> + pr_cont("\n");
> +}
> +
> +/**
> + * lsm_early_cred - during initialization allocate a composite cred blob
> + * @cred: the cred that needs a blob
> + *
> + * Allocate the cred blob for all the modules
> + */
> +static void __init lsm_early_cred(struct cred *cred)
> +{
> + int rc = lsm_cred_alloc(cred, GFP_KERNEL);
> +
> + if (rc)
> + panic("%s: Early cred alloc failed.\n", __func__);
> +}
> +
> +/**
> + * lsm_early_task - during initialization allocate a composite task blob
> + * @task: the task that needs a blob
> + *
> + * Allocate the task blob for all the modules
> + */
> +static void __init lsm_early_task(struct task_struct *task)
> +{
> + int rc = lsm_task_alloc(task);
> +
> + if (rc)
> + panic("%s: Early task alloc failed.\n", __func__);
> +}
> +
> +static void __init ordered_lsm_init(void)
> +{
> + struct lsm_info **lsm;
> +
> + if (chosen_lsm_order) {
> + if (chosen_major_lsm) {
> + pr_warn("security=%s is ignored because it is superseded by lsm=%s\n",
> + chosen_major_lsm, chosen_lsm_order);
> + chosen_major_lsm = NULL;
> + }
> + ordered_lsm_parse(chosen_lsm_order, "cmdline");
> + } else
> + ordered_lsm_parse(builtin_lsm_order, "builtin");
> +
> + for (lsm = ordered_lsms; *lsm; lsm++)
> + prepare_lsm(*lsm);
> +
> + report_lsm_order();
> +
> + init_debug("cred blob size = %d\n", blob_sizes.lbs_cred);
> + init_debug("file blob size = %d\n", blob_sizes.lbs_file);
> + init_debug("ib blob size = %d\n", blob_sizes.lbs_ib);
> + init_debug("inode blob size = %d\n", blob_sizes.lbs_inode);
> + init_debug("ipc blob size = %d\n", blob_sizes.lbs_ipc);
> +#ifdef CONFIG_KEYS
> + init_debug("key blob size = %d\n", blob_sizes.lbs_key);
> +#endif /* CONFIG_KEYS */
> + init_debug("msg_msg blob size = %d\n", blob_sizes.lbs_msg_msg);
> + init_debug("sock blob size = %d\n", blob_sizes.lbs_sock);
> + init_debug("superblock blob size = %d\n", blob_sizes.lbs_superblock);
> + init_debug("perf event blob size = %d\n", blob_sizes.lbs_perf_event);
> + init_debug("task blob size = %d\n", blob_sizes.lbs_task);
> + init_debug("tun device blob size = %d\n", blob_sizes.lbs_tun_dev);
> + init_debug("xattr slots = %d\n", blob_sizes.lbs_xattr_count);
> + init_debug("bdev blob size = %d\n", blob_sizes.lbs_bdev);
> +
> + /*
> + * Create any kmem_caches needed for blobs
> + */
> + if (blob_sizes.lbs_file)
> + lsm_file_cache = kmem_cache_create("lsm_file_cache",
> + blob_sizes.lbs_file, 0,
> + SLAB_PANIC, NULL);
> + if (blob_sizes.lbs_inode)
> + lsm_inode_cache = kmem_cache_create("lsm_inode_cache",
> + blob_sizes.lbs_inode, 0,
> + SLAB_PANIC, NULL);
> +
> + lsm_early_cred((struct cred *) current->cred);
> + lsm_early_task(current);
> + for (lsm = ordered_lsms; *lsm; lsm++)
> + initialize_lsm(*lsm);
> +}
> +
> +static bool match_last_lsm(const char *list, const char *lsm)
> +{
> + const char *last;
> +
> + if (WARN_ON(!list || !lsm))
> + return false;
> + last = strrchr(list, ',');
> + if (last)
> + /* Pass the comma, strcmp() will check for '\0' */
> + last++;
> + else
> + last = list;
> + return !strcmp(last, lsm);
> +}
> +
> +static int lsm_append(const char *new, char **result)
> +{
> + char *cp;
> +
> + if (*result == NULL) {
> + *result = kstrdup(new, GFP_KERNEL);
> + if (*result == NULL)
> + return -ENOMEM;
> + } else {
> + /* Check if it is the last registered name */
> + if (match_last_lsm(*result, new))
> + return 0;
> + cp = kasprintf(GFP_KERNEL, "%s,%s", *result, new);
> + if (cp == NULL)
> + return -ENOMEM;
> + kfree(*result);
> + *result = cp;
> + }
> + return 0;
> +}
> +
> +static void __init lsm_static_call_init(struct security_hook_list *hl)
> +{
> + struct lsm_static_call *scall = hl->scalls;
> + int i;
> +
> + for (i = 0; i < MAX_LSM_COUNT; i++) {
> + /* Update the first static call that is not used yet */
> + if (!scall->hl) {
> + __static_call_update(scall->key, scall->trampoline,
> + hl->hook.lsm_func_addr);
> + scall->hl = hl;
> + static_branch_enable(scall->active);
> + return;
> + }
> + scall++;
> + }
> + panic("%s - Ran out of static slots.\n", __func__);
> +}
> +
> +/**
> + * security_add_hooks - Add a modules hooks to the hook lists.
> + * @hooks: the hooks to add
> + * @count: the number of hooks to add
> + * @lsmid: the identification information for the security module
> + *
> + * Each LSM has to register its hooks with the infrastructure.
> + */
> +void __init security_add_hooks(struct security_hook_list *hooks, int count,
> + const struct lsm_id *lsmid)
> +{
> + int i;
> +
> + /*
> + * A security module may call security_add_hooks() more
> + * than once during initialization, and LSM initialization
> + * is serialized. Landlock is one such case.
> + * Look at the previous entry, if there is one, for duplication.
> + */
> + if (lsm_active_cnt == 0 || lsm_idlist[lsm_active_cnt - 1] != lsmid) {
> + if (lsm_active_cnt >= MAX_LSM_COUNT)
> + panic("%s Too many LSMs registered.\n", __func__);
> + lsm_idlist[lsm_active_cnt++] = lsmid;
> + }
> +
> + for (i = 0; i < count; i++) {
> + hooks[i].lsmid = lsmid;
> + lsm_static_call_init(&hooks[i]);
> + }
> +
> + /*
> + * Don't try to append during early_security_init(), we'll come back
> + * and fix this up afterwards.
> + */
> + if (slab_is_available()) {
> + if (lsm_append(lsmid->name, &lsm_names) < 0)
> + panic("%s - Cannot get early memory.\n", __func__);
> + }
> +}
> +
> +int __init early_security_init(void)
> +{
> + struct lsm_info *lsm;
> +
> + for (lsm = __start_early_lsm_info; lsm < __end_early_lsm_info; lsm++) {
> + if (!lsm->enabled)
> + lsm->enabled = &lsm_enabled_true;
> + prepare_lsm(lsm);
> + initialize_lsm(lsm);
> + }
> +
> + return 0;
> +}
> +
> +/**
> + * security_init - initializes the security framework
> + *
> + * This should be called early in the kernel initialization sequence.
> + */
> +int __init security_init(void)
> +{
> + struct lsm_info *lsm;
> +
> + init_debug("legacy security=%s\n", chosen_major_lsm ? : " *unspecified*");
> + init_debug(" CONFIG_LSM=%s\n", builtin_lsm_order);
> + init_debug("boot arg lsm=%s\n", chosen_lsm_order ? : " *unspecified*");
> +
> + /*
> + * Append the names of the early LSM modules now that kmalloc() is
> + * available
> + */
> + for (lsm = __start_early_lsm_info; lsm < __end_early_lsm_info; lsm++) {
> + init_debug(" early started: %s (%s)\n", lsm->name,
> + is_enabled(lsm) ? "enabled" : "disabled");
> + if (lsm->enabled)
> + lsm_append(lsm->name, &lsm_names);
> + }
> +
> + /* Load LSMs in specified order. */
> + ordered_lsm_init();
> +
> + return 0;
> +}
> diff --git a/security/security.c b/security/security.c
> index 477be0a17e3f..8d370a4c5e74 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -32,24 +32,7 @@
> #include <net/flow.h>
> #include <net/sock.h>
>
> -#define SECURITY_HOOK_ACTIVE_KEY(HOOK, IDX) security_hook_active_##HOOK##_##IDX
> -
> -/*
> - * Identifier for the LSM static calls.
> - * HOOK is an LSM hook as defined in linux/lsm_hookdefs.h
> - * IDX is the index of the static call. 0 <= NUM < MAX_LSM_COUNT
> - */
> -#define LSM_STATIC_CALL(HOOK, IDX) lsm_static_call_##HOOK##_##IDX
> -
> -/*
> - * Call the macro M for each LSM hook MAX_LSM_COUNT times.
> - */
> -#define LSM_LOOP_UNROLL(M, ...) \
> -do { \
> - UNROLL(MAX_LSM_COUNT, M, __VA_ARGS__) \
> -} while (0)
> -
> -#define LSM_DEFINE_UNROLL(M, ...) UNROLL(MAX_LSM_COUNT, M, __VA_ARGS__)
> +#include "lsm.h"
>
> /*
> * These are descriptions of the reasons that can be passed to the
> @@ -90,21 +73,29 @@ const char *const lockdown_reasons[LOCKDOWN_CONFIDENTIALITY_MAX + 1] = {
> [LOCKDOWN_CONFIDENTIALITY_MAX] = "confidentiality",
> };
>
> -static struct kmem_cache *lsm_file_cache;
> -static struct kmem_cache *lsm_inode_cache;
> +struct lsm_blob_sizes blob_sizes;
>
> -char *lsm_names;
> -static struct lsm_blob_sizes blob_sizes __ro_after_init;
> +struct kmem_cache *lsm_file_cache;
> +struct kmem_cache *lsm_inode_cache;
>
> -/* Boot-time LSM user choice */
> -static __initdata const char *chosen_lsm_order;
> -static __initdata const char *chosen_major_lsm;
> +#define SECURITY_HOOK_ACTIVE_KEY(HOOK, IDX) security_hook_active_##HOOK##_##IDX
>
> -static __initconst const char *const builtin_lsm_order = CONFIG_LSM;
> +/*
> + * Identifier for the LSM static calls.
> + * HOOK is an LSM hook as defined in linux/lsm_hookdefs.h
> + * IDX is the index of the static call. 0 <= NUM < MAX_LSM_COUNT
> + */
> +#define LSM_STATIC_CALL(HOOK, IDX) lsm_static_call_##HOOK##_##IDX
>
> -/* Ordered list of LSMs to initialize. */
> -static __initdata struct lsm_info *ordered_lsms[MAX_LSM_COUNT + 1];
> -static __initdata struct lsm_info *exclusive;
> +/*
> + * Call the macro M for each LSM hook MAX_LSM_COUNT times.
> + */
> +#define LSM_LOOP_UNROLL(M, ...) \
> +do { \
> + UNROLL(MAX_LSM_COUNT, M, __VA_ARGS__) \
> +} while (0)
> +
> +#define LSM_DEFINE_UNROLL(M, ...) UNROLL(MAX_LSM_COUNT, M, __VA_ARGS__)
>
> #ifdef CONFIG_HAVE_STATIC_CALL
> #define LSM_HOOK_TRAMP(NAME, NUM) \
> @@ -155,490 +146,25 @@ struct lsm_static_calls_table
> #undef INIT_LSM_STATIC_CALL
> };
>
> -static __initdata bool debug;
> -#define init_debug(...) \
> - do { \
> - if (debug) \
> - pr_info(__VA_ARGS__); \
> - } while (0)
> -
> -static bool __init is_enabled(struct lsm_info *lsm)
> -{
> - if (!lsm->enabled)
> - return false;
> -
> - return *lsm->enabled;
> -}
> -
> -/* Mark an LSM's enabled flag. */
> -static int lsm_enabled_true __initdata = 1;
> -static int lsm_enabled_false __initdata = 0;
> -static void __init set_enabled(struct lsm_info *lsm, bool enabled)
> -{
> - /*
> - * When an LSM hasn't configured an enable variable, we can use
> - * a hard-coded location for storing the default enabled state.
> - */
> - if (!lsm->enabled) {
> - if (enabled)
> - lsm->enabled = &lsm_enabled_true;
> - else
> - lsm->enabled = &lsm_enabled_false;
> - } else if (lsm->enabled == &lsm_enabled_true) {
> - if (!enabled)
> - lsm->enabled = &lsm_enabled_false;
> - } else if (lsm->enabled == &lsm_enabled_false) {
> - if (enabled)
> - lsm->enabled = &lsm_enabled_true;
> - } else {
> - *lsm->enabled = enabled;
> - }
> -}
> -
> -/* Is an LSM already listed in the ordered LSMs list? */
> -static bool __init exists_ordered_lsm(struct lsm_info *lsm)
> -{
> - struct lsm_info **check;
> -
> - for (check = ordered_lsms; *check; check++)
> - if (*check == lsm)
> - return true;
> -
> - return false;
> -}
> -
> -/* Append an LSM to the list of ordered LSMs to initialize. */
> -static int last_lsm __initdata;
> -static void __init append_ordered_lsm(struct lsm_info *lsm, const char *from)
> -{
> - /* Ignore duplicate selections. */
> - if (exists_ordered_lsm(lsm))
> - return;
> -
> - if (WARN(last_lsm == MAX_LSM_COUNT, "%s: out of LSM static calls!?\n", from))
> - return;
> -
> - /* Enable this LSM, if it is not already set. */
> - if (!lsm->enabled)
> - lsm->enabled = &lsm_enabled_true;
> - ordered_lsms[last_lsm++] = lsm;
> -
> - init_debug("%s ordered: %s (%s)\n", from, lsm->name,
> - is_enabled(lsm) ? "enabled" : "disabled");
> -}
> -
> -/* Is an LSM allowed to be initialized? */
> -static bool __init lsm_allowed(struct lsm_info *lsm)
> -{
> - /* Skip if the LSM is disabled. */
> - if (!is_enabled(lsm))
> - return false;
> -
> - /* Not allowed if another exclusive LSM already initialized. */
> - if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && exclusive) {
> - init_debug("exclusive disabled: %s\n", lsm->name);
> - return false;
> - }
> -
> - return true;
> -}
> -
> -static void __init lsm_set_blob_size(int *need, int *lbs)
> -{
> - int offset;
> -
> - if (*need <= 0)
> - return;
> -
> - offset = ALIGN(*lbs, sizeof(void *));
> - *lbs = offset + *need;
> - *need = offset;
> -}
> -
> -static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed)
> -{
> - if (!needed)
> - return;
> -
> - lsm_set_blob_size(&needed->lbs_cred, &blob_sizes.lbs_cred);
> - lsm_set_blob_size(&needed->lbs_file, &blob_sizes.lbs_file);
> - lsm_set_blob_size(&needed->lbs_ib, &blob_sizes.lbs_ib);
> - /*
> - * The inode blob gets an rcu_head in addition to
> - * what the modules might need.
> - */
> - if (needed->lbs_inode && blob_sizes.lbs_inode == 0)
> - blob_sizes.lbs_inode = sizeof(struct rcu_head);
> - lsm_set_blob_size(&needed->lbs_inode, &blob_sizes.lbs_inode);
> - lsm_set_blob_size(&needed->lbs_ipc, &blob_sizes.lbs_ipc);
> - lsm_set_blob_size(&needed->lbs_key, &blob_sizes.lbs_key);
> - lsm_set_blob_size(&needed->lbs_msg_msg, &blob_sizes.lbs_msg_msg);
> - lsm_set_blob_size(&needed->lbs_perf_event, &blob_sizes.lbs_perf_event);
> - lsm_set_blob_size(&needed->lbs_sock, &blob_sizes.lbs_sock);
> - lsm_set_blob_size(&needed->lbs_superblock, &blob_sizes.lbs_superblock);
> - lsm_set_blob_size(&needed->lbs_task, &blob_sizes.lbs_task);
> - lsm_set_blob_size(&needed->lbs_tun_dev, &blob_sizes.lbs_tun_dev);
> - lsm_set_blob_size(&needed->lbs_xattr_count,
> - &blob_sizes.lbs_xattr_count);
> - lsm_set_blob_size(&needed->lbs_bdev, &blob_sizes.lbs_bdev);
> -}
> -
> -/* Prepare LSM for initialization. */
> -static void __init prepare_lsm(struct lsm_info *lsm)
> -{
> - int enabled = lsm_allowed(lsm);
> -
> - /* Record enablement (to handle any following exclusive LSMs). */
> - set_enabled(lsm, enabled);
> -
> - /* If enabled, do pre-initialization work. */
> - if (enabled) {
> - if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && !exclusive) {
> - exclusive = lsm;
> - init_debug("exclusive chosen: %s\n", lsm->name);
> - }
> -
> - lsm_set_blob_sizes(lsm->blobs);
> - }
> -}
> -
> -/* Initialize a given LSM, if it is enabled. */
> -static void __init initialize_lsm(struct lsm_info *lsm)
> -{
> - if (is_enabled(lsm)) {
> - int ret;
> -
> - init_debug("initializing %s\n", lsm->name);
> - ret = lsm->init();
> - WARN(ret, "%s failed to initialize: %d\n", lsm->name, ret);
> - }
> -}
> -
> -/*
> - * Current index to use while initializing the lsm id list.
> - */
> -u32 lsm_active_cnt __ro_after_init;
> -const struct lsm_id *lsm_idlist[MAX_LSM_COUNT];
> -
> -/* Populate ordered LSMs list from comma-separated LSM name list. */
> -static void __init ordered_lsm_parse(const char *order, const char *origin)
> -{
> - struct lsm_info *lsm;
> - char *sep, *name, *next;
> -
> - /* LSM_ORDER_FIRST is always first. */
> - for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
> - if (lsm->order == LSM_ORDER_FIRST)
> - append_ordered_lsm(lsm, " first");
> - }
> -
> - /* Process "security=", if given. */
> - if (chosen_major_lsm) {
> - struct lsm_info *major;
> -
> - /*
> - * To match the original "security=" behavior, this
> - * explicitly does NOT fallback to another Legacy Major
> - * if the selected one was separately disabled: disable
> - * all non-matching Legacy Major LSMs.
> - */
> - for (major = __start_lsm_info; major < __end_lsm_info;
> - major++) {
> - if ((major->flags & LSM_FLAG_LEGACY_MAJOR) &&
> - strcmp(major->name, chosen_major_lsm) != 0) {
> - set_enabled(major, false);
> - init_debug("security=%s disabled: %s (only one legacy major LSM)\n",
> - chosen_major_lsm, major->name);
> - }
> - }
> - }
> -
> - sep = kstrdup(order, GFP_KERNEL);
> - next = sep;
> - /* Walk the list, looking for matching LSMs. */
> - while ((name = strsep(&next, ",")) != NULL) {
> - bool found = false;
> -
> - for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
> - if (strcmp(lsm->name, name) == 0) {
> - if (lsm->order == LSM_ORDER_MUTABLE)
> - append_ordered_lsm(lsm, origin);
> - found = true;
> - }
> - }
> -
> - if (!found)
> - init_debug("%s ignored: %s (not built into kernel)\n",
> - origin, name);
> - }
> -
> - /* Process "security=", if given. */
> - if (chosen_major_lsm) {
> - for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
> - if (exists_ordered_lsm(lsm))
> - continue;
> - if (strcmp(lsm->name, chosen_major_lsm) == 0)
> - append_ordered_lsm(lsm, "security=");
> - }
> - }
> -
> - /* LSM_ORDER_LAST is always last. */
> - for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
> - if (lsm->order == LSM_ORDER_LAST)
> - append_ordered_lsm(lsm, " last");
> - }
> -
> - /* Disable all LSMs not in the ordered list. */
> - for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
> - if (exists_ordered_lsm(lsm))
> - continue;
> - set_enabled(lsm, false);
> - init_debug("%s skipped: %s (not in requested order)\n",
> - origin, lsm->name);
> - }
> -
> - kfree(sep);
> -}
> -
> -static void __init lsm_static_call_init(struct security_hook_list *hl)
> -{
> - struct lsm_static_call *scall = hl->scalls;
> - int i;
> -
> - for (i = 0; i < MAX_LSM_COUNT; i++) {
> - /* Update the first static call that is not used yet */
> - if (!scall->hl) {
> - __static_call_update(scall->key, scall->trampoline,
> - hl->hook.lsm_func_addr);
> - scall->hl = hl;
> - static_branch_enable(scall->active);
> - return;
> - }
> - scall++;
> - }
> - panic("%s - Ran out of static slots.\n", __func__);
> -}
> -
> -static void __init lsm_early_cred(struct cred *cred);
> -static void __init lsm_early_task(struct task_struct *task);
> -
> -static int lsm_append(const char *new, char **result);
> -
> -static void __init report_lsm_order(void)
> -{
> - struct lsm_info **lsm, *early;
> - int first = 0;
> -
> - pr_info("initializing lsm=");
> -
> - /* Report each enabled LSM name, comma separated. */
> - for (early = __start_early_lsm_info;
> - early < __end_early_lsm_info; early++)
> - if (is_enabled(early))
> - pr_cont("%s%s", first++ == 0 ? "" : ",", early->name);
> - for (lsm = ordered_lsms; *lsm; lsm++)
> - if (is_enabled(*lsm))
> - pr_cont("%s%s", first++ == 0 ? "" : ",", (*lsm)->name);
> -
> - pr_cont("\n");
> -}
> -
> -static void __init ordered_lsm_init(void)
> -{
> - struct lsm_info **lsm;
> -
> - if (chosen_lsm_order) {
> - if (chosen_major_lsm) {
> - pr_warn("security=%s is ignored because it is superseded by lsm=%s\n",
> - chosen_major_lsm, chosen_lsm_order);
> - chosen_major_lsm = NULL;
> - }
> - ordered_lsm_parse(chosen_lsm_order, "cmdline");
> - } else
> - ordered_lsm_parse(builtin_lsm_order, "builtin");
> -
> - for (lsm = ordered_lsms; *lsm; lsm++)
> - prepare_lsm(*lsm);
> -
> - report_lsm_order();
> -
> - init_debug("cred blob size = %d\n", blob_sizes.lbs_cred);
> - init_debug("file blob size = %d\n", blob_sizes.lbs_file);
> - init_debug("ib blob size = %d\n", blob_sizes.lbs_ib);
> - init_debug("inode blob size = %d\n", blob_sizes.lbs_inode);
> - init_debug("ipc blob size = %d\n", blob_sizes.lbs_ipc);
> -#ifdef CONFIG_KEYS
> - init_debug("key blob size = %d\n", blob_sizes.lbs_key);
> -#endif /* CONFIG_KEYS */
> - init_debug("msg_msg blob size = %d\n", blob_sizes.lbs_msg_msg);
> - init_debug("sock blob size = %d\n", blob_sizes.lbs_sock);
> - init_debug("superblock blob size = %d\n", blob_sizes.lbs_superblock);
> - init_debug("perf event blob size = %d\n", blob_sizes.lbs_perf_event);
> - init_debug("task blob size = %d\n", blob_sizes.lbs_task);
> - init_debug("tun device blob size = %d\n", blob_sizes.lbs_tun_dev);
> - init_debug("xattr slots = %d\n", blob_sizes.lbs_xattr_count);
> - init_debug("bdev blob size = %d\n", blob_sizes.lbs_bdev);
> -
> - /*
> - * Create any kmem_caches needed for blobs
> - */
> - if (blob_sizes.lbs_file)
> - lsm_file_cache = kmem_cache_create("lsm_file_cache",
> - blob_sizes.lbs_file, 0,
> - SLAB_PANIC, NULL);
> - if (blob_sizes.lbs_inode)
> - lsm_inode_cache = kmem_cache_create("lsm_inode_cache",
> - blob_sizes.lbs_inode, 0,
> - SLAB_PANIC, NULL);
> -
> - lsm_early_cred((struct cred *) current->cred);
> - lsm_early_task(current);
> - for (lsm = ordered_lsms; *lsm; lsm++)
> - initialize_lsm(*lsm);
> -}
> -
> -int __init early_security_init(void)
> -{
> - struct lsm_info *lsm;
> -
> - for (lsm = __start_early_lsm_info; lsm < __end_early_lsm_info; lsm++) {
> - if (!lsm->enabled)
> - lsm->enabled = &lsm_enabled_true;
> - prepare_lsm(lsm);
> - initialize_lsm(lsm);
> - }
> -
> - return 0;
> -}
> -
> /**
> - * security_init - initializes the security framework
> + * lsm_file_alloc - allocate a composite file blob
> + * @file: the file that needs a blob
> *
> - * This should be called early in the kernel initialization sequence.
> - */
> -int __init security_init(void)
> -{
> - struct lsm_info *lsm;
> -
> - init_debug("legacy security=%s\n", chosen_major_lsm ? : " *unspecified*");
> - init_debug(" CONFIG_LSM=%s\n", builtin_lsm_order);
> - init_debug("boot arg lsm=%s\n", chosen_lsm_order ? : " *unspecified*");
> -
> - /*
> - * Append the names of the early LSM modules now that kmalloc() is
> - * available
> - */
> - for (lsm = __start_early_lsm_info; lsm < __end_early_lsm_info; lsm++) {
> - init_debug(" early started: %s (%s)\n", lsm->name,
> - is_enabled(lsm) ? "enabled" : "disabled");
> - if (lsm->enabled)
> - lsm_append(lsm->name, &lsm_names);
> - }
> -
> - /* Load LSMs in specified order. */
> - ordered_lsm_init();
> -
> - return 0;
> -}
> -
> -/* Save user chosen LSM */
> -static int __init choose_major_lsm(char *str)
> -{
> - chosen_major_lsm = str;
> - return 1;
> -}
> -__setup("security=", choose_major_lsm);
> -
> -/* Explicitly choose LSM initialization order. */
> -static int __init choose_lsm_order(char *str)
> -{
> - chosen_lsm_order = str;
> - return 1;
> -}
> -__setup("lsm=", choose_lsm_order);
> -
> -/* Enable LSM order debugging. */
> -static int __init enable_debug(char *str)
> -{
> - debug = true;
> - return 1;
> -}
> -__setup("lsm.debug", enable_debug);
> -
> -static bool match_last_lsm(const char *list, const char *lsm)
> -{
> - const char *last;
> -
> - if (WARN_ON(!list || !lsm))
> - return false;
> - last = strrchr(list, ',');
> - if (last)
> - /* Pass the comma, strcmp() will check for '\0' */
> - last++;
> - else
> - last = list;
> - return !strcmp(last, lsm);
> -}
> -
> -static int lsm_append(const char *new, char **result)
> -{
> - char *cp;
> -
> - if (*result == NULL) {
> - *result = kstrdup(new, GFP_KERNEL);
> - if (*result == NULL)
> - return -ENOMEM;
> - } else {
> - /* Check if it is the last registered name */
> - if (match_last_lsm(*result, new))
> - return 0;
> - cp = kasprintf(GFP_KERNEL, "%s,%s", *result, new);
> - if (cp == NULL)
> - return -ENOMEM;
> - kfree(*result);
> - *result = cp;
> - }
> - return 0;
> -}
> -
> -/**
> - * security_add_hooks - Add a modules hooks to the hook lists.
> - * @hooks: the hooks to add
> - * @count: the number of hooks to add
> - * @lsmid: the identification information for the security module
> + * Allocate the file blob for all the modules
> *
> - * Each LSM has to register its hooks with the infrastructure.
> + * Returns 0, or -ENOMEM if memory can't be allocated.
> */
> -void __init security_add_hooks(struct security_hook_list *hooks, int count,
> - const struct lsm_id *lsmid)
> +static int lsm_file_alloc(struct file *file)
> {
> - int i;
> -
> - /*
> - * A security module may call security_add_hooks() more
> - * than once during initialization, and LSM initialization
> - * is serialized. Landlock is one such case.
> - * Look at the previous entry, if there is one, for duplication.
> - */
> - if (lsm_active_cnt == 0 || lsm_idlist[lsm_active_cnt - 1] != lsmid) {
> - if (lsm_active_cnt >= MAX_LSM_COUNT)
> - panic("%s Too many LSMs registered.\n", __func__);
> - lsm_idlist[lsm_active_cnt++] = lsmid;
> + if (!lsm_file_cache) {
> + file->f_security = NULL;
> + return 0;
> }
>
> - for (i = 0; i < count; i++) {
> - hooks[i].lsmid = lsmid;
> - lsm_static_call_init(&hooks[i]);
> - }
> -
> - /*
> - * Don't try to append during early_security_init(), we'll come back
> - * and fix this up afterwards.
> - */
> - if (slab_is_available()) {
> - if (lsm_append(lsmid->name, &lsm_names) < 0)
> - panic("%s - Cannot get early memory.\n", __func__);
> - }
> + file->f_security = kmem_cache_zalloc(lsm_file_cache, GFP_KERNEL);
> + if (file->f_security == NULL)
> + return -ENOMEM;
> + return 0;
> }
>
> /**
> @@ -673,46 +199,11 @@ static int lsm_blob_alloc(void **dest, size_t size, gfp_t gfp)
> *
> * Returns 0, or -ENOMEM if memory can't be allocated.
> */
> -static int lsm_cred_alloc(struct cred *cred, gfp_t gfp)
> +int lsm_cred_alloc(struct cred *cred, gfp_t gfp)
> {
> return lsm_blob_alloc(&cred->security, blob_sizes.lbs_cred, gfp);
> }
>
> -/**
> - * lsm_early_cred - during initialization allocate a composite cred blob
> - * @cred: the cred that needs a blob
> - *
> - * Allocate the cred blob for all the modules
> - */
> -static void __init lsm_early_cred(struct cred *cred)
> -{
> - int rc = lsm_cred_alloc(cred, GFP_KERNEL);
> -
> - if (rc)
> - panic("%s: Early cred alloc failed.\n", __func__);
> -}
> -
> -/**
> - * lsm_file_alloc - allocate a composite file blob
> - * @file: the file that needs a blob
> - *
> - * Allocate the file blob for all the modules
> - *
> - * Returns 0, or -ENOMEM if memory can't be allocated.
> - */
> -static int lsm_file_alloc(struct file *file)
> -{
> - if (!lsm_file_cache) {
> - file->f_security = NULL;
> - return 0;
> - }
> -
> - file->f_security = kmem_cache_zalloc(lsm_file_cache, GFP_KERNEL);
> - if (file->f_security == NULL)
> - return -ENOMEM;
> - return 0;
> -}
> -
> /**
> * lsm_inode_alloc - allocate a composite inode blob
> * @inode: the inode that needs a blob
> @@ -743,7 +234,7 @@ static int lsm_inode_alloc(struct inode *inode, gfp_t gfp)
> *
> * Returns 0, or -ENOMEM if memory can't be allocated.
> */
> -static int lsm_task_alloc(struct task_struct *task)
> +int lsm_task_alloc(struct task_struct *task)
> {
> return lsm_blob_alloc(&task->security, blob_sizes.lbs_task, GFP_KERNEL);
> }
> @@ -812,20 +303,6 @@ static int lsm_bdev_alloc(struct block_device *bdev)
> return 0;
> }
>
> -/**
> - * lsm_early_task - during initialization allocate a composite task blob
> - * @task: the task that needs a blob
> - *
> - * Allocate the task blob for all the modules
> - */
> -static void __init lsm_early_task(struct task_struct *task)
> -{
> - int rc = lsm_task_alloc(task);
> -
> - if (rc)
> - panic("%s: Early task alloc failed.\n", __func__);
> -}
> -
> /**
> * lsm_superblock_alloc - allocate a composite superblock blob
> * @sb: the superblock that needs a blob
More information about the Linux-security-module-archive
mailing list