1 //! Shim lock protocol.
2 
3 #![cfg(any(
4     target_arch = "x86",
5     target_arch = "x86_64",
6     target_arch = "arm",
7     target_arch = "aarch64"
8 ))]
9 
10 use crate::proto::unsafe_protocol;
11 use crate::result::Error;
12 use crate::{Result, Status, StatusExt};
13 use core::ffi::c_void;
14 use core::mem::MaybeUninit;
15 
16 // The `PE_COFF_LOADER_IMAGE_CONTEXT` type. None of our methods need to inspect
17 // the fields of this struct, we just need to make sure it is the right size.
18 #[repr(C)]
19 struct Context {
20     _image_address: u64,
21     _image_size: u64,
22     _entry_point: u64,
23     _size_of_headers: usize,
24     _image_type: u16,
25     _number_of_sections: u16,
26     _section_alignment: u32,
27     _first_section: *const c_void,
28     _reloc_dir: *const c_void,
29     _sec_dir: *const c_void,
30     _number_of_rva_and_sizes: u64,
31     _pe_hdr: *const c_void,
32 }
33 
34 const SHA1_DIGEST_SIZE: usize = 20;
35 const SHA256_DIGEST_SIZE: usize = 32;
36 
37 /// Authenticode hashes of some UEFI application.
38 #[derive(Debug)]
39 pub struct Hashes {
40     /// SHA256 Authenticode Digest
41     pub sha256: [u8; SHA256_DIGEST_SIZE],
42     /// SHA1 Authenticode Digest
43     pub sha1: [u8; SHA1_DIGEST_SIZE],
44 }
45 
46 // These macros set the correct calling convention for the Shim protocol methods.
47 
48 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
49 macro_rules! shim_function {
50     (fn $args:tt -> $return_type:ty) => (extern "sysv64" fn $args -> $return_type)
51 }
52 
53 #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
54 macro_rules! shim_function {
55     (fn $args:tt -> $return_type:ty) => (extern "C" fn $args -> $return_type)
56 }
57 
58 /// The Shim lock protocol.
59 ///
60 /// This protocol is not part of the UEFI specification, but is
61 /// installed by the [Shim bootloader](https://github.com/rhboot/shim)
62 /// which is commonly used by Linux distributions to support UEFI
63 /// Secure Boot. Shim is built with an embedded certificate that is
64 /// used to validate another EFI application before running it. That
65 /// application may itself be a bootloader that needs to validate
66 /// another EFI application before running it, and the shim lock
67 /// protocol exists to support that.
68 #[derive(Debug)]
69 #[repr(C)]
70 #[unsafe_protocol("605dab50-e046-4300-abb6-3dd810dd8b23")]
71 pub struct ShimLock {
72     verify: shim_function! { fn(buffer: *const u8, size: u32) -> Status },
73     hash: shim_function! {
74         fn(
75             buffer: *const u8,
76             size: u32,
77             context: *mut Context,
78             sha256: *mut [u8; SHA256_DIGEST_SIZE],
79             sha1: *mut [u8; SHA1_DIGEST_SIZE]
80         ) -> Status
81     },
82     context: shim_function! { fn(buffer: *const u8, size: u32, context: *mut Context) -> Status },
83 }
84 
85 impl ShimLock {
86     /// Verify that an EFI application is signed by the certificate
87     /// embedded in shim.
88     ///
89     /// The buffer's size must fit in a `u32`; if that condition is not
90     /// met then a `BAD_BUFFER_SIZE` error will be returned and the shim
91     /// lock protocol will not be called.
verify(&self, buffer: &[u8]) -> Result92     pub fn verify(&self, buffer: &[u8]) -> Result {
93         let size: u32 = buffer
94             .len()
95             .try_into()
96             .map_err(|_| Error::from(Status::BAD_BUFFER_SIZE))?;
97         (self.verify)(buffer.as_ptr(), size).to_result()
98     }
99     /// Compute the Authenticode Hash of the provided EFI application.
100     ///
101     /// The buffer's size must fit in a `u32`; if that condition is not
102     /// met then a `BAD_BUFFER_SIZE` error will be returned and the shim
103     /// lock protocol will not be called.
hash(&self, buffer: &[u8], hashes: &mut Hashes) -> Result104     pub fn hash(&self, buffer: &[u8], hashes: &mut Hashes) -> Result {
105         let ptr: *const u8 = buffer.as_ptr();
106         let size: u32 = buffer
107             .len()
108             .try_into()
109             .map_err(|_| Error::from(Status::BAD_BUFFER_SIZE))?;
110 
111         let mut context = MaybeUninit::<Context>::uninit();
112         (self.context)(ptr, size, context.as_mut_ptr()).to_result()?;
113         (self.hash)(
114             ptr,
115             size,
116             context.as_mut_ptr(),
117             &mut hashes.sha256,
118             &mut hashes.sha1,
119         )
120         .to_result()
121     }
122 }
123