1use 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 _ => {
91 error!(EINVAL)
92 }
93 }
94}
95
96#[derive(Debug, FromPrimitive)]
97enum MetadataType {
98 MerkleTree = 1,
99 Descriptor = 2,
100 Signature = 3,
101}
102
103#[derive(Debug)]
105pub enum FsVerityState {
106 None,
108 Building,
114 FsVerity,
119}
120
121impl FsVerityState {
122 pub fn check_writable(&self) -> Result<(), Errno> {
124 match self {
125 FsVerityState::None => Ok(()),
126 FsVerityState::Building => error!(ETXTBSY),
127 FsVerityState::FsVerity => error!(EACCES),
128 }
129 }
130}
131
132pub mod ioctl {
133
134 use crate::mm::{MemoryAccessor, MemoryAccessorExt};
135 use crate::task::CurrentTask;
136 use crate::vfs::fsverity::{
137 FsVerityState, HashAlgorithm, MetadataType, fsverity_descriptor_from_enable_arg,
138 fsverity_enable_arg, fsverity_measurement, fsverity_read_metadata_arg,
139 };
140 use crate::vfs::{FileObject, FileWriteGuardMode};
141 use num_traits::FromPrimitive;
142 use starnix_sync::{FileOpsCore, LockEqualOrBefore, Locked};
143 use starnix_syscalls::{SUCCESS, SyscallResult};
144 use starnix_uapi::errors::Errno;
145 use starnix_uapi::user_address::{UserAddress, UserRef};
146 use starnix_uapi::{errno, error, uapi};
147 use zerocopy::IntoBytes;
148
149 pub fn enable<L>(
151 locked: &mut Locked<L>,
152 task: &CurrentTask,
153 arg: UserAddress,
154 file: &FileObject,
155 ) -> Result<SyscallResult, Errno>
156 where
157 L: LockEqualOrBefore<FileOpsCore>,
158 {
159 if file.can_write() {
160 return error!(ETXTBSY);
161 }
162 let block_size = file.name.entry.node.fs().statfs(locked, task)?.f_bsize as u32;
163 let args: fsverity_enable_arg = task.read_object(arg.into())?;
165 let mut descriptor = fsverity_descriptor_from_enable_arg(task, block_size, &args)?;
166 descriptor.data_size = file.node().fetch_and_refresh_info(locked, task)?.size as u64;
167 let _mapping = file.name.clone().into_mapping(Some(FileWriteGuardMode::ExecMapping))?;
169 let mut fsverity = file.node().fsverity.lock();
170 match *fsverity {
171 FsVerityState::Building => error!(EBUSY),
172 FsVerityState::FsVerity => error!(EEXIST),
173 FsVerityState::None => {
174 *fsverity = FsVerityState::Building;
175 file.node().ops().enable_fsverity(&descriptor)?;
176 *fsverity = FsVerityState::FsVerity;
177 Ok(SUCCESS)
178 }
179 }
180 }
181
182 pub fn measure<L>(
184 locked: &mut Locked<L>,
185 task: &CurrentTask,
186 arg: UserAddress,
187 file: &FileObject,
188 ) -> Result<SyscallResult, Errno>
189 where
190 L: LockEqualOrBefore<FileOpsCore>,
191 {
192 let header_ref = UserRef::<uapi::fsverity_digest>::new(arg);
193 let digest_addr = header_ref.next()?.addr();
194 let header = task.read_object(header_ref.clone())?;
195 match &*file.node().fsverity.lock() {
196 FsVerityState::FsVerity => {
197 let block_size = file.name.entry.node.fs().statfs(locked, task)?.f_bsize as u32;
198 if !block_size.is_power_of_two() {
199 return error!(EINVAL);
200 }
201 let descriptor =
202 file.node().ops().get_fsverity_descriptor(block_size.ilog2() as u8)?;
203 let digest_algorithm = HashAlgorithm::from_u8(descriptor.hash_algorithm)
204 .ok_or_else(|| errno!(EINVAL))?;
205 let required_size = match digest_algorithm {
206 HashAlgorithm::SHA256 => 32,
207 HashAlgorithm::SHA512 => 64,
208 };
209 if (header.digest_size as usize) < required_size {
210 return error!(EOVERFLOW);
211 }
212 let output_header = uapi::fsverity_digest {
213 digest_algorithm: descriptor.hash_algorithm as u16,
214 digest_size: required_size as u16,
215 ..Default::default()
216 };
217 task.write_object(header_ref, &output_header)?;
218 task.write_memory(digest_addr, &fsverity_measurement(&descriptor)?)?;
219 Ok(SUCCESS)
220 }
221 _ => error!(ENODATA),
222 }
223 }
224
225 pub fn read_metadata(
227 task: &CurrentTask,
228 arg: UserAddress,
229 file: &FileObject,
230 ) -> Result<SyscallResult, Errno> {
231 let arg: fsverity_read_metadata_arg = task.read_object(arg.into())?;
232 match &*file.node().fsverity.lock() {
233 FsVerityState::FsVerity => {
234 match MetadataType::from_u64(arg.metadata_type).ok_or_else(|| errno!(EINVAL))? {
235 MetadataType::MerkleTree => {
236 error!(EOPNOTSUPP)
237 }
238 MetadataType::Descriptor => {
239 let descriptor = file.node().ops().get_fsverity_descriptor(12)?;
241 task.write_memory(
242 UserAddress::from(arg.buf_ptr).into(),
243 &descriptor.as_bytes()
244 [arg.offset as usize..(arg.offset + arg.length) as usize],
245 )?;
246 Ok(SUCCESS)
247 }
248 MetadataType::Signature => {
249 error!(EOPNOTSUPP)
250 }
251 }
252 }
253 _ => error!(ENODATA),
254 }
255 }
256}