1+ use crate :: integrations:: shim:: hook:: SecurityHook ;
12use crate :: utils;
23use crate :: utils:: ResolvedPath ;
4+ use crate :: utils:: variables:: VariableController ;
35use anyhow:: { Context , Result , anyhow, bail} ;
46use std:: ffi:: c_void;
57use uefi:: Handle ;
68use uefi:: boot:: LoadImageSource ;
7- use uefi:: proto:: device_path:: DevicePath ;
9+ use uefi:: proto:: device_path:: text:: { AllowShortcuts , DisplayOnly } ;
10+ use uefi:: proto:: device_path:: { DevicePath , FfiDevicePath } ;
811use uefi:: proto:: unsafe_protocol;
12+ use uefi_raw:: table:: runtime:: VariableVendor ;
913use uefi_raw:: { Guid , Status , guid} ;
1014
15+ /// Security hook support.
16+ mod hook;
17+
1118/// Support for the shim loader application for Secure Boot.
1219pub struct ShimSupport ;
1320
@@ -17,6 +24,12 @@ pub enum ShimInput<'a> {
1724 OwnedDataBuffer ( Option < & ' a ResolvedPath > , Vec < u8 > ) ,
1825 /// Data loaded into a buffer and ready to be verified.
1926 DataBuffer ( Option < & ' a ResolvedPath > , & ' a [ u8 ] ) ,
27+ /// Low-level data buffer provided by the security hook.
28+ SecurityHookBuffer ( Option < * const FfiDevicePath > , & ' a [ u8 ] ) ,
29+ /// Low-level owned data buffer provided by the security hook.
30+ SecurityHookOwnedBuffer ( Option < * const FfiDevicePath > , Vec < u8 > ) ,
31+ /// Low-level path provided by the security hook.
32+ SecurityHookPath ( * const FfiDevicePath ) ,
2033 /// Data is provided as a resolved path. We will need to load the data to verify it.
2134 /// The output will them return the loaded data.
2235 ResolvedPath ( & ' a ResolvedPath ) ,
@@ -27,6 +40,9 @@ impl<'a> ShimInput<'a> {
2740 pub fn buffer ( & self ) -> Option < & [ u8 ] > {
2841 match self {
2942 ShimInput :: OwnedDataBuffer ( _, data) => Some ( data) ,
43+ ShimInput :: SecurityHookOwnedBuffer ( _, data) => Some ( data) ,
44+ ShimInput :: SecurityHookBuffer ( _, data) => Some ( data) ,
45+ ShimInput :: SecurityHookPath ( _) => None ,
3046 ShimInput :: DataBuffer ( _, data) => Some ( data) ,
3147 ShimInput :: ResolvedPath ( _) => None ,
3248 }
@@ -37,7 +53,14 @@ impl<'a> ShimInput<'a> {
3753 match self {
3854 ShimInput :: OwnedDataBuffer ( path, _) => path. as_ref ( ) . map ( |it| it. full_path . as_ref ( ) ) ,
3955 ShimInput :: DataBuffer ( path, _) => path. as_ref ( ) . map ( |it| it. full_path . as_ref ( ) ) ,
56+ ShimInput :: SecurityHookBuffer ( path, _) => {
57+ path. map ( |it| unsafe { DevicePath :: from_ffi_ptr ( it) } )
58+ }
59+ ShimInput :: SecurityHookPath ( path) => unsafe { Some ( DevicePath :: from_ffi_ptr ( * path) ) } ,
4060 ShimInput :: ResolvedPath ( path) => Some ( path. full_path . as_ref ( ) ) ,
61+ ShimInput :: SecurityHookOwnedBuffer ( path, _) => {
62+ path. map ( |it| unsafe { DevicePath :: from_ffi_ptr ( it) } )
63+ }
4164 }
4265 }
4366
@@ -51,9 +74,33 @@ impl<'a> ShimInput<'a> {
5174 Ok ( ShimInput :: OwnedDataBuffer ( root, data. to_vec ( ) ) )
5275 }
5376
77+ ShimInput :: SecurityHookPath ( ffi_path) => {
78+ // Acquire the file path.
79+ let Some ( path) = self . file_path ( ) else {
80+ bail ! ( "unable to convert security hook path to device path" ) ;
81+ } ;
82+ // Convert the underlying path to a string.
83+ let path = path
84+ . to_string ( DisplayOnly ( false ) , AllowShortcuts ( false ) )
85+ . context ( "unable to convert device path to string" ) ?;
86+ let path = utils:: resolve_path ( None , & path. to_string ( ) )
87+ . context ( "unable to resolve path" ) ?;
88+ // Read the file path.
89+ let data = path. read_file ( ) ?;
90+ Ok ( ShimInput :: SecurityHookOwnedBuffer ( Some ( ffi_path) , data) )
91+ }
92+
93+ ShimInput :: SecurityHookBuffer ( _, _) => {
94+ bail ! ( "unable to convert security hook buffer to owned data buffer" )
95+ }
96+
5497 ShimInput :: ResolvedPath ( path) => {
5598 Ok ( ShimInput :: OwnedDataBuffer ( Some ( path) , path. read_file ( ) ?) )
5699 }
100+
101+ ShimInput :: SecurityHookOwnedBuffer ( path, data) => {
102+ Ok ( ShimInput :: SecurityHookOwnedBuffer ( path, data) )
103+ }
57104 }
58105 }
59106}
@@ -83,6 +130,10 @@ struct ShimLockProtocol {
83130}
84131
85132impl ShimSupport {
133+ /// Variable controller for the shim lock.
134+ const SHIM_LOCK_VARIABLES : VariableController =
135+ VariableController :: new ( VariableVendor ( Self :: SHIM_LOCK_GUID ) ) ;
136+
86137 /// GUID for the shim lock protocol.
87138 const SHIM_LOCK_GUID : Guid = guid ! ( "605dab50-e046-4300-abb6-3dd810dd8b23" ) ;
88139 /// GUID for the shim image loader protocol.
@@ -103,7 +154,7 @@ impl ShimSupport {
103154 }
104155
105156 /// Use the shim to validate the `input`, returning [ShimVerificationOutput] when complete.
106- pub fn validate ( input : ShimInput ) -> Result < ShimVerificationOutput > {
157+ pub fn verify ( input : ShimInput ) -> Result < ShimVerificationOutput > {
107158 // Acquire the handle to the shim lock protocol.
108159 let handle = utils:: find_handle ( & Self :: SHIM_LOCK_GUID )
109160 . context ( "unable to find shim lock protocol" ) ?
@@ -117,21 +168,27 @@ impl ShimSupport {
117168 ShimInput :: OwnedDataBuffer ( _, _data) => {
118169 bail ! ( "owned data buffer is not supported in the verification function" ) ;
119170 }
171+ ShimInput :: SecurityHookBuffer ( _, _) => None ,
172+ ShimInput :: SecurityHookOwnedBuffer ( _, _) => None ,
120173 ShimInput :: DataBuffer ( _, _) => None ,
121174 ShimInput :: ResolvedPath ( path) => Some ( path. read_file ( ) ?) ,
175+ ShimInput :: SecurityHookPath ( _) => None ,
122176 } ;
123177
124178 // Convert the input to a buffer.
125179 // If the input provides the data buffer, we will use that.
126180 // Otherwise, we will use the data loaded by this function.
127- let buffer = match input {
128- ShimInput :: OwnedDataBuffer ( _root, _data) => {
129- bail ! ( "owned data buffer is not supported in the verification function" ) ;
130- }
131- ShimInput :: DataBuffer ( _root, data) => data,
181+ let buffer = match & input {
182+ ShimInput :: OwnedDataBuffer ( _root, data) => data,
183+ ShimInput :: DataBuffer ( _root, data) => * data,
132184 ShimInput :: ResolvedPath ( _path) => maybe_loaded_data
133185 . as_deref ( )
134186 . context ( "expected data buffer to be loaded already" ) ?,
187+ ShimInput :: SecurityHookBuffer ( _, data) => data,
188+ ShimInput :: SecurityHookOwnedBuffer ( _, data) => data,
189+ ShimInput :: SecurityHookPath ( _) => {
190+ bail ! ( "security hook path input not supported in the verification function" )
191+ }
135192 } ;
136193
137194 // Check if the buffer is too large to verify.
@@ -168,12 +225,19 @@ impl ShimSupport {
168225
169226 // Determines whether LoadImage in Boot Services must be patched.
170227 // Version 16 of the shim doesn't require extra effort to load Secure Boot binaries.
171- // If the image loader is installed, we can skip over the security override .
172- let requires_security_override = shim_loaded && !shim_loader_available;
228+ // If the image loader is installed, we can skip over the security hook .
229+ let requires_security_hook = shim_loaded && !shim_loader_available;
173230
174- // If the security override is required, we will bail for now.
175- if requires_security_override {
176- bail ! ( "shim image loader protocol is not available, please upgrade to shim version 16" ) ;
231+ // If the security hook is required, we will bail for now.
232+ if requires_security_hook {
233+ // Install the security hook, if possible. If it's not, this is necessary to continue
234+ // so we should bail.
235+ let installed = SecurityHook :: install ( ) . context ( "unable to install security hook" ) ?;
236+ if !installed {
237+ bail ! ( "unable to install security hook require for this platform" ) ;
238+ }
239+ // Retain the shim protocol after load.
240+ Self :: retain ( ) ?
177241 }
178242
179243 // Converts the shim input to an owned data buffer.
@@ -188,6 +252,21 @@ impl ShimSupport {
188252 } ;
189253
190254 // Loads the image using Boot Services LoadImage function.
191- uefi:: boot:: load_image ( current_image, source) . context ( "unable to load image" )
255+ let result = uefi:: boot:: load_image ( current_image, source) . context ( "unable to load image" ) ;
256+
257+ // If the security override is required, we will uninstall the security hook.
258+ if requires_security_hook {
259+ SecurityHook :: uninstall ( ) . context ( "unable to uninstall security hook" ) ?;
260+ }
261+ result
262+ }
263+
264+ /// Set the ShimRetainProtocol variable to indicate that shim should retain the protocols
265+ /// for the full lifetime of boot services.
266+ pub fn retain ( ) -> Result < ( ) > {
267+ Self :: SHIM_LOCK_VARIABLES
268+ . set_bool ( "ShimRetainProtocol" , true )
269+ . context ( "unable to retain shim protocol" ) ?;
270+ Ok ( ( ) )
192271 }
193272}
0 commit comments