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 SetVariable() function */
117 
118 /** Original GetVariable() function */
120 
121 /** Verify read from SbatLevel variable */
123 
124 /**
125  * Check if variable is SbatLevel
126  *
127  * @v name Variable name
128  * @v guid Variable namespace GUID
129  * @ret is_sbatlevel Variable is SbatLevel
130  */
131 static int efi_shim_is_sbatlevel ( const CHAR16 *name, const EFI_GUID *guid ) {
132  static CHAR16 sbatlevel[] = L"SbatLevel";
134 
135  return ( ( memcmp ( name, sbatlevel, sizeof ( sbatlevel ) ) == 0 ) &&
136  ( memcmp ( guid, shimlock, sizeof ( *shimlock ) ) == 0 ) );
137 }
138 
139 /**
140  * Unlock UEFI shim
141  *
142  */
143 static void efi_shim_unlock ( void ) {
145  uint8_t empty[0];
146  union {
148  void *interface;
149  } u;
150  EFI_STATUS efirc;
151 
152  /* Locate shim lock protocol */
153  if ( ( efirc = bs->LocateProtocol ( &efi_shim_lock_protocol_guid,
154  NULL, &u.interface ) ) == 0 ) {
155  u.lock->Verify ( empty, sizeof ( empty ) );
156  DBGC ( &efi_shim, "SHIM unlocked via %p\n", u.lock );
157  }
158 }
159 
160 /**
161  * Wrap SetVariable()
162  *
163  * @v name Variable name
164  * @v guid Variable namespace GUID
165  * @v attrs Attributes
166  * @v len Buffer size
167  * @v data Data buffer
168  * @ret efirc EFI status code
169  */
170 static EFI_STATUS EFIAPI
172  UINTN len, VOID *data ) {
173  EFI_STATUS efirc;
174 
175  /* Call original SetVariable() */
177 
178  /* Allow verification of SbatLevel variable content */
179  if ( efi_shim_is_sbatlevel ( name, guid ) && ( efirc == 0 ) ) {
180  DBGC ( &efi_shim, "SHIM detected write to %ls:\n", name );
181  DBGC_HDA ( &efi_shim, 0, data, len );
183  }
184 
185  return efirc;
186 }
187 
188 /**
189  * Wrap GetVariable()
190  *
191  * @v name Variable name
192  * @v guid Variable namespace GUID
193  * @v attrs Attributes to fill in
194  * @v len Buffer size
195  * @v data Data buffer
196  * @ret efirc EFI status code
197  */
198 static EFI_STATUS EFIAPI
200  UINTN *len, VOID *data ) {
201  char *value = data;
202  EFI_STATUS efirc;
203 
204  /* Call original GetVariable() */
206 
207  /* Patch SbatLevel variable if applicable */
208  if ( efi_shim_is_sbatlevel ( name, guid ) && data && ( efirc == 0 ) ) {
209  if ( efi_shim_allow_sbat ) {
210  DBGC ( &efi_shim, "SHIM allowing read from %ls:\n",
211  name );
212  } else if ( efi_shim_sbatlevel_verify ) {
213  DBGC ( &efi_shim, "SHIM allowing one read from %ls:\n",
214  name );
216  } else {
217  DBGC ( &efi_shim, "SHIM patching read from %ls:\n",
218  name );
219  value[0] = '\0';
220  }
221  DBGC_HDA ( &efi_shim, 0, data, *len );
222  }
223 
224  return efirc;
225 }
226 
227 /**
228  * Wrap GetMemoryMap()
229  *
230  * @v len Memory map size
231  * @v map Memory map
232  * @v key Memory map key
233  * @v desclen Descriptor size
234  * @v descver Descriptor version
235  * @ret efirc EFI status code
236  */
239  UINTN *key, UINTN *desclen,
240  UINT32 *descver ) {
242 
243  /* Unlock shim */
244  if ( ! efi_shim_require_loader )
245  efi_shim_unlock();
246 
247  /* Uninstall runtime services wrappers, if still installed */
248  if ( rs->SetVariable == efi_shim_set_variable ) {
250  DBGC ( &efi_shim, "SHIM uninstalled SetVariable() wrapper\n" );
251  } else if ( rs->SetVariable != efi_shim_orig_set_variable ) {
252  DBGC ( &efi_shim, "SHIM could not uninstall SetVariable() "
253  "wrapper!\n" );
254  }
255  if ( rs->GetVariable == efi_shim_get_variable ) {
257  DBGC ( &efi_shim, "SHIM uninstalled GetVariable() wrapper\n" );
258  } else if ( rs->GetVariable != efi_shim_orig_get_variable ) {
259  DBGC ( &efi_shim, "SHIM could not uninstall GetVariable() "
260  "wrapper!\n" );
261  }
262 
263  /* Hand off to original GetMemoryMap() */
264  return efi_shim_orig_get_memory_map ( len, map, key, desclen,
265  descver );
266 }
267 
268 /**
269  * Inhibit use of PXE base code
270  *
271  * @v handle EFI handle
272  * @ret rc Return status code
273  */
276  union {
278  void *interface;
279  } u;
280  EFI_STATUS efirc;
281  int rc;
282 
283  /* Locate PXE base code */
284  if ( ( efirc = bs->OpenProtocol ( handle,
286  &u.interface, efi_image_handle, NULL,
288  rc = -EEFI ( efirc );
289  DBGC ( &efi_shim, "SHIM could not open PXE base code: %s\n",
290  strerror ( rc ) );
291  goto err_no_base;
292  }
293 
294  /* Stop PXE base code */
295  if ( ( efirc = u.pxe->Stop ( u.pxe ) ) != 0 ) {
296  rc = -EEFI ( efirc );
297  DBGC ( &efi_shim, "SHIM could not stop PXE base code: %s\n",
298  strerror ( rc ) );
299  goto err_stop;
300  }
301 
302  /* Success */
303  rc = 0;
304  DBGC ( &efi_shim, "SHIM stopped PXE base code\n" );
305 
306  err_stop:
309  err_no_base:
310  return rc;
311 }
312 
313 /**
314  * Update command line
315  *
316  * @v shim Shim image
317  * @v cmdline Command line to update
318  * @ret rc Return status code
319  */
320 static int efi_shim_cmdline ( struct image *shim, wchar_t **cmdline ) {
321  wchar_t *shimcmdline;
322  int len;
323  int rc;
324 
325  /* Construct new command line */
326  len = ( shim->cmdline ?
327  efi_asprintf ( &shimcmdline, "%s %s", shim->name,
328  shim->cmdline ) :
329  efi_asprintf ( &shimcmdline, "%s %ls", shim->name,
330  *cmdline ) );
331  if ( len < 0 ) {
332  rc = len;
333  DBGC ( &efi_shim, "SHIM could not construct command line: "
334  "%s\n", strerror ( rc ) );
335  return rc;
336  }
337 
338  /* Replace command line */
339  free ( *cmdline );
340  *cmdline = shimcmdline;
341 
342  return 0;
343 }
344 
345 /**
346  * Install UEFI shim special handling
347  *
348  * @v shim Shim image
349  * @v handle EFI device handle
350  * @v cmdline Command line to update
351  * @ret rc Return status code
352  */
354  wchar_t **cmdline ) {
357  int rc;
358 
359  /* Stop PXE base code */
360  if ( ( ! efi_shim_allow_pxe ) &&
361  ( ( rc = efi_shim_inhibit_pxe ( handle ) ) != 0 ) ) {
362  return rc;
363  }
364 
365  /* Update command line */
366  if ( ( rc = efi_shim_cmdline ( shim, cmdline ) ) != 0 )
367  return rc;
368 
369  /* Record original boot and runtime services functions */
373 
374  /* Wrap relevant boot and runtime services functions */
378  DBGC ( &efi_shim, "SHIM installed wrappers\n" );
379 
380  return 0;
381 }
382 
383 /**
384  * Uninstall UEFI shim special handling
385  *
386  */
387 void efi_shim_uninstall ( void ) {
390 
391  /* Restore original boot and runtime services functions */
395  DBGC ( &efi_shim, "SHIM uninstalled wrappers\n" );
396 }
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:2081
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:258
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:143
#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:1995
Definition of an EFI memory descriptor.
Definition: UefiSpec.h:145
128 bit buffer containing a unique identifier value.
Definition: Base.h:215
Error codes.
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:353
#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:116
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:1987
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:237
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:320
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.
pseudo_bit_t value[0x00020]
Definition: arbel.h:13
#define DBGC_HDA(...)
Definition: compiler.h:506
EFI_SET_VARIABLE SetVariable
Definition: UefiSpec.h:1891
EFI_GET_VARIABLE GetVariable
Definition: UefiSpec.h:1889
uint32_t attrs
Extended attributes (optional)
Definition: memmap.c:32
#define EFI_OPEN_PROTOCOL_GET_PROTOCOL
Definition: UefiSpec.h:1344
EFI Runtime Services Table.
Definition: UefiSpec.h:1866
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:131
#define EFIAPI
EFI Boot Services Table.
Definition: UefiSpec.h:1917
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 __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:1934
static EFI_GET_VARIABLE efi_shim_orig_get_variable
Original GetVariable() function.
Definition: efi_shim.c:119
EFI_RUNTIME_SERVICES * RuntimeServices
A pointer to the EFI Runtime Services Table.
Definition: UefiSpec.h:2077
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:199
void efi_shim_uninstall(void)
Uninstall UEFI shim special handling.
Definition: efi_shim.c:387
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:1986
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:698
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
uint32_t len
Length.
Definition: ena.h:14
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:171
static int efi_shim_sbatlevel_verify
Verify read from SbatLevel variable.
Definition: efi_shim.c:122
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:783
union @383 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:274
Definition: efi.h:59