diff --git a/authfs/fd_server/Android.bp b/authfs/fd_server/Android.bp index 9c810a89..f82b72f9 100644 --- a/authfs/fd_server/Android.bp +++ b/authfs/fd_server/Android.bp @@ -13,6 +13,7 @@ rust_binary { "libclap", "liblibc", "liblog_rust", + "libnix", ], apex_available: ["com.android.virt"], } diff --git a/authfs/fd_server/src/fsverity.rs b/authfs/fd_server/src/fsverity.rs new file mode 100644 index 00000000..e89bbd0f --- /dev/null +++ b/authfs/fd_server/src/fsverity.rs @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use nix::ioctl_readwrite; +use std::io; + +// Constants/values from uapi/linux/fsverity.h +const FS_VERITY_METADATA_TYPE_MERKLE_TREE: u64 = 1; +const FS_VERITY_METADATA_TYPE_SIGNATURE: u64 = 3; +const FS_IOCTL_MAGIC: u8 = b'f'; +const FS_IOCTL_READ_VERITY_METADATA: u8 = 135; + +#[repr(C)] +pub struct fsverity_read_metadata_arg { + metadata_type: u64, + offset: u64, + length: u64, + buf_ptr: u64, + __reserved: u64, +} + +ioctl_readwrite!( + read_verity_metadata, + FS_IOCTL_MAGIC, + FS_IOCTL_READ_VERITY_METADATA, + fsverity_read_metadata_arg +); + +fn read_metadata(fd: i32, metadata_type: u64, offset: u64, buf: &mut [u8]) -> io::Result { + let mut arg = fsverity_read_metadata_arg { + metadata_type, + offset, + length: buf.len() as u64, + buf_ptr: buf.as_mut_ptr() as u64, + __reserved: 0, + }; + Ok(unsafe { read_verity_metadata(fd, &mut arg) }.map_err(|e| { + if let nix::Error::Sys(errno) = e { + io::Error::from_raw_os_error(errno as i32) + } else { + // Document of nix::sys::ioctl indicates the macro-generated function always returns an + // nix::errno::Errno, which can be converted nix::Error::Sys above. As the result, this + // branch is unreachable. + unreachable!(); + } + })? as usize) +} + +/// Read the raw Merkle tree from the fd, if it exists. The API semantics is similar to a regular +/// pread(2), and may not return full requested buffer. +pub fn read_merkle_tree(fd: i32, offset: u64, buf: &mut [u8]) -> io::Result { + read_metadata(fd, FS_VERITY_METADATA_TYPE_MERKLE_TREE, offset, buf) +} + +/// Read the fs-verity signature from the fd (if exists). The returned signature should be complete. +pub fn read_signature(fd: i32, buf: &mut [u8]) -> io::Result { + read_metadata(fd, FS_VERITY_METADATA_TYPE_SIGNATURE, 0 /* offset */, buf) +} diff --git a/authfs/fd_server/src/main.rs b/authfs/fd_server/src/main.rs index 44817d5b..99b6e9eb 100644 --- a/authfs/fd_server/src/main.rs +++ b/authfs/fd_server/src/main.rs @@ -25,6 +25,8 @@ //! [1] Since the remote binder is not ready, this currently implementation uses local binder //! first. +mod fsverity; + use std::cmp::min; use std::collections::BTreeMap; use std::convert::TryInto; @@ -32,7 +34,7 @@ use std::ffi::CString; use std::fs::File; use std::io; use std::os::unix::fs::FileExt; -use std::os::unix::io::FromRawFd; +use std::os::unix::io::{AsRawFd, FromRawFd}; use anyhow::{bail, Context, Result}; use binder::IBinderInternal; // TODO(178852354): remove once set_requesting_sid is exposed in the API. @@ -130,18 +132,22 @@ impl IVirtFdService for FdService { let offset: u64 = validate_and_cast_offset(offset)?; match &self.get_file_config(id)? { - FdConfig::Readonly { alt_merkle_tree, .. } => { - if let Some(file) = &alt_merkle_tree { - read_into_buf(&file, size, offset).map_err(|e| { + FdConfig::Readonly { file, alt_merkle_tree, .. } => { + if let Some(tree_file) = &alt_merkle_tree { + read_into_buf(&tree_file, size, offset).map_err(|e| { error!("readFsverityMerkleTree: read error: {}", e); Status::from(ERROR_IO) }) } else { - // TODO(victorhsieh) retrieve from the fd when the new ioctl is ready - Err(new_binder_exception( - ExceptionCode::UNSUPPORTED_OPERATION, - "Not implemented yet", - )) + let mut buf = vec![0; size]; + let s = fsverity::read_merkle_tree(file.as_raw_fd(), offset, &mut buf) + .map_err(|e| { + error!("readFsverityMerkleTree: failed to retrieve merkle tree: {}", e); + Status::from(e.raw_os_error().unwrap_or(ERROR_IO)) + })?; + debug_assert!(s <= buf.len(), "Shouldn't return more bytes than asked"); + buf.truncate(s); + Ok(buf) } } FdConfig::ReadWrite(_file) => { @@ -155,20 +161,24 @@ impl IVirtFdService for FdService { fn readFsveritySignature(&self, id: i32) -> BinderResult> { match &self.get_file_config(id)? { - FdConfig::Readonly { alt_signature, .. } => { - if let Some(file) = &alt_signature { + FdConfig::Readonly { file, alt_signature, .. } => { + if let Some(sig_file) = &alt_signature { // Supposedly big enough buffer size to store signature. let size = MAX_REQUESTING_DATA as usize; - read_into_buf(&file, size, 0).map_err(|e| { + let offset = 0; + read_into_buf(&sig_file, size, offset).map_err(|e| { error!("readFsveritySignature: read error: {}", e); Status::from(ERROR_IO) }) } else { - // TODO(victorhsieh) retrieve from the fd when the new ioctl is ready - Err(new_binder_exception( - ExceptionCode::UNSUPPORTED_OPERATION, - "Not implemented yet", - )) + let mut buf = vec![0; MAX_REQUESTING_DATA as usize]; + let s = fsverity::read_signature(file.as_raw_fd(), &mut buf).map_err(|e| { + error!("readFsverityMerkleTree: failed to retrieve merkle tree: {}", e); + Status::from(e.raw_os_error().unwrap_or(ERROR_IO)) + })?; + debug_assert!(s <= buf.len(), "Shouldn't return more bytes than asked"); + buf.truncate(s); + Ok(buf) } } FdConfig::ReadWrite(_file) => {