Message ID | 20250409185019.238841-33-paul@paul-moore.com (mailing list archive) |
---|---|
State | New |
Headers | show |
Series | Rework the LSM initialization | expand |
On Wed, Apr 09, 2025 at 02:49:47PM -0400, 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. No behavioral changes, but this allows for the section arrays to avoid being exposed in a common header. I think the reorganization it worth it for this alone! :) > > Signed-off-by: Paul Moore <paul@paul-moore.com> Reviewed-by: Kees Cook <kees@kernel.org>
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@paul-moore.com> Reviewed-by: John Johansen <john.johansen@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
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
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@paul-moore.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