5339 lines · 152.1 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* * Security plug functions * * Copyright (C) 2001 WireX Communications, Inc <chris@wirex.com> * Copyright (C) 2001-2002 Greg Kroah-Hartman <greg@kroah.com> * Copyright (C) 2001 Networks Associates Technology, Inc <ssmalley@nai.com> * Copyright (C) 2016 Mellanox Technologies * Copyright (C) 2023 Microsoft Corporation <paul@paul-moore.com> */ #define pr_fmt(fmt) "LSM: " fmt #include <linux/bpf.h>#include <linux/capability.h>#include <linux/dcache.h>#include <linux/export.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/kernel_read_file.h>#include <linux/lsm_hooks.h>#include <linux/integrity.h>#include <linux/ima.h>#include <linux/evm.h>#include <linux/fsnotify.h>#include <linux/mman.h>#include <linux/mount.h>#include <linux/personality.h>#include <linux/backing-dev.h>#include <linux/string.h>#include <linux/msg.h>#include <net/flow.h> /* How many LSMs were built into the kernel? */#define LSM_COUNT (__end_lsm_info - __start_lsm_info) /* * These are descriptions of the reasons that can be passed to the * security_locked_down() LSM hook. Placing this array here allows * all security modules to use the same descriptions for auditing * purposes. */const char *const lockdown_reasons[LOCKDOWN_CONFIDENTIALITY_MAX + 1] = {	[LOCKDOWN_NONE] = "none",	[LOCKDOWN_MODULE_SIGNATURE] = "unsigned module loading",	[LOCKDOWN_DEV_MEM] = "/dev/mem,kmem,port",	[LOCKDOWN_EFI_TEST] = "/dev/efi_test access",	[LOCKDOWN_KEXEC] = "kexec of unsigned images",	[LOCKDOWN_HIBERNATION] = "hibernation",	[LOCKDOWN_PCI_ACCESS] = "direct PCI access",	[LOCKDOWN_IOPORT] = "raw io port access",	[LOCKDOWN_MSR] = "raw MSR access",	[LOCKDOWN_ACPI_TABLES] = "modifying ACPI tables",	[LOCKDOWN_DEVICE_TREE] = "modifying device tree contents",	[LOCKDOWN_PCMCIA_CIS] = "direct PCMCIA CIS storage",	[LOCKDOWN_TIOCSSERIAL] = "reconfiguration of serial port IO",	[LOCKDOWN_MODULE_PARAMETERS] = "unsafe module parameters",	[LOCKDOWN_MMIOTRACE] = "unsafe mmio",	[LOCKDOWN_DEBUGFS] = "debugfs access",	[LOCKDOWN_XMON_WR] = "xmon write access",	[LOCKDOWN_BPF_WRITE_USER] = "use of bpf to write user RAM",	[LOCKDOWN_DBG_WRITE_KERNEL] = "use of kgdb/kdb to write kernel RAM",	[LOCKDOWN_RTAS_ERROR_INJECTION] = "RTAS error injection",	[LOCKDOWN_INTEGRITY_MAX] = "integrity",	[LOCKDOWN_KCORE] = "/proc/kcore access",	[LOCKDOWN_KPROBES] = "use of kprobes",	[LOCKDOWN_BPF_READ_KERNEL] = "use of bpf to read kernel RAM",	[LOCKDOWN_DBG_READ_KERNEL] = "use of kgdb/kdb to read kernel RAM",	[LOCKDOWN_PERF] = "unsafe use of perf",	[LOCKDOWN_TRACEFS] = "use of tracefs",	[LOCKDOWN_XMON_RW] = "xmon read and write access",	[LOCKDOWN_XFRM_SECRET] = "xfrm SA secret",	[LOCKDOWN_CONFIDENTIALITY_MAX] = "confidentiality",}; struct security_hook_heads security_hook_heads __ro_after_init;static BLOCKING_NOTIFIER_HEAD(blocking_lsm_notifier_chain); static struct kmem_cache *lsm_file_cache;static struct kmem_cache *lsm_inode_cache; char *lsm_names;static struct lsm_blob_sizes blob_sizes __ro_after_init; /* Boot-time LSM user choice */static __initdata const char *chosen_lsm_order;static __initdata const char *chosen_major_lsm; static __initconst const char *const builtin_lsm_order = CONFIG_LSM; /* Ordered list of LSMs to initialize. */static __initdata struct lsm_info **ordered_lsms;static __initdata struct lsm_info *exclusive; static __initdata bool debug;#define init_debug(...)						\	do {							\		if (debug)					\			pr_info(__VA_ARGS__);			\	} while (0) static bool __init is_enabled(struct lsm_info *lsm){	if (!lsm->enabled)		return false; 	return *lsm->enabled;} /* Mark an LSM's enabled flag. */static int lsm_enabled_true __initdata = 1;static int lsm_enabled_false __initdata = 0;static void __init set_enabled(struct lsm_info *lsm, bool enabled){	/*	 * When an LSM hasn't configured an enable variable, we can use	 * a hard-coded location for storing the default enabled state.	 */	if (!lsm->enabled) {		if (enabled)			lsm->enabled = &lsm_enabled_true;		else			lsm->enabled = &lsm_enabled_false;	} else if (lsm->enabled == &lsm_enabled_true) {		if (!enabled)			lsm->enabled = &lsm_enabled_false;	} else if (lsm->enabled == &lsm_enabled_false) {		if (enabled)			lsm->enabled = &lsm_enabled_true;	} else {		*lsm->enabled = enabled;	}} /* Is an LSM already listed in the ordered LSMs list? */static bool __init exists_ordered_lsm(struct lsm_info *lsm){	struct lsm_info **check; 	for (check = ordered_lsms; *check; check++)		if (*check == lsm)			return true; 	return false;} /* Append an LSM to the list of ordered LSMs to initialize. */static int last_lsm __initdata;static void __init append_ordered_lsm(struct lsm_info *lsm, const char *from){	/* Ignore duplicate selections. */	if (exists_ordered_lsm(lsm))		return; 	if (WARN(last_lsm == LSM_COUNT, "%s: out of LSM slots!?\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);	/*	 * 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_msg_msg, &blob_sizes.lbs_msg_msg);	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_xattr_count,			  &blob_sizes.lbs_xattr_count);} /* 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);	}} /* Populate ordered LSMs list from comma-separated LSM name list. */static void __init ordered_lsm_parse(const char *order, const char *origin){	struct lsm_info *lsm;	char *sep, *name, *next; 	/* LSM_ORDER_FIRST is always first. */	for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {		if (lsm->order == LSM_ORDER_FIRST)			append_ordered_lsm(lsm, "  first");	} 	/* Process "security=", if given. */	if (chosen_major_lsm) {		struct lsm_info *major; 		/*		 * To match the original "security=" behavior, this		 * explicitly does NOT fallback to another Legacy Major		 * if the selected one was separately disabled: disable		 * all non-matching Legacy Major LSMs.		 */		for (major = __start_lsm_info; major < __end_lsm_info;		     major++) {			if ((major->flags & LSM_FLAG_LEGACY_MAJOR) &&			    strcmp(major->name, chosen_major_lsm) != 0) {				set_enabled(major, false);				init_debug("security=%s disabled: %s (only one legacy major LSM)\n",					   chosen_major_lsm, major->name);			}		}	} 	sep = kstrdup(order, GFP_KERNEL);	next = sep;	/* Walk the list, looking for matching LSMs. */	while ((name = strsep(&next, ",")) != NULL) {		bool found = false; 		for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {			if (strcmp(lsm->name, name) == 0) {				if (lsm->order == LSM_ORDER_MUTABLE)					append_ordered_lsm(lsm, origin);				found = true;			}		} 		if (!found)			init_debug("%s ignored: %s (not built into kernel)\n",				   origin, name);	} 	/* Process "security=", if given. */	if (chosen_major_lsm) {		for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {			if (exists_ordered_lsm(lsm))				continue;			if (strcmp(lsm->name, chosen_major_lsm) == 0)				append_ordered_lsm(lsm, "security=");		}	} 	/* LSM_ORDER_LAST is always last. */	for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {		if (lsm->order == LSM_ORDER_LAST)			append_ordered_lsm(lsm, "   last");	} 	/* Disable all LSMs not in the ordered list. */	for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {		if (exists_ordered_lsm(lsm))			continue;		set_enabled(lsm, false);		init_debug("%s skipped: %s (not in requested order)\n",			   origin, lsm->name);	} 	kfree(sep);} static void __init lsm_early_cred(struct cred *cred);static void __init lsm_early_task(struct task_struct *task); static int lsm_append(const char *new, char **result); static void __init report_lsm_order(void){	struct lsm_info **lsm, *early;	int first = 0; 	pr_info("initializing lsm="); 	/* Report each enabled LSM name, comma separated. */	for (early = __start_early_lsm_info;	     early < __end_early_lsm_info; early++)		if (is_enabled(early))			pr_cont("%s%s", first++ == 0 ? "" : ",", early->name);	for (lsm = ordered_lsms; *lsm; lsm++)		if (is_enabled(*lsm))			pr_cont("%s%s", first++ == 0 ? "" : ",", (*lsm)->name); 	pr_cont("\n");} static void __init ordered_lsm_init(void){	struct lsm_info **lsm; 	ordered_lsms = kcalloc(LSM_COUNT + 1, sizeof(*ordered_lsms),			       GFP_KERNEL); 	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("inode blob size      = %d\n", blob_sizes.lbs_inode);	init_debug("ipc blob size        = %d\n", blob_sizes.lbs_ipc);	init_debug("msg_msg blob size    = %d\n", blob_sizes.lbs_msg_msg);	init_debug("superblock blob size = %d\n", blob_sizes.lbs_superblock);	init_debug("task blob size       = %d\n", blob_sizes.lbs_task);	init_debug("xattr slots          = %d\n", blob_sizes.lbs_xattr_count); 	/*	 * 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); 	kfree(ordered_lsms);} int __init early_security_init(void){	struct lsm_info *lsm; #define LSM_HOOK(RET, DEFAULT, NAME, ...) \	INIT_HLIST_HEAD(&security_hook_heads.NAME);#include "linux/lsm_hook_defs.h"#undef LSM_HOOK 	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;} /* Save user chosen LSM */static int __init choose_major_lsm(char *str){	chosen_major_lsm = str;	return 1;}__setup("security=", choose_major_lsm); /* Explicitly choose LSM initialization order. */static int __init choose_lsm_order(char *str){	chosen_lsm_order = str;	return 1;}__setup("lsm=", choose_lsm_order); /* Enable LSM order debugging. */static int __init enable_debug(char *str){	debug = true;	return 1;}__setup("lsm.debug", enable_debug); static bool match_last_lsm(const char *list, const char *lsm){	const char *last; 	if (WARN_ON(!list || !lsm))		return false;	last = strrchr(list, ',');	if (last)		/* Pass the comma, strcmp() will check for '\0' */		last++;	else		last = list;	return !strcmp(last, lsm);} static int lsm_append(const char *new, char **result){	char *cp; 	if (*result == NULL) {		*result = kstrdup(new, GFP_KERNEL);		if (*result == NULL)			return -ENOMEM;	} else {		/* Check if it is the last registered name */

Lines 1–500 of 5339.