apple_security_framework/
trust_settings.rs1use std::ptr;
4
5use core_foundation::{
6 array::{CFArray, CFArrayRef},
7 base::{CFIndex, TCFType},
8 dictionary::CFDictionary,
9 number::CFNumber,
10 string::CFString,
11};
12use core_foundation_sys::base::CFTypeRef;
13use security_framework_sys::{
14 base::{errSecNoTrustSettings, errSecSuccess},
15 trust_settings::*,
16};
17
18use crate::{
19 base::{Error, Result},
20 certificate::SecCertificate,
21 cvt,
22};
23
24#[derive(Debug, Copy, Clone, PartialEq, Eq)]
26#[repr(u32)]
27pub enum Domain {
28 User = kSecTrustSettingsDomainUser,
30 Admin = kSecTrustSettingsDomainAdmin,
32 System = kSecTrustSettingsDomainSystem,
34}
35
36impl From<Domain> for SecTrustSettingsDomain {
37 #[inline]
38 fn from(domain: Domain) -> SecTrustSettingsDomain {
39 match domain {
40 Domain::User => kSecTrustSettingsDomainUser,
41 Domain::Admin => kSecTrustSettingsDomainAdmin,
42 Domain::System => kSecTrustSettingsDomainSystem,
43 }
44 }
45}
46
47#[derive(Debug, Copy, Clone, PartialEq, Eq)]
49pub enum TrustSettingsForCertificate {
50 Invalid,
52
53 TrustRoot,
56
57 TrustAsRoot,
59
60 Deny,
62
63 Unspecified,
65}
66
67impl TrustSettingsForCertificate {
68 fn new(value: i64) -> Self {
70 if value < 0 || value > i64::from(u32::max_value()) {
71 return Self::Invalid;
72 }
73 match value as u32 {
74 kSecTrustSettingsResultTrustRoot => Self::TrustRoot,
75 kSecTrustSettingsResultTrustAsRoot => Self::TrustAsRoot,
76 kSecTrustSettingsResultDeny => Self::Deny,
77 kSecTrustSettingsResultUnspecified => Self::Unspecified,
78 _ => Self::Invalid,
79 }
80 }
81}
82
83pub struct TrustSettings {
85 domain: Domain,
86}
87
88impl TrustSettings {
89 #[inline(always)]
96 #[must_use]
97 pub fn new(domain: Domain) -> Self {
98 Self { domain }
99 }
100
101 pub fn iter(&self) -> Result<TrustSettingsIter> {
104 let array = unsafe {
105 let mut array_ptr: CFArrayRef = ptr::null_mut();
106
107 match SecTrustSettingsCopyCertificates(self.domain.into(), &mut array_ptr) {
111 errSecNoTrustSettings => CFArray::from_CFTypes(&[]),
112 errSecSuccess => CFArray::<SecCertificate>::wrap_under_create_rule(array_ptr),
113 err => return Err(Error::from_code(err)),
114 }
115 };
116
117 Ok(TrustSettingsIter { index: 0, array })
118 }
119
120 #[cfg(target_os = "macos")]
134 pub fn set_trust_settings_always(&self, cert: &SecCertificate) -> Result<()> {
135 let domain = self.domain;
136 let trust_settings: CFTypeRef = ptr::null_mut();
137 cvt(unsafe {
138 SecTrustSettingsSetTrustSettings(
139 cert.as_CFTypeRef() as *mut _,
140 domain.into(),
141 trust_settings,
142 )
143 })
144 }
145
146 pub fn tls_trust_settings_for_certificate(
159 &self,
160 cert: &SecCertificate,
161 ) -> Result<Option<TrustSettingsForCertificate>> {
162 let trust_settings = unsafe {
163 let mut array_ptr: CFArrayRef = ptr::null_mut();
164 let cert_ptr = cert.as_CFTypeRef() as *mut _;
165 cvt(SecTrustSettingsCopyTrustSettings(
166 cert_ptr,
167 self.domain.into(),
168 &mut array_ptr,
169 ))?;
170 CFArray::<CFDictionary>::wrap_under_create_rule(array_ptr)
171 };
172
173 for settings in trust_settings.iter() {
174 let is_not_ssl_policy = {
176 let policy_name_key = CFString::from_static_string("kSecTrustSettingsPolicyName");
177 let ssl_policy_name = CFString::from_static_string("sslServer");
178
179 let maybe_name: Option<CFString> = settings
180 .find(policy_name_key.as_CFTypeRef().cast())
181 .map(|name| unsafe { CFString::wrap_under_get_rule((*name).cast()) });
182
183 matches!(maybe_name, Some(ref name) if name != &ssl_policy_name)
184 };
185
186 if is_not_ssl_policy {
187 continue;
188 }
189
190 let maybe_trust_result = {
192 let settings_result_key = CFString::from_static_string("kSecTrustSettingsResult");
193 settings
194 .find(settings_result_key.as_CFTypeRef().cast())
195 .map(|num| unsafe { CFNumber::wrap_under_get_rule((*num).cast()) })
196 .and_then(|num| num.to_i64())
197 };
198
199 let trust_result = TrustSettingsForCertificate::new(
202 maybe_trust_result.unwrap_or_else(|| i64::from(kSecTrustSettingsResultTrustRoot)),
203 );
204
205 match trust_result {
206 TrustSettingsForCertificate::Unspecified | TrustSettingsForCertificate::Invalid => {
207 continue;
208 }
209 _ => return Ok(Some(trust_result)),
210 }
211 }
212
213 Ok(None)
217 }
218}
219
220pub struct TrustSettingsIter {
222 array: CFArray<SecCertificate>,
223 index: CFIndex,
224}
225
226impl Iterator for TrustSettingsIter {
227 type Item = SecCertificate;
228
229 #[inline]
230 fn next(&mut self) -> Option<Self::Item> {
231 if self.index >= self.array.len() {
232 None
233 } else {
234 let cert = self.array.get(self.index).unwrap();
235 self.index += 1;
236 Some(cert.clone())
237 }
238 }
239
240 #[inline]
241 fn size_hint(&self) -> (usize, Option<usize>) {
242 let left = (self.array.len() as usize).saturating_sub(self.index as usize);
243 (left, Some(left))
244 }
245}
246
247#[cfg(test)]
248mod test {
249 use super::*;
250 use crate::test::certificate;
251
252 fn list_for_domain(domain: Domain) {
253 println!("--- domain: {:?}", domain);
254 let ts = TrustSettings::new(domain);
255 let iterator = ts.iter().unwrap();
256
257 for (i, cert) in iterator.enumerate() {
258 println!("cert({:?}) = {:?}", i, cert);
259 println!(
260 " settings = {:?}",
261 ts.tls_trust_settings_for_certificate(&cert)
262 );
263 }
264 println!("---");
265 }
266
267 #[test]
268 fn list_for_user() {
269 list_for_domain(Domain::User);
270 }
271
272 #[test]
273 fn list_for_system() {
274 list_for_domain(Domain::System);
275 }
276
277 #[test]
278 fn list_for_admin() {
279 list_for_domain(Domain::Admin);
280 }
281
282 #[test]
283 fn test_system_certs_are_present() {
284 let system = TrustSettings::new(Domain::System).iter().unwrap().count();
285
286 assert!(system > 100);
288 }
289
290 #[test]
291 fn test_isrg_root_exists_and_is_trusted() {
292 let ts = TrustSettings::new(Domain::System);
293 assert_eq!(
294 ts.iter()
295 .unwrap()
296 .find(|cert| cert.subject_summary() == "ISRG Root X1")
297 .and_then(|cert| ts.tls_trust_settings_for_certificate(&cert).unwrap()),
298 None
299 );
300 }
305
306 #[test]
307 fn test_unknown_cert_is_not_trusted() {
308 let ts = TrustSettings::new(Domain::System);
309 let cert = certificate();
310 assert_eq!(
311 ts.tls_trust_settings_for_certificate(&cert)
312 .err()
313 .unwrap()
314 .message(),
315 Some("The specified item could not be found in the keychain.".into())
316 );
317 }
318}