1 // SPDX-License-Identifier: BSD-2-Clause-Patent
3 * shim - trivial UEFI first-stage bootloader
5 * Copyright Red Hat, Inc
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,
23 static EFI_SYSTEM_TABLE
*systab
;
26 get_active_systab(void)
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) */
40 static EFI_HANDLE last_loaded_image
;
43 unhook_system_services(void)
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
;
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
)
61 EFI_STATUS efi_status
;
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
;
70 last_loaded_image
= *ImageHandle
;
74 static EFI_STATUS EFIAPI
75 replacement_start_image(EFI_HANDLE image_handle
, UINTN
*exit_data_size
, CHAR16
**exit_data
)
77 EFI_STATUS efi_status
;
78 unhook_system_services();
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;
85 loader_is_participating
= 1;
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
,
93 &retain_protocol_attrs
,
94 &retain_protocol_size
,
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();
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();
108 if (EFI_ERROR(efi_status2
)) {
109 console_print(L
"Something has gone seriously wrong: %r\n",
111 console_print(L
"shim cannot continue, sorry.\n");
113 RT
->ResetSystem(EfiResetShutdown
,
114 EFI_SECURITY_VIOLATION
,
118 hook_system_services(systab
);
119 loader_is_participating
= 0;
124 #if !defined(DISABLE_EBS_PROTECTION)
125 static EFI_STATUS EFIAPI
126 exit_boot_services(EFI_HANDLE image_key
, UINTN map_key
)
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
);
138 console_print(L
"Bootloader has not verified loaded image.\n");
139 console_print(L
"System is compromised. halting.\n");
141 RT
->ResetSystem(EfiResetShutdown
, EFI_SECURITY_VIOLATION
, 0, NULL
);
142 return EFI_SECURITY_VIOLATION
;
144 #endif /* !defined(DISABLE_EBS_PROTECTION) */
146 static EFI_STATUS EFIAPI
147 do_exit(EFI_HANDLE ImageHandle
, EFI_STATUS ExitStatus
,
148 UINTN ExitDataSize
, CHAR16
*ExitData
)
150 EFI_STATUS efi_status
;
154 restore_loaded_image();
156 efi_status
= BS
->Exit(ImageHandle
, ExitStatus
,
157 ExitDataSize
, ExitData
);
158 if (EFI_ERROR(efi_status
)) {
159 EFI_STATUS efi_status2
= shim_init();
161 if (EFI_ERROR(efi_status2
)) {
162 console_print(L
"Something has gone seriously wrong: %r\n",
164 console_print(L
"shim cannot continue, sorry.\n");
166 RT
->ResetSystem(EfiResetShutdown
,
167 EFI_SECURITY_VIOLATION
, 0, NULL
);
174 hook_system_services(EFI_SYSTEM_TABLE
*local_systab
)
176 systab
= local_systab
;
177 BS
= systab
->BootServices
;
179 /* We need to hook various calls to make this work... */
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
;
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
;
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) */
207 systab
->BootServices
->Exit
= system_exit
;
208 BS
= systab
->BootServices
;
212 hook_exit(EFI_SYSTEM_TABLE
*local_systab
)
214 systab
= local_systab
;
215 BS
= local_systab
->BootServices
;
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
220 system_exit
= systab
->BootServices
->Exit
;
221 systab
->BootServices
->Exit
= do_exit
;