1 //! Querying trust settings.
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
;
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
::*;
16 use crate::base
::Error
;
17 use crate::base
::Result
;
18 use crate::certificate
::SecCertificate
;
21 /// Which set of trust settings to query
22 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
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
,
33 impl From
<Domain
> for SecTrustSettingsDomain
{
35 fn from(domain
: Domain
) -> SecTrustSettingsDomain
{
37 Domain
::User
=> kSecTrustSettingsDomainUser
,
38 Domain
::Admin
=> kSecTrustSettingsDomainAdmin
,
39 Domain
::System
=> kSecTrustSettingsDomainSystem
,
44 /// Trust settings for a specific certificate in a specific domain
45 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
46 pub enum TrustSettingsForCertificate
{
50 /// This is a root certificate and is trusted, either explicitly or
54 /// This is a non-root certificate but is explicitly trusted.
57 /// Cert is explicitly distrusted.
60 /// Neither trusted nor distrusted.
64 impl TrustSettingsForCertificate
{
65 /// Create from `kSecTrustSettingsResult*` constant
66 fn new(value
: i64) -> Self {
67 if value
< 0 || value
> i64::from(u32::max_value()) {
71 kSecTrustSettingsResultTrustRoot
=> Self::TrustRoot
,
72 kSecTrustSettingsResultTrustAsRoot
=> Self::TrustAsRoot
,
73 kSecTrustSettingsResultDeny
=> Self::Deny
,
74 kSecTrustSettingsResultUnspecified
=> Self::Unspecified
,
80 /// Allows access to the certificates and their trust settings in a given domain.
81 pub struct TrustSettings
{
86 /// Create a new `TrustSettings` for the given domain.
88 /// You can call `iter()` to discover the certificates with settings in this domain.
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.
94 pub fn new(domain
: Domain
) -> Self {
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
> {
102 let mut array_ptr
: CFArrayRef
= ptr
::null_mut();
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
)),
114 Ok(TrustSettingsIter { index: 0, array }
)
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."
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.
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.
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();
135 SecTrustSettingsSetTrustSettings(
136 cert
.as_CFTypeRef() as *mut _
,
143 /// Returns the aggregate trust setting for the given certificate.
145 /// This tells you whether the certificate should be trusted as a TLS
146 /// root certificate.
148 /// If the certificate has no trust settings in the given domain, the
149 /// `errSecItemNotFound` error is returned.
151 /// If the certificate has no specific trust settings for TLS in the
152 /// given domain `None` is returned.
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
,
163 CFArray
::<CFDictionary
>::wrap_under_create_rule(array_ptr
)
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");
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()) }
);
176 matches
!(maybe_name
, Some(ref name
) if name
!= &ssl_policy_name
)
179 if is_not_ssl_policy
{
183 // Evaluate "effective trust settings" for this usage constraint.
184 let maybe_trust_result
= {
185 let settings_result_key
= CFString
::from_static_string("kSecTrustSettingsResult");
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())
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
)));
198 TrustSettingsForCertificate
::Unspecified
|
199 TrustSettingsForCertificate
::Invalid
=> { continue; }
,
200 _
=> return Ok(Some(trust_result
)),
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.
211 /// Iterator over certificates.
212 pub struct TrustSettingsIter
{
213 array
: CFArray
<SecCertificate
>,
217 impl Iterator
for TrustSettingsIter
{
218 type Item
= SecCertificate
;
221 fn next(&mut self) -> Option
<Self::Item
> {
222 if self.index
>= self.array
.len() {
225 let cert
= self.array
.get(self.index
).unwrap();
232 fn size_hint(&self) -> (usize, Option
<usize>) {
233 let left
= (self.array
.len() as usize).saturating_sub(self.index
as usize);
241 use crate::test
::certificate
;
243 fn list_for_domain(domain
: Domain
) {
244 println
!("--- domain: {:?}", domain
);
245 let ts
= TrustSettings
::new(domain
);
246 let iterator
= ts
.iter().unwrap();
248 for (i
, cert
) in iterator
.enumerate() {
249 println
!("cert({:?}) = {:?}", i
, cert
);
250 println
!(" settings = {:?}", ts
.tls_trust_settings_for_certificate(&cert
));
257 list_for_domain(Domain
::User
);
261 fn list_for_system() {
262 list_for_domain(Domain
::System
);
266 fn list_for_admin() {
267 list_for_domain(Domain
::Admin
);
271 fn test_system_certs_are_present() {
272 let system
= TrustSettings
::new(Domain
::System
).iter().unwrap().count();
274 // 168 at the time of writing
275 assert
!(system
> 100);
279 fn test_isrg_root_exists_and_is_trusted() {
280 let ts
= TrustSettings
::new(Domain
::System
);
284 .find(|cert
| cert
.subject_summary() == "ISRG Root X1")
285 .and_then(|cert
| ts
.tls_trust_settings_for_certificate(&cert
).unwrap()),
288 // ^ this is a case where None means "always trust", according to Apple docs:
290 // "Note that an empty Trust Settings array means "always trust this cert,
291 // with a resulting kSecTrustSettingsResult of kSecTrustSettingsResultTrustRoot"."
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
)
302 Some("The specified item could not be found in the keychain.".into()));