]> git.proxmox.com Git - efi-boot-shim.git/blob - replacements.c
bump version to 15.8-1+pmx1
[efi-boot-shim.git] / replacements.c
1 // SPDX-License-Identifier: BSD-2-Clause-Patent
2 /*
3 * shim - trivial UEFI first-stage bootloader
4 *
5 * Copyright Red Hat, Inc
6 */
7
8 /* Chemical agents lend themselves to covert use in sabotage against
9 * which it is exceedingly difficult to visualize any really effective
10 * defense... I will not dwell upon this use of CBW because, as one
11 * pursues the possibilities of such covert uses, one discovers that the
12 * scenarios resemble that in which the components of a nuclear weapon
13 * are smuggled into New York City and assembled in the basement of the
14 * Empire State Building.
15 * In other words, once the possibility is recognized to exist, about
16 * all that one can do is worry about it.
17 * -- Dr. Ivan L Bennett, Jr., testifying before the Subcommittee on
18 * National Security Policy and Scientific Developments, November 20,
19 * 1969.
20 */
21 #include "shim.h"
22
23 static EFI_SYSTEM_TABLE *systab;
24
25 EFI_SYSTEM_TABLE *
26 get_active_systab(void)
27 {
28 if (systab)
29 return systab;
30 return ST;
31 }
32
33 static typeof(systab->BootServices->LoadImage) system_load_image;
34 static typeof(systab->BootServices->StartImage) system_start_image;
35 static typeof(systab->BootServices->Exit) system_exit;
36 #if !defined(DISABLE_EBS_PROTECTION)
37 static typeof(systab->BootServices->ExitBootServices) system_exit_boot_services;
38 #endif /* !defined(DISABLE_EBS_PROTECTION) */
39
40 static EFI_HANDLE last_loaded_image;
41
42 void
43 unhook_system_services(void)
44 {
45 if (!systab)
46 return;
47
48 systab->BootServices->LoadImage = system_load_image;
49 systab->BootServices->StartImage = system_start_image;
50 #if !defined(DISABLE_EBS_PROTECTION)
51 systab->BootServices->ExitBootServices = system_exit_boot_services;
52 #endif /* !defined(DISABLE_EBS_PROTECTION) */
53 BS = systab->BootServices;
54 }
55
56 static EFI_STATUS EFIAPI
57 load_image(BOOLEAN BootPolicy, EFI_HANDLE ParentImageHandle,
58 EFI_DEVICE_PATH *DevicePath, VOID *SourceBuffer,
59 UINTN SourceSize, EFI_HANDLE *ImageHandle)
60 {
61 EFI_STATUS efi_status;
62
63 unhook_system_services();
64 efi_status = BS->LoadImage(BootPolicy, ParentImageHandle, DevicePath,
65 SourceBuffer, SourceSize, ImageHandle);
66 hook_system_services(systab);
67 if (EFI_ERROR(efi_status))
68 last_loaded_image = NULL;
69 else
70 last_loaded_image = *ImageHandle;
71 return efi_status;
72 }
73
74 static EFI_STATUS EFIAPI
75 replacement_start_image(EFI_HANDLE image_handle, UINTN *exit_data_size, CHAR16 **exit_data)
76 {
77 EFI_STATUS efi_status;
78 unhook_system_services();
79
80 if (image_handle == last_loaded_image) {
81 UINT8 retain_protocol = 0;
82 UINTN retain_protocol_size = sizeof(retain_protocol);
83 UINT32 retain_protocol_attrs = 0;
84
85 loader_is_participating = 1;
86
87 /* If a boot component asks us, keep our protocol around - it will be used to
88 * validate further PE payloads (e.g.: by the UKI stub, before the kernel is booted).
89 * But also check that the variable was set by a boot component, to ensure that
90 * nobody at runtime can attempt to change shim's behaviour. */
91 efi_status = RT->GetVariable(SHIM_RETAIN_PROTOCOL_VAR_NAME,
92 &SHIM_LOCK_GUID,
93 &retain_protocol_attrs,
94 &retain_protocol_size,
95 &retain_protocol);
96 if (EFI_ERROR(efi_status) ||
97 (retain_protocol_attrs & EFI_VARIABLE_NON_VOLATILE) ||
98 !(retain_protocol_attrs & EFI_VARIABLE_BOOTSERVICE_ACCESS) ||
99 retain_protocol_size != sizeof(retain_protocol) ||
100 retain_protocol == 0)
101 uninstall_shim_protocols();
102 }
103 efi_status = BS->StartImage(image_handle, exit_data_size, exit_data);
104 if (EFI_ERROR(efi_status)) {
105 if (image_handle == last_loaded_image) {
106 EFI_STATUS efi_status2 = install_shim_protocols();
107
108 if (EFI_ERROR(efi_status2)) {
109 console_print(L"Something has gone seriously wrong: %r\n",
110 efi_status2);
111 console_print(L"shim cannot continue, sorry.\n");
112 usleep(5000000);
113 RT->ResetSystem(EfiResetShutdown,
114 EFI_SECURITY_VIOLATION,
115 0, NULL);
116 }
117 }
118 hook_system_services(systab);
119 loader_is_participating = 0;
120 }
121 return efi_status;
122 }
123
124 #if !defined(DISABLE_EBS_PROTECTION)
125 static EFI_STATUS EFIAPI
126 exit_boot_services(EFI_HANDLE image_key, UINTN map_key)
127 {
128 if (loader_is_participating ||
129 verification_method == VERIFIED_BY_HASH) {
130 unhook_system_services();
131 EFI_STATUS efi_status;
132 efi_status = BS->ExitBootServices(image_key, map_key);
133 if (EFI_ERROR(efi_status))
134 hook_system_services(systab);
135 return efi_status;
136 }
137
138 console_print(L"Bootloader has not verified loaded image.\n");
139 console_print(L"System is compromised. halting.\n");
140 usleep(5000000);
141 RT->ResetSystem(EfiResetShutdown, EFI_SECURITY_VIOLATION, 0, NULL);
142 return EFI_SECURITY_VIOLATION;
143 }
144 #endif /* !defined(DISABLE_EBS_PROTECTION) */
145
146 static EFI_STATUS EFIAPI
147 do_exit(EFI_HANDLE ImageHandle, EFI_STATUS ExitStatus,
148 UINTN ExitDataSize, CHAR16 *ExitData)
149 {
150 EFI_STATUS efi_status;
151
152 shim_fini();
153
154 restore_loaded_image();
155
156 efi_status = BS->Exit(ImageHandle, ExitStatus,
157 ExitDataSize, ExitData);
158 if (EFI_ERROR(efi_status)) {
159 EFI_STATUS efi_status2 = shim_init();
160
161 if (EFI_ERROR(efi_status2)) {
162 console_print(L"Something has gone seriously wrong: %r\n",
163 efi_status2);
164 console_print(L"shim cannot continue, sorry.\n");
165 usleep(5000000);
166 RT->ResetSystem(EfiResetShutdown,
167 EFI_SECURITY_VIOLATION, 0, NULL);
168 }
169 }
170 return efi_status;
171 }
172
173 void
174 hook_system_services(EFI_SYSTEM_TABLE *local_systab)
175 {
176 systab = local_systab;
177 BS = systab->BootServices;
178
179 /* We need to hook various calls to make this work... */
180
181 /* We need LoadImage() hooked so that fallback.c can load shim
182 * without having to fake LoadImage as well. This allows it
183 * to call the system LoadImage(), and have us track the output
184 * and mark loader_is_participating in replacement_start_image. This
185 * means anything added by fallback has to be verified by the system
186 * db, which we want to preserve anyway, since that's all launching
187 * through BDS gives us. */
188 system_load_image = systab->BootServices->LoadImage;
189 systab->BootServices->LoadImage = load_image;
190
191 /* we need StartImage() so that we can allow chain booting to an
192 * image trusted by the firmware */
193 system_start_image = systab->BootServices->StartImage;
194 systab->BootServices->StartImage = replacement_start_image;
195
196 #if !defined(DISABLE_EBS_PROTECTION)
197 /* we need to hook ExitBootServices() so a) we can enforce the policy
198 * and b) we can unwrap when we're done. */
199 system_exit_boot_services = systab->BootServices->ExitBootServices;
200 systab->BootServices->ExitBootServices = exit_boot_services;
201 #endif /* defined(DISABLE_EBS_PROTECTION) */
202 }
203
204 void
205 unhook_exit(void)
206 {
207 systab->BootServices->Exit = system_exit;
208 BS = systab->BootServices;
209 }
210
211 void
212 hook_exit(EFI_SYSTEM_TABLE *local_systab)
213 {
214 systab = local_systab;
215 BS = local_systab->BootServices;
216
217 /* we need to hook Exit() so that we can allow users to quit the
218 * bootloader and still e.g. start a new one or run an internal
219 * shell. */
220 system_exit = systab->BootServices->Exit;
221 systab->BootServices->Exit = do_exit;
222 }