forked from quic-go/quic-go
Merge pull request #459 from lucas-clemente/fix-458
Fix reading of tls.Config certificates
This commit is contained in:
@@ -55,30 +55,50 @@ func (c *certChain) GetLeafCert(sni string) ([]byte, error) {
|
||||
return cert.Certificate[0], nil
|
||||
}
|
||||
|
||||
func (c *certChain) getCertForSNI(sni string) (*tls.Certificate, error) {
|
||||
if c.config.GetCertificate != nil {
|
||||
cert, err := c.config.GetCertificate(&tls.ClientHelloInfo{ServerName: sni})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
func (cc *certChain) getCertForSNI(sni string) (*tls.Certificate, error) {
|
||||
c := cc.config
|
||||
c, err := maybeGetConfigForClient(c, sni)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// The rest of this function is mostly copied from crypto/tls.getCertificate
|
||||
|
||||
if c.GetCertificate != nil {
|
||||
cert, err := c.GetCertificate(&tls.ClientHelloInfo{ServerName: sni})
|
||||
if cert != nil || err != nil {
|
||||
return cert, err
|
||||
}
|
||||
if cert != nil {
|
||||
}
|
||||
|
||||
if len(c.Certificates) == 0 {
|
||||
return nil, errNoMatchingCertificate
|
||||
}
|
||||
|
||||
if len(c.Certificates) == 1 || c.NameToCertificate == nil {
|
||||
// There's only one choice, so no point doing any work.
|
||||
return &c.Certificates[0], nil
|
||||
}
|
||||
|
||||
name := strings.ToLower(sni)
|
||||
for len(name) > 0 && name[len(name)-1] == '.' {
|
||||
name = name[:len(name)-1]
|
||||
}
|
||||
|
||||
if cert, ok := c.NameToCertificate[name]; ok {
|
||||
return cert, nil
|
||||
}
|
||||
|
||||
// try replacing labels in the name with wildcards until we get a
|
||||
// match.
|
||||
labels := strings.Split(name, ".")
|
||||
for i := range labels {
|
||||
labels[i] = "*"
|
||||
candidate := strings.Join(labels, ".")
|
||||
if cert, ok := c.NameToCertificate[candidate]; ok {
|
||||
return cert, nil
|
||||
}
|
||||
}
|
||||
|
||||
if len(c.config.NameToCertificate) != 0 {
|
||||
if cert, ok := c.config.NameToCertificate[sni]; ok {
|
||||
return cert, nil
|
||||
}
|
||||
wildcardSNI := "*" + strings.TrimLeftFunc(sni, func(r rune) bool { return r != '.' })
|
||||
if cert, ok := c.config.NameToCertificate[wildcardSNI]; ok {
|
||||
return cert, nil
|
||||
}
|
||||
}
|
||||
|
||||
if len(c.config.Certificates) != 0 {
|
||||
return &c.config.Certificates[0], nil
|
||||
}
|
||||
|
||||
return nil, errNoMatchingCertificate
|
||||
// If nothing matches, return the first certificate.
|
||||
return &c.Certificates[0], nil
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"compress/flate"
|
||||
"compress/zlib"
|
||||
"crypto/tls"
|
||||
"reflect"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/testdata"
|
||||
|
||||
@@ -84,6 +85,7 @@ var _ = Describe("Proof", func() {
|
||||
})
|
||||
|
||||
It("uses NameToCertificate entries", func() {
|
||||
config.Certificates = []tls.Certificate{cert, cert} // two entries so the long path is used
|
||||
config.NameToCertificate = map[string]*tls.Certificate{
|
||||
"quic.clemente.io": &cert,
|
||||
}
|
||||
@@ -94,6 +96,7 @@ var _ = Describe("Proof", func() {
|
||||
})
|
||||
|
||||
It("uses NameToCertificate entries with wildcard", func() {
|
||||
config.Certificates = []tls.Certificate{cert, cert} // two entries so the long path is used
|
||||
config.NameToCertificate = map[string]*tls.Certificate{
|
||||
"*.clemente.io": &cert,
|
||||
}
|
||||
@@ -125,5 +128,21 @@ var _ = Describe("Proof", func() {
|
||||
_, err := cc.GetLeafCert("invalid domain")
|
||||
Expect(err).To(MatchError(errNoMatchingCertificate))
|
||||
})
|
||||
|
||||
It("respects GetConfigForClient", func() {
|
||||
if !reflect.ValueOf(tls.Config{}).FieldByName("GetConfigForClient").IsValid() {
|
||||
// Pre 1.8, we don't have to do anything
|
||||
return
|
||||
}
|
||||
nestedConfig := &tls.Config{Certificates: []tls.Certificate{cert}}
|
||||
l := func(chi *tls.ClientHelloInfo) (*tls.Config, error) {
|
||||
Expect(chi.ServerName).To(Equal("quic.clemente.io"))
|
||||
return nestedConfig, nil
|
||||
}
|
||||
reflect.ValueOf(config).Elem().FieldByName("GetConfigForClient").Set(reflect.ValueOf(l))
|
||||
resultCert, err := cc.getCertForSNI("quic.clemente.io")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(*resultCert).To(Equal(cert))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
14
crypto/config_for_client_1.8.go
Normal file
14
crypto/config_for_client_1.8.go
Normal file
@@ -0,0 +1,14 @@
|
||||
// +build go1.8
|
||||
|
||||
package crypto
|
||||
|
||||
import "crypto/tls"
|
||||
|
||||
func maybeGetConfigForClient(c *tls.Config, sni string) (*tls.Config, error) {
|
||||
if c.GetConfigForClient == nil {
|
||||
return c, nil
|
||||
}
|
||||
return c.GetConfigForClient(&tls.ClientHelloInfo{
|
||||
ServerName: sni,
|
||||
})
|
||||
}
|
||||
9
crypto/config_for_client_pre1.8.go
Normal file
9
crypto/config_for_client_pre1.8.go
Normal file
@@ -0,0 +1,9 @@
|
||||
// +build !go1.8
|
||||
|
||||
package crypto
|
||||
|
||||
import "crypto/tls"
|
||||
|
||||
func maybeGetConfigForClient(c *tls.Config, sni string) (*tls.Config, error) {
|
||||
return c, nil
|
||||
}
|
||||
Reference in New Issue
Block a user