iPXE
efi_shim.c
Go to the documentation of this file.
1/*
2 * Copyright (C) 2022 Michael Brown <mbrown@fensystems.co.uk>.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301, USA.
18 *
19 * You can also choose to distribute this program under the terms of
20 * the Unmodified Binary Distribution Licence (as given in the file
21 * COPYING.UBDL), provided that you have satisfied its requirements.
22 */
23
24FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25FILE_SECBOOT ( PERMITTED );
26
27#include <string.h>
28#include <stdlib.h>
29#include <errno.h>
30#include <ipxe/image.h>
31#include <ipxe/efi/efi.h>
33#include <ipxe/efi/efi_shim.h>
36
37/** @file
38 *
39 * UEFI shim special handling
40 *
41 */
42
43FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
44FILE_SECBOOT ( PERMITTED );
45
46/**
47 * Require use of a third party loader binary
48 *
49 * The UEFI shim is gradually becoming less capable of directly
50 * executing a Linux kernel image, due to an ever increasing list of
51 * assumptions that it will only ever be used in conjunction with a
52 * second stage loader binary such as GRUB.
53 *
54 * For example: shim will erroneously complain if the image that it
55 * loads and executes does not in turn call in to the "shim lock
56 * protocol" to verify a separate newly loaded binary before calling
57 * ExitBootServices(), even if no such separate binary is used or
58 * required.
59 *
60 * Experience shows that there is unfortunately no point in trying to
61 * get a fix for this upstreamed into shim. We therefore default to
62 * reducing the Secure Boot attack surface by removing, where
63 * possible, this spurious requirement for the use of an additional
64 * second stage loader.
65 *
66 * This option may be used to require the use of an additional second
67 * stage loader binary, in case this behaviour is ever desirable.
68 */
70
71/**
72 * Allow use of PXE base code protocol
73 *
74 * We provide shim with access to all of the relevant downloaded files
75 * via our EFI_SIMPLE_FILE_SYSTEM_PROTOCOL interface. However, shim
76 * will instead try to redownload the files via TFTP since it prefers
77 * to use the EFI_PXE_BASE_CODE_PROTOCOL installed on the same handle.
78 *
79 * Experience shows that there is unfortunately no point in trying to
80 * get a fix for this upstreamed into shim. We therefore default to
81 * working around this undesirable behaviour by stopping the PXE base
82 * code protocol before invoking shim.
83 *
84 * This option may be used to allow shim to use the PXE base code
85 * protocol, in case this behaviour is ever desirable.
86 */
88
89/**
90 * Allow SBAT variable access
91 *
92 * The UEFI shim implements a fairly nicely designed revocation
93 * mechanism designed around the concept of security generations.
94 * Unfortunately nobody in the shim community has thus far added the
95 * relevant metadata to the Linux kernel, with the result that current
96 * versions of shim are incapable of booting current versions of the
97 * Linux kernel.
98 *
99 * Experience shows that there is unfortunately no point in trying to
100 * get a fix for this upstreamed into shim. We therefore default to
101 * working around this undesirable behaviour by patching data read
102 * from the "SbatLevel" variable used to hold SBAT configuration.
103 *
104 * This option may be used to allow shim unpatched access to the
105 * "SbatLevel" variable, in case this behaviour is ever desirable.
106 */
108
109/** UEFI shim image */
110struct image_tag efi_shim __image_tag = {
111 .name = "SHIM",
112};
113
114/** Original GetMemoryMap() function */
116
117/** Original SetVariable() function */
119
120/** Original GetVariable() function */
122
123/** Verify read from SbatLevel variable */
125
126/**
127 * Check if variable is SbatLevel
128 *
129 * @v name Variable name
130 * @v guid Variable namespace GUID
131 * @ret is_sbatlevel Variable is SbatLevel
132 */
133static int efi_shim_is_sbatlevel ( const CHAR16 *name, const EFI_GUID *guid ) {
134 static CHAR16 sbatlevel[] = L"SbatLevel";
136
137 return ( ( memcmp ( name, sbatlevel, sizeof ( sbatlevel ) ) == 0 ) &&
138 ( memcmp ( guid, shimlock, sizeof ( *shimlock ) ) == 0 ) );
139}
140
141/**
142 * Unlock UEFI shim
143 *
144 */
145static void efi_shim_unlock ( void ) {
146 EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
149 uint8_t empty[0];
150 EFI_HANDLE *handles;
151 UINTN num_handles;
152 unsigned int i;
153 EFI_STATUS efirc;
154 int rc;
155
156 /* Locate shim lock protocol(s) */
157 if ( ( efirc = bs->LocateHandleBuffer ( ByProtocol, protocol,
158 NULL, &num_handles,
159 &handles ) ) != 0 ) {
160 rc = -EEFI ( efirc );
161 DBGC ( &efi_shim, "SHIM could not locate shim locks: %s\n",
162 strerror ( rc ) );
163 goto err_locate;
164 }
165
166 /* Unlock each shim lock */
167 for ( i = 0 ; i < num_handles ; i++ ) {
168
169 /* Open shim lock protocol */
170 if ( ( rc = efi_open ( handles[i], protocol, &lock ) ) != 0 ) {
171 DBGC ( &efi_shim, "SHIM could not open lock %d (%p): "
172 "%s\n", i, handles[i], strerror ( rc ) );
173 continue;
174 }
175
176 /* Unlock shim lock */
177 lock->Verify ( empty, sizeof ( empty ) );
178 DBGC ( &efi_shim, "SHIM unlocked lock %d (%p) via %p\n",
179 i, handles[i], lock );
180 }
181
182 bs->FreePool ( handles );
183 err_locate:
184 return;
185}
186
187/**
188 * Wrap SetVariable()
189 *
190 * @v name Variable name
191 * @v guid Variable namespace GUID
192 * @v attrs Attributes
193 * @v len Buffer size
194 * @v data Data buffer
195 * @ret efirc EFI status code
196 */
197static EFI_STATUS EFIAPI
199 UINTN len, VOID *data ) {
200 EFI_STATUS efirc;
201
202 /* Call original SetVariable() */
203 efirc = efi_shim_orig_set_variable ( name, guid, attrs, len, data );
204
205 /* Allow verification of SbatLevel variable content */
206 if ( efi_shim_is_sbatlevel ( name, guid ) && ( efirc == 0 ) ) {
207 DBGC ( &efi_shim, "SHIM detected write to %ls:\n", name );
208 DBGC_HDA ( &efi_shim, 0, data, len );
210 }
211
212 return efirc;
213}
214
215/**
216 * Wrap GetVariable()
217 *
218 * @v name Variable name
219 * @v guid Variable namespace GUID
220 * @v attrs Attributes to fill in
221 * @v len Buffer size
222 * @v data Data buffer
223 * @ret efirc EFI status code
224 */
225static EFI_STATUS EFIAPI
227 UINTN *len, VOID *data ) {
228 char *value = data;
229 EFI_STATUS efirc;
230
231 /* Call original GetVariable() */
232 efirc = efi_shim_orig_get_variable ( name, guid, attrs, len, data );
233
234 /* Patch SbatLevel variable if applicable */
235 if ( efi_shim_is_sbatlevel ( name, guid ) && data && ( efirc == 0 ) ) {
236 if ( efi_shim_allow_sbat ) {
237 DBGC ( &efi_shim, "SHIM allowing read from %ls:\n",
238 name );
239 } else if ( efi_shim_sbatlevel_verify ) {
240 DBGC ( &efi_shim, "SHIM allowing one read from %ls:\n",
241 name );
243 } else {
244 DBGC ( &efi_shim, "SHIM patching read from %ls:\n",
245 name );
246 value[0] = '\0';
247 }
248 DBGC_HDA ( &efi_shim, 0, data, *len );
249 }
250
251 return efirc;
252}
253
254/**
255 * Wrap GetMemoryMap()
256 *
257 * @v len Memory map size
258 * @v map Memory map
259 * @v key Memory map key
260 * @v desclen Descriptor size
261 * @v descver Descriptor version
262 * @ret efirc EFI status code
263 */
266 UINTN *key, UINTN *desclen,
267 UINT32 *descver ) {
268 EFI_RUNTIME_SERVICES *rs = efi_systab->RuntimeServices;
269
270 /* Unlock shim */
273
274 /* Uninstall runtime services wrappers, if still installed */
275 if ( rs->SetVariable == efi_shim_set_variable ) {
277 DBGC ( &efi_shim, "SHIM uninstalled SetVariable() wrapper\n" );
278 } else if ( rs->SetVariable != efi_shim_orig_set_variable ) {
279 DBGC ( &efi_shim, "SHIM could not uninstall SetVariable() "
280 "wrapper!\n" );
281 }
282 if ( rs->GetVariable == efi_shim_get_variable ) {
284 DBGC ( &efi_shim, "SHIM uninstalled GetVariable() wrapper\n" );
285 } else if ( rs->GetVariable != efi_shim_orig_get_variable ) {
286 DBGC ( &efi_shim, "SHIM could not uninstall GetVariable() "
287 "wrapper!\n" );
288 }
289
290 /* Hand off to original GetMemoryMap() */
291 return efi_shim_orig_get_memory_map ( len, map, key, desclen,
292 descver );
293}
294
295/**
296 * Inhibit use of PXE base code
297 *
298 * @v handle EFI handle
299 * @ret rc Return status code
300 */
303 EFI_STATUS efirc;
304 int rc;
305
306 /* Locate PXE base code */
308 &pxe ) ) != 0 ) {
309 DBGC ( &efi_shim, "SHIM could not open PXE base code: %s\n",
310 strerror ( rc ) );
311 return rc;
312 }
313
314 /* Stop PXE base code */
315 if ( ( efirc = pxe->Stop ( pxe ) ) != 0 ) {
316 rc = -EEFI ( efirc );
317 DBGC ( &efi_shim, "SHIM could not stop PXE base code: %s\n",
318 strerror ( rc ) );
319 return rc;
320 }
321
322 DBGC ( &efi_shim, "SHIM stopped PXE base code\n" );
323 return 0;
324}
325
326/**
327 * Update command line
328 *
329 * @v shim Shim image
330 * @v cmdline Command line to update
331 * @ret rc Return status code
332 */
333static int efi_shim_cmdline ( struct image *shim, wchar_t **cmdline ) {
334 wchar_t *shimcmdline;
335 int len;
336 int rc;
337
338 /* Construct new command line */
339 len = ( shim->cmdline ?
340 efi_asprintf ( &shimcmdline, "%s %s", shim->name,
341 shim->cmdline ) :
342 efi_asprintf ( &shimcmdline, "%s %ls", shim->name,
343 *cmdline ) );
344 if ( len < 0 ) {
345 rc = len;
346 DBGC ( &efi_shim, "SHIM could not construct command line: "
347 "%s\n", strerror ( rc ) );
348 return rc;
349 }
350
351 /* Replace command line */
352 free ( *cmdline );
353 *cmdline = shimcmdline;
354
355 return 0;
356}
357
358/**
359 * Install UEFI shim special handling
360 *
361 * @v shim Shim image
362 * @v handle EFI device handle
363 * @v cmdline Command line to update
364 * @ret rc Return status code
365 */
367 wchar_t **cmdline ) {
368 EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
369 EFI_RUNTIME_SERVICES *rs = efi_systab->RuntimeServices;
370 int rc;
371
372 /* Stop PXE base code */
373 if ( ( ! efi_shim_allow_pxe ) &&
374 ( ( rc = efi_shim_inhibit_pxe ( handle ) ) != 0 ) ) {
375 return rc;
376 }
377
378 /* Update command line */
379 if ( ( rc = efi_shim_cmdline ( shim, cmdline ) ) != 0 )
380 return rc;
381
382 /* Record original boot and runtime services functions */
386
387 /* Wrap relevant boot and runtime services functions */
391 DBGC ( &efi_shim, "SHIM installed wrappers\n" );
392
393 return 0;
394}
395
396/**
397 * Uninstall UEFI shim special handling
398 *
399 */
400void efi_shim_uninstall ( void ) {
401 EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
402 EFI_RUNTIME_SERVICES *rs = efi_systab->RuntimeServices;
403
404 /* Restore original boot and runtime services functions */
408 DBGC ( &efi_shim, "SHIM uninstalled wrappers\n" );
409}
UINT64 UINTN
Unsigned value of native width.
unsigned short CHAR16
2-byte Character.
#define EFIAPI
unsigned int UINT32
4-byte unsigned value.
#define NULL
NULL pointer (VOID *)
Definition Base.h:322
#define VOID
Undeclared type.
Definition Base.h:272
union @162305117151260234136356364136041353210355154177 key
Sense key.
Definition scsi.h:3
EFI PXE Base Code Protocol definitions, which is used to access PXE-compatible devices for network ac...
struct _EFI_PXE_BASE_CODE_PROTOCOL EFI_PXE_BASE_CODE_PROTOCOL
Definition PxeBaseCode.h:30
EFI "shim lock" protocol.
struct _EFI_SHIM_LOCK_PROTOCOL EFI_SHIM_LOCK_PROTOCOL
RETURN_STATUS EFI_STATUS
Function return status for EFI API.
GUID EFI_GUID
128-bit buffer containing a unique identifier value.
EFI_STATUS(EFIAPI * EFI_GET_VARIABLE)(IN CHAR16 *VariableName, IN EFI_GUID *VendorGuid, OUT UINT32 *Attributes OPTIONAL, IN OUT UINTN *DataSize, OUT VOID *Data OPTIONAL)
Returns the value of a variable.
Definition UefiSpec.h:709
EFI_STATUS(EFIAPI * EFI_GET_MEMORY_MAP)(IN OUT UINTN *MemoryMapSize, OUT EFI_MEMORY_DESCRIPTOR *MemoryMap, OUT UINTN *MapKey, OUT UINTN *DescriptorSize, OUT UINT32 *DescriptorVersion)
Returns the current memory map.
Definition UefiSpec.h:269
EFI_STATUS(EFIAPI * EFI_SET_VARIABLE)(IN CHAR16 *VariableName, IN EFI_GUID *VendorGuid, IN UINT32 Attributes, IN UINTN DataSize, IN VOID *Data)
Sets the value of a variable.
Definition UefiSpec.h:794
@ ByProtocol
Retrieve the set of handles from the handle database that support a specified protocol.
Definition UefiSpec.h:1531
struct arbelprm_rc_send_wqe rc
Definition arbel.h:3
pseudo_bit_t value[0x00020]
Definition arbel.h:2
unsigned char uint8_t
Definition stdint.h:10
const char * name
Definition ath9k_hw.c:1986
ring len
Length.
Definition dwmac.h:226
uint64_t guid
GUID.
Definition edd.h:1
EFI_GUID efi_shim_lock_protocol_guid
Shim lock protocol GUID.
Definition efi_guid.c:333
EFI_GUID efi_pxe_base_code_protocol_guid
PXE base code protocol GUID.
Definition efi_guid.c:321
static int efi_shim_sbatlevel_verify
Verify read from SbatLevel variable.
Definition efi_shim.c:124
static void efi_shim_unlock(void)
Unlock UEFI shim.
Definition efi_shim.c:145
static int efi_shim_cmdline(struct image *shim, wchar_t **cmdline)
Update command line.
Definition efi_shim.c:333
static EFI_SET_VARIABLE efi_shim_orig_set_variable
Original SetVariable() function.
Definition efi_shim.c:118
int efi_shim_allow_pxe
Allow use of PXE base code protocol.
Definition efi_shim.c:87
static EFI_GET_MEMORY_MAP efi_shim_orig_get_memory_map
Original GetMemoryMap() function.
Definition efi_shim.c:115
static EFI_STATUS EFIAPI efi_shim_get_variable(CHAR16 *name, EFI_GUID *guid, UINT32 *attrs, UINTN *len, VOID *data)
Wrap GetVariable()
Definition efi_shim.c:226
int efi_shim_require_loader
Require use of a third party loader binary.
Definition efi_shim.c:69
void efi_shim_uninstall(void)
Uninstall UEFI shim special handling.
Definition efi_shim.c:400
int efi_shim_allow_sbat
Allow SBAT variable access.
Definition efi_shim.c:107
int efi_shim_install(struct image *shim, EFI_HANDLE handle, wchar_t **cmdline)
Install UEFI shim special handling.
Definition efi_shim.c:366
static int efi_shim_inhibit_pxe(EFI_HANDLE handle)
Inhibit use of PXE base code.
Definition efi_shim.c:301
static EFIAPI EFI_STATUS efi_shim_get_memory_map(UINTN *len, EFI_MEMORY_DESCRIPTOR *map, UINTN *key, UINTN *desclen, UINT32 *descver)
Wrap GetMemoryMap()
Definition efi_shim.c:264
static EFI_STATUS EFIAPI efi_shim_set_variable(CHAR16 *name, EFI_GUID *guid, UINT32 attrs, UINTN len, VOID *data)
Wrap SetVariable()
Definition efi_shim.c:198
static EFI_GET_VARIABLE efi_shim_orig_get_variable
Original GetVariable() function.
Definition efi_shim.c:121
static int efi_shim_is_sbatlevel(const CHAR16 *name, const EFI_GUID *guid)
Check if variable is SbatLevel.
Definition efi_shim.c:133
UEFI shim special handling.
int efi_asprintf(wchar_t **wstrp, const char *fmt,...)
Write a formatted string to newly allocated memory.
EFI strings.
uint8_t data[48]
Additional event data.
Definition ena.h:11
Error codes.
#define DBGC(...)
Definition compiler.h:505
#define DBGC_HDA(...)
Definition compiler.h:506
#define FILE_LICENCE(_licence)
Declare a particular licence as applying to a file.
Definition compiler.h:896
#define FILE_SECBOOT(_status)
Declare a file's UEFI Secure Boot permission status.
Definition compiler.h:926
Executable images.
#define __image_tag
An image tag.
Definition image.h:184
EFI API.
#define efi_open(handle, protocol, interface)
Open protocol for ephemeral use.
Definition efi.h:452
#define EFI_HANDLE
Definition efi.h:53
#define EEFI(efirc)
Convert an EFI status code to an iPXE status code.
Definition efi.h:181
EFI_SYSTEM_TABLE * efi_systab
uint16_t handle
Handle.
Definition smbios.h:5
String functions.
static __always_inline int struct dma_mapping * map
Definition dma.h:184
uint32_t cmdline
Definition multiboot.h:4
static void(* free)(struct refcnt *refcnt))
Definition refcnt.h:55
int shim(struct image *image, int require_loader, int allow_pxe, int allow_sbat)
Set shim image.
Definition shimmgmt.c:46
uint16_t protocol
Protocol ID.
Definition stp.h:7
char * strerror(int errno)
Retrieve string representation of error number.
Definition strerror.c:79
int memcmp(const void *first, const void *second, size_t len)
Compare memory regions.
Definition string.c:115
EFI Boot Services Table.
Definition UefiSpec.h:1931
EFI_FREE_POOL FreePool
Definition UefiSpec.h:1950
EFI_GET_MEMORY_MAP GetMemoryMap
Definition UefiSpec.h:1948
EFI_LOCATE_HANDLE_BUFFER LocateHandleBuffer
Definition UefiSpec.h:2008
Definition of an EFI memory descriptor.
Definition UefiSpec.h:156
EFI Runtime Services Table.
Definition UefiSpec.h:1880
EFI_SET_VARIABLE SetVariable
Definition UefiSpec.h:1905
EFI_GET_VARIABLE GetVariable
Definition UefiSpec.h:1903
EFI_PXE_BASE_CODE_STOP Stop
EFI_SHIM_LOCK_VERIFY Verify
Definition ShimLock.h:27
An image tag.
Definition image.h:173
An executable image.
Definition image.h:24