security/loadpin: Allow to exclude specific file types
Linux kernel already provide MODULE_SIG and KEXEC_VERIFY_SIG to make sure loaded kernel module and kernel image are trusted. This patch adds a kernel command line option "loadpin.exclude" which allows to exclude specific file types from LoadPin. This is useful when people want to use different mechanisms to verify module and kernel image while still use LoadPin to protect the integrity of other files kernel loads. Signed-off-by: Ke Wu <mikewu@google.com> Reviewed-by: James Morris <jamorris@linux.microsoft.com> [kees: fix array size issue reported by Coverity via Colin Ian King] Signed-off-by: Kees Cook <keescook@chromium.org>
This commit is contained in:
Родитель
cd6c84d8f0
Коммит
0ff9848067
|
@ -19,3 +19,13 @@ block device backing the filesystem is not read-only, a sysctl is
|
||||||
created to toggle pinning: ``/proc/sys/kernel/loadpin/enabled``. (Having
|
created to toggle pinning: ``/proc/sys/kernel/loadpin/enabled``. (Having
|
||||||
a mutable filesystem means pinning is mutable too, but having the
|
a mutable filesystem means pinning is mutable too, but having the
|
||||||
sysctl allows for easy testing on systems with a mutable filesystem.)
|
sysctl allows for easy testing on systems with a mutable filesystem.)
|
||||||
|
|
||||||
|
It's also possible to exclude specific file types from LoadPin using kernel
|
||||||
|
command line option "``loadpin.exclude``". By default, all files are
|
||||||
|
included, but they can be excluded using kernel command line option such
|
||||||
|
as "``loadpin.exclude=kernel-module,kexec-image``". This allows to use
|
||||||
|
different mechanisms such as ``CONFIG_MODULE_SIG`` and
|
||||||
|
``CONFIG_KEXEC_VERIFY_SIG`` to verify kernel module and kernel image while
|
||||||
|
still use LoadPin to protect the integrity of other files kernel loads. The
|
||||||
|
full list of valid file types can be found in ``kernel_read_file_str``
|
||||||
|
defined in ``include/linux/fs.h``.
|
||||||
|
|
|
@ -45,6 +45,8 @@ static void report_load(const char *origin, struct file *file, char *operation)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int enforce = IS_ENABLED(CONFIG_SECURITY_LOADPIN_ENFORCE);
|
static int enforce = IS_ENABLED(CONFIG_SECURITY_LOADPIN_ENFORCE);
|
||||||
|
static char *exclude_read_files[READING_MAX_ID];
|
||||||
|
static int ignore_read_file_id[READING_MAX_ID] __ro_after_init;
|
||||||
static struct super_block *pinned_root;
|
static struct super_block *pinned_root;
|
||||||
static DEFINE_SPINLOCK(pinned_root_spinlock);
|
static DEFINE_SPINLOCK(pinned_root_spinlock);
|
||||||
|
|
||||||
|
@ -129,6 +131,13 @@ static int loadpin_read_file(struct file *file, enum kernel_read_file_id id)
|
||||||
struct super_block *load_root;
|
struct super_block *load_root;
|
||||||
const char *origin = kernel_read_file_id_str(id);
|
const char *origin = kernel_read_file_id_str(id);
|
||||||
|
|
||||||
|
/* If the file id is excluded, ignore the pinning. */
|
||||||
|
if ((unsigned int)id < ARRAY_SIZE(ignore_read_file_id) &&
|
||||||
|
ignore_read_file_id[id]) {
|
||||||
|
report_load(origin, file, "pinning-excluded");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* This handles the older init_module API that has a NULL file. */
|
/* This handles the older init_module API that has a NULL file. */
|
||||||
if (!file) {
|
if (!file) {
|
||||||
if (!enforce) {
|
if (!enforce) {
|
||||||
|
@ -187,10 +196,47 @@ static struct security_hook_list loadpin_hooks[] __lsm_ro_after_init = {
|
||||||
LSM_HOOK_INIT(kernel_load_data, loadpin_load_data),
|
LSM_HOOK_INIT(kernel_load_data, loadpin_load_data),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void __init parse_exclude(void)
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
char *cur;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make sure all the arrays stay within expected sizes. This
|
||||||
|
* is slightly weird because kernel_read_file_str[] includes
|
||||||
|
* READING_MAX_ID, which isn't actually meaningful here.
|
||||||
|
*/
|
||||||
|
BUILD_BUG_ON(ARRAY_SIZE(exclude_read_files) !=
|
||||||
|
ARRAY_SIZE(ignore_read_file_id));
|
||||||
|
BUILD_BUG_ON(ARRAY_SIZE(kernel_read_file_str) <
|
||||||
|
ARRAY_SIZE(ignore_read_file_id));
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(exclude_read_files); i++) {
|
||||||
|
cur = exclude_read_files[i];
|
||||||
|
if (!cur)
|
||||||
|
break;
|
||||||
|
if (*cur == '\0')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (j = 0; j < ARRAY_SIZE(ignore_read_file_id); j++) {
|
||||||
|
if (strcmp(cur, kernel_read_file_str[j]) == 0) {
|
||||||
|
pr_info("excluding: %s\n",
|
||||||
|
kernel_read_file_str[j]);
|
||||||
|
ignore_read_file_id[j] = 1;
|
||||||
|
/*
|
||||||
|
* Can not break, because one read_file_str
|
||||||
|
* may map to more than on read_file_id.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int __init loadpin_init(void)
|
static int __init loadpin_init(void)
|
||||||
{
|
{
|
||||||
pr_info("ready to pin (currently %senforcing)\n",
|
pr_info("ready to pin (currently %senforcing)\n",
|
||||||
enforce ? "" : "not ");
|
enforce ? "" : "not ");
|
||||||
|
parse_exclude();
|
||||||
security_add_hooks(loadpin_hooks, ARRAY_SIZE(loadpin_hooks), "loadpin");
|
security_add_hooks(loadpin_hooks, ARRAY_SIZE(loadpin_hooks), "loadpin");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -203,3 +249,5 @@ DEFINE_LSM(loadpin) = {
|
||||||
/* Should not be mutable after boot, so not listed in sysfs (perm == 0). */
|
/* Should not be mutable after boot, so not listed in sysfs (perm == 0). */
|
||||||
module_param(enforce, int, 0);
|
module_param(enforce, int, 0);
|
||||||
MODULE_PARM_DESC(enforce, "Enforce module/firmware pinning");
|
MODULE_PARM_DESC(enforce, "Enforce module/firmware pinning");
|
||||||
|
module_param_array_named(exclude, exclude_read_files, charp, NULL, 0);
|
||||||
|
MODULE_PARM_DESC(exclude, "Exclude pinning specific read file types");
|
||||||
|
|
Загрузка…
Ссылка в новой задаче