Skip to main content

security_framework/
trust.rs

1//! Trust evaluation support.
2use core_foundation::array::CFArray;
3#[cfg(target_os = "macos")]
4use core_foundation::array::CFArrayRef;
5use core_foundation::base::TCFType;
6use core_foundation::data::CFData;
7use core_foundation::date::CFDate;
8use core_foundation::{declare_TCFType, impl_TCFType};
9use core_foundation_sys::base::{Boolean, CFIndex};
10
11use security_framework_sys::trust::*;
12use std::ptr;
13
14use crate::base::Result;
15use crate::certificate::SecCertificate;
16use crate::cvt;
17use crate::key::SecKey;
18use crate::policy::SecPolicy;
19use core_foundation::error::{CFError, CFErrorRef};
20
21/// The result of trust evaluation.
22#[derive(Debug, Copy, Clone, PartialEq, Eq)]
23pub struct TrustResult(SecTrustResultType);
24
25impl TrustResult {
26    /// Indicates a denial by the user, do not proceed.
27    pub const DENY: Self = Self(kSecTrustResultDeny);
28    /// Indicates a trust policy failure that the user cannot override.
29    pub const FATAL_TRUST_FAILURE: Self = Self(kSecTrustResultFatalTrustFailure);
30    /// An invalid setting or result.
31    pub const INVALID: Self = Self(kSecTrustResultInvalid);
32    /// An error not related to trust validation.
33    pub const OTHER_ERROR: Self = Self(kSecTrustResultOtherError);
34    /// You may proceed.
35    pub const PROCEED: Self = Self(kSecTrustResultProceed);
36    /// Indicates a trust policy failure that the user can override.
37    pub const RECOVERABLE_TRUST_FAILURE: Self = Self(kSecTrustResultRecoverableTrustFailure);
38    /// The certificate is implicitly trusted.
39    pub const UNSPECIFIED: Self = Self(kSecTrustResultUnspecified);
40}
41
42impl TrustResult {
43    /// Returns true if the result is "successful" - specifically `PROCEED` or `UNSPECIFIED`.
44    #[inline]
45    #[must_use]
46    pub fn success(self) -> bool {
47        matches!(self, Self::PROCEED | Self::UNSPECIFIED)
48    }
49}
50
51declare_TCFType! {
52    /// A type representing a trust evaluation for a certificate.
53    SecTrust, SecTrustRef
54}
55impl_TCFType!(SecTrust, SecTrustRef, SecTrustGetTypeID);
56
57unsafe impl Sync for SecTrust {}
58unsafe impl Send for SecTrust {}
59
60#[cfg(target_os = "macos")]
61bitflags::bitflags! {
62    /// The option flags used to configure the evaluation of a `SecTrust`.
63    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
64    pub struct TrustOptions: SecTrustOptionFlags {
65        /// Allow expired certificates (except for the root certificate).
66        const ALLOW_EXPIRED = kSecTrustOptionAllowExpired;
67        /// Allow CA certificates as leaf certificates.
68        const LEAF_IS_CA = kSecTrustOptionLeafIsCA;
69        /// Allow network downloads of CA certificates.
70        const FETCH_ISSUER_FROM_NET = kSecTrustOptionFetchIssuerFromNet;
71        /// Allow expired root certificates.
72        const ALLOW_EXPIRED_ROOT = kSecTrustOptionAllowExpiredRoot;
73        /// Require a positive revocation check for each certificate.
74        const REQUIRE_REVOCATION_PER_CERT =  kSecTrustOptionRequireRevPerCert;
75        /// Use TrustSettings instead of anchors.
76        const USE_TRUST_SETTINGS = kSecTrustOptionUseTrustSettings;
77        /// Treat properly self-signed certificates as anchors implicitly.
78        const IMPLICIT_ANCHORS =  kSecTrustOptionImplicitAnchors;
79    }
80}
81
82impl SecTrust {
83    /// Creates a `SecTrustRef` that is configured with a certificate chain, for validating
84    /// that chain against a collection of policies.
85    pub fn create_with_certificates(
86        certs: &[SecCertificate],
87        policies: &[SecPolicy],
88    ) -> Result<Self> {
89        let cert_array = CFArray::from_CFTypes(certs);
90        let policy_array = CFArray::from_CFTypes(policies);
91        let mut trust = ptr::null_mut();
92        unsafe {
93            cvt(SecTrustCreateWithCertificates(
94                cert_array.as_CFTypeRef(),
95                policy_array.as_CFTypeRef(),
96                &mut trust,
97            ))?;
98            Ok(Self(trust))
99        }
100    }
101
102    /// Sets the date and time against which the certificates in this trust object
103    /// are verified.
104    #[inline]
105    pub fn set_trust_verify_date(&mut self, date: &CFDate) -> Result<()> {
106        unsafe { cvt(SecTrustSetVerifyDate(self.0, date.as_concrete_TypeRef())) }
107    }
108
109    /// Sets additional anchor certificates used to validate trust.
110    pub fn set_anchor_certificates(&mut self, certs: &[SecCertificate]) -> Result<()> {
111        let certs = CFArray::from_CFTypes(certs);
112
113        unsafe {
114            cvt(SecTrustSetAnchorCertificates(
115                self.0,
116                certs.as_concrete_TypeRef(),
117            ))
118        }
119    }
120
121    /// Retrieves the anchor (root) certificates stored by macOS
122    #[cfg(target_os = "macos")]
123    pub fn copy_anchor_certificates() -> Result<Vec<SecCertificate>> {
124        let mut array: CFArrayRef = ptr::null();
125
126        unsafe {
127            cvt(SecTrustCopyAnchorCertificates(&mut array))?;
128        }
129
130        if array.is_null() {
131            return Ok(vec![]);
132        }
133
134        let array = unsafe { CFArray::<SecCertificate>::wrap_under_create_rule(array) };
135        Ok(array.into_iter().map(|c| c.clone()).collect())
136    }
137
138    /// If set to `true`, only the certificates specified by
139    /// `set_anchor_certificates` will be trusted, but not globally trusted
140    /// certificates.
141    #[inline]
142    pub fn set_trust_anchor_certificates_only(&mut self, only: bool) -> Result<()> {
143        unsafe { cvt(SecTrustSetAnchorCertificatesOnly(self.0, Boolean::from(only))) }
144    }
145
146    /// Sets the policy used to evaluate trust.
147    #[inline]
148    pub fn set_policy(&mut self, policy: &SecPolicy) -> Result<()> {
149        unsafe { cvt(SecTrustSetPolicies(self.0, policy.as_CFTypeRef())) }
150    }
151
152    /// Sets option flags for customizing evaluation of a trust object.
153    #[cfg(target_os = "macos")]
154    #[inline]
155    pub fn set_options(&mut self, options: TrustOptions) -> Result<()> {
156        unsafe { cvt(SecTrustSetOptions(self.0, options.bits())) }
157    }
158
159    /// Indicates whether this trust object is permitted to
160    /// fetch missing intermediate certificates from the network.
161    pub fn get_network_fetch_allowed(&mut self) -> Result<bool> {
162        let mut allowed = 0;
163
164        unsafe { cvt(SecTrustGetNetworkFetchAllowed(self.0, &mut allowed))? };
165
166        Ok(allowed != 0)
167    }
168
169    /// Specifies whether this trust object is permitted to
170    /// fetch missing intermediate certificates from the network.
171    #[inline]
172    pub fn set_network_fetch_allowed(&mut self, allowed: bool) -> Result<()> {
173        unsafe { cvt(SecTrustSetNetworkFetchAllowed(self.0, u8::from(allowed))) }
174    }
175
176    /// Attaches Online Certificate Status Protocol (OSCP) response data
177    /// to this trust object.
178    pub fn set_trust_ocsp_response<I: Iterator<Item = impl AsRef<[u8]>>>(
179        &mut self,
180        ocsp_response: I,
181    ) -> Result<()> {
182        let response: Vec<CFData> = ocsp_response
183            .into_iter()
184            .map(|bytes| CFData::from_buffer(bytes.as_ref()))
185            .collect();
186
187        let response = CFArray::from_CFTypes(&response);
188
189        unsafe { cvt(SecTrustSetOCSPResponse(self.0, response.as_CFTypeRef())) }
190    }
191
192    /// Attaches signed certificate timestamp data to this trust object.
193    pub fn set_signed_certificate_timestamps<I: Iterator<Item = impl AsRef<[u8]>>>(
194        &mut self,
195        scts: I,
196    ) -> Result<()> {
197        let scts: Vec<CFData> = scts
198            .into_iter()
199            .map(|bytes| CFData::from_buffer(bytes.as_ref()))
200            .collect();
201
202        let scts = CFArray::from_CFTypes(&scts);
203
204        unsafe { cvt(SecTrustSetSignedCertificateTimestamps(self.0, scts.as_concrete_TypeRef())) }
205    }
206
207    /// Returns the public key for a leaf certificate after it has been evaluated.
208    #[inline]
209    pub fn copy_public_key(&mut self) -> Result<SecKey> {
210        unsafe { Ok(SecKey::wrap_under_create_rule(SecTrustCopyPublicKey(self.0))) }
211    }
212
213    /// Evaluates trust.
214    #[deprecated(note = "use evaluate_with_error")]
215    pub fn evaluate(&self) -> Result<TrustResult> {
216        #[allow(deprecated)]
217        unsafe {
218            let mut result = kSecTrustResultInvalid;
219            cvt(SecTrustEvaluate(self.0, &mut result))?;
220            Ok(TrustResult(result))
221        }
222    }
223
224    /// Evaluates trust. Requires macOS 10.14 (checked at runtime) or iOS,
225    /// otherwise it just calls `evaluate()`.
226    pub fn evaluate_with_error(&self) -> Result<(), CFError> {
227        let mut error: CFErrorRef = ::std::ptr::null_mut();
228        let result = unsafe { SecTrustEvaluateWithError(self.0, &mut error) };
229        if !result {
230            assert!(!error.is_null());
231            // SAFETY: `SecTrustEvaluateWithError` expects us to release
232            // the error.
233            let error = unsafe { CFError::wrap_under_create_rule(error) };
234            return Err(error);
235        }
236        Ok(())
237    }
238
239    /// Gets the whole evaluated certificate chain.
240    ///
241    /// Note: evaluate must first be called on the `SecTrust`.
242    #[cfg(any(feature = "macos-12", not(target_os = "macos")))]
243    pub fn chain(&self) -> Vec<SecCertificate> {
244        let array = unsafe { SecTrustCopyCertificateChain(self.0) };
245
246        if array.is_null() {
247            return vec![];
248        }
249
250        let array = unsafe { CFArray::<SecCertificate>::wrap_under_create_rule(array) };
251        array.into_iter().map(|c| c.clone()).collect()
252    }
253
254    /// Returns the number of certificates in an evaluated certificate chain.
255    ///
256    /// Note: evaluate must first be called on the `SecTrust`.
257    #[inline(always)]
258    #[must_use]
259    #[deprecated(note = "deprecated by Apple, use chain(), enable macos-12 feature")]
260    // FIXME: this should have been usize. Don't expose CFIndex in Rust APIs.
261    pub fn certificate_count(&self) -> CFIndex {
262        unsafe { SecTrustGetCertificateCount(self.0) }
263    }
264
265    /// Returns a specific certificate from the certificate chain used to evaluate trust.
266    ///
267    /// Note: evaluate must first be called on the `SecTrust`.
268    #[deprecated(note = "deprecated by Apple, use chain(), enable macos-12 feature")]
269    #[must_use]
270    pub fn certificate_at_index(&self, ix: CFIndex) -> Option<SecCertificate> {
271        #[allow(deprecated)]
272        unsafe {
273            if self.certificate_count() <= ix {
274                None
275            } else {
276                let certificate = SecTrustGetCertificateAtIndex(self.0, ix);
277                Some(SecCertificate::wrap_under_get_rule(certificate.cast()))
278            }
279        }
280    }
281}
282
283#[cfg(test)]
284mod test {
285    use crate::policy::SecPolicy;
286    use crate::secure_transport::SslProtocolSide;
287    use crate::test::certificate;
288    use crate::trust::SecTrust;
289
290    #[test]
291    #[allow(deprecated)]
292    fn create_with_certificates() {
293        let cert = certificate();
294        let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io"));
295        let trust = SecTrust::create_with_certificates(&[cert], &[ssl_policy]).unwrap();
296        assert!(!trust.evaluate().unwrap().success());
297    }
298
299    #[test]
300    fn create_with_certificates_new() {
301        let cert = certificate();
302        let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io"));
303        let trust = SecTrust::create_with_certificates(&[cert], &[ssl_policy]).unwrap();
304        assert!(trust.evaluate_with_error().is_err());
305    }
306
307    #[test]
308    #[allow(deprecated)]
309    fn certificate_count_and_at_index() {
310        let cert = certificate();
311        let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io"));
312        let trust = SecTrust::create_with_certificates(&[cert], &[ssl_policy]).unwrap();
313        trust.evaluate().unwrap();
314
315        let count = trust.certificate_count();
316        // 1 (self-signed) or 2 (CA-signed, macOS builds chain)
317        assert!(count >= 1);
318
319        let cert_bytes = trust.certificate_at_index(0).unwrap().to_der();
320        assert_eq!(cert_bytes, certificate().to_der());
321    }
322
323    #[test]
324    #[allow(deprecated)]
325    fn certificate_count_and_at_index_new() {
326        let cert = certificate();
327        let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io"));
328        let trust = SecTrust::create_with_certificates(&[cert], &[ssl_policy]).unwrap();
329        assert!(trust.evaluate_with_error().is_err());
330
331        let count = trust.certificate_count();
332        // 1 (self-signed) or 2 (CA-signed, macOS builds chain)
333        assert!(count >= 1);
334
335        let cert_bytes = trust.certificate_at_index(0).unwrap().to_der();
336        assert_eq!(cert_bytes, certificate().to_der());
337    }
338
339    #[test]
340    #[allow(deprecated)]
341    fn certificate_at_index_out_of_bounds() {
342        let cert = certificate();
343        let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io"));
344
345        let trust = SecTrust::create_with_certificates(std::slice::from_ref(&cert), std::slice::from_ref(&ssl_policy)).unwrap();
346        trust.evaluate().unwrap();
347        assert!(trust.certificate_at_index(10).is_none());
348
349        let trust = SecTrust::create_with_certificates(&[cert], &[ssl_policy]).unwrap();
350        assert!(trust.evaluate_with_error().is_err());
351        assert!(trust.certificate_at_index(10).is_none());
352    }
353
354    #[test]
355    #[allow(deprecated)]
356    fn set_policy() {
357        let cert = certificate();
358        let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io.bogus"));
359        let mut trust = SecTrust::create_with_certificates(&[cert], &[ssl_policy]).unwrap();
360        let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io"));
361        trust.set_policy(&ssl_policy).unwrap();
362        assert!(!trust.evaluate().unwrap().success());
363    }
364
365    #[test]
366    fn set_policy_new() {
367        let cert = certificate();
368        let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io.bogus"));
369        let mut trust = SecTrust::create_with_certificates(&[cert], &[ssl_policy]).unwrap();
370        let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io"));
371        trust.set_policy(&ssl_policy).unwrap();
372        assert!(trust.evaluate_with_error().is_err());
373    }
374}