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.
evaluate_with_error(&self) -> Result<(), CFError>129     pub fn evaluate_with_error(&self) -> Result<(), CFError> {
130         unsafe {
131             let mut error: CFErrorRef = ::std::ptr::null_mut();
132             if !SecTrustEvaluateWithError(self.0, &mut error) {
133                 assert!(!error.is_null());
134                 let error = CFError::wrap_under_create_rule(error);
135                 return Err(error);
136             }
137             Ok(())
138         }
139     }
140 
141     /// Returns the number of certificates in an evaluated certificate chain.
142     ///
143     /// Note: evaluate must first be called on the SecTrust.
144     #[inline(always)]
certificate_count(&self) -> CFIndex145     pub fn certificate_count(&self) -> CFIndex {
146         unsafe { SecTrustGetCertificateCount(self.0) }
147     }
148 
149     /// Returns a specific certificate from the certificate chain used to evaluate trust.
150     ///
151     /// Note: evaluate must first be called on the SecTrust.
certificate_at_index(&self, ix: CFIndex) -> Option<SecCertificate>152     pub fn certificate_at_index(&self, ix: CFIndex) -> Option<SecCertificate> {
153         unsafe {
154             if self.certificate_count() <= ix {
155                 None
156             } else {
157                 let certificate = SecTrustGetCertificateAtIndex(self.0, ix);
158                 Some(SecCertificate::wrap_under_get_rule(certificate as *mut _))
159             }
160         }
161     }
162 }
163 
164 #[cfg(test)]
165 mod test {
166     use crate::policy::SecPolicy;
167     use crate::secure_transport::SslProtocolSide;
168     use crate::test::certificate;
169     use crate::trust::SecTrust;
170 
171     #[test]
create_with_certificates()172     fn create_with_certificates() {
173         let cert = certificate();
174         let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io"));
175         let trust = SecTrust::create_with_certificates(&[cert], &[ssl_policy]).unwrap();
176         assert_eq!(trust.evaluate().unwrap().success(), false)
177     }
178 
179     #[test]
create_with_certificates_new()180     fn create_with_certificates_new() {
181         let cert = certificate();
182         let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io"));
183         let trust = SecTrust::create_with_certificates(&[cert], &[ssl_policy]).unwrap();
184         assert!(trust.evaluate_with_error().is_err());
185     }
186 
187     #[test]
certificate_count_and_at_index()188     fn certificate_count_and_at_index() {
189         let cert = certificate();
190         let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io"));
191         let trust = SecTrust::create_with_certificates(&[cert], &[ssl_policy]).unwrap();
192         trust.evaluate().unwrap();
193 
194         let count = trust.certificate_count();
195         assert_eq!(count, 1);
196 
197         let cert_bytes = trust.certificate_at_index(0).unwrap().to_der();
198         assert_eq!(cert_bytes, certificate().to_der());
199     }
200 
201     #[test]
certificate_count_and_at_index_new()202     fn certificate_count_and_at_index_new() {
203         let cert = certificate();
204         let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io"));
205         let trust = SecTrust::create_with_certificates(&[cert], &[ssl_policy]).unwrap();
206         assert!(trust.evaluate_with_error().is_err());
207 
208         let count = trust.certificate_count();
209         assert_eq!(count, 1);
210 
211         let cert_bytes = trust.certificate_at_index(0).unwrap().to_der();
212         assert_eq!(cert_bytes, certificate().to_der());
213     }
214 
215     #[test]
certificate_at_index_out_of_bounds()216     fn certificate_at_index_out_of_bounds() {
217         let cert = certificate();
218         let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io"));
219 
220         let trust = SecTrust::create_with_certificates(&[cert.clone()], &[ssl_policy.clone()]).unwrap();
221         trust.evaluate().unwrap();
222         assert!(trust.certificate_at_index(1).is_none());
223 
224         let trust = SecTrust::create_with_certificates(&[cert.clone()], &[ssl_policy.clone()]).unwrap();
225         assert!(trust.evaluate_with_error().is_err());
226         assert!(trust.certificate_at_index(1).is_none());
227     }
228 
229     #[test]
set_policy()230     fn set_policy() {
231         let cert = certificate();
232         let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io.bogus"));
233         let mut trust = SecTrust::create_with_certificates(&[cert], &[ssl_policy]).unwrap();
234         let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io"));
235         trust.set_policy(&ssl_policy).unwrap();
236         assert_eq!(trust.evaluate().unwrap().success(), false)
237     }
238 
239     #[test]
set_policy_new()240     fn set_policy_new() {
241         let cert = certificate();
242         let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io.bogus"));
243         let mut trust = SecTrust::create_with_certificates(&[cert], &[ssl_policy]).unwrap();
244         let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io"));
245         trust.set_policy(&ssl_policy).unwrap();
246         assert!(trust.evaluate_with_error().is_err());
247     }
248 }
249