1 /*
2  * Copyright (C) 2021 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 use anyhow::Result;
18 use log::error;
19 use nix::{
20     errno::Errno, fcntl::openat, fcntl::OFlag, sys::stat::fchmod, sys::stat::mkdirat,
21     sys::stat::mode_t, sys::stat::Mode, sys::statvfs::statvfs, sys::statvfs::Statvfs,
22     unistd::unlinkat, unistd::UnlinkatFlags,
23 };
24 use std::cmp::min;
25 use std::collections::{btree_map, BTreeMap};
26 use std::convert::TryInto;
27 use std::fs::File;
28 use std::io;
29 use std::os::unix::fs::FileExt;
30 use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd};
31 use std::path::{Component, Path, PathBuf, MAIN_SEPARATOR};
32 use std::sync::{Arc, RwLock};
33 
34 use authfs_aidl_interface::aidl::com::android::virt::fs::IVirtFdService::{
35     BnVirtFdService, FsStat::FsStat, IVirtFdService, MAX_REQUESTING_DATA,
36 };
37 use authfs_fsverity_metadata::{
38     get_fsverity_metadata_path, parse_fsverity_metadata, FSVerityMetadata,
39 };
40 use binder::{
41     BinderFeatures, ExceptionCode, Interface, Result as BinderResult, Status, StatusCode, Strong,
42 };
43 
44 /// Bitflags of forbidden file mode, e.g. setuid, setgid and sticky bit.
45 const FORBIDDEN_MODES: Mode = Mode::from_bits_truncate(!0o777);
46 
47 /// Configuration of a file descriptor to be served/exposed/shared.
48 pub enum FdConfig {
49     /// A read-only file to serve by this server. The file is supposed to be verifiable with the
50     /// associated fs-verity metadata.
51     Readonly {
52         /// The file to read from. fs-verity metadata can be retrieved from this file's FD.
53         file: File,
54 
55         // Alternative metadata storing merkle tree and signature.
56         alt_metadata: Option<Box<FSVerityMetadata>>,
57     },
58 
59     /// A readable/writable file to serve by this server. This backing file should just be a
60     /// regular file and does not have any specific property.
61     ReadWrite(File),
62 
63     /// A read-only directory to serve by this server.
64     InputDir(OwnedFd),
65 
66     /// A writable directory to serve by this server.
67     OutputDir(OwnedFd),
68 }
69 
70 pub struct FdService {
71     /// A pool of opened files and directories, which can be looked up by the FD number.
72     fd_pool: Arc<RwLock<BTreeMap<i32, FdConfig>>>,
73 }
74 
75 impl FdService {
new_binder(fd_pool: BTreeMap<i32, FdConfig>) -> Strong<dyn IVirtFdService>76     pub fn new_binder(fd_pool: BTreeMap<i32, FdConfig>) -> Strong<dyn IVirtFdService> {
77         BnVirtFdService::new_binder(
78             FdService { fd_pool: Arc::new(RwLock::new(fd_pool)) },
79             BinderFeatures::default(),
80         )
81     }
82 
83     /// Handles the requesting file `id` with `handle_fn` if it is in the FD pool. This function
84     /// returns whatever `handle_fn` returns.
handle_fd<F, R>(&self, id: i32, handle_fn: F) -> BinderResult<R> where F: FnOnce(&FdConfig) -> BinderResult<R>,85     fn handle_fd<F, R>(&self, id: i32, handle_fn: F) -> BinderResult<R>
86     where
87         F: FnOnce(&FdConfig) -> BinderResult<R>,
88     {
89         let fd_pool = self.fd_pool.read().unwrap();
90         let fd_config = fd_pool.get(&id).ok_or_else(|| new_errno_error(Errno::EBADF))?;
91         handle_fn(fd_config)
92     }
93 
94     /// Inserts a new FD and corresponding `FdConfig` created by `create_fn` to the FD pool, then
95     /// returns the new FD number.
insert_new_fd<F>(&self, fd: i32, create_fn: F) -> BinderResult<i32> where F: FnOnce(&mut FdConfig) -> BinderResult<(i32, FdConfig)>,96     fn insert_new_fd<F>(&self, fd: i32, create_fn: F) -> BinderResult<i32>
97     where
98         F: FnOnce(&mut FdConfig) -> BinderResult<(i32, FdConfig)>,
99     {
100         let mut fd_pool = self.fd_pool.write().unwrap();
101         let fd_config = fd_pool.get_mut(&fd).ok_or_else(|| new_errno_error(Errno::EBADF))?;
102         let (new_fd, new_fd_config) = create_fn(fd_config)?;
103         if let btree_map::Entry::Vacant(entry) = fd_pool.entry(new_fd) {
104             entry.insert(new_fd_config);
105             Ok(new_fd)
106         } else {
107             Err(Status::new_exception_str(
108                 ExceptionCode::ILLEGAL_STATE,
109                 Some(format!(
110                     "The newly created FD {} is already in the pool unexpectedly",
111                     new_fd
112                 )),
113             ))
114         }
115     }
116 }
117 
118 impl Interface for FdService {}
119 
120 impl IVirtFdService for FdService {
readFile(&self, id: i32, offset: i64, size: i32) -> BinderResult<Vec<u8>>121     fn readFile(&self, id: i32, offset: i64, size: i32) -> BinderResult<Vec<u8>> {
122         let size: usize = validate_and_cast_size(size)?;
123         let offset: u64 = validate_and_cast_offset(offset)?;
124 
125         self.handle_fd(id, |config| match config {
126             FdConfig::Readonly { file, .. } | FdConfig::ReadWrite(file) => {
127                 read_into_buf(file, size, offset).map_err(|e| {
128                     error!("readFile: read error: {}", e);
129                     new_errno_error(Errno::EIO)
130                 })
131             }
132             FdConfig::InputDir(_) | FdConfig::OutputDir(_) => Err(new_errno_error(Errno::EISDIR)),
133         })
134     }
135 
readFsverityMerkleTree(&self, id: i32, offset: i64, size: i32) -> BinderResult<Vec<u8>>136     fn readFsverityMerkleTree(&self, id: i32, offset: i64, size: i32) -> BinderResult<Vec<u8>> {
137         let size: usize = validate_and_cast_size(size)?;
138         let offset: u64 = validate_and_cast_offset(offset)?;
139 
140         self.handle_fd(id, |config| match config {
141             FdConfig::Readonly { file, alt_metadata, .. } => {
142                 let mut buf = vec![0; size];
143 
144                 let s = if let Some(metadata) = &alt_metadata {
145                     metadata.read_merkle_tree(offset, &mut buf).map_err(|e| {
146                         error!("readFsverityMerkleTree: read error: {}", e);
147                         new_errno_error(Errno::EIO)
148                     })?
149                 } else {
150                     fsverity::read_merkle_tree(file.as_raw_fd(), offset, &mut buf).map_err(|e| {
151                         error!("readFsverityMerkleTree: failed to retrieve merkle tree: {}", e);
152                         new_errno_error(Errno::EIO)
153                     })?
154                 };
155                 debug_assert!(s <= buf.len(), "Shouldn't return more bytes than asked");
156                 buf.truncate(s);
157                 Ok(buf)
158             }
159             FdConfig::ReadWrite(_file) => {
160                 // For a writable file, Merkle tree is not expected to be served since Auth FS
161                 // doesn't trust it anyway. Auth FS may keep the Merkle tree privately for its own
162                 // use.
163                 Err(new_errno_error(Errno::ENOSYS))
164             }
165             FdConfig::InputDir(_) | FdConfig::OutputDir(_) => Err(new_errno_error(Errno::EISDIR)),
166         })
167     }
168 
readFsveritySignature(&self, id: i32) -> BinderResult<Vec<u8>>169     fn readFsveritySignature(&self, id: i32) -> BinderResult<Vec<u8>> {
170         self.handle_fd(id, |config| match config {
171             FdConfig::Readonly { file, alt_metadata, .. } => {
172                 if let Some(metadata) = &alt_metadata {
173                     if let Some(signature) = &metadata.signature {
174                         Ok(signature.clone())
175                     } else {
176                         Err(Status::new_service_specific_error_str(
177                             -1,
178                             Some("metadata doesn't contain a signature"),
179                         ))
180                     }
181                 } else {
182                     let mut buf = vec![0; MAX_REQUESTING_DATA as usize];
183                     let s = fsverity::read_signature(file.as_raw_fd(), &mut buf).map_err(|e| {
184                         error!("readFsverityMerkleTree: failed to retrieve merkle tree: {}", e);
185                         new_errno_error(Errno::EIO)
186                     })?;
187                     debug_assert!(s <= buf.len(), "Shouldn't return more bytes than asked");
188                     buf.truncate(s);
189                     Ok(buf)
190                 }
191             }
192             FdConfig::ReadWrite(_file) => {
193                 // There is no signature for a writable file.
194                 Err(new_errno_error(Errno::ENOSYS))
195             }
196             FdConfig::InputDir(_) | FdConfig::OutputDir(_) => Err(new_errno_error(Errno::EISDIR)),
197         })
198     }
199 
writeFile(&self, id: i32, buf: &[u8], offset: i64) -> BinderResult<i32>200     fn writeFile(&self, id: i32, buf: &[u8], offset: i64) -> BinderResult<i32> {
201         self.handle_fd(id, |config| match config {
202             FdConfig::Readonly { .. } => Err(StatusCode::INVALID_OPERATION.into()),
203             FdConfig::ReadWrite(file) => {
204                 let offset: u64 = offset.try_into().map_err(|_| new_errno_error(Errno::EINVAL))?;
205                 // Check buffer size just to make `as i32` safe below.
206                 if buf.len() > i32::MAX as usize {
207                     return Err(new_errno_error(Errno::EOVERFLOW));
208                 }
209                 Ok(file.write_at(buf, offset).map_err(|e| {
210                     error!("writeFile: write error: {}", e);
211                     new_errno_error(Errno::EIO)
212                 })? as i32)
213             }
214             FdConfig::InputDir(_) | FdConfig::OutputDir(_) => Err(new_errno_error(Errno::EISDIR)),
215         })
216     }
217 
resize(&self, id: i32, size: i64) -> BinderResult<()>218     fn resize(&self, id: i32, size: i64) -> BinderResult<()> {
219         self.handle_fd(id, |config| match config {
220             FdConfig::Readonly { .. } => Err(StatusCode::INVALID_OPERATION.into()),
221             FdConfig::ReadWrite(file) => {
222                 if size < 0 {
223                     return Err(new_errno_error(Errno::EINVAL));
224                 }
225                 file.set_len(size as u64).map_err(|e| {
226                     error!("resize: set_len error: {}", e);
227                     new_errno_error(Errno::EIO)
228                 })
229             }
230             FdConfig::InputDir(_) | FdConfig::OutputDir(_) => Err(new_errno_error(Errno::EISDIR)),
231         })
232     }
233 
getFileSize(&self, id: i32) -> BinderResult<i64>234     fn getFileSize(&self, id: i32) -> BinderResult<i64> {
235         self.handle_fd(id, |config| match config {
236             FdConfig::Readonly { file, .. } => {
237                 let size = file
238                     .metadata()
239                     .map_err(|e| {
240                         error!("getFileSize error: {}", e);
241                         new_errno_error(Errno::EIO)
242                     })?
243                     .len();
244                 Ok(size.try_into().map_err(|e| {
245                     error!("getFileSize: File too large: {}", e);
246                     new_errno_error(Errno::EFBIG)
247                 })?)
248             }
249             FdConfig::ReadWrite(_file) => {
250                 // Content and metadata of a writable file needs to be tracked by authfs, since
251                 // fd_server isn't considered trusted. So there is no point to support getFileSize
252                 // for a writable file.
253                 Err(new_errno_error(Errno::ENOSYS))
254             }
255             FdConfig::InputDir(_) | FdConfig::OutputDir(_) => Err(new_errno_error(Errno::EISDIR)),
256         })
257     }
258 
openFileInDirectory(&self, dir_fd: i32, file_path: &str) -> BinderResult<i32>259     fn openFileInDirectory(&self, dir_fd: i32, file_path: &str) -> BinderResult<i32> {
260         let path_buf = PathBuf::from(file_path);
261         // Checks if the path is a simple, related path.
262         if path_buf.components().any(|c| !matches!(c, Component::Normal(_))) {
263             return Err(new_errno_error(Errno::EINVAL));
264         }
265 
266         self.insert_new_fd(dir_fd, |config| match config {
267             FdConfig::InputDir(dir) => {
268                 let file = open_readonly_at(dir.as_fd(), &path_buf).map_err(new_errno_error)?;
269 
270                 let metadata_path_buf = get_fsverity_metadata_path(&path_buf);
271                 let metadata = open_readonly_at(dir.as_fd(), &metadata_path_buf)
272                     .ok()
273                     .and_then(|f| parse_fsverity_metadata(f).ok());
274 
275                 Ok((file.as_raw_fd(), FdConfig::Readonly { file, alt_metadata: metadata }))
276             }
277             FdConfig::OutputDir(_) => {
278                 Err(new_errno_error(Errno::ENOSYS)) // TODO: Implement when needed
279             }
280             _ => Err(new_errno_error(Errno::ENOTDIR)),
281         })
282     }
283 
createFileInDirectory(&self, dir_fd: i32, basename: &str, mode: i32) -> BinderResult<i32>284     fn createFileInDirectory(&self, dir_fd: i32, basename: &str, mode: i32) -> BinderResult<i32> {
285         validate_basename(basename)?;
286 
287         self.insert_new_fd(dir_fd, |config| match config {
288             FdConfig::InputDir(_) => Err(new_errno_error(Errno::EACCES)),
289             FdConfig::OutputDir(dir) => {
290                 let mode = validate_file_mode(mode)?;
291                 let new_fd = openat(
292                     Some(dir.as_raw_fd()),
293                     basename,
294                     // This function is supposed to be only called when FUSE/authfs thinks the file
295                     // does not exist. However, if the file does exist from the view of fd_server
296                     // (where the execution context is considered untrusted), we prefer to honor
297                     // authfs and still allow the create to success. Therefore, always use O_TRUNC.
298                     OFlag::O_CREAT | OFlag::O_RDWR | OFlag::O_TRUNC,
299                     mode,
300                 )
301                 .map_err(new_errno_error)?;
302                 // SAFETY: new_fd is just created and not an error.
303                 let new_file = unsafe { File::from_raw_fd(new_fd) };
304                 Ok((new_fd, FdConfig::ReadWrite(new_file)))
305             }
306             _ => Err(new_errno_error(Errno::ENOTDIR)),
307         })
308     }
309 
createDirectoryInDirectory( &self, dir_fd: i32, basename: &str, mode: i32, ) -> BinderResult<i32>310     fn createDirectoryInDirectory(
311         &self,
312         dir_fd: i32,
313         basename: &str,
314         mode: i32,
315     ) -> BinderResult<i32> {
316         validate_basename(basename)?;
317 
318         self.insert_new_fd(dir_fd, |config| match config {
319             FdConfig::InputDir(_) => Err(new_errno_error(Errno::EACCES)),
320             FdConfig::OutputDir(_) => {
321                 let mode = validate_file_mode(mode)?;
322                 mkdirat(Some(dir_fd), basename, mode).map_err(new_errno_error)?;
323                 let new_dir_fd = openat(
324                     Some(dir_fd),
325                     basename,
326                     OFlag::O_DIRECTORY | OFlag::O_RDONLY,
327                     Mode::empty(),
328                 )
329                 .map_err(new_errno_error)?;
330                 // SAFETY: new_dir_fd is just created and not an error.
331                 let fd_owner = unsafe { OwnedFd::from_raw_fd(new_dir_fd) };
332                 Ok((new_dir_fd, FdConfig::OutputDir(fd_owner)))
333             }
334             _ => Err(new_errno_error(Errno::ENOTDIR)),
335         })
336     }
337 
deleteFile(&self, dir_fd: i32, basename: &str) -> BinderResult<()>338     fn deleteFile(&self, dir_fd: i32, basename: &str) -> BinderResult<()> {
339         validate_basename(basename)?;
340 
341         self.handle_fd(dir_fd, |config| match config {
342             FdConfig::OutputDir(_) => {
343                 unlinkat(Some(dir_fd), basename, UnlinkatFlags::NoRemoveDir)
344                     .map_err(new_errno_error)?;
345                 Ok(())
346             }
347             FdConfig::InputDir(_) => Err(new_errno_error(Errno::EACCES)),
348             _ => Err(new_errno_error(Errno::ENOTDIR)),
349         })
350     }
351 
deleteDirectory(&self, dir_fd: i32, basename: &str) -> BinderResult<()>352     fn deleteDirectory(&self, dir_fd: i32, basename: &str) -> BinderResult<()> {
353         validate_basename(basename)?;
354 
355         self.handle_fd(dir_fd, |config| match config {
356             FdConfig::OutputDir(_) => {
357                 unlinkat(Some(dir_fd), basename, UnlinkatFlags::RemoveDir)
358                     .map_err(new_errno_error)?;
359                 Ok(())
360             }
361             FdConfig::InputDir(_) => Err(new_errno_error(Errno::EACCES)),
362             _ => Err(new_errno_error(Errno::ENOTDIR)),
363         })
364     }
365 
chmod(&self, fd: i32, mode: i32) -> BinderResult<()>366     fn chmod(&self, fd: i32, mode: i32) -> BinderResult<()> {
367         self.handle_fd(fd, |config| match config {
368             FdConfig::ReadWrite(_) | FdConfig::OutputDir(_) => {
369                 let mode = validate_file_mode(mode)?;
370                 fchmod(fd, mode).map_err(new_errno_error)
371             }
372             _ => Err(new_errno_error(Errno::EACCES)),
373         })
374     }
375 
statfs(&self) -> BinderResult<FsStat>376     fn statfs(&self) -> BinderResult<FsStat> {
377         let st = statvfs("/data").map_err(new_errno_error)?;
378         try_into_fs_stat(st).map_err(|_e| new_errno_error(Errno::EINVAL))
379     }
380 }
381 
382 // FFI types like `c_long` vary on 32/64-bit, and the check is only needed on
383 // 64-bit conversions. Fixing this lint makes the code less readable.
384 #[allow(unknown_lints)]
385 #[allow(clippy::unnecessary_fallible_conversions)]
try_into_fs_stat(st: Statvfs) -> Result<FsStat, std::num::TryFromIntError>386 fn try_into_fs_stat(st: Statvfs) -> Result<FsStat, std::num::TryFromIntError> {
387     Ok(FsStat {
388         blockSize: st.block_size().try_into()?,
389         fragmentSize: st.fragment_size().try_into()?,
390         blockNumbers: st.blocks().try_into()?,
391         blockAvailable: st.blocks_available().try_into()?,
392         inodesAvailable: st.files_available().try_into()?,
393         maxFilename: st.name_max().try_into()?,
394     })
395 }
396 
read_into_buf(file: &File, max_size: usize, offset: u64) -> io::Result<Vec<u8>>397 fn read_into_buf(file: &File, max_size: usize, offset: u64) -> io::Result<Vec<u8>> {
398     let remaining = file.metadata()?.len().saturating_sub(offset);
399     let buf_size = min(remaining, max_size as u64) as usize;
400     let mut buf = vec![0; buf_size];
401     file.read_exact_at(&mut buf, offset)?;
402     Ok(buf)
403 }
404 
new_errno_error(errno: Errno) -> Status405 fn new_errno_error(errno: Errno) -> Status {
406     Status::new_service_specific_error_str(errno as i32, Some(errno.desc()))
407 }
408 
open_readonly_at(dir_fd: BorrowedFd, path: &Path) -> nix::Result<File>409 fn open_readonly_at(dir_fd: BorrowedFd, path: &Path) -> nix::Result<File> {
410     let new_fd = openat(Some(dir_fd.as_raw_fd()), path, OFlag::O_RDONLY, Mode::empty())?;
411     // SAFETY: new_fd is just created successfully and not owned.
412     let new_file = unsafe { File::from_raw_fd(new_fd) };
413     Ok(new_file)
414 }
415 
validate_and_cast_offset(offset: i64) -> Result<u64, Status>416 fn validate_and_cast_offset(offset: i64) -> Result<u64, Status> {
417     offset.try_into().map_err(|_| new_errno_error(Errno::EINVAL))
418 }
419 
validate_and_cast_size(size: i32) -> Result<usize, Status>420 fn validate_and_cast_size(size: i32) -> Result<usize, Status> {
421     if size > MAX_REQUESTING_DATA {
422         Err(new_errno_error(Errno::EFBIG))
423     } else {
424         size.try_into().map_err(|_| new_errno_error(Errno::EINVAL))
425     }
426 }
427 
validate_basename(name: &str) -> BinderResult<()>428 fn validate_basename(name: &str) -> BinderResult<()> {
429     if name.contains(MAIN_SEPARATOR) {
430         Err(new_errno_error(Errno::EINVAL))
431     } else {
432         Ok(())
433     }
434 }
435 
validate_file_mode(mode: i32) -> BinderResult<Mode>436 fn validate_file_mode(mode: i32) -> BinderResult<Mode> {
437     let mode = Mode::from_bits(mode as mode_t).ok_or_else(|| new_errno_error(Errno::EINVAL))?;
438     if mode.intersects(FORBIDDEN_MODES) {
439         Err(new_errno_error(Errno::EPERM))
440     } else {
441         Ok(mode)
442     }
443 }
444