f3b8788cde
Create a struct lsm_id to contain identifying information about Linux Security Modules (LSMs). At inception this contains the name of the module and an identifier associated with the security module. Change the security_add_hooks() interface to use this structure. Change the individual modules to maintain their own struct lsm_id and pass it to security_add_hooks(). The values are for LSM identifiers are defined in a new UAPI header file linux/lsm.h. Each existing LSM has been updated to include it's LSMID in the lsm_id. The LSM ID values are sequential, with the oldest module LSM_ID_CAPABILITY being the lowest value and the existing modules numbered in the order they were included in the main line kernel. This is an arbitrary convention for assigning the values, but none better presents itself. The value 0 is defined as being invalid. The values 1-99 are reserved for any special case uses which may arise in the future. This may include attributes of the LSM infrastructure itself, possibly related to namespacing or network attribute management. A special range is identified for such attributes to help reduce confusion for developers unfamiliar with LSMs. LSM attribute values are defined for the attributes presented by modules that are available today. As with the LSM IDs, The value 0 is defined as being invalid. The values 1-99 are reserved for any special case uses which may arise in the future. Cc: linux-security-module <linux-security-module@vger.kernel.org> Signed-off-by: Casey Schaufler <casey@schaufler-ca.com> Reviewed-by: Kees Cook <keescook@chromium.org> Reviewed-by: Serge Hallyn <serge@hallyn.com> Reviewed-by: Mickael Salaun <mic@digikod.net> Reviewed-by: John Johansen <john.johansen@canonical.com> Signed-off-by: Kees Cook <keescook@chromium.org> Nacked-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> [PM: forward ported beyond v6.6 due merge window changes] Signed-off-by: Paul Moore <paul@paul-moore.com>
174 lines
4.2 KiB
C
174 lines
4.2 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/* Lock down the kernel
|
|
*
|
|
* Copyright (C) 2016 Red Hat, Inc. All Rights Reserved.
|
|
* Written by David Howells (dhowells@redhat.com)
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public Licence
|
|
* as published by the Free Software Foundation; either version
|
|
* 2 of the Licence, or (at your option) any later version.
|
|
*/
|
|
|
|
#include <linux/security.h>
|
|
#include <linux/export.h>
|
|
#include <linux/lsm_hooks.h>
|
|
#include <uapi/linux/lsm.h>
|
|
|
|
static enum lockdown_reason kernel_locked_down;
|
|
|
|
static const enum lockdown_reason lockdown_levels[] = {LOCKDOWN_NONE,
|
|
LOCKDOWN_INTEGRITY_MAX,
|
|
LOCKDOWN_CONFIDENTIALITY_MAX};
|
|
|
|
/*
|
|
* Put the kernel into lock-down mode.
|
|
*/
|
|
static int lock_kernel_down(const char *where, enum lockdown_reason level)
|
|
{
|
|
if (kernel_locked_down >= level)
|
|
return -EPERM;
|
|
|
|
kernel_locked_down = level;
|
|
pr_notice("Kernel is locked down from %s; see man kernel_lockdown.7\n",
|
|
where);
|
|
return 0;
|
|
}
|
|
|
|
static int __init lockdown_param(char *level)
|
|
{
|
|
if (!level)
|
|
return -EINVAL;
|
|
|
|
if (strcmp(level, "integrity") == 0)
|
|
lock_kernel_down("command line", LOCKDOWN_INTEGRITY_MAX);
|
|
else if (strcmp(level, "confidentiality") == 0)
|
|
lock_kernel_down("command line", LOCKDOWN_CONFIDENTIALITY_MAX);
|
|
else
|
|
return -EINVAL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
early_param("lockdown", lockdown_param);
|
|
|
|
/**
|
|
* lockdown_is_locked_down - Find out if the kernel is locked down
|
|
* @what: Tag to use in notice generated if lockdown is in effect
|
|
*/
|
|
static int lockdown_is_locked_down(enum lockdown_reason what)
|
|
{
|
|
if (WARN(what >= LOCKDOWN_CONFIDENTIALITY_MAX,
|
|
"Invalid lockdown reason"))
|
|
return -EPERM;
|
|
|
|
if (kernel_locked_down >= what) {
|
|
if (lockdown_reasons[what])
|
|
pr_notice_ratelimited("Lockdown: %s: %s is restricted; see man kernel_lockdown.7\n",
|
|
current->comm, lockdown_reasons[what]);
|
|
return -EPERM;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct security_hook_list lockdown_hooks[] __ro_after_init = {
|
|
LSM_HOOK_INIT(locked_down, lockdown_is_locked_down),
|
|
};
|
|
|
|
const struct lsm_id lockdown_lsmid = {
|
|
.name = "lockdown",
|
|
.id = LSM_ID_LOCKDOWN,
|
|
};
|
|
|
|
static int __init lockdown_lsm_init(void)
|
|
{
|
|
#if defined(CONFIG_LOCK_DOWN_KERNEL_FORCE_INTEGRITY)
|
|
lock_kernel_down("Kernel configuration", LOCKDOWN_INTEGRITY_MAX);
|
|
#elif defined(CONFIG_LOCK_DOWN_KERNEL_FORCE_CONFIDENTIALITY)
|
|
lock_kernel_down("Kernel configuration", LOCKDOWN_CONFIDENTIALITY_MAX);
|
|
#endif
|
|
security_add_hooks(lockdown_hooks, ARRAY_SIZE(lockdown_hooks),
|
|
&lockdown_lsmid);
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t lockdown_read(struct file *filp, char __user *buf, size_t count,
|
|
loff_t *ppos)
|
|
{
|
|
char temp[80];
|
|
int i, offset = 0;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(lockdown_levels); i++) {
|
|
enum lockdown_reason level = lockdown_levels[i];
|
|
|
|
if (lockdown_reasons[level]) {
|
|
const char *label = lockdown_reasons[level];
|
|
|
|
if (kernel_locked_down == level)
|
|
offset += sprintf(temp+offset, "[%s] ", label);
|
|
else
|
|
offset += sprintf(temp+offset, "%s ", label);
|
|
}
|
|
}
|
|
|
|
/* Convert the last space to a newline if needed. */
|
|
if (offset > 0)
|
|
temp[offset-1] = '\n';
|
|
|
|
return simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
|
|
}
|
|
|
|
static ssize_t lockdown_write(struct file *file, const char __user *buf,
|
|
size_t n, loff_t *ppos)
|
|
{
|
|
char *state;
|
|
int i, len, err = -EINVAL;
|
|
|
|
state = memdup_user_nul(buf, n);
|
|
if (IS_ERR(state))
|
|
return PTR_ERR(state);
|
|
|
|
len = strlen(state);
|
|
if (len && state[len-1] == '\n') {
|
|
state[len-1] = '\0';
|
|
len--;
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_SIZE(lockdown_levels); i++) {
|
|
enum lockdown_reason level = lockdown_levels[i];
|
|
const char *label = lockdown_reasons[level];
|
|
|
|
if (label && !strcmp(state, label))
|
|
err = lock_kernel_down("securityfs", level);
|
|
}
|
|
|
|
kfree(state);
|
|
return err ? err : n;
|
|
}
|
|
|
|
static const struct file_operations lockdown_ops = {
|
|
.read = lockdown_read,
|
|
.write = lockdown_write,
|
|
};
|
|
|
|
static int __init lockdown_secfs_init(void)
|
|
{
|
|
struct dentry *dentry;
|
|
|
|
dentry = securityfs_create_file("lockdown", 0644, NULL, NULL,
|
|
&lockdown_ops);
|
|
return PTR_ERR_OR_ZERO(dentry);
|
|
}
|
|
|
|
core_initcall(lockdown_secfs_init);
|
|
|
|
#ifdef CONFIG_SECURITY_LOCKDOWN_LSM_EARLY
|
|
DEFINE_EARLY_LSM(lockdown) = {
|
|
#else
|
|
DEFINE_LSM(lockdown) = {
|
|
#endif
|
|
.name = "lockdown",
|
|
.init = lockdown_lsm_init,
|
|
};
|