blob: 41b922db7c100b5ae0cf5a31ba0d36e0ebda0eac [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;
34use std::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;
41mod fsverity;
42mod fusefs;
43mod reader;
Victor Hsiehf01f3232020-12-11 13:31:31 -080044mod remote_file;
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080045
46use auth::FakeAuthenticator;
47use fsverity::FsverityChunkedFileReader;
48use fusefs::{FileConfig, Inode};
49use reader::ChunkedFileReader;
Victor Hsiehf01f3232020-12-11 13:31:31 -080050use remote_file::{RemoteChunkedFileReader, RemoteFsverityMerkleTreeReader};
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080051
52#[derive(StructOpt)]
Victor Hsiehf01f3232020-12-11 13:31:31 -080053struct Args {
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080054 /// Mount point of AuthFS.
55 #[structopt(parse(from_os_str))]
56 mount_point: PathBuf,
57
Victor Hsiehf01f3232020-12-11 13:31:31 -080058 /// A verifiable read-only file. Can be multiple.
59 ///
60 /// For example, `--remote-verified-file 5:10:1234:/path/to/cert` tells the filesystem to
61 /// associate entry 5 with a remote file 10 of size 1234 bytes, and need to be verified against
62 /// the /path/to/cert.
63 #[structopt(long, parse(try_from_str = parse_remote_verified_file_option))]
64 remote_verified_file: Vec<RemoteVerifiedFileConfig>,
65
66 /// An unverifiable read-only file. Can be multiple.
67 ///
68 /// For example, `--remote-unverified-file 5:10:1234` tells the filesystem to associate entry 5
69 /// with a remote file 10 of size 1234 bytes.
70 #[structopt(long, parse(try_from_str = parse_remote_unverified_file_option))]
71 remote_unverified_file: Vec<RemoteUnverifiedFileConfig>,
72
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080073 /// Debug only. A readonly file to be protected by fs-verity. Can be multiple.
74 #[structopt(long, parse(try_from_str = parse_local_verified_file_option))]
75 local_verified_file: Vec<LocalVerifiedFileConfig>,
76
77 /// Debug only. An unverified read-only file. Can be multiple.
78 #[structopt(long, parse(try_from_str = parse_local_unverified_file_option))]
79 local_unverified_file: Vec<LocalUnverifiedFileConfig>,
80}
81
Victor Hsiehf01f3232020-12-11 13:31:31 -080082struct RemoteVerifiedFileConfig {
83 ino: Inode,
84
85 /// ID to refer to the remote file.
86 remote_id: i32,
87
88 /// Expected size of the remote file. Necessary for signature check and Merkle tree
89 /// verification.
90 file_size: u64,
91
92 /// Certificate to verify the authenticity of the file's fs-verity signature.
93 /// TODO(170494765): Implement PKCS#7 signature verification.
94 _certificate_path: PathBuf,
95}
96
97struct RemoteUnverifiedFileConfig {
98 ino: Inode,
99
100 /// ID to refer to the remote file.
101 remote_id: i32,
102
103 /// Expected size of the remote file.
104 file_size: u64,
105}
106
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800107struct LocalVerifiedFileConfig {
108 ino: Inode,
Victor Hsiehf01f3232020-12-11 13:31:31 -0800109
110 /// Local path of the backing file.
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800111 file_path: PathBuf,
Victor Hsiehf01f3232020-12-11 13:31:31 -0800112
113 /// Local path of the backing file's fs-verity Merkle tree dump.
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800114 merkle_tree_dump_path: PathBuf,
Victor Hsiehf01f3232020-12-11 13:31:31 -0800115
116 /// Local path of fs-verity signature for the backing file.
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800117 signature_path: PathBuf,
Victor Hsiehf01f3232020-12-11 13:31:31 -0800118
119 /// Certificate to verify the authenticity of the file's fs-verity signature.
120 /// TODO(170494765): Implement PKCS#7 signature verification.
121 _certificate_path: PathBuf,
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800122}
123
124struct LocalUnverifiedFileConfig {
125 ino: Inode,
Victor Hsiehf01f3232020-12-11 13:31:31 -0800126
127 /// Local path of the backing file.
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800128 file_path: PathBuf,
129}
130
Victor Hsiehf01f3232020-12-11 13:31:31 -0800131fn parse_remote_verified_file_option(option: &str) -> Result<RemoteVerifiedFileConfig> {
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800132 let strs: Vec<&str> = option.split(':').collect();
133 if strs.len() != 4 {
134 bail!("Invalid option: {}", option);
135 }
Victor Hsiehf01f3232020-12-11 13:31:31 -0800136 Ok(RemoteVerifiedFileConfig {
137 ino: strs[0].parse::<Inode>()?,
138 remote_id: strs[1].parse::<i32>()?,
139 file_size: strs[2].parse::<u64>()?,
140 _certificate_path: PathBuf::from(strs[3]),
141 })
142}
143
144fn parse_remote_unverified_file_option(option: &str) -> Result<RemoteUnverifiedFileConfig> {
145 let strs: Vec<&str> = option.split(':').collect();
146 if strs.len() != 3 {
147 bail!("Invalid option: {}", option);
148 }
149 Ok(RemoteUnverifiedFileConfig {
150 ino: strs[0].parse::<Inode>()?,
151 remote_id: strs[1].parse::<i32>()?,
152 file_size: strs[2].parse::<u64>()?,
153 })
154}
155
156fn parse_local_verified_file_option(option: &str) -> Result<LocalVerifiedFileConfig> {
157 let strs: Vec<&str> = option.split(':').collect();
158 if strs.len() != 5 {
159 bail!("Invalid option: {}", option);
160 }
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800161 Ok(LocalVerifiedFileConfig {
Victor Hsiehf01f3232020-12-11 13:31:31 -0800162 ino: strs[0].parse::<Inode>()?,
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800163 file_path: PathBuf::from(strs[1]),
164 merkle_tree_dump_path: PathBuf::from(strs[2]),
165 signature_path: PathBuf::from(strs[3]),
Victor Hsiehf01f3232020-12-11 13:31:31 -0800166 _certificate_path: PathBuf::from(strs[4]),
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800167 })
168}
169
170fn parse_local_unverified_file_option(option: &str) -> Result<LocalUnverifiedFileConfig> {
171 let strs: Vec<&str> = option.split(':').collect();
172 if strs.len() != 2 {
173 bail!("Invalid option: {}", option);
174 }
175 Ok(LocalUnverifiedFileConfig {
Victor Hsiehf01f3232020-12-11 13:31:31 -0800176 ino: strs[0].parse::<Inode>()?,
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800177 file_path: PathBuf::from(strs[1]),
178 })
179}
180
Victor Hsiehf01f3232020-12-11 13:31:31 -0800181fn new_config_remote_verified_file(remote_id: i32, file_size: u64) -> Result<FileConfig> {
182 let service = remote_file::server::get_local_service();
Andrew Walbrancc093862021-03-05 16:59:35 +0000183 let signature = service.readFsveritySignature(remote_id).context("Failed to read signature")?;
Victor Hsiehf01f3232020-12-11 13:31:31 -0800184
185 let service = Arc::new(Mutex::new(service));
186 let authenticator = FakeAuthenticator::always_succeed();
187 Ok(FileConfig::RemoteVerifiedFile(
188 FsverityChunkedFileReader::new(
189 &authenticator,
190 RemoteChunkedFileReader::new(Arc::clone(&service), remote_id),
191 file_size,
192 signature,
193 RemoteFsverityMerkleTreeReader::new(Arc::clone(&service), remote_id),
194 )?,
195 file_size,
196 ))
197}
198
199fn new_config_remote_unverified_file(remote_id: i32, file_size: u64) -> Result<FileConfig> {
200 let file_reader = RemoteChunkedFileReader::new(
201 Arc::new(Mutex::new(remote_file::server::get_local_service())),
202 remote_id,
203 );
204 Ok(FileConfig::RemoteUnverifiedFile(file_reader, file_size))
205}
206
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800207fn new_config_local_verified_file(
208 protected_file: &PathBuf,
209 merkle_tree_dump: &PathBuf,
210 signature: &PathBuf,
211) -> Result<FileConfig> {
212 let file = File::open(&protected_file)?;
213 let file_size = file.metadata()?.len();
214 let file_reader = ChunkedFileReader::new(file)?;
215 let merkle_tree_reader = ChunkedFileReader::new(File::open(merkle_tree_dump)?)?;
216 let authenticator = FakeAuthenticator::always_succeed();
217 let mut sig = Vec::new();
218 let _ = File::open(signature)?.read_to_end(&mut sig)?;
219 let file_reader = FsverityChunkedFileReader::new(
220 &authenticator,
221 file_reader,
222 file_size,
223 sig,
224 merkle_tree_reader,
225 )?;
226 Ok(FileConfig::LocalVerifiedFile(file_reader, file_size))
227}
228
229fn new_config_local_unverified_file(file_path: &PathBuf) -> Result<FileConfig> {
Victor Hsiehfa4477a2021-02-08 10:51:50 -0800230 let file_reader = ChunkedFileReader::new(File::open(file_path)?)?;
231 let file_size = file_reader.len();
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800232 Ok(FileConfig::LocalUnverifiedFile(file_reader, file_size))
233}
234
Victor Hsiehf01f3232020-12-11 13:31:31 -0800235fn prepare_file_pool(args: &Args) -> Result<BTreeMap<Inode, FileConfig>> {
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800236 let mut file_pool = BTreeMap::new();
237
Victor Hsiehf01f3232020-12-11 13:31:31 -0800238 for config in &args.remote_verified_file {
239 file_pool.insert(
240 config.ino,
241 new_config_remote_verified_file(config.remote_id, config.file_size)?,
242 );
243 }
244
245 for config in &args.remote_unverified_file {
246 file_pool.insert(
247 config.ino,
248 new_config_remote_unverified_file(config.remote_id, config.file_size)?,
249 );
250 }
251
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800252 for config in &args.local_verified_file {
253 file_pool.insert(
254 config.ino,
255 new_config_local_verified_file(
256 &config.file_path,
257 &config.merkle_tree_dump_path,
258 &config.signature_path,
259 )?,
260 );
261 }
262
263 for config in &args.local_unverified_file {
264 file_pool.insert(config.ino, new_config_local_unverified_file(&config.file_path)?);
265 }
266
267 Ok(file_pool)
268}
269
270fn main() -> Result<()> {
Victor Hsiehf01f3232020-12-11 13:31:31 -0800271 let args = Args::from_args();
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800272 let file_pool = prepare_file_pool(&args)?;
273 fusefs::loop_forever(file_pool, &args.mount_point)?;
Victor Hsiehf01f3232020-12-11 13:31:31 -0800274 bail!("Unexpected exit after the handler loop")
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800275}