blob: 0add77f1202e8900aec0d64aaeb5bc1b1be4d7ed [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
Victor Hsieh88e50172021-10-15 13:27:13 -070025//! source (e.g. remote file server), verification method (e.g. certificate for fs-verity
26//! verification, or no verification if expected to mount over dm-verity), and file ID. Regardless
27//! of the actual file name, the exposed file names through AuthFS are currently integer, e.g.
28//! /mountpoint/42.
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080029
Andrew Walbrancc093862021-03-05 16:59:35 +000030use anyhow::{bail, Context, Result};
Alan Stokese1b6e1c2021-10-01 12:44:49 +010031use log::error;
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080032use std::collections::BTreeMap;
Victor Hsieh50d75ac2021-09-03 14:46:55 -070033use std::convert::TryInto;
Victor Hsieh88e50172021-10-15 13:27:13 -070034use std::path::PathBuf;
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080035use structopt::StructOpt;
36
37mod auth;
38mod common;
39mod crypto;
Victor Hsieh09e26262021-03-03 16:00:55 -080040mod file;
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080041mod fsverity;
42mod fusefs;
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080043
44use auth::FakeAuthenticator;
Victor Hsieh45636232021-10-15 17:52:51 -070045use file::{RemoteDirEditor, RemoteFileEditor, RemoteFileReader, RemoteMerkleTreeReader};
Victor Hsieh6a47e7f2021-03-03 15:53:49 -080046use fsverity::{VerifiedFileEditor, VerifiedFileReader};
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080047use fusefs::{FileConfig, Inode};
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080048
49#[derive(StructOpt)]
Victor Hsiehf01f3232020-12-11 13:31:31 -080050struct Args {
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080051 /// Mount point of AuthFS.
52 #[structopt(parse(from_os_str))]
53 mount_point: PathBuf,
54
Victor Hsieh2445e332021-06-04 16:44:53 -070055 /// CID of the VM where the service runs.
56 #[structopt(long)]
Victor Hsieh1a8cd042021-09-03 16:29:45 -070057 cid: u32,
Victor Hsieh2445e332021-06-04 16:44:53 -070058
Victor Hsieh4cc3b792021-08-04 12:00:04 -070059 /// Extra options to FUSE
60 #[structopt(short = "o")]
61 extra_options: Option<String>,
62
Victor Hsieh09e26262021-03-03 16:00:55 -080063 /// A read-only remote file with integrity check. Can be multiple.
Victor Hsiehf01f3232020-12-11 13:31:31 -080064 ///
Victor Hsieh50d75ac2021-09-03 14:46:55 -070065 /// For example, `--remote-verified-file 5:10:/path/to/cert` tells the filesystem to associate
66 /// entry 5 with a remote file 10, and need to be verified against the /path/to/cert.
Victor Hsieh09e26262021-03-03 16:00:55 -080067 #[structopt(long, parse(try_from_str = parse_remote_ro_file_option))]
68 remote_ro_file: Vec<OptionRemoteRoFile>,
Victor Hsiehf01f3232020-12-11 13:31:31 -080069
Victor Hsieh09e26262021-03-03 16:00:55 -080070 /// A read-only remote file without integrity check. Can be multiple.
Victor Hsiehf01f3232020-12-11 13:31:31 -080071 ///
Victor Hsieh50d75ac2021-09-03 14:46:55 -070072 /// For example, `--remote-unverified-file 5:10` tells the filesystem to associate entry 5
73 /// with a remote file 10.
Victor Hsieh09e26262021-03-03 16:00:55 -080074 #[structopt(long, parse(try_from_str = parse_remote_ro_file_unverified_option))]
75 remote_ro_file_unverified: Vec<OptionRemoteRoFileUnverified>,
Victor Hsiehf01f3232020-12-11 13:31:31 -080076
Victor Hsieh6a47e7f2021-03-03 15:53:49 -080077 /// A new read-writable remote file with integrity check. Can be multiple.
78 ///
79 /// For example, `--remote-new-verified-file 12:34` tells the filesystem to associate entry 12
80 /// with a remote file 34.
81 #[structopt(long, parse(try_from_str = parse_remote_new_rw_file_option))]
82 remote_new_rw_file: Vec<OptionRemoteRwFile>,
83
Victor Hsieh45636232021-10-15 17:52:51 -070084 /// A new directory that is assumed empty in the backing filesystem. New files created in this
85 /// directory are integrity-protected in the same way as --remote-new-verified-file. Can be
86 /// multiple.
87 ///
88 /// For example, `--remote-new-verified-dir 12:34` tells the filesystem to associate entry 12
89 /// with a remote dir FD 34.
90 #[structopt(long, parse(try_from_str = parse_remote_new_rw_dir_option))]
91 remote_new_rw_dir: Vec<OptionRemoteRwDir>,
92
Victor Hsieh9d0ab622021-04-26 17:07:02 -070093 /// Enable debugging features.
94 #[structopt(long)]
95 debug: bool,
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080096}
97
Victor Hsieh09e26262021-03-03 16:00:55 -080098struct OptionRemoteRoFile {
Victor Hsiehf01f3232020-12-11 13:31:31 -080099 ino: Inode,
100
101 /// ID to refer to the remote file.
102 remote_id: i32,
103
Victor Hsiehf01f3232020-12-11 13:31:31 -0800104 /// Certificate to verify the authenticity of the file's fs-verity signature.
105 /// TODO(170494765): Implement PKCS#7 signature verification.
106 _certificate_path: PathBuf,
107}
108
Victor Hsieh09e26262021-03-03 16:00:55 -0800109struct OptionRemoteRoFileUnverified {
Victor Hsiehf01f3232020-12-11 13:31:31 -0800110 ino: Inode,
111
112 /// ID to refer to the remote file.
113 remote_id: i32,
Victor Hsiehf01f3232020-12-11 13:31:31 -0800114}
115
Victor Hsieh6a47e7f2021-03-03 15:53:49 -0800116struct OptionRemoteRwFile {
117 ino: Inode,
118
119 /// ID to refer to the remote file.
120 remote_id: i32,
121}
122
Victor Hsieh45636232021-10-15 17:52:51 -0700123struct OptionRemoteRwDir {
124 ino: Inode,
125
126 /// ID to refer to the remote dir.
127 remote_id: i32,
128}
129
Victor Hsieh09e26262021-03-03 16:00:55 -0800130fn parse_remote_ro_file_option(option: &str) -> Result<OptionRemoteRoFile> {
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800131 let strs: Vec<&str> = option.split(':').collect();
Victor Hsieh50d75ac2021-09-03 14:46:55 -0700132 if strs.len() != 3 {
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800133 bail!("Invalid option: {}", option);
134 }
Victor Hsieh09e26262021-03-03 16:00:55 -0800135 Ok(OptionRemoteRoFile {
Victor Hsiehf01f3232020-12-11 13:31:31 -0800136 ino: strs[0].parse::<Inode>()?,
137 remote_id: strs[1].parse::<i32>()?,
Victor Hsieh50d75ac2021-09-03 14:46:55 -0700138 _certificate_path: PathBuf::from(strs[2]),
Victor Hsiehf01f3232020-12-11 13:31:31 -0800139 })
140}
141
Victor Hsieh09e26262021-03-03 16:00:55 -0800142fn parse_remote_ro_file_unverified_option(option: &str) -> Result<OptionRemoteRoFileUnverified> {
Victor Hsiehf01f3232020-12-11 13:31:31 -0800143 let strs: Vec<&str> = option.split(':').collect();
Victor Hsieh50d75ac2021-09-03 14:46:55 -0700144 if strs.len() != 2 {
Victor Hsiehf01f3232020-12-11 13:31:31 -0800145 bail!("Invalid option: {}", option);
146 }
Victor Hsieh09e26262021-03-03 16:00:55 -0800147 Ok(OptionRemoteRoFileUnverified {
Victor Hsiehf01f3232020-12-11 13:31:31 -0800148 ino: strs[0].parse::<Inode>()?,
149 remote_id: strs[1].parse::<i32>()?,
Victor Hsiehf01f3232020-12-11 13:31:31 -0800150 })
151}
152
Victor Hsieh6a47e7f2021-03-03 15:53:49 -0800153fn parse_remote_new_rw_file_option(option: &str) -> Result<OptionRemoteRwFile> {
154 let strs: Vec<&str> = option.split(':').collect();
155 if strs.len() != 2 {
156 bail!("Invalid option: {}", option);
157 }
158 Ok(OptionRemoteRwFile {
159 ino: strs[0].parse::<Inode>().unwrap(),
160 remote_id: strs[1].parse::<i32>().unwrap(),
161 })
162}
163
Victor Hsieh45636232021-10-15 17:52:51 -0700164fn parse_remote_new_rw_dir_option(option: &str) -> Result<OptionRemoteRwDir> {
165 let strs: Vec<&str> = option.split(':').collect();
166 if strs.len() != 2 {
167 bail!("Invalid option: {}", option);
168 }
169 Ok(OptionRemoteRwDir {
170 ino: strs[0].parse::<Inode>().unwrap(),
171 remote_id: strs[1].parse::<i32>().unwrap(),
172 })
173}
174
Victor Hsieh2445e332021-06-04 16:44:53 -0700175fn new_config_remote_verified_file(
176 service: file::VirtFdService,
177 remote_id: i32,
178 file_size: u64,
179) -> Result<FileConfig> {
Andrew Walbrancc093862021-03-05 16:59:35 +0000180 let signature = service.readFsveritySignature(remote_id).context("Failed to read signature")?;
Victor Hsiehf01f3232020-12-11 13:31:31 -0800181
Victor Hsiehf01f3232020-12-11 13:31:31 -0800182 let authenticator = FakeAuthenticator::always_succeed();
Victor Hsieh88e50172021-10-15 13:27:13 -0700183 Ok(FileConfig::VerifiedReadonly {
Victor Hsieh1bcf4112021-03-19 14:26:57 -0700184 reader: VerifiedFileReader::new(
Victor Hsiehf01f3232020-12-11 13:31:31 -0800185 &authenticator,
Victor Hsiehc3d45b12021-06-30 09:16:41 -0700186 RemoteFileReader::new(service.clone(), remote_id),
Victor Hsiehf01f3232020-12-11 13:31:31 -0800187 file_size,
188 signature,
Victor Hsiehc3d45b12021-06-30 09:16:41 -0700189 RemoteMerkleTreeReader::new(service.clone(), remote_id),
Victor Hsiehf01f3232020-12-11 13:31:31 -0800190 )?,
191 file_size,
Victor Hsieh1bcf4112021-03-19 14:26:57 -0700192 })
Victor Hsiehf01f3232020-12-11 13:31:31 -0800193}
194
Victor Hsieh2445e332021-06-04 16:44:53 -0700195fn new_config_remote_unverified_file(
196 service: file::VirtFdService,
197 remote_id: i32,
198 file_size: u64,
199) -> Result<FileConfig> {
Victor Hsiehc3d45b12021-06-30 09:16:41 -0700200 let reader = RemoteFileReader::new(service, remote_id);
Victor Hsieh88e50172021-10-15 13:27:13 -0700201 Ok(FileConfig::UnverifiedReadonly { reader, file_size })
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800202}
203
Victor Hsieh2445e332021-06-04 16:44:53 -0700204fn new_config_remote_new_verified_file(
205 service: file::VirtFdService,
206 remote_id: i32,
207) -> Result<FileConfig> {
Victor Hsiehc3d45b12021-06-30 09:16:41 -0700208 let remote_file = RemoteFileEditor::new(service, remote_id);
Victor Hsieh88e50172021-10-15 13:27:13 -0700209 Ok(FileConfig::VerifiedNew { editor: VerifiedFileEditor::new(remote_file) })
Victor Hsieh6a47e7f2021-03-03 15:53:49 -0800210}
211
Victor Hsieh45636232021-10-15 17:52:51 -0700212fn new_config_remote_new_verified_dir(
213 service: file::VirtFdService,
214 remote_id: i32,
215) -> Result<FileConfig> {
216 let dir = RemoteDirEditor::new(service, remote_id);
217 Ok(FileConfig::VerifiedNewDirectory { dir })
218}
219
Victor Hsiehf01f3232020-12-11 13:31:31 -0800220fn prepare_file_pool(args: &Args) -> Result<BTreeMap<Inode, FileConfig>> {
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800221 let mut file_pool = BTreeMap::new();
222
Victor Hsieh88e50172021-10-15 13:27:13 -0700223 let service = file::get_rpc_binder_service(args.cid)?;
Victor Hsieh2445e332021-06-04 16:44:53 -0700224
Victor Hsieh88e50172021-10-15 13:27:13 -0700225 for config in &args.remote_ro_file {
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800226 file_pool.insert(
227 config.ino,
Victor Hsieh88e50172021-10-15 13:27:13 -0700228 new_config_remote_verified_file(
229 service.clone(),
230 config.remote_id,
231 service.getFileSize(config.remote_id)?.try_into()?,
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800232 )?,
233 );
234 }
235
Victor Hsieh88e50172021-10-15 13:27:13 -0700236 for config in &args.remote_ro_file_unverified {
237 file_pool.insert(
238 config.ino,
239 new_config_remote_unverified_file(
240 service.clone(),
241 config.remote_id,
242 service.getFileSize(config.remote_id)?.try_into()?,
243 )?,
244 );
245 }
246
247 for config in &args.remote_new_rw_file {
248 file_pool.insert(
249 config.ino,
250 new_config_remote_new_verified_file(service.clone(), config.remote_id)?,
251 );
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800252 }
253
Victor Hsieh45636232021-10-15 17:52:51 -0700254 for config in &args.remote_new_rw_dir {
255 file_pool.insert(
256 config.ino,
257 new_config_remote_new_verified_dir(service.clone(), config.remote_id)?,
258 );
259 }
260
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800261 Ok(file_pool)
262}
263
Alan Stokese1b6e1c2021-10-01 12:44:49 +0100264fn try_main() -> Result<()> {
Victor Hsiehf01f3232020-12-11 13:31:31 -0800265 let args = Args::from_args();
Victor Hsieh9d0ab622021-04-26 17:07:02 -0700266
267 let log_level = if args.debug { log::Level::Debug } else { log::Level::Info };
268 android_logger::init_once(
269 android_logger::Config::default().with_tag("authfs").with_min_level(log_level),
270 );
271
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800272 let file_pool = prepare_file_pool(&args)?;
Victor Hsieh4cc3b792021-08-04 12:00:04 -0700273 fusefs::loop_forever(file_pool, &args.mount_point, &args.extra_options)?;
Victor Hsiehf01f3232020-12-11 13:31:31 -0800274 bail!("Unexpected exit after the handler loop")
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800275}
Alan Stokese1b6e1c2021-10-01 12:44:49 +0100276
277fn main() {
278 if let Err(e) = try_main() {
279 error!("failed with {:?}", e);
280 std::process::exit(1);
281 }
282}