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 
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25 
26 #include <string.h>
27 #include <stdlib.h>
28 #include <errno.h>
29 #include <ipxe/image.h>
30 #include <ipxe/efi/efi.h>
31 #include <ipxe/efi/efi_strings.h>
32 #include <ipxe/efi/efi_shim.h>
35 
36 /** @file
37  *
38  * UEFI shim special handling
39  *
40  */
41 
42 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
43 
44 /**
45  * Require use of a third party loader binary
46  *
47  * The UEFI shim is gradually becoming less capable of directly
48  * executing a Linux kernel image, due to an ever increasing list of
49  * assumptions that it will only ever be used in conjunction with a
50  * second stage loader binary such as GRUB.
51  *
52  * For example: shim will erroneously complain if the image that it
53  * loads and executes does not in turn call in to the "shim lock
54  * protocol" to verify a separate newly loaded binary before calling
55  * ExitBootServices(), even if no such separate binary is used or
56  * required.
57  *
58  * Experience shows that there is unfortunately no point in trying to
59  * get a fix for this upstreamed into shim. We therefore default to
60  * reducing the Secure Boot attack surface by removing, where
61  * possible, this spurious requirement for the use of an additional
62  * second stage loader.
63  *
64  * This option may be used to require the use of an additional second
65  * stage loader binary, in case this behaviour is ever desirable.
66  */
68 
69 /**
70  * Allow use of PXE base code protocol
71  *
72  * We provide shim with access to all of the relevant downloaded files
73  * via our EFI_SIMPLE_FILE_SYSTEM_PROTOCOL interface. However, shim
74  * will instead try to redownload the files via TFTP since it prefers
75  * to use the EFI_PXE_BASE_CODE_PROTOCOL installed on the same handle.
76  *
77  * Experience shows that there is unfortunately no point in trying to
78  * get a fix for this upstreamed into shim. We therefore default to
79  * working around this undesirable behaviour by stopping the PXE base
80  * code protocol before invoking shim.
81  *
82  * This option may be used to allow shim to use the PXE base code
83  * protocol, in case this behaviour is ever desirable.
84  */
86 
87 /**
88  * Allow SBAT variable access
89  *
90  * The UEFI shim implements a fairly nicely designed revocation
91  * mechanism designed around the concept of security generations.
92  * Unfortunately nobody in the shim community has thus far added the
93  * relevant metadata to the Linux kernel, with the result that current
94  * versions of shim are incapable of booting current versions of the
95  * Linux kernel.
96  *
97  * Experience shows that there is unfortunately no point in trying to
98  * get a fix for this upstreamed into shim. We therefore default to
99  * working around this undesirable behaviour by patching data read
100  * from the "SbatLevel" variable used to hold SBAT configuration.
101  *
102  * This option may be used to allow shim unpatched access to the
103  * "SbatLevel" variable, in case this behaviour is ever desirable.
104  */
106 
107 /** UEFI shim image */
108 struct image_tag efi_shim __image_tag = {
109  .name = "SHIM",
110 };
111 
112 /** Original GetMemoryMap() function */
114 
115 /** Original ExitBootServices() function */
117 
118 /** Original SetVariable() function */
120 
121 /** Original GetVariable() function */
123 
124 /** Verify read from SbatLevel variable */
126 
127 /**
128  * Check if variable is SbatLevel
129  *
130  * @v name Variable name
131  * @v guid Variable namespace GUID
132  * @ret is_sbatlevel Variable is SbatLevel
133  */
134 static int efi_shim_is_sbatlevel ( const CHAR16 *name, const EFI_GUID *guid ) {
135  static CHAR16 sbatlevel[] = L"SbatLevel";
137 
138  return ( ( memcmp ( name, sbatlevel, sizeof ( sbatlevel ) ) == 0 ) &&
139  ( memcmp ( guid, shimlock, sizeof ( *shimlock ) ) == 0 ) );
140 }
141 
142 /**
143  * Unlock UEFI shim
144  *
145  */
146 static void efi_shim_unlock ( void ) {
148  uint8_t empty[0];
149  union {
151  void *interface;
152  } u;
153  EFI_STATUS efirc;
154 
155  /* Locate shim lock protocol */
156  if ( ( efirc = bs->LocateProtocol ( &efi_shim_lock_protocol_guid,
157  NULL, &u.interface ) ) == 0 ) {
158  u.lock->Verify ( empty, sizeof ( empty ) );
159  DBGC ( &efi_shim, "SHIM unlocked via %p\n", u.lock );
160  }
161 }
162 
163 /**
164  * Wrap GetMemoryMap()
165  *
166  * @v len Memory map size
167  * @v map Memory map
168  * @v key Memory map key
169  * @v desclen Descriptor size
170  * @v descver Descriptor version
171  * @ret efirc EFI status code
172  */
175  UINTN *key, UINTN *desclen,
176  UINT32 *descver ) {
177 
178  /* Unlock shim */
179  if ( ! efi_shim_require_loader )
180  efi_shim_unlock();
181 
182  /* Hand off to original GetMemoryMap() */
183  return efi_shim_orig_get_memory_map ( len, map, key, desclen,
184  descver );
185 }
186 
187 /**
188  * Wrap ExitBootServices()
189  *
190  * @v handle Image handle
191  * @v key Memory map key
192  * @ret efirc EFI status code
193  */
195  UINTN key ) {
197 
198  /* Restore original runtime services functions */
201 
202  /* Hand off to original ExitBootServices() */
204 }
205 
206 /**
207  * Wrap SetVariable()
208  *
209  * @v name Variable name
210  * @v guid Variable namespace GUID
211  * @v attrs Attributes
212  * @v len Buffer size
213  * @v data Data buffer
214  * @ret efirc EFI status code
215  */
216 static EFI_STATUS EFIAPI
218  UINTN len, VOID *data ) {
219  EFI_STATUS efirc;
220 
221  /* Call original SetVariable() */
223 
224  /* Allow verification of SbatLevel variable content */
225  if ( efi_shim_is_sbatlevel ( name, guid ) && ( efirc == 0 ) ) {
226  DBGC ( &efi_shim, "SHIM detected write to %ls:\n", name );
227  DBGC_HDA ( &efi_shim, 0, data, len );
229  }
230 
231  return efirc;
232 }
233 
234 /**
235  * Wrap GetVariable()
236  *
237  * @v name Variable name
238  * @v guid Variable namespace GUID
239  * @v attrs Attributes to fill in
240  * @v len Buffer size
241  * @v data Data buffer
242  * @ret efirc EFI status code
243  */
244 static EFI_STATUS EFIAPI
246  UINTN *len, VOID *data ) {
247  char *value = data;
248  EFI_STATUS efirc;
249 
250  /* Call original GetVariable() */
252 
253  /* Patch SbatLevel variable if applicable */
254  if ( efi_shim_is_sbatlevel ( name, guid ) && data && ( efirc == 0 ) ) {
255  if ( efi_shim_allow_sbat ) {
256  DBGC ( &efi_shim, "SHIM allowing read from %ls:\n",
257  name );
258  } else if ( efi_shim_sbatlevel_verify ) {
259  DBGC ( &efi_shim, "SHIM allowing one read from %ls:\n",
260  name );
262  } else {
263  DBGC ( &efi_shim, "SHIM patching read from %ls:\n",
264  name );
265  value[0] = '\0';
266  }
267  DBGC_HDA ( &efi_shim, 0, data, *len );
268  }
269 
270  return efirc;
271 }
272 
273 /**
274  * Inhibit use of PXE base code
275  *
276  * @v handle EFI handle
277  * @ret rc Return status code
278  */
281  union {
283  void *interface;
284  } u;
285  EFI_STATUS efirc;
286  int rc;
287 
288  /* Locate PXE base code */
289  if ( ( efirc = bs->OpenProtocol ( handle,
291  &u.interface, efi_image_handle, NULL,
293  rc = -EEFI ( efirc );
294  DBGC ( &efi_shim, "SHIM could not open PXE base code: %s\n",
295  strerror ( rc ) );
296  goto err_no_base;
297  }
298 
299  /* Stop PXE base code */
300  if ( ( efirc = u.pxe->Stop ( u.pxe ) ) != 0 ) {
301  rc = -EEFI ( efirc );
302  DBGC ( &efi_shim, "SHIM could not stop PXE base code: %s\n",
303  strerror ( rc ) );
304  goto err_stop;
305  }
306 
307  /* Success */
308  rc = 0;
309  DBGC ( &efi_shim, "SHIM stopped PXE base code\n" );
310 
311  err_stop:
314  err_no_base:
315  return rc;
316 }
317 
318 /**
319  * Update command line
320  *
321  * @v shim Shim image
322  * @v cmdline Command line to update
323  * @ret rc Return status code
324  */
325 static int efi_shim_cmdline ( struct image *shim, wchar_t **cmdline ) {
326  wchar_t *shimcmdline;
327  int len;
328  int rc;
329 
330  /* Construct new command line */
331  len = ( shim->cmdline ?
332  efi_asprintf ( &shimcmdline, "%s %s", shim->name,
333  shim->cmdline ) :
334  efi_asprintf ( &shimcmdline, "%s %ls", shim->name,
335  *cmdline ) );
336  if ( len < 0 ) {
337  rc = len;
338  DBGC ( &efi_shim, "SHIM could not construct command line: "
339  "%s\n", strerror ( rc ) );
340  return rc;
341  }
342 
343  /* Replace command line */
344  free ( *cmdline );
345  *cmdline = shimcmdline;
346 
347  return 0;
348 }
349 
350 /**
351  * Install UEFI shim special handling
352  *
353  * @v shim Shim image
354  * @v handle EFI device handle
355  * @v cmdline Command line to update
356  * @ret rc Return status code
357  */
359  wchar_t **cmdline ) {
362  int rc;
363 
364  /* Stop PXE base code */
365  if ( ( ! efi_shim_allow_pxe ) &&
366  ( ( rc = efi_shim_inhibit_pxe ( handle ) ) != 0 ) ) {
367  return rc;
368  }
369 
370  /* Update command line */
371  if ( ( rc = efi_shim_cmdline ( shim, cmdline ) ) != 0 )
372  return rc;
373 
374  /* Record original boot and runtime services functions */
379 
380  /* Wrap relevant boot and runtime services functions */
385 
386  return 0;
387 }
388 
389 /**
390  * Uninstall UEFI shim special handling
391  *
392  */
393 void efi_shim_uninstall ( void ) {
396 
397  /* Restore original boot and runtime services functions */
402 }
int efi_shim_allow_sbat
Allow SBAT variable access.
Definition: efi_shim.c:105
EFI_BOOT_SERVICES * BootServices
A pointer to the EFI Boot Services Table.
Definition: UefiSpec.h:2030
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:243
struct arbelprm_rc_send_wqe rc
Definition: arbel.h:14
const char * name
Definition: ath9k_hw.c:1984
static void efi_shim_unlock(void)
Unlock UEFI shim.
Definition: efi_shim.c:146
#define EEFI(efirc)
Convert an EFI status code to an iPXE status code.
Definition: efi.h:171
An image tag.
Definition: image.h:154
EFI_LOCATE_PROTOCOL LocateProtocol
Definition: UefiSpec.h:1944
Definition of an EFI memory descriptor.
Definition: UefiSpec.h:130
128 bit buffer containing a unique identifier value.
Definition: Base.h:215
Error codes.
EFI_EXIT_BOOT_SERVICES ExitBootServices
Definition: UefiSpec.h:1917
EFI strings.
struct image_tag efi_shim __image_tag
UEFI shim image.
Definition: efi_shim.c:108
int efi_shim_install(struct image *shim, EFI_HANDLE handle, wchar_t **cmdline)
Install UEFI shim special handling.
Definition: efi_shim.c:358
#define DBGC(...)
Definition: compiler.h:505
unsigned int UINT32
Definition: ProcessorBind.h:98
static EFI_SET_VARIABLE efi_shim_orig_set_variable
Original SetVariable() function.
Definition: efi_shim.c:119
unsigned short CHAR16
EFI_GUID efi_pxe_base_code_protocol_guid
PXE base code protocol GUID.
Definition: efi_guid.c:291
EFI PXE Base Code Protocol definitions, which is used to access PXE-compatible devices for network ac...
An executable image.
Definition: image.h:24
EFI_CLOSE_PROTOCOL CloseProtocol
Definition: UefiSpec.h:1936
static EFI_GET_MEMORY_MAP efi_shim_orig_get_memory_map
Original GetMemoryMap() function.
Definition: efi_shim.c:113
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:173
int efi_shim_require_loader
Require use of a third party loader binary.
Definition: efi_shim.c:67
UEFI shim special handling.
static int efi_shim_cmdline(struct image *shim, wchar_t **cmdline)
Update command line.
Definition: efi_shim.c:325
An object interface.
Definition: interface.h:124
int efi_shim_allow_pxe
Allow use of PXE base code protocol.
Definition: efi_shim.c:85
Executable images.
#define DBGC_HDA(...)
Definition: compiler.h:506
EFI_SET_VARIABLE SetVariable
Definition: UefiSpec.h:1840
EFI_GET_VARIABLE GetVariable
Definition: UefiSpec.h:1838
uint32_t attrs
Extended attributes (optional)
Definition: memmap.c:32
#define EFI_OPEN_PROTOCOL_GET_PROTOCOL
Definition: UefiSpec.h:1299
pseudo_bit_t value[0x00020]
Definition: arbel.h:13
EFI Runtime Services Table.
Definition: UefiSpec.h:1815
char * strerror(int errno)
Retrieve string representation of error number.
Definition: strerror.c:78
static void(* free)(struct refcnt *refcnt))
Definition: refcnt.h:54
static int efi_shim_is_sbatlevel(const CHAR16 *name, const EFI_GUID *guid)
Check if variable is SbatLevel.
Definition: efi_shim.c:134
#define EFIAPI
static EFI_EXIT_BOOT_SERVICES efi_shim_orig_exit_boot_services
Original ExitBootServices() function.
Definition: efi_shim.c:116
EFI Boot Services Table.
Definition: UefiSpec.h:1866
EFI_HANDLE efi_image_handle
Image handle passed to entry point.
Definition: efi_init.c:34
unsigned char uint8_t
Definition: stdint.h:10
UINT64 UINTN
Unsigned value of native width.
static EFIAPI EFI_STATUS efi_shim_exit_boot_services(EFI_HANDLE handle, UINTN key)
Wrap ExitBootServices()
Definition: efi_shim.c:194
static __always_inline int struct dma_mapping * map
Definition: dma.h:181
#define VOID
Undeclared type.
Definition: Base.h:271
FILE_LICENCE(GPL2_OR_LATER_OR_UBDL)
EFI API.
uint64_t guid
GUID.
Definition: edd.h:30
EFI "shim lock" protocol.
EFI_GET_MEMORY_MAP GetMemoryMap
Definition: UefiSpec.h:1883
static EFI_GET_VARIABLE efi_shim_orig_get_variable
Original GetVariable() function.
Definition: efi_shim.c:122
EFI_RUNTIME_SERVICES * RuntimeServices
A pointer to the EFI Runtime Services Table.
Definition: UefiSpec.h:2026
uint32_t len
Length.
Definition: ena.h:14
RETURN_STATUS EFI_STATUS
Function return status for EFI API.
Definition: UefiBaseType.h:31
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:245
void efi_shim_uninstall(void)
Uninstall UEFI shim special handling.
Definition: efi_shim.c:393
union @17 u
uint8_t data[48]
Additional event data.
Definition: ena.h:22
EFI_GUID efi_shim_lock_protocol_guid
Shim lock protocol GUID.
Definition: efi_guid.c:299
EFI_SYSTEM_TABLE * efi_systab
EFI_OPEN_PROTOCOL OpenProtocol
Definition: UefiSpec.h:1935
EFI_STATUS(EFIAPI * EFI_EXIT_BOOT_SERVICES)(IN EFI_HANDLE ImageHandle, IN UINTN MapKey)
Terminates all boot services.
Definition: UefiSpec.h:987
The EFI_PXE_BASE_CODE_PROTOCOL is used to control PXE-compatible devices.
Definition: PxeBaseCode.h:908
int efi_asprintf(wchar_t **wstrp, const char *fmt,...)
Write a formatted string to newly allocated memory.
Definition: efi_strings.c:188
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:673
uint32_t cmdline
Definition: multiboot.h:16
uint16_t handle
Handle.
Definition: smbios.h:16
int memcmp(const void *first, const void *second, size_t len)
Compare memory regions.
Definition: string.c:114
int shim(struct image *image, int require_loader, int allow_pxe, int allow_sbat)
Set shim image.
Definition: shimmgmt.c:45
#define NULL
NULL pointer (VOID *)
Definition: Base.h:321
String functions.
const char * name
Name.
Definition: image.h:156
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:217
static int efi_shim_sbatlevel_verify
Verify read from SbatLevel variable.
Definition: efi_shim.c:125
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:751
union @382 key
Sense key.
Definition: scsi.h:18
static int efi_shim_inhibit_pxe(EFI_HANDLE handle)
Inhibit use of PXE base code.
Definition: efi_shim.c:279
Definition: efi.h:59