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