• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 //! Rust bindgen interface for FSVerity Metadata file (.fsv_meta)
18 use authfs_fsverity_metadata_bindgen::{
19     fsverity_descriptor, fsverity_metadata_header, FSVERITY_HASH_ALG_SHA256,
20     FSVERITY_SIGNATURE_TYPE_NONE, FSVERITY_SIGNATURE_TYPE_PKCS7, FSVERITY_SIGNATURE_TYPE_RAW,
21 };
22 
23 use ring::digest::{Context, SHA256};
24 use std::cmp::min;
25 use std::ffi::OsString;
26 use std::fs::File;
27 use std::io::{self, Read, Seek};
28 use std::mem::{size_of, zeroed};
29 use std::os::unix::fs::{FileExt, MetadataExt};
30 use std::path::{Path, PathBuf};
31 use std::slice::from_raw_parts_mut;
32 
33 /// Offset of `descriptor` in `struct fsverity_metadatata_header`.
34 const DESCRIPTOR_OFFSET: usize = 4;
35 
36 /// Structure for parsed metadata.
37 pub struct FSVerityMetadata {
38     /// Header for the metadata.
39     pub header: fsverity_metadata_header,
40 
41     /// fs-verity digest of the file, with hash algorithm defined in the fs-verity descriptor.
42     pub digest: Vec<u8>,
43 
44     /// Optional signature for the metadata.
45     pub signature: Option<Vec<u8>>,
46 
47     metadata_file: File,
48 
49     merkle_tree_offset: u64,
50 }
51 
52 impl FSVerityMetadata {
53     /// Read the raw Merkle tree from the metadata, if it exists. The API semantics is similar to a
54     /// regular pread(2), and may not return full requested buffer.
read_merkle_tree(&self, offset: u64, buf: &mut [u8]) -> io::Result<usize>55     pub fn read_merkle_tree(&self, offset: u64, buf: &mut [u8]) -> io::Result<usize> {
56         let file_size = self.metadata_file.metadata()?.size();
57         let start = self.merkle_tree_offset + offset;
58         let end = min(file_size, start + buf.len() as u64);
59         let read_size = (end - start) as usize;
60         debug_assert!(read_size <= buf.len());
61         if read_size == 0 {
62             Ok(0)
63         } else {
64             self.metadata_file.read_exact_at(&mut buf[..read_size], start)?;
65             Ok(read_size)
66         }
67     }
68 }
69 
70 /// Common block and page size in Linux.
71 pub const CHUNK_SIZE: u64 = authfs_fsverity_metadata_bindgen::CHUNK_SIZE;
72 
73 /// Derive a path of metadata for a given path.
74 /// e.g. "system/framework/foo.jar" -> "system/framework/foo.jar.fsv_meta"
get_fsverity_metadata_path(path: &Path) -> PathBuf75 pub fn get_fsverity_metadata_path(path: &Path) -> PathBuf {
76     let mut os_string: OsString = path.into();
77     os_string.push(".fsv_meta");
78     os_string.into()
79 }
80 
81 /// Parse metadata from given file, and returns a structure for the metadata.
parse_fsverity_metadata(mut metadata_file: File) -> io::Result<Box<FSVerityMetadata>>82 pub fn parse_fsverity_metadata(mut metadata_file: File) -> io::Result<Box<FSVerityMetadata>> {
83     let (header, digest) = {
84         // SAFETY: The header doesn't include any pointers.
85         let mut header: fsverity_metadata_header = unsafe { zeroed() };
86 
87         // SAFETY: fsverity_metadata_header is packed, so reading/write from/to the back_buffer
88         // won't overflow.
89         let back_buffer = unsafe {
90             from_raw_parts_mut(
91                 &mut header as *mut fsverity_metadata_header as *mut u8,
92                 size_of::<fsverity_metadata_header>(),
93             )
94         };
95         metadata_file.read_exact(back_buffer)?;
96 
97         // Digest needs to be calculated with the raw value (without changing the endianness).
98         let digest = match header.descriptor.hash_algorithm {
99             FSVERITY_HASH_ALG_SHA256 => {
100                 let mut context = Context::new(&SHA256);
101                 context.update(
102                     &back_buffer
103                         [DESCRIPTOR_OFFSET..DESCRIPTOR_OFFSET + size_of::<fsverity_descriptor>()],
104                 );
105                 Ok(context.finish().as_ref().to_owned())
106             }
107             alg => Err(io::Error::new(
108                 io::ErrorKind::Other,
109                 format!("Unsupported hash algorithm {}, continue (likely failing soon)", alg),
110             )),
111         }?;
112 
113         // TODO(inseob): This doesn't seem ideal. Maybe we can consider nom?
114         header.version = u32::from_le(header.version);
115         header.descriptor.data_size = u64::from_le(header.descriptor.data_size);
116         header.signature_type = u32::from_le(header.signature_type);
117         header.signature_size = u32::from_le(header.signature_size);
118         (header, digest)
119     };
120 
121     if header.version != 1 {
122         return Err(io::Error::new(io::ErrorKind::Other, "unsupported metadata version"));
123     }
124 
125     let signature = match header.signature_type {
126         FSVERITY_SIGNATURE_TYPE_NONE => None,
127         FSVERITY_SIGNATURE_TYPE_PKCS7 | FSVERITY_SIGNATURE_TYPE_RAW => {
128             // TODO: unpad pkcs7?
129             let mut buf = vec![0u8; header.signature_size as usize];
130             metadata_file.read_exact(&mut buf)?;
131             Some(buf)
132         }
133         _ => return Err(io::Error::new(io::ErrorKind::Other, "unknown signature type")),
134     };
135 
136     // merkle tree is at the next 4K boundary
137     let merkle_tree_offset =
138         (metadata_file.stream_position()? + CHUNK_SIZE - 1) / CHUNK_SIZE * CHUNK_SIZE;
139 
140     Ok(Box::new(FSVerityMetadata { header, digest, signature, metadata_file, merkle_tree_offset }))
141 }
142