blob: d03e3ca496b6a3965d21cd924569ac596abbd4e5 [file] [log] [blame]
Victor Hsieh88ac6ca2020-11-13 15:20:24 -08001/*
2 * Copyright (C) 2020 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//! This crate implements AuthFS, a FUSE-based, non-generic filesystem where file access is
18//! authenticated. This filesystem assumes the underlying layer is not trusted, e.g. file may be
19//! provided by an untrusted host/VM, so that the content can't be simply trusted. However, with a
20//! public key from a trusted party, this filesystem can still verify a (read-only) file signed by
21//! the trusted party even if the host/VM as the blob provider is malicious. With the Merkle tree,
22//! each read of file block can be verified individually only when needed.
23//!
24//! AuthFS only serve files that are specifically configured. A file configuration may include the
25//! source (e.g. local file or remote file server), verification method (e.g. certificate for
26//! fs-verity verification, or no verification if expected to mount over dm-verity), and file ID.
27//! Regardless of the actual file name, the exposed file names through AuthFS are currently integer,
28//! e.g. /mountpoint/42.
29
Andrew Walbrancc093862021-03-05 16:59:35 +000030use anyhow::{bail, Context, Result};
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080031use std::collections::BTreeMap;
32use std::fs::File;
33use std::io::Read;
Victor Hsieh6cf75b52021-04-01 12:45:49 -070034use std::path::{Path, PathBuf};
Victor Hsiehf01f3232020-12-11 13:31:31 -080035use std::sync::{Arc, Mutex};
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080036use structopt::StructOpt;
37
38mod auth;
39mod common;
40mod crypto;
Victor Hsieh09e26262021-03-03 16:00:55 -080041mod file;
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080042mod fsverity;
43mod fusefs;
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080044
45use auth::FakeAuthenticator;
Victor Hsieh6a47e7f2021-03-03 15:53:49 -080046use file::{LocalFileReader, RemoteFileEditor, RemoteFileReader, RemoteMerkleTreeReader};
47use fsverity::{VerifiedFileEditor, VerifiedFileReader};
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080048use fusefs::{FileConfig, Inode};
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080049
50#[derive(StructOpt)]
Victor Hsiehf01f3232020-12-11 13:31:31 -080051struct Args {
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080052 /// Mount point of AuthFS.
53 #[structopt(parse(from_os_str))]
54 mount_point: PathBuf,
55
Victor Hsieh2445e332021-06-04 16:44:53 -070056 /// CID of the VM where the service runs.
57 #[structopt(long)]
58 cid: Option<u32>,
59
Victor Hsieh09e26262021-03-03 16:00:55 -080060 /// A read-only remote file with integrity check. Can be multiple.
Victor Hsiehf01f3232020-12-11 13:31:31 -080061 ///
62 /// For example, `--remote-verified-file 5:10:1234:/path/to/cert` tells the filesystem to
63 /// associate entry 5 with a remote file 10 of size 1234 bytes, and need to be verified against
64 /// the /path/to/cert.
Victor Hsieh09e26262021-03-03 16:00:55 -080065 #[structopt(long, parse(try_from_str = parse_remote_ro_file_option))]
66 remote_ro_file: Vec<OptionRemoteRoFile>,
Victor Hsiehf01f3232020-12-11 13:31:31 -080067
Victor Hsieh09e26262021-03-03 16:00:55 -080068 /// A read-only remote file without integrity check. Can be multiple.
Victor Hsiehf01f3232020-12-11 13:31:31 -080069 ///
70 /// For example, `--remote-unverified-file 5:10:1234` tells the filesystem to associate entry 5
71 /// with a remote file 10 of size 1234 bytes.
Victor Hsieh09e26262021-03-03 16:00:55 -080072 #[structopt(long, parse(try_from_str = parse_remote_ro_file_unverified_option))]
73 remote_ro_file_unverified: Vec<OptionRemoteRoFileUnverified>,
Victor Hsiehf01f3232020-12-11 13:31:31 -080074
Victor Hsieh6a47e7f2021-03-03 15:53:49 -080075 /// A new read-writable remote file with integrity check. Can be multiple.
76 ///
77 /// For example, `--remote-new-verified-file 12:34` tells the filesystem to associate entry 12
78 /// with a remote file 34.
79 #[structopt(long, parse(try_from_str = parse_remote_new_rw_file_option))]
80 remote_new_rw_file: Vec<OptionRemoteRwFile>,
81
Victor Hsieh09e26262021-03-03 16:00:55 -080082 /// Debug only. A read-only local file with integrity check. Can be multiple.
83 #[structopt(long, parse(try_from_str = parse_local_file_ro_option))]
84 local_ro_file: Vec<OptionLocalFileRo>,
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080085
Victor Hsieh09e26262021-03-03 16:00:55 -080086 /// Debug only. A read-only local file without integrity check. Can be multiple.
87 #[structopt(long, parse(try_from_str = parse_local_ro_file_unverified_ro_option))]
88 local_ro_file_unverified: Vec<OptionLocalRoFileUnverified>,
Victor Hsieh9d0ab622021-04-26 17:07:02 -070089
90 /// Enable debugging features.
91 #[structopt(long)]
92 debug: bool,
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080093}
94
Victor Hsieh9ab13e52021-06-29 09:23:29 -070095impl Args {
96 fn has_remote_files(&self) -> bool {
97 !self.remote_ro_file.is_empty()
98 || !self.remote_ro_file_unverified.is_empty()
99 || !self.remote_new_rw_file.is_empty()
100 }
101}
102
Victor Hsieh09e26262021-03-03 16:00:55 -0800103struct OptionRemoteRoFile {
Victor Hsiehf01f3232020-12-11 13:31:31 -0800104 ino: Inode,
105
106 /// ID to refer to the remote file.
107 remote_id: i32,
108
109 /// Expected size of the remote file. Necessary for signature check and Merkle tree
110 /// verification.
111 file_size: u64,
112
113 /// Certificate to verify the authenticity of the file's fs-verity signature.
114 /// TODO(170494765): Implement PKCS#7 signature verification.
115 _certificate_path: PathBuf,
116}
117
Victor Hsieh09e26262021-03-03 16:00:55 -0800118struct OptionRemoteRoFileUnverified {
Victor Hsiehf01f3232020-12-11 13:31:31 -0800119 ino: Inode,
120
121 /// ID to refer to the remote file.
122 remote_id: i32,
123
124 /// Expected size of the remote file.
125 file_size: u64,
126}
127
Victor Hsieh6a47e7f2021-03-03 15:53:49 -0800128struct OptionRemoteRwFile {
129 ino: Inode,
130
131 /// ID to refer to the remote file.
132 remote_id: i32,
133}
134
Victor Hsieh09e26262021-03-03 16:00:55 -0800135struct OptionLocalFileRo {
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800136 ino: Inode,
Victor Hsiehf01f3232020-12-11 13:31:31 -0800137
138 /// Local path of the backing file.
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800139 file_path: PathBuf,
Victor Hsiehf01f3232020-12-11 13:31:31 -0800140
141 /// Local path of the backing file's fs-verity Merkle tree dump.
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800142 merkle_tree_dump_path: PathBuf,
Victor Hsiehf01f3232020-12-11 13:31:31 -0800143
144 /// Local path of fs-verity signature for the backing file.
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800145 signature_path: PathBuf,
Victor Hsiehf01f3232020-12-11 13:31:31 -0800146
147 /// Certificate to verify the authenticity of the file's fs-verity signature.
148 /// TODO(170494765): Implement PKCS#7 signature verification.
149 _certificate_path: PathBuf,
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800150}
151
Victor Hsieh09e26262021-03-03 16:00:55 -0800152struct OptionLocalRoFileUnverified {
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800153 ino: Inode,
Victor Hsiehf01f3232020-12-11 13:31:31 -0800154
155 /// Local path of the backing file.
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800156 file_path: PathBuf,
157}
158
Victor Hsieh09e26262021-03-03 16:00:55 -0800159fn parse_remote_ro_file_option(option: &str) -> Result<OptionRemoteRoFile> {
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800160 let strs: Vec<&str> = option.split(':').collect();
161 if strs.len() != 4 {
162 bail!("Invalid option: {}", option);
163 }
Victor Hsieh09e26262021-03-03 16:00:55 -0800164 Ok(OptionRemoteRoFile {
Victor Hsiehf01f3232020-12-11 13:31:31 -0800165 ino: strs[0].parse::<Inode>()?,
166 remote_id: strs[1].parse::<i32>()?,
167 file_size: strs[2].parse::<u64>()?,
168 _certificate_path: PathBuf::from(strs[3]),
169 })
170}
171
Victor Hsieh09e26262021-03-03 16:00:55 -0800172fn parse_remote_ro_file_unverified_option(option: &str) -> Result<OptionRemoteRoFileUnverified> {
Victor Hsiehf01f3232020-12-11 13:31:31 -0800173 let strs: Vec<&str> = option.split(':').collect();
174 if strs.len() != 3 {
175 bail!("Invalid option: {}", option);
176 }
Victor Hsieh09e26262021-03-03 16:00:55 -0800177 Ok(OptionRemoteRoFileUnverified {
Victor Hsiehf01f3232020-12-11 13:31:31 -0800178 ino: strs[0].parse::<Inode>()?,
179 remote_id: strs[1].parse::<i32>()?,
180 file_size: strs[2].parse::<u64>()?,
181 })
182}
183
Victor Hsieh6a47e7f2021-03-03 15:53:49 -0800184fn parse_remote_new_rw_file_option(option: &str) -> Result<OptionRemoteRwFile> {
185 let strs: Vec<&str> = option.split(':').collect();
186 if strs.len() != 2 {
187 bail!("Invalid option: {}", option);
188 }
189 Ok(OptionRemoteRwFile {
190 ino: strs[0].parse::<Inode>().unwrap(),
191 remote_id: strs[1].parse::<i32>().unwrap(),
192 })
193}
194
Victor Hsieh09e26262021-03-03 16:00:55 -0800195fn parse_local_file_ro_option(option: &str) -> Result<OptionLocalFileRo> {
Victor Hsiehf01f3232020-12-11 13:31:31 -0800196 let strs: Vec<&str> = option.split(':').collect();
197 if strs.len() != 5 {
198 bail!("Invalid option: {}", option);
199 }
Victor Hsieh09e26262021-03-03 16:00:55 -0800200 Ok(OptionLocalFileRo {
Victor Hsiehf01f3232020-12-11 13:31:31 -0800201 ino: strs[0].parse::<Inode>()?,
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800202 file_path: PathBuf::from(strs[1]),
203 merkle_tree_dump_path: PathBuf::from(strs[2]),
204 signature_path: PathBuf::from(strs[3]),
Victor Hsiehf01f3232020-12-11 13:31:31 -0800205 _certificate_path: PathBuf::from(strs[4]),
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800206 })
207}
208
Victor Hsieh09e26262021-03-03 16:00:55 -0800209fn parse_local_ro_file_unverified_ro_option(option: &str) -> Result<OptionLocalRoFileUnverified> {
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800210 let strs: Vec<&str> = option.split(':').collect();
211 if strs.len() != 2 {
212 bail!("Invalid option: {}", option);
213 }
Victor Hsieh09e26262021-03-03 16:00:55 -0800214 Ok(OptionLocalRoFileUnverified {
Victor Hsiehf01f3232020-12-11 13:31:31 -0800215 ino: strs[0].parse::<Inode>()?,
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800216 file_path: PathBuf::from(strs[1]),
217 })
218}
219
Victor Hsieh2445e332021-06-04 16:44:53 -0700220fn new_config_remote_verified_file(
221 service: file::VirtFdService,
222 remote_id: i32,
223 file_size: u64,
224) -> Result<FileConfig> {
Andrew Walbrancc093862021-03-05 16:59:35 +0000225 let signature = service.readFsveritySignature(remote_id).context("Failed to read signature")?;
Victor Hsiehf01f3232020-12-11 13:31:31 -0800226
227 let service = Arc::new(Mutex::new(service));
228 let authenticator = FakeAuthenticator::always_succeed();
Victor Hsieh1bcf4112021-03-19 14:26:57 -0700229 Ok(FileConfig::RemoteVerifiedReadonlyFile {
230 reader: VerifiedFileReader::new(
Victor Hsiehf01f3232020-12-11 13:31:31 -0800231 &authenticator,
Victor Hsieh09e26262021-03-03 16:00:55 -0800232 RemoteFileReader::new(Arc::clone(&service), remote_id),
Victor Hsiehf01f3232020-12-11 13:31:31 -0800233 file_size,
234 signature,
Victor Hsieh09e26262021-03-03 16:00:55 -0800235 RemoteMerkleTreeReader::new(Arc::clone(&service), remote_id),
Victor Hsiehf01f3232020-12-11 13:31:31 -0800236 )?,
237 file_size,
Victor Hsieh1bcf4112021-03-19 14:26:57 -0700238 })
Victor Hsiehf01f3232020-12-11 13:31:31 -0800239}
240
Victor Hsieh2445e332021-06-04 16:44:53 -0700241fn new_config_remote_unverified_file(
242 service: file::VirtFdService,
243 remote_id: i32,
244 file_size: u64,
245) -> Result<FileConfig> {
246 let reader = RemoteFileReader::new(Arc::new(Mutex::new(service)), remote_id);
Victor Hsieh1bcf4112021-03-19 14:26:57 -0700247 Ok(FileConfig::RemoteUnverifiedReadonlyFile { reader, file_size })
Victor Hsiehf01f3232020-12-11 13:31:31 -0800248}
249
Victor Hsieh09e26262021-03-03 16:00:55 -0800250fn new_config_local_ro_file(
Victor Hsieh6cf75b52021-04-01 12:45:49 -0700251 protected_file: &Path,
252 merkle_tree_dump: &Path,
253 signature: &Path,
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800254) -> Result<FileConfig> {
255 let file = File::open(&protected_file)?;
256 let file_size = file.metadata()?.len();
Victor Hsieh09e26262021-03-03 16:00:55 -0800257 let file_reader = LocalFileReader::new(file)?;
258 let merkle_tree_reader = LocalFileReader::new(File::open(merkle_tree_dump)?)?;
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800259 let authenticator = FakeAuthenticator::always_succeed();
260 let mut sig = Vec::new();
261 let _ = File::open(signature)?.read_to_end(&mut sig)?;
Victor Hsieh1bcf4112021-03-19 14:26:57 -0700262 let reader =
Victor Hsieh09e26262021-03-03 16:00:55 -0800263 VerifiedFileReader::new(&authenticator, file_reader, file_size, sig, merkle_tree_reader)?;
Victor Hsieh1bcf4112021-03-19 14:26:57 -0700264 Ok(FileConfig::LocalVerifiedReadonlyFile { reader, file_size })
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800265}
266
Victor Hsieh6cf75b52021-04-01 12:45:49 -0700267fn new_config_local_ro_file_unverified(file_path: &Path) -> Result<FileConfig> {
Victor Hsieh1bcf4112021-03-19 14:26:57 -0700268 let reader = LocalFileReader::new(File::open(file_path)?)?;
269 let file_size = reader.len();
270 Ok(FileConfig::LocalUnverifiedReadonlyFile { reader, file_size })
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800271}
272
Victor Hsieh2445e332021-06-04 16:44:53 -0700273fn new_config_remote_new_verified_file(
274 service: file::VirtFdService,
275 remote_id: i32,
276) -> Result<FileConfig> {
277 let remote_file = RemoteFileEditor::new(Arc::new(Mutex::new(service)), remote_id);
Victor Hsieh1bcf4112021-03-19 14:26:57 -0700278 Ok(FileConfig::RemoteVerifiedNewFile { editor: VerifiedFileEditor::new(remote_file) })
Victor Hsieh6a47e7f2021-03-03 15:53:49 -0800279}
280
Victor Hsiehf01f3232020-12-11 13:31:31 -0800281fn prepare_file_pool(args: &Args) -> Result<BTreeMap<Inode, FileConfig>> {
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800282 let mut file_pool = BTreeMap::new();
283
Victor Hsieh9ab13e52021-06-29 09:23:29 -0700284 if args.has_remote_files() {
285 let service = file::get_binder_service(args.cid)?;
Victor Hsieh2445e332021-06-04 16:44:53 -0700286
Victor Hsieh9ab13e52021-06-29 09:23:29 -0700287 for config in &args.remote_ro_file {
288 file_pool.insert(
289 config.ino,
290 new_config_remote_verified_file(
291 service.clone(),
292 config.remote_id,
293 config.file_size,
294 )?,
295 );
296 }
Victor Hsiehf01f3232020-12-11 13:31:31 -0800297
Victor Hsieh9ab13e52021-06-29 09:23:29 -0700298 for config in &args.remote_ro_file_unverified {
299 file_pool.insert(
300 config.ino,
301 new_config_remote_unverified_file(
302 service.clone(),
303 config.remote_id,
304 config.file_size,
305 )?,
306 );
307 }
Victor Hsiehf01f3232020-12-11 13:31:31 -0800308
Victor Hsieh9ab13e52021-06-29 09:23:29 -0700309 for config in &args.remote_new_rw_file {
310 file_pool.insert(
311 config.ino,
312 new_config_remote_new_verified_file(service.clone(), config.remote_id)?,
313 );
314 }
Victor Hsieh6a47e7f2021-03-03 15:53:49 -0800315 }
316
Victor Hsieh09e26262021-03-03 16:00:55 -0800317 for config in &args.local_ro_file {
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800318 file_pool.insert(
319 config.ino,
Victor Hsieh09e26262021-03-03 16:00:55 -0800320 new_config_local_ro_file(
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800321 &config.file_path,
322 &config.merkle_tree_dump_path,
323 &config.signature_path,
324 )?,
325 );
326 }
327
Victor Hsieh09e26262021-03-03 16:00:55 -0800328 for config in &args.local_ro_file_unverified {
329 file_pool.insert(config.ino, new_config_local_ro_file_unverified(&config.file_path)?);
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800330 }
331
332 Ok(file_pool)
333}
334
335fn main() -> Result<()> {
Victor Hsiehf01f3232020-12-11 13:31:31 -0800336 let args = Args::from_args();
Victor Hsieh9d0ab622021-04-26 17:07:02 -0700337
338 let log_level = if args.debug { log::Level::Debug } else { log::Level::Info };
339 android_logger::init_once(
340 android_logger::Config::default().with_tag("authfs").with_min_level(log_level),
341 );
342
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800343 let file_pool = prepare_file_pool(&args)?;
344 fusefs::loop_forever(file_pool, &args.mount_point)?;
Victor Hsiehf01f3232020-12-11 13:31:31 -0800345 bail!("Unexpected exit after the handler loop")
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800346}