]> git.proxmox.com Git - rustc.git/blob - vendor/security-framework/src/trust_settings.rs
New upstream version 1.73.0+dfsg1
[rustc.git] / vendor / security-framework / src / trust_settings.rs
1 //! Querying trust settings.
2
3 use core_foundation::array::{CFArray, CFArrayRef};
4 use core_foundation::base::{CFIndex, TCFType};
5 use core_foundation::dictionary::CFDictionary;
6 use core_foundation::number::CFNumber;
7 use core_foundation::string::CFString;
8
9 use core_foundation_sys::base::CFTypeRef;
10 use security_framework_sys::base::errSecNoTrustSettings;
11 use security_framework_sys::base::errSecSuccess;
12 use security_framework_sys::trust_settings::*;
13
14 use std::ptr;
15
16 use crate::base::Error;
17 use crate::base::Result;
18 use crate::certificate::SecCertificate;
19 use crate::cvt;
20
21 /// Which set of trust settings to query
22 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
23 #[repr(u32)]
24 pub enum Domain {
25 /// Per-user trust settings
26 User = kSecTrustSettingsDomainUser,
27 /// Locally administered, system-wide trust settings
28 Admin = kSecTrustSettingsDomainAdmin,
29 /// System trust settings
30 System = kSecTrustSettingsDomainSystem,
31 }
32
33 impl From<Domain> for SecTrustSettingsDomain {
34 #[inline]
35 fn from(domain: Domain) -> SecTrustSettingsDomain {
36 match domain {
37 Domain::User => kSecTrustSettingsDomainUser,
38 Domain::Admin => kSecTrustSettingsDomainAdmin,
39 Domain::System => kSecTrustSettingsDomainSystem,
40 }
41 }
42 }
43
44 /// Trust settings for a specific certificate in a specific domain
45 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
46 pub enum TrustSettingsForCertificate {
47 /// Not used
48 Invalid,
49
50 /// This is a root certificate and is trusted, either explicitly or
51 /// implicitly.
52 TrustRoot,
53
54 /// This is a non-root certificate but is explicitly trusted.
55 TrustAsRoot,
56
57 /// Cert is explicitly distrusted.
58 Deny,
59
60 /// Neither trusted nor distrusted.
61 Unspecified,
62 }
63
64 impl TrustSettingsForCertificate {
65 /// Create from `kSecTrustSettingsResult*` constant
66 fn new(value: i64) -> Self {
67 if value < 0 || value > i64::from(u32::max_value()) {
68 return Self::Invalid;
69 }
70 match value as u32 {
71 kSecTrustSettingsResultTrustRoot => Self::TrustRoot,
72 kSecTrustSettingsResultTrustAsRoot => Self::TrustAsRoot,
73 kSecTrustSettingsResultDeny => Self::Deny,
74 kSecTrustSettingsResultUnspecified => Self::Unspecified,
75 _ => Self::Invalid,
76 }
77 }
78 }
79
80 /// Allows access to the certificates and their trust settings in a given domain.
81 pub struct TrustSettings {
82 domain: Domain,
83 }
84
85 impl TrustSettings {
86 /// Create a new `TrustSettings` for the given domain.
87 ///
88 /// You can call `iter()` to discover the certificates with settings in this domain.
89 ///
90 /// Then you can call `tls_trust_settings_for_certificate()` with a given certificate
91 /// to learn what the aggregate trust setting for that certificate within this domain.
92 #[inline(always)]
93 #[must_use]
94 pub fn new(domain: Domain) -> Self {
95 Self { domain }
96 }
97
98 /// Create an iterator over the certificates with settings in this domain.
99 /// This produces an empty iterator if there are no such certificates.
100 pub fn iter(&self) -> Result<TrustSettingsIter> {
101 let array = unsafe {
102 let mut array_ptr: CFArrayRef = ptr::null_mut();
103
104 // SecTrustSettingsCopyCertificates returns errSecNoTrustSettings
105 // if no items have trust settings in the given domain. We map
106 // that to an empty TrustSettings iterator.
107 match SecTrustSettingsCopyCertificates(self.domain.into(), &mut array_ptr) {
108 errSecNoTrustSettings => CFArray::from_CFTypes(&[]),
109 errSecSuccess => CFArray::<SecCertificate>::wrap_under_create_rule(array_ptr),
110 err => return Err(Error::from_code(err)),
111 }
112 };
113
114 Ok(TrustSettingsIter { index: 0, array })
115 }
116
117 ///set trust settings to ""always trust this root certificate regardless of use.".
118 /// Sets the trust settings for the provided certificate to "always trust this root certificate
119 /// regardless of use."
120 ///
121 /// This method configures the trust settings for the specified certificate, indicating that it should
122 /// always be trusted as a TLS root certificate, regardless of its usage.
123 ///
124 /// If successful, the trust settings are updated for the certificate in the given domain. If the
125 /// certificate had no previous trust settings in the domain, new trust settings are created. If the
126 /// certificate had existing trust settings, they are replaced with the new settings.
127 ///
128 /// It is not possible to modify per-user trust settings when not running in a GUI
129 /// environment, if you try it will return error `2070: errSecInternalComponent`
130 #[cfg(target_os="macos")]
131 pub fn set_trust_settings_always(&self, cert: &SecCertificate) -> Result<()> {
132 let domain = self.domain;
133 let trust_settings: CFTypeRef = ptr::null_mut();
134 cvt(unsafe {
135 SecTrustSettingsSetTrustSettings(
136 cert.as_CFTypeRef() as *mut _,
137 domain.into(),
138 trust_settings,
139 )
140 })
141 }
142
143 /// Returns the aggregate trust setting for the given certificate.
144 ///
145 /// This tells you whether the certificate should be trusted as a TLS
146 /// root certificate.
147 ///
148 /// If the certificate has no trust settings in the given domain, the
149 /// `errSecItemNotFound` error is returned.
150 ///
151 /// If the certificate has no specific trust settings for TLS in the
152 /// given domain `None` is returned.
153 ///
154 /// Otherwise, the specific trust settings are aggregated and returned.
155 pub fn tls_trust_settings_for_certificate(&self, cert: &SecCertificate)
156 -> Result<Option<TrustSettingsForCertificate>> {
157 let trust_settings = unsafe {
158 let mut array_ptr: CFArrayRef = ptr::null_mut();
159 let cert_ptr = cert.as_CFTypeRef() as *mut _;
160 cvt(SecTrustSettingsCopyTrustSettings(cert_ptr,
161 self.domain.into(),
162 &mut array_ptr))?;
163 CFArray::<CFDictionary>::wrap_under_create_rule(array_ptr)
164 };
165
166 for settings in trust_settings.iter() {
167 // Reject settings for non-SSL policies
168 let is_not_ssl_policy = {
169 let policy_name_key = CFString::from_static_string("kSecTrustSettingsPolicyName");
170 let ssl_policy_name = CFString::from_static_string("sslServer");
171
172 let maybe_name: Option<CFString> = settings
173 .find(policy_name_key.as_CFTypeRef().cast())
174 .map(|name| unsafe { CFString::wrap_under_get_rule((*name).cast()) });
175
176 matches!(maybe_name, Some(ref name) if name != &ssl_policy_name)
177 };
178
179 if is_not_ssl_policy {
180 continue;
181 }
182
183 // Evaluate "effective trust settings" for this usage constraint.
184 let maybe_trust_result = {
185 let settings_result_key = CFString::from_static_string("kSecTrustSettingsResult");
186 settings
187 .find(settings_result_key.as_CFTypeRef().cast())
188 .map(|num| unsafe { CFNumber::wrap_under_get_rule((*num).cast()) })
189 .and_then(|num| num.to_i64())
190 };
191
192 // "Note that an empty Trust Settings array means "always trust this cert,
193 // with a resulting kSecTrustSettingsResult of kSecTrustSettingsResultTrustRoot"."
194 let trust_result = TrustSettingsForCertificate::new(maybe_trust_result
195 .unwrap_or_else(|| i64::from(kSecTrustSettingsResultTrustRoot)));
196
197 match trust_result {
198 TrustSettingsForCertificate::Unspecified |
199 TrustSettingsForCertificate::Invalid => { continue; },
200 _ => return Ok(Some(trust_result)),
201 }
202 }
203
204 // There were no more specific settings. This might mean the certificate
205 // is to be trusted anyway (since, eg, it's in system store), but leave
206 // the caller to make this decision.
207 Ok(None)
208 }
209 }
210
211 /// Iterator over certificates.
212 pub struct TrustSettingsIter {
213 array: CFArray<SecCertificate>,
214 index: CFIndex,
215 }
216
217 impl Iterator for TrustSettingsIter {
218 type Item = SecCertificate;
219
220 #[inline]
221 fn next(&mut self) -> Option<Self::Item> {
222 if self.index >= self.array.len() {
223 None
224 } else {
225 let cert = self.array.get(self.index).unwrap();
226 self.index += 1;
227 Some(cert.clone())
228 }
229 }
230
231 #[inline]
232 fn size_hint(&self) -> (usize, Option<usize>) {
233 let left = (self.array.len() as usize).saturating_sub(self.index as usize);
234 (left, Some(left))
235 }
236 }
237
238 #[cfg(test)]
239 mod test {
240 use super::*;
241 use crate::test::certificate;
242
243 fn list_for_domain(domain: Domain) {
244 println!("--- domain: {:?}", domain);
245 let ts = TrustSettings::new(domain);
246 let iterator = ts.iter().unwrap();
247
248 for (i, cert) in iterator.enumerate() {
249 println!("cert({:?}) = {:?}", i, cert);
250 println!(" settings = {:?}", ts.tls_trust_settings_for_certificate(&cert));
251 }
252 println!("---");
253 }
254
255 #[test]
256 fn list_for_user() {
257 list_for_domain(Domain::User);
258 }
259
260 #[test]
261 fn list_for_system() {
262 list_for_domain(Domain::System);
263 }
264
265 #[test]
266 fn list_for_admin() {
267 list_for_domain(Domain::Admin);
268 }
269
270 #[test]
271 fn test_system_certs_are_present() {
272 let system = TrustSettings::new(Domain::System).iter().unwrap().count();
273
274 // 168 at the time of writing
275 assert!(system > 100);
276 }
277
278 #[test]
279 fn test_isrg_root_exists_and_is_trusted() {
280 let ts = TrustSettings::new(Domain::System);
281 assert_eq!(
282 ts.iter()
283 .unwrap()
284 .find(|cert| cert.subject_summary() == "ISRG Root X1")
285 .and_then(|cert| ts.tls_trust_settings_for_certificate(&cert).unwrap()),
286 None
287 );
288 // ^ this is a case where None means "always trust", according to Apple docs:
289 //
290 // "Note that an empty Trust Settings array means "always trust this cert,
291 // with a resulting kSecTrustSettingsResult of kSecTrustSettingsResultTrustRoot"."
292 }
293
294 #[test]
295 fn test_unknown_cert_is_not_trusted() {
296 let ts = TrustSettings::new(Domain::System);
297 let cert = certificate();
298 assert_eq!(ts.tls_trust_settings_for_certificate(&cert)
299 .err()
300 .unwrap()
301 .message(),
302 Some("The specified item could not be found in the keychain.".into()));
303 }
304 }
305