Skip to content

Commit 56134a2

Browse files
committed
crypto/x509: add directory name constraints
Fixes #15196
1 parent 654b031 commit 56134a2

File tree

5 files changed

+1453
-23
lines changed

5 files changed

+1453
-23
lines changed

src/crypto/x509/parser.go

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -504,27 +504,40 @@ func parseNameConstraintsExtension(out *Certificate, e pkix.Extension) (unhandle
504504
return false, errors.New("x509: empty name constraints extension")
505505
}
506506

507-
getValues := func(subtrees cryptobyte.String) (dnsNames []string, ips []*net.IPNet, emails, uriDomains []string, err error) {
507+
getValues := func(subtrees cryptobyte.String) (dirNames []pkix.RDNSequence, dnsNames []string, ips []*net.IPNet, emails, uriDomains []string, err error) {
508508
for !subtrees.Empty() {
509509
var seq, value cryptobyte.String
510510
var tag cryptobyte_asn1.Tag
511511
if !subtrees.ReadASN1(&seq, cryptobyte_asn1.SEQUENCE) ||
512512
!seq.ReadAnyASN1(&value, &tag) {
513-
return nil, nil, nil, nil, fmt.Errorf("x509: invalid NameConstraints extension")
513+
return nil, nil, nil, nil, nil, fmt.Errorf("x509: invalid NameConstraints extension")
514514
}
515515

516516
var (
517-
dnsTag = cryptobyte_asn1.Tag(2).ContextSpecific()
518-
emailTag = cryptobyte_asn1.Tag(1).ContextSpecific()
519-
ipTag = cryptobyte_asn1.Tag(7).ContextSpecific()
520-
uriTag = cryptobyte_asn1.Tag(6).ContextSpecific()
517+
dirNameTag = cryptobyte_asn1.Tag(4).ContextSpecific().Constructed()
518+
dnsTag = cryptobyte_asn1.Tag(2).ContextSpecific()
519+
emailTag = cryptobyte_asn1.Tag(1).ContextSpecific()
520+
ipTag = cryptobyte_asn1.Tag(7).ContextSpecific()
521+
uriTag = cryptobyte_asn1.Tag(6).ContextSpecific()
521522
)
522523

523524
switch tag {
525+
case dirNameTag:
526+
527+
var dirName pkix.RDNSequence
528+
529+
if rest, err := asn1.Unmarshal(value, &dirName); err != nil {
530+
return nil, nil, nil, nil, nil, err
531+
} else if len(rest) != 0 {
532+
return nil, nil, nil, nil, nil, errors.New("x509: trailing data after dirname constraint")
533+
}
534+
535+
dirNames = append(dirNames, dirName)
536+
524537
case dnsTag:
525538
domain := string(value)
526539
if err := isIA5String(domain); err != nil {
527-
return nil, nil, nil, nil, errors.New("x509: invalid constraint value: " + err.Error())
540+
return nil, nil, nil, nil, nil, errors.New("x509: invalid constraint value: " + err.Error())
528541
}
529542

530543
trimmedDomain := domain
@@ -536,7 +549,7 @@ func parseNameConstraintsExtension(out *Certificate, e pkix.Extension) (unhandle
536549
trimmedDomain = trimmedDomain[1:]
537550
}
538551
if _, ok := domainToReverseLabels(trimmedDomain); !ok {
539-
return nil, nil, nil, nil, fmt.Errorf("x509: failed to parse dnsName constraint %q", domain)
552+
return nil, nil, nil, nil, nil, fmt.Errorf("x509: failed to parse dnsName constraint %q", domain)
540553
}
541554
dnsNames = append(dnsNames, domain)
542555

@@ -554,26 +567,26 @@ func parseNameConstraintsExtension(out *Certificate, e pkix.Extension) (unhandle
554567
mask = value[16:]
555568

556569
default:
557-
return nil, nil, nil, nil, fmt.Errorf("x509: IP constraint contained value of length %d", l)
570+
return nil, nil, nil, nil, nil, fmt.Errorf("x509: IP constraint contained value of length %d", l)
558571
}
559572

560573
if !isValidIPMask(mask) {
561-
return nil, nil, nil, nil, fmt.Errorf("x509: IP constraint contained invalid mask %x", mask)
574+
return nil, nil, nil, nil, nil, fmt.Errorf("x509: IP constraint contained invalid mask %x", mask)
562575
}
563576

564577
ips = append(ips, &net.IPNet{IP: net.IP(ip), Mask: net.IPMask(mask)})
565578

566579
case emailTag:
567580
constraint := string(value)
568581
if err := isIA5String(constraint); err != nil {
569-
return nil, nil, nil, nil, errors.New("x509: invalid constraint value: " + err.Error())
582+
return nil, nil, nil, nil, nil, errors.New("x509: invalid constraint value: " + err.Error())
570583
}
571584

572585
// If the constraint contains an @ then
573586
// it specifies an exact mailbox name.
574587
if strings.Contains(constraint, "@") {
575588
if _, ok := parseRFC2821Mailbox(constraint); !ok {
576-
return nil, nil, nil, nil, fmt.Errorf("x509: failed to parse rfc822Name constraint %q", constraint)
589+
return nil, nil, nil, nil, nil, fmt.Errorf("x509: failed to parse rfc822Name constraint %q", constraint)
577590
}
578591
} else {
579592
// Otherwise it's a domain name.
@@ -582,19 +595,19 @@ func parseNameConstraintsExtension(out *Certificate, e pkix.Extension) (unhandle
582595
domain = domain[1:]
583596
}
584597
if _, ok := domainToReverseLabels(domain); !ok {
585-
return nil, nil, nil, nil, fmt.Errorf("x509: failed to parse rfc822Name constraint %q", constraint)
598+
return nil, nil, nil, nil, nil, fmt.Errorf("x509: failed to parse rfc822Name constraint %q", constraint)
586599
}
587600
}
588601
emails = append(emails, constraint)
589602

590603
case uriTag:
591604
domain := string(value)
592605
if err := isIA5String(domain); err != nil {
593-
return nil, nil, nil, nil, errors.New("x509: invalid constraint value: " + err.Error())
606+
return nil, nil, nil, nil, nil, errors.New("x509: invalid constraint value: " + err.Error())
594607
}
595608

596609
if net.ParseIP(domain) != nil {
597-
return nil, nil, nil, nil, fmt.Errorf("x509: failed to parse URI constraint %q: cannot be IP address", domain)
610+
return nil, nil, nil, nil, nil, fmt.Errorf("x509: failed to parse URI constraint %q: cannot be IP address", domain)
598611
}
599612

600613
trimmedDomain := domain
@@ -606,7 +619,7 @@ func parseNameConstraintsExtension(out *Certificate, e pkix.Extension) (unhandle
606619
trimmedDomain = trimmedDomain[1:]
607620
}
608621
if _, ok := domainToReverseLabels(trimmedDomain); !ok {
609-
return nil, nil, nil, nil, fmt.Errorf("x509: failed to parse URI constraint %q", domain)
622+
return nil, nil, nil, nil, nil, fmt.Errorf("x509: failed to parse URI constraint %q", domain)
610623
}
611624
uriDomains = append(uriDomains, domain)
612625

@@ -615,13 +628,13 @@ func parseNameConstraintsExtension(out *Certificate, e pkix.Extension) (unhandle
615628
}
616629
}
617630

618-
return dnsNames, ips, emails, uriDomains, nil
631+
return dirNames, dnsNames, ips, emails, uriDomains, nil
619632
}
620633

621-
if out.PermittedDNSDomains, out.PermittedIPRanges, out.PermittedEmailAddresses, out.PermittedURIDomains, err = getValues(permitted); err != nil {
634+
if out.PermittedDirNames, out.PermittedDNSDomains, out.PermittedIPRanges, out.PermittedEmailAddresses, out.PermittedURIDomains, err = getValues(permitted); err != nil {
622635
return false, err
623636
}
624-
if out.ExcludedDNSDomains, out.ExcludedIPRanges, out.ExcludedEmailAddresses, out.ExcludedURIDomains, err = getValues(excluded); err != nil {
637+
if out.ExcludedDirNames, out.ExcludedDNSDomains, out.ExcludedIPRanges, out.ExcludedEmailAddresses, out.ExcludedURIDomains, err = getValues(excluded); err != nil {
625638
return false, err
626639
}
627640
out.PermittedDNSDomainsCritical = e.Critical

src/crypto/x509/verify.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"bytes"
99
"crypto"
1010
"crypto/x509/pkix"
11+
"encoding/asn1"
1112
"errors"
1213
"fmt"
1314
"net"
@@ -493,6 +494,34 @@ func matchDomainConstraint(domain, constraint string) (bool, error) {
493494
return true, nil
494495
}
495496

497+
func matchDirNameConstraint(dirname pkix.RDNSequence, constraint pkix.RDNSequence) (bool, error) {
498+
499+
if len(constraint) > len(dirname) {
500+
return false, nil
501+
}
502+
for i, rdn := range constraint {
503+
if len(rdn) > len(dirname[i]) {
504+
return false, nil
505+
}
506+
for j, const_tv := range rdn {
507+
dirname_tv := dirname[i][j]
508+
if len(const_tv.Type) != len(dirname_tv.Type) {
509+
return false, nil
510+
}
511+
for k, _ := range const_tv.Type {
512+
if const_tv.Type[k] != dirname_tv.Type[k] {
513+
return false, nil
514+
}
515+
}
516+
if const_tv.Value != dirname_tv.Value {
517+
return false, nil
518+
}
519+
}
520+
}
521+
522+
return true, nil
523+
}
524+
496525
// checkNameConstraints checks that c permits a child certificate to claim the
497526
// given name, of type nameType. The argument parsedName contains the parsed
498527
// form of name, suitable for passing to the match function. The total number
@@ -599,6 +628,26 @@ func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *V
599628
leaf = currentChain[0]
600629
}
601630

631+
if (certType == intermediateCertificate || certType == rootCertificate) && c.hasNameConstraints() {
632+
for _, cert := range currentChain {
633+
var subject pkix.RDNSequence
634+
635+
// cert.Subject.ToRDNSequence cannot be used as it ignores unknown RDN
636+
if rest, err := asn1.Unmarshal(cert.RawSubject, &subject); err != nil {
637+
return err
638+
} else if len(rest) != 0 {
639+
return errors.New("x509: trailing data after X.509 subject")
640+
}
641+
642+
if err := c.checkNameConstraints(&comparisonCount, maxConstraintComparisons, "directory Name", cert.Subject.String(), subject,
643+
func(parsedName, constraint interface{}) (bool, error) {
644+
return matchDirNameConstraint(parsedName.(pkix.RDNSequence), constraint.(pkix.RDNSequence))
645+
}, c.PermittedDirNames, c.ExcludedDirNames); err != nil {
646+
return err
647+
}
648+
}
649+
}
650+
602651
if (certType == intermediateCertificate || certType == rootCertificate) &&
603652
c.hasNameConstraints() {
604653
toCheck := []*Certificate{}

0 commit comments

Comments
 (0)