]>
Commit | Line | Data |
---|---|---|
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 | 9 | bool 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 | 18 | SecureBootMode 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 | 35 | EFI_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 | ||
126 | out_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 | 133 | static 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 | ||
143 | static 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 | ||
158 | static 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. */ | |
185 | void 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 | 215 | void 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 | } |