blob: ed3a0ea0b354567448124bdd011431edca892700 [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
86 fn get_file_config(&self, id: i32) -> BinderResult<&FdConfig> {
87 self.fd_pool.get(&id).ok_or_else(|| Status::from(ERROR_UNKNOWN_FD))
88 }
89}
90
91impl Interface for FdService {}
92
93impl IVirtFdService for FdService {
94 fn readFile(&self, id: i32, offset: i64, size: i32) -> BinderResult<Vec<u8>> {
95 let size: usize = validate_and_cast_size(size)?;
96 let offset: u64 = validate_and_cast_offset(offset)?;
97
98 match self.get_file_config(id)? {
99 FdConfig::Readonly { file, .. } | FdConfig::ReadWrite(file) => {
100 read_into_buf(file, size, offset).map_err(|e| {
101 error!("readFile: read error: {}", e);
102 Status::from(ERROR_IO)
103 })
104 }
105 }
106 }
107
108 fn readFsverityMerkleTree(&self, id: i32, offset: i64, size: i32) -> BinderResult<Vec<u8>> {
109 let size: usize = validate_and_cast_size(size)?;
110 let offset: u64 = validate_and_cast_offset(offset)?;
111
112 match &self.get_file_config(id)? {
113 FdConfig::Readonly { file, alt_merkle_tree, .. } => {
114 if let Some(tree_file) = &alt_merkle_tree {
115 read_into_buf(tree_file, size, offset).map_err(|e| {
116 error!("readFsverityMerkleTree: read error: {}", e);
117 Status::from(ERROR_IO)
118 })
119 } else {
120 let mut buf = vec![0; size];
121 let s = fsverity::read_merkle_tree(file.as_raw_fd(), offset, &mut buf)
122 .map_err(|e| {
123 error!("readFsverityMerkleTree: failed to retrieve merkle tree: {}", e);
124 Status::from(e.raw_os_error().unwrap_or(ERROR_IO))
125 })?;
126 debug_assert!(s <= buf.len(), "Shouldn't return more bytes than asked");
127 buf.truncate(s);
128 Ok(buf)
129 }
130 }
131 FdConfig::ReadWrite(_file) => {
132 // For a writable file, Merkle tree is not expected to be served since Auth FS
133 // doesn't trust it anyway. Auth FS may keep the Merkle tree privately for its own
134 // use.
135 Err(new_binder_exception(ExceptionCode::UNSUPPORTED_OPERATION, "Unsupported"))
136 }
137 }
138 }
139
140 fn readFsveritySignature(&self, id: i32) -> BinderResult<Vec<u8>> {
141 match &self.get_file_config(id)? {
142 FdConfig::Readonly { file, alt_signature, .. } => {
143 if let Some(sig_file) = &alt_signature {
144 // Supposedly big enough buffer size to store signature.
145 let size = MAX_REQUESTING_DATA as usize;
146 let offset = 0;
147 read_into_buf(sig_file, size, offset).map_err(|e| {
148 error!("readFsveritySignature: read error: {}", e);
149 Status::from(ERROR_IO)
150 })
151 } else {
152 let mut buf = vec![0; MAX_REQUESTING_DATA as usize];
153 let s = fsverity::read_signature(file.as_raw_fd(), &mut buf).map_err(|e| {
154 error!("readFsverityMerkleTree: failed to retrieve merkle tree: {}", e);
155 Status::from(e.raw_os_error().unwrap_or(ERROR_IO))
156 })?;
157 debug_assert!(s <= buf.len(), "Shouldn't return more bytes than asked");
158 buf.truncate(s);
159 Ok(buf)
160 }
161 }
162 FdConfig::ReadWrite(_file) => {
163 // There is no signature for a writable file.
164 Err(new_binder_exception(ExceptionCode::UNSUPPORTED_OPERATION, "Unsupported"))
165 }
166 }
167 }
168
169 fn writeFile(&self, id: i32, buf: &[u8], offset: i64) -> BinderResult<i32> {
170 match &self.get_file_config(id)? {
171 FdConfig::Readonly { .. } => Err(StatusCode::INVALID_OPERATION.into()),
172 FdConfig::ReadWrite(file) => {
173 let offset: u64 = offset.try_into().map_err(|_| {
174 new_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT, "Invalid offset")
175 })?;
176 // Check buffer size just to make `as i32` safe below.
177 if buf.len() > i32::MAX as usize {
178 return Err(new_binder_exception(
179 ExceptionCode::ILLEGAL_ARGUMENT,
180 "Buffer size is too big",
181 ));
182 }
183 Ok(file.write_at(buf, offset).map_err(|e| {
184 error!("writeFile: write error: {}", e);
185 Status::from(ERROR_IO)
186 })? as i32)
187 }
188 }
189 }
190
191 fn resize(&self, id: i32, size: i64) -> BinderResult<()> {
192 match &self.get_file_config(id)? {
193 FdConfig::Readonly { .. } => Err(StatusCode::INVALID_OPERATION.into()),
194 FdConfig::ReadWrite(file) => {
195 if size < 0 {
196 return Err(new_binder_exception(
197 ExceptionCode::ILLEGAL_ARGUMENT,
198 "Invalid size to resize to",
199 ));
200 }
201 file.set_len(size as u64).map_err(|e| {
202 error!("resize: set_len error: {}", e);
203 Status::from(ERROR_IO)
204 })
205 }
206 }
207 }
208
209 fn getFileSize(&self, id: i32) -> BinderResult<i64> {
210 match &self.get_file_config(id)? {
211 FdConfig::Readonly { file, .. } => {
212 let size = file
213 .metadata()
214 .map_err(|e| {
215 error!("getFileSize error: {}", e);
216 Status::from(ERROR_IO)
217 })?
218 .len();
219 Ok(size.try_into().map_err(|e| {
220 error!("getFileSize: File too large: {}", e);
221 Status::from(ERROR_FILE_TOO_LARGE)
222 })?)
223 }
224 FdConfig::ReadWrite(_file) => {
225 // Content and metadata of a writable file needs to be tracked by authfs, since
226 // fd_server isn't considered trusted. So there is no point to support getFileSize
227 // for a writable file.
228 Err(new_binder_exception(ExceptionCode::UNSUPPORTED_OPERATION, "Unsupported"))
229 }
230 }
231 }
232}
233
234fn read_into_buf(file: &File, max_size: usize, offset: u64) -> io::Result<Vec<u8>> {
235 let remaining = file.metadata()?.len().saturating_sub(offset);
236 let buf_size = min(remaining, max_size as u64) as usize;
237 let mut buf = vec![0; buf_size];
238 file.read_exact_at(&mut buf, offset)?;
239 Ok(buf)
240}