Skip to main content

verifyos_cli/parsers/
macho_parser.rs

1use apple_codesign::MachFile;
2use std::path::Path;
3
4#[derive(Debug, thiserror::Error)]
5pub enum MachOError {
6    #[error("Failed to parse Mach-O file: {0}")]
7    ParseError(#[from] Box<apple_codesign::AppleCodesignError>),
8    #[error("No code signature found")]
9    NoSignature,
10    #[error("Entitlements differ across Mach-O architecture slices")]
11    MismatchedEntitlements,
12}
13
14pub struct MachOExecutable {
15    pub entitlements: Option<String>,
16}
17
18impl MachOExecutable {
19    pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self, MachOError> {
20        let file_data =
21            std::fs::read(path).map_err(|e| Box::new(apple_codesign::AppleCodesignError::Io(e)))?;
22        let mach_file = MachFile::parse(&file_data).map_err(Box::new)?;
23
24        let mut first_entitlements: Option<Option<String>> = None;
25
26        for macho in mach_file.iter_macho() {
27            let mut current_ent = None;
28            if let Some(sig) = macho.code_signature().map_err(Box::new)? {
29                if let Some(ent) = sig.entitlements().map_err(Box::new)? {
30                    current_ent = Some(ent.as_str().to_string());
31                }
32            }
33
34            match &first_entitlements {
35                None => {
36                    first_entitlements = Some(current_ent);
37                }
38                Some(first) => {
39                    if first != &current_ent {
40                        return Err(MachOError::MismatchedEntitlements);
41                    }
42                }
43            }
44        }
45
46        Ok(Self {
47            entitlements: first_entitlements.flatten(),
48        })
49    }
50}