xref: /OK3568_Linux_fs/kernel/security/lockdown/lockdown.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /* Lock down the kernel
3*4882a593Smuzhiyun  *
4*4882a593Smuzhiyun  * Copyright (C) 2016 Red Hat, Inc. All Rights Reserved.
5*4882a593Smuzhiyun  * Written by David Howells (dhowells@redhat.com)
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  * This program is free software; you can redistribute it and/or
8*4882a593Smuzhiyun  * modify it under the terms of the GNU General Public Licence
9*4882a593Smuzhiyun  * as published by the Free Software Foundation; either version
10*4882a593Smuzhiyun  * 2 of the Licence, or (at your option) any later version.
11*4882a593Smuzhiyun  */
12*4882a593Smuzhiyun 
13*4882a593Smuzhiyun #include <linux/security.h>
14*4882a593Smuzhiyun #include <linux/export.h>
15*4882a593Smuzhiyun #include <linux/lsm_hooks.h>
16*4882a593Smuzhiyun 
17*4882a593Smuzhiyun static enum lockdown_reason kernel_locked_down;
18*4882a593Smuzhiyun 
19*4882a593Smuzhiyun static const char *const lockdown_reasons[LOCKDOWN_CONFIDENTIALITY_MAX+1] = {
20*4882a593Smuzhiyun 	[LOCKDOWN_NONE] = "none",
21*4882a593Smuzhiyun 	[LOCKDOWN_MODULE_SIGNATURE] = "unsigned module loading",
22*4882a593Smuzhiyun 	[LOCKDOWN_DEV_MEM] = "/dev/mem,kmem,port",
23*4882a593Smuzhiyun 	[LOCKDOWN_EFI_TEST] = "/dev/efi_test access",
24*4882a593Smuzhiyun 	[LOCKDOWN_KEXEC] = "kexec of unsigned images",
25*4882a593Smuzhiyun 	[LOCKDOWN_HIBERNATION] = "hibernation",
26*4882a593Smuzhiyun 	[LOCKDOWN_PCI_ACCESS] = "direct PCI access",
27*4882a593Smuzhiyun 	[LOCKDOWN_IOPORT] = "raw io port access",
28*4882a593Smuzhiyun 	[LOCKDOWN_MSR] = "raw MSR access",
29*4882a593Smuzhiyun 	[LOCKDOWN_ACPI_TABLES] = "modifying ACPI tables",
30*4882a593Smuzhiyun 	[LOCKDOWN_PCMCIA_CIS] = "direct PCMCIA CIS storage",
31*4882a593Smuzhiyun 	[LOCKDOWN_TIOCSSERIAL] = "reconfiguration of serial port IO",
32*4882a593Smuzhiyun 	[LOCKDOWN_MODULE_PARAMETERS] = "unsafe module parameters",
33*4882a593Smuzhiyun 	[LOCKDOWN_MMIOTRACE] = "unsafe mmio",
34*4882a593Smuzhiyun 	[LOCKDOWN_DEBUGFS] = "debugfs access",
35*4882a593Smuzhiyun 	[LOCKDOWN_XMON_WR] = "xmon write access",
36*4882a593Smuzhiyun 	[LOCKDOWN_BPF_WRITE_USER] = "use of bpf to write user RAM",
37*4882a593Smuzhiyun 	[LOCKDOWN_INTEGRITY_MAX] = "integrity",
38*4882a593Smuzhiyun 	[LOCKDOWN_KCORE] = "/proc/kcore access",
39*4882a593Smuzhiyun 	[LOCKDOWN_KPROBES] = "use of kprobes",
40*4882a593Smuzhiyun 	[LOCKDOWN_BPF_READ] = "use of bpf to read kernel RAM",
41*4882a593Smuzhiyun 	[LOCKDOWN_PERF] = "unsafe use of perf",
42*4882a593Smuzhiyun 	[LOCKDOWN_TRACEFS] = "use of tracefs",
43*4882a593Smuzhiyun 	[LOCKDOWN_XMON_RW] = "xmon read and write access",
44*4882a593Smuzhiyun 	[LOCKDOWN_CONFIDENTIALITY_MAX] = "confidentiality",
45*4882a593Smuzhiyun };
46*4882a593Smuzhiyun 
47*4882a593Smuzhiyun static const enum lockdown_reason lockdown_levels[] = {LOCKDOWN_NONE,
48*4882a593Smuzhiyun 						 LOCKDOWN_INTEGRITY_MAX,
49*4882a593Smuzhiyun 						 LOCKDOWN_CONFIDENTIALITY_MAX};
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun /*
52*4882a593Smuzhiyun  * Put the kernel into lock-down mode.
53*4882a593Smuzhiyun  */
lock_kernel_down(const char * where,enum lockdown_reason level)54*4882a593Smuzhiyun static int lock_kernel_down(const char *where, enum lockdown_reason level)
55*4882a593Smuzhiyun {
56*4882a593Smuzhiyun 	if (kernel_locked_down >= level)
57*4882a593Smuzhiyun 		return -EPERM;
58*4882a593Smuzhiyun 
59*4882a593Smuzhiyun 	kernel_locked_down = level;
60*4882a593Smuzhiyun 	pr_notice("Kernel is locked down from %s; see man kernel_lockdown.7\n",
61*4882a593Smuzhiyun 		  where);
62*4882a593Smuzhiyun 	return 0;
63*4882a593Smuzhiyun }
64*4882a593Smuzhiyun 
lockdown_param(char * level)65*4882a593Smuzhiyun static int __init lockdown_param(char *level)
66*4882a593Smuzhiyun {
67*4882a593Smuzhiyun 	if (!level)
68*4882a593Smuzhiyun 		return -EINVAL;
69*4882a593Smuzhiyun 
70*4882a593Smuzhiyun 	if (strcmp(level, "integrity") == 0)
71*4882a593Smuzhiyun 		lock_kernel_down("command line", LOCKDOWN_INTEGRITY_MAX);
72*4882a593Smuzhiyun 	else if (strcmp(level, "confidentiality") == 0)
73*4882a593Smuzhiyun 		lock_kernel_down("command line", LOCKDOWN_CONFIDENTIALITY_MAX);
74*4882a593Smuzhiyun 	else
75*4882a593Smuzhiyun 		return -EINVAL;
76*4882a593Smuzhiyun 
77*4882a593Smuzhiyun 	return 0;
78*4882a593Smuzhiyun }
79*4882a593Smuzhiyun 
80*4882a593Smuzhiyun early_param("lockdown", lockdown_param);
81*4882a593Smuzhiyun 
82*4882a593Smuzhiyun /**
83*4882a593Smuzhiyun  * lockdown_is_locked_down - Find out if the kernel is locked down
84*4882a593Smuzhiyun  * @what: Tag to use in notice generated if lockdown is in effect
85*4882a593Smuzhiyun  */
lockdown_is_locked_down(enum lockdown_reason what)86*4882a593Smuzhiyun static int lockdown_is_locked_down(enum lockdown_reason what)
87*4882a593Smuzhiyun {
88*4882a593Smuzhiyun 	if (WARN(what >= LOCKDOWN_CONFIDENTIALITY_MAX,
89*4882a593Smuzhiyun 		 "Invalid lockdown reason"))
90*4882a593Smuzhiyun 		return -EPERM;
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun 	if (kernel_locked_down >= what) {
93*4882a593Smuzhiyun 		if (lockdown_reasons[what])
94*4882a593Smuzhiyun 			pr_notice("Lockdown: %s: %s is restricted; see man kernel_lockdown.7\n",
95*4882a593Smuzhiyun 				  current->comm, lockdown_reasons[what]);
96*4882a593Smuzhiyun 		return -EPERM;
97*4882a593Smuzhiyun 	}
98*4882a593Smuzhiyun 
99*4882a593Smuzhiyun 	return 0;
100*4882a593Smuzhiyun }
101*4882a593Smuzhiyun 
102*4882a593Smuzhiyun static struct security_hook_list lockdown_hooks[] __lsm_ro_after_init = {
103*4882a593Smuzhiyun 	LSM_HOOK_INIT(locked_down, lockdown_is_locked_down),
104*4882a593Smuzhiyun };
105*4882a593Smuzhiyun 
lockdown_lsm_init(void)106*4882a593Smuzhiyun static int __init lockdown_lsm_init(void)
107*4882a593Smuzhiyun {
108*4882a593Smuzhiyun #if defined(CONFIG_LOCK_DOWN_KERNEL_FORCE_INTEGRITY)
109*4882a593Smuzhiyun 	lock_kernel_down("Kernel configuration", LOCKDOWN_INTEGRITY_MAX);
110*4882a593Smuzhiyun #elif defined(CONFIG_LOCK_DOWN_KERNEL_FORCE_CONFIDENTIALITY)
111*4882a593Smuzhiyun 	lock_kernel_down("Kernel configuration", LOCKDOWN_CONFIDENTIALITY_MAX);
112*4882a593Smuzhiyun #endif
113*4882a593Smuzhiyun 	security_add_hooks(lockdown_hooks, ARRAY_SIZE(lockdown_hooks),
114*4882a593Smuzhiyun 			   "lockdown");
115*4882a593Smuzhiyun 	return 0;
116*4882a593Smuzhiyun }
117*4882a593Smuzhiyun 
lockdown_read(struct file * filp,char __user * buf,size_t count,loff_t * ppos)118*4882a593Smuzhiyun static ssize_t lockdown_read(struct file *filp, char __user *buf, size_t count,
119*4882a593Smuzhiyun 			     loff_t *ppos)
120*4882a593Smuzhiyun {
121*4882a593Smuzhiyun 	char temp[80];
122*4882a593Smuzhiyun 	int i, offset = 0;
123*4882a593Smuzhiyun 
124*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(lockdown_levels); i++) {
125*4882a593Smuzhiyun 		enum lockdown_reason level = lockdown_levels[i];
126*4882a593Smuzhiyun 
127*4882a593Smuzhiyun 		if (lockdown_reasons[level]) {
128*4882a593Smuzhiyun 			const char *label = lockdown_reasons[level];
129*4882a593Smuzhiyun 
130*4882a593Smuzhiyun 			if (kernel_locked_down == level)
131*4882a593Smuzhiyun 				offset += sprintf(temp+offset, "[%s] ", label);
132*4882a593Smuzhiyun 			else
133*4882a593Smuzhiyun 				offset += sprintf(temp+offset, "%s ", label);
134*4882a593Smuzhiyun 		}
135*4882a593Smuzhiyun 	}
136*4882a593Smuzhiyun 
137*4882a593Smuzhiyun 	/* Convert the last space to a newline if needed. */
138*4882a593Smuzhiyun 	if (offset > 0)
139*4882a593Smuzhiyun 		temp[offset-1] = '\n';
140*4882a593Smuzhiyun 
141*4882a593Smuzhiyun 	return simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
142*4882a593Smuzhiyun }
143*4882a593Smuzhiyun 
lockdown_write(struct file * file,const char __user * buf,size_t n,loff_t * ppos)144*4882a593Smuzhiyun static ssize_t lockdown_write(struct file *file, const char __user *buf,
145*4882a593Smuzhiyun 			      size_t n, loff_t *ppos)
146*4882a593Smuzhiyun {
147*4882a593Smuzhiyun 	char *state;
148*4882a593Smuzhiyun 	int i, len, err = -EINVAL;
149*4882a593Smuzhiyun 
150*4882a593Smuzhiyun 	state = memdup_user_nul(buf, n);
151*4882a593Smuzhiyun 	if (IS_ERR(state))
152*4882a593Smuzhiyun 		return PTR_ERR(state);
153*4882a593Smuzhiyun 
154*4882a593Smuzhiyun 	len = strlen(state);
155*4882a593Smuzhiyun 	if (len && state[len-1] == '\n') {
156*4882a593Smuzhiyun 		state[len-1] = '\0';
157*4882a593Smuzhiyun 		len--;
158*4882a593Smuzhiyun 	}
159*4882a593Smuzhiyun 
160*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(lockdown_levels); i++) {
161*4882a593Smuzhiyun 		enum lockdown_reason level = lockdown_levels[i];
162*4882a593Smuzhiyun 		const char *label = lockdown_reasons[level];
163*4882a593Smuzhiyun 
164*4882a593Smuzhiyun 		if (label && !strcmp(state, label))
165*4882a593Smuzhiyun 			err = lock_kernel_down("securityfs", level);
166*4882a593Smuzhiyun 	}
167*4882a593Smuzhiyun 
168*4882a593Smuzhiyun 	kfree(state);
169*4882a593Smuzhiyun 	return err ? err : n;
170*4882a593Smuzhiyun }
171*4882a593Smuzhiyun 
172*4882a593Smuzhiyun static const struct file_operations lockdown_ops = {
173*4882a593Smuzhiyun 	.read  = lockdown_read,
174*4882a593Smuzhiyun 	.write = lockdown_write,
175*4882a593Smuzhiyun };
176*4882a593Smuzhiyun 
lockdown_secfs_init(void)177*4882a593Smuzhiyun static int __init lockdown_secfs_init(void)
178*4882a593Smuzhiyun {
179*4882a593Smuzhiyun 	struct dentry *dentry;
180*4882a593Smuzhiyun 
181*4882a593Smuzhiyun 	dentry = securityfs_create_file("lockdown", 0644, NULL, NULL,
182*4882a593Smuzhiyun 					&lockdown_ops);
183*4882a593Smuzhiyun 	return PTR_ERR_OR_ZERO(dentry);
184*4882a593Smuzhiyun }
185*4882a593Smuzhiyun 
186*4882a593Smuzhiyun core_initcall(lockdown_secfs_init);
187*4882a593Smuzhiyun 
188*4882a593Smuzhiyun #ifdef CONFIG_SECURITY_LOCKDOWN_LSM_EARLY
189*4882a593Smuzhiyun DEFINE_EARLY_LSM(lockdown) = {
190*4882a593Smuzhiyun #else
191*4882a593Smuzhiyun DEFINE_LSM(lockdown) = {
192*4882a593Smuzhiyun #endif
193*4882a593Smuzhiyun 	.name = "lockdown",
194*4882a593Smuzhiyun 	.init = lockdown_lsm_init,
195*4882a593Smuzhiyun };
196