162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Lock down the kernel 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2016 Red Hat, Inc. All Rights Reserved. 562306a36Sopenharmony_ci * Written by David Howells (dhowells@redhat.com) 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or 862306a36Sopenharmony_ci * modify it under the terms of the GNU General Public Licence 962306a36Sopenharmony_ci * as published by the Free Software Foundation; either version 1062306a36Sopenharmony_ci * 2 of the Licence, or (at your option) any later version. 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/security.h> 1462306a36Sopenharmony_ci#include <linux/export.h> 1562306a36Sopenharmony_ci#include <linux/lsm_hooks.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistatic enum lockdown_reason kernel_locked_down; 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic const enum lockdown_reason lockdown_levels[] = {LOCKDOWN_NONE, 2062306a36Sopenharmony_ci LOCKDOWN_INTEGRITY_MAX, 2162306a36Sopenharmony_ci LOCKDOWN_CONFIDENTIALITY_MAX}; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* 2462306a36Sopenharmony_ci * Put the kernel into lock-down mode. 2562306a36Sopenharmony_ci */ 2662306a36Sopenharmony_cistatic int lock_kernel_down(const char *where, enum lockdown_reason level) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci if (kernel_locked_down >= level) 2962306a36Sopenharmony_ci return -EPERM; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci kernel_locked_down = level; 3262306a36Sopenharmony_ci pr_notice("Kernel is locked down from %s; see man kernel_lockdown.7\n", 3362306a36Sopenharmony_ci where); 3462306a36Sopenharmony_ci return 0; 3562306a36Sopenharmony_ci} 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic int __init lockdown_param(char *level) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci if (!level) 4062306a36Sopenharmony_ci return -EINVAL; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci if (strcmp(level, "integrity") == 0) 4362306a36Sopenharmony_ci lock_kernel_down("command line", LOCKDOWN_INTEGRITY_MAX); 4462306a36Sopenharmony_ci else if (strcmp(level, "confidentiality") == 0) 4562306a36Sopenharmony_ci lock_kernel_down("command line", LOCKDOWN_CONFIDENTIALITY_MAX); 4662306a36Sopenharmony_ci else 4762306a36Sopenharmony_ci return -EINVAL; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci return 0; 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ciearly_param("lockdown", lockdown_param); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/** 5562306a36Sopenharmony_ci * lockdown_is_locked_down - Find out if the kernel is locked down 5662306a36Sopenharmony_ci * @what: Tag to use in notice generated if lockdown is in effect 5762306a36Sopenharmony_ci */ 5862306a36Sopenharmony_cistatic int lockdown_is_locked_down(enum lockdown_reason what) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci if (WARN(what >= LOCKDOWN_CONFIDENTIALITY_MAX, 6162306a36Sopenharmony_ci "Invalid lockdown reason")) 6262306a36Sopenharmony_ci return -EPERM; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci if (kernel_locked_down >= what) { 6562306a36Sopenharmony_ci if (lockdown_reasons[what]) 6662306a36Sopenharmony_ci pr_notice_ratelimited("Lockdown: %s: %s is restricted; see man kernel_lockdown.7\n", 6762306a36Sopenharmony_ci current->comm, lockdown_reasons[what]); 6862306a36Sopenharmony_ci return -EPERM; 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci return 0; 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic struct security_hook_list lockdown_hooks[] __ro_after_init = { 7562306a36Sopenharmony_ci LSM_HOOK_INIT(locked_down, lockdown_is_locked_down), 7662306a36Sopenharmony_ci}; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic int __init lockdown_lsm_init(void) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci#if defined(CONFIG_LOCK_DOWN_KERNEL_FORCE_INTEGRITY) 8162306a36Sopenharmony_ci lock_kernel_down("Kernel configuration", LOCKDOWN_INTEGRITY_MAX); 8262306a36Sopenharmony_ci#elif defined(CONFIG_LOCK_DOWN_KERNEL_FORCE_CONFIDENTIALITY) 8362306a36Sopenharmony_ci lock_kernel_down("Kernel configuration", LOCKDOWN_CONFIDENTIALITY_MAX); 8462306a36Sopenharmony_ci#endif 8562306a36Sopenharmony_ci security_add_hooks(lockdown_hooks, ARRAY_SIZE(lockdown_hooks), 8662306a36Sopenharmony_ci "lockdown"); 8762306a36Sopenharmony_ci return 0; 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic ssize_t lockdown_read(struct file *filp, char __user *buf, size_t count, 9162306a36Sopenharmony_ci loff_t *ppos) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci char temp[80]; 9462306a36Sopenharmony_ci int i, offset = 0; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(lockdown_levels); i++) { 9762306a36Sopenharmony_ci enum lockdown_reason level = lockdown_levels[i]; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci if (lockdown_reasons[level]) { 10062306a36Sopenharmony_ci const char *label = lockdown_reasons[level]; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci if (kernel_locked_down == level) 10362306a36Sopenharmony_ci offset += sprintf(temp+offset, "[%s] ", label); 10462306a36Sopenharmony_ci else 10562306a36Sopenharmony_ci offset += sprintf(temp+offset, "%s ", label); 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci /* Convert the last space to a newline if needed. */ 11062306a36Sopenharmony_ci if (offset > 0) 11162306a36Sopenharmony_ci temp[offset-1] = '\n'; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci return simple_read_from_buffer(buf, count, ppos, temp, strlen(temp)); 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic ssize_t lockdown_write(struct file *file, const char __user *buf, 11762306a36Sopenharmony_ci size_t n, loff_t *ppos) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci char *state; 12062306a36Sopenharmony_ci int i, len, err = -EINVAL; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci state = memdup_user_nul(buf, n); 12362306a36Sopenharmony_ci if (IS_ERR(state)) 12462306a36Sopenharmony_ci return PTR_ERR(state); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci len = strlen(state); 12762306a36Sopenharmony_ci if (len && state[len-1] == '\n') { 12862306a36Sopenharmony_ci state[len-1] = '\0'; 12962306a36Sopenharmony_ci len--; 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(lockdown_levels); i++) { 13362306a36Sopenharmony_ci enum lockdown_reason level = lockdown_levels[i]; 13462306a36Sopenharmony_ci const char *label = lockdown_reasons[level]; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci if (label && !strcmp(state, label)) 13762306a36Sopenharmony_ci err = lock_kernel_down("securityfs", level); 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci kfree(state); 14162306a36Sopenharmony_ci return err ? err : n; 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic const struct file_operations lockdown_ops = { 14562306a36Sopenharmony_ci .read = lockdown_read, 14662306a36Sopenharmony_ci .write = lockdown_write, 14762306a36Sopenharmony_ci}; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic int __init lockdown_secfs_init(void) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci struct dentry *dentry; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci dentry = securityfs_create_file("lockdown", 0644, NULL, NULL, 15462306a36Sopenharmony_ci &lockdown_ops); 15562306a36Sopenharmony_ci return PTR_ERR_OR_ZERO(dentry); 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cicore_initcall(lockdown_secfs_init); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci#ifdef CONFIG_SECURITY_LOCKDOWN_LSM_EARLY 16162306a36Sopenharmony_ciDEFINE_EARLY_LSM(lockdown) = { 16262306a36Sopenharmony_ci#else 16362306a36Sopenharmony_ciDEFINE_LSM(lockdown) = { 16462306a36Sopenharmony_ci#endif 16562306a36Sopenharmony_ci .name = "lockdown", 16662306a36Sopenharmony_ci .init = lockdown_lsm_init, 16762306a36Sopenharmony_ci}; 168