blob: aaf30ad351f990d9846b80d735aab83b651f8e5b [file] [log] [blame]
Matthew Garrett000d3882019-08-19 17:17:39 -07001// SPDX-License-Identifier: GPL-2.0
2/* Lock down the kernel
3 *
4 * Copyright (C) 2016 Red Hat, Inc. All Rights Reserved.
5 * Written by David Howells (dhowells@redhat.com)
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public Licence
9 * as published by the Free Software Foundation; either version
10 * 2 of the Licence, or (at your option) any later version.
11 */
12
13#include <linux/security.h>
14#include <linux/export.h>
15#include <linux/lsm_hooks.h>
16
17static enum lockdown_reason kernel_locked_down;
18
19static char *lockdown_reasons[LOCKDOWN_CONFIDENTIALITY_MAX+1] = {
20 [LOCKDOWN_NONE] = "none",
David Howells49fcf732019-08-19 17:17:40 -070021 [LOCKDOWN_MODULE_SIGNATURE] = "unsigned module loading",
Matthew Garrett9b9d8dd2019-08-19 17:17:41 -070022 [LOCKDOWN_DEV_MEM] = "/dev/mem,kmem,port",
Matthew Garrett7d31f462019-08-19 17:17:42 -070023 [LOCKDOWN_KEXEC] = "kexec of unsigned images",
Matthew Garrett000d3882019-08-19 17:17:39 -070024 [LOCKDOWN_INTEGRITY_MAX] = "integrity",
25 [LOCKDOWN_CONFIDENTIALITY_MAX] = "confidentiality",
26};
27
28static enum lockdown_reason lockdown_levels[] = {LOCKDOWN_NONE,
29 LOCKDOWN_INTEGRITY_MAX,
30 LOCKDOWN_CONFIDENTIALITY_MAX};
31
32/*
33 * Put the kernel into lock-down mode.
34 */
35static int lock_kernel_down(const char *where, enum lockdown_reason level)
36{
37 if (kernel_locked_down >= level)
38 return -EPERM;
39
40 kernel_locked_down = level;
41 pr_notice("Kernel is locked down from %s; see man kernel_lockdown.7\n",
42 where);
43 return 0;
44}
45
46static int __init lockdown_param(char *level)
47{
48 if (!level)
49 return -EINVAL;
50
51 if (strcmp(level, "integrity") == 0)
52 lock_kernel_down("command line", LOCKDOWN_INTEGRITY_MAX);
53 else if (strcmp(level, "confidentiality") == 0)
54 lock_kernel_down("command line", LOCKDOWN_CONFIDENTIALITY_MAX);
55 else
56 return -EINVAL;
57
58 return 0;
59}
60
61early_param("lockdown", lockdown_param);
62
63/**
64 * lockdown_is_locked_down - Find out if the kernel is locked down
65 * @what: Tag to use in notice generated if lockdown is in effect
66 */
67static int lockdown_is_locked_down(enum lockdown_reason what)
68{
69 if (kernel_locked_down >= what) {
70 if (lockdown_reasons[what])
71 pr_notice("Lockdown: %s is restricted; see man kernel_lockdown.7\n",
72 lockdown_reasons[what]);
73 return -EPERM;
74 }
75
76 return 0;
77}
78
79static struct security_hook_list lockdown_hooks[] __lsm_ro_after_init = {
80 LSM_HOOK_INIT(locked_down, lockdown_is_locked_down),
81};
82
83static int __init lockdown_lsm_init(void)
84{
85#if defined(CONFIG_LOCK_DOWN_KERNEL_FORCE_INTEGRITY)
86 lock_kernel_down("Kernel configuration", LOCKDOWN_INTEGRITY_MAX);
87#elif defined(CONFIG_LOCK_DOWN_KERNEL_FORCE_CONFIDENTIALITY)
88 lock_kernel_down("Kernel configuration", LOCKDOWN_CONFIDENTIALITY_MAX);
89#endif
90 security_add_hooks(lockdown_hooks, ARRAY_SIZE(lockdown_hooks),
91 "lockdown");
92 return 0;
93}
94
95static ssize_t lockdown_read(struct file *filp, char __user *buf, size_t count,
96 loff_t *ppos)
97{
98 char temp[80];
99 int i, offset = 0;
100
101 for (i = 0; i < ARRAY_SIZE(lockdown_levels); i++) {
102 enum lockdown_reason level = lockdown_levels[i];
103
104 if (lockdown_reasons[level]) {
105 const char *label = lockdown_reasons[level];
106
107 if (kernel_locked_down == level)
108 offset += sprintf(temp+offset, "[%s] ", label);
109 else
110 offset += sprintf(temp+offset, "%s ", label);
111 }
112 }
113
114 /* Convert the last space to a newline if needed. */
115 if (offset > 0)
116 temp[offset-1] = '\n';
117
118 return simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
119}
120
121static ssize_t lockdown_write(struct file *file, const char __user *buf,
122 size_t n, loff_t *ppos)
123{
124 char *state;
125 int i, len, err = -EINVAL;
126
127 state = memdup_user_nul(buf, n);
128 if (IS_ERR(state))
129 return PTR_ERR(state);
130
131 len = strlen(state);
132 if (len && state[len-1] == '\n') {
133 state[len-1] = '\0';
134 len--;
135 }
136
137 for (i = 0; i < ARRAY_SIZE(lockdown_levels); i++) {
138 enum lockdown_reason level = lockdown_levels[i];
139 const char *label = lockdown_reasons[level];
140
141 if (label && !strcmp(state, label))
142 err = lock_kernel_down("securityfs", level);
143 }
144
145 kfree(state);
146 return err ? err : n;
147}
148
149static const struct file_operations lockdown_ops = {
150 .read = lockdown_read,
151 .write = lockdown_write,
152};
153
154static int __init lockdown_secfs_init(void)
155{
156 struct dentry *dentry;
157
158 dentry = securityfs_create_file("lockdown", 0600, NULL, NULL,
159 &lockdown_ops);
160 return PTR_ERR_OR_ZERO(dentry);
161}
162
163core_initcall(lockdown_secfs_init);
164
165#ifdef CONFIG_SECURITY_LOCKDOWN_LSM_EARLY
166DEFINE_EARLY_LSM(lockdown) = {
167#else
168DEFINE_LSM(lockdown) = {
169#endif
170 .name = "lockdown",
171 .init = lockdown_lsm_init,
172};