xref: /aosp_15_r20/external/avb/rust/src/verify.rs (revision d289c2ba6de359471b23d594623b906876bc48a0)
1 // Copyright 2023, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! Verification APIs.
16 //!
17 //! This module is responsible for all the conversions required to pass information between
18 //! libavb and Rust for verifying images.
19 
20 extern crate alloc;
21 
22 use crate::{
23     descriptor::{get_descriptors, Descriptor, DescriptorResult},
24     error::{
25         slot_verify_enum_to_result, vbmeta_verify_enum_to_result, SlotVerifyError,
26         SlotVerifyNoDataResult, SlotVerifyResult, VbmetaVerifyResult,
27     },
28     ops, Ops,
29 };
30 use alloc::vec::Vec;
31 use avb_bindgen::{
32     avb_slot_verify, avb_slot_verify_data_free, AvbPartitionData, AvbSlotVerifyData, AvbVBMetaData,
33 };
34 use core::{
35     ffi::{c_char, CStr},
36     fmt,
37     marker::PhantomData,
38     pin::pin,
39     ptr::{self, null, null_mut, NonNull},
40     slice,
41 };
42 
43 /// `AvbHashtreeErrorMode`; see libavb docs for descriptions of each mode.
44 pub use avb_bindgen::AvbHashtreeErrorMode as HashtreeErrorMode;
45 /// `AvbSlotVerifyFlags`; see libavb docs for descriptions of each flag.
46 pub use avb_bindgen::AvbSlotVerifyFlags as SlotVerifyFlags;
47 
48 /// Returns `Err(SlotVerifyError::Internal)` if the given pointer is `NULL`.
check_nonnull<T>(ptr: *const T) -> SlotVerifyNoDataResult<()>49 fn check_nonnull<T>(ptr: *const T) -> SlotVerifyNoDataResult<()> {
50     match ptr.is_null() {
51         true => Err(SlotVerifyError::Internal),
52         false => Ok(()),
53     }
54 }
55 
56 /// Wraps a raw C `AvbVBMetaData` struct.
57 ///
58 /// This provides a Rust safe view over the raw data; no copies are made.
59 //
60 // `repr(transparent)` guarantees that size and alignment match the underlying type exactly, so that
61 // we can cast the array of `AvbVBMetaData` structs directly into a slice of `VbmetaData` wrappers
62 // without allocating any additional memory.
63 #[repr(transparent)]
64 pub struct VbmetaData(AvbVBMetaData);
65 
66 impl VbmetaData {
67     /// Validates the internal data so the accessors can be fail-free. This should be called on all
68     /// `VbmetaData` objects before they are handed to the user.
69     ///
70     /// Normally this would be done in a `new()` function but we never instantiate `VbmetaData`
71     /// objects ourselves, we just cast them from the C structs provided by libavb.
72     ///
73     /// Returns `Err(SlotVerifyError::Internal)` on failure.
validate(&self) -> SlotVerifyNoDataResult<()>74     fn validate(&self) -> SlotVerifyNoDataResult<()> {
75         check_nonnull(self.0.partition_name)?;
76         check_nonnull(self.0.vbmeta_data)?;
77         Ok(())
78     }
79 
80     /// Returns the name of the partition this vbmeta image was loaded from.
partition_name(&self) -> &CStr81     pub fn partition_name(&self) -> &CStr {
82         // SAFETY:
83         // * libavb gives us a properly-allocated and nul-terminated string.
84         // * the returned contents remain valid and unmodified while we exist.
85         unsafe { CStr::from_ptr(self.0.partition_name) }
86     }
87 
88     /// Returns the vbmeta image contents.
data(&self) -> &[u8]89     pub fn data(&self) -> &[u8] {
90         // SAFETY:
91         // * libavb gives us a properly-allocated byte array.
92         // * the returned contents remain valid and unmodified while we exist.
93         unsafe { slice::from_raw_parts(self.0.vbmeta_data, self.0.vbmeta_size) }
94     }
95 
96     /// Returns the vbmeta verification result.
verify_result(&self) -> VbmetaVerifyResult<()>97     pub fn verify_result(&self) -> VbmetaVerifyResult<()> {
98         vbmeta_verify_enum_to_result(self.0.verify_result)
99     }
100 
101     /// Extracts the descriptors from the vbmeta image.
102     ///
103     /// Note that this function allocates memory to hold the `Descriptor` objects.
104     ///
105     /// # Returns
106     /// A vector of descriptors, or `DescriptorError` on failure.
descriptors(&self) -> DescriptorResult<Vec<Descriptor>>107     pub fn descriptors(&self) -> DescriptorResult<Vec<Descriptor>> {
108         // SAFETY: the only way to get a `VbmetaData` object is via the return value of
109         // `slot_verify()`, so we know we have been properly validated.
110         unsafe { get_descriptors(self) }
111     }
112 
113     /// Gets a property from the vbmeta image for the given key
114     ///
115     /// This function re-implements the libavb avb_property_lookup logic.
116     ///
117     /// # Returns
118     /// Byte array with property data or None in case property not found or failure.
get_property_value(&self, key: &str) -> Option<&[u8]>119     pub fn get_property_value(&self, key: &str) -> Option<&[u8]> {
120         self.descriptors().ok()?.iter().find_map(|d| match d {
121             Descriptor::Property(p) if p.key == key => Some(p.value),
122             _ => None,
123         })
124     }
125 }
126 
127 impl fmt::Display for VbmetaData {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result128     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129         write!(f, "{:?}: {:?}", self.partition_name(), self.verify_result())
130     }
131 }
132 
133 /// Forwards to `Display` formatting; the default `Debug` formatting implementation isn't very
134 /// useful as it's mostly raw pointer addresses.
135 impl fmt::Debug for VbmetaData {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result136     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137         fmt::Display::fmt(self, f)
138     }
139 }
140 
141 /// Wraps a raw C `AvbPartitionData` struct.
142 ///
143 /// This provides a Rust safe view over the raw data; no copies are made.
144 #[repr(transparent)]
145 pub struct PartitionData(AvbPartitionData);
146 
147 impl PartitionData {
148     /// Validates the internal data so the accessors can be fail-free. This should be called on all
149     /// `PartitionData` objects before they are handed to the user.
150     ///
151     /// Normally this would be done in a `new()` function but we never instantiate `PartitionData`
152     /// objects ourselves, we just cast them from the C structs provided by libavb.
153     ///
154     /// Returns `Err(SlotVerifyError::Internal)` on failure.
validate(&self) -> SlotVerifyNoDataResult<()>155     fn validate(&self) -> SlotVerifyNoDataResult<()> {
156         check_nonnull(self.0.partition_name)?;
157         check_nonnull(self.0.data)?;
158         Ok(())
159     }
160 
161     /// Returns the name of the partition this image was loaded from.
partition_name(&self) -> &CStr162     pub fn partition_name(&self) -> &CStr {
163         // SAFETY:
164         // * libavb gives us a properly-allocated and nul-terminated string.
165         // * the returned contents remain valid and unmodified while we exist.
166         unsafe { CStr::from_ptr(self.0.partition_name) }
167     }
168 
169     /// Returns the image contents.
data(&self) -> &[u8]170     pub fn data(&self) -> &[u8] {
171         // SAFETY:
172         // * libavb gives us a properly-allocated byte array.
173         // * the returned contents remain valid and unmodified while we exist.
174         unsafe { slice::from_raw_parts(self.0.data, self.0.data_size) }
175     }
176 
177     /// Returns whether this partition was preloaded via `get_preloaded_partition()`.
preloaded(&self) -> bool178     pub fn preloaded(&self) -> bool {
179         self.0.preloaded
180     }
181 
182     /// Returns the verification result for this partition.
183     ///
184     /// Only top-level `Verification` errors will contain valid `SlotVerifyData` objects, if this
185     /// individual partition returns a `Verification` error the error will always contain `None`.
verify_result(&self) -> SlotVerifyNoDataResult<()>186     pub fn verify_result(&self) -> SlotVerifyNoDataResult<()> {
187         slot_verify_enum_to_result(self.0.verify_result)
188     }
189 }
190 
191 /// A "(p)" after the partition name indicates a preloaded partition.
192 impl fmt::Display for PartitionData {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result193     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
194         write!(
195             f,
196             "{:?}{}: {:?}",
197             self.partition_name(),
198             match self.preloaded() {
199                 true => "(p)",
200                 false => "",
201             },
202             self.verify_result()
203         )
204     }
205 }
206 
207 /// Forwards to `Display` formatting; the default `Debug` formatting implementation isn't very
208 /// useful as it's mostly raw pointer addresses.
209 impl fmt::Debug for PartitionData {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result210     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
211         fmt::Display::fmt(self, f)
212     }
213 }
214 
215 /// Wraps a raw C `AvbSlotVerifyData` struct.
216 ///
217 /// This provides a Rust safe view over the raw data; no copies are made.
218 ///
219 /// # Lifetimes
220 /// * `'a`: the lifetime of any preloaded partition data borrowed from an `Ops<'a>` object.
221 ///
222 /// If the `Ops` doesn't provide any preloaded data, `SlotVerifyData` doesn't borrow anything
223 /// and instead allocates and owns all data internally, freeing it accordingly on `Drop`. In this
224 /// case, `'a` can be `'static` which imposes no lifetime restrictions on `SlotVerifyData`.
225 pub struct SlotVerifyData<'a> {
226     /// Internally owns the underlying data and deletes it on drop.
227     raw_data: NonNull<AvbSlotVerifyData>,
228 
229     /// This provides the necessary lifetimes so the compiler can make sure that the preloaded
230     /// partition data stays alive at least as long as we do, since the underlying
231     /// `AvbSlotVerifyData` may wrap this data rather than making a copy.
232     //
233     // We do not want to actually borrow an `Ops` here, since in some cases `Ops` is just a
234     // temporary object and may go out of scope before us. The only shared data is the preloaded
235     // partition contents, not the entire `Ops` object.
236     _preloaded: PhantomData<&'a [u8]>,
237 }
238 
239 // Useful so that `SlotVerifyError`, which may hold a `SlotVerifyData`, can derive `PartialEq`.
240 impl<'a> PartialEq for SlotVerifyData<'a> {
eq(&self, other: &Self) -> bool241     fn eq(&self, other: &Self) -> bool {
242         // A `SlotVerifyData` uniquely owns the underlying data so is only equal to itself.
243         ptr::eq(self, other)
244     }
245 }
246 
247 impl<'a> Eq for SlotVerifyData<'a> {}
248 
249 impl<'a> SlotVerifyData<'a> {
250     /// Creates a `SlotVerifyData` wrapping the given raw `AvbSlotVerifyData`.
251     ///
252     /// The returned `SlotVerifyData` will take ownership of the given `AvbSlotVerifyData` and
253     /// properly release the allocated memory when it drops.
254     ///
255     /// If `ops` provided any preloaded data, the returned `SlotVerifyData` also borrows the data to
256     /// account for the underlying `AvbSlotVerifyData` holding a pointer to it. If there was no
257     /// preloaded data, then `SlotVerifyData` owns all its data.
258     ///
259     /// # Arguments
260     /// * `data`: a `AvbSlotVerifyData` object created by libavb using `ops`.
261     /// * `ops`: the user-provided `Ops` object that was used for verification; only used here to
262     ///          grab the preloaded data lifetime.
263     ///
264     /// # Returns
265     /// The new object, or `Err(SlotVerifyError::Internal)` if the data looks invalid.
266     ///
267     /// # Safety
268     /// * `data` must be a valid `AvbSlotVerifyData` object created by libavb using `ops`.
269     /// * after calling this function, do not access `data` except through the returned object
new( data: *mut AvbSlotVerifyData, _ops: &dyn Ops<'a>, ) -> SlotVerifyNoDataResult<Self>270     unsafe fn new(
271         data: *mut AvbSlotVerifyData,
272         _ops: &dyn Ops<'a>,
273     ) -> SlotVerifyNoDataResult<Self> {
274         let ret = Self {
275             raw_data: NonNull::new(data).ok_or(SlotVerifyError::Internal)?,
276             _preloaded: PhantomData,
277         };
278 
279         // Validate all the contained data here so accessors will never fail.
280         // SAFETY: `raw_data` points to a valid `AvbSlotVerifyData` object owned by us.
281         let data = unsafe { ret.raw_data.as_ref() };
282         check_nonnull(data.ab_suffix)?;
283         check_nonnull(data.vbmeta_images)?;
284         check_nonnull(data.loaded_partitions)?;
285         check_nonnull(data.cmdline)?;
286         ret.vbmeta_data().iter().try_for_each(|v| v.validate())?;
287         ret.partition_data().iter().try_for_each(|i| i.validate())?;
288 
289         Ok(ret)
290     }
291 
292     /// Returns the slot suffix string.
ab_suffix(&self) -> &CStr293     pub fn ab_suffix(&self) -> &CStr {
294         // SAFETY:
295         // * `raw_data` points to a valid `AvbSlotVerifyData` object owned by us.
296         // * libavb gives us a properly-allocated and nul-terminated string.
297         // * the returned contents remain valid and unmodified while we exist.
298         unsafe { CStr::from_ptr(self.raw_data.as_ref().ab_suffix) }
299     }
300 
301     /// Returns the `VbmetaData` structs.
vbmeta_data(&self) -> &[VbmetaData]302     pub fn vbmeta_data(&self) -> &[VbmetaData] {
303         // SAFETY:
304         // * `raw_data` points to a valid `AvbSlotVerifyData` object owned by us.
305         // * libavb gives us a properly-allocated array of structs.
306         // * the returned contents remain valid and unmodified while we exist.
307         unsafe {
308             slice::from_raw_parts(
309                 // `repr(transparent)` means we can cast between these types.
310                 self.raw_data.as_ref().vbmeta_images as *const VbmetaData,
311                 self.raw_data.as_ref().num_vbmeta_images,
312             )
313         }
314     }
315 
316     /// Returns the `PartitionData` structs.
partition_data(&self) -> &[PartitionData]317     pub fn partition_data(&self) -> &[PartitionData] {
318         // SAFETY:
319         // * `raw_data` points to a valid `AvbSlotVerifyData` object owned by us.
320         // * libavb gives us a properly-allocated array of structs.
321         // * the returned contents remain valid and unmodified while we exist.
322         unsafe {
323             slice::from_raw_parts(
324                 // `repr(transparent)` means we can cast between these types.
325                 self.raw_data.as_ref().loaded_partitions as *const PartitionData,
326                 self.raw_data.as_ref().num_loaded_partitions,
327             )
328         }
329     }
330 
331     /// Returns the kernel commandline.
cmdline(&self) -> &CStr332     pub fn cmdline(&self) -> &CStr {
333         // SAFETY:
334         // * `raw_data` points to a valid `AvbSlotVerifyData` object owned by us.
335         // * libavb gives us a properly-allocated and nul-terminated string.
336         // * the returned contents remain valid and unmodified while we exist.
337         unsafe { CStr::from_ptr(self.raw_data.as_ref().cmdline) }
338     }
339 
340     /// Returns the rollback indices.
rollback_indexes(&self) -> &[u64]341     pub fn rollback_indexes(&self) -> &[u64] {
342         // SAFETY: `raw_data` points to a valid `AvbSlotVerifyData` object owned by us.
343         &unsafe { self.raw_data.as_ref() }.rollback_indexes[..]
344     }
345 
346     /// Returns the resolved hashtree error mode.
resolved_hashtree_error_mode(&self) -> HashtreeErrorMode347     pub fn resolved_hashtree_error_mode(&self) -> HashtreeErrorMode {
348         // SAFETY: `raw_data` points to a valid `AvbSlotVerifyData` object owned by us.
349         unsafe { self.raw_data.as_ref() }.resolved_hashtree_error_mode
350     }
351 }
352 
353 /// Frees any internally-allocated and owned data.
354 impl<'a> Drop for SlotVerifyData<'a> {
drop(&mut self)355     fn drop(&mut self) {
356         // SAFETY:
357         // * `raw_data` points to a valid `AvbSlotVerifyData` object owned by us.
358         // * libavb created the object and requires us to free it by calling this function.
359         unsafe { avb_slot_verify_data_free(self.raw_data.as_ptr()) };
360     }
361 }
362 
363 /// Implements `Display` to make it easy to print some basic information.
364 ///
365 /// This implementation will print the slot, partition name, and verification status for all
366 /// vbmetadata and images.
367 impl<'a> fmt::Display for SlotVerifyData<'a> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result368     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
369         write!(
370             f,
371             "slot: {:?}, vbmeta: {:?}, images: {:?}",
372             self.ab_suffix(),
373             self.vbmeta_data(),
374             self.partition_data()
375         )
376     }
377 }
378 
379 /// Forwards to `Display` formatting; the default `Debug` formatting implementation isn't very
380 /// useful as it's mostly raw pointer addresses.
381 impl<'a> fmt::Debug for SlotVerifyData<'a> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result382     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
383         fmt::Display::fmt(self, f)
384     }
385 }
386 
387 /// Performs verification of the requested images.
388 ///
389 /// This wraps `avb_slot_verify()` for Rust, see the original docs for more details.
390 ///
391 /// # Arguments
392 /// * `ops`: implementation of the required verification callbacks.
393 /// * `requested_partition`: the set of partition names to verify.
394 /// * `ab_suffix`: the slot suffix to append to the partition names, or None.
395 /// * `flags`: flags to configure verification.
396 /// * `hashtree_error_mode`: desired error handling behavior.
397 ///
398 /// # Returns
399 /// `Ok` if verification completed successfully, the verification error otherwise. `SlotVerifyData`
400 /// will be returned in two cases:
401 ///
402 /// 1. always returned on verification success
403 /// 2. if `AllowVerificationError` is given in `flags`, it will also be returned on verification
404 ///    failure
405 ///
406 /// A returned `SlotVerifyData` will also borrow any preloaded data provided by `ops`. The `ops`
407 /// object itself can go out of scope, but any preloaded data it could provide must outlive the
408 /// returned object.
slot_verify<'a>( ops: &mut dyn Ops<'a>, requested_partitions: &[&CStr], ab_suffix: Option<&CStr>, flags: SlotVerifyFlags, hashtree_error_mode: HashtreeErrorMode, ) -> SlotVerifyResult<'a, SlotVerifyData<'a>>409 pub fn slot_verify<'a>(
410     ops: &mut dyn Ops<'a>,
411     requested_partitions: &[&CStr],
412     ab_suffix: Option<&CStr>,
413     flags: SlotVerifyFlags,
414     hashtree_error_mode: HashtreeErrorMode,
415 ) -> SlotVerifyResult<'a, SlotVerifyData<'a>> {
416     // libavb detects the size of the `requested_partitions` array by NULL termination. Expecting
417     // the Rust caller to do this would make the API much more awkward, so we populate a
418     // NULL-terminated array of c-string pointers ourselves. For now we use a fixed-sized array
419     // rather than dynamically allocating, 8 should be more than enough.
420     const MAX_PARTITION_ARRAY_SIZE: usize = 8 + 1; // Max 8 partition names + 1 for NULL terminator.
421     if requested_partitions.len() >= MAX_PARTITION_ARRAY_SIZE {
422         return Err(SlotVerifyError::Internal);
423     }
424     let mut partitions_array = [null() as *const c_char; MAX_PARTITION_ARRAY_SIZE];
425     for (source, dest) in requested_partitions.iter().zip(partitions_array.iter_mut()) {
426         *dest = source.as_ptr();
427     }
428 
429     // To be more Rust idiomatic we allow `ab_suffix` to be `None`, but libavb requires a valid
430     // pointer to an empty string in this case, not NULL.
431     let ab_suffix = ab_suffix.unwrap_or(CStr::from_bytes_with_nul(b"\0").unwrap());
432 
433     let ops_bridge = pin!(ops::OpsBridge::new(ops));
434     let mut out_data: *mut AvbSlotVerifyData = null_mut();
435 
436     // Call the libavb verification function.
437     //
438     // Note: do not use the `?` operator to return-early here; in some cases `out_data` will be
439     // allocated and returned even on verification failure, and we need to take ownership of it
440     // or else the memory will leak.
441     //
442     // SAFETY:
443     // * `ops_bridge.init_and_get_c_ops()` gives us a valid `AvbOps`.
444     // * we've properly initialized all objects passed into libavb.
445     // * if `out_data` is non-null on return, we take ownership via `SlotVerifyData`.
446     let result = slot_verify_enum_to_result(unsafe {
447         avb_slot_verify(
448             ops_bridge.init_and_get_c_ops(),
449             partitions_array.as_ptr(),
450             ab_suffix.as_ptr(),
451             flags,
452             hashtree_error_mode,
453             &mut out_data,
454         )
455     });
456 
457     // If `out_data` is non-null, take ownership so memory gets released on drop.
458     let data = match out_data.is_null() {
459         true => None,
460         // SAFETY: `out_data` was properly allocated by libavb and ownership has passed to us.
461         false => Some(unsafe { SlotVerifyData::new(out_data, ops)? }),
462     };
463 
464     // Fold the verify data into the result.
465     match result {
466         // libavb will always provide verification data on success.
467         Ok(()) => Ok(data.unwrap()),
468         // Data may also be provided on verification failure, fold it into the error.
469         Err(SlotVerifyError::Verification(None)) => Err(SlotVerifyError::Verification(data)),
470         // No other error provides verification data.
471         Err(e) => Err(e),
472     }
473 }
474