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