blob: f0b523798b801017bdc060f9a74becb2b9618f8f [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
30use anyhow::{bail, Result};
31use std::collections::BTreeMap;
32use std::fs::File;
33use std::io::Read;
34use std::path::PathBuf;
35use structopt::StructOpt;
36
37mod auth;
38mod common;
39mod crypto;
40mod fsverity;
41mod fusefs;
42mod reader;
43
44use auth::FakeAuthenticator;
45use fsverity::FsverityChunkedFileReader;
46use fusefs::{FileConfig, Inode};
47use reader::ChunkedFileReader;
48
49#[derive(StructOpt)]
50struct Options {
51 /// Mount point of AuthFS.
52 #[structopt(parse(from_os_str))]
53 mount_point: PathBuf,
54
55 /// Debug only. A readonly file to be protected by fs-verity. Can be multiple.
56 #[structopt(long, parse(try_from_str = parse_local_verified_file_option))]
57 local_verified_file: Vec<LocalVerifiedFileConfig>,
58
59 /// Debug only. An unverified read-only file. Can be multiple.
60 #[structopt(long, parse(try_from_str = parse_local_unverified_file_option))]
61 local_unverified_file: Vec<LocalUnverifiedFileConfig>,
62}
63
64struct LocalVerifiedFileConfig {
65 ino: Inode,
66 file_path: PathBuf,
67 merkle_tree_dump_path: PathBuf,
68 signature_path: PathBuf,
69}
70
71struct LocalUnverifiedFileConfig {
72 ino: Inode,
73 file_path: PathBuf,
74}
75
76fn parse_local_verified_file_option(option: &str) -> Result<LocalVerifiedFileConfig> {
77 let strs: Vec<&str> = option.split(':').collect();
78 if strs.len() != 4 {
79 bail!("Invalid option: {}", option);
80 }
81 Ok(LocalVerifiedFileConfig {
82 ino: strs[0].parse::<Inode>().unwrap(),
83 file_path: PathBuf::from(strs[1]),
84 merkle_tree_dump_path: PathBuf::from(strs[2]),
85 signature_path: PathBuf::from(strs[3]),
86 })
87}
88
89fn parse_local_unverified_file_option(option: &str) -> Result<LocalUnverifiedFileConfig> {
90 let strs: Vec<&str> = option.split(':').collect();
91 if strs.len() != 2 {
92 bail!("Invalid option: {}", option);
93 }
94 Ok(LocalUnverifiedFileConfig {
95 ino: strs[0].parse::<Inode>().unwrap(),
96 file_path: PathBuf::from(strs[1]),
97 })
98}
99
100fn new_config_local_verified_file(
101 protected_file: &PathBuf,
102 merkle_tree_dump: &PathBuf,
103 signature: &PathBuf,
104) -> Result<FileConfig> {
105 let file = File::open(&protected_file)?;
106 let file_size = file.metadata()?.len();
107 let file_reader = ChunkedFileReader::new(file)?;
108 let merkle_tree_reader = ChunkedFileReader::new(File::open(merkle_tree_dump)?)?;
109 let authenticator = FakeAuthenticator::always_succeed();
110 let mut sig = Vec::new();
111 let _ = File::open(signature)?.read_to_end(&mut sig)?;
112 let file_reader = FsverityChunkedFileReader::new(
113 &authenticator,
114 file_reader,
115 file_size,
116 sig,
117 merkle_tree_reader,
118 )?;
119 Ok(FileConfig::LocalVerifiedFile(file_reader, file_size))
120}
121
122fn new_config_local_unverified_file(file_path: &PathBuf) -> Result<FileConfig> {
123 let file = File::open(file_path)?;
124 let file_size = file.metadata()?.len();
125 let file_reader = ChunkedFileReader::new(file)?;
126 Ok(FileConfig::LocalUnverifiedFile(file_reader, file_size))
127}
128
129fn prepare_file_pool(args: &Options) -> Result<BTreeMap<Inode, FileConfig>> {
130 let mut file_pool = BTreeMap::new();
131
132 for config in &args.local_verified_file {
133 file_pool.insert(
134 config.ino,
135 new_config_local_verified_file(
136 &config.file_path,
137 &config.merkle_tree_dump_path,
138 &config.signature_path,
139 )?,
140 );
141 }
142
143 for config in &args.local_unverified_file {
144 file_pool.insert(config.ino, new_config_local_unverified_file(&config.file_path)?);
145 }
146
147 Ok(file_pool)
148}
149
150fn main() -> Result<()> {
151 let args = Options::from_args();
152 let file_pool = prepare_file_pool(&args)?;
153 fusefs::loop_forever(file_pool, &args.mount_point)?;
154 Ok(())
155}