blob: f08d9ea828c32fbddd563d5ff11d95aacc48e074 [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 Hsieh50d75ac2021-09-03 14:46:55 -070032use std::convert::TryInto;
Victor Hsiehd18b9752021-11-09 16:03:34 -080033use std::path::{Path, PathBuf};
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080034use structopt::StructOpt;
35
36mod auth;
37mod common;
38mod crypto;
Victor Hsieh09e26262021-03-03 16:00:55 -080039mod file;
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080040mod fsverity;
41mod fusefs;
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080042
43use auth::FakeAuthenticator;
Victor Hsiehd18b9752021-11-09 16:03:34 -080044use file::{
45 InMemoryDir, RemoteDirEditor, RemoteFileEditor, RemoteFileReader, RemoteMerkleTreeReader,
46};
Victor Hsieh6a47e7f2021-03-03 15:53:49 -080047use fsverity::{VerifiedFileEditor, VerifiedFileReader};
Victor Hsieh4d6b9d42021-11-08 15:53:49 -080048use fusefs::{AuthFs, AuthFsEntry};
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)]
Victor Hsieh1a8cd042021-09-03 16:29:45 -070058 cid: u32,
Victor Hsieh2445e332021-06-04 16:44:53 -070059
Victor Hsieh4cc3b792021-08-04 12:00:04 -070060 /// Extra options to FUSE
61 #[structopt(short = "o")]
62 extra_options: Option<String>,
63
Victor Hsieh09e26262021-03-03 16:00:55 -080064 /// A read-only remote file with integrity check. Can be multiple.
Victor Hsiehf01f3232020-12-11 13:31:31 -080065 ///
Victor Hsiehb3588ce2021-11-02 15:02:32 -070066 /// For example, `--remote-ro-file 5:/path/to/cert` tells the filesystem to associate the
67 /// file $MOUNTPOINT/5 with a remote FD 5, and need to be verified against 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 ///
Victor Hsiehb3588ce2021-11-02 15:02:32 -070073 /// For example, `--remote-ro-file-unverified 5` tells the filesystem to associate the file
74 /// $MOUNTPOINT/5 with a remote FD 5.
75 #[structopt(long)]
76 remote_ro_file_unverified: Vec<i32>,
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 ///
Victor Hsiehb3588ce2021-11-02 15:02:32 -070080 /// For example, `--remote-new-rw-file 5` tells the filesystem to associate the file
81 /// $MOUNTPOINT/5 with a remote FD 5.
82 #[structopt(long)]
83 remote_new_rw_file: Vec<i32>,
Victor Hsieh6a47e7f2021-03-03 15:53:49 -080084
Victor Hsiehd18b9752021-11-09 16:03:34 -080085 /// A read-only directory that represents a remote directory. The directory view is constructed
86 /// and finalized during the filesystem initialization based on the provided mapping file
87 /// (which is a serialized protobuf of android.security.fsverity.FSVerityDigests, which
88 /// essentially provides <file path, fs-verity digest> mappings of exported files). The mapping
89 /// file is supposed to come from a trusted location in order to provide a trusted view as well
90 /// as verified access of included files with their fs-verity digest. Not all files on the
91 /// remote host may be included in the mapping file, so the directory view may be partial. The
92 /// directory structure won't change throughout the filesystem lifetime.
93 ///
94 /// For example, `--remote-ro-dir 5:/path/to/mapping:/prefix/` tells the filesystem to
95 /// construct a directory structure defined in the mapping file at $MOUNTPOINT/5, which may
96 /// include a file like /5/system/framework/framework.jar. "/prefix/" tells the filesystem to
97 /// strip the path (e.g. "/system/") from the mount point to match the expected location of the
98 /// remote FD (e.g. a directory FD of "/system" in the remote).
99 #[structopt(long, parse(try_from_str = parse_remote_new_ro_dir_option))]
100 remote_ro_dir: Vec<OptionRemoteRoDir>,
101
Victor Hsieh45636232021-10-15 17:52:51 -0700102 /// A new directory that is assumed empty in the backing filesystem. New files created in this
103 /// directory are integrity-protected in the same way as --remote-new-verified-file. Can be
104 /// multiple.
105 ///
Victor Hsiehb3588ce2021-11-02 15:02:32 -0700106 /// For example, `--remote-new-rw-dir 5` tells the filesystem to associate $MOUNTPOINT/5
107 /// with a remote dir FD 5.
108 #[structopt(long)]
109 remote_new_rw_dir: Vec<i32>,
Victor Hsieh45636232021-10-15 17:52:51 -0700110
Victor Hsieh9d0ab622021-04-26 17:07:02 -0700111 /// Enable debugging features.
112 #[structopt(long)]
113 debug: bool,
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800114}
115
Victor Hsieh09e26262021-03-03 16:00:55 -0800116struct OptionRemoteRoFile {
Victor Hsiehf01f3232020-12-11 13:31:31 -0800117 /// ID to refer to the remote file.
Victor Hsiehb3588ce2021-11-02 15:02:32 -0700118 remote_fd: i32,
Victor Hsiehf01f3232020-12-11 13:31:31 -0800119
Victor Hsiehf01f3232020-12-11 13:31:31 -0800120 /// Certificate to verify the authenticity of the file's fs-verity signature.
121 /// TODO(170494765): Implement PKCS#7 signature verification.
122 _certificate_path: PathBuf,
123}
124
Victor Hsiehd18b9752021-11-09 16:03:34 -0800125struct OptionRemoteRoDir {
126 /// ID to refer to the remote dir.
127 remote_dir_fd: i32,
128
129 /// A mapping file that describes the expecting file/directory structure and integrity metadata
130 /// in the remote directory. The file contains serialized protobuf of
131 /// android.security.fsverity.FSVerityDigests.
132 /// TODO(203251769): Really use the file when it's generated.
133 #[allow(dead_code)]
134 mapping_file_path: PathBuf,
135
136 prefix: PathBuf,
137}
138
Victor Hsieh09e26262021-03-03 16:00:55 -0800139fn parse_remote_ro_file_option(option: &str) -> Result<OptionRemoteRoFile> {
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800140 let strs: Vec<&str> = option.split(':').collect();
Victor Hsiehb3588ce2021-11-02 15:02:32 -0700141 if strs.len() != 2 {
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800142 bail!("Invalid option: {}", option);
143 }
Victor Hsieh09e26262021-03-03 16:00:55 -0800144 Ok(OptionRemoteRoFile {
Victor Hsiehb3588ce2021-11-02 15:02:32 -0700145 remote_fd: strs[0].parse::<i32>()?,
146 _certificate_path: PathBuf::from(strs[1]),
Victor Hsieh45636232021-10-15 17:52:51 -0700147 })
148}
149
Victor Hsiehd18b9752021-11-09 16:03:34 -0800150fn parse_remote_new_ro_dir_option(option: &str) -> Result<OptionRemoteRoDir> {
151 let strs: Vec<&str> = option.split(':').collect();
152 if strs.len() != 3 {
153 bail!("Invalid option: {}", option);
154 }
155 Ok(OptionRemoteRoDir {
156 remote_dir_fd: strs[0].parse::<i32>().unwrap(),
157 mapping_file_path: PathBuf::from(strs[1]),
158 prefix: PathBuf::from(strs[2]),
159 })
160}
161
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700162fn new_remote_verified_file_entry(
Victor Hsieh2445e332021-06-04 16:44:53 -0700163 service: file::VirtFdService,
Victor Hsiehb3588ce2021-11-02 15:02:32 -0700164 remote_fd: i32,
Victor Hsieh2445e332021-06-04 16:44:53 -0700165 file_size: u64,
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700166) -> Result<AuthFsEntry> {
Victor Hsiehb3588ce2021-11-02 15:02:32 -0700167 let signature = service.readFsveritySignature(remote_fd).context("Failed to read signature")?;
Victor Hsiehf01f3232020-12-11 13:31:31 -0800168
Victor Hsiehf01f3232020-12-11 13:31:31 -0800169 let authenticator = FakeAuthenticator::always_succeed();
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700170 Ok(AuthFsEntry::VerifiedReadonly {
Victor Hsieh1bcf4112021-03-19 14:26:57 -0700171 reader: VerifiedFileReader::new(
Victor Hsiehf01f3232020-12-11 13:31:31 -0800172 &authenticator,
Victor Hsiehb3588ce2021-11-02 15:02:32 -0700173 RemoteFileReader::new(service.clone(), remote_fd),
Victor Hsiehf01f3232020-12-11 13:31:31 -0800174 file_size,
175 signature,
Victor Hsiehb3588ce2021-11-02 15:02:32 -0700176 RemoteMerkleTreeReader::new(service.clone(), remote_fd),
Victor Hsiehf01f3232020-12-11 13:31:31 -0800177 )?,
178 file_size,
Victor Hsieh1bcf4112021-03-19 14:26:57 -0700179 })
Victor Hsiehf01f3232020-12-11 13:31:31 -0800180}
181
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700182fn new_remote_unverified_file_entry(
Victor Hsieh2445e332021-06-04 16:44:53 -0700183 service: file::VirtFdService,
Victor Hsiehb3588ce2021-11-02 15:02:32 -0700184 remote_fd: i32,
Victor Hsieh2445e332021-06-04 16:44:53 -0700185 file_size: u64,
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700186) -> Result<AuthFsEntry> {
Victor Hsiehb3588ce2021-11-02 15:02:32 -0700187 let reader = RemoteFileReader::new(service, remote_fd);
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700188 Ok(AuthFsEntry::UnverifiedReadonly { reader, file_size })
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800189}
190
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700191fn new_remote_new_verified_file_entry(
Victor Hsieh2445e332021-06-04 16:44:53 -0700192 service: file::VirtFdService,
Victor Hsiehb3588ce2021-11-02 15:02:32 -0700193 remote_fd: i32,
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700194) -> Result<AuthFsEntry> {
Victor Hsiehb3588ce2021-11-02 15:02:32 -0700195 let remote_file = RemoteFileEditor::new(service, remote_fd);
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700196 Ok(AuthFsEntry::VerifiedNew { editor: VerifiedFileEditor::new(remote_file) })
Victor Hsieh6a47e7f2021-03-03 15:53:49 -0800197}
198
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700199fn new_remote_new_verified_dir_entry(
Victor Hsieh45636232021-10-15 17:52:51 -0700200 service: file::VirtFdService,
Victor Hsiehb3588ce2021-11-02 15:02:32 -0700201 remote_fd: i32,
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700202) -> Result<AuthFsEntry> {
Victor Hsiehb3588ce2021-11-02 15:02:32 -0700203 let dir = RemoteDirEditor::new(service, remote_fd);
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700204 Ok(AuthFsEntry::VerifiedNewDirectory { dir })
Victor Hsieh45636232021-10-15 17:52:51 -0700205}
206
Victor Hsieh4d6b9d42021-11-08 15:53:49 -0800207fn prepare_root_dir_entries(authfs: &mut AuthFs, args: &Args) -> Result<()> {
Victor Hsieh88e50172021-10-15 13:27:13 -0700208 let service = file::get_rpc_binder_service(args.cid)?;
Victor Hsieh2445e332021-06-04 16:44:53 -0700209
Victor Hsieh88e50172021-10-15 13:27:13 -0700210 for config in &args.remote_ro_file {
Victor Hsieh4d6b9d42021-11-08 15:53:49 -0800211 authfs.add_entry_at_root_dir(
Victor Hsieh60c2f412021-11-03 13:02:19 -0700212 remote_fd_to_path_buf(config.remote_fd),
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700213 new_remote_verified_file_entry(
Victor Hsieh88e50172021-10-15 13:27:13 -0700214 service.clone(),
Victor Hsiehb3588ce2021-11-02 15:02:32 -0700215 config.remote_fd,
216 service.getFileSize(config.remote_fd)?.try_into()?,
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800217 )?,
Victor Hsieh4d6b9d42021-11-08 15:53:49 -0800218 )?;
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800219 }
220
Victor Hsiehb3588ce2021-11-02 15:02:32 -0700221 for remote_fd in &args.remote_ro_file_unverified {
222 let remote_fd = *remote_fd;
Victor Hsieh4d6b9d42021-11-08 15:53:49 -0800223 authfs.add_entry_at_root_dir(
Victor Hsieh60c2f412021-11-03 13:02:19 -0700224 remote_fd_to_path_buf(remote_fd),
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700225 new_remote_unverified_file_entry(
Victor Hsieh88e50172021-10-15 13:27:13 -0700226 service.clone(),
Victor Hsiehb3588ce2021-11-02 15:02:32 -0700227 remote_fd,
228 service.getFileSize(remote_fd)?.try_into()?,
Victor Hsieh88e50172021-10-15 13:27:13 -0700229 )?,
Victor Hsieh4d6b9d42021-11-08 15:53:49 -0800230 )?;
Victor Hsieh88e50172021-10-15 13:27:13 -0700231 }
232
Victor Hsiehb3588ce2021-11-02 15:02:32 -0700233 for remote_fd in &args.remote_new_rw_file {
234 let remote_fd = *remote_fd;
Victor Hsieh4d6b9d42021-11-08 15:53:49 -0800235 authfs.add_entry_at_root_dir(
Victor Hsieh60c2f412021-11-03 13:02:19 -0700236 remote_fd_to_path_buf(remote_fd),
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700237 new_remote_new_verified_file_entry(service.clone(), remote_fd)?,
Victor Hsieh4d6b9d42021-11-08 15:53:49 -0800238 )?;
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800239 }
240
Victor Hsiehb3588ce2021-11-02 15:02:32 -0700241 for remote_fd in &args.remote_new_rw_dir {
242 let remote_fd = *remote_fd;
Victor Hsieh4d6b9d42021-11-08 15:53:49 -0800243 authfs.add_entry_at_root_dir(
Victor Hsieh60c2f412021-11-03 13:02:19 -0700244 remote_fd_to_path_buf(remote_fd),
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700245 new_remote_new_verified_dir_entry(service.clone(), remote_fd)?,
Victor Hsieh4d6b9d42021-11-08 15:53:49 -0800246 )?;
Victor Hsieh45636232021-10-15 17:52:51 -0700247 }
248
Victor Hsiehd18b9752021-11-09 16:03:34 -0800249 for config in &args.remote_ro_dir {
250 let dir_root_inode = authfs.add_entry_at_root_dir(
251 remote_fd_to_path_buf(config.remote_dir_fd),
252 AuthFsEntry::ReadonlyDirectory { dir: InMemoryDir::new() },
253 )?;
254
255 // TODO(203251769): Read actual path from config.mapping_file_path when it's generated.
256 let paths = vec![
257 Path::new("/system/framework/framework.jar"),
258 Path::new("/system/framework/services.jar"),
259 ];
260
261 for path in &paths {
262 let file_entry = {
263 // TODO(205883847): Not all files will be used. Open the remote file lazily.
264 let related_path = path.strip_prefix(&config.prefix)?;
265 let remote_file = RemoteFileReader::new_by_path(
266 service.clone(),
267 config.remote_dir_fd,
268 related_path,
269 )?;
270 let file_size = service.getFileSize(remote_file.get_remote_fd())?.try_into()?;
271 // TODO(203251769): Switch to VerifiedReadonly
272 AuthFsEntry::UnverifiedReadonly { reader: remote_file, file_size }
273 };
274 authfs.add_entry_at_ro_dir_by_path(
275 dir_root_inode,
276 path.strip_prefix("/")?,
277 file_entry,
278 )?;
279 }
280 }
281
Victor Hsieh4d6b9d42021-11-08 15:53:49 -0800282 Ok(())
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800283}
284
Victor Hsieh60c2f412021-11-03 13:02:19 -0700285fn remote_fd_to_path_buf(fd: i32) -> PathBuf {
286 PathBuf::from(fd.to_string())
287}
288
Alan Stokese1b6e1c2021-10-01 12:44:49 +0100289fn try_main() -> Result<()> {
Victor Hsiehf01f3232020-12-11 13:31:31 -0800290 let args = Args::from_args();
Victor Hsieh9d0ab622021-04-26 17:07:02 -0700291
292 let log_level = if args.debug { log::Level::Debug } else { log::Level::Info };
293 android_logger::init_once(
294 android_logger::Config::default().with_tag("authfs").with_min_level(log_level),
295 );
296
Victor Hsieh4d6b9d42021-11-08 15:53:49 -0800297 let mut authfs = AuthFs::new();
298 prepare_root_dir_entries(&mut authfs, &args)?;
299 fusefs::loop_forever(authfs, &args.mount_point, &args.extra_options)?;
Victor Hsiehf01f3232020-12-11 13:31:31 -0800300 bail!("Unexpected exit after the handler loop")
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800301}
Alan Stokese1b6e1c2021-10-01 12:44:49 +0100302
303fn main() {
304 if let Err(e) = try_main() {
305 error!("failed with {:?}", e);
306 std::process::exit(1);
307 }
308}