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