• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
114 impl fmt::Display for VbmetaData {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result115     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116         write!(f, "{:?}: {:?}", self.partition_name(), self.verify_result())
117     }
118 }
119 
120 /// Forwards to `Display` formatting; the default `Debug` formatting implementation isn't very
121 /// useful as it's mostly raw pointer addresses.
122 impl fmt::Debug for VbmetaData {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result123     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124         fmt::Display::fmt(self, f)
125     }
126 }
127 
128 /// Wraps a raw C `AvbPartitionData` struct.
129 ///
130 /// This provides a Rust safe view over the raw data; no copies are made.
131 #[repr(transparent)]
132 pub struct PartitionData(AvbPartitionData);
133 
134 impl PartitionData {
135     /// Validates the internal data so the accessors can be fail-free. This should be called on all
136     /// `PartitionData` objects before they are handed to the user.
137     ///
138     /// Normally this would be done in a `new()` function but we never instantiate `PartitionData`
139     /// objects ourselves, we just cast them from the C structs provided by libavb.
140     ///
141     /// Returns `Err(SlotVerifyError::Internal)` on failure.
validate(&self) -> SlotVerifyNoDataResult<()>142     fn validate(&self) -> SlotVerifyNoDataResult<()> {
143         check_nonnull(self.0.partition_name)?;
144         check_nonnull(self.0.data)?;
145         Ok(())
146     }
147 
148     /// Returns the name of the partition this image was loaded from.
partition_name(&self) -> &CStr149     pub fn partition_name(&self) -> &CStr {
150         // SAFETY:
151         // * libavb gives us a properly-allocated and nul-terminated string.
152         // * the returned contents remain valid and unmodified while we exist.
153         unsafe { CStr::from_ptr(self.0.partition_name) }
154     }
155 
156     /// Returns the image contents.
data(&self) -> &[u8]157     pub fn data(&self) -> &[u8] {
158         // SAFETY:
159         // * libavb gives us a properly-allocated byte array.
160         // * the returned contents remain valid and unmodified while we exist.
161         unsafe { slice::from_raw_parts(self.0.data, self.0.data_size) }
162     }
163 
164     /// Returns whether this partition was preloaded via `get_preloaded_partition()`.
preloaded(&self) -> bool165     pub fn preloaded(&self) -> bool {
166         self.0.preloaded
167     }
168 
169     /// Returns the verification result for this partition.
170     ///
171     /// Only top-level `Verification` errors will contain valid `SlotVerifyData` objects, if this
172     /// individual partition returns a `Verification` error the error will always contain `None`.
verify_result(&self) -> SlotVerifyNoDataResult<()>173     pub fn verify_result(&self) -> SlotVerifyNoDataResult<()> {
174         slot_verify_enum_to_result(self.0.verify_result)
175     }
176 }
177 
178 /// A "(p)" after the partition name indicates a preloaded partition.
179 impl fmt::Display for PartitionData {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result180     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
181         write!(
182             f,
183             "{:?}{}: {:?}",
184             self.partition_name(),
185             match self.preloaded() {
186                 true => "(p)",
187                 false => "",
188             },
189             self.verify_result()
190         )
191     }
192 }
193 
194 /// Forwards to `Display` formatting; the default `Debug` formatting implementation isn't very
195 /// useful as it's mostly raw pointer addresses.
196 impl fmt::Debug for PartitionData {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result197     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
198         fmt::Display::fmt(self, f)
199     }
200 }
201 
202 /// Wraps a raw C `AvbSlotVerifyData` struct.
203 ///
204 /// This provides a Rust safe view over the raw data; no copies are made.
205 ///
206 /// # Lifetimes
207 /// * `'a`: the lifetime of any preloaded partition data borrowed from an `Ops<'a>` object.
208 ///
209 /// If the `Ops` doesn't provide any preloaded data, `SlotVerifyData` doesn't borrow anything
210 /// and instead allocates and owns all data internally, freeing it accordingly on `Drop`. In this
211 /// case, `'a` can be `'static` which imposes no lifetime restrictions on `SlotVerifyData`.
212 pub struct SlotVerifyData<'a> {
213     /// Internally owns the underlying data and deletes it on drop.
214     raw_data: NonNull<AvbSlotVerifyData>,
215 
216     /// This provides the necessary lifetimes so the compiler can make sure that the preloaded
217     /// partition data stays alive at least as long as we do, since the underlying
218     /// `AvbSlotVerifyData` may wrap this data rather than making a copy.
219     //
220     // We do not want to actually borrow an `Ops` here, since in some cases `Ops` is just a
221     // temporary object and may go out of scope before us. The only shared data is the preloaded
222     // partition contents, not the entire `Ops` object.
223     _preloaded: PhantomData<&'a [u8]>,
224 }
225 
226 // Useful so that `SlotVerifyError`, which may hold a `SlotVerifyData`, can derive `PartialEq`.
227 impl<'a> PartialEq for SlotVerifyData<'a> {
eq(&self, other: &Self) -> bool228     fn eq(&self, other: &Self) -> bool {
229         // A `SlotVerifyData` uniquely owns the underlying data so is only equal to itself.
230         ptr::eq(self, other)
231     }
232 }
233 
234 impl<'a> Eq for SlotVerifyData<'a> {}
235 
236 impl<'a> SlotVerifyData<'a> {
237     /// Creates a `SlotVerifyData` wrapping the given raw `AvbSlotVerifyData`.
238     ///
239     /// The returned `SlotVerifyData` will take ownership of the given `AvbSlotVerifyData` and
240     /// properly release the allocated memory when it drops.
241     ///
242     /// If `ops` provided any preloaded data, the returned `SlotVerifyData` also borrows the data to
243     /// account for the underlying `AvbSlotVerifyData` holding a pointer to it. If there was no
244     /// preloaded data, then `SlotVerifyData` owns all its data.
245     ///
246     /// # Arguments
247     /// * `data`: a `AvbSlotVerifyData` object created by libavb using `ops`.
248     /// * `ops`: the user-provided `Ops` object that was used for verification; only used here to
249     ///          grab the preloaded data lifetime.
250     ///
251     /// # Returns
252     /// The new object, or `Err(SlotVerifyError::Internal)` if the data looks invalid.
253     ///
254     /// # Safety
255     /// * `data` must be a valid `AvbSlotVerifyData` object created by libavb using `ops`.
256     /// * after calling this function, do not access `data` except through the returned object
new( data: *mut AvbSlotVerifyData, _ops: &dyn Ops<'a>, ) -> SlotVerifyNoDataResult<Self>257     unsafe fn new(
258         data: *mut AvbSlotVerifyData,
259         _ops: &dyn Ops<'a>,
260     ) -> SlotVerifyNoDataResult<Self> {
261         let ret = Self {
262             raw_data: NonNull::new(data).ok_or(SlotVerifyError::Internal)?,
263             _preloaded: PhantomData,
264         };
265 
266         // Validate all the contained data here so accessors will never fail.
267         // SAFETY: `raw_data` points to a valid `AvbSlotVerifyData` object owned by us.
268         let data = unsafe { ret.raw_data.as_ref() };
269         check_nonnull(data.ab_suffix)?;
270         check_nonnull(data.vbmeta_images)?;
271         check_nonnull(data.loaded_partitions)?;
272         check_nonnull(data.cmdline)?;
273         ret.vbmeta_data().iter().try_for_each(|v| v.validate())?;
274         ret.partition_data().iter().try_for_each(|i| i.validate())?;
275 
276         Ok(ret)
277     }
278 
279     /// Returns the slot suffix string.
ab_suffix(&self) -> &CStr280     pub fn ab_suffix(&self) -> &CStr {
281         // SAFETY:
282         // * `raw_data` points to a valid `AvbSlotVerifyData` object owned by us.
283         // * libavb gives us a properly-allocated and nul-terminated string.
284         // * the returned contents remain valid and unmodified while we exist.
285         unsafe { CStr::from_ptr(self.raw_data.as_ref().ab_suffix) }
286     }
287 
288     /// Returns the `VbmetaData` structs.
vbmeta_data(&self) -> &[VbmetaData]289     pub fn vbmeta_data(&self) -> &[VbmetaData] {
290         // SAFETY:
291         // * `raw_data` points to a valid `AvbSlotVerifyData` object owned by us.
292         // * libavb gives us a properly-allocated array of structs.
293         // * the returned contents remain valid and unmodified while we exist.
294         unsafe {
295             slice::from_raw_parts(
296                 // `repr(transparent)` means we can cast between these types.
297                 self.raw_data.as_ref().vbmeta_images as *const VbmetaData,
298                 self.raw_data.as_ref().num_vbmeta_images,
299             )
300         }
301     }
302 
303     /// Returns the `PartitionData` structs.
partition_data(&self) -> &[PartitionData]304     pub fn partition_data(&self) -> &[PartitionData] {
305         // SAFETY:
306         // * `raw_data` points to a valid `AvbSlotVerifyData` object owned by us.
307         // * libavb gives us a properly-allocated array of structs.
308         // * the returned contents remain valid and unmodified while we exist.
309         unsafe {
310             slice::from_raw_parts(
311                 // `repr(transparent)` means we can cast between these types.
312                 self.raw_data.as_ref().loaded_partitions as *const PartitionData,
313                 self.raw_data.as_ref().num_loaded_partitions,
314             )
315         }
316     }
317 
318     /// Returns the kernel commandline.
cmdline(&self) -> &CStr319     pub fn cmdline(&self) -> &CStr {
320         // SAFETY:
321         // * `raw_data` points to a valid `AvbSlotVerifyData` object owned by us.
322         // * libavb gives us a properly-allocated and nul-terminated string.
323         // * the returned contents remain valid and unmodified while we exist.
324         unsafe { CStr::from_ptr(self.raw_data.as_ref().cmdline) }
325     }
326 
327     /// Returns the rollback indices.
rollback_indexes(&self) -> &[u64]328     pub fn rollback_indexes(&self) -> &[u64] {
329         // SAFETY: `raw_data` points to a valid `AvbSlotVerifyData` object owned by us.
330         &unsafe { self.raw_data.as_ref() }.rollback_indexes[..]
331     }
332 
333     /// Returns the resolved hashtree error mode.
resolved_hashtree_error_mode(&self) -> HashtreeErrorMode334     pub fn resolved_hashtree_error_mode(&self) -> HashtreeErrorMode {
335         // SAFETY: `raw_data` points to a valid `AvbSlotVerifyData` object owned by us.
336         unsafe { self.raw_data.as_ref() }.resolved_hashtree_error_mode
337     }
338 }
339 
340 /// Frees any internally-allocated and owned data.
341 impl<'a> Drop for SlotVerifyData<'a> {
drop(&mut self)342     fn drop(&mut self) {
343         // SAFETY:
344         // * `raw_data` points to a valid `AvbSlotVerifyData` object owned by us.
345         // * libavb created the object and requires us to free it by calling this function.
346         unsafe { avb_slot_verify_data_free(self.raw_data.as_ptr()) };
347     }
348 }
349 
350 /// Implements `Display` to make it easy to print some basic information.
351 ///
352 /// This implementation will print the slot, partition name, and verification status for all
353 /// vbmetadata and images.
354 impl<'a> fmt::Display for SlotVerifyData<'a> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result355     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
356         write!(
357             f,
358             "slot: {:?}, vbmeta: {:?}, images: {:?}",
359             self.ab_suffix(),
360             self.vbmeta_data(),
361             self.partition_data()
362         )
363     }
364 }
365 
366 /// Forwards to `Display` formatting; the default `Debug` formatting implementation isn't very
367 /// useful as it's mostly raw pointer addresses.
368 impl<'a> fmt::Debug for SlotVerifyData<'a> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result369     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
370         fmt::Display::fmt(self, f)
371     }
372 }
373 
374 /// Performs verification of the requested images.
375 ///
376 /// This wraps `avb_slot_verify()` for Rust, see the original docs for more details.
377 ///
378 /// # Arguments
379 /// * `ops`: implementation of the required verification callbacks.
380 /// * `requested_partition`: the set of partition names to verify.
381 /// * `ab_suffix`: the slot suffix to append to the partition names, or None.
382 /// * `flags`: flags to configure verification.
383 /// * `hashtree_error_mode`: desired error handling behavior.
384 ///
385 /// # Returns
386 /// `Ok` if verification completed successfully, the verification error otherwise. `SlotVerifyData`
387 /// will be returned in two cases:
388 ///
389 /// 1. always returned on verification success
390 /// 2. if `AllowVerificationError` is given in `flags`, it will also be returned on verification
391 ///    failure
392 ///
393 /// A returned `SlotVerifyData` will also borrow any preloaded data provided by `ops`. The `ops`
394 /// object itself can go out of scope, but any preloaded data it could provide must outlive the
395 /// 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>>396 pub fn slot_verify<'a>(
397     ops: &mut dyn Ops<'a>,
398     requested_partitions: &[&CStr],
399     ab_suffix: Option<&CStr>,
400     flags: SlotVerifyFlags,
401     hashtree_error_mode: HashtreeErrorMode,
402 ) -> SlotVerifyResult<'a, SlotVerifyData<'a>> {
403     // libavb detects the size of the `requested_partitions` array by NULL termination. Expecting
404     // the Rust caller to do this would make the API much more awkward, so we populate a
405     // NULL-terminated array of c-string pointers ourselves. For now we use a fixed-sized array
406     // rather than dynamically allocating, 8 should be more than enough.
407     const MAX_PARTITION_ARRAY_SIZE: usize = 8 + 1; // Max 8 partition names + 1 for NULL terminator.
408     if requested_partitions.len() >= MAX_PARTITION_ARRAY_SIZE {
409         return Err(SlotVerifyError::Internal);
410     }
411     let mut partitions_array = [null() as *const c_char; MAX_PARTITION_ARRAY_SIZE];
412     for (source, dest) in requested_partitions.iter().zip(partitions_array.iter_mut()) {
413         *dest = source.as_ptr();
414     }
415 
416     // To be more Rust idiomatic we allow `ab_suffix` to be `None`, but libavb requires a valid
417     // pointer to an empty string in this case, not NULL.
418     let ab_suffix = ab_suffix.unwrap_or(CStr::from_bytes_with_nul(b"\0").unwrap());
419 
420     let ops_bridge = pin!(ops::OpsBridge::new(ops));
421     let mut out_data: *mut AvbSlotVerifyData = null_mut();
422 
423     // Call the libavb verification function.
424     //
425     // Note: do not use the `?` operator to return-early here; in some cases `out_data` will be
426     // allocated and returned even on verification failure, and we need to take ownership of it
427     // or else the memory will leak.
428     //
429     // SAFETY:
430     // * `ops_bridge.init_and_get_c_ops()` gives us a valid `AvbOps`.
431     // * we've properly initialized all objects passed into libavb.
432     // * if `out_data` is non-null on return, we take ownership via `SlotVerifyData`.
433     let result = slot_verify_enum_to_result(unsafe {
434         avb_slot_verify(
435             ops_bridge.init_and_get_c_ops(),
436             partitions_array.as_ptr(),
437             ab_suffix.as_ptr(),
438             flags,
439             hashtree_error_mode,
440             &mut out_data,
441         )
442     });
443 
444     // If `out_data` is non-null, take ownership so memory gets released on drop.
445     let data = match out_data.is_null() {
446         true => None,
447         // SAFETY: `out_data` was properly allocated by libavb and ownership has passed to us.
448         false => Some(unsafe { SlotVerifyData::new(out_data, ops)? }),
449     };
450 
451     // Fold the verify data into the result.
452     match result {
453         // libavb will always provide verification data on success.
454         Ok(()) => Ok(data.unwrap()),
455         // Data may also be provided on verification failure, fold it into the error.
456         Err(SlotVerifyError::Verification(None)) => Err(SlotVerifyError::Verification(data)),
457         // No other error provides verification data.
458         Err(e) => Err(e),
459     }
460 }
461