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