12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091 |
- // Copyright 2011 The Go Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- package x509
- import (
- "bytes"
- "errors"
- "fmt"
- "net"
- "net/url"
- "reflect"
- "runtime"
- "strconv"
- "strings"
- "time"
- "unicode/utf8"
- "github.com/google/certificate-transparency-go/asn1"
- )
- type InvalidReason int
- const (
- // NotAuthorizedToSign results when a certificate is signed by another
- // which isn't marked as a CA certificate.
- NotAuthorizedToSign InvalidReason = iota
- // Expired results when a certificate has expired, based on the time
- // given in the VerifyOptions.
- Expired
- // CANotAuthorizedForThisName results when an intermediate or root
- // certificate has a name constraint which doesn't permit a DNS or
- // other name (including IP address) in the leaf certificate.
- CANotAuthorizedForThisName
- // TooManyIntermediates results when a path length constraint is
- // violated.
- TooManyIntermediates
- // IncompatibleUsage results when the certificate's key usage indicates
- // that it may only be used for a different purpose.
- IncompatibleUsage
- // NameMismatch results when the subject name of a parent certificate
- // does not match the issuer name in the child.
- NameMismatch
- // NameConstraintsWithoutSANs results when a leaf certificate doesn't
- // contain a Subject Alternative Name extension, but a CA certificate
- // contains name constraints.
- NameConstraintsWithoutSANs
- // UnconstrainedName results when a CA certificate contains permitted
- // name constraints, but leaf certificate contains a name of an
- // unsupported or unconstrained type.
- UnconstrainedName
- // TooManyConstraints results when the number of comparision operations
- // needed to check a certificate exceeds the limit set by
- // VerifyOptions.MaxConstraintComparisions. This limit exists to
- // prevent pathological certificates can consuming excessive amounts of
- // CPU time to verify.
- TooManyConstraints
- // CANotAuthorizedForExtKeyUsage results when an intermediate or root
- // certificate does not permit an extended key usage that is claimed by
- // the leaf certificate.
- CANotAuthorizedForExtKeyUsage
- )
- // CertificateInvalidError results when an odd error occurs. Users of this
- // library probably want to handle all these errors uniformly.
- type CertificateInvalidError struct {
- Cert *Certificate
- Reason InvalidReason
- Detail string
- }
- func (e CertificateInvalidError) Error() string {
- switch e.Reason {
- case NotAuthorizedToSign:
- return "x509: certificate is not authorized to sign other certificates"
- case Expired:
- return "x509: certificate has expired or is not yet valid"
- case CANotAuthorizedForThisName:
- return "x509: a root or intermediate certificate is not authorized to sign for this name: " + e.Detail
- case CANotAuthorizedForExtKeyUsage:
- return "x509: a root or intermediate certificate is not authorized for an extended key usage: " + e.Detail
- case TooManyIntermediates:
- return "x509: too many intermediates for path length constraint"
- case IncompatibleUsage:
- return "x509: certificate specifies an incompatible key usage: " + e.Detail
- case NameMismatch:
- return "x509: issuer name does not match subject from issuing certificate"
- case NameConstraintsWithoutSANs:
- return "x509: issuer has name constraints but leaf doesn't have a SAN extension"
- case UnconstrainedName:
- return "x509: issuer has name constraints but leaf contains unknown or unconstrained name: " + e.Detail
- }
- return "x509: unknown error"
- }
- // HostnameError results when the set of authorized names doesn't match the
- // requested name.
- type HostnameError struct {
- Certificate *Certificate
- Host string
- }
- func (h HostnameError) Error() string {
- c := h.Certificate
- var valid string
- if ip := net.ParseIP(h.Host); ip != nil {
- // Trying to validate an IP
- if len(c.IPAddresses) == 0 {
- return "x509: cannot validate certificate for " + h.Host + " because it doesn't contain any IP SANs"
- }
- for _, san := range c.IPAddresses {
- if len(valid) > 0 {
- valid += ", "
- }
- valid += san.String()
- }
- } else {
- if c.hasSANExtension() {
- valid = strings.Join(c.DNSNames, ", ")
- } else {
- valid = c.Subject.CommonName
- }
- }
- if len(valid) == 0 {
- return "x509: certificate is not valid for any names, but wanted to match " + h.Host
- }
- return "x509: certificate is valid for " + valid + ", not " + h.Host
- }
- // UnknownAuthorityError results when the certificate issuer is unknown
- type UnknownAuthorityError struct {
- Cert *Certificate
- // hintErr contains an error that may be helpful in determining why an
- // authority wasn't found.
- hintErr error
- // hintCert contains a possible authority certificate that was rejected
- // because of the error in hintErr.
- hintCert *Certificate
- }
- func (e UnknownAuthorityError) Error() string {
- s := "x509: certificate signed by unknown authority"
- if e.hintErr != nil {
- certName := e.hintCert.Subject.CommonName
- if len(certName) == 0 {
- if len(e.hintCert.Subject.Organization) > 0 {
- certName = e.hintCert.Subject.Organization[0]
- } else {
- certName = "serial:" + e.hintCert.SerialNumber.String()
- }
- }
- s += fmt.Sprintf(" (possibly because of %q while trying to verify candidate authority certificate %q)", e.hintErr, certName)
- }
- return s
- }
- // SystemRootsError results when we fail to load the system root certificates.
- type SystemRootsError struct {
- Err error
- }
- func (se SystemRootsError) Error() string {
- msg := "x509: failed to load system roots and no roots provided"
- if se.Err != nil {
- return msg + "; " + se.Err.Error()
- }
- return msg
- }
- // errNotParsed is returned when a certificate without ASN.1 contents is
- // verified. Platform-specific verification needs the ASN.1 contents.
- var errNotParsed = errors.New("x509: missing ASN.1 contents; use ParseCertificate")
- // VerifyOptions contains parameters for Certificate.Verify. It's a structure
- // because other PKIX verification APIs have ended up needing many options.
- type VerifyOptions struct {
- DNSName string
- Intermediates *CertPool
- Roots *CertPool // if nil, the system roots are used
- CurrentTime time.Time // if zero, the current time is used
- // Options to disable various verification checks.
- DisableTimeChecks bool
- DisableCriticalExtensionChecks bool
- DisableNameChecks bool
- DisableEKUChecks bool
- DisablePathLenChecks bool
- DisableNameConstraintChecks bool
- // KeyUsage specifies which Extended Key Usage values are acceptable. A leaf
- // certificate is accepted if it contains any of the listed values. An empty
- // list means ExtKeyUsageServerAuth. To accept any key usage, include
- // ExtKeyUsageAny.
- //
- // Certificate chains are required to nest extended key usage values,
- // irrespective of this value. This matches the Windows CryptoAPI behavior,
- // but not the spec.
- KeyUsages []ExtKeyUsage
- // MaxConstraintComparisions is the maximum number of comparisons to
- // perform when checking a given certificate's name constraints. If
- // zero, a sensible default is used. This limit prevents pathological
- // certificates from consuming excessive amounts of CPU time when
- // validating.
- MaxConstraintComparisions int
- }
- const (
- leafCertificate = iota
- intermediateCertificate
- rootCertificate
- )
- // rfc2821Mailbox represents a “mailbox” (which is an email address to most
- // people) by breaking it into the “local” (i.e. before the '@') and “domain”
- // parts.
- type rfc2821Mailbox struct {
- local, domain string
- }
- // parseRFC2821Mailbox parses an email address into local and domain parts,
- // based on the ABNF for a “Mailbox” from RFC 2821. According to
- // https://tools.ietf.org/html/rfc5280#section-4.2.1.6 that's correct for an
- // rfc822Name from a certificate: “The format of an rfc822Name is a "Mailbox"
- // as defined in https://tools.ietf.org/html/rfc2821#section-4.1.2”.
- func parseRFC2821Mailbox(in string) (mailbox rfc2821Mailbox, ok bool) {
- if len(in) == 0 {
- return mailbox, false
- }
- localPartBytes := make([]byte, 0, len(in)/2)
- if in[0] == '"' {
- // Quoted-string = DQUOTE *qcontent DQUOTE
- // non-whitespace-control = %d1-8 / %d11 / %d12 / %d14-31 / %d127
- // qcontent = qtext / quoted-pair
- // qtext = non-whitespace-control /
- // %d33 / %d35-91 / %d93-126
- // quoted-pair = ("\" text) / obs-qp
- // text = %d1-9 / %d11 / %d12 / %d14-127 / obs-text
- //
- // (Names beginning with “obs-” are the obsolete syntax from
- // https://tools.ietf.org/html/rfc2822#section-4. Since it has
- // been 16 years, we no longer accept that.)
- in = in[1:]
- QuotedString:
- for {
- if len(in) == 0 {
- return mailbox, false
- }
- c := in[0]
- in = in[1:]
- switch {
- case c == '"':
- break QuotedString
- case c == '\\':
- // quoted-pair
- if len(in) == 0 {
- return mailbox, false
- }
- if in[0] == 11 ||
- in[0] == 12 ||
- (1 <= in[0] && in[0] <= 9) ||
- (14 <= in[0] && in[0] <= 127) {
- localPartBytes = append(localPartBytes, in[0])
- in = in[1:]
- } else {
- return mailbox, false
- }
- case c == 11 ||
- c == 12 ||
- // Space (char 32) is not allowed based on the
- // BNF, but RFC 3696 gives an example that
- // assumes that it is. Several “verified”
- // errata continue to argue about this point.
- // We choose to accept it.
- c == 32 ||
- c == 33 ||
- c == 127 ||
- (1 <= c && c <= 8) ||
- (14 <= c && c <= 31) ||
- (35 <= c && c <= 91) ||
- (93 <= c && c <= 126):
- // qtext
- localPartBytes = append(localPartBytes, c)
- default:
- return mailbox, false
- }
- }
- } else {
- // Atom ("." Atom)*
- NextChar:
- for len(in) > 0 {
- // atext from https://tools.ietf.org/html/rfc2822#section-3.2.4
- c := in[0]
- switch {
- case c == '\\':
- // Examples given in RFC 3696 suggest that
- // escaped characters can appear outside of a
- // quoted string. Several “verified” errata
- // continue to argue the point. We choose to
- // accept it.
- in = in[1:]
- if len(in) == 0 {
- return mailbox, false
- }
- fallthrough
- case ('0' <= c && c <= '9') ||
- ('a' <= c && c <= 'z') ||
- ('A' <= c && c <= 'Z') ||
- c == '!' || c == '#' || c == '$' || c == '%' ||
- c == '&' || c == '\'' || c == '*' || c == '+' ||
- c == '-' || c == '/' || c == '=' || c == '?' ||
- c == '^' || c == '_' || c == '`' || c == '{' ||
- c == '|' || c == '}' || c == '~' || c == '.':
- localPartBytes = append(localPartBytes, in[0])
- in = in[1:]
- default:
- break NextChar
- }
- }
- if len(localPartBytes) == 0 {
- return mailbox, false
- }
- // https://tools.ietf.org/html/rfc3696#section-3
- // “period (".") may also appear, but may not be used to start
- // or end the local part, nor may two or more consecutive
- // periods appear.”
- twoDots := []byte{'.', '.'}
- if localPartBytes[0] == '.' ||
- localPartBytes[len(localPartBytes)-1] == '.' ||
- bytes.Contains(localPartBytes, twoDots) {
- return mailbox, false
- }
- }
- if len(in) == 0 || in[0] != '@' {
- return mailbox, false
- }
- in = in[1:]
- // The RFC species a format for domains, but that's known to be
- // violated in practice so we accept that anything after an '@' is the
- // domain part.
- if _, ok := domainToReverseLabels(in); !ok {
- return mailbox, false
- }
- mailbox.local = string(localPartBytes)
- mailbox.domain = in
- return mailbox, true
- }
- // domainToReverseLabels converts a textual domain name like foo.example.com to
- // the list of labels in reverse order, e.g. ["com", "example", "foo"].
- func domainToReverseLabels(domain string) (reverseLabels []string, ok bool) {
- for len(domain) > 0 {
- if i := strings.LastIndexByte(domain, '.'); i == -1 {
- reverseLabels = append(reverseLabels, domain)
- domain = ""
- } else {
- reverseLabels = append(reverseLabels, domain[i+1:len(domain)])
- domain = domain[:i]
- }
- }
- if len(reverseLabels) > 0 && len(reverseLabels[0]) == 0 {
- // An empty label at the end indicates an absolute value.
- return nil, false
- }
- for _, label := range reverseLabels {
- if len(label) == 0 {
- // Empty labels are otherwise invalid.
- return nil, false
- }
- for _, c := range label {
- if c < 33 || c > 126 {
- // Invalid character.
- return nil, false
- }
- }
- }
- return reverseLabels, true
- }
- func matchEmailConstraint(mailbox rfc2821Mailbox, constraint string) (bool, error) {
- // If the constraint contains an @, then it specifies an exact mailbox
- // name.
- if strings.Contains(constraint, "@") {
- constraintMailbox, ok := parseRFC2821Mailbox(constraint)
- if !ok {
- return false, fmt.Errorf("x509: internal error: cannot parse constraint %q", constraint)
- }
- return mailbox.local == constraintMailbox.local && strings.EqualFold(mailbox.domain, constraintMailbox.domain), nil
- }
- // Otherwise the constraint is like a DNS constraint of the domain part
- // of the mailbox.
- return matchDomainConstraint(mailbox.domain, constraint)
- }
- func matchURIConstraint(uri *url.URL, constraint string) (bool, error) {
- // https://tools.ietf.org/html/rfc5280#section-4.2.1.10
- // “a uniformResourceIdentifier that does not include an authority
- // component with a host name specified as a fully qualified domain
- // name (e.g., if the URI either does not include an authority
- // component or includes an authority component in which the host name
- // is specified as an IP address), then the application MUST reject the
- // certificate.”
- host := uri.Host
- if len(host) == 0 {
- return false, fmt.Errorf("URI with empty host (%q) cannot be matched against constraints", uri.String())
- }
- if strings.Contains(host, ":") && !strings.HasSuffix(host, "]") {
- var err error
- host, _, err = net.SplitHostPort(uri.Host)
- if err != nil {
- return false, err
- }
- }
- if strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]") ||
- net.ParseIP(host) != nil {
- return false, fmt.Errorf("URI with IP (%q) cannot be matched against constraints", uri.String())
- }
- return matchDomainConstraint(host, constraint)
- }
- func matchIPConstraint(ip net.IP, constraint *net.IPNet) (bool, error) {
- if len(ip) != len(constraint.IP) {
- return false, nil
- }
- for i := range ip {
- if mask := constraint.Mask[i]; ip[i]&mask != constraint.IP[i]&mask {
- return false, nil
- }
- }
- return true, nil
- }
- func matchDomainConstraint(domain, constraint string) (bool, error) {
- // The meaning of zero length constraints is not specified, but this
- // code follows NSS and accepts them as matching everything.
- if len(constraint) == 0 {
- return true, nil
- }
- domainLabels, ok := domainToReverseLabels(domain)
- if !ok {
- return false, fmt.Errorf("x509: internal error: cannot parse domain %q", domain)
- }
- // RFC 5280 says that a leading period in a domain name means that at
- // least one label must be prepended, but only for URI and email
- // constraints, not DNS constraints. The code also supports that
- // behaviour for DNS constraints.
- mustHaveSubdomains := false
- if constraint[0] == '.' {
- mustHaveSubdomains = true
- constraint = constraint[1:]
- }
- constraintLabels, ok := domainToReverseLabels(constraint)
- if !ok {
- return false, fmt.Errorf("x509: internal error: cannot parse domain %q", constraint)
- }
- if len(domainLabels) < len(constraintLabels) ||
- (mustHaveSubdomains && len(domainLabels) == len(constraintLabels)) {
- return false, nil
- }
- for i, constraintLabel := range constraintLabels {
- if !strings.EqualFold(constraintLabel, domainLabels[i]) {
- return false, nil
- }
- }
- return true, nil
- }
- // checkNameConstraints checks that c permits a child certificate to claim the
- // given name, of type nameType. The argument parsedName contains the parsed
- // form of name, suitable for passing to the match function. The total number
- // of comparisons is tracked in the given count and should not exceed the given
- // limit.
- func (c *Certificate) checkNameConstraints(count *int,
- maxConstraintComparisons int,
- nameType string,
- name string,
- parsedName interface{},
- match func(parsedName, constraint interface{}) (match bool, err error),
- permitted, excluded interface{}) error {
- excludedValue := reflect.ValueOf(excluded)
- *count += excludedValue.Len()
- if *count > maxConstraintComparisons {
- return CertificateInvalidError{c, TooManyConstraints, ""}
- }
- for i := 0; i < excludedValue.Len(); i++ {
- constraint := excludedValue.Index(i).Interface()
- match, err := match(parsedName, constraint)
- if err != nil {
- return CertificateInvalidError{c, CANotAuthorizedForThisName, err.Error()}
- }
- if match {
- return CertificateInvalidError{c, CANotAuthorizedForThisName, fmt.Sprintf("%s %q is excluded by constraint %q", nameType, name, constraint)}
- }
- }
- permittedValue := reflect.ValueOf(permitted)
- *count += permittedValue.Len()
- if *count > maxConstraintComparisons {
- return CertificateInvalidError{c, TooManyConstraints, ""}
- }
- ok := true
- for i := 0; i < permittedValue.Len(); i++ {
- constraint := permittedValue.Index(i).Interface()
- var err error
- if ok, err = match(parsedName, constraint); err != nil {
- return CertificateInvalidError{c, CANotAuthorizedForThisName, err.Error()}
- }
- if ok {
- break
- }
- }
- if !ok {
- return CertificateInvalidError{c, CANotAuthorizedForThisName, fmt.Sprintf("%s %q is not permitted by any constraint", nameType, name)}
- }
- return nil
- }
- const (
- checkingAgainstIssuerCert = iota
- checkingAgainstLeafCert
- )
- // ekuPermittedBy returns true iff the given extended key usage is permitted by
- // the given EKU from a certificate. Normally, this would be a simple
- // comparison plus a special case for the “any” EKU. But, in order to support
- // existing certificates, some exceptions are made.
- func ekuPermittedBy(eku, certEKU ExtKeyUsage, context int) bool {
- if certEKU == ExtKeyUsageAny || eku == certEKU {
- return true
- }
- // Some exceptions are made to support existing certificates. Firstly,
- // the ServerAuth and SGC EKUs are treated as a group.
- mapServerAuthEKUs := func(eku ExtKeyUsage) ExtKeyUsage {
- if eku == ExtKeyUsageNetscapeServerGatedCrypto || eku == ExtKeyUsageMicrosoftServerGatedCrypto {
- return ExtKeyUsageServerAuth
- }
- return eku
- }
- eku = mapServerAuthEKUs(eku)
- certEKU = mapServerAuthEKUs(certEKU)
- if eku == certEKU {
- return true
- }
- // If checking a requested EKU against the list in a leaf certificate there
- // are fewer exceptions.
- if context == checkingAgainstLeafCert {
- return false
- }
- // ServerAuth in a CA permits ClientAuth in the leaf.
- return (eku == ExtKeyUsageClientAuth && certEKU == ExtKeyUsageServerAuth) ||
- // Any CA may issue an OCSP responder certificate.
- eku == ExtKeyUsageOCSPSigning ||
- // Code-signing CAs can use Microsoft's commercial and
- // kernel-mode EKUs.
- (eku == ExtKeyUsageMicrosoftCommercialCodeSigning || eku == ExtKeyUsageMicrosoftKernelCodeSigning) && certEKU == ExtKeyUsageCodeSigning
- }
- // isValid performs validity checks on c given that it is a candidate to append
- // to the chain in currentChain.
- func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *VerifyOptions) error {
- if !opts.DisableCriticalExtensionChecks && len(c.UnhandledCriticalExtensions) > 0 {
- return UnhandledCriticalExtension{ID: c.UnhandledCriticalExtensions[0]}
- }
- if !opts.DisableNameChecks && len(currentChain) > 0 {
- child := currentChain[len(currentChain)-1]
- if !bytes.Equal(child.RawIssuer, c.RawSubject) {
- return CertificateInvalidError{c, NameMismatch, ""}
- }
- }
- if !opts.DisableTimeChecks {
- now := opts.CurrentTime
- if now.IsZero() {
- now = time.Now()
- }
- if now.Before(c.NotBefore) || now.After(c.NotAfter) {
- return CertificateInvalidError{c, Expired, ""}
- }
- }
- maxConstraintComparisons := opts.MaxConstraintComparisions
- if maxConstraintComparisons == 0 {
- maxConstraintComparisons = 250000
- }
- comparisonCount := 0
- var leaf *Certificate
- if certType == intermediateCertificate || certType == rootCertificate {
- if len(currentChain) == 0 {
- return errors.New("x509: internal error: empty chain when appending CA cert")
- }
- leaf = currentChain[0]
- }
- if !opts.DisableNameConstraintChecks && (certType == intermediateCertificate || certType == rootCertificate) && c.hasNameConstraints() {
- sanExtension, ok := leaf.getSANExtension()
- if !ok {
- // This is the deprecated, legacy case of depending on
- // the CN as a hostname. Chains modern enough to be
- // using name constraints should not be depending on
- // CNs.
- return CertificateInvalidError{c, NameConstraintsWithoutSANs, ""}
- }
- err := forEachSAN(sanExtension, func(tag int, data []byte) error {
- switch tag {
- case nameTypeEmail:
- name := string(data)
- mailbox, ok := parseRFC2821Mailbox(name)
- if !ok {
- return fmt.Errorf("x509: cannot parse rfc822Name %q", mailbox)
- }
- if err := c.checkNameConstraints(&comparisonCount, maxConstraintComparisons, "email address", name, mailbox,
- func(parsedName, constraint interface{}) (bool, error) {
- return matchEmailConstraint(parsedName.(rfc2821Mailbox), constraint.(string))
- }, c.PermittedEmailAddresses, c.ExcludedEmailAddresses); err != nil {
- return err
- }
- case nameTypeDNS:
- name := string(data)
- if _, ok := domainToReverseLabels(name); !ok {
- return fmt.Errorf("x509: cannot parse dnsName %q", name)
- }
- if err := c.checkNameConstraints(&comparisonCount, maxConstraintComparisons, "DNS name", name, name,
- func(parsedName, constraint interface{}) (bool, error) {
- return matchDomainConstraint(parsedName.(string), constraint.(string))
- }, c.PermittedDNSDomains, c.ExcludedDNSDomains); err != nil {
- return err
- }
- case nameTypeURI:
- name := string(data)
- uri, err := url.Parse(name)
- if err != nil {
- return fmt.Errorf("x509: internal error: URI SAN %q failed to parse", name)
- }
- if err := c.checkNameConstraints(&comparisonCount, maxConstraintComparisons, "URI", name, uri,
- func(parsedName, constraint interface{}) (bool, error) {
- return matchURIConstraint(parsedName.(*url.URL), constraint.(string))
- }, c.PermittedURIDomains, c.ExcludedURIDomains); err != nil {
- return err
- }
- case nameTypeIP:
- ip := net.IP(data)
- if l := len(ip); l != net.IPv4len && l != net.IPv6len {
- return fmt.Errorf("x509: internal error: IP SAN %x failed to parse", data)
- }
- if err := c.checkNameConstraints(&comparisonCount, maxConstraintComparisons, "IP address", ip.String(), ip,
- func(parsedName, constraint interface{}) (bool, error) {
- return matchIPConstraint(parsedName.(net.IP), constraint.(*net.IPNet))
- }, c.PermittedIPRanges, c.ExcludedIPRanges); err != nil {
- return err
- }
- default:
- // Unknown SAN types are ignored.
- }
- return nil
- })
- if err != nil {
- return err
- }
- }
- checkEKUs := !opts.DisableEKUChecks && certType == intermediateCertificate
- // If no extended key usages are specified, then all are acceptable.
- if checkEKUs && (len(c.ExtKeyUsage) == 0 && len(c.UnknownExtKeyUsage) == 0) {
- checkEKUs = false
- }
- // If the “any” key usage is permitted, then no more checks are needed.
- if checkEKUs {
- for _, caEKU := range c.ExtKeyUsage {
- comparisonCount++
- if caEKU == ExtKeyUsageAny {
- checkEKUs = false
- break
- }
- }
- }
- if checkEKUs {
- NextEKU:
- for _, eku := range leaf.ExtKeyUsage {
- if comparisonCount > maxConstraintComparisons {
- return CertificateInvalidError{c, TooManyConstraints, ""}
- }
- for _, caEKU := range c.ExtKeyUsage {
- comparisonCount++
- if ekuPermittedBy(eku, caEKU, checkingAgainstIssuerCert) {
- continue NextEKU
- }
- }
- oid, _ := oidFromExtKeyUsage(eku)
- return CertificateInvalidError{c, CANotAuthorizedForExtKeyUsage, fmt.Sprintf("EKU not permitted: %#v", oid)}
- }
- NextUnknownEKU:
- for _, eku := range leaf.UnknownExtKeyUsage {
- if comparisonCount > maxConstraintComparisons {
- return CertificateInvalidError{c, TooManyConstraints, ""}
- }
- for _, caEKU := range c.UnknownExtKeyUsage {
- comparisonCount++
- if caEKU.Equal(eku) {
- continue NextUnknownEKU
- }
- }
- return CertificateInvalidError{c, CANotAuthorizedForExtKeyUsage, fmt.Sprintf("EKU not permitted: %#v", eku)}
- }
- }
- // KeyUsage status flags are ignored. From Engineering Security, Peter
- // Gutmann: A European government CA marked its signing certificates as
- // being valid for encryption only, but no-one noticed. Another
- // European CA marked its signature keys as not being valid for
- // signatures. A different CA marked its own trusted root certificate
- // as being invalid for certificate signing. Another national CA
- // distributed a certificate to be used to encrypt data for the
- // country’s tax authority that was marked as only being usable for
- // digital signatures but not for encryption. Yet another CA reversed
- // the order of the bit flags in the keyUsage due to confusion over
- // encoding endianness, essentially setting a random keyUsage in
- // certificates that it issued. Another CA created a self-invalidating
- // certificate by adding a certificate policy statement stipulating
- // that the certificate had to be used strictly as specified in the
- // keyUsage, and a keyUsage containing a flag indicating that the RSA
- // encryption key could only be used for Diffie-Hellman key agreement.
- if certType == intermediateCertificate && (!c.BasicConstraintsValid || !c.IsCA) {
- return CertificateInvalidError{c, NotAuthorizedToSign, ""}
- }
- if !opts.DisablePathLenChecks && c.BasicConstraintsValid && c.MaxPathLen >= 0 {
- numIntermediates := len(currentChain) - 1
- if numIntermediates > c.MaxPathLen {
- return CertificateInvalidError{c, TooManyIntermediates, ""}
- }
- }
- return nil
- }
- // formatOID formats an ASN.1 OBJECT IDENTIFER in the common, dotted style.
- func formatOID(oid asn1.ObjectIdentifier) string {
- ret := ""
- for i, v := range oid {
- if i > 0 {
- ret += "."
- }
- ret += strconv.Itoa(v)
- }
- return ret
- }
- // Verify attempts to verify c by building one or more chains from c to a
- // certificate in opts.Roots, using certificates in opts.Intermediates if
- // needed. If successful, it returns one or more chains where the first
- // element of the chain is c and the last element is from opts.Roots.
- //
- // If opts.Roots is nil and system roots are unavailable the returned error
- // will be of type SystemRootsError.
- //
- // Name constraints in the intermediates will be applied to all names claimed
- // in the chain, not just opts.DNSName. Thus it is invalid for a leaf to claim
- // example.com if an intermediate doesn't permit it, even if example.com is not
- // the name being validated. Note that DirectoryName constraints are not
- // supported.
- //
- // Extended Key Usage values are enforced down a chain, so an intermediate or
- // root that enumerates EKUs prevents a leaf from asserting an EKU not in that
- // list.
- //
- // WARNING: this function doesn't do any revocation checking.
- func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err error) {
- // Platform-specific verification needs the ASN.1 contents so
- // this makes the behavior consistent across platforms.
- if len(c.Raw) == 0 {
- return nil, errNotParsed
- }
- if opts.Intermediates != nil {
- for _, intermediate := range opts.Intermediates.certs {
- if len(intermediate.Raw) == 0 {
- return nil, errNotParsed
- }
- }
- }
- // Use Windows's own verification and chain building.
- if opts.Roots == nil && runtime.GOOS == "windows" {
- return c.systemVerify(&opts)
- }
- if opts.Roots == nil {
- opts.Roots = systemRootsPool()
- if opts.Roots == nil {
- return nil, SystemRootsError{systemRootsErr}
- }
- }
- err = c.isValid(leafCertificate, nil, &opts)
- if err != nil {
- return
- }
- if len(opts.DNSName) > 0 {
- err = c.VerifyHostname(opts.DNSName)
- if err != nil {
- return
- }
- }
- requestedKeyUsages := make([]ExtKeyUsage, len(opts.KeyUsages))
- copy(requestedKeyUsages, opts.KeyUsages)
- if len(requestedKeyUsages) == 0 {
- requestedKeyUsages = append(requestedKeyUsages, ExtKeyUsageServerAuth)
- }
- // If no key usages are specified, then any are acceptable.
- checkEKU := !opts.DisableEKUChecks && len(c.ExtKeyUsage) > 0
- for _, eku := range requestedKeyUsages {
- if eku == ExtKeyUsageAny {
- checkEKU = false
- break
- }
- }
- if checkEKU {
- foundMatch := false
- NextUsage:
- for _, eku := range requestedKeyUsages {
- for _, leafEKU := range c.ExtKeyUsage {
- if ekuPermittedBy(eku, leafEKU, checkingAgainstLeafCert) {
- foundMatch = true
- break NextUsage
- }
- }
- }
- if !foundMatch {
- msg := "leaf contains the following, recognized EKUs: "
- for i, leafEKU := range c.ExtKeyUsage {
- oid, ok := oidFromExtKeyUsage(leafEKU)
- if !ok {
- continue
- }
- if i > 0 {
- msg += ", "
- }
- msg += formatOID(oid)
- }
- return nil, CertificateInvalidError{c, IncompatibleUsage, msg}
- }
- }
- var candidateChains [][]*Certificate
- if opts.Roots.contains(c) {
- candidateChains = append(candidateChains, []*Certificate{c})
- } else {
- if candidateChains, err = c.buildChains(make(map[int][][]*Certificate), []*Certificate{c}, &opts); err != nil {
- return nil, err
- }
- }
- return candidateChains, nil
- }
- func appendToFreshChain(chain []*Certificate, cert *Certificate) []*Certificate {
- n := make([]*Certificate, len(chain)+1)
- copy(n, chain)
- n[len(chain)] = cert
- return n
- }
- func (c *Certificate) buildChains(cache map[int][][]*Certificate, currentChain []*Certificate, opts *VerifyOptions) (chains [][]*Certificate, err error) {
- possibleRoots, failedRoot, rootErr := opts.Roots.findVerifiedParents(c)
- nextRoot:
- for _, rootNum := range possibleRoots {
- root := opts.Roots.certs[rootNum]
- for _, cert := range currentChain {
- if cert.Equal(root) {
- continue nextRoot
- }
- }
- err = root.isValid(rootCertificate, currentChain, opts)
- if err != nil {
- continue
- }
- chains = append(chains, appendToFreshChain(currentChain, root))
- }
- possibleIntermediates, failedIntermediate, intermediateErr := opts.Intermediates.findVerifiedParents(c)
- nextIntermediate:
- for _, intermediateNum := range possibleIntermediates {
- intermediate := opts.Intermediates.certs[intermediateNum]
- for _, cert := range currentChain {
- if cert.Equal(intermediate) {
- continue nextIntermediate
- }
- }
- err = intermediate.isValid(intermediateCertificate, currentChain, opts)
- if err != nil {
- continue
- }
- var childChains [][]*Certificate
- childChains, ok := cache[intermediateNum]
- if !ok {
- childChains, err = intermediate.buildChains(cache, appendToFreshChain(currentChain, intermediate), opts)
- cache[intermediateNum] = childChains
- }
- chains = append(chains, childChains...)
- }
- if len(chains) > 0 {
- err = nil
- }
- if len(chains) == 0 && err == nil {
- hintErr := rootErr
- hintCert := failedRoot
- if hintErr == nil {
- hintErr = intermediateErr
- hintCert = failedIntermediate
- }
- err = UnknownAuthorityError{c, hintErr, hintCert}
- }
- return
- }
- func matchHostnames(pattern, host string) bool {
- host = strings.TrimSuffix(host, ".")
- pattern = strings.TrimSuffix(pattern, ".")
- if len(pattern) == 0 || len(host) == 0 {
- return false
- }
- patternParts := strings.Split(pattern, ".")
- hostParts := strings.Split(host, ".")
- if len(patternParts) != len(hostParts) {
- return false
- }
- for i, patternPart := range patternParts {
- if i == 0 && patternPart == "*" {
- continue
- }
- if patternPart != hostParts[i] {
- return false
- }
- }
- return true
- }
- // toLowerCaseASCII returns a lower-case version of in. See RFC 6125 6.4.1. We use
- // an explicitly ASCII function to avoid any sharp corners resulting from
- // performing Unicode operations on DNS labels.
- func toLowerCaseASCII(in string) string {
- // If the string is already lower-case then there's nothing to do.
- isAlreadyLowerCase := true
- for _, c := range in {
- if c == utf8.RuneError {
- // If we get a UTF-8 error then there might be
- // upper-case ASCII bytes in the invalid sequence.
- isAlreadyLowerCase = false
- break
- }
- if 'A' <= c && c <= 'Z' {
- isAlreadyLowerCase = false
- break
- }
- }
- if isAlreadyLowerCase {
- return in
- }
- out := []byte(in)
- for i, c := range out {
- if 'A' <= c && c <= 'Z' {
- out[i] += 'a' - 'A'
- }
- }
- return string(out)
- }
- // VerifyHostname returns nil if c is a valid certificate for the named host.
- // Otherwise it returns an error describing the mismatch.
- func (c *Certificate) VerifyHostname(h string) error {
- // IP addresses may be written in [ ].
- candidateIP := h
- if len(h) >= 3 && h[0] == '[' && h[len(h)-1] == ']' {
- candidateIP = h[1 : len(h)-1]
- }
- if ip := net.ParseIP(candidateIP); ip != nil {
- // We only match IP addresses against IP SANs.
- // https://tools.ietf.org/html/rfc6125#appendix-B.2
- for _, candidate := range c.IPAddresses {
- if ip.Equal(candidate) {
- return nil
- }
- }
- return HostnameError{c, candidateIP}
- }
- lowered := toLowerCaseASCII(h)
- if c.hasSANExtension() {
- for _, match := range c.DNSNames {
- if matchHostnames(toLowerCaseASCII(match), lowered) {
- return nil
- }
- }
- // If Subject Alt Name is given, we ignore the common name.
- } else if matchHostnames(toLowerCaseASCII(c.Subject.CommonName), lowered) {
- return nil
- }
- return HostnameError{c, h}
- }
|