use {
crate::{
certificate::{
AppleCertificate, CertificateAuthorityExtension, CertificateProfile,
CodeSigningCertificateExtension,
},
code_requirement::{CodeRequirementExpression, CodeRequirementMatchExpression},
error::AppleCodesignError,
},
once_cell::sync::Lazy,
std::{convert::TryFrom, ops::Deref},
x509_certificate::CapturedX509Certificate,
};
static POLICY_MAC_DEVELOPER_ID: Lazy<CodeRequirementExpression<'static>> = Lazy::new(|| {
CodeRequirementExpression::And(
Box::new(CodeRequirementExpression::And(
Box::new(CodeRequirementExpression::AnchorAppleGeneric),
Box::new(CodeRequirementExpression::CertificateGeneric(
1,
CertificateAuthorityExtension::DeveloperId.as_oid(),
CodeRequirementMatchExpression::Exists,
)),
)),
Box::new(CodeRequirementExpression::Or(
Box::new(CodeRequirementExpression::CertificateGeneric(
0,
CodeSigningCertificateExtension::DeveloperIdInstaller.as_oid(),
CodeRequirementMatchExpression::Exists,
)),
Box::new(CodeRequirementExpression::CertificateGeneric(
0,
CodeSigningCertificateExtension::DeveloperIdApplication.as_oid(),
CodeRequirementMatchExpression::Exists,
)),
)),
)
});
static POLICY_NOTARIZED_EXECUTABLE: Lazy<CodeRequirementExpression<'static>> = Lazy::new(|| {
CodeRequirementExpression::And(
Box::new(CodeRequirementExpression::And(
Box::new(CodeRequirementExpression::And(
Box::new(CodeRequirementExpression::AnchorAppleGeneric),
Box::new(CodeRequirementExpression::CertificateGeneric(
1,
CertificateAuthorityExtension::DeveloperId.as_oid(),
CodeRequirementMatchExpression::Exists,
)),
)),
Box::new(CodeRequirementExpression::CertificateGeneric(
0,
CodeSigningCertificateExtension::DeveloperIdApplication.as_oid(),
CodeRequirementMatchExpression::Exists,
)),
)),
Box::new(CodeRequirementExpression::Notarized),
)
});
static POLICY_NOTARIZED_INSTALLER: Lazy<CodeRequirementExpression<'static>> = Lazy::new(|| {
CodeRequirementExpression::And(
Box::new(CodeRequirementExpression::And(
Box::new(CodeRequirementExpression::And(
Box::new(CodeRequirementExpression::AnchorAppleGeneric),
Box::new(CodeRequirementExpression::CertificateGeneric(
1,
CertificateAuthorityExtension::DeveloperId.as_oid(),
CodeRequirementMatchExpression::Exists,
)),
)),
Box::new(CodeRequirementExpression::Or(
Box::new(CodeRequirementExpression::CertificateGeneric(
0,
CodeSigningCertificateExtension::DeveloperIdInstaller.as_oid(),
CodeRequirementMatchExpression::Exists,
)),
Box::new(CodeRequirementExpression::CertificateGeneric(
0,
CodeSigningCertificateExtension::DeveloperIdApplication.as_oid(),
CodeRequirementMatchExpression::Exists,
)),
)),
)),
Box::new(CodeRequirementExpression::Notarized),
)
});
pub enum ExecutionPolicy {
DeveloperIdSigned,
DeveloperIdNotarizedExecutable,
DeveloperIdNotarizedInstaller,
}
impl Deref for ExecutionPolicy {
type Target = CodeRequirementExpression<'static>;
fn deref(&self) -> &Self::Target {
match self {
Self::DeveloperIdSigned => POLICY_MAC_DEVELOPER_ID.deref(),
Self::DeveloperIdNotarizedExecutable => POLICY_NOTARIZED_EXECUTABLE.deref(),
Self::DeveloperIdNotarizedInstaller => POLICY_NOTARIZED_INSTALLER.deref(),
}
}
}
impl TryFrom<&str> for ExecutionPolicy {
type Error = AppleCodesignError;
fn try_from(s: &str) -> Result<Self, Self::Error> {
match s {
"developer-id-signed" => Ok(Self::DeveloperIdSigned),
"developer-id-notarized-executable" => Ok(Self::DeveloperIdNotarizedExecutable),
"developer-id-notarized-installer" => Ok(Self::DeveloperIdNotarizedInstaller),
_ => Err(AppleCodesignError::UnknownPolicy(s.to_string())),
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn get_policies() {
ExecutionPolicy::DeveloperIdSigned.to_bytes().unwrap();
ExecutionPolicy::DeveloperIdNotarizedExecutable
.to_bytes()
.unwrap();
ExecutionPolicy::DeveloperIdNotarizedInstaller
.to_bytes()
.unwrap();
}
}
pub fn derive_designated_requirements(
cert: &CapturedX509Certificate,
identifier: Option<String>,
) -> Result<Option<CodeRequirementExpression<'static>>, AppleCodesignError> {
let profile = if let Some(profile) = cert.apple_guess_profile() {
profile
} else {
return Ok(None);
};
match profile {
CertificateProfile::AppleDevelopment | CertificateProfile::AppleDistribution => {
let cn = cert.subject_common_name().ok_or_else(|| {
AppleCodesignError::PolicyFormulationError(format!(
"(deriving for {:?}) certificate common name not available",
profile
))
})?;
let expr = CodeRequirementExpression::And(
Box::new(CodeRequirementExpression::AnchorAppleGeneric),
Box::new(CodeRequirementExpression::And(
Box::new(CodeRequirementExpression::CertificateField(
0,
"subject.CN".to_string().into(),
CodeRequirementMatchExpression::Equal(cn.into()),
)),
Box::new(CodeRequirementExpression::CertificateGeneric(
1,
CertificateAuthorityExtension::AppleWorldwideDeveloperRelations.as_oid(),
CodeRequirementMatchExpression::Exists,
)),
)),
);
Ok(Some(if let Some(identifier) = identifier {
CodeRequirementExpression::And(
Box::new(CodeRequirementExpression::Identifier(identifier.into())),
Box::new(expr),
)
} else {
expr
}))
}
CertificateProfile::DeveloperIdApplication => {
let team_id = cert.apple_team_id().ok_or_else(|| {
AppleCodesignError::PolicyFormulationError(format!(
"(deriving for {:?}) could not find team identifier in signing certificate",
profile
))
})?;
let expr = CodeRequirementExpression::And(
Box::new(CodeRequirementExpression::AnchorAppleGeneric),
Box::new(CodeRequirementExpression::And(
Box::new(CodeRequirementExpression::CertificateGeneric(
1,
CertificateAuthorityExtension::DeveloperId.as_oid(),
CodeRequirementMatchExpression::Exists,
)),
Box::new(CodeRequirementExpression::And(
Box::new(CodeRequirementExpression::CertificateGeneric(
0,
CodeSigningCertificateExtension::DeveloperIdApplication.as_oid(),
CodeRequirementMatchExpression::Exists,
)),
Box::new(CodeRequirementExpression::CertificateField(
0,
"subject.OU".to_string().into(),
CodeRequirementMatchExpression::Equal(team_id.into()),
)),
)),
)),
);
Ok(Some(if let Some(identifier) = identifier {
CodeRequirementExpression::And(
Box::new(CodeRequirementExpression::Identifier(identifier.into())),
Box::new(expr),
)
} else {
expr
}))
}
CertificateProfile::MacInstallerDistribution | CertificateProfile::DeveloperIdInstaller => {
Err(AppleCodesignError::PolicyFormulationError(format!(
"(deriving for {:?}) we do not know how to handle this policy",
profile
)))
}
}
}