summaryrefslogtreecommitdiff
path: root/modules/caddytls/certselection.go
diff options
context:
space:
mode:
Diffstat (limited to 'modules/caddytls/certselection.go')
-rw-r--r--modules/caddytls/certselection.go101
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