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