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