diff --git a/.github/actions/fetch-vectors/action.yml b/.github/actions/fetch-vectors/action.yml index ecfd62d52391..76cd53ba9ad9 100644 --- a/.github/actions/fetch-vectors/action.yml +++ b/.github/actions/fetch-vectors/action.yml @@ -16,5 +16,5 @@ runs: with: repository: "C2SP/x509-limbo" path: "x509-limbo" - # Latest commit on the x509-limbo main branch, as of Dec 31, 2024. - ref: "7a34e9bbe787eec72876bc39ad261f6ec036b9f1" # x509-limbo-ref + # Latest commit on the x509-limbo main branch, as of Jan 08, 2025. + ref: "eec7dc996cccf326db9cd4dfe1878d33f9efa0c8" # x509-limbo-ref diff --git a/src/rust/cryptography-x509-verification/src/lib.rs b/src/rust/cryptography-x509-verification/src/lib.rs index 75ec6ce005da..cdcbe5ccf50d 100644 --- a/src/rust/cryptography-x509-verification/src/lib.rs +++ b/src/rust/cryptography-x509-verification/src/lib.rs @@ -23,13 +23,12 @@ use cryptography_x509::{ name::GeneralName, oid::{NAME_CONSTRAINTS_OID, SUBJECT_ALTERNATIVE_NAME_OID}, }; -use types::{RFC822Constraint, RFC822Name}; +use types::{DNSPattern, RFC822Constraint, RFC822Name}; use crate::certificate::cert_is_self_issued; use crate::ops::{CryptoOps, VerificationCertificate}; use crate::policy::Policy; use crate::trust_store::Store; -use crate::types::DNSName; use crate::types::{DNSConstraint, IPAddress, IPConstraint}; use crate::ApplyNameConstraintStatus::{Applied, Skipped}; @@ -155,45 +154,53 @@ impl<'a, 'chain> NameChain<'a, 'chain> { budget.name_constraint_check()?; match (constraint, san) { - (GeneralName::DNSName(pattern), GeneralName::DNSName(name)) => { - match (DNSConstraint::new(pattern.0), DNSName::new(name.0)) { - (Some(pattern), Some(name)) => Ok(Applied(pattern.matches(&name))), + (GeneralName::DNSName(constraint), GeneralName::DNSName(name)) => { + // NOTE: A DNS SAN can be a wildcard pattern instead of a normal DNS name. + // These are handled by matching unconditionally on the inner name, + // since a NC of `foo.com` will match both `foo.com` and any arbitrarily deep + // subdomain of `foo.com`, where a wildcard SAN like `*.foo.com` will only + // match exactly one subdomain of `foo.com`. Therefore, the NC's matching + // set is a strict superset of any possible wildcard SAN pattern. + match (DNSConstraint::new(constraint.0), DNSPattern::new(name.0)) { + (Some(constraint), Some(name)) => { + Ok(Applied(constraint.matches(name.inner_name()))) + } (_, None) => Err(ValidationError::new(ValidationErrorKind::Other(format!( "unsatisfiable DNS name constraint: malformed SAN {}", name.0 )))), (None, _) => Err(ValidationError::new(ValidationErrorKind::Other(format!( "malformed DNS name constraint: {}", - pattern.0 + constraint.0 )))), } } - (GeneralName::IPAddress(pattern), GeneralName::IPAddress(name)) => { + (GeneralName::IPAddress(constraint), GeneralName::IPAddress(name)) => { match ( - IPConstraint::from_bytes(pattern), + IPConstraint::from_bytes(constraint), IPAddress::from_bytes(name), ) { - (Some(pattern), Some(name)) => Ok(Applied(pattern.matches(&name))), + (Some(constraint), Some(name)) => Ok(Applied(constraint.matches(&name))), (_, None) => Err(ValidationError::new(ValidationErrorKind::Other(format!( "unsatisfiable IP name constraint: malformed SAN {:?}", name, )))), (None, _) => Err(ValidationError::new(ValidationErrorKind::Other(format!( "malformed IP name constraints: {:?}", - pattern + constraint )))), } } - (GeneralName::RFC822Name(pattern), GeneralName::RFC822Name(name)) => { - match (RFC822Constraint::new(pattern.0), RFC822Name::new(name.0)) { - (Some(pattern), Some(name)) => Ok(Applied(pattern.matches(&name))), + (GeneralName::RFC822Name(constraint), GeneralName::RFC822Name(name)) => { + match (RFC822Constraint::new(constraint.0), RFC822Name::new(name.0)) { + (Some(constraint), Some(name)) => Ok(Applied(constraint.matches(&name))), (_, None) => Err(ValidationError::new(ValidationErrorKind::Other(format!( "unsatisfiable RFC822 name constraint: malformed SAN {:?}", name.0, )))), (None, _) => Err(ValidationError::new(ValidationErrorKind::Other(format!( "malformed RFC822 name constraints: {:?}", - pattern.0 + constraint.0 )))), } } diff --git a/src/rust/cryptography-x509-verification/src/types.rs b/src/rust/cryptography-x509-verification/src/types.rs index 32d527828dfa..61da3b5bef9e 100644 --- a/src/rust/cryptography-x509-verification/src/types.rs +++ b/src/rust/cryptography-x509-verification/src/types.rs @@ -138,6 +138,19 @@ impl<'a> DNSPattern<'a> { }, } } + + /// Returns the inner `DNSName` within this `DNSPattern`, e.g. + /// `foo.com` for `*.foo.com` or `example.com` for `example.com`. + /// + /// This API must not be used to bypass pattern matching; it exists + /// solely to enable checks that only require the inner name, such + /// as Name Constraint checks. + pub fn inner_name(&self) -> &DNSName<'a> { + match self { + DNSPattern::Exact(dnsname) => dnsname, + DNSPattern::Wildcard(dnsname) => dnsname, + } + } } /// A `DNSConstraint` represents a DNS name constraint as defined in [RFC 5280 4.2.1.10].