Skip to main content

security/
code.rs

1use base64::Engine;
2use bitflags::bitflags;
3use serde_json::Value;
4use std::collections::BTreeMap;
5use std::path::{Path, PathBuf};
6
7use crate::bridge;
8use crate::error::Result;
9
10bitflags! {
11    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
12    /// Mirrors `SecCSFlags`.
13    pub struct CodeSigningFlags: u32 {
14        /// Mirrors a `SecCSFlags` bit.
15        const CHECK_ALL_ARCHITECTURES = 1 << 0;
16        /// Mirrors a `SecCSFlags` bit.
17        const DO_NOT_VALIDATE_EXECUTABLE = 1 << 1;
18        /// Mirrors a `SecCSFlags` bit.
19        const DO_NOT_VALIDATE_RESOURCES = 1 << 2;
20        /// Mirrors a `SecCSFlags` bit.
21        const BASIC_VALIDATE_ONLY = Self::DO_NOT_VALIDATE_EXECUTABLE.bits() | Self::DO_NOT_VALIDATE_RESOURCES.bits();
22        /// Mirrors a `SecCSFlags` bit.
23        const CHECK_NESTED_CODE = 1 << 3;
24        /// Mirrors a `SecCSFlags` bit.
25        const STRICT_VALIDATE = 1 << 4;
26        /// Mirrors a `SecCSFlags` bit.
27        const FULL_REPORT = 1 << 5;
28        /// Mirrors a `SecCSFlags` bit.
29        const CHECK_GATEKEEPER_ARCHITECTURES = (1 << 6) | Self::CHECK_ALL_ARCHITECTURES.bits();
30        /// Mirrors a `SecCSFlags` bit.
31        const RESTRICT_SYMLINKS = 1 << 7;
32        /// Mirrors a `SecCSFlags` bit.
33        const RESTRICT_TO_APP_LIKE = 1 << 8;
34        /// Mirrors a `SecCSFlags` bit.
35        const RESTRICT_SIDEBAND_DATA = 1 << 9;
36        /// Mirrors a `SecCSFlags` bit.
37        const USE_SOFTWARE_SIGNING_CERT = 1 << 10;
38        /// Mirrors a `SecCSFlags` bit.
39        const VALIDATE_PEH = 1 << 11;
40        /// Mirrors a `SecCSFlags` bit.
41        const SINGLE_THREADED = 1 << 12;
42        /// Mirrors a `SecCSFlags` bit.
43        const ALLOW_NETWORK_ACCESS = 1 << 16;
44        /// Mirrors a `SecCSFlags` bit.
45        const FAST_EXECUTABLE_VALIDATION = 1 << 17;
46
47        /// Mirrors a `SecCSFlags` bit.
48        const SIGNING_INFORMATION = 1 << 1;
49        /// Mirrors a `SecCSFlags` bit.
50        const DYNAMIC_INFORMATION = 1 << 3;
51        /// Mirrors a `SecCSFlags` bit.
52        const USE_ALL_ARCHITECTURES = 1 << 0;
53    }
54}
55
56#[derive(Debug, Clone, PartialEq, Eq)]
57#[non_exhaustive]
58/// Mirrors values returned by `SecCodeCopySigningInformation`.
59pub enum SigningValue {
60    /// Mirrors a value decoded from `SecCodeCopySigningInformation`.
61    Boolean(bool),
62    /// Mirrors a value decoded from `SecCodeCopySigningInformation`.
63    Integer(i64),
64    /// Mirrors a value decoded from `SecCodeCopySigningInformation`.
65    String(String),
66    /// Mirrors a value decoded from `SecCodeCopySigningInformation`.
67    Data(Vec<u8>),
68    /// Mirrors a value decoded from `SecCodeCopySigningInformation`.
69    Array(Vec<Self>),
70    /// Mirrors a value decoded from `SecCodeCopySigningInformation`.
71    Dictionary(BTreeMap<String, Self>),
72    /// Mirrors a value decoded from `SecCodeCopySigningInformation`.
73    Unknown(String),
74}
75
76#[derive(Debug, Clone, PartialEq, Eq)]
77/// Mirrors structured data returned by `SecCodeCopySigningInformation`.
78pub struct SigningInformation {
79    /// Mirrors a field returned by `SecCodeCopySigningInformation`.
80    pub identifier: Option<String>,
81    /// Mirrors a field returned by `SecCodeCopySigningInformation`.
82    pub team_identifier: Option<String>,
83    /// Mirrors a field returned by `SecCodeCopySigningInformation`.
84    pub entitlements: BTreeMap<String, SigningValue>,
85    /// Mirrors a field returned by `SecCodeCopySigningInformation`.
86    pub sandboxed: bool,
87    /// Mirrors a field returned by `SecCodeCopySigningInformation`.
88    pub status: Option<u32>,
89}
90
91impl SigningInformation {
92    /// Wraps the corresponding Security.framework operation for `SigningInformation`.
93    pub const fn is_signed(&self) -> bool {
94        self.identifier.is_some()
95    }
96}
97
98#[derive(Debug)]
99/// Wraps `SecCodeRef`.
100pub struct Code {
101    handle: bridge::Handle,
102}
103
104impl Code {
105    /// Wraps the corresponding `SecCodeRef` operation.
106    pub fn type_id() -> usize {
107        unsafe { bridge::security_code_get_type_id() }
108    }
109
110    /// Wraps the corresponding `SecCodeRef` operation.
111    pub fn current() -> Result<Self> {
112        let mut status = 0;
113        let mut error = std::ptr::null_mut();
114        let raw = unsafe { bridge::security_code_copy_self(&mut status, &mut error) };
115        bridge::required_handle("security_code_copy_self", raw, status, error)
116            .map(|handle| Self { handle })
117    }
118
119    /// Wraps the corresponding `SecCodeRef` operation.
120    pub fn host(&self) -> Result<Self> {
121        let mut status = 0;
122        let mut error = std::ptr::null_mut();
123        let raw = unsafe {
124            bridge::security_code_copy_host(self.handle.as_ptr(), &mut status, &mut error)
125        };
126        bridge::required_handle("security_code_copy_host", raw, status, error)
127            .map(|handle| Self { handle })
128    }
129
130    /// Wraps the corresponding `SecCodeRef` operation.
131    pub fn guest_with_attributes(
132        host: Option<&Self>,
133        attributes: Option<&Value>,
134        flags: CodeSigningFlags,
135    ) -> Result<Self> {
136        let attributes = attributes.map(bridge::json_cstring).transpose()?;
137        let mut status = 0;
138        let mut error = std::ptr::null_mut();
139        let raw = unsafe {
140            bridge::security_code_copy_guest_with_attributes(
141                host.map_or(std::ptr::null_mut(), |value| value.handle.as_ptr()),
142                attributes
143                    .as_ref()
144                    .map_or(std::ptr::null(), |value| value.as_ptr()),
145                flags.bits(),
146                &mut status,
147                &mut error,
148            )
149        };
150        bridge::required_handle(
151            "security_code_copy_guest_with_attributes",
152            raw,
153            status,
154            error,
155        )
156        .map(|handle| Self { handle })
157    }
158
159    /// Wraps the corresponding `SecCodeRef` operation.
160    pub fn static_code(&self) -> Result<StaticCode> {
161        let mut status = 0;
162        let mut error = std::ptr::null_mut();
163        let raw = unsafe {
164            bridge::security_code_copy_static_code(self.handle.as_ptr(), &mut status, &mut error)
165        };
166        bridge::required_handle("security_code_copy_static_code", raw, status, error)
167            .map(StaticCode::from_handle)
168    }
169
170    /// Wraps the corresponding `SecCodeRef` operation.
171    pub fn signing_information(&self) -> Result<SigningInformation> {
172        self.static_code()?.signing_information()
173    }
174
175    /// Wraps the corresponding `SecCodeRef` operation.
176    pub fn task(&self) -> Result<Task> {
177        Task::current()
178    }
179}
180
181#[derive(Debug)]
182/// Wraps `SecRequirementRef`.
183pub struct Requirement {
184    handle: bridge::Handle,
185}
186
187impl Requirement {
188    fn from_handle(handle: bridge::Handle) -> Self {
189        Self { handle }
190    }
191
192    fn handle(&self) -> &bridge::Handle {
193        &self.handle
194    }
195
196    /// Wraps the corresponding `SecRequirementRef` operation.
197    pub fn type_id() -> usize {
198        unsafe { bridge::security_requirement_get_type_id() }
199    }
200
201    /// Wraps the corresponding `SecRequirementRef` operation.
202    pub fn from_data(data: &[u8]) -> Result<Self> {
203        let mut status = 0;
204        let mut error = std::ptr::null_mut();
205        let raw = unsafe {
206            bridge::security_requirement_create_with_data(
207                data.as_ptr().cast(),
208                bridge::len_to_isize(data.len())?,
209                &mut status,
210                &mut error,
211            )
212        };
213        bridge::required_handle("security_requirement_create_with_data", raw, status, error)
214            .map(Self::from_handle)
215    }
216
217    /// Wraps the corresponding `SecRequirementRef` operation.
218    pub fn from_string(text: &str) -> Result<Self> {
219        let text = bridge::cstring(text)?;
220        let mut status = 0;
221        let mut error = std::ptr::null_mut();
222        let raw = unsafe {
223            bridge::security_requirement_create_with_string(text.as_ptr(), &mut status, &mut error)
224        };
225        bridge::required_handle(
226            "security_requirement_create_with_string",
227            raw,
228            status,
229            error,
230        )
231        .map(Self::from_handle)
232    }
233
234    /// Wraps the corresponding `SecRequirementRef` operation.
235    pub fn from_string_with_errors(text: &str) -> Result<Self> {
236        let text = bridge::cstring(text)?;
237        let mut status = 0;
238        let mut error = std::ptr::null_mut();
239        let raw = unsafe {
240            bridge::security_requirement_create_with_string_and_errors(
241                text.as_ptr(),
242                &mut status,
243                &mut error,
244            )
245        };
246        bridge::required_handle(
247            "security_requirement_create_with_string_and_errors",
248            raw,
249            status,
250            error,
251        )
252        .map(Self::from_handle)
253    }
254
255    /// Wraps the corresponding `SecRequirementRef` operation.
256    pub fn data(&self) -> Result<Vec<u8>> {
257        let mut status = 0;
258        let mut error = std::ptr::null_mut();
259        let raw = unsafe {
260            bridge::security_requirement_copy_data(self.handle.as_ptr(), &mut status, &mut error)
261        };
262        bridge::required_data("security_requirement_copy_data", raw, status, error)
263    }
264
265    /// Wraps the corresponding `SecRequirementRef` operation.
266    pub fn string(&self) -> Result<String> {
267        let mut status = 0;
268        let mut error = std::ptr::null_mut();
269        let raw = unsafe {
270            bridge::security_requirement_copy_string(self.handle.as_ptr(), &mut status, &mut error)
271        };
272        bridge::required_string("security_requirement_copy_string", raw, status, error)
273    }
274}
275
276#[derive(Debug)]
277/// Wraps `SecStaticCodeRef`.
278pub struct StaticCode {
279    handle: bridge::Handle,
280}
281
282impl StaticCode {
283    fn from_handle(handle: bridge::Handle) -> Self {
284        Self { handle }
285    }
286
287    /// Wraps the corresponding `SecStaticCodeRef` operation.
288    pub fn type_id() -> usize {
289        unsafe { bridge::security_static_code_get_type_id() }
290    }
291
292    /// Wraps the corresponding `SecStaticCodeRef` operation.
293    pub fn from_path(path: impl AsRef<Path>) -> Result<Self> {
294        let path = bridge::cstring(&path.as_ref().to_string_lossy())?;
295        let mut status = 0;
296        let mut error = std::ptr::null_mut();
297        let raw = unsafe {
298            bridge::security_static_code_create_with_path(path.as_ptr(), &mut status, &mut error)
299        };
300        bridge::required_handle("security_static_code_create_with_path", raw, status, error)
301            .map(Self::from_handle)
302    }
303
304    /// Wraps the corresponding `SecStaticCodeRef` operation.
305    pub fn from_path_with_attributes(path: impl AsRef<Path>, attributes: &Value) -> Result<Self> {
306        let path = bridge::cstring(&path.as_ref().to_string_lossy())?;
307        let attributes = bridge::json_cstring(attributes)?;
308        let mut status = 0;
309        let mut error = std::ptr::null_mut();
310        let raw = unsafe {
311            bridge::security_static_code_create_with_path_and_attributes(
312                path.as_ptr(),
313                attributes.as_ptr(),
314                &mut status,
315                &mut error,
316            )
317        };
318        bridge::required_handle(
319            "security_static_code_create_with_path_and_attributes",
320            raw,
321            status,
322            error,
323        )
324        .map(Self::from_handle)
325    }
326
327    /// Wraps the corresponding `SecStaticCodeRef` operation.
328    pub fn check_validity(&self) -> Result<()> {
329        let mut error = std::ptr::null_mut();
330        let status = unsafe {
331            bridge::security_static_code_check_validity(self.handle.as_ptr(), &mut error)
332        };
333        bridge::status_result("security_static_code_check_validity", status, error)
334    }
335
336    /// Wraps the corresponding `SecStaticCodeRef` operation.
337    pub fn check_validity_with_errors(
338        &self,
339        flags: CodeSigningFlags,
340        requirement: Option<&Requirement>,
341    ) -> Result<()> {
342        let mut error = std::ptr::null_mut();
343        let status = unsafe {
344            bridge::security_static_code_check_validity_with_errors(
345                self.handle.as_ptr(),
346                flags.bits(),
347                requirement.map_or(std::ptr::null_mut(), |value| value.handle().as_ptr()),
348                &mut error,
349            )
350        };
351        bridge::status_result(
352            "security_static_code_check_validity_with_errors",
353            status,
354            error,
355        )
356    }
357
358    /// Wraps the corresponding `SecStaticCodeRef` operation.
359    pub fn check_static_validity(
360        &self,
361        flags: CodeSigningFlags,
362        requirement: Option<&Requirement>,
363    ) -> Result<()> {
364        let mut error = std::ptr::null_mut();
365        let status = unsafe {
366            bridge::security_static_code_check_static_validity(
367                self.handle.as_ptr(),
368                flags.bits(),
369                requirement.map_or(std::ptr::null_mut(), |value| value.handle().as_ptr()),
370                &mut error,
371            )
372        };
373        bridge::status_result("security_static_code_check_static_validity", status, error)
374    }
375
376    /// Wraps the corresponding `SecStaticCodeRef` operation.
377    pub fn check_static_validity_with_errors(
378        &self,
379        flags: CodeSigningFlags,
380        requirement: Option<&Requirement>,
381    ) -> Result<()> {
382        let mut error = std::ptr::null_mut();
383        let status = unsafe {
384            bridge::security_static_code_check_static_validity_with_errors(
385                self.handle.as_ptr(),
386                flags.bits(),
387                requirement.map_or(std::ptr::null_mut(), |value| value.handle().as_ptr()),
388                &mut error,
389            )
390        };
391        bridge::status_result(
392            "security_static_code_check_static_validity_with_errors",
393            status,
394            error,
395        )
396    }
397
398    /// Wraps the corresponding `SecStaticCodeRef` operation.
399    pub fn path(&self) -> Result<PathBuf> {
400        let mut status = 0;
401        let mut error = std::ptr::null_mut();
402        let raw = unsafe {
403            bridge::security_static_code_copy_path(self.handle.as_ptr(), &mut status, &mut error)
404        };
405        bridge::required_string("security_static_code_copy_path", raw, status, error)
406            .map(PathBuf::from)
407    }
408
409    /// Wraps the corresponding `SecStaticCodeRef` operation.
410    pub fn designated_requirement(&self) -> Result<String> {
411        let mut status = 0;
412        let mut error = std::ptr::null_mut();
413        let raw = unsafe {
414            bridge::security_static_code_copy_designated_requirement(
415                self.handle.as_ptr(),
416                &mut status,
417                &mut error,
418            )
419        };
420        bridge::required_string(
421            "security_static_code_copy_designated_requirement",
422            raw,
423            status,
424            error,
425        )
426    }
427
428    /// Wraps the corresponding `SecStaticCodeRef` operation.
429    pub fn signing_information(&self) -> Result<SigningInformation> {
430        let mut status = 0;
431        let mut error = std::ptr::null_mut();
432        let raw = unsafe {
433            bridge::security_static_code_copy_signing_information(
434                self.handle.as_ptr(),
435                &mut status,
436                &mut error,
437            )
438        };
439        let value: Value = bridge::required_json(
440            "security_static_code_copy_signing_information",
441            raw,
442            status,
443            error,
444        )?;
445        Ok(SigningInformation {
446            identifier: find_string(&value, &["identifier", "Identifier"]),
447            team_identifier: find_string(&value, &["teamid", "TeamIdentifier", "teamIdentifier"]),
448            entitlements: find_object(
449                &value,
450                &[
451                    "entitlements-dict",
452                    "EntitlementsDict",
453                    "entitlements",
454                    "Entitlements",
455                ],
456            )
457            .map(json_object_to_map)
458            .unwrap_or_default(),
459            sandboxed: matches!(
460                find_object(
461                    &value,
462                    &[
463                        "entitlements-dict",
464                        "EntitlementsDict",
465                        "entitlements",
466                        "Entitlements",
467                    ],
468                )
469                .and_then(|value| value.get("com.apple.security.app-sandbox")),
470                Some(Value::Bool(true))
471            ),
472            status: find_integer(&value, &["status", "Status"])
473                .and_then(|value| u32::try_from(value).ok()),
474        })
475    }
476
477    /// Wraps the corresponding `SecStaticCodeRef` operation.
478    pub fn validate_file_resource(
479        &self,
480        relative_path: &str,
481        data: &[u8],
482        flags: CodeSigningFlags,
483    ) -> Result<()> {
484        let relative_path = bridge::cstring(relative_path)?;
485        let mut error = std::ptr::null_mut();
486        let status = unsafe {
487            bridge::security_static_code_validate_file_resource(
488                self.handle.as_ptr(),
489                relative_path.as_ptr(),
490                data.as_ptr().cast(),
491                bridge::len_to_isize(data.len())?,
492                flags.bits(),
493                &mut error,
494            )
495        };
496        bridge::status_result("security_static_code_validate_file_resource", status, error)
497    }
498
499    /// Wraps the corresponding `SecStaticCodeRef` operation.
500    pub fn map_memory(&self, flags: CodeSigningFlags) -> Result<()> {
501        let mut error = std::ptr::null_mut();
502        let status = unsafe {
503            bridge::security_static_code_map_memory(self.handle.as_ptr(), flags.bits(), &mut error)
504        };
505        bridge::status_result("security_static_code_map_memory", status, error)
506    }
507}
508
509#[derive(Debug)]
510/// Wraps `SecTaskRef`.
511pub struct Task {
512    handle: bridge::Handle,
513}
514
515impl Task {
516    /// Wraps the corresponding `SecTaskRef` operation.
517    pub fn type_id() -> usize {
518        unsafe { bridge::security_task_get_type_id() }
519    }
520
521    /// Wraps the corresponding `SecTaskRef` operation.
522    pub fn current() -> Result<Self> {
523        let mut status = 0;
524        let mut error = std::ptr::null_mut();
525        let raw = unsafe { bridge::security_task_create_from_self(&mut status, &mut error) };
526        bridge::required_handle("security_task_create_from_self", raw, status, error)
527            .map(|handle| Self { handle })
528    }
529
530    /// Wraps the corresponding `SecTaskRef` operation.
531    pub fn current_with_audit_token() -> Result<Self> {
532        let mut status = 0;
533        let mut error = std::ptr::null_mut();
534        let raw = unsafe {
535            bridge::security_task_create_from_current_audit_token(&mut status, &mut error)
536        };
537        bridge::required_handle(
538            "security_task_create_from_current_audit_token",
539            raw,
540            status,
541            error,
542        )
543        .map(|handle| Self { handle })
544    }
545
546    /// Wraps the corresponding `SecTaskRef` operation.
547    pub fn signing_identifier(&self) -> Result<Option<String>> {
548        let mut status = 0;
549        let mut error = std::ptr::null_mut();
550        let raw = unsafe {
551            bridge::security_task_copy_signing_identifier(
552                self.handle.as_ptr(),
553                &mut status,
554                &mut error,
555            )
556        };
557        if raw.is_null() && status == 0 {
558            Ok(None)
559        } else {
560            bridge::required_string("security_task_copy_signing_identifier", raw, status, error)
561                .map(Some)
562        }
563    }
564
565    /// Wraps the corresponding `SecTaskRef` operation.
566    pub fn entitlement(&self, entitlement: &str) -> Result<Option<Value>> {
567        let entitlement = bridge::cstring(entitlement)?;
568        let mut status = 0;
569        let mut error = std::ptr::null_mut();
570        let raw = unsafe {
571            bridge::security_task_copy_value_for_entitlement(
572                self.handle.as_ptr(),
573                entitlement.as_ptr(),
574                &mut status,
575                &mut error,
576            )
577        };
578        if raw.is_null() && status == 0 {
579            return Ok(None);
580        }
581        let value: Value = bridge::required_json(
582            "security_task_copy_value_for_entitlement",
583            raw,
584            status,
585            error,
586        )?;
587        Ok(Some(value))
588    }
589
590    /// Wraps the corresponding `SecTaskRef` operation.
591    pub fn entitlements(&self, entitlements: &[&str]) -> Result<Value> {
592        let entitlements = bridge::json_cstring(&entitlements)?;
593        let mut status = 0;
594        let mut error = std::ptr::null_mut();
595        let raw = unsafe {
596            bridge::security_task_copy_values_for_entitlements(
597                self.handle.as_ptr(),
598                entitlements.as_ptr(),
599                &mut status,
600                &mut error,
601            )
602        };
603        bridge::required_json(
604            "security_task_copy_values_for_entitlements",
605            raw,
606            status,
607            error,
608        )
609    }
610}
611
612fn find_object<'a>(value: &'a Value, keys: &[&str]) -> Option<&'a serde_json::Map<String, Value>> {
613    keys.iter()
614        .find_map(|key| value.get(*key))
615        .and_then(Value::as_object)
616}
617
618fn find_string(value: &Value, keys: &[&str]) -> Option<String> {
619    keys.iter()
620        .find_map(|key| value.get(*key))
621        .and_then(Value::as_str)
622        .map(ToOwned::to_owned)
623}
624
625fn find_integer(value: &Value, keys: &[&str]) -> Option<i64> {
626    keys.iter()
627        .find_map(|key| value.get(*key))
628        .and_then(Value::as_i64)
629}
630
631fn json_object_to_map(object: &serde_json::Map<String, Value>) -> BTreeMap<String, SigningValue> {
632    object
633        .iter()
634        .map(|(key, value)| (key.clone(), signing_value(value)))
635        .collect()
636}
637
638fn signing_value(value: &Value) -> SigningValue {
639    match value {
640        Value::Bool(value) => SigningValue::Boolean(*value),
641        Value::Number(value) => value.as_i64().map_or_else(
642            || SigningValue::Unknown(value.to_string()),
643            SigningValue::Integer,
644        ),
645        Value::String(value) => SigningValue::String(value.clone()),
646        Value::Array(values) => {
647            if let Some(data) = data_from_wrapped_json(value) {
648                SigningValue::Data(data)
649            } else {
650                SigningValue::Array(values.iter().map(signing_value).collect())
651            }
652        }
653        Value::Object(object) => {
654            if let Some(data) = data_from_wrapped_json(value) {
655                SigningValue::Data(data)
656            } else {
657                SigningValue::Dictionary(json_object_to_map(object))
658            }
659        }
660        Value::Null => SigningValue::Unknown("null".to_owned()),
661    }
662}
663
664fn data_from_wrapped_json(value: &Value) -> Option<Vec<u8>> {
665    let object = value.as_object()?;
666    if object.get("_type")?.as_str()? != "data" {
667        return None;
668    }
669    let base64 = object.get("base64")?.as_str()?;
670    base64::engine::general_purpose::STANDARD
671        .decode(base64)
672        .ok()
673}