1 //! Trust evaluation support.
2 
3 use core_foundation::array::CFArray;
4 use core_foundation::base::TCFType;
5 use core_foundation_sys::base::{Boolean, CFIndex};
6 
7 use security_framework_sys::trust::*;
8 use std::ptr;
9 
10 use crate::base::Result;
11 use crate::certificate::SecCertificate;
12 use crate::cvt;
13 use crate::key::SecKey;
14 use crate::policy::SecPolicy;
15 use core_foundation::error::{CFError, CFErrorRef};
16 
17 /// The result of trust evaluation.
18 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
19 pub struct TrustResult(SecTrustResultType);
20 
21 impl TrustResult {
22     /// An invalid setting or result.
23     pub const INVALID: Self = Self(kSecTrustResultInvalid);
24 
25     /// You may proceed.
26     pub const PROCEED: Self = Self(kSecTrustResultProceed);
27 
28     /// Indicates a denial by the user, do not proceed.
29     pub const DENY: Self = Self(kSecTrustResultDeny);
30 
31     /// The certificate is implicitly trusted.
32     pub const UNSPECIFIED: Self = Self(kSecTrustResultUnspecified);
33 
34     /// Indicates a trust policy failure that the user can override.
35     pub const RECOVERABLE_TRUST_FAILURE: Self =
36         Self(kSecTrustResultRecoverableTrustFailure);
37 
38     /// Indicates a trust policy failure that the user cannot override.
39     pub const FATAL_TRUST_FAILURE: Self = Self(kSecTrustResultFatalTrustFailure);
40 
41     /// An error not related to trust validation.
42     pub const OTHER_ERROR: Self = Self(kSecTrustResultOtherError);
43 }
44 
45 impl TrustResult {
46     /// Returns true if the result is "successful" - specifically `PROCEED` or `UNSPECIFIED`.
47     #[inline]
success(self) -> bool48     pub fn success(self) -> bool {
49         match self {
50             Self::PROCEED | Self::UNSPECIFIED => true,
51             _ => false,
52         }
53     }
54 }
55 
56 declare_TCFType! {
57     /// A type representing a trust evaluation for a certificate.
58     SecTrust, SecTrustRef
59 }
60 impl_TCFType!(SecTrust, SecTrustRef, SecTrustGetTypeID);
61 
62 unsafe impl Sync for SecTrust {}
63 unsafe impl Send for SecTrust {}
64 
65 impl SecTrust {
66     /// Creates a SecTrustRef that is configured with a certificate chain, for validating
67     /// that chain against a collection of policies.
create_with_certificates( certs: &[SecCertificate], policies: &[SecPolicy], ) -> Result<Self>68     pub fn create_with_certificates(
69         certs: &[SecCertificate],
70         policies: &[SecPolicy],
71     ) -> Result<Self> {
72         let cert_array = CFArray::from_CFTypes(&certs);
73         let policy_array = CFArray::from_CFTypes(&policies);
74         let mut trust = ptr::null_mut();
75         unsafe {
76             cvt(SecTrustCreateWithCertificates(
77                 cert_array.as_CFTypeRef(),
78                 policy_array.as_CFTypeRef(),
79                 &mut trust,
80             ))?;
81             Ok(Self(trust))
82         }
83     }
84 
85     /// Sets additional anchor certificates used to validate trust.
set_anchor_certificates(&mut self, certs: &[SecCertificate]) -> Result<()>86     pub fn set_anchor_certificates(&mut self, certs: &[SecCertificate]) -> Result<()> {
87         let certs = CFArray::from_CFTypes(&certs);
88 
89         unsafe {
90             cvt(SecTrustSetAnchorCertificates(
91                 self.0,
92                 certs.as_concrete_TypeRef(),
93             ))
94         }
95     }
96 
97     /// If set to `true`, only the certificates specified by
98     /// `set_anchor_certificates` will be trusted, but not globally trusted
99     /// certificates.
set_trust_anchor_certificates_only(&mut self, only: bool) -> Result<()>100     pub fn set_trust_anchor_certificates_only(&mut self, only: bool) -> Result<()> {
101         unsafe { cvt(SecTrustSetAnchorCertificatesOnly(self.0, only as Boolean)) }
102     }
103 
104     /// Sets the policy used to evaluate trust.
set_policy(&mut self, policy: &SecPolicy) -> Result<()>105     pub fn set_policy(&mut self, policy: &SecPolicy) -> Result<()> {
106         unsafe { cvt(SecTrustSetPolicies(self.0, policy.as_CFTypeRef())) }
107     }
108 
109     /// Returns the public key for a leaf certificate after it has been evaluated.
110     #[inline]
copy_public_key(&mut self) -> Result<SecKey>111     pub fn copy_public_key(&mut self) -> Result<SecKey> {
112         unsafe {
113             Ok(SecKey::wrap_under_create_rule(SecTrustCopyPublicKey(
114                 self.0,
115             )))
116         }
117     }
118 
119     /// Evaluates trust.
evaluate(&self) -> Result<TrustResult>120     pub fn evaluate(&self) -> Result<TrustResult> {
121         unsafe {
122             let mut result = kSecTrustResultInvalid;
123             cvt(SecTrustEvaluate(self.0, &mut result))?;
124             Ok(TrustResult(result))
125         }
126     }
127 
128     /// Evaluates trust. Requires macOS 10.14, otherwise it just calls `evaluate()`
evaluate_with_error(&self) -> Result<(), CFError>129     pub fn evaluate_with_error(&self) -> Result<(), CFError> {
130         #[cfg(feature = "OSX_10_14")]
131         unsafe {
132             let mut error: CFErrorRef = ::std::ptr::null_mut();
133             if !SecTrustEvaluateWithError(self.0, &mut error) {
134                 assert!(!error.is_null());
135                 let error = CFError::wrap_under_create_rule(error);
136                 return Err(error);
137             }
138             Ok(())
139         }
140         #[cfg(not(feature = "OSX_10_14"))]
141         {
142             use security_framework_sys::base::errSecNotTrusted;
143             use security_framework_sys::base::errSecTrustSettingDeny;
144 
145             let code = match self.evaluate() {
146                 Ok(res) if res.success() => return Ok(()),
147                 Ok(TrustResult::DENY) => errSecTrustSettingDeny,
148                 Ok(_) => errSecNotTrusted,
149                 Err(err) => err.code(),
150             };
151             Err(cferror_from_osstatus(code))
152         }
153     }
154 
155     /// Returns the number of certificates in an evaluated certificate chain.
156     ///
157     /// Note: evaluate must first be called on the SecTrust.
158     #[inline(always)]
certificate_count(&self) -> CFIndex159     pub fn certificate_count(&self) -> CFIndex {
160         unsafe { SecTrustGetCertificateCount(self.0) }
161     }
162 
163     /// Returns a specific certificate from the certificate chain used to evaluate trust.
164     ///
165     /// Note: evaluate must first be called on the SecTrust.
certificate_at_index(&self, ix: CFIndex) -> Option<SecCertificate>166     pub fn certificate_at_index(&self, ix: CFIndex) -> Option<SecCertificate> {
167         unsafe {
168             if self.certificate_count() <= ix {
169                 None
170             } else {
171                 let certificate = SecTrustGetCertificateAtIndex(self.0, ix);
172                 Some(SecCertificate::wrap_under_get_rule(certificate as *mut _))
173             }
174         }
175     }
176 }
177 
178 #[cfg(not(feature = "OSX_10_14"))]
179 extern "C" {
CFErrorCreate(allocator: core_foundation_sys::base::CFAllocatorRef, domain: core_foundation_sys::string::CFStringRef, code: CFIndex, userInfo: core_foundation_sys::dictionary::CFDictionaryRef) -> CFErrorRef180     fn CFErrorCreate(allocator: core_foundation_sys::base::CFAllocatorRef, domain: core_foundation_sys::string::CFStringRef, code: CFIndex, userInfo: core_foundation_sys::dictionary::CFDictionaryRef) -> CFErrorRef;
181 }
182 
183 #[cfg(not(feature = "OSX_10_14"))]
cferror_from_osstatus(code: core_foundation_sys::base::OSStatus) -> CFError184 fn cferror_from_osstatus(code: core_foundation_sys::base::OSStatus) -> CFError {
185     unsafe {
186         let error = CFErrorCreate(ptr::null_mut(), core_foundation_sys::error::kCFErrorDomainOSStatus, code as _, ptr::null_mut());
187         assert!(!error.is_null());
188         CFError::wrap_under_create_rule(error)
189     }
190 }
191 
192 #[cfg(test)]
193 mod test {
194     use crate::policy::SecPolicy;
195     use crate::secure_transport::SslProtocolSide;
196     use crate::test::certificate;
197     use crate::trust::SecTrust;
198 
199     #[test]
create_with_certificates()200     fn create_with_certificates() {
201         let cert = certificate();
202         let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io"));
203         let trust = SecTrust::create_with_certificates(&[cert], &[ssl_policy]).unwrap();
204         assert_eq!(trust.evaluate().unwrap().success(), false)
205     }
206 
207     #[test]
create_with_certificates_new()208     fn create_with_certificates_new() {
209         let cert = certificate();
210         let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io"));
211         let trust = SecTrust::create_with_certificates(&[cert], &[ssl_policy]).unwrap();
212         assert!(trust.evaluate_with_error().is_err());
213     }
214 
215     #[test]
certificate_count_and_at_index()216     fn certificate_count_and_at_index() {
217         let cert = certificate();
218         let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io"));
219         let trust = SecTrust::create_with_certificates(&[cert], &[ssl_policy]).unwrap();
220         trust.evaluate().unwrap();
221 
222         let count = trust.certificate_count();
223         assert_eq!(count, 1);
224 
225         let cert_bytes = trust.certificate_at_index(0).unwrap().to_der();
226         assert_eq!(cert_bytes, certificate().to_der());
227     }
228 
229     #[test]
certificate_count_and_at_index_new()230     fn certificate_count_and_at_index_new() {
231         let cert = certificate();
232         let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io"));
233         let trust = SecTrust::create_with_certificates(&[cert], &[ssl_policy]).unwrap();
234         assert!(trust.evaluate_with_error().is_err());
235 
236         let count = trust.certificate_count();
237         assert_eq!(count, 1);
238 
239         let cert_bytes = trust.certificate_at_index(0).unwrap().to_der();
240         assert_eq!(cert_bytes, certificate().to_der());
241     }
242 
243     #[test]
certificate_at_index_out_of_bounds()244     fn certificate_at_index_out_of_bounds() {
245         let cert = certificate();
246         let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io"));
247 
248         let trust = SecTrust::create_with_certificates(&[cert.clone()], &[ssl_policy.clone()]).unwrap();
249         trust.evaluate().unwrap();
250         assert!(trust.certificate_at_index(1).is_none());
251 
252         let trust = SecTrust::create_with_certificates(&[cert.clone()], &[ssl_policy.clone()]).unwrap();
253         assert!(trust.evaluate_with_error().is_err());
254         assert!(trust.certificate_at_index(1).is_none());
255     }
256 
257     #[test]
set_policy()258     fn set_policy() {
259         let cert = certificate();
260         let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io.bogus"));
261         let mut trust = SecTrust::create_with_certificates(&[cert], &[ssl_policy]).unwrap();
262         let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io"));
263         trust.set_policy(&ssl_policy).unwrap();
264         assert_eq!(trust.evaluate().unwrap().success(), false)
265     }
266 
267     #[test]
set_policy_new()268     fn set_policy_new() {
269         let cert = certificate();
270         let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io.bogus"));
271         let mut trust = SecTrust::create_with_certificates(&[cert], &[ssl_policy]).unwrap();
272         let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io"));
273         trust.set_policy(&ssl_policy).unwrap();
274         assert!(trust.evaluate_with_error().is_err());
275     }
276 }
277