From 67a4b6a89b99aff0883114e4ecba4b11aedc29a5 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Thu, 6 Feb 2025 16:44:10 -0500 Subject: lsm: split the init code out into lsm_init.c 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. Reviewed-by: Kees Cook Reviewed-by: John Johansen Reviewed-by: Casey Schaufler Reviewed-by: Mimi Zohar Signed-off-by: Paul Moore --- security/lsm_init.c | 543 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 543 insertions(+) create mode 100644 security/lsm_init.c (limited to 'security/lsm_init.c') diff --git a/security/lsm_init.c b/security/lsm_init.c new file mode 100644 index 000000000000..124213b906af --- /dev/null +++ b/security/lsm_init.c @@ -0,0 +1,543 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * LSM initialization functions + */ + +#define pr_fmt(fmt) "LSM: " fmt + +#include +#include + +#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); + lsm_set_blob_size(&needed->lbs_bpf_map, &blob_sizes.lbs_bpf_map); + lsm_set_blob_size(&needed->lbs_bpf_prog, &blob_sizes.lbs_bpf_prog); + lsm_set_blob_size(&needed->lbs_bpf_token, &blob_sizes.lbs_bpf_token); +} + +/* 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); + init_debug("bpf map blob size = %d\n", blob_sizes.lbs_bpf_map); + init_debug("bpf prog blob size = %d\n", blob_sizes.lbs_bpf_prog); + init_debug("bpf token blob size = %d\n", blob_sizes.lbs_bpf_token); + + /* + * 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; +} -- cgit From e02578561d47567be26e603c6d27c10a5aa4c2c4 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Tue, 11 Feb 2025 12:19:47 -0500 Subject: lsm: consolidate lsm_allowed() and prepare_lsm() into lsm_prepare() Simplify and consolidate the lsm_allowed() and prepare_lsm() functions into a new function, lsm_prepare(). Reviewed-by: Casey Schaufler Reviewed-by: John Johansen Reviewed-by: Mimi Zohar Signed-off-by: Paul Moore --- security/lsm_init.c | 105 ++++++++++++++++++++++------------------------------ 1 file changed, 44 insertions(+), 61 deletions(-) (limited to 'security/lsm_init.c') diff --git a/security/lsm_init.c b/security/lsm_init.c index 124213b906af..6f40ab1d2f54 100644 --- a/security/lsm_init.c +++ b/security/lsm_init.c @@ -123,22 +123,6 @@ static void __init append_ordered_lsm(struct lsm_info *lsm, const char *from) 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; @@ -151,54 +135,53 @@ static void __init lsm_set_blob_size(int *need, int *lbs) *need = offset; } -static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed) +/** + * lsm_prepare - Prepare the LSM framework for a new LSM + * @lsm: LSM definition + */ +static void __init lsm_prepare(struct lsm_info *lsm) { - if (!needed) + struct lsm_blob_sizes *blobs; + + if (!is_enabled(lsm)) { + set_enabled(lsm, false); return; + } else if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && exclusive) { + init_debug("exclusive disabled: %s\n", lsm->name); + set_enabled(lsm, false); + 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) + /* Mark the LSM as enabled. */ + set_enabled(lsm, true); + if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && !exclusive) { + init_debug("exclusive chosen: %s\n", lsm->name); + exclusive = lsm; + } + + /* Register the LSM blob sizes. */ + blobs = lsm->blobs; + lsm_set_blob_size(&blobs->lbs_cred, &blob_sizes.lbs_cred); + lsm_set_blob_size(&blobs->lbs_file, &blob_sizes.lbs_file); + lsm_set_blob_size(&blobs->lbs_ib, &blob_sizes.lbs_ib); + /* inode blob gets an rcu_head in addition to LSM blobs. */ + if (blobs->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, + lsm_set_blob_size(&blobs->lbs_inode, &blob_sizes.lbs_inode); + lsm_set_blob_size(&blobs->lbs_ipc, &blob_sizes.lbs_ipc); + lsm_set_blob_size(&blobs->lbs_key, &blob_sizes.lbs_key); + lsm_set_blob_size(&blobs->lbs_msg_msg, &blob_sizes.lbs_msg_msg); + lsm_set_blob_size(&blobs->lbs_perf_event, &blob_sizes.lbs_perf_event); + lsm_set_blob_size(&blobs->lbs_sock, &blob_sizes.lbs_sock); + lsm_set_blob_size(&blobs->lbs_superblock, &blob_sizes.lbs_superblock); + lsm_set_blob_size(&blobs->lbs_task, &blob_sizes.lbs_task); + lsm_set_blob_size(&blobs->lbs_tun_dev, &blob_sizes.lbs_tun_dev); + lsm_set_blob_size(&blobs->lbs_xattr_count, &blob_sizes.lbs_xattr_count); - lsm_set_blob_size(&needed->lbs_bdev, &blob_sizes.lbs_bdev); - lsm_set_blob_size(&needed->lbs_bpf_map, &blob_sizes.lbs_bpf_map); - lsm_set_blob_size(&needed->lbs_bpf_prog, &blob_sizes.lbs_bpf_prog); - lsm_set_blob_size(&needed->lbs_bpf_token, &blob_sizes.lbs_bpf_token); -} - -/* 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); - } + lsm_set_blob_size(&blobs->lbs_bdev, &blob_sizes.lbs_bdev); + lsm_set_blob_size(&blobs->lbs_bpf_map, &blob_sizes.lbs_bpf_map); + lsm_set_blob_size(&blobs->lbs_bpf_prog, &blob_sizes.lbs_bpf_prog); + lsm_set_blob_size(&blobs->lbs_bpf_token, &blob_sizes.lbs_bpf_token); } /* Initialize a given LSM, if it is enabled. */ @@ -361,7 +344,7 @@ static void __init ordered_lsm_init(void) ordered_lsm_parse(builtin_lsm_order, "builtin"); for (lsm = ordered_lsms; *lsm; lsm++) - prepare_lsm(*lsm); + lsm_prepare(*lsm); report_lsm_order(); @@ -505,7 +488,7 @@ int __init early_security_init(void) for (lsm = __start_early_lsm_info; lsm < __end_early_lsm_info; lsm++) { if (!lsm->enabled) lsm->enabled = &lsm_enabled_true; - prepare_lsm(lsm); + lsm_prepare(lsm); initialize_lsm(lsm); } -- cgit From 37f788f65528611f4482e2135d11ca34afb25828 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Wed, 9 Apr 2025 17:59:42 -0400 Subject: lsm: introduce looping macros for the initialization code There are three common for loop patterns in the LSM initialization code to loop through the ordered LSM list and the registered "early" LSMs. This patch implements these loop patterns as macros to help simplify the code and reduce the chance for errors. Reviewed-by: Casey Schaufler Reviewed-by: John Johansen Reviewed-by: Mimi Zohar Signed-off-by: Paul Moore --- security/lsm_init.c | 42 +++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 15 deletions(-) (limited to 'security/lsm_init.c') diff --git a/security/lsm_init.c b/security/lsm_init.c index 6f40ab1d2f54..18828a65c364 100644 --- a/security/lsm_init.c +++ b/security/lsm_init.c @@ -32,6 +32,15 @@ static __initdata bool debug; pr_info(__VA_ARGS__); \ } while (0) +#define lsm_order_for_each(iter) \ + for ((iter) = ordered_lsms; *(iter); (iter)++) +#define lsm_for_each_raw(iter) \ + for ((iter) = __start_lsm_info; \ + (iter) < __end_lsm_info; (iter)++) +#define lsm_early_for_each_raw(iter) \ + for ((iter) = __start_early_lsm_info; \ + (iter) < __end_early_lsm_info; (iter)++) + static int lsm_append(const char *new, char **result); /* Save user chosen LSM */ @@ -96,9 +105,10 @@ static bool __init exists_ordered_lsm(struct lsm_info *lsm) { struct lsm_info **check; - for (check = ordered_lsms; *check; check++) + lsm_order_for_each(check) { if (*check == lsm) return true; + } return false; } @@ -209,7 +219,7 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) char *sep, *name, *next; /* LSM_ORDER_FIRST is always first. */ - for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) { + lsm_for_each_raw(lsm) { if (lsm->order == LSM_ORDER_FIRST) append_ordered_lsm(lsm, " first"); } @@ -224,8 +234,7 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) * if the selected one was separately disabled: disable * all non-matching Legacy Major LSMs. */ - for (major = __start_lsm_info; major < __end_lsm_info; - major++) { + lsm_for_each_raw(major) { if ((major->flags & LSM_FLAG_LEGACY_MAJOR) && strcmp(major->name, chosen_major_lsm) != 0) { set_enabled(major, false); @@ -241,7 +250,7 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) while ((name = strsep(&next, ",")) != NULL) { bool found = false; - for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) { + lsm_for_each_raw(lsm) { if (strcmp(lsm->name, name) == 0) { if (lsm->order == LSM_ORDER_MUTABLE) append_ordered_lsm(lsm, origin); @@ -256,7 +265,7 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) /* Process "security=", if given. */ if (chosen_major_lsm) { - for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) { + lsm_for_each_raw(lsm) { if (exists_ordered_lsm(lsm)) continue; if (strcmp(lsm->name, chosen_major_lsm) == 0) @@ -265,13 +274,13 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) } /* LSM_ORDER_LAST is always last. */ - for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) { + lsm_for_each_raw(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++) { + lsm_for_each_raw(lsm) { if (exists_ordered_lsm(lsm)) continue; set_enabled(lsm, false); @@ -290,13 +299,14 @@ static void __init report_lsm_order(void) pr_info("initializing lsm="); /* Report each enabled LSM name, comma separated. */ - for (early = __start_early_lsm_info; - early < __end_early_lsm_info; early++) + lsm_early_for_each_raw(early) { if (is_enabled(early)) pr_cont("%s%s", first++ == 0 ? "" : ",", early->name); - for (lsm = ordered_lsms; *lsm; lsm++) + } + lsm_order_for_each(lsm) { if (is_enabled(*lsm)) pr_cont("%s%s", first++ == 0 ? "" : ",", (*lsm)->name); + } pr_cont("\n"); } @@ -343,8 +353,9 @@ static void __init ordered_lsm_init(void) } else ordered_lsm_parse(builtin_lsm_order, "builtin"); - for (lsm = ordered_lsms; *lsm; lsm++) + lsm_order_for_each(lsm) { lsm_prepare(*lsm); + } report_lsm_order(); @@ -382,8 +393,9 @@ static void __init ordered_lsm_init(void) lsm_early_cred((struct cred *) current->cred); lsm_early_task(current); - for (lsm = ordered_lsms; *lsm; lsm++) + lsm_order_for_each(lsm) { initialize_lsm(*lsm); + } } static bool match_last_lsm(const char *list, const char *lsm) @@ -485,7 +497,7 @@ int __init early_security_init(void) { struct lsm_info *lsm; - for (lsm = __start_early_lsm_info; lsm < __end_early_lsm_info; lsm++) { + lsm_early_for_each_raw(lsm) { if (!lsm->enabled) lsm->enabled = &lsm_enabled_true; lsm_prepare(lsm); @@ -512,7 +524,7 @@ int __init security_init(void) * 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++) { + lsm_early_for_each_raw(lsm) { init_debug(" early started: %s (%s)\n", lsm->name, is_enabled(lsm) ? "enabled" : "disabled"); if (lsm->enabled) -- cgit From cb1513db7a6ed82d22853608d78bbf72ad8c67c1 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Sat, 12 Jul 2025 16:10:15 -0400 Subject: lsm: integrate report_lsm_order() code into caller With only one caller of report_lsm_order(), insert the function's code directly into the caller and ger rid of report_lsm_order(). Reviewed-by: Casey Schaufler Reviewed-by: John Johansen Reviewed-by: Mimi Zohar Signed-off-by: Paul Moore --- security/lsm_init.c | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) (limited to 'security/lsm_init.c') diff --git a/security/lsm_init.c b/security/lsm_init.c index 18828a65c364..09afa7ad719e 100644 --- a/security/lsm_init.c +++ b/security/lsm_init.c @@ -291,26 +291,6 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) 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. */ - lsm_early_for_each_raw(early) { - if (is_enabled(early)) - pr_cont("%s%s", first++ == 0 ? "" : ",", early->name); - } - lsm_order_for_each(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 @@ -341,7 +321,9 @@ static void __init lsm_early_task(struct task_struct *task) static void __init ordered_lsm_init(void) { + unsigned int first = 0; struct lsm_info **lsm; + struct lsm_info *early; if (chosen_lsm_order) { if (chosen_major_lsm) { @@ -357,7 +339,16 @@ static void __init ordered_lsm_init(void) lsm_prepare(*lsm); } - report_lsm_order(); + pr_info("initializing lsm="); + lsm_early_for_each_raw(early) { + if (is_enabled(early)) + pr_cont("%s%s", first++ == 0 ? "" : ",", early->name); + } + lsm_order_for_each(lsm) { + if (is_enabled(*lsm)) + pr_cont("%s%s", first++ == 0 ? "" : ",", (*lsm)->name); + } + pr_cont("\n"); init_debug("cred blob size = %d\n", blob_sizes.lbs_cred); init_debug("file blob size = %d\n", blob_sizes.lbs_file); -- cgit From 92ed3500c9a91f43e094c9b8fb4bab9976565d74 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Sat, 12 Jul 2025 16:27:39 -0400 Subject: lsm: integrate lsm_early_cred() and lsm_early_task() into caller With only one caller of lsm_early_cred() and lsm_early_task(), insert the functions' code directly into the caller and ger rid of the two functions. Reviewed-by: Casey Schaufler Reviewed-by: John Johansen Reviewed-by: Mimi Zohar Signed-off-by: Paul Moore --- security/lsm_init.c | 35 +++++------------------------------ 1 file changed, 5 insertions(+), 30 deletions(-) (limited to 'security/lsm_init.c') diff --git a/security/lsm_init.c b/security/lsm_init.c index 09afa7ad719e..8bb473aca113 100644 --- a/security/lsm_init.c +++ b/security/lsm_init.c @@ -291,34 +291,6 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) kfree(sep); } -/** - * 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) { unsigned int first = 0; @@ -382,8 +354,11 @@ static void __init ordered_lsm_init(void) blob_sizes.lbs_inode, 0, SLAB_PANIC, NULL); - lsm_early_cred((struct cred *) current->cred); - lsm_early_task(current); + if (lsm_cred_alloc((struct cred __rcu *)current->cred, GFP_KERNEL)) + panic("%s: early cred alloc failed.\n", __func__); + if (lsm_task_alloc(current)) + panic("%s: early task alloc failed.\n", __func__); + lsm_order_for_each(lsm) { initialize_lsm(*lsm); } -- cgit From faabedcd6e88ca1f65ef45d711d2e0c7288fd551 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Tue, 11 Feb 2025 12:59:30 -0500 Subject: lsm: rename ordered_lsm_init() to lsm_init_ordered() The new name more closely fits the rest of the naming scheme in security/lsm_init.c. This patch also adds a trivial comment block to the top of the function. Reviewed-by: Casey Schaufler Reviewed-by: John Johansen Reviewed-by: Mimi Zohar Signed-off-by: Paul Moore --- security/lsm_init.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'security/lsm_init.c') diff --git a/security/lsm_init.c b/security/lsm_init.c index 8bb473aca113..9249d5f37ae9 100644 --- a/security/lsm_init.c +++ b/security/lsm_init.c @@ -291,7 +291,10 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) kfree(sep); } -static void __init ordered_lsm_init(void) +/** + * lsm_init_ordered - Initialize the ordered LSMs + */ +static void __init lsm_init_ordered(void) { unsigned int first = 0; struct lsm_info **lsm; @@ -342,9 +345,6 @@ static void __init ordered_lsm_init(void) init_debug("bpf prog blob size = %d\n", blob_sizes.lbs_bpf_prog); init_debug("bpf token blob size = %d\n", blob_sizes.lbs_bpf_token); - /* - * 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, @@ -498,7 +498,7 @@ int __init security_init(void) } /* Load LSMs in specified order. */ - ordered_lsm_init(); + lsm_init_ordered(); return 0; } -- cgit From 9f9dc69e06ecbc61e7a50b823b82a78daf130dc0 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Wed, 12 Feb 2025 14:45:06 -0500 Subject: lsm: replace the name field with a pointer to the lsm_id struct Reduce the duplication between the lsm_id struct and the DEFINE_LSM() definition by linking the lsm_id struct directly into the individual LSM's DEFINE_LSM() instance. Linking the lsm_id into the LSM definition also allows us to simplify the security_add_hooks() function by removing the code which populates the lsm_idlist[] array and moving it into the normal LSM startup code where the LSM list is parsed and the individual LSMs are enabled, making for a cleaner implementation with less overhead at boot. Reviewed-by: Kees Cook Reviewed-by: John Johansen Reviewed-by: Casey Schaufler Reviewed-by: Mimi Zohar Signed-off-by: Paul Moore --- security/lsm_init.c | 45 ++++++++++++++++++--------------------------- 1 file changed, 18 insertions(+), 27 deletions(-) (limited to 'security/lsm_init.c') diff --git a/security/lsm_init.c b/security/lsm_init.c index 9249d5f37ae9..692d61a2ea10 100644 --- a/security/lsm_init.c +++ b/security/lsm_init.c @@ -127,9 +127,10 @@ static void __init append_ordered_lsm(struct lsm_info *lsm, const char *from) /* Enable this LSM, if it is not already set. */ if (!lsm->enabled) lsm->enabled = &lsm_enabled_true; - ordered_lsms[last_lsm++] = lsm; + ordered_lsms[last_lsm] = lsm; + lsm_idlist[last_lsm++] = lsm->id; - init_debug("%s ordered: %s (%s)\n", from, lsm->name, + init_debug("%s ordered: %s (%s)\n", from, lsm->id->name, is_enabled(lsm) ? "enabled" : "disabled"); } @@ -157,7 +158,7 @@ static void __init lsm_prepare(struct lsm_info *lsm) set_enabled(lsm, false); return; } else if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && exclusive) { - init_debug("exclusive disabled: %s\n", lsm->name); + init_debug("exclusive disabled: %s\n", lsm->id->name); set_enabled(lsm, false); return; } @@ -165,7 +166,7 @@ static void __init lsm_prepare(struct lsm_info *lsm) /* Mark the LSM as enabled. */ set_enabled(lsm, true); if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && !exclusive) { - init_debug("exclusive chosen: %s\n", lsm->name); + init_debug("exclusive chosen: %s\n", lsm->id->name); exclusive = lsm; } @@ -200,9 +201,9 @@ static void __init initialize_lsm(struct lsm_info *lsm) if (is_enabled(lsm)) { int ret; - init_debug("initializing %s\n", lsm->name); + init_debug("initializing %s\n", lsm->id->name); ret = lsm->init(); - WARN(ret, "%s failed to initialize: %d\n", lsm->name, ret); + WARN(ret, "%s failed to initialize: %d\n", lsm->id->name, ret); } } @@ -236,10 +237,10 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) */ lsm_for_each_raw(major) { if ((major->flags & LSM_FLAG_LEGACY_MAJOR) && - strcmp(major->name, chosen_major_lsm) != 0) { + strcmp(major->id->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); + chosen_major_lsm, major->id->name); } } } @@ -251,7 +252,7 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) bool found = false; lsm_for_each_raw(lsm) { - if (strcmp(lsm->name, name) == 0) { + if (strcmp(lsm->id->name, name) == 0) { if (lsm->order == LSM_ORDER_MUTABLE) append_ordered_lsm(lsm, origin); found = true; @@ -268,7 +269,7 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) lsm_for_each_raw(lsm) { if (exists_ordered_lsm(lsm)) continue; - if (strcmp(lsm->name, chosen_major_lsm) == 0) + if (strcmp(lsm->id->name, chosen_major_lsm) == 0) append_ordered_lsm(lsm, "security="); } } @@ -285,7 +286,7 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) continue; set_enabled(lsm, false); init_debug("%s skipped: %s (not in requested order)\n", - origin, lsm->name); + origin, lsm->id->name); } kfree(sep); @@ -317,11 +318,13 @@ static void __init lsm_init_ordered(void) pr_info("initializing lsm="); lsm_early_for_each_raw(early) { if (is_enabled(early)) - pr_cont("%s%s", first++ == 0 ? "" : ",", early->name); + pr_cont("%s%s", + first++ == 0 ? "" : ",", early->id->name); } lsm_order_for_each(lsm) { if (is_enabled(*lsm)) - pr_cont("%s%s", first++ == 0 ? "" : ",", (*lsm)->name); + pr_cont("%s%s", + first++ == 0 ? "" : ",", (*lsm)->id->name); } pr_cont("\n"); @@ -432,18 +435,6 @@ void __init security_add_hooks(struct security_hook_list *hooks, int count, { 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]); @@ -491,10 +482,10 @@ int __init security_init(void) * available */ lsm_early_for_each_raw(lsm) { - init_debug(" early started: %s (%s)\n", lsm->name, + init_debug(" early started: %s (%s)\n", lsm->id->name, is_enabled(lsm) ? "enabled" : "disabled"); if (lsm->enabled) - lsm_append(lsm->name, &lsm_names); + lsm_append(lsm->id->name, &lsm_names); } /* Load LSMs in specified order. */ -- cgit From 592b104f9b516b2c22cb23a2f4c34486fdb21bae Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Tue, 11 Feb 2025 17:13:26 -0500 Subject: lsm: rename the lsm order variables for consistency Rename the builtin_lsm_order variable to lsm_order_builtin, chosen_lsm_order to lsm_order_cmdline, chosen_major_lsm to lsm_order_legacy, ordered_lsms[] to lsm_order[], and exclusive to lsm_exclusive. This patch also renames the associated kernel command line parsing functions and adds some basic function comment blocks. The parsing function choose_major_lsm() was renamed to lsm_choose_security(), choose_lsm_order() to lsm_choose_lsm(), and enable_debug() to lsm_debug_enable(). Reviewed-by: Kees Cook Reviewed-by: John Johansen Reviewed-by: Casey Schaufler Reviewed-by: Mimi Zohar Signed-off-by: Paul Moore --- security/lsm_init.c | 86 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 48 insertions(+), 38 deletions(-) (limited to 'security/lsm_init.c') diff --git a/security/lsm_init.c b/security/lsm_init.c index 692d61a2ea10..a0785ca081c7 100644 --- a/security/lsm_init.c +++ b/security/lsm_init.c @@ -16,14 +16,14 @@ char *lsm_names; 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; +/* Build and boot-time LSM ordering. */ +static __initconst const char *const lsm_order_builtin = CONFIG_LSM; +static __initdata const char *lsm_order_cmdline; +static __initdata const char *lsm_order_legacy; /* 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 struct lsm_info *lsm_order[MAX_LSM_COUNT + 1]; +static __initdata struct lsm_info *lsm_exclusive; static __initdata bool debug; #define init_debug(...) \ @@ -33,7 +33,7 @@ static __initdata bool debug; } while (0) #define lsm_order_for_each(iter) \ - for ((iter) = ordered_lsms; *(iter); (iter)++) + for ((iter) = lsm_order; *(iter); (iter)++) #define lsm_for_each_raw(iter) \ for ((iter) = __start_lsm_info; \ (iter) < __end_lsm_info; (iter)++) @@ -41,31 +41,41 @@ static __initdata bool debug; for ((iter) = __start_early_lsm_info; \ (iter) < __end_early_lsm_info; (iter)++) -static int lsm_append(const char *new, char **result); - -/* Save user chosen LSM */ -static int __init choose_major_lsm(char *str) +/** + * lsm_choose_security - Legacy "major" LSM selection + * @str: kernel command line parameter + */ +static int __init lsm_choose_security(char *str) { - chosen_major_lsm = str; + lsm_order_legacy = str; return 1; } -__setup("security=", choose_major_lsm); +__setup("security=", lsm_choose_security); -/* Explicitly choose LSM initialization order. */ -static int __init choose_lsm_order(char *str) +/** + * lsm_choose_lsm - Modern LSM selection + * @str: kernel command line parameter + */ +static int __init lsm_choose_lsm(char *str) { - chosen_lsm_order = str; + lsm_order_cmdline = str; return 1; } -__setup("lsm=", choose_lsm_order); +__setup("lsm=", lsm_choose_lsm); -/* Enable LSM order debugging. */ -static int __init enable_debug(char *str) +/** + * lsm_debug_enable - Enable LSM framework debugging + * @str: kernel command line parameter + * + * Currently we only provide debug info during LSM initialization, but we may + * want to expand this in the future. + */ +static int __init lsm_debug_enable(char *str) { debug = true; return 1; } -__setup("lsm.debug", enable_debug); +__setup("lsm.debug", lsm_debug_enable); /* Mark an LSM's enabled flag. */ static int lsm_enabled_true __initdata = 1; @@ -127,7 +137,7 @@ static void __init append_ordered_lsm(struct lsm_info *lsm, const char *from) /* Enable this LSM, if it is not already set. */ if (!lsm->enabled) lsm->enabled = &lsm_enabled_true; - ordered_lsms[last_lsm] = lsm; + lsm_order[last_lsm] = lsm; lsm_idlist[last_lsm++] = lsm->id; init_debug("%s ordered: %s (%s)\n", from, lsm->id->name, @@ -157,7 +167,7 @@ static void __init lsm_prepare(struct lsm_info *lsm) if (!is_enabled(lsm)) { set_enabled(lsm, false); return; - } else if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && exclusive) { + } else if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && lsm_exclusive) { init_debug("exclusive disabled: %s\n", lsm->id->name); set_enabled(lsm, false); return; @@ -165,9 +175,9 @@ static void __init lsm_prepare(struct lsm_info *lsm) /* Mark the LSM as enabled. */ set_enabled(lsm, true); - if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && !exclusive) { + if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && !lsm_exclusive) { init_debug("exclusive chosen: %s\n", lsm->id->name); - exclusive = lsm; + lsm_exclusive = lsm; } /* Register the LSM blob sizes. */ @@ -226,7 +236,7 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) } /* Process "security=", if given. */ - if (chosen_major_lsm) { + if (lsm_order_legacy) { struct lsm_info *major; /* @@ -237,10 +247,10 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) */ lsm_for_each_raw(major) { if ((major->flags & LSM_FLAG_LEGACY_MAJOR) && - strcmp(major->id->name, chosen_major_lsm) != 0) { + strcmp(major->id->name, lsm_order_legacy) != 0) { set_enabled(major, false); init_debug("security=%s disabled: %s (only one legacy major LSM)\n", - chosen_major_lsm, major->id->name); + lsm_order_legacy, major->id->name); } } } @@ -265,11 +275,11 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) } /* Process "security=", if given. */ - if (chosen_major_lsm) { + if (lsm_order_legacy) { lsm_for_each_raw(lsm) { if (exists_ordered_lsm(lsm)) continue; - if (strcmp(lsm->id->name, chosen_major_lsm) == 0) + if (strcmp(lsm->id->name, lsm_order_legacy) == 0) append_ordered_lsm(lsm, "security="); } } @@ -301,15 +311,15 @@ static void __init lsm_init_ordered(void) struct lsm_info **lsm; struct lsm_info *early; - if (chosen_lsm_order) { - if (chosen_major_lsm) { + if (lsm_order_cmdline) { + if (lsm_order_legacy) { pr_warn("security=%s is ignored because it is superseded by lsm=%s\n", - chosen_major_lsm, chosen_lsm_order); - chosen_major_lsm = NULL; + lsm_order_legacy, lsm_order_cmdline); + lsm_order_legacy = NULL; } - ordered_lsm_parse(chosen_lsm_order, "cmdline"); + ordered_lsm_parse(lsm_order_cmdline, "cmdline"); } else - ordered_lsm_parse(builtin_lsm_order, "builtin"); + ordered_lsm_parse(lsm_order_builtin, "builtin"); lsm_order_for_each(lsm) { lsm_prepare(*lsm); @@ -473,9 +483,9 @@ 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*"); + init_debug("legacy security=%s\n", lsm_order_legacy ? : " *unspecified*"); + init_debug(" CONFIG_LSM=%s\n", lsm_order_builtin); + init_debug("boot arg lsm=%s\n", lsm_order_cmdline ? : " *unspecified*"); /* * Append the names of the early LSM modules now that kmalloc() is -- cgit From 250898ca335f337bc032a9693dc0a30a1cb85825 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Wed, 12 Feb 2025 15:36:51 -0500 Subject: lsm: rework lsm_active_cnt and lsm_idlist[] Move the LSM active count and lsm_id list declarations out of a header that is visible across the kernel and into a header that is limited to the LSM framework. This not only helps keep the include/linux headers smaller and cleaner, it helps prevent misuse of these variables. Reviewed-by: Casey Schaufler Reviewed-by: John Johansen Reviewed-by: Mimi Zohar Signed-off-by: Paul Moore --- security/lsm_init.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'security/lsm_init.c') diff --git a/security/lsm_init.c b/security/lsm_init.c index a0785ca081c7..d40f31e79bd5 100644 --- a/security/lsm_init.c +++ b/security/lsm_init.c @@ -217,12 +217,6 @@ static void __init initialize_lsm(struct lsm_info *lsm) } } -/* - * 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) { -- cgit From 935d508d4d7ab9d19c603bd7eb2937249551d507 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Thu, 13 Feb 2025 17:34:12 -0500 Subject: lsm: get rid of the lsm_names list and do some cleanup The LSM currently has a lot of code to maintain a list of the currently active LSMs in a human readable string, with the only user being the "/sys/kernel/security/lsm" code. Let's drop all of that code and generate the string on first use and then cache it for subsequent use. Signed-off-by: Paul Moore --- security/lsm_init.c | 49 ------------------------------------------------- 1 file changed, 49 deletions(-) (limited to 'security/lsm_init.c') diff --git a/security/lsm_init.c b/security/lsm_init.c index d40f31e79bd5..574fff354d3f 100644 --- a/security/lsm_init.c +++ b/security/lsm_init.c @@ -10,8 +10,6 @@ #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[]; @@ -371,42 +369,6 @@ static void __init lsm_init_ordered(void) } } -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; @@ -443,15 +405,6 @@ void __init security_add_hooks(struct security_hook_list *hooks, int count, 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) @@ -488,8 +441,6 @@ int __init security_init(void) lsm_early_for_each_raw(lsm) { init_debug(" early started: %s (%s)\n", lsm->id->name, is_enabled(lsm) ? "enabled" : "disabled"); - if (lsm->enabled) - lsm_append(lsm->id->name, &lsm_names); } /* Load LSMs in specified order. */ -- cgit From 2d67172612fd9df2c4d08533515ef483cb526dd9 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Thu, 10 Apr 2025 22:04:26 -0400 Subject: lsm: rework the LSM enable/disable setter/getter functions In addition to style changes, rename set_enabled() to lsm_enabled_set() and is_enabled() to lsm_is_enabled() to better fit within the LSM initialization code. Reviewed-by: Casey Schaufler Reviewed-by: John Johansen Reviewed-by: Mimi Zohar Signed-off-by: Paul Moore --- security/lsm_init.c | 62 ++++++++++++++++++++++++++--------------------------- 1 file changed, 31 insertions(+), 31 deletions(-) (limited to 'security/lsm_init.c') diff --git a/security/lsm_init.c b/security/lsm_init.c index 574fff354d3f..9bfc35b44b63 100644 --- a/security/lsm_init.c +++ b/security/lsm_init.c @@ -10,6 +10,10 @@ #include "lsm.h" +/* LSM enabled constants. */ +static __initdata int lsm_enabled_true = 1; +static __initdata int lsm_enabled_false = 0; + /* 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[]; @@ -75,37 +79,33 @@ static int __init lsm_debug_enable(char *str) } __setup("lsm.debug", lsm_debug_enable); -/* 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) +/** + * lsm_enabled_set - Mark a LSM as enabled + * @lsm: LSM definition + * @enabled: enabled flag + */ +static void __init lsm_enabled_set(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; + if (!lsm->enabled || + lsm->enabled == &lsm_enabled_true || + lsm->enabled == &lsm_enabled_false) { + lsm->enabled = enabled ? &lsm_enabled_true : &lsm_enabled_false; } else { *lsm->enabled = enabled; } } -static inline bool is_enabled(struct lsm_info *lsm) +/** + * lsm_is_enabled - Determine if a LSM is enabled + * @lsm: LSM definition + */ +static inline bool lsm_is_enabled(struct lsm_info *lsm) { - if (!lsm->enabled) - return false; - - return *lsm->enabled; + return (lsm->enabled ? *lsm->enabled : false); } /* Is an LSM already listed in the ordered LSMs list? */ @@ -139,7 +139,7 @@ static void __init append_ordered_lsm(struct lsm_info *lsm, const char *from) lsm_idlist[last_lsm++] = lsm->id; init_debug("%s ordered: %s (%s)\n", from, lsm->id->name, - is_enabled(lsm) ? "enabled" : "disabled"); + lsm_is_enabled(lsm) ? "enabled" : "disabled"); } static void __init lsm_set_blob_size(int *need, int *lbs) @@ -162,17 +162,17 @@ static void __init lsm_prepare(struct lsm_info *lsm) { struct lsm_blob_sizes *blobs; - if (!is_enabled(lsm)) { - set_enabled(lsm, false); + if (!lsm_is_enabled(lsm)) { + lsm_enabled_set(lsm, false); return; } else if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && lsm_exclusive) { init_debug("exclusive disabled: %s\n", lsm->id->name); - set_enabled(lsm, false); + lsm_enabled_set(lsm, false); return; } /* Mark the LSM as enabled. */ - set_enabled(lsm, true); + lsm_enabled_set(lsm, true); if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && !lsm_exclusive) { init_debug("exclusive chosen: %s\n", lsm->id->name); lsm_exclusive = lsm; @@ -206,7 +206,7 @@ static void __init lsm_prepare(struct lsm_info *lsm) /* Initialize a given LSM, if it is enabled. */ static void __init initialize_lsm(struct lsm_info *lsm) { - if (is_enabled(lsm)) { + if (lsm_is_enabled(lsm)) { int ret; init_debug("initializing %s\n", lsm->id->name); @@ -240,7 +240,7 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) lsm_for_each_raw(major) { if ((major->flags & LSM_FLAG_LEGACY_MAJOR) && strcmp(major->id->name, lsm_order_legacy) != 0) { - set_enabled(major, false); + lsm_enabled_set(major, false); init_debug("security=%s disabled: %s (only one legacy major LSM)\n", lsm_order_legacy, major->id->name); } @@ -286,7 +286,7 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) lsm_for_each_raw(lsm) { if (exists_ordered_lsm(lsm)) continue; - set_enabled(lsm, false); + lsm_enabled_set(lsm, false); init_debug("%s skipped: %s (not in requested order)\n", origin, lsm->id->name); } @@ -319,12 +319,12 @@ static void __init lsm_init_ordered(void) pr_info("initializing lsm="); lsm_early_for_each_raw(early) { - if (is_enabled(early)) + if (lsm_is_enabled(early)) pr_cont("%s%s", first++ == 0 ? "" : ",", early->id->name); } lsm_order_for_each(lsm) { - if (is_enabled(*lsm)) + if (lsm_is_enabled(*lsm)) pr_cont("%s%s", first++ == 0 ? "" : ",", (*lsm)->id->name); } @@ -440,7 +440,7 @@ int __init security_init(void) */ lsm_early_for_each_raw(lsm) { init_debug(" early started: %s (%s)\n", lsm->id->name, - is_enabled(lsm) ? "enabled" : "disabled"); + lsm_is_enabled(lsm) ? "enabled" : "disabled"); } /* Load LSMs in specified order. */ -- cgit From a748372a282ae1e23d5d4b14a3e190c28764cfd2 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Sun, 13 Jul 2025 17:37:56 -0400 Subject: lsm: rename exists_ordered_lsm() to lsm_order_exists() Also add a header comment block to the function. Reviewed-by: Casey Schaufler Reviewed-by: John Johansen Reviewed-by: Mimi Zohar Signed-off-by: Paul Moore --- security/lsm_init.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'security/lsm_init.c') diff --git a/security/lsm_init.c b/security/lsm_init.c index 9bfc35b44b63..4b66a8cfe8eb 100644 --- a/security/lsm_init.c +++ b/security/lsm_init.c @@ -108,8 +108,11 @@ static inline bool lsm_is_enabled(struct lsm_info *lsm) return (lsm->enabled ? *lsm->enabled : false); } -/* Is an LSM already listed in the ordered LSMs list? */ -static bool __init exists_ordered_lsm(struct lsm_info *lsm) +/** + * lsm_order_exists - Determine if a LSM exists in the ordered list + * @lsm: LSM definition + */ +static bool __init lsm_order_exists(struct lsm_info *lsm) { struct lsm_info **check; @@ -126,7 +129,7 @@ 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)) + if (lsm_order_exists(lsm)) return; if (WARN(last_lsm == MAX_LSM_COUNT, "%s: out of LSM static calls!?\n", from)) @@ -269,7 +272,7 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) /* Process "security=", if given. */ if (lsm_order_legacy) { lsm_for_each_raw(lsm) { - if (exists_ordered_lsm(lsm)) + if (lsm_order_exists(lsm)) continue; if (strcmp(lsm->id->name, lsm_order_legacy) == 0) append_ordered_lsm(lsm, "security="); @@ -284,7 +287,7 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) /* Disable all LSMs not in the ordered list. */ lsm_for_each_raw(lsm) { - if (exists_ordered_lsm(lsm)) + if (lsm_order_exists(lsm)) continue; lsm_enabled_set(lsm, false); init_debug("%s skipped: %s (not in requested order)\n", -- cgit From 24a9c58978ee368cbd796a03cb6e8ade6e0b6f5f Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Wed, 16 Jul 2025 15:04:10 -0400 Subject: lsm: rename/rework append_ordered_lsm() into lsm_order_append() Rename append_ordered_lsm() to lsm_order_append() to better match convention and do some rework. The rework includes moving the LSM_FLAG_EXCLUSIVE logic from lsm_prepare() to lsm_order_append() in order to consolidate the individual LSM append/activation code, and adding logic to skip appending explicitly disabled LSMs to the active LSM list. Reviewed-by: Casey Schaufler Reviewed-by: John Johansen Signed-off-by: Paul Moore --- security/lsm_init.c | 76 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 43 insertions(+), 33 deletions(-) (limited to 'security/lsm_init.c') diff --git a/security/lsm_init.c b/security/lsm_init.c index 4b66a8cfe8eb..1881cd28f0a0 100644 --- a/security/lsm_init.c +++ b/security/lsm_init.c @@ -124,24 +124,48 @@ static bool __init lsm_order_exists(struct lsm_info *lsm) 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) +/** + * lsm_order_append - Append a LSM to the ordered list + * @lsm: LSM definition + * @src: source of the addition + * + * Append @lsm to the enabled LSM array after ensuring that it hasn't been + * explicitly disabled, is a duplicate entry, or would run afoul of the + * LSM_FLAG_EXCLUSIVE logic. + */ +static void __init lsm_order_append(struct lsm_info *lsm, const char *src) { /* Ignore duplicate selections. */ if (lsm_order_exists(lsm)) return; - if (WARN(last_lsm == MAX_LSM_COUNT, "%s: out of LSM static calls!?\n", from)) - return; + /* Skip explicitly disabled LSMs. */ + if (lsm->enabled && !lsm_is_enabled(lsm)) + goto out; - /* Enable this LSM, if it is not already set. */ - if (!lsm->enabled) - lsm->enabled = &lsm_enabled_true; - lsm_order[last_lsm] = lsm; - lsm_idlist[last_lsm++] = lsm->id; + if (WARN(lsm_active_cnt == MAX_LSM_COUNT, + "%s: out of LSM static calls!?\n", src)) { + lsm_enabled_set(lsm, false); + goto out; + } + + if (lsm->flags & LSM_FLAG_EXCLUSIVE) { + if (lsm_exclusive) { + init_debug("exclusive disabled: %s\n", lsm->id->name); + lsm_enabled_set(lsm, false); + goto out; + } else { + init_debug("exclusive chosen: %s\n", lsm->id->name); + lsm_exclusive = lsm; + } + } - init_debug("%s ordered: %s (%s)\n", from, lsm->id->name, + lsm_enabled_set(lsm, true); + lsm_order[lsm_active_cnt] = lsm; + lsm_idlist[lsm_active_cnt++] = lsm->id; + +out: + init_debug("%s ordered: %s (%s)\n", src, lsm->id->name, lsm_is_enabled(lsm) ? "enabled" : "disabled"); } @@ -163,26 +187,12 @@ static void __init lsm_set_blob_size(int *need, int *lbs) */ static void __init lsm_prepare(struct lsm_info *lsm) { - struct lsm_blob_sizes *blobs; + struct lsm_blob_sizes *blobs = lsm->blobs; - if (!lsm_is_enabled(lsm)) { - lsm_enabled_set(lsm, false); - return; - } else if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && lsm_exclusive) { - init_debug("exclusive disabled: %s\n", lsm->id->name); - lsm_enabled_set(lsm, false); + if (!blobs) return; - } - - /* Mark the LSM as enabled. */ - lsm_enabled_set(lsm, true); - if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && !lsm_exclusive) { - init_debug("exclusive chosen: %s\n", lsm->id->name); - lsm_exclusive = lsm; - } /* Register the LSM blob sizes. */ - blobs = lsm->blobs; lsm_set_blob_size(&blobs->lbs_cred, &blob_sizes.lbs_cred); lsm_set_blob_size(&blobs->lbs_file, &blob_sizes.lbs_file); lsm_set_blob_size(&blobs->lbs_ib, &blob_sizes.lbs_ib); @@ -227,7 +237,7 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) /* LSM_ORDER_FIRST is always first. */ lsm_for_each_raw(lsm) { if (lsm->order == LSM_ORDER_FIRST) - append_ordered_lsm(lsm, " first"); + lsm_order_append(lsm, " first"); } /* Process "security=", if given. */ @@ -259,7 +269,7 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) lsm_for_each_raw(lsm) { if (strcmp(lsm->id->name, name) == 0) { if (lsm->order == LSM_ORDER_MUTABLE) - append_ordered_lsm(lsm, origin); + lsm_order_append(lsm, origin); found = true; } } @@ -275,14 +285,14 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) if (lsm_order_exists(lsm)) continue; if (strcmp(lsm->id->name, lsm_order_legacy) == 0) - append_ordered_lsm(lsm, "security="); + lsm_order_append(lsm, "security="); } } /* LSM_ORDER_LAST is always last. */ lsm_for_each_raw(lsm) { if (lsm->order == LSM_ORDER_LAST) - append_ordered_lsm(lsm, " last"); + lsm_order_append(lsm, " last"); } /* Disable all LSMs not in the ordered list. */ @@ -415,8 +425,8 @@ int __init early_security_init(void) struct lsm_info *lsm; lsm_early_for_each_raw(lsm) { - if (!lsm->enabled) - lsm->enabled = &lsm_enabled_true; + lsm_enabled_set(lsm, true); + lsm_order_append(lsm, "early"); lsm_prepare(lsm); initialize_lsm(lsm); } -- cgit From 752db06571816a3870b17814882425318b5ec0ef Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Sun, 13 Jul 2025 17:51:12 -0400 Subject: lsm: rename/rework ordered_lsm_parse() to lsm_order_parse() Rename ordered_lsm_parse() to lsm_order_parse() for the sake of consistency with the other LSM initialization routines, and also do some minor rework of the function. Aside from some minor style decisions, the majority of the rework involved shuffling the order of the LSM_FLAG_LEGACY and LSM_ORDER_FIRST code so that the LSM_FLAG_LEGACY checks are handled first; it is important to note that this doesn't affect the order in which the LSMs are registered. Reviewed-by: Casey Schaufler Reviewed-by: John Johansen Signed-off-by: Paul Moore --- security/lsm_init.c | 82 ++++++++++++++++++++++++----------------------------- 1 file changed, 37 insertions(+), 45 deletions(-) (limited to 'security/lsm_init.c') diff --git a/security/lsm_init.c b/security/lsm_init.c index 1881cd28f0a0..f0066857bd1a 100644 --- a/security/lsm_init.c +++ b/security/lsm_init.c @@ -228,83 +228,75 @@ static void __init initialize_lsm(struct lsm_info *lsm) } } -/* Populate ordered LSMs list from comma-separated LSM name list. */ -static void __init ordered_lsm_parse(const char *order, const char *origin) +/** + * lsm_order_parse - Parse the comma delimited LSM list + * @list: LSM list + * @src: source of the list + */ +static void __init lsm_order_parse(const char *list, const char *src) { struct lsm_info *lsm; char *sep, *name, *next; - /* LSM_ORDER_FIRST is always first. */ - lsm_for_each_raw(lsm) { - if (lsm->order == LSM_ORDER_FIRST) - lsm_order_append(lsm, " first"); - } - - /* Process "security=", if given. */ + /* Handle any Legacy LSM exclusions if one was specified. */ if (lsm_order_legacy) { - 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. + * 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. */ - lsm_for_each_raw(major) { - if ((major->flags & LSM_FLAG_LEGACY_MAJOR) && - strcmp(major->id->name, lsm_order_legacy) != 0) { - lsm_enabled_set(major, false); + lsm_for_each_raw(lsm) { + if ((lsm->flags & LSM_FLAG_LEGACY_MAJOR) && + strcmp(lsm->id->name, lsm_order_legacy)) { + lsm_enabled_set(lsm, false); init_debug("security=%s disabled: %s (only one legacy major LSM)\n", - lsm_order_legacy, major->id->name); + lsm_order_legacy, lsm->id->name); } } } - sep = kstrdup(order, GFP_KERNEL); + /* LSM_ORDER_FIRST */ + lsm_for_each_raw(lsm) { + if (lsm->order == LSM_ORDER_FIRST) + lsm_order_append(lsm, "first"); + } + + /* Normal or "mutable" LSMs */ + sep = kstrdup(list, GFP_KERNEL); next = sep; /* Walk the list, looking for matching LSMs. */ while ((name = strsep(&next, ",")) != NULL) { - bool found = false; - lsm_for_each_raw(lsm) { - if (strcmp(lsm->id->name, name) == 0) { - if (lsm->order == LSM_ORDER_MUTABLE) - lsm_order_append(lsm, origin); - found = true; - } + if (!strcmp(lsm->id->name, name) && + lsm->order == LSM_ORDER_MUTABLE) + lsm_order_append(lsm, src); } - - if (!found) - init_debug("%s ignored: %s (not built into kernel)\n", - origin, name); } + kfree(sep); - /* Process "security=", if given. */ + /* Legacy LSM if specified. */ if (lsm_order_legacy) { lsm_for_each_raw(lsm) { - if (lsm_order_exists(lsm)) - continue; - if (strcmp(lsm->id->name, lsm_order_legacy) == 0) - lsm_order_append(lsm, "security="); + if (!strcmp(lsm->id->name, lsm_order_legacy)) + lsm_order_append(lsm, src); } } - /* LSM_ORDER_LAST is always last. */ + /* LSM_ORDER_LAST */ lsm_for_each_raw(lsm) { if (lsm->order == LSM_ORDER_LAST) - lsm_order_append(lsm, " last"); + lsm_order_append(lsm, "last"); } - /* Disable all LSMs not in the ordered list. */ + /* Disable all LSMs not previously enabled. */ lsm_for_each_raw(lsm) { if (lsm_order_exists(lsm)) continue; lsm_enabled_set(lsm, false); init_debug("%s skipped: %s (not in requested order)\n", - origin, lsm->id->name); + src, lsm->id->name); } - - kfree(sep); } /** @@ -322,9 +314,9 @@ static void __init lsm_init_ordered(void) lsm_order_legacy, lsm_order_cmdline); lsm_order_legacy = NULL; } - ordered_lsm_parse(lsm_order_cmdline, "cmdline"); + lsm_order_parse(lsm_order_cmdline, "cmdline"); } else - ordered_lsm_parse(lsm_order_builtin, "builtin"); + lsm_order_parse(lsm_order_builtin, "builtin"); lsm_order_for_each(lsm) { lsm_prepare(*lsm); -- cgit From 291271e691740003021cf5b48fa7cf7e3371eaa7 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Tue, 11 Feb 2025 17:49:11 -0500 Subject: lsm: cleanup the LSM blob size code Convert the lsm_blob_size fields to unsigned integers as there is no current need for them to be negative, change "lsm_set_blob_size()" to "lsm_blob_size_update()" to better reflect reality, and perform some other minor cleanups to the associated code. Reviewed-by: Kees Cook Reviewed-by: John Johansen Reviewed-by: Casey Schaufler Reviewed-by: Mimi Zohar Signed-off-by: Paul Moore --- security/lsm_init.c | 57 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 24 deletions(-) (limited to 'security/lsm_init.c') diff --git a/security/lsm_init.c b/security/lsm_init.c index f0066857bd1a..6b1f8f18a43c 100644 --- a/security/lsm_init.c +++ b/security/lsm_init.c @@ -169,16 +169,22 @@ out: lsm_is_enabled(lsm) ? "enabled" : "disabled"); } -static void __init lsm_set_blob_size(int *need, int *lbs) +/** + * lsm_blob_size_update - Update the LSM blob size and offset information + * @sz_req: the requested additional blob size + * @sz_cur: the existing blob size + */ +static void __init lsm_blob_size_update(unsigned int *sz_req, + unsigned int *sz_cur) { - int offset; + unsigned int offset; - if (*need <= 0) + if (*sz_req == 0) return; - offset = ALIGN(*lbs, sizeof(void *)); - *lbs = offset + *need; - *need = offset; + offset = ALIGN(*sz_cur, sizeof(void *)); + *sz_cur = offset + *sz_req; + *sz_req = offset; } /** @@ -193,27 +199,30 @@ static void __init lsm_prepare(struct lsm_info *lsm) return; /* Register the LSM blob sizes. */ - lsm_set_blob_size(&blobs->lbs_cred, &blob_sizes.lbs_cred); - lsm_set_blob_size(&blobs->lbs_file, &blob_sizes.lbs_file); - lsm_set_blob_size(&blobs->lbs_ib, &blob_sizes.lbs_ib); + blobs = lsm->blobs; + lsm_blob_size_update(&blobs->lbs_cred, &blob_sizes.lbs_cred); + lsm_blob_size_update(&blobs->lbs_file, &blob_sizes.lbs_file); + lsm_blob_size_update(&blobs->lbs_ib, &blob_sizes.lbs_ib); /* inode blob gets an rcu_head in addition to LSM blobs. */ if (blobs->lbs_inode && blob_sizes.lbs_inode == 0) blob_sizes.lbs_inode = sizeof(struct rcu_head); - lsm_set_blob_size(&blobs->lbs_inode, &blob_sizes.lbs_inode); - lsm_set_blob_size(&blobs->lbs_ipc, &blob_sizes.lbs_ipc); - lsm_set_blob_size(&blobs->lbs_key, &blob_sizes.lbs_key); - lsm_set_blob_size(&blobs->lbs_msg_msg, &blob_sizes.lbs_msg_msg); - lsm_set_blob_size(&blobs->lbs_perf_event, &blob_sizes.lbs_perf_event); - lsm_set_blob_size(&blobs->lbs_sock, &blob_sizes.lbs_sock); - lsm_set_blob_size(&blobs->lbs_superblock, &blob_sizes.lbs_superblock); - lsm_set_blob_size(&blobs->lbs_task, &blob_sizes.lbs_task); - lsm_set_blob_size(&blobs->lbs_tun_dev, &blob_sizes.lbs_tun_dev); - lsm_set_blob_size(&blobs->lbs_xattr_count, - &blob_sizes.lbs_xattr_count); - lsm_set_blob_size(&blobs->lbs_bdev, &blob_sizes.lbs_bdev); - lsm_set_blob_size(&blobs->lbs_bpf_map, &blob_sizes.lbs_bpf_map); - lsm_set_blob_size(&blobs->lbs_bpf_prog, &blob_sizes.lbs_bpf_prog); - lsm_set_blob_size(&blobs->lbs_bpf_token, &blob_sizes.lbs_bpf_token); + lsm_blob_size_update(&blobs->lbs_inode, &blob_sizes.lbs_inode); + lsm_blob_size_update(&blobs->lbs_ipc, &blob_sizes.lbs_ipc); + lsm_blob_size_update(&blobs->lbs_key, &blob_sizes.lbs_key); + lsm_blob_size_update(&blobs->lbs_msg_msg, &blob_sizes.lbs_msg_msg); + lsm_blob_size_update(&blobs->lbs_perf_event, + &blob_sizes.lbs_perf_event); + lsm_blob_size_update(&blobs->lbs_sock, &blob_sizes.lbs_sock); + lsm_blob_size_update(&blobs->lbs_superblock, + &blob_sizes.lbs_superblock); + lsm_blob_size_update(&blobs->lbs_task, &blob_sizes.lbs_task); + lsm_blob_size_update(&blobs->lbs_tun_dev, &blob_sizes.lbs_tun_dev); + lsm_blob_size_update(&blobs->lbs_xattr_count, + &blob_sizes.lbs_xattr_count); + lsm_blob_size_update(&blobs->lbs_bdev, &blob_sizes.lbs_bdev); + lsm_blob_size_update(&blobs->lbs_bpf_map, &blob_sizes.lbs_bpf_map); + lsm_blob_size_update(&blobs->lbs_bpf_prog, &blob_sizes.lbs_bpf_prog); + lsm_blob_size_update(&blobs->lbs_bpf_token, &blob_sizes.lbs_bpf_token); } /* Initialize a given LSM, if it is enabled. */ -- cgit From 27be5600fe852c52d5b70f4ac9406879b39c864e Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Tue, 11 Feb 2025 18:24:04 -0500 Subject: lsm: cleanup initialize_lsm() and rename to lsm_init_single() Rename initialize_lsm() to be more consistent with the rest of the LSM initialization changes and rework the function itself to better fit with the "exit on fail" coding pattern. Reviewed-by: Kees Cook Reviewed-by: John Johansen Reviewed-by: Casey Schaufler Reviewed-by: Mimi Zohar Signed-off-by: Paul Moore --- security/lsm_init.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) (limited to 'security/lsm_init.c') diff --git a/security/lsm_init.c b/security/lsm_init.c index 6b1f8f18a43c..eb473f982ddb 100644 --- a/security/lsm_init.c +++ b/security/lsm_init.c @@ -169,6 +169,7 @@ out: lsm_is_enabled(lsm) ? "enabled" : "disabled"); } + /** * lsm_blob_size_update - Update the LSM blob size and offset information * @sz_req: the requested additional blob size @@ -225,16 +226,20 @@ static void __init lsm_prepare(struct lsm_info *lsm) lsm_blob_size_update(&blobs->lbs_bpf_token, &blob_sizes.lbs_bpf_token); } -/* Initialize a given LSM, if it is enabled. */ -static void __init initialize_lsm(struct lsm_info *lsm) +/** + * lsm_init_single - Initialize a given LSM + * @lsm: LSM definition + */ +static void __init lsm_init_single(struct lsm_info *lsm) { - if (lsm_is_enabled(lsm)) { - int ret; + int ret; - init_debug("initializing %s\n", lsm->id->name); - ret = lsm->init(); - WARN(ret, "%s failed to initialize: %d\n", lsm->id->name, ret); - } + if (!lsm_is_enabled(lsm)) + return; + + init_debug("initializing %s\n", lsm->id->name); + ret = lsm->init(); + WARN(ret, "%s failed to initialize: %d\n", lsm->id->name, ret); } /** @@ -379,7 +384,7 @@ static void __init lsm_init_ordered(void) panic("%s: early task alloc failed.\n", __func__); lsm_order_for_each(lsm) { - initialize_lsm(*lsm); + lsm_init_single(*lsm); } } @@ -429,7 +434,7 @@ int __init early_security_init(void) lsm_enabled_set(lsm, true); lsm_order_append(lsm, "early"); lsm_prepare(lsm); - initialize_lsm(lsm); + lsm_init_single(lsm); } return 0; -- cgit From 45a41d1394aa2ed0305f0560f93bb87be7192481 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Wed, 12 Feb 2025 18:10:37 -0500 Subject: lsm: fold lsm_init_ordered() into security_init() With only security_init() calling lsm_init_ordered, it makes little sense to keep lsm_init_ordered() as a standalone function. Fold lsm_init_ordered() into security_init(). Reviewed-by: Casey Schaufler Reviewed-by: John Johansen Signed-off-by: Paul Moore --- security/lsm_init.c | 155 ++++++++++++++++++++++++---------------------------- 1 file changed, 71 insertions(+), 84 deletions(-) (limited to 'security/lsm_init.c') diff --git a/security/lsm_init.c b/security/lsm_init.c index eb473f982ddb..9cbb10c42e38 100644 --- a/security/lsm_init.c +++ b/security/lsm_init.c @@ -18,6 +18,9 @@ static __initdata int lsm_enabled_false = 0; extern struct lsm_info __start_lsm_info[], __end_lsm_info[]; extern struct lsm_info __start_early_lsm_info[], __end_early_lsm_info[]; +/* Number of "early" LSMs */ +static __initdata unsigned int lsm_count_early; + /* Build and boot-time LSM ordering. */ static __initconst const char *const lsm_order_builtin = CONFIG_LSM; static __initdata const char *lsm_order_cmdline; @@ -169,7 +172,6 @@ out: lsm_is_enabled(lsm) ? "enabled" : "disabled"); } - /** * lsm_blob_size_update - Update the LSM blob size and offset information * @sz_req: the requested additional blob size @@ -313,14 +315,74 @@ static void __init lsm_order_parse(const char *list, const char *src) } } +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__); +} + /** - * lsm_init_ordered - Initialize the ordered LSMs + * 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. */ -static void __init lsm_init_ordered(void) +void __init security_add_hooks(struct security_hook_list *hooks, int count, + const struct lsm_id *lsmid) { - unsigned int first = 0; + int i; + + for (i = 0; i < count; i++) { + hooks[i].lsmid = lsmid; + lsm_static_call_init(&hooks[i]); + } +} + +int __init early_security_init(void) +{ + struct lsm_info *lsm; + + lsm_early_for_each_raw(lsm) { + lsm_enabled_set(lsm, true); + lsm_order_append(lsm, "early"); + lsm_prepare(lsm); + lsm_init_single(lsm); + lsm_count_early++; + } + + return 0; +} + +/** + * security_init - Initializes the LSM framework + * + * This should be called early in the kernel initialization sequence. + */ +int __init security_init(void) +{ + unsigned int cnt; struct lsm_info **lsm; struct lsm_info *early; + unsigned int first = 0; + + init_debug("legacy security=%s\n", lsm_order_legacy ? : " *unspecified*"); + init_debug(" CONFIG_LSM=%s\n", lsm_order_builtin); + init_debug("boot arg lsm=%s\n", lsm_order_cmdline ? : " *unspecified*"); if (lsm_order_cmdline) { if (lsm_order_legacy) { @@ -332,9 +394,8 @@ static void __init lsm_init_ordered(void) } else lsm_order_parse(lsm_order_builtin, "builtin"); - lsm_order_for_each(lsm) { + lsm_order_for_each(lsm) lsm_prepare(*lsm); - } pr_info("initializing lsm="); lsm_early_for_each_raw(early) { @@ -383,87 +444,13 @@ static void __init lsm_init_ordered(void) if (lsm_task_alloc(current)) panic("%s: early task alloc failed.\n", __func__); + cnt = 0; lsm_order_for_each(lsm) { + /* skip the "early" LSMs as they have already been setup */ + if (cnt++ < lsm_count_early) + continue; lsm_init_single(*lsm); } -} - -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; - - for (i = 0; i < count; i++) { - hooks[i].lsmid = lsmid; - lsm_static_call_init(&hooks[i]); - } -} - -int __init early_security_init(void) -{ - struct lsm_info *lsm; - - lsm_early_for_each_raw(lsm) { - lsm_enabled_set(lsm, true); - lsm_order_append(lsm, "early"); - lsm_prepare(lsm); - lsm_init_single(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", lsm_order_legacy ? : " *unspecified*"); - init_debug(" CONFIG_LSM=%s\n", lsm_order_builtin); - init_debug("boot arg lsm=%s\n", lsm_order_cmdline ? : " *unspecified*"); - - /* - * Append the names of the early LSM modules now that kmalloc() is - * available - */ - lsm_early_for_each_raw(lsm) { - init_debug(" early started: %s (%s)\n", lsm->id->name, - lsm_is_enabled(lsm) ? "enabled" : "disabled"); - } - - /* Load LSMs in specified order. */ - lsm_init_ordered(); return 0; } -- cgit From 450705334f698990804b470437f3014cee979486 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Wed, 12 Feb 2025 18:17:03 -0500 Subject: lsm: add/tweak function header comment blocks in lsm_init.c Add function header comments for lsm_static_call_init() and early_security_init(), tweak the existing comment block for security_add_hooks(). Reviewed-by: Casey Schaufler Reviewed-by: John Johansen Signed-off-by: Paul Moore --- security/lsm_init.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'security/lsm_init.c') diff --git a/security/lsm_init.c b/security/lsm_init.c index 9cbb10c42e38..bf861081d592 100644 --- a/security/lsm_init.c +++ b/security/lsm_init.c @@ -315,6 +315,10 @@ static void __init lsm_order_parse(const char *list, const char *src) } } +/** + * lsm_static_call_init - Initialize a LSM's static calls + * @hl: LSM hook list + */ static void __init lsm_static_call_init(struct security_hook_list *hl) { struct lsm_static_call *scall = hl->scalls; @@ -335,12 +339,12 @@ static void __init lsm_static_call_init(struct security_hook_list *hl) } /** - * 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 + * security_add_hooks - Add a LSM's hooks to the LSM framework's hook lists + * @hooks: LSM hooks to add + * @count: number of hooks to add + * @lsmid: identification information for the LSM * - * Each LSM has to register its hooks with the infrastructure. + * Each LSM has to register its hooks with the LSM framework. */ void __init security_add_hooks(struct security_hook_list *hooks, int count, const struct lsm_id *lsmid) @@ -353,6 +357,9 @@ void __init security_add_hooks(struct security_hook_list *hooks, int count, } } +/** + * early_security_init - Initialize the early LSMs + */ int __init early_security_init(void) { struct lsm_info *lsm; -- cgit From 5137e583ba2635b82667dc63cb35305750420411 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Wed, 12 Feb 2025 18:20:01 -0500 Subject: lsm: cleanup the debug and console output in lsm_init.c Move away from an init specific init_debug() macro to a more general lsm_pr()/lsm_pr_cont()/lsm_pr_dbg() set of macros that are available both before and after init. In the process we do a number of minor changes to improve the LSM initialization output and cleanup the code somewhat. Reviewed-by: Casey Schaufler Reviewed-by: John Johansen Signed-off-by: Paul Moore --- security/lsm_init.c | 123 ++++++++++++++++++++++------------------------------ 1 file changed, 53 insertions(+), 70 deletions(-) (limited to 'security/lsm_init.c') diff --git a/security/lsm_init.c b/security/lsm_init.c index bf861081d592..cd1779e03497 100644 --- a/security/lsm_init.c +++ b/security/lsm_init.c @@ -30,13 +30,6 @@ static __initdata const char *lsm_order_legacy; static __initdata struct lsm_info *lsm_order[MAX_LSM_COUNT + 1]; static __initdata struct lsm_info *lsm_exclusive; -static __initdata bool debug; -#define init_debug(...) \ - do { \ - if (debug) \ - pr_info(__VA_ARGS__); \ - } while (0) - #define lsm_order_for_each(iter) \ for ((iter) = lsm_order; *(iter); (iter)++) #define lsm_for_each_raw(iter) \ @@ -77,7 +70,7 @@ __setup("lsm=", lsm_choose_lsm); */ static int __init lsm_debug_enable(char *str) { - debug = true; + lsm_debug = true; return 1; } __setup("lsm.debug", lsm_debug_enable); @@ -143,22 +136,28 @@ static void __init lsm_order_append(struct lsm_info *lsm, const char *src) return; /* Skip explicitly disabled LSMs. */ - if (lsm->enabled && !lsm_is_enabled(lsm)) - goto out; + if (lsm->enabled && !lsm_is_enabled(lsm)) { + lsm_pr_dbg("skip previously disabled LSM %s:%s\n", + src, lsm->id->name); + return; + } - if (WARN(lsm_active_cnt == MAX_LSM_COUNT, - "%s: out of LSM static calls!?\n", src)) { + if (lsm_active_cnt == MAX_LSM_COUNT) { + pr_warn("exceeded maximum LSM count on %s:%s\n", + src, lsm->id->name); lsm_enabled_set(lsm, false); - goto out; + return; } if (lsm->flags & LSM_FLAG_EXCLUSIVE) { if (lsm_exclusive) { - init_debug("exclusive disabled: %s\n", lsm->id->name); + lsm_pr_dbg("skip exclusive LSM conflict %s:%s\n", + src, lsm->id->name); lsm_enabled_set(lsm, false); - goto out; + return; } else { - init_debug("exclusive chosen: %s\n", lsm->id->name); + lsm_pr_dbg("select exclusive LSM %s:%s\n", + src, lsm->id->name); lsm_exclusive = lsm; } } @@ -167,9 +166,7 @@ static void __init lsm_order_append(struct lsm_info *lsm, const char *src) lsm_order[lsm_active_cnt] = lsm; lsm_idlist[lsm_active_cnt++] = lsm->id; -out: - init_debug("%s ordered: %s (%s)\n", src, lsm->id->name, - lsm_is_enabled(lsm) ? "enabled" : "disabled"); + lsm_pr_dbg("enabling LSM %s:%s\n", src, lsm->id->name); } /** @@ -239,7 +236,7 @@ static void __init lsm_init_single(struct lsm_info *lsm) if (!lsm_is_enabled(lsm)) return; - init_debug("initializing %s\n", lsm->id->name); + lsm_pr_dbg("initializing %s\n", lsm->id->name); ret = lsm->init(); WARN(ret, "%s failed to initialize: %d\n", lsm->id->name, ret); } @@ -266,8 +263,8 @@ static void __init lsm_order_parse(const char *list, const char *src) if ((lsm->flags & LSM_FLAG_LEGACY_MAJOR) && strcmp(lsm->id->name, lsm_order_legacy)) { lsm_enabled_set(lsm, false); - init_debug("security=%s disabled: %s (only one legacy major LSM)\n", - lsm_order_legacy, lsm->id->name); + lsm_pr_dbg("skip legacy LSM conflict %s:%s\n", + src, lsm->id->name); } } } @@ -310,8 +307,7 @@ static void __init lsm_order_parse(const char *list, const char *src) if (lsm_order_exists(lsm)) continue; lsm_enabled_set(lsm, false); - init_debug("%s skipped: %s (not in requested order)\n", - src, lsm->id->name); + lsm_pr_dbg("skip disabled LSM %s:%s\n", src, lsm->id->name); } } @@ -319,7 +315,7 @@ static void __init lsm_order_parse(const char *list, const char *src) * lsm_static_call_init - Initialize a LSM's static calls * @hl: LSM hook list */ -static void __init lsm_static_call_init(struct security_hook_list *hl) +static int __init lsm_static_call_init(struct security_hook_list *hl) { struct lsm_static_call *scall = hl->scalls; int i; @@ -331,11 +327,12 @@ static void __init lsm_static_call_init(struct security_hook_list *hl) hl->hook.lsm_func_addr); scall->hl = hl; static_branch_enable(scall->active); - return; + return 0; } scall++; } - panic("%s - Ran out of static slots.\n", __func__); + + return -ENOSPC; } /** @@ -353,7 +350,9 @@ void __init security_add_hooks(struct security_hook_list *hooks, int count, for (i = 0; i < count; i++) { hooks[i].lsmid = lsmid; - lsm_static_call_init(&hooks[i]); + if (lsm_static_call_init(&hooks[i])) + panic("exhausted LSM callback slots with LSM %s\n", + lsmid->name); } } @@ -384,19 +383,16 @@ int __init security_init(void) { unsigned int cnt; struct lsm_info **lsm; - struct lsm_info *early; - unsigned int first = 0; - init_debug("legacy security=%s\n", lsm_order_legacy ? : " *unspecified*"); - init_debug(" CONFIG_LSM=%s\n", lsm_order_builtin); - init_debug("boot arg lsm=%s\n", lsm_order_cmdline ? : " *unspecified*"); + if (lsm_debug) { + lsm_pr("built-in LSM list: %s\n", lsm_order_builtin); + lsm_pr("legacy LSM parameter: %s\n", lsm_order_legacy); + lsm_pr("boot LSM parameter: %s\n", lsm_order_cmdline); + } if (lsm_order_cmdline) { - if (lsm_order_legacy) { - pr_warn("security=%s is ignored because it is superseded by lsm=%s\n", - lsm_order_legacy, lsm_order_cmdline); + if (lsm_order_legacy) lsm_order_legacy = NULL; - } lsm_order_parse(lsm_order_cmdline, "cmdline"); } else lsm_order_parse(lsm_order_builtin, "builtin"); @@ -404,38 +400,25 @@ int __init security_init(void) lsm_order_for_each(lsm) lsm_prepare(*lsm); - pr_info("initializing lsm="); - lsm_early_for_each_raw(early) { - if (lsm_is_enabled(early)) - pr_cont("%s%s", - first++ == 0 ? "" : ",", early->id->name); - } - lsm_order_for_each(lsm) { - if (lsm_is_enabled(*lsm)) - pr_cont("%s%s", - first++ == 0 ? "" : ",", (*lsm)->id->name); + if (lsm_debug) { + lsm_pr("blob(cred) size %d\n", blob_sizes.lbs_cred); + lsm_pr("blob(file) size %d\n", blob_sizes.lbs_file); + lsm_pr("blob(ib) size %d\n", blob_sizes.lbs_ib); + lsm_pr("blob(inode) size %d\n", blob_sizes.lbs_inode); + lsm_pr("blob(ipc) size %d\n", blob_sizes.lbs_ipc); + lsm_pr("blob(key) size %d\n", blob_sizes.lbs_key); + lsm_pr("blob(msg_msg)_size %d\n", blob_sizes.lbs_msg_msg); + lsm_pr("blob(sock) size %d\n", blob_sizes.lbs_sock); + lsm_pr("blob(superblock) size %d\n", blob_sizes.lbs_superblock); + lsm_pr("blob(perf_event) size %d\n", blob_sizes.lbs_perf_event); + lsm_pr("blob(task) size %d\n", blob_sizes.lbs_task); + lsm_pr("blob(tun_dev) size %d\n", blob_sizes.lbs_tun_dev); + lsm_pr("blob(xattr) count %d\n", blob_sizes.lbs_xattr_count); + lsm_pr("blob(bdev) size %d\n", blob_sizes.lbs_bdev); + lsm_pr("blob(bpf_map) size %d\n", blob_sizes.lbs_bpf_map); + lsm_pr("blob(bpf_prog) size %d\n", blob_sizes.lbs_bpf_prog); + lsm_pr("blob(bpf_token) size %d\n", blob_sizes.lbs_bpf_token); } - pr_cont("\n"); - - 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); - init_debug("bpf map blob size = %d\n", blob_sizes.lbs_bpf_map); - init_debug("bpf prog blob size = %d\n", blob_sizes.lbs_bpf_prog); - init_debug("bpf token blob size = %d\n", blob_sizes.lbs_bpf_token); if (blob_sizes.lbs_file) lsm_file_cache = kmem_cache_create("lsm_file_cache", @@ -447,9 +430,9 @@ int __init security_init(void) SLAB_PANIC, NULL); if (lsm_cred_alloc((struct cred __rcu *)current->cred, GFP_KERNEL)) - panic("%s: early cred alloc failed.\n", __func__); + panic("early LSM cred alloc failed\n"); if (lsm_task_alloc(current)) - panic("%s: early task alloc failed.\n", __func__); + panic("early LSM task alloc failed\n"); cnt = 0; lsm_order_for_each(lsm) { -- cgit From ac3c47cece27014e34d2ec561d72c0a7c7de50a9 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Wed, 19 Mar 2025 16:38:20 -0400 Subject: lsm: output available LSMs when debugging This will display all of the LSMs built into the kernel, regardless of if they are enabled or not. Reviewed-by: Casey Schaufler Reviewed-by: John Johansen Reviewed-by: Mimi Zohar Signed-off-by: Paul Moore --- security/lsm_init.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) (limited to 'security/lsm_init.c') diff --git a/security/lsm_init.c b/security/lsm_init.c index cd1779e03497..cfcf3bf7127f 100644 --- a/security/lsm_init.c +++ b/security/lsm_init.c @@ -363,6 +363,8 @@ int __init early_security_init(void) { struct lsm_info *lsm; + /* NOTE: lsm_pr_dbg() doesn't work here as lsm_debug is not yet set */ + lsm_early_for_each_raw(lsm) { lsm_enabled_set(lsm, true); lsm_order_append(lsm, "early"); @@ -385,9 +387,24 @@ int __init security_init(void) struct lsm_info **lsm; if (lsm_debug) { - lsm_pr("built-in LSM list: %s\n", lsm_order_builtin); + struct lsm_info *i; + + cnt = 0; + lsm_pr("available LSMs: "); + lsm_early_for_each_raw(i) + lsm_pr_cont("%s%s(E)", (cnt++ ? "," : ""), i->id->name); + lsm_for_each_raw(i) + lsm_pr_cont("%s%s", (cnt++ ? "," : ""), i->id->name); + lsm_pr_cont("\n"); + + lsm_pr("built-in LSM config: %s\n", lsm_order_builtin); + lsm_pr("legacy LSM parameter: %s\n", lsm_order_legacy); lsm_pr("boot LSM parameter: %s\n", lsm_order_cmdline); + + /* see the note about lsm_pr_dbg() in early_security_init() */ + lsm_early_for_each_raw(i) + lsm_pr("enabled LSM early:%s\n", i->id->name); } if (lsm_order_cmdline) { -- cgit From 3423c6397ce21356c3c2fac0b2727d428d96cfa4 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Mon, 21 Jul 2025 16:36:03 -0400 Subject: lsm: group lsm_order_parse() with the other lsm_order_*() functions Move the lsm_order_parse() function near the other lsm_order_*() functions to improve readability. No code changes. Reviewed-by: Casey Schaufler Reviewed-by: John Johansen Reviewed-by: Mimi Zohar Signed-off-by: Paul Moore --- security/lsm_init.c | 140 ++++++++++++++++++++++++++-------------------------- 1 file changed, 70 insertions(+), 70 deletions(-) (limited to 'security/lsm_init.c') diff --git a/security/lsm_init.c b/security/lsm_init.c index cfcf3bf7127f..fd69bde9112e 100644 --- a/security/lsm_init.c +++ b/security/lsm_init.c @@ -169,6 +169,76 @@ static void __init lsm_order_append(struct lsm_info *lsm, const char *src) lsm_pr_dbg("enabling LSM %s:%s\n", src, lsm->id->name); } +/** + * lsm_order_parse - Parse the comma delimited LSM list + * @list: LSM list + * @src: source of the list + */ +static void __init lsm_order_parse(const char *list, const char *src) +{ + struct lsm_info *lsm; + char *sep, *name, *next; + + /* Handle any Legacy LSM exclusions if one was specified. */ + if (lsm_order_legacy) { + /* + * 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. + */ + lsm_for_each_raw(lsm) { + if ((lsm->flags & LSM_FLAG_LEGACY_MAJOR) && + strcmp(lsm->id->name, lsm_order_legacy)) { + lsm_enabled_set(lsm, false); + lsm_pr_dbg("skip legacy LSM conflict %s:%s\n", + src, lsm->id->name); + } + } + } + + /* LSM_ORDER_FIRST */ + lsm_for_each_raw(lsm) { + if (lsm->order == LSM_ORDER_FIRST) + lsm_order_append(lsm, "first"); + } + + /* Normal or "mutable" LSMs */ + sep = kstrdup(list, GFP_KERNEL); + next = sep; + /* Walk the list, looking for matching LSMs. */ + while ((name = strsep(&next, ",")) != NULL) { + lsm_for_each_raw(lsm) { + if (!strcmp(lsm->id->name, name) && + lsm->order == LSM_ORDER_MUTABLE) + lsm_order_append(lsm, src); + } + } + kfree(sep); + + /* Legacy LSM if specified. */ + if (lsm_order_legacy) { + lsm_for_each_raw(lsm) { + if (!strcmp(lsm->id->name, lsm_order_legacy)) + lsm_order_append(lsm, src); + } + } + + /* LSM_ORDER_LAST */ + lsm_for_each_raw(lsm) { + if (lsm->order == LSM_ORDER_LAST) + lsm_order_append(lsm, "last"); + } + + /* Disable all LSMs not previously enabled. */ + lsm_for_each_raw(lsm) { + if (lsm_order_exists(lsm)) + continue; + lsm_enabled_set(lsm, false); + lsm_pr_dbg("skip disabled LSM %s:%s\n", src, lsm->id->name); + } +} + /** * lsm_blob_size_update - Update the LSM blob size and offset information * @sz_req: the requested additional blob size @@ -241,76 +311,6 @@ static void __init lsm_init_single(struct lsm_info *lsm) WARN(ret, "%s failed to initialize: %d\n", lsm->id->name, ret); } -/** - * lsm_order_parse - Parse the comma delimited LSM list - * @list: LSM list - * @src: source of the list - */ -static void __init lsm_order_parse(const char *list, const char *src) -{ - struct lsm_info *lsm; - char *sep, *name, *next; - - /* Handle any Legacy LSM exclusions if one was specified. */ - if (lsm_order_legacy) { - /* - * 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. - */ - lsm_for_each_raw(lsm) { - if ((lsm->flags & LSM_FLAG_LEGACY_MAJOR) && - strcmp(lsm->id->name, lsm_order_legacy)) { - lsm_enabled_set(lsm, false); - lsm_pr_dbg("skip legacy LSM conflict %s:%s\n", - src, lsm->id->name); - } - } - } - - /* LSM_ORDER_FIRST */ - lsm_for_each_raw(lsm) { - if (lsm->order == LSM_ORDER_FIRST) - lsm_order_append(lsm, "first"); - } - - /* Normal or "mutable" LSMs */ - sep = kstrdup(list, GFP_KERNEL); - next = sep; - /* Walk the list, looking for matching LSMs. */ - while ((name = strsep(&next, ",")) != NULL) { - lsm_for_each_raw(lsm) { - if (!strcmp(lsm->id->name, name) && - lsm->order == LSM_ORDER_MUTABLE) - lsm_order_append(lsm, src); - } - } - kfree(sep); - - /* Legacy LSM if specified. */ - if (lsm_order_legacy) { - lsm_for_each_raw(lsm) { - if (!strcmp(lsm->id->name, lsm_order_legacy)) - lsm_order_append(lsm, src); - } - } - - /* LSM_ORDER_LAST */ - lsm_for_each_raw(lsm) { - if (lsm->order == LSM_ORDER_LAST) - lsm_order_append(lsm, "last"); - } - - /* Disable all LSMs not previously enabled. */ - lsm_for_each_raw(lsm) { - if (lsm_order_exists(lsm)) - continue; - lsm_enabled_set(lsm, false); - lsm_pr_dbg("skip disabled LSM %s:%s\n", src, lsm->id->name); - } -} - /** * lsm_static_call_init - Initialize a LSM's static calls * @hl: LSM hook list -- cgit From cdc028812f727907d1575cf454a5f01ddffa7750 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Tue, 11 Feb 2025 12:18:35 -0500 Subject: lsm: introduce an initcall mechanism into the LSM framework Currently the individual LSMs register their own initcalls, and while this should be harmless, it can be wasteful in the case where a LSM is disabled at boot as the initcall will still be executed. This patch introduces support for managing the initcalls in the LSM framework, and future patches will convert the existing LSMs over to this new mechanism. Only initcall types which are used by the current in-tree LSMs are supported, additional initcall types can easily be added in the future if needed. Reviewed-by: Kees Cook Reviewed-by: Casey Schaufler Reviewed-by: John Johansen Reviewed-by: Mimi Zohar Signed-off-by: Paul Moore --- security/lsm_init.c | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) (limited to 'security/lsm_init.c') diff --git a/security/lsm_init.c b/security/lsm_init.c index fd69bde9112e..aacdac406ba5 100644 --- a/security/lsm_init.c +++ b/security/lsm_init.c @@ -39,6 +39,27 @@ static __initdata struct lsm_info *lsm_exclusive; for ((iter) = __start_early_lsm_info; \ (iter) < __end_early_lsm_info; (iter)++) +#define lsm_initcall(level) \ + ({ \ + int _r, _rc = 0; \ + struct lsm_info **_lp, *_l; \ + lsm_order_for_each(_lp) { \ + _l = *_lp; \ + if (!_l->initcall_##level) \ + continue; \ + lsm_pr_dbg("running %s %s initcall", \ + _l->id->name, #level); \ + _r = _l->initcall_##level(); \ + if (_r) { \ + pr_warn("failed LSM %s %s initcall with errno %d\n", \ + _l->id->name, #level, _r); \ + if (!_rc) \ + _rc = _r; \ + } \ + } \ + _rc; \ + }) + /** * lsm_choose_security - Legacy "major" LSM selection * @str: kernel command line parameter @@ -461,3 +482,71 @@ int __init security_init(void) return 0; } + +/** + * security_initcall_pure - Run the LSM pure initcalls + */ +static int __init security_initcall_pure(void) +{ + return lsm_initcall(pure); +} +pure_initcall(security_initcall_pure); + +/** + * security_initcall_early - Run the LSM early initcalls + */ +static int __init security_initcall_early(void) +{ + return lsm_initcall(early); +} +early_initcall(security_initcall_early); + +/** + * security_initcall_core - Run the LSM core initcalls + */ +static int __init security_initcall_core(void) +{ + return lsm_initcall(core); +} +core_initcall(security_initcall_core); + +/** + * security_initcall_subsys - Run the LSM subsys initcalls + */ +static int __init security_initcall_subsys(void) +{ + return lsm_initcall(subsys); +} +subsys_initcall(security_initcall_subsys); + +/** + * security_initcall_fs - Run the LSM fs initcalls + */ +static int __init security_initcall_fs(void) +{ + return lsm_initcall(fs); +} +fs_initcall(security_initcall_fs); + +/** + * security_initcall_device - Run the LSM device initcalls + */ +static int __init security_initcall_device(void) +{ + return lsm_initcall(device); +} +device_initcall(security_initcall_device); + +/** + * security_initcall_late - Run the LSM late initcalls + */ +static int __init security_initcall_late(void) +{ + int rc; + + rc = lsm_initcall(late); + lsm_pr_dbg("all enabled LSMs fully activated\n"); + + return rc; +} +late_initcall(security_initcall_late); -- cgit From 4ab5efcc2829a38a3adcfdd9cd0c0e0eb6fb6939 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Tue, 18 Feb 2025 17:25:20 -0500 Subject: lsm: consolidate all of the LSM framework initcalls The LSM framework itself registers a small number of initcalls, this patch converts these initcalls into the new initcall mechanism. Reviewed-by: Casey Schaufler Reviewed-by: John Johansen Signed-off-by: Paul Moore --- security/lsm_init.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'security/lsm_init.c') diff --git a/security/lsm_init.c b/security/lsm_init.c index aacdac406ba5..0f668bca98f9 100644 --- a/security/lsm_init.c +++ b/security/lsm_init.c @@ -488,7 +488,12 @@ int __init security_init(void) */ static int __init security_initcall_pure(void) { - return lsm_initcall(pure); + int rc_adr, rc_lsm; + + rc_adr = min_addr_init(); + rc_lsm = lsm_initcall(pure); + + return (rc_adr ? rc_adr : rc_lsm); } pure_initcall(security_initcall_pure); @@ -506,7 +511,12 @@ early_initcall(security_initcall_early); */ static int __init security_initcall_core(void) { - return lsm_initcall(core); + int rc_sfs, rc_lsm; + + rc_sfs = securityfs_init(); + rc_lsm = lsm_initcall(core); + + return (rc_sfs ? rc_sfs : rc_lsm); } core_initcall(security_initcall_core); -- cgit From dfa024bc3f67a97e1a975dd66b83af8b3845eb19 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Fri, 21 Feb 2025 11:53:29 -0500 Subject: lsm: add a LSM_STARTED_ALL notification event Add a new LSM notifier event, LSM_STARTED_ALL, which is fired once at boot when all of the LSMs have been started. Reviewed-by: Kees Cook Reviewed-by: Casey Schaufler Reviewed-by: John Johansen Signed-off-by: Paul Moore --- security/lsm_init.c | 1 + 1 file changed, 1 insertion(+) (limited to 'security/lsm_init.c') diff --git a/security/lsm_init.c b/security/lsm_init.c index 0f668bca98f9..6bb67d41ce52 100644 --- a/security/lsm_init.c +++ b/security/lsm_init.c @@ -556,6 +556,7 @@ static int __init security_initcall_late(void) rc = lsm_initcall(late); lsm_pr_dbg("all enabled LSMs fully activated\n"); + call_blocking_lsm_notifier(LSM_STARTED_ALL, NULL); return rc; } -- cgit From 9a948eefad594c42717f29824dd40d6dc0b7aa13 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Tue, 18 Nov 2025 19:18:10 -0500 Subject: lsm: use unrcu_pointer() for current->cred in security_init() We need to directly allocate the cred's LSM state for the initial task when we initialize the LSM framework. Unfortunately, this results in a RCU related type mismatch, use the unrcu_pointer() macro to handle this a bit more elegantly. The explicit type casting still remains as we need to work around the constification of current->cred in this particular case. Reviewed-by: Xiu Jianfeng Signed-off-by: Paul Moore --- security/lsm_init.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'security/lsm_init.c') diff --git a/security/lsm_init.c b/security/lsm_init.c index 6bb67d41ce52..05bd52e6b1f2 100644 --- a/security/lsm_init.c +++ b/security/lsm_init.c @@ -467,7 +467,8 @@ int __init security_init(void) blob_sizes.lbs_inode, 0, SLAB_PANIC, NULL); - if (lsm_cred_alloc((struct cred __rcu *)current->cred, GFP_KERNEL)) + if (lsm_cred_alloc((struct cred *)unrcu_pointer(current->cred), + GFP_KERNEL)) panic("early LSM cred alloc failed\n"); if (lsm_task_alloc(current)) panic("early LSM task alloc failed\n"); -- cgit