starnix_core/vfs/
fsverity.rs1use crate::mm::MemoryAccessorExt;
6use crate::task::CurrentTask;
7use mundane::hash::{Digest, Hasher, Sha256, Sha512};
8use num_derive::FromPrimitive;
9use num_traits::FromPrimitive;
10use starnix_logging::track_stub;
11use starnix_uapi::errors::Errno;
12use starnix_uapi::user_address::UserAddress;
13use starnix_uapi::{
14 FS_VERITY_HASH_ALG_SHA256, FS_VERITY_HASH_ALG_SHA512, errno, error, fsverity_descriptor,
15 fsverity_enable_arg, fsverity_read_metadata_arg,
16};
17use zerocopy::IntoBytes;
18
19#[derive(Copy, Clone, Debug, Eq, FromPrimitive, PartialEq)]
20enum HashAlgorithm {
21 SHA256 = FS_VERITY_HASH_ALG_SHA256 as isize,
22 SHA512 = FS_VERITY_HASH_ALG_SHA512 as isize,
23}
24
25fn fsverity_descriptor_from_enable_arg(
28 current_task: &CurrentTask,
29 filesystem_block_size: u32,
30 src: &fsverity_enable_arg,
31) -> Result<fsverity_descriptor, Errno> {
32 if src.version != 1 {
33 return error!(EINVAL);
34 }
35 if src.block_size.count_ones() != 1
37 || src.block_size < 1024
38 || src.block_size > std::cmp::min(4096, filesystem_block_size)
39 {
40 return error!(EINVAL);
41 }
42 if src.hash_algorithm != FS_VERITY_HASH_ALG_SHA256
43 && src.hash_algorithm != FS_VERITY_HASH_ALG_SHA512
44 {
45 return error!(ENOTSUP);
46 }
47 if src.salt_size > 32 {
48 return error!(EINVAL);
49 }
50 if src.sig_size > 0 {
51 track_stub!(TODO("https://fxbug.dev/302620572"), "fsverity_enable signature");
54 return error!(ENOTSUP);
55 }
56 let salt =
57 current_task.read_memory_to_vec(UserAddress::from(src.salt_ptr), src.salt_size as usize)?;
58 let mut desc = fsverity_descriptor {
59 version: 1,
60 hash_algorithm: HashAlgorithm::from_u32(src.hash_algorithm).ok_or_else(|| errno!(EINVAL))?
61 as u8,
62 salt_size: salt.len() as u8,
63 log_blocksize: (u32::BITS - 1 - src.block_size.leading_zeros()) as u8,
64 ..Default::default()
65 };
66 desc.salt[..src.salt_size as usize].clone_from_slice(salt.as_slice());
67 Ok(desc)
68}
69
70fn fsverity_measurement(descriptor: &fsverity_descriptor) -> Result<Box<[u8]>, Errno> {
73 match descriptor.hash_algorithm.into() {
74 FS_VERITY_HASH_ALG_SHA256 => {
75 let mut hasher = Sha256::default();
76 if descriptor.salt_size > 0 {
77 hasher.update(&descriptor.salt[..descriptor.salt_size as usize]);
78 }
79 hasher.update(descriptor.as_bytes());
80 Ok(Box::new(hasher.finish().bytes()))
81 }
82 FS_VERITY_HASH_ALG_SHA512 => {
83 let mut hasher = Sha512::default();
84 if descriptor.salt_size > 0 {
85 hasher.update(&descriptor.salt[..descriptor.salt_size as usize]);
86 }
87 hasher.update(descriptor.as_bytes());
88 Ok(Box::new(hasher.finish().bytes()))
89 }
90 _ => error!(EINVAL),
91 }
92}
93
94#[derive(Debug, FromPrimitive)]
95enum MetadataType {
96 MerkleTree = 1,
97 Descriptor = 2,
98 Signature = 3,
99}
100
101#[derive(Debug, Default)]
103pub enum FsVerityState {
104 #[default]
106 None,
107 Building,
113 FsVerity,
118}
119
120impl FsVerityState {
121 pub fn check_writable(&self) -> Result<(), Errno> {
123 match self {
124 FsVerityState::None => Ok(()),
125 FsVerityState::Building => error!(ETXTBSY),
126 FsVerityState::FsVerity => error!(EACCES),
127 }
128 }
129}
130
131pub mod ioctl {
132
133 use crate::mm::{MemoryAccessor, MemoryAccessorExt};
134 use crate::task::CurrentTask;
135 use crate::vfs::fsverity::{
136 FsVerityState, HashAlgorithm, MetadataType, fsverity_descriptor_from_enable_arg,
137 fsverity_enable_arg, fsverity_measurement, fsverity_read_metadata_arg,
138 };
139 use crate::vfs::{FileObject, FileWriteGuardMode};
140 use num_traits::FromPrimitive;
141
142 use starnix_syscalls::{SUCCESS, SyscallResult};
143 use starnix_uapi::errors::Errno;
144 use starnix_uapi::user_address::{UserAddress, UserRef};
145 use starnix_uapi::{errno, error, uapi};
146 use zerocopy::IntoBytes;
147
148 pub fn enable(
150 task: &CurrentTask,
151 arg: UserAddress,
152 file: &FileObject,
153 ) -> Result<SyscallResult, Errno> {
154 if file.can_write() {
155 return error!(ETXTBSY);
156 }
157 let block_size = file.name.entry.node.fs().statfs(task)?.f_bsize as u32;
158 let args: fsverity_enable_arg = task.read_object(arg.into())?;
160 let mut descriptor = fsverity_descriptor_from_enable_arg(task, block_size, &args)?;
161 descriptor.data_size = file.node().fetch_and_refresh_info(task)?.size as u64;
162 let _mapping = file.name.clone().into_mapping(Some(FileWriteGuardMode::ExecMapping))?;
164 {
165 let mut fsverity = file.node().fsverity.lock();
166 match *fsverity {
167 FsVerityState::Building => return error!(EBUSY),
168 FsVerityState::FsVerity => return error!(EEXIST),
169 FsVerityState::None => *fsverity = FsVerityState::Building,
170 }
171 }
172 let result = file.node().enable_fsverity(task, &descriptor);
173 *file.node().fsverity.lock() =
174 if result.is_ok() { FsVerityState::FsVerity } else { FsVerityState::None };
175 result.map(|()| SUCCESS)
176 }
177
178 pub fn measure(
180 task: &CurrentTask,
181 arg: UserAddress,
182 file: &FileObject,
183 ) -> Result<SyscallResult, Errno> {
184 let header_ref = UserRef::<uapi::fsverity_digest>::new(arg);
185 let digest_addr = header_ref.next()?.addr();
186 let header = task.read_object(header_ref.clone())?;
187 match &*file.node().fsverity.lock() {
188 FsVerityState::FsVerity => {
189 let block_size = file.name.entry.node.fs().statfs(task)?.f_bsize as u32;
190 if !block_size.is_power_of_two() {
191 return error!(EINVAL);
192 }
193 let descriptor =
194 file.node().ops().get_fsverity_descriptor(block_size.ilog2() as u8)?;
195 let digest_algorithm = HashAlgorithm::from_u8(descriptor.hash_algorithm)
196 .ok_or_else(|| errno!(EINVAL))?;
197 let required_size = match digest_algorithm {
198 HashAlgorithm::SHA256 => 32,
199 HashAlgorithm::SHA512 => 64,
200 };
201 if (header.digest_size as usize) < required_size {
202 return error!(EOVERFLOW);
203 }
204 let output_header = uapi::fsverity_digest {
205 digest_algorithm: descriptor.hash_algorithm as u16,
206 digest_size: required_size as u16,
207 ..Default::default()
208 };
209 task.write_object(header_ref, &output_header)?;
210 task.write_memory(digest_addr, &fsverity_measurement(&descriptor)?)?;
211 Ok(SUCCESS)
212 }
213 _ => error!(ENODATA),
214 }
215 }
216
217 pub fn read_metadata(
219 task: &CurrentTask,
220 arg: UserAddress,
221 file: &FileObject,
222 ) -> Result<SyscallResult, Errno> {
223 let arg: fsverity_read_metadata_arg = task.read_object(arg.into())?;
224 match &*file.node().fsverity.lock() {
225 FsVerityState::FsVerity => {
226 match MetadataType::from_u64(arg.metadata_type).ok_or_else(|| errno!(EINVAL))? {
227 MetadataType::MerkleTree => {
228 error!(EOPNOTSUPP)
229 }
230 MetadataType::Descriptor => {
231 let descriptor = file.node().ops().get_fsverity_descriptor(12)?;
233 task.write_memory(
234 UserAddress::from(arg.buf_ptr).into(),
235 &descriptor.as_bytes()
236 [arg.offset as usize..(arg.offset + arg.length) as usize],
237 )?;
238 Ok(SUCCESS)
239 }
240 MetadataType::Signature => {
241 error!(EOPNOTSUPP)
242 }
243 }
244 }
245 _ => error!(ENODATA),
246 }
247 }
248}