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 FILE_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>
32 #include <ipxe/efi/efi_strings.h>
33 #include <ipxe/efi/efi_shim.h>
36 
37 /** @file
38  *
39  * UEFI shim special handling
40  *
41  */
42 
43 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
44 FILE_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 */
110 struct 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  */
133 static 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  */
145 static void efi_shim_unlock ( void ) {
147  uint8_t empty[0];
148  union {
150  void *interface;
151  } u;
152  EFI_STATUS efirc;
153 
154  /* Locate shim lock protocol */
155  if ( ( efirc = bs->LocateProtocol ( &efi_shim_lock_protocol_guid,
156  NULL, &u.interface ) ) == 0 ) {
157  u.lock->Verify ( empty, sizeof ( empty ) );
158  DBGC ( &efi_shim, "SHIM unlocked via %p\n", u.lock );
159  }
160 }
161 
162 /**
163  * Wrap SetVariable()
164  *
165  * @v name Variable name
166  * @v guid Variable namespace GUID
167  * @v attrs Attributes
168  * @v len Buffer size
169  * @v data Data buffer
170  * @ret efirc EFI status code
171  */
172 static EFI_STATUS EFIAPI
174  UINTN len, VOID *data ) {
175  EFI_STATUS efirc;
176 
177  /* Call original SetVariable() */
178  efirc = efi_shim_orig_set_variable ( name, guid, attrs, len, data );
179 
180  /* Allow verification of SbatLevel variable content */
181  if ( efi_shim_is_sbatlevel ( name, guid ) && ( efirc == 0 ) ) {
182  DBGC ( &efi_shim, "SHIM detected write to %ls:\n", name );
183  DBGC_HDA ( &efi_shim, 0, data, len );
185  }
186 
187  return efirc;
188 }
189 
190 /**
191  * Wrap GetVariable()
192  *
193  * @v name Variable name
194  * @v guid Variable namespace GUID
195  * @v attrs Attributes to fill in
196  * @v len Buffer size
197  * @v data Data buffer
198  * @ret efirc EFI status code
199  */
200 static EFI_STATUS EFIAPI
202  UINTN *len, VOID *data ) {
203  char *value = data;
204  EFI_STATUS efirc;
205 
206  /* Call original GetVariable() */
207  efirc = efi_shim_orig_get_variable ( name, guid, attrs, len, data );
208 
209  /* Patch SbatLevel variable if applicable */
210  if ( efi_shim_is_sbatlevel ( name, guid ) && data && ( efirc == 0 ) ) {
211  if ( efi_shim_allow_sbat ) {
212  DBGC ( &efi_shim, "SHIM allowing read from %ls:\n",
213  name );
214  } else if ( efi_shim_sbatlevel_verify ) {
215  DBGC ( &efi_shim, "SHIM allowing one read from %ls:\n",
216  name );
218  } else {
219  DBGC ( &efi_shim, "SHIM patching read from %ls:\n",
220  name );
221  value[0] = '\0';
222  }
223  DBGC_HDA ( &efi_shim, 0, data, *len );
224  }
225 
226  return efirc;
227 }
228 
229 /**
230  * Wrap GetMemoryMap()
231  *
232  * @v len Memory map size
233  * @v map Memory map
234  * @v key Memory map key
235  * @v desclen Descriptor size
236  * @v descver Descriptor version
237  * @ret efirc EFI status code
238  */
241  UINTN *key, UINTN *desclen,
242  UINT32 *descver ) {
244 
245  /* Unlock shim */
246  if ( ! efi_shim_require_loader )
247  efi_shim_unlock();
248 
249  /* Uninstall runtime services wrappers, if still installed */
250  if ( rs->SetVariable == efi_shim_set_variable ) {
252  DBGC ( &efi_shim, "SHIM uninstalled SetVariable() wrapper\n" );
253  } else if ( rs->SetVariable != efi_shim_orig_set_variable ) {
254  DBGC ( &efi_shim, "SHIM could not uninstall SetVariable() "
255  "wrapper!\n" );
256  }
257  if ( rs->GetVariable == efi_shim_get_variable ) {
259  DBGC ( &efi_shim, "SHIM uninstalled GetVariable() wrapper\n" );
260  } else if ( rs->GetVariable != efi_shim_orig_get_variable ) {
261  DBGC ( &efi_shim, "SHIM could not uninstall GetVariable() "
262  "wrapper!\n" );
263  }
264 
265  /* Hand off to original GetMemoryMap() */
266  return efi_shim_orig_get_memory_map ( len, map, key, desclen,
267  descver );
268 }
269 
270 /**
271  * Inhibit use of PXE base code
272  *
273  * @v handle EFI handle
274  * @ret rc Return status code
275  */
278  EFI_STATUS efirc;
279  int rc;
280 
281  /* Locate PXE base code */
283  &pxe ) ) != 0 ) {
284  DBGC ( &efi_shim, "SHIM could not open PXE base code: %s\n",
285  strerror ( rc ) );
286  return rc;
287  }
288 
289  /* Stop PXE base code */
290  if ( ( efirc = pxe->Stop ( pxe ) ) != 0 ) {
291  rc = -EEFI ( efirc );
292  DBGC ( &efi_shim, "SHIM could not stop PXE base code: %s\n",
293  strerror ( rc ) );
294  return rc;
295  }
296 
297  DBGC ( &efi_shim, "SHIM stopped PXE base code\n" );
298  return 0;
299 }
300 
301 /**
302  * Update command line
303  *
304  * @v shim Shim image
305  * @v cmdline Command line to update
306  * @ret rc Return status code
307  */
308 static int efi_shim_cmdline ( struct image *shim, wchar_t **cmdline ) {
309  wchar_t *shimcmdline;
310  int len;
311  int rc;
312 
313  /* Construct new command line */
314  len = ( shim->cmdline ?
315  efi_asprintf ( &shimcmdline, "%s %s", shim->name,
316  shim->cmdline ) :
317  efi_asprintf ( &shimcmdline, "%s %ls", shim->name,
318  *cmdline ) );
319  if ( len < 0 ) {
320  rc = len;
321  DBGC ( &efi_shim, "SHIM could not construct command line: "
322  "%s\n", strerror ( rc ) );
323  return rc;
324  }
325 
326  /* Replace command line */
327  free ( *cmdline );
328  *cmdline = shimcmdline;
329 
330  return 0;
331 }
332 
333 /**
334  * Install UEFI shim special handling
335  *
336  * @v shim Shim image
337  * @v handle EFI device handle
338  * @v cmdline Command line to update
339  * @ret rc Return status code
340  */
342  wchar_t **cmdline ) {
345  int rc;
346 
347  /* Stop PXE base code */
348  if ( ( ! efi_shim_allow_pxe ) &&
349  ( ( rc = efi_shim_inhibit_pxe ( handle ) ) != 0 ) ) {
350  return rc;
351  }
352 
353  /* Update command line */
354  if ( ( rc = efi_shim_cmdline ( shim, cmdline ) ) != 0 )
355  return rc;
356 
357  /* Record original boot and runtime services functions */
361 
362  /* Wrap relevant boot and runtime services functions */
366  DBGC ( &efi_shim, "SHIM installed wrappers\n" );
367 
368  return 0;
369 }
370 
371 /**
372  * Uninstall UEFI shim special handling
373  *
374  */
375 void efi_shim_uninstall ( void ) {
378 
379  /* Restore original boot and runtime services functions */
383  DBGC ( &efi_shim, "SHIM uninstalled wrappers\n" );
384 }
int efi_shim_allow_sbat
Allow SBAT variable access.
Definition: efi_shim.c:107
EFI_BOOT_SERVICES * BootServices
A pointer to the EFI Boot Services Table.
Definition: UefiSpec.h:2099
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
struct arbelprm_rc_send_wqe rc
Definition: arbel.h:14
const char * name
Definition: ath9k_hw.c:1986
static void efi_shim_unlock(void)
Unlock UEFI shim.
Definition: efi_shim.c:145
#define EEFI(efirc)
Convert an EFI status code to an iPXE status code.
Definition: efi.h:175
An image tag.
Definition: image.h:173
EFI_LOCATE_PROTOCOL LocateProtocol
Definition: UefiSpec.h:2009
Definition of an EFI memory descriptor.
Definition: UefiSpec.h:156
128 bit buffer containing a unique identifier value.
Definition: Base.h:216
Error codes.
EFI strings.
struct image_tag efi_shim __image_tag
UEFI shim image.
Definition: efi_shim.c:110
int efi_shim_install(struct image *shim, EFI_HANDLE handle, wchar_t **cmdline)
Install UEFI shim special handling.
Definition: efi_shim.c:341
#define DBGC(...)
Definition: compiler.h:505
unsigned int UINT32
Definition: ProcessorBind.h:99
static EFI_SET_VARIABLE efi_shim_orig_set_variable
Original SetVariable() function.
Definition: efi_shim.c:118
unsigned short CHAR16
EFI_GUID efi_pxe_base_code_protocol_guid
PXE base code protocol GUID.
Definition: efi_guid.c:321
union @18 u
EFI PXE Base Code Protocol definitions, which is used to access PXE-compatible devices for network ac...
An executable image.
Definition: image.h:24
static EFI_GET_MEMORY_MAP efi_shim_orig_get_memory_map
Original GetMemoryMap() function.
Definition: efi_shim.c:115
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:239
FILE_SECBOOT(PERMITTED)
int efi_shim_require_loader
Require use of a third party loader binary.
Definition: efi_shim.c:69
UEFI shim special handling.
static int efi_shim_cmdline(struct image *shim, wchar_t **cmdline)
Update command line.
Definition: efi_shim.c:308
An object interface.
Definition: interface.h:125
int efi_shim_allow_pxe
Allow use of PXE base code protocol.
Definition: efi_shim.c:87
Executable images.
pseudo_bit_t value[0x00020]
Definition: arbel.h:13
#define DBGC_HDA(...)
Definition: compiler.h:506
ring len
Length.
Definition: dwmac.h:231
EFI_SET_VARIABLE SetVariable
Definition: UefiSpec.h:1905
EFI_GET_VARIABLE GetVariable
Definition: UefiSpec.h:1903
EFI Runtime Services Table.
Definition: UefiSpec.h:1880
char * strerror(int errno)
Retrieve string representation of error number.
Definition: strerror.c:79
static void(* free)(struct refcnt *refcnt))
Definition: refcnt.h:55
static int efi_shim_is_sbatlevel(const CHAR16 *name, const EFI_GUID *guid)
Check if variable is SbatLevel.
Definition: efi_shim.c:133
#define EFIAPI
EFI Boot Services Table.
Definition: UefiSpec.h:1931
#define efi_open(handle, protocol, interface)
Open protocol for ephemeral use.
Definition: efi.h:444
unsigned char uint8_t
Definition: stdint.h:10
UINT64 UINTN
Unsigned value of native width.
#define VOID
Undeclared type.
Definition: Base.h:272
FILE_LICENCE(GPL2_OR_LATER_OR_UBDL)
EFI API.
uint64_t guid
GUID.
Definition: edd.h:31
EFI_PXE_BASE_CODE_STOP Stop
Definition: PxeBaseCode.h:915
EFI "shim lock" protocol.
EFI_GET_MEMORY_MAP GetMemoryMap
Definition: UefiSpec.h:1948
static EFI_GET_VARIABLE efi_shim_orig_get_variable
Original GetVariable() function.
Definition: efi_shim.c:121
EFI_RUNTIME_SERVICES * RuntimeServices
A pointer to the EFI Runtime Services Table.
Definition: UefiSpec.h:2095
static __always_inline int struct dma_mapping * map
Definition: dma.h:184
RETURN_STATUS EFI_STATUS
Function return status for EFI API.
Definition: UefiBaseType.h:32
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:201
void efi_shim_uninstall(void)
Uninstall UEFI shim special handling.
Definition: efi_shim.c:375
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:333
EFI_SYSTEM_TABLE * efi_systab
The EFI_PXE_BASE_CODE_PROTOCOL is used to control PXE-compatible devices.
Definition: PxeBaseCode.h:907
int efi_asprintf(wchar_t **wstrp, const char *fmt,...)
Write a formatted string to newly allocated memory.
Definition: efi_strings.c:189
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
uint32_t cmdline
Definition: multiboot.h:16
uint16_t handle
Handle.
Definition: smbios.h:17
int memcmp(const void *first, const void *second, size_t len)
Compare memory regions.
Definition: string.c:115
int shim(struct image *image, int require_loader, int allow_pxe, int allow_sbat)
Set shim image.
Definition: shimmgmt.c:46
#define NULL
NULL pointer (VOID *)
Definition: Base.h:322
String functions.
const char * name
Name.
Definition: image.h:175
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:173
static int efi_shim_sbatlevel_verify
Verify read from SbatLevel variable.
Definition: efi_shim.c:124
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
union @391 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:276
Definition: efi.h:62