diff options
Diffstat (limited to 'modules/caddytls/certselection.go')
-rw-r--r-- | modules/caddytls/certselection.go | 101 |
1 files changed, 70 insertions, 31 deletions
diff --git a/modules/caddytls/certselection.go b/modules/caddytls/certselection.go index 88b7e0b..8a24034 100644 --- a/modules/caddytls/certselection.go +++ b/modules/caddytls/certselection.go @@ -20,65 +20,104 @@ import ( "fmt" "math/big" - "github.com/caddyserver/caddy/v2" "github.com/caddyserver/certmagic" ) -func init() { - caddy.RegisterModule(CustomCertSelectionPolicy{}) -} - // CustomCertSelectionPolicy represents a policy for selecting the certificate // used to complete a handshake when there may be multiple options. All fields // specified must match the candidate certificate for it to be chosen. // This was needed to solve https://github.com/caddyserver/caddy/issues/2588. type CustomCertSelectionPolicy struct { - SerialNumber *big.Int `json:"serial_number,omitempty"` - SubjectOrganization string `json:"subject_organization,omitempty"` - PublicKeyAlgorithm PublicKeyAlgorithm `json:"public_key_algorithm,omitempty"` - Tag string `json:"tag,omitempty"` -} + // The certificate must have one of these serial numbers. + SerialNumber []*big.Int `json:"serial_number,omitempty"` -// CaddyModule returns the Caddy module information. -func (CustomCertSelectionPolicy) CaddyModule() caddy.ModuleInfo { - return caddy.ModuleInfo{ - ID: "tls.certificate_selection.custom", - New: func() caddy.Module { return new(CustomCertSelectionPolicy) }, - } + // The certificate must have one of these organization names. + SubjectOrganization []string `json:"subject_organization,omitempty"` + + // The certificate must use this public key algorithm. + PublicKeyAlgorithm PublicKeyAlgorithm `json:"public_key_algorithm,omitempty"` + + // The certificate must have at least one of the tags in the list. + AnyTag []string `json:"any_tag,omitempty"` + + // The certificate must have all of the tags in the list. + AllTags []string `json:"all_tags,omitempty"` } -// SelectCertificate implements certmagic.CertificateSelector. -func (p CustomCertSelectionPolicy) SelectCertificate(_ *tls.ClientHelloInfo, choices []certmagic.Certificate) (certmagic.Certificate, error) { +// SelectCertificate implements certmagic.CertificateSelector. It +// only chooses a certificate that at least meets the criteria in +// p. It then chooses the first non-expired certificate that is +// compatible with the client. If none are valid, it chooses the +// first viable candidate anyway. +func (p CustomCertSelectionPolicy) SelectCertificate(hello *tls.ClientHelloInfo, choices []certmagic.Certificate) (certmagic.Certificate, error) { + viable := make([]certmagic.Certificate, 0, len(choices)) + +nextChoice: for _, cert := range choices { - if p.SerialNumber != nil && cert.SerialNumber.Cmp(p.SerialNumber) != 0 { - continue + if len(p.SerialNumber) > 0 { + var found bool + for _, sn := range p.SerialNumber { + if cert.Leaf.SerialNumber.Cmp(sn) == 0 { + found = true + break + } + } + if !found { + continue + } + } + + if len(p.SubjectOrganization) > 0 { + var found bool + for _, subjOrg := range p.SubjectOrganization { + for _, org := range cert.Leaf.Subject.Organization { + if subjOrg == org { + found = true + break + } + } + } + if !found { + continue + } } if p.PublicKeyAlgorithm != PublicKeyAlgorithm(x509.UnknownPublicKeyAlgorithm) && - PublicKeyAlgorithm(cert.PublicKeyAlgorithm) != p.PublicKeyAlgorithm { + PublicKeyAlgorithm(cert.Leaf.PublicKeyAlgorithm) != p.PublicKeyAlgorithm { continue } - if p.SubjectOrganization != "" { - var matchOrg bool - for _, org := range cert.Subject.Organization { - if p.SubjectOrganization == org { - matchOrg = true + if len(p.AnyTag) > 0 { + var found bool + for _, tag := range p.AnyTag { + if cert.HasTag(tag) { + found = true break } } - if !matchOrg { + if !found { continue } } - if p.Tag != "" && !cert.HasTag(p.Tag) { - continue + if len(p.AllTags) > 0 { + for _, tag := range p.AllTags { + if !cert.HasTag(tag) { + continue nextChoice + } + } } - return cert, nil + // this certificate at least meets the policy's requirements, + // but we still have to check expiration and compatibility + viable = append(viable, cert) } - return certmagic.Certificate{}, fmt.Errorf("no certificates matched custom selection policy") + + if len(viable) == 0 { + return certmagic.Certificate{}, fmt.Errorf("no certificates matched custom selection policy") + } + + return certmagic.DefaultCertificateSelector(hello, viable) } // Interface guard |