certViewer/cmd/web/constraints.go (93 lines of code) (raw):

/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* The following code is adapted from: * https://github.com/mozilla/tls-observatory/blob/7bc42856d2e5594614b56c2f55baf42bb9751b3d/certificate/constraints/constraints.go */ package main import ( "crypto/x509" "fmt" "net" "time" ) type Constraints struct { PermittedDNSDomains []string ExcludedDNSDomains []string PermittedIPRanges []*net.IPNet ExcludedIPRanges []*net.IPNet } // GetConstraints returns the Constraints for a given x509 certificate func GetConstraints(cert *x509.Certificate) (*Constraints, error) { certs, err := x509.ParseCertificates(cert.Raw) if err != nil { return nil, err } if len(certs) != 1 { return nil, fmt.Errorf("cert.Raw must contain exactly one certificate") } constraintCert := certs[0] return &Constraints{ PermittedDNSDomains: constraintCert.PermittedDNSDomains, ExcludedDNSDomains: constraintCert.ExcludedDNSDomains, PermittedIPRanges: constraintCert.PermittedIPRanges, ExcludedIPRanges: constraintCert.ExcludedIPRanges, }, nil } func isAllZeros(buf []byte, length int) bool { if length > len(buf) { return false } for i := 0; i < length; i++ { if buf[i] != 0 { return false } } return true } // IsTechnicallyConstrained determines if a given certificate is technically constrained. // Slightly modified from https://github.com/jcjones/gx509/blob/master/gx509/technicalconstraints.go func IsTechnicallyConstrained(cert *x509.Certificate) bool { // There must be Extended Key Usage flags if len(cert.ExtKeyUsage) == 0 { return false } // For certificates with a notBefore before 23 August 2016, the // id-Netscape-stepUp OID (aka Netscape Server Gated Crypto ("nsSGC")) is // treated as equivalent to id-kp-serverAuth. nsSGCCutoff := time.Date(2016, time.August, 23, 0, 0, 0, 0, time.UTC) stepUpEquivalentToServerAuth := cert.NotBefore.Before(nsSGCCutoff) var hasServerAuth bool var hasStepUp bool for _, usage := range cert.ExtKeyUsage { switch usage { case x509.ExtKeyUsageAny: // Do not permit ExtKeyUsageAny return false case x509.ExtKeyUsageServerAuth: hasServerAuth = true case x509.ExtKeyUsageNetscapeServerGatedCrypto: hasStepUp = true } } // Must be marked for Server Auth, or have StepUp and be from before the cutoff if !(hasServerAuth || (stepUpEquivalentToServerAuth && hasStepUp)) { return true } // For iPAddresses in excludedSubtrees, both IPv4 and IPv6 must be present // and the constraints must cover the entire range (0.0.0.0/0 for IPv4 and // ::0/0 for IPv6). var excludesIPv4 bool var excludesIPv6 bool constraints, _ := GetConstraints(cert) for _, cidr := range constraints.ExcludedIPRanges { if cidr.IP.Equal(net.IPv4zero) && isAllZeros(cidr.Mask, net.IPv4len) { excludesIPv4 = true } if cidr.IP.Equal(net.IPv6zero) && isAllZeros(cidr.Mask, net.IPv6len) { excludesIPv6 = true } } hasIPAddressInPermittedSubtrees := len(constraints.PermittedIPRanges) > 0 hasIPAddressesInExcludedSubtrees := excludesIPv4 && excludesIPv6 // There must be at least one DNSname constraint hasDNSName := len(cert.PermittedDNSDomains) > 0 || len(constraints.ExcludedDNSDomains) > 0 if hasDNSName && (hasIPAddressInPermittedSubtrees || hasIPAddressesInExcludedSubtrees) { return true } return false } // IsTechnicallyConstrainedMozPolicyV25 determines if a given certificate is technically constrained // according to the Mozilla Root Store Policy V2.5. // https://www.mozilla.org/en-US/about/governance/policies/security-group/certs/policy/ func IsTechnicallyConstrainedMozPolicyV25(cert *x509.Certificate) bool { // The logic from IsTechnicallyConstrained is extended here due to paragraph // three of section 5.3.1: // // If the certificate includes the id-kp-emailProtection extended key usage, // it MUST include the Name Constraints X.509v3 extension with constraints on // rfc822Name, with at least one name in permittedSubtrees, each such name having // its ownership validated according to section 3.2.2.4 of the Baseline Requirements. for _, extKeyUsage := range cert.ExtKeyUsage { if extKeyUsage == x509.ExtKeyUsageEmailProtection { if len(cert.PermittedEmailAddresses) == 0 { return false } break } } return IsTechnicallyConstrained(cert) }