use apple_cf::CFError;
use crate::certificate::Certificate;
use crate::error::{Result, SecurityError};
use crate::ffi;
use crate::private::{cf_array, cf_error_description, cf_string, sec_error_message, OwnedCf};
pub struct Policy {
raw: ffi::SecPolicyRef,
}
impl Policy {
pub fn basic_x509() -> Result<Self> {
let raw = unsafe { ffi::SecPolicyCreateBasicX509() };
if raw.is_null() {
return Err(SecurityError::CoreFoundation(CFError::new(
"SecPolicyCreateBasicX509",
)));
}
Ok(Self { raw })
}
pub fn ssl(server: bool, hostname: Option<&str>) -> Result<Self> {
let hostname = hostname.map(cf_string).transpose()?;
let raw = unsafe {
ffi::SecPolicyCreateSSL(
u8::from(server),
hostname
.as_ref()
.map_or(std::ptr::null(), OwnedCf::as_string),
)
};
if raw.is_null() {
return Err(SecurityError::CoreFoundation(CFError::new(
"SecPolicyCreateSSL",
)));
}
Ok(Self { raw })
}
#[must_use]
pub const fn as_raw(&self) -> ffi::SecPolicyRef {
self.raw
}
}
impl Drop for Policy {
fn drop(&mut self) {
if !self.raw.is_null() {
unsafe { ffi::CFRelease(self.raw.cast()) };
}
}
}
impl core::fmt::Debug for Policy {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Policy").field("raw", &self.raw).finish()
}
}
pub struct Trust {
raw: ffi::SecTrustRef,
}
impl Trust {
pub fn new(certificate: &Certificate, policies: &[Policy]) -> Result<Self> {
if policies.is_empty() {
return Err(SecurityError::InvalidArgument(
"at least one trust policy is required".to_owned(),
));
}
let policy_input = policy_input(policies)?;
let mut trust = std::ptr::null();
let status = unsafe {
ffi::SecTrustCreateWithCertificates(
certificate.as_raw().cast(),
policy_input.raw,
&mut trust,
)
};
if status != ffi::status::SUCCESS {
return Err(SecurityError::from_status(
"SecTrustCreateWithCertificates",
status,
sec_error_message(status),
));
}
if trust.is_null() {
return Err(SecurityError::CoreFoundation(CFError::new(
"SecTrustCreateWithCertificates",
)));
}
Ok(Self { raw: trust })
}
pub fn set_policies(&mut self, policies: &[Policy]) -> Result<()> {
if policies.is_empty() {
return Err(SecurityError::InvalidArgument(
"at least one trust policy is required".to_owned(),
));
}
let policy_input = policy_input(policies)?;
let status = unsafe { ffi::SecTrustSetPolicies(self.raw, policy_input.raw) };
if status == ffi::status::SUCCESS {
Ok(())
} else {
Err(SecurityError::from_status(
"SecTrustSetPolicies",
status,
sec_error_message(status),
))
}
}
pub fn evaluate(&self) -> Result<()> {
let mut error = std::ptr::null();
let ok = unsafe { ffi::SecTrustEvaluateWithError(self.raw, &mut error) };
if ok != 0 {
Ok(())
} else {
Err(SecurityError::TrustEvaluationFailed(cf_error_description(
error,
)))
}
}
#[must_use]
pub const fn as_raw(&self) -> ffi::SecTrustRef {
self.raw
}
}
impl Drop for Trust {
fn drop(&mut self) {
if !self.raw.is_null() {
unsafe { ffi::CFRelease(self.raw.cast()) };
}
}
}
impl core::fmt::Debug for Trust {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Trust").field("raw", &self.raw).finish()
}
}
struct PolicyInput {
raw: ffi::CFTypeRef,
_array: Option<OwnedCf>,
}
fn policy_input(policies: &[Policy]) -> Result<PolicyInput> {
if policies.len() == 1 {
return Ok(PolicyInput {
raw: policies[0].as_raw().cast(),
_array: None,
});
}
let policy_refs = policies
.iter()
.map(|policy| policy.as_raw().cast())
.collect::<Vec<_>>();
let array = cf_array(&policy_refs)?;
Ok(PolicyInput {
raw: array.as_ptr(),
_array: Some(array),
})
}