blob: 32ea3de322dd4d8e251fd2837160808b563cdf28 [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 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 Hsieh6a47e7f2021-03-03 15:53:49 -080045use file::{LocalFileReader, RemoteFileEditor, RemoteFileReader, RemoteMerkleTreeReader};
46use 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)]
57 cid: Option<u32>,
58
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 ///
65 /// For example, `--remote-verified-file 5:10:1234:/path/to/cert` tells the filesystem to
66 /// associate entry 5 with a remote file 10 of size 1234 bytes, and need to be verified against
67 /// the /path/to/cert.
Victor Hsieh09e26262021-03-03 16:00:55 -080068 #[structopt(long, parse(try_from_str = parse_remote_ro_file_option))]
69 remote_ro_file: Vec<OptionRemoteRoFile>,
Victor Hsiehf01f3232020-12-11 13:31:31 -080070
Victor Hsieh09e26262021-03-03 16:00:55 -080071 /// A read-only remote file without integrity check. Can be multiple.
Victor Hsiehf01f3232020-12-11 13:31:31 -080072 ///
73 /// For example, `--remote-unverified-file 5:10:1234` tells the filesystem to associate entry 5
74 /// with a remote file 10 of size 1234 bytes.
Victor Hsieh09e26262021-03-03 16:00:55 -080075 #[structopt(long, parse(try_from_str = parse_remote_ro_file_unverified_option))]
76 remote_ro_file_unverified: Vec<OptionRemoteRoFileUnverified>,
Victor Hsiehf01f3232020-12-11 13:31:31 -080077
Victor Hsieh6a47e7f2021-03-03 15:53:49 -080078 /// A new read-writable remote file with integrity check. Can be multiple.
79 ///
80 /// For example, `--remote-new-verified-file 12:34` tells the filesystem to associate entry 12
81 /// with a remote file 34.
82 #[structopt(long, parse(try_from_str = parse_remote_new_rw_file_option))]
83 remote_new_rw_file: Vec<OptionRemoteRwFile>,
84
Victor Hsieh09e26262021-03-03 16:00:55 -080085 /// Debug only. A read-only local file with integrity check. Can be multiple.
86 #[structopt(long, parse(try_from_str = parse_local_file_ro_option))]
87 local_ro_file: Vec<OptionLocalFileRo>,
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080088
Victor Hsieh09e26262021-03-03 16:00:55 -080089 /// Debug only. A read-only local file without integrity check. Can be multiple.
90 #[structopt(long, parse(try_from_str = parse_local_ro_file_unverified_ro_option))]
91 local_ro_file_unverified: Vec<OptionLocalRoFileUnverified>,
Victor Hsieh9d0ab622021-04-26 17:07:02 -070092
93 /// Enable debugging features.
94 #[structopt(long)]
95 debug: bool,
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080096}
97
Victor Hsieh9ab13e52021-06-29 09:23:29 -070098impl Args {
99 fn has_remote_files(&self) -> bool {
100 !self.remote_ro_file.is_empty()
101 || !self.remote_ro_file_unverified.is_empty()
102 || !self.remote_new_rw_file.is_empty()
103 }
104}
105
Victor Hsieh09e26262021-03-03 16:00:55 -0800106struct OptionRemoteRoFile {
Victor Hsiehf01f3232020-12-11 13:31:31 -0800107 ino: Inode,
108
109 /// ID to refer to the remote file.
110 remote_id: i32,
111
112 /// Expected size of the remote file. Necessary for signature check and Merkle tree
113 /// verification.
114 file_size: u64,
115
116 /// Certificate to verify the authenticity of the file's fs-verity signature.
117 /// TODO(170494765): Implement PKCS#7 signature verification.
118 _certificate_path: PathBuf,
119}
120
Victor Hsieh09e26262021-03-03 16:00:55 -0800121struct OptionRemoteRoFileUnverified {
Victor Hsiehf01f3232020-12-11 13:31:31 -0800122 ino: Inode,
123
124 /// ID to refer to the remote file.
125 remote_id: i32,
126
127 /// Expected size of the remote file.
128 file_size: u64,
129}
130
Victor Hsieh6a47e7f2021-03-03 15:53:49 -0800131struct OptionRemoteRwFile {
132 ino: Inode,
133
134 /// ID to refer to the remote file.
135 remote_id: i32,
136}
137
Victor Hsieh09e26262021-03-03 16:00:55 -0800138struct OptionLocalFileRo {
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800139 ino: Inode,
Victor Hsiehf01f3232020-12-11 13:31:31 -0800140
141 /// Local path of the backing file.
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800142 file_path: PathBuf,
Victor Hsiehf01f3232020-12-11 13:31:31 -0800143
144 /// Local path of the backing file's fs-verity Merkle tree dump.
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800145 merkle_tree_dump_path: PathBuf,
Victor Hsiehf01f3232020-12-11 13:31:31 -0800146
147 /// Local path of fs-verity signature for the backing file.
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800148 signature_path: PathBuf,
Victor Hsiehf01f3232020-12-11 13:31:31 -0800149
150 /// Certificate to verify the authenticity of the file's fs-verity signature.
151 /// TODO(170494765): Implement PKCS#7 signature verification.
152 _certificate_path: PathBuf,
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800153}
154
Victor Hsieh09e26262021-03-03 16:00:55 -0800155struct OptionLocalRoFileUnverified {
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800156 ino: Inode,
Victor Hsiehf01f3232020-12-11 13:31:31 -0800157
158 /// Local path of the backing file.
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800159 file_path: PathBuf,
160}
161
Victor Hsieh09e26262021-03-03 16:00:55 -0800162fn parse_remote_ro_file_option(option: &str) -> Result<OptionRemoteRoFile> {
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800163 let strs: Vec<&str> = option.split(':').collect();
164 if strs.len() != 4 {
165 bail!("Invalid option: {}", option);
166 }
Victor Hsieh09e26262021-03-03 16:00:55 -0800167 Ok(OptionRemoteRoFile {
Victor Hsiehf01f3232020-12-11 13:31:31 -0800168 ino: strs[0].parse::<Inode>()?,
169 remote_id: strs[1].parse::<i32>()?,
170 file_size: strs[2].parse::<u64>()?,
171 _certificate_path: PathBuf::from(strs[3]),
172 })
173}
174
Victor Hsieh09e26262021-03-03 16:00:55 -0800175fn parse_remote_ro_file_unverified_option(option: &str) -> Result<OptionRemoteRoFileUnverified> {
Victor Hsiehf01f3232020-12-11 13:31:31 -0800176 let strs: Vec<&str> = option.split(':').collect();
177 if strs.len() != 3 {
178 bail!("Invalid option: {}", option);
179 }
Victor Hsieh09e26262021-03-03 16:00:55 -0800180 Ok(OptionRemoteRoFileUnverified {
Victor Hsiehf01f3232020-12-11 13:31:31 -0800181 ino: strs[0].parse::<Inode>()?,
182 remote_id: strs[1].parse::<i32>()?,
183 file_size: strs[2].parse::<u64>()?,
184 })
185}
186
Victor Hsieh6a47e7f2021-03-03 15:53:49 -0800187fn parse_remote_new_rw_file_option(option: &str) -> Result<OptionRemoteRwFile> {
188 let strs: Vec<&str> = option.split(':').collect();
189 if strs.len() != 2 {
190 bail!("Invalid option: {}", option);
191 }
192 Ok(OptionRemoteRwFile {
193 ino: strs[0].parse::<Inode>().unwrap(),
194 remote_id: strs[1].parse::<i32>().unwrap(),
195 })
196}
197
Victor Hsieh09e26262021-03-03 16:00:55 -0800198fn parse_local_file_ro_option(option: &str) -> Result<OptionLocalFileRo> {
Victor Hsiehf01f3232020-12-11 13:31:31 -0800199 let strs: Vec<&str> = option.split(':').collect();
200 if strs.len() != 5 {
201 bail!("Invalid option: {}", option);
202 }
Victor Hsieh09e26262021-03-03 16:00:55 -0800203 Ok(OptionLocalFileRo {
Victor Hsiehf01f3232020-12-11 13:31:31 -0800204 ino: strs[0].parse::<Inode>()?,
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800205 file_path: PathBuf::from(strs[1]),
206 merkle_tree_dump_path: PathBuf::from(strs[2]),
207 signature_path: PathBuf::from(strs[3]),
Victor Hsiehf01f3232020-12-11 13:31:31 -0800208 _certificate_path: PathBuf::from(strs[4]),
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800209 })
210}
211
Victor Hsieh09e26262021-03-03 16:00:55 -0800212fn parse_local_ro_file_unverified_ro_option(option: &str) -> Result<OptionLocalRoFileUnverified> {
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800213 let strs: Vec<&str> = option.split(':').collect();
214 if strs.len() != 2 {
215 bail!("Invalid option: {}", option);
216 }
Victor Hsieh09e26262021-03-03 16:00:55 -0800217 Ok(OptionLocalRoFileUnverified {
Victor Hsiehf01f3232020-12-11 13:31:31 -0800218 ino: strs[0].parse::<Inode>()?,
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800219 file_path: PathBuf::from(strs[1]),
220 })
221}
222
Victor Hsieh2445e332021-06-04 16:44:53 -0700223fn new_config_remote_verified_file(
224 service: file::VirtFdService,
225 remote_id: i32,
226 file_size: u64,
227) -> Result<FileConfig> {
Andrew Walbrancc093862021-03-05 16:59:35 +0000228 let signature = service.readFsveritySignature(remote_id).context("Failed to read signature")?;
Victor Hsiehf01f3232020-12-11 13:31:31 -0800229
Victor Hsiehf01f3232020-12-11 13:31:31 -0800230 let authenticator = FakeAuthenticator::always_succeed();
Chris Wailes68c39f82021-07-27 16:03:44 -0700231 Ok(FileConfig::RemoteVerifiedReadonly {
Victor Hsieh1bcf4112021-03-19 14:26:57 -0700232 reader: VerifiedFileReader::new(
Victor Hsiehf01f3232020-12-11 13:31:31 -0800233 &authenticator,
Victor Hsiehc3d45b12021-06-30 09:16:41 -0700234 RemoteFileReader::new(service.clone(), remote_id),
Victor Hsiehf01f3232020-12-11 13:31:31 -0800235 file_size,
236 signature,
Victor Hsiehc3d45b12021-06-30 09:16:41 -0700237 RemoteMerkleTreeReader::new(service.clone(), remote_id),
Victor Hsiehf01f3232020-12-11 13:31:31 -0800238 )?,
239 file_size,
Victor Hsieh1bcf4112021-03-19 14:26:57 -0700240 })
Victor Hsiehf01f3232020-12-11 13:31:31 -0800241}
242
Victor Hsieh2445e332021-06-04 16:44:53 -0700243fn new_config_remote_unverified_file(
244 service: file::VirtFdService,
245 remote_id: i32,
246 file_size: u64,
247) -> Result<FileConfig> {
Victor Hsiehc3d45b12021-06-30 09:16:41 -0700248 let reader = RemoteFileReader::new(service, remote_id);
Chris Wailes68c39f82021-07-27 16:03:44 -0700249 Ok(FileConfig::RemoteUnverifiedReadonly { reader, file_size })
Victor Hsiehf01f3232020-12-11 13:31:31 -0800250}
251
Victor Hsieh09e26262021-03-03 16:00:55 -0800252fn new_config_local_ro_file(
Victor Hsieh6cf75b52021-04-01 12:45:49 -0700253 protected_file: &Path,
254 merkle_tree_dump: &Path,
255 signature: &Path,
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800256) -> Result<FileConfig> {
257 let file = File::open(&protected_file)?;
258 let file_size = file.metadata()?.len();
Victor Hsieh09e26262021-03-03 16:00:55 -0800259 let file_reader = LocalFileReader::new(file)?;
260 let merkle_tree_reader = LocalFileReader::new(File::open(merkle_tree_dump)?)?;
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800261 let authenticator = FakeAuthenticator::always_succeed();
262 let mut sig = Vec::new();
263 let _ = File::open(signature)?.read_to_end(&mut sig)?;
Victor Hsieh1bcf4112021-03-19 14:26:57 -0700264 let reader =
Victor Hsieh09e26262021-03-03 16:00:55 -0800265 VerifiedFileReader::new(&authenticator, file_reader, file_size, sig, merkle_tree_reader)?;
Chris Wailes68c39f82021-07-27 16:03:44 -0700266 Ok(FileConfig::LocalVerifiedReadonly { reader, file_size })
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800267}
268
Victor Hsieh6cf75b52021-04-01 12:45:49 -0700269fn new_config_local_ro_file_unverified(file_path: &Path) -> Result<FileConfig> {
Victor Hsieh1bcf4112021-03-19 14:26:57 -0700270 let reader = LocalFileReader::new(File::open(file_path)?)?;
271 let file_size = reader.len();
Chris Wailes68c39f82021-07-27 16:03:44 -0700272 Ok(FileConfig::LocalUnverifiedReadonly { reader, file_size })
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800273}
274
Victor Hsieh2445e332021-06-04 16:44:53 -0700275fn new_config_remote_new_verified_file(
276 service: file::VirtFdService,
277 remote_id: i32,
278) -> Result<FileConfig> {
Victor Hsiehc3d45b12021-06-30 09:16:41 -0700279 let remote_file = RemoteFileEditor::new(service, remote_id);
Chris Wailes68c39f82021-07-27 16:03:44 -0700280 Ok(FileConfig::RemoteVerifiedNew { editor: VerifiedFileEditor::new(remote_file) })
Victor Hsieh6a47e7f2021-03-03 15:53:49 -0800281}
282
Victor Hsiehf01f3232020-12-11 13:31:31 -0800283fn prepare_file_pool(args: &Args) -> Result<BTreeMap<Inode, FileConfig>> {
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800284 let mut file_pool = BTreeMap::new();
285
Victor Hsieh9ab13e52021-06-29 09:23:29 -0700286 if args.has_remote_files() {
287 let service = file::get_binder_service(args.cid)?;
Victor Hsieh2445e332021-06-04 16:44:53 -0700288
Victor Hsieh9ab13e52021-06-29 09:23:29 -0700289 for config in &args.remote_ro_file {
290 file_pool.insert(
291 config.ino,
292 new_config_remote_verified_file(
293 service.clone(),
294 config.remote_id,
295 config.file_size,
296 )?,
297 );
298 }
Victor Hsiehf01f3232020-12-11 13:31:31 -0800299
Victor Hsieh9ab13e52021-06-29 09:23:29 -0700300 for config in &args.remote_ro_file_unverified {
301 file_pool.insert(
302 config.ino,
303 new_config_remote_unverified_file(
304 service.clone(),
305 config.remote_id,
306 config.file_size,
307 )?,
308 );
309 }
Victor Hsiehf01f3232020-12-11 13:31:31 -0800310
Victor Hsieh9ab13e52021-06-29 09:23:29 -0700311 for config in &args.remote_new_rw_file {
312 file_pool.insert(
313 config.ino,
314 new_config_remote_new_verified_file(service.clone(), config.remote_id)?,
315 );
316 }
Victor Hsieh6a47e7f2021-03-03 15:53:49 -0800317 }
318
Victor Hsieh09e26262021-03-03 16:00:55 -0800319 for config in &args.local_ro_file {
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800320 file_pool.insert(
321 config.ino,
Victor Hsieh09e26262021-03-03 16:00:55 -0800322 new_config_local_ro_file(
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800323 &config.file_path,
324 &config.merkle_tree_dump_path,
325 &config.signature_path,
326 )?,
327 );
328 }
329
Victor Hsieh09e26262021-03-03 16:00:55 -0800330 for config in &args.local_ro_file_unverified {
331 file_pool.insert(config.ino, new_config_local_ro_file_unverified(&config.file_path)?);
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800332 }
333
334 Ok(file_pool)
335}
336
337fn main() -> Result<()> {
Victor Hsiehf01f3232020-12-11 13:31:31 -0800338 let args = Args::from_args();
Victor Hsieh9d0ab622021-04-26 17:07:02 -0700339
340 let log_level = if args.debug { log::Level::Debug } else { log::Level::Info };
341 android_logger::init_once(
342 android_logger::Config::default().with_tag("authfs").with_min_level(log_level),
343 );
344
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800345 let file_pool = prepare_file_pool(&args)?;
Victor Hsieh4cc3b792021-08-04 12:00:04 -0700346 fusefs::loop_forever(file_pool, &args.mount_point, &args.extra_options)?;
Victor Hsiehf01f3232020-12-11 13:31:31 -0800347 bail!("Unexpected exit after the handler loop")
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800348}