]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/boot/efi/secure-boot.c
Merge pull request #28919 from fbuihuu/custom-config-file-install-path
[thirdparty/systemd.git] / src / boot / efi / secure-boot.c
CommitLineData
ce0f078f
DDM
1/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
ba279392 3#include "console.h"
5080a60a 4#include "proto/security-arch.h"
ce0f078f
DDM
5#include "secure-boot.h"
6#include "util.h"
ba279392 7#include "vmm.h"
ce0f078f 8
e5a1b8f9 9bool secure_boot_enabled(void) {
febe5561 10 bool secure = false; /* avoid false maybe-uninitialized warning */
2a7c1675 11 EFI_STATUS err;
ce0f078f 12
19f08504 13 err = efivar_get_boolean_u8(MAKE_GUID_PTR(EFI_GLOBAL_VARIABLE), u"SecureBoot", &secure);
ce0f078f 14
2a5e4fe4 15 return err == EFI_SUCCESS && secure;
ce0f078f 16}
9137c03c 17
c4964512 18SecureBootMode secure_boot_mode(void) {
e5a1b8f9 19 bool secure, audit = false, deployed = false, setup = false;
c4964512
JJ
20 EFI_STATUS err;
21
19f08504 22 err = efivar_get_boolean_u8(MAKE_GUID_PTR(EFI_GLOBAL_VARIABLE), u"SecureBoot", &secure);
2a5e4fe4 23 if (err != EFI_SUCCESS)
c4964512
JJ
24 return SECURE_BOOT_UNSUPPORTED;
25
e5a1b8f9 26 /* We can assume false for all these if they are abscent (AuditMode and
c4964512 27 * DeployedMode may not exist on older firmware). */
19f08504
JJ
28 (void) efivar_get_boolean_u8(MAKE_GUID_PTR(EFI_GLOBAL_VARIABLE), u"AuditMode", &audit);
29 (void) efivar_get_boolean_u8(MAKE_GUID_PTR(EFI_GLOBAL_VARIABLE), u"DeployedMode", &deployed);
30 (void) efivar_get_boolean_u8(MAKE_GUID_PTR(EFI_GLOBAL_VARIABLE), u"SetupMode", &setup);
c4964512
JJ
31
32 return decode_secure_boot_mode(secure, audit, deployed, setup);
33}
34
1e8e7f44 35EFI_STATUS secure_boot_enroll_at(EFI_FILE *root_dir, const char16_t *path, bool force) {
e6b0cfad
VD
36 assert(root_dir);
37 assert(path);
38
39 EFI_STATUS err;
40
41 clear_screen(COLOR_NORMAL);
42
3e87a057
JJ
43 /* Enrolling secure boot keys is safe to do in virtualized environments as there is nothing
44 * we can brick there. */
1e8e7f44
JJ
45 bool is_safe = in_hypervisor();
46
47 if (!is_safe && !force)
48 return EFI_SUCCESS;
49
50 printf("Enrolling secure boot keys from directory: %ls\n", path);
51
52 if (!is_safe) {
9220b2c4 53 printf("Warning: Enrolling custom Secure Boot keys might soft-brick your machine!\n");
3e87a057
JJ
54
55 unsigned timeout_sec = 15;
56 for (;;) {
9220b2c4 57 printf("\rEnrolling in %2u s, press any key to abort.", timeout_sec);
3e87a057
JJ
58
59 uint64_t key;
60 err = console_key_read(&key, 1000 * 1000);
61 if (err == EFI_NOT_READY)
62 continue;
63 if (err == EFI_TIMEOUT) {
64 if (timeout_sec == 0) /* continue enrolling keys */
65 break;
66 timeout_sec--;
67 continue;
68 }
69 if (err != EFI_SUCCESS)
c2c62035 70 return log_error_status(
3e87a057 71 err,
c2c62035 72 "Error waiting for user input to enroll Secure Boot keys: %m");
3e87a057
JJ
73
74 /* user aborted, returning EFI_SUCCESS here allows the user to go back to the menu */
75 return EFI_SUCCESS;
e6b0cfad 76 }
e6b0cfad
VD
77 }
78
79 _cleanup_(file_closep) EFI_FILE *dir = NULL;
80
81 err = open_directory(root_dir, path, &dir);
82 if (err != EFI_SUCCESS)
c2c62035 83 return log_error_status(err, "Failed opening keys directory %ls: %m", path);
e6b0cfad
VD
84
85 struct {
86 const char16_t *name;
87 const char16_t *filename;
88 const EFI_GUID vendor;
89 char *buffer;
90 size_t size;
91 } sb_vars[] = {
5080a60a 92 { u"db", u"db.auth", EFI_IMAGE_SECURITY_DATABASE_GUID, NULL, 0 },
adb9485a
JJ
93 { u"KEK", u"KEK.auth", EFI_GLOBAL_VARIABLE, NULL, 0 },
94 { u"PK", u"PK.auth", EFI_GLOBAL_VARIABLE, NULL, 0 },
e6b0cfad
VD
95 };
96
97 /* Make sure all keys files exist before we start enrolling them by loading them from the disk first. */
98 for (size_t i = 0; i < ELEMENTSOF(sb_vars); i++) {
99 err = file_read(dir, sb_vars[i].filename, 0, 0, &sb_vars[i].buffer, &sb_vars[i].size);
100 if (err != EFI_SUCCESS) {
c2c62035 101 log_error_status(err, "Failed reading file %ls\\%ls: %m", path, sb_vars[i].filename);
e6b0cfad
VD
102 goto out_deallocate;
103 }
104 }
105
106 for (size_t i = 0; i < ELEMENTSOF(sb_vars); i++) {
107 uint32_t sb_vars_opts =
108 EFI_VARIABLE_NON_VOLATILE |
109 EFI_VARIABLE_BOOTSERVICE_ACCESS |
110 EFI_VARIABLE_RUNTIME_ACCESS |
111 EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS;
112
113 err = efivar_set_raw(&sb_vars[i].vendor, sb_vars[i].name, sb_vars[i].buffer, sb_vars[i].size, sb_vars_opts);
114 if (err != EFI_SUCCESS) {
c2c62035 115 log_error_status(err, "Failed to write %ls secure boot variable: %m", sb_vars[i].name);
e6b0cfad
VD
116 goto out_deallocate;
117 }
118 }
119
120 /* The system should be in secure boot mode now and we could continue a regular boot. But at least
121 * TPM PCR7 measurements should change on next boot. Reboot now so that any OS we load does not end
122 * up relying on the old PCR state. */
123 RT->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL);
124 assert_not_reached();
125
126out_deallocate:
127 for (size_t i = 0; i < ELEMENTSOF(sb_vars); i++)
1d278ad7 128 free(sb_vars[i].buffer);
e6b0cfad
VD
129
130 return err;
131}
6731a102 132
5489c13b 133static struct SecurityOverride {
967a8685
JJ
134 EFI_SECURITY_ARCH_PROTOCOL *security;
135 EFI_SECURITY2_ARCH_PROTOCOL *security2;
136 EFI_SECURITY_FILE_AUTHENTICATION_STATE original_hook;
137 EFI_SECURITY2_FILE_AUTHENTICATION original_hook2;
5489c13b
JJ
138
139 security_validator_t validator;
140 const void *validator_ctx;
141} security_override;
142
143static EFIAPI EFI_STATUS security_hook(
144 const EFI_SECURITY_ARCH_PROTOCOL *this,
145 uint32_t authentication_status,
146 const EFI_DEVICE_PATH *file) {
147
148 assert(security_override.validator);
967a8685
JJ
149 assert(security_override.security);
150 assert(security_override.original_hook);
5489c13b
JJ
151
152 if (security_override.validator(security_override.validator_ctx, file, NULL, 0))
153 return EFI_SUCCESS;
154
967a8685 155 return security_override.original_hook(security_override.security, authentication_status, file);
5489c13b
JJ
156}
157
158static EFIAPI EFI_STATUS security2_hook(
159 const EFI_SECURITY2_ARCH_PROTOCOL *this,
160 const EFI_DEVICE_PATH *device_path,
161 void *file_buffer,
162 size_t file_size,
5080a60a 163 bool boot_policy) {
5489c13b
JJ
164
165 assert(security_override.validator);
967a8685
JJ
166 assert(security_override.security2);
167 assert(security_override.original_hook2);
5489c13b
JJ
168
169 if (security_override.validator(security_override.validator_ctx, device_path, file_buffer, file_size))
170 return EFI_SUCCESS;
171
967a8685
JJ
172 return security_override.original_hook2(
173 security_override.security2, device_path, file_buffer, file_size, boot_policy);
5489c13b
JJ
174}
175
967a8685
JJ
176/* This replaces the platform provided security arch protocols hooks (defined in the UEFI Platform
177 * Initialization Specification) with our own that uses the given validator to decide if a image is to be
178 * trusted. If not running in secure boot or the protocols are not available nothing happens. The override
179 * must be removed with uninstall_security_override() after LoadImage() has been called.
180 *
181 * This is a hack as we do not own the security protocol instances and modifying them is not an official part
182 * of their spec. But there is little else we can do to circumvent secure boot short of implementing our own
183 * PE loader. We could replace the firmware instances with our own instance using
184 * ReinstallProtocolInterface(), but some firmware will still use the old ones. */
185void install_security_override(security_validator_t validator, const void *validator_ctx) {
6731a102
JJ
186 EFI_STATUS err;
187
5489c13b 188 assert(validator);
6731a102
JJ
189
190 if (!secure_boot_enabled())
191 return;
192
5489c13b 193 security_override = (struct SecurityOverride) {
5489c13b
JJ
194 .validator = validator,
195 .validator_ctx = validator_ctx,
196 };
6731a102 197
967a8685 198 EFI_SECURITY_ARCH_PROTOCOL *security = NULL;
19f08504 199 err = BS->LocateProtocol(MAKE_GUID_PTR(EFI_SECURITY_ARCH_PROTOCOL), NULL, (void **) &security);
967a8685
JJ
200 if (err == EFI_SUCCESS) {
201 security_override.security = security;
202 security_override.original_hook = security->FileAuthenticationState;
203 security->FileAuthenticationState = security_hook;
204 }
205
206 EFI_SECURITY2_ARCH_PROTOCOL *security2 = NULL;
19f08504 207 err = BS->LocateProtocol(MAKE_GUID_PTR(EFI_SECURITY2_ARCH_PROTOCOL), NULL, (void **) &security2);
967a8685
JJ
208 if (err == EFI_SUCCESS) {
209 security_override.security2 = security2;
210 security_override.original_hook2 = security2->FileAuthentication;
211 security2->FileAuthentication = security2_hook;
212 }
5489c13b 213}
6731a102 214
5489c13b 215void uninstall_security_override(void) {
967a8685
JJ
216 if (security_override.original_hook)
217 security_override.security->FileAuthenticationState = security_override.original_hook;
218 if (security_override.original_hook2)
219 security_override.security2->FileAuthentication = security_override.original_hook2;
6731a102 220}