blob: b235025447f8e1e532ccb51caeade153459dabe0 [file] [log] [blame]
Victor Hsiehb0f5fc82021-10-15 17:24:19 -07001/*
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
17use anyhow::Result;
18use log::error;
19use std::cmp::min;
20use std::collections::BTreeMap;
21use std::convert::TryInto;
22use std::fs::File;
23use std::io;
24use std::os::unix::fs::FileExt;
25use std::os::unix::io::AsRawFd;
26
27use crate::fsverity;
28use authfs_aidl_interface::aidl::com::android::virt::fs::IVirtFdService::{
29 BnVirtFdService, IVirtFdService, ERROR_FILE_TOO_LARGE, ERROR_IO, ERROR_UNKNOWN_FD,
30 MAX_REQUESTING_DATA,
31};
32use authfs_aidl_interface::binder::{
33 BinderFeatures, ExceptionCode, Interface, Result as BinderResult, Status, StatusCode, Strong,
34};
35use binder_common::new_binder_exception;
36
37fn validate_and_cast_offset(offset: i64) -> Result<u64, Status> {
38 offset.try_into().map_err(|_| {
39 new_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT, format!("Invalid offset: {}", offset))
40 })
41}
42
43fn validate_and_cast_size(size: i32) -> Result<usize, Status> {
44 if size > MAX_REQUESTING_DATA {
45 Err(new_binder_exception(
46 ExceptionCode::ILLEGAL_ARGUMENT,
47 format!("Unexpectedly large size: {}", size),
48 ))
49 } else {
50 size.try_into().map_err(|_| {
51 new_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT, format!("Invalid size: {}", size))
52 })
53 }
54}
55
56/// Configuration of a file descriptor to be served/exposed/shared.
57pub enum FdConfig {
58 /// A read-only file to serve by this server. The file is supposed to be verifiable with the
59 /// associated fs-verity metadata.
60 Readonly {
61 /// The file to read from. fs-verity metadata can be retrieved from this file's FD.
62 file: File,
63
64 /// Alternative Merkle tree stored in another file.
65 alt_merkle_tree: Option<File>,
66
67 /// Alternative signature stored in another file.
68 alt_signature: Option<File>,
69 },
70
71 /// A readable/writable file to serve by this server. This backing file should just be a
72 /// regular file and does not have any specific property.
73 ReadWrite(File),
74}
75
76pub struct FdService {
77 /// A pool of opened files, may be readonly or read-writable.
78 fd_pool: BTreeMap<i32, FdConfig>,
79}
80
81impl FdService {
82 pub fn new_binder(fd_pool: BTreeMap<i32, FdConfig>) -> Strong<dyn IVirtFdService> {
83 BnVirtFdService::new_binder(FdService { fd_pool }, BinderFeatures::default())
84 }
85
Victor Hsiehc85e4ef2021-10-18 15:28:53 -070086 /// Handles the requesting file `id` with `handler` if it is in the FD pool. This function
87 /// returns whatever the handler returns.
88 fn handle_fd<F, R>(&self, id: i32, handler: F) -> BinderResult<R>
89 where
90 F: FnOnce(&FdConfig) -> BinderResult<R>,
91 {
92 let fd_config = self.fd_pool.get(&id).ok_or_else(|| Status::from(ERROR_UNKNOWN_FD))?;
93 handler(fd_config)
Victor Hsiehb0f5fc82021-10-15 17:24:19 -070094 }
95}
96
97impl Interface for FdService {}
98
99impl IVirtFdService for FdService {
100 fn readFile(&self, id: i32, offset: i64, size: i32) -> BinderResult<Vec<u8>> {
101 let size: usize = validate_and_cast_size(size)?;
102 let offset: u64 = validate_and_cast_offset(offset)?;
103
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700104 self.handle_fd(id, |config| match config {
Victor Hsiehb0f5fc82021-10-15 17:24:19 -0700105 FdConfig::Readonly { file, .. } | FdConfig::ReadWrite(file) => {
106 read_into_buf(file, size, offset).map_err(|e| {
107 error!("readFile: read error: {}", e);
108 Status::from(ERROR_IO)
109 })
110 }
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700111 })
Victor Hsiehb0f5fc82021-10-15 17:24:19 -0700112 }
113
114 fn readFsverityMerkleTree(&self, id: i32, offset: i64, size: i32) -> BinderResult<Vec<u8>> {
115 let size: usize = validate_and_cast_size(size)?;
116 let offset: u64 = validate_and_cast_offset(offset)?;
117
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700118 self.handle_fd(id, |config| match config {
Victor Hsiehb0f5fc82021-10-15 17:24:19 -0700119 FdConfig::Readonly { file, alt_merkle_tree, .. } => {
120 if let Some(tree_file) = &alt_merkle_tree {
121 read_into_buf(tree_file, size, offset).map_err(|e| {
122 error!("readFsverityMerkleTree: read error: {}", e);
123 Status::from(ERROR_IO)
124 })
125 } else {
126 let mut buf = vec![0; size];
127 let s = fsverity::read_merkle_tree(file.as_raw_fd(), offset, &mut buf)
128 .map_err(|e| {
129 error!("readFsverityMerkleTree: failed to retrieve merkle tree: {}", e);
130 Status::from(e.raw_os_error().unwrap_or(ERROR_IO))
131 })?;
132 debug_assert!(s <= buf.len(), "Shouldn't return more bytes than asked");
133 buf.truncate(s);
134 Ok(buf)
135 }
136 }
137 FdConfig::ReadWrite(_file) => {
138 // For a writable file, Merkle tree is not expected to be served since Auth FS
139 // doesn't trust it anyway. Auth FS may keep the Merkle tree privately for its own
140 // use.
141 Err(new_binder_exception(ExceptionCode::UNSUPPORTED_OPERATION, "Unsupported"))
142 }
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700143 })
Victor Hsiehb0f5fc82021-10-15 17:24:19 -0700144 }
145
146 fn readFsveritySignature(&self, id: i32) -> BinderResult<Vec<u8>> {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700147 self.handle_fd(id, |config| match config {
Victor Hsiehb0f5fc82021-10-15 17:24:19 -0700148 FdConfig::Readonly { file, alt_signature, .. } => {
149 if let Some(sig_file) = &alt_signature {
150 // Supposedly big enough buffer size to store signature.
151 let size = MAX_REQUESTING_DATA as usize;
152 let offset = 0;
153 read_into_buf(sig_file, size, offset).map_err(|e| {
154 error!("readFsveritySignature: read error: {}", e);
155 Status::from(ERROR_IO)
156 })
157 } else {
158 let mut buf = vec![0; MAX_REQUESTING_DATA as usize];
159 let s = fsverity::read_signature(file.as_raw_fd(), &mut buf).map_err(|e| {
160 error!("readFsverityMerkleTree: failed to retrieve merkle tree: {}", e);
161 Status::from(e.raw_os_error().unwrap_or(ERROR_IO))
162 })?;
163 debug_assert!(s <= buf.len(), "Shouldn't return more bytes than asked");
164 buf.truncate(s);
165 Ok(buf)
166 }
167 }
168 FdConfig::ReadWrite(_file) => {
169 // There is no signature for a writable file.
170 Err(new_binder_exception(ExceptionCode::UNSUPPORTED_OPERATION, "Unsupported"))
171 }
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700172 })
Victor Hsiehb0f5fc82021-10-15 17:24:19 -0700173 }
174
175 fn writeFile(&self, id: i32, buf: &[u8], offset: i64) -> BinderResult<i32> {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700176 self.handle_fd(id, |config| match config {
Victor Hsiehb0f5fc82021-10-15 17:24:19 -0700177 FdConfig::Readonly { .. } => Err(StatusCode::INVALID_OPERATION.into()),
178 FdConfig::ReadWrite(file) => {
179 let offset: u64 = offset.try_into().map_err(|_| {
180 new_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT, "Invalid offset")
181 })?;
182 // Check buffer size just to make `as i32` safe below.
183 if buf.len() > i32::MAX as usize {
184 return Err(new_binder_exception(
185 ExceptionCode::ILLEGAL_ARGUMENT,
186 "Buffer size is too big",
187 ));
188 }
189 Ok(file.write_at(buf, offset).map_err(|e| {
190 error!("writeFile: write error: {}", e);
191 Status::from(ERROR_IO)
192 })? as i32)
193 }
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700194 })
Victor Hsiehb0f5fc82021-10-15 17:24:19 -0700195 }
196
197 fn resize(&self, id: i32, size: i64) -> BinderResult<()> {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700198 self.handle_fd(id, |config| match config {
Victor Hsiehb0f5fc82021-10-15 17:24:19 -0700199 FdConfig::Readonly { .. } => Err(StatusCode::INVALID_OPERATION.into()),
200 FdConfig::ReadWrite(file) => {
201 if size < 0 {
202 return Err(new_binder_exception(
203 ExceptionCode::ILLEGAL_ARGUMENT,
204 "Invalid size to resize to",
205 ));
206 }
207 file.set_len(size as u64).map_err(|e| {
208 error!("resize: set_len error: {}", e);
209 Status::from(ERROR_IO)
210 })
211 }
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700212 })
Victor Hsiehb0f5fc82021-10-15 17:24:19 -0700213 }
214
215 fn getFileSize(&self, id: i32) -> BinderResult<i64> {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700216 self.handle_fd(id, |config| match config {
Victor Hsiehb0f5fc82021-10-15 17:24:19 -0700217 FdConfig::Readonly { file, .. } => {
218 let size = file
219 .metadata()
220 .map_err(|e| {
221 error!("getFileSize error: {}", e);
222 Status::from(ERROR_IO)
223 })?
224 .len();
225 Ok(size.try_into().map_err(|e| {
226 error!("getFileSize: File too large: {}", e);
227 Status::from(ERROR_FILE_TOO_LARGE)
228 })?)
229 }
230 FdConfig::ReadWrite(_file) => {
231 // Content and metadata of a writable file needs to be tracked by authfs, since
232 // fd_server isn't considered trusted. So there is no point to support getFileSize
233 // for a writable file.
234 Err(new_binder_exception(ExceptionCode::UNSUPPORTED_OPERATION, "Unsupported"))
235 }
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700236 })
Victor Hsiehb0f5fc82021-10-15 17:24:19 -0700237 }
238}
239
240fn read_into_buf(file: &File, max_size: usize, offset: u64) -> io::Result<Vec<u8>> {
241 let remaining = file.metadata()?.len().saturating_sub(offset);
242 let buf_size = min(remaining, max_size as u64) as usize;
243 let mut buf = vec![0; buf_size];
244 file.read_exact_at(&mut buf, offset)?;
245 Ok(buf)
246}