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