Merge pull request #459 from lucas-clemente/fix-458

Fix reading of tls.Config certificates
This commit is contained in:
Lucas Clemente
2017-03-02 12:40:24 +01:00
committed by GitHub
4 changed files with 83 additions and 21 deletions

View File

@@ -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
}

View File

@@ -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))
})
})
})

View 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,
})
}

View 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
}