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