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