1 /* 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 30 use anyhow::{bail, Context, Result}; 31 use std::collections::BTreeMap; 32 use std::fs::File; 33 use std::io::Read; 34 use std::path::{Path, PathBuf}; 35 use std::sync::{Arc, Mutex}; 36 use structopt::StructOpt; 37 38 mod auth; 39 mod common; 40 mod crypto; 41 mod file; 42 mod fsverity; 43 mod fusefs; 44 45 use auth::FakeAuthenticator; 46 use file::{LocalFileReader, RemoteFileEditor, RemoteFileReader, RemoteMerkleTreeReader}; 47 use fsverity::{VerifiedFileEditor, VerifiedFileReader}; 48 use fusefs::{FileConfig, Inode}; 49 50 #[derive(StructOpt)] 51 struct Args { 52 /// Mount point of AuthFS. 53 #[structopt(parse(from_os_str))] 54 mount_point: PathBuf, 55 56 /// A read-only remote file with integrity check. Can be multiple. 57 /// 58 /// For example, `--remote-verified-file 5:10:1234:/path/to/cert` tells the filesystem to 59 /// associate entry 5 with a remote file 10 of size 1234 bytes, and need to be verified against 60 /// the /path/to/cert. 61 #[structopt(long, parse(try_from_str = parse_remote_ro_file_option))] 62 remote_ro_file: Vec<OptionRemoteRoFile>, 63 64 /// A read-only remote file without integrity check. Can be multiple. 65 /// 66 /// For example, `--remote-unverified-file 5:10:1234` tells the filesystem to associate entry 5 67 /// with a remote file 10 of size 1234 bytes. 68 #[structopt(long, parse(try_from_str = parse_remote_ro_file_unverified_option))] 69 remote_ro_file_unverified: Vec<OptionRemoteRoFileUnverified>, 70 71 /// A new read-writable remote file with integrity check. Can be multiple. 72 /// 73 /// For example, `--remote-new-verified-file 12:34` tells the filesystem to associate entry 12 74 /// with a remote file 34. 75 #[structopt(long, parse(try_from_str = parse_remote_new_rw_file_option))] 76 remote_new_rw_file: Vec<OptionRemoteRwFile>, 77 78 /// Debug only. A read-only local file with integrity check. Can be multiple. 79 #[structopt(long, parse(try_from_str = parse_local_file_ro_option))] 80 local_ro_file: Vec<OptionLocalFileRo>, 81 82 /// Debug only. A read-only local file without integrity check. Can be multiple. 83 #[structopt(long, parse(try_from_str = parse_local_ro_file_unverified_ro_option))] 84 local_ro_file_unverified: Vec<OptionLocalRoFileUnverified>, 85 86 /// Enable debugging features. 87 #[structopt(long)] 88 debug: bool, 89 } 90 91 struct OptionRemoteRoFile { 92 ino: Inode, 93 94 /// ID to refer to the remote file. 95 remote_id: i32, 96 97 /// Expected size of the remote file. Necessary for signature check and Merkle tree 98 /// verification. 99 file_size: u64, 100 101 /// Certificate to verify the authenticity of the file's fs-verity signature. 102 /// TODO(170494765): Implement PKCS#7 signature verification. 103 _certificate_path: PathBuf, 104 } 105 106 struct OptionRemoteRoFileUnverified { 107 ino: Inode, 108 109 /// ID to refer to the remote file. 110 remote_id: i32, 111 112 /// Expected size of the remote file. 113 file_size: u64, 114 } 115 116 struct OptionRemoteRwFile { 117 ino: Inode, 118 119 /// ID to refer to the remote file. 120 remote_id: i32, 121 } 122 123 struct OptionLocalFileRo { 124 ino: Inode, 125 126 /// Local path of the backing file. 127 file_path: PathBuf, 128 129 /// Local path of the backing file's fs-verity Merkle tree dump. 130 merkle_tree_dump_path: PathBuf, 131 132 /// Local path of fs-verity signature for the backing file. 133 signature_path: PathBuf, 134 135 /// Certificate to verify the authenticity of the file's fs-verity signature. 136 /// TODO(170494765): Implement PKCS#7 signature verification. 137 _certificate_path: PathBuf, 138 } 139 140 struct OptionLocalRoFileUnverified { 141 ino: Inode, 142 143 /// Local path of the backing file. 144 file_path: PathBuf, 145 } 146 147 fn parse_remote_ro_file_option(option: &str) -> Result<OptionRemoteRoFile> { 148 let strs: Vec<&str> = option.split(':').collect(); 149 if strs.len() != 4 { 150 bail!("Invalid option: {}", option); 151 } 152 Ok(OptionRemoteRoFile { 153 ino: strs[0].parse::<Inode>()?, 154 remote_id: strs[1].parse::<i32>()?, 155 file_size: strs[2].parse::<u64>()?, 156 _certificate_path: PathBuf::from(strs[3]), 157 }) 158 } 159 160 fn parse_remote_ro_file_unverified_option(option: &str) -> Result<OptionRemoteRoFileUnverified> { 161 let strs: Vec<&str> = option.split(':').collect(); 162 if strs.len() != 3 { 163 bail!("Invalid option: {}", option); 164 } 165 Ok(OptionRemoteRoFileUnverified { 166 ino: strs[0].parse::<Inode>()?, 167 remote_id: strs[1].parse::<i32>()?, 168 file_size: strs[2].parse::<u64>()?, 169 }) 170 } 171 172 fn parse_remote_new_rw_file_option(option: &str) -> Result<OptionRemoteRwFile> { 173 let strs: Vec<&str> = option.split(':').collect(); 174 if strs.len() != 2 { 175 bail!("Invalid option: {}", option); 176 } 177 Ok(OptionRemoteRwFile { 178 ino: strs[0].parse::<Inode>().unwrap(), 179 remote_id: strs[1].parse::<i32>().unwrap(), 180 }) 181 } 182 183 fn parse_local_file_ro_option(option: &str) -> Result<OptionLocalFileRo> { 184 let strs: Vec<&str> = option.split(':').collect(); 185 if strs.len() != 5 { 186 bail!("Invalid option: {}", option); 187 } 188 Ok(OptionLocalFileRo { 189 ino: strs[0].parse::<Inode>()?, 190 file_path: PathBuf::from(strs[1]), 191 merkle_tree_dump_path: PathBuf::from(strs[2]), 192 signature_path: PathBuf::from(strs[3]), 193 _certificate_path: PathBuf::from(strs[4]), 194 }) 195 } 196 197 fn parse_local_ro_file_unverified_ro_option(option: &str) -> Result<OptionLocalRoFileUnverified> { 198 let strs: Vec<&str> = option.split(':').collect(); 199 if strs.len() != 2 { 200 bail!("Invalid option: {}", option); 201 } 202 Ok(OptionLocalRoFileUnverified { 203 ino: strs[0].parse::<Inode>()?, 204 file_path: PathBuf::from(strs[1]), 205 }) 206 } 207 208 fn new_config_remote_verified_file(remote_id: i32, file_size: u64) -> Result<FileConfig> { 209 let service = file::get_local_binder(); 210 let signature = service.readFsveritySignature(remote_id).context("Failed to read signature")?; 211 212 let service = Arc::new(Mutex::new(service)); 213 let authenticator = FakeAuthenticator::always_succeed(); 214 Ok(FileConfig::RemoteVerifiedReadonlyFile { 215 reader: VerifiedFileReader::new( 216 &authenticator, 217 RemoteFileReader::new(Arc::clone(&service), remote_id), 218 file_size, 219 signature, 220 RemoteMerkleTreeReader::new(Arc::clone(&service), remote_id), 221 )?, 222 file_size, 223 }) 224 } 225 226 fn new_config_remote_unverified_file(remote_id: i32, file_size: u64) -> Result<FileConfig> { 227 let reader = RemoteFileReader::new(Arc::new(Mutex::new(file::get_local_binder())), remote_id); 228 Ok(FileConfig::RemoteUnverifiedReadonlyFile { reader, file_size }) 229 } 230 231 fn new_config_local_ro_file( 232 protected_file: &Path, 233 merkle_tree_dump: &Path, 234 signature: &Path, 235 ) -> Result<FileConfig> { 236 let file = File::open(&protected_file)?; 237 let file_size = file.metadata()?.len(); 238 let file_reader = LocalFileReader::new(file)?; 239 let merkle_tree_reader = LocalFileReader::new(File::open(merkle_tree_dump)?)?; 240 let authenticator = FakeAuthenticator::always_succeed(); 241 let mut sig = Vec::new(); 242 let _ = File::open(signature)?.read_to_end(&mut sig)?; 243 let reader = 244 VerifiedFileReader::new(&authenticator, file_reader, file_size, sig, merkle_tree_reader)?; 245 Ok(FileConfig::LocalVerifiedReadonlyFile { reader, file_size }) 246 } 247 248 fn new_config_local_ro_file_unverified(file_path: &Path) -> Result<FileConfig> { 249 let reader = LocalFileReader::new(File::open(file_path)?)?; 250 let file_size = reader.len(); 251 Ok(FileConfig::LocalUnverifiedReadonlyFile { reader, file_size }) 252 } 253 254 fn new_config_remote_new_verified_file(remote_id: i32) -> Result<FileConfig> { 255 let remote_file = 256 RemoteFileEditor::new(Arc::new(Mutex::new(file::get_local_binder())), remote_id); 257 Ok(FileConfig::RemoteVerifiedNewFile { editor: VerifiedFileEditor::new(remote_file) }) 258 } 259 260 fn prepare_file_pool(args: &Args) -> Result<BTreeMap<Inode, FileConfig>> { 261 let mut file_pool = BTreeMap::new(); 262 263 for config in &args.remote_ro_file { 264 file_pool.insert( 265 config.ino, 266 new_config_remote_verified_file(config.remote_id, config.file_size)?, 267 ); 268 } 269 270 for config in &args.remote_ro_file_unverified { 271 file_pool.insert( 272 config.ino, 273 new_config_remote_unverified_file(config.remote_id, config.file_size)?, 274 ); 275 } 276 277 for config in &args.remote_new_rw_file { 278 file_pool.insert(config.ino, new_config_remote_new_verified_file(config.remote_id)?); 279 } 280 281 for config in &args.local_ro_file { 282 file_pool.insert( 283 config.ino, 284 new_config_local_ro_file( 285 &config.file_path, 286 &config.merkle_tree_dump_path, 287 &config.signature_path, 288 )?, 289 ); 290 } 291 292 for config in &args.local_ro_file_unverified { 293 file_pool.insert(config.ino, new_config_local_ro_file_unverified(&config.file_path)?); 294 } 295 296 Ok(file_pool) 297 } 298 299 fn main() -> Result<()> { 300 let args = Args::from_args(); 301 302 let log_level = if args.debug { log::Level::Debug } else { log::Level::Info }; 303 android_logger::init_once( 304 android_logger::Config::default().with_tag("authfs").with_min_level(log_level), 305 ); 306 307 let file_pool = prepare_file_pool(&args)?; 308 fusefs::loop_forever(file_pool, &args.mount_point)?; 309 bail!("Unexpected exit after the handler loop") 310 } 311