xref: /openbmc/linux/security/lockdown/lockdown.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
1000d388eSMatthew Garrett // SPDX-License-Identifier: GPL-2.0
2000d388eSMatthew Garrett /* Lock down the kernel
3000d388eSMatthew Garrett  *
4000d388eSMatthew Garrett  * Copyright (C) 2016 Red Hat, Inc. All Rights Reserved.
5000d388eSMatthew Garrett  * Written by David Howells (dhowells@redhat.com)
6000d388eSMatthew Garrett  *
7000d388eSMatthew Garrett  * This program is free software; you can redistribute it and/or
8000d388eSMatthew Garrett  * modify it under the terms of the GNU General Public Licence
9000d388eSMatthew Garrett  * as published by the Free Software Foundation; either version
10000d388eSMatthew Garrett  * 2 of the Licence, or (at your option) any later version.
11000d388eSMatthew Garrett  */
12000d388eSMatthew Garrett 
13000d388eSMatthew Garrett #include <linux/security.h>
14000d388eSMatthew Garrett #include <linux/export.h>
15000d388eSMatthew Garrett #include <linux/lsm_hooks.h>
16000d388eSMatthew Garrett 
17000d388eSMatthew Garrett static enum lockdown_reason kernel_locked_down;
18000d388eSMatthew Garrett 
19f8a9bc62SMatthew Garrett static const enum lockdown_reason lockdown_levels[] = {LOCKDOWN_NONE,
20000d388eSMatthew Garrett 						 LOCKDOWN_INTEGRITY_MAX,
21000d388eSMatthew Garrett 						 LOCKDOWN_CONFIDENTIALITY_MAX};
22000d388eSMatthew Garrett 
23000d388eSMatthew Garrett /*
24000d388eSMatthew Garrett  * Put the kernel into lock-down mode.
25000d388eSMatthew Garrett  */
lock_kernel_down(const char * where,enum lockdown_reason level)26000d388eSMatthew Garrett static int lock_kernel_down(const char *where, enum lockdown_reason level)
27000d388eSMatthew Garrett {
28000d388eSMatthew Garrett 	if (kernel_locked_down >= level)
29000d388eSMatthew Garrett 		return -EPERM;
30000d388eSMatthew Garrett 
31000d388eSMatthew Garrett 	kernel_locked_down = level;
32000d388eSMatthew Garrett 	pr_notice("Kernel is locked down from %s; see man kernel_lockdown.7\n",
33000d388eSMatthew Garrett 		  where);
34000d388eSMatthew Garrett 	return 0;
35000d388eSMatthew Garrett }
36000d388eSMatthew Garrett 
lockdown_param(char * level)37000d388eSMatthew Garrett static int __init lockdown_param(char *level)
38000d388eSMatthew Garrett {
39000d388eSMatthew Garrett 	if (!level)
40000d388eSMatthew Garrett 		return -EINVAL;
41000d388eSMatthew Garrett 
42000d388eSMatthew Garrett 	if (strcmp(level, "integrity") == 0)
43000d388eSMatthew Garrett 		lock_kernel_down("command line", LOCKDOWN_INTEGRITY_MAX);
44000d388eSMatthew Garrett 	else if (strcmp(level, "confidentiality") == 0)
45000d388eSMatthew Garrett 		lock_kernel_down("command line", LOCKDOWN_CONFIDENTIALITY_MAX);
46000d388eSMatthew Garrett 	else
47000d388eSMatthew Garrett 		return -EINVAL;
48000d388eSMatthew Garrett 
49000d388eSMatthew Garrett 	return 0;
50000d388eSMatthew Garrett }
51000d388eSMatthew Garrett 
52000d388eSMatthew Garrett early_param("lockdown", lockdown_param);
53000d388eSMatthew Garrett 
54000d388eSMatthew Garrett /**
55000d388eSMatthew Garrett  * lockdown_is_locked_down - Find out if the kernel is locked down
56000d388eSMatthew Garrett  * @what: Tag to use in notice generated if lockdown is in effect
57000d388eSMatthew Garrett  */
lockdown_is_locked_down(enum lockdown_reason what)58000d388eSMatthew Garrett static int lockdown_is_locked_down(enum lockdown_reason what)
59000d388eSMatthew Garrett {
60b602614aSMatthew Garrett 	if (WARN(what >= LOCKDOWN_CONFIDENTIALITY_MAX,
61b602614aSMatthew Garrett 		 "Invalid lockdown reason"))
62b602614aSMatthew Garrett 		return -EPERM;
63b602614aSMatthew Garrett 
64000d388eSMatthew Garrett 	if (kernel_locked_down >= what) {
65000d388eSMatthew Garrett 		if (lockdown_reasons[what])
661e7d8bcbSNathan Lynch 			pr_notice_ratelimited("Lockdown: %s: %s is restricted; see man kernel_lockdown.7\n",
67b602614aSMatthew Garrett 				  current->comm, lockdown_reasons[what]);
68000d388eSMatthew Garrett 		return -EPERM;
69000d388eSMatthew Garrett 	}
70000d388eSMatthew Garrett 
71000d388eSMatthew Garrett 	return 0;
72000d388eSMatthew Garrett }
73000d388eSMatthew Garrett 
74*f22f9aafSPaul Moore static struct security_hook_list lockdown_hooks[] __ro_after_init = {
75000d388eSMatthew Garrett 	LSM_HOOK_INIT(locked_down, lockdown_is_locked_down),
76000d388eSMatthew Garrett };
77000d388eSMatthew Garrett 
lockdown_lsm_init(void)78000d388eSMatthew Garrett static int __init lockdown_lsm_init(void)
79000d388eSMatthew Garrett {
80000d388eSMatthew Garrett #if defined(CONFIG_LOCK_DOWN_KERNEL_FORCE_INTEGRITY)
81000d388eSMatthew Garrett 	lock_kernel_down("Kernel configuration", LOCKDOWN_INTEGRITY_MAX);
82000d388eSMatthew Garrett #elif defined(CONFIG_LOCK_DOWN_KERNEL_FORCE_CONFIDENTIALITY)
83000d388eSMatthew Garrett 	lock_kernel_down("Kernel configuration", LOCKDOWN_CONFIDENTIALITY_MAX);
84000d388eSMatthew Garrett #endif
85000d388eSMatthew Garrett 	security_add_hooks(lockdown_hooks, ARRAY_SIZE(lockdown_hooks),
86000d388eSMatthew Garrett 			   "lockdown");
87000d388eSMatthew Garrett 	return 0;
88000d388eSMatthew Garrett }
89000d388eSMatthew Garrett 
lockdown_read(struct file * filp,char __user * buf,size_t count,loff_t * ppos)90000d388eSMatthew Garrett static ssize_t lockdown_read(struct file *filp, char __user *buf, size_t count,
91000d388eSMatthew Garrett 			     loff_t *ppos)
92000d388eSMatthew Garrett {
93000d388eSMatthew Garrett 	char temp[80];
94000d388eSMatthew Garrett 	int i, offset = 0;
95000d388eSMatthew Garrett 
96000d388eSMatthew Garrett 	for (i = 0; i < ARRAY_SIZE(lockdown_levels); i++) {
97000d388eSMatthew Garrett 		enum lockdown_reason level = lockdown_levels[i];
98000d388eSMatthew Garrett 
99000d388eSMatthew Garrett 		if (lockdown_reasons[level]) {
100000d388eSMatthew Garrett 			const char *label = lockdown_reasons[level];
101000d388eSMatthew Garrett 
102000d388eSMatthew Garrett 			if (kernel_locked_down == level)
103000d388eSMatthew Garrett 				offset += sprintf(temp+offset, "[%s] ", label);
104000d388eSMatthew Garrett 			else
105000d388eSMatthew Garrett 				offset += sprintf(temp+offset, "%s ", label);
106000d388eSMatthew Garrett 		}
107000d388eSMatthew Garrett 	}
108000d388eSMatthew Garrett 
109000d388eSMatthew Garrett 	/* Convert the last space to a newline if needed. */
110000d388eSMatthew Garrett 	if (offset > 0)
111000d388eSMatthew Garrett 		temp[offset-1] = '\n';
112000d388eSMatthew Garrett 
113000d388eSMatthew Garrett 	return simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
114000d388eSMatthew Garrett }
115000d388eSMatthew Garrett 
lockdown_write(struct file * file,const char __user * buf,size_t n,loff_t * ppos)116000d388eSMatthew Garrett static ssize_t lockdown_write(struct file *file, const char __user *buf,
117000d388eSMatthew Garrett 			      size_t n, loff_t *ppos)
118000d388eSMatthew Garrett {
119000d388eSMatthew Garrett 	char *state;
120000d388eSMatthew Garrett 	int i, len, err = -EINVAL;
121000d388eSMatthew Garrett 
122000d388eSMatthew Garrett 	state = memdup_user_nul(buf, n);
123000d388eSMatthew Garrett 	if (IS_ERR(state))
124000d388eSMatthew Garrett 		return PTR_ERR(state);
125000d388eSMatthew Garrett 
126000d388eSMatthew Garrett 	len = strlen(state);
127000d388eSMatthew Garrett 	if (len && state[len-1] == '\n') {
128000d388eSMatthew Garrett 		state[len-1] = '\0';
129000d388eSMatthew Garrett 		len--;
130000d388eSMatthew Garrett 	}
131000d388eSMatthew Garrett 
132000d388eSMatthew Garrett 	for (i = 0; i < ARRAY_SIZE(lockdown_levels); i++) {
133000d388eSMatthew Garrett 		enum lockdown_reason level = lockdown_levels[i];
134000d388eSMatthew Garrett 		const char *label = lockdown_reasons[level];
135000d388eSMatthew Garrett 
136000d388eSMatthew Garrett 		if (label && !strcmp(state, label))
137000d388eSMatthew Garrett 			err = lock_kernel_down("securityfs", level);
138000d388eSMatthew Garrett 	}
139000d388eSMatthew Garrett 
140000d388eSMatthew Garrett 	kfree(state);
141000d388eSMatthew Garrett 	return err ? err : n;
142000d388eSMatthew Garrett }
143000d388eSMatthew Garrett 
144000d388eSMatthew Garrett static const struct file_operations lockdown_ops = {
145000d388eSMatthew Garrett 	.read  = lockdown_read,
146000d388eSMatthew Garrett 	.write = lockdown_write,
147000d388eSMatthew Garrett };
148000d388eSMatthew Garrett 
lockdown_secfs_init(void)149000d388eSMatthew Garrett static int __init lockdown_secfs_init(void)
150000d388eSMatthew Garrett {
151000d388eSMatthew Garrett 	struct dentry *dentry;
152000d388eSMatthew Garrett 
15360cf7c5eSJeremy Cline 	dentry = securityfs_create_file("lockdown", 0644, NULL, NULL,
154000d388eSMatthew Garrett 					&lockdown_ops);
155000d388eSMatthew Garrett 	return PTR_ERR_OR_ZERO(dentry);
156000d388eSMatthew Garrett }
157000d388eSMatthew Garrett 
158000d388eSMatthew Garrett core_initcall(lockdown_secfs_init);
159000d388eSMatthew Garrett 
160000d388eSMatthew Garrett #ifdef CONFIG_SECURITY_LOCKDOWN_LSM_EARLY
161000d388eSMatthew Garrett DEFINE_EARLY_LSM(lockdown) = {
162000d388eSMatthew Garrett #else
163000d388eSMatthew Garrett DEFINE_LSM(lockdown) = {
164000d388eSMatthew Garrett #endif
165000d388eSMatthew Garrett 	.name = "lockdown",
166000d388eSMatthew Garrett 	.init = lockdown_lsm_init,
167000d388eSMatthew Garrett };
168