]> Cypherpunks repositories - gostls13.git/commitdiff
http: check https certificate against host name
authorRuss Cox <rsc@golang.org>
Sun, 12 Sep 2010 03:41:12 +0000 (23:41 -0400)
committerRuss Cox <rsc@golang.org>
Sun, 12 Sep 2010 03:41:12 +0000 (23:41 -0400)
Fixes #1093.

R=agl, agl1
CC=golang-dev
https://golang.org/cl/2115045

src/pkg/crypto/tls/conn.go
src/pkg/crypto/x509/x509.go
src/pkg/crypto/x509/x509_test.go
src/pkg/http/client.go

index 85e76a00c0275ccd50ae90d81606574c1f16702f..78566fa8c534a703edd31ebeb4f56c7e3636ff4f 100644 (file)
@@ -670,3 +670,10 @@ func (c *Conn) PeerCertificates() []*x509.Certificate {
 
        return c.peerCertificates
 }
+
+// VerifyHostname checks that the peer certificate chain is valid for
+// connecting to host.  If so, it returns nil; if not, it returns an os.Error
+// describing the problem.
+func (c *Conn) VerifyHostname(host string) os.Error {
+       return c.PeerCertificates()[0].VerifyHostname(host)
+}
index e4a05d3ef0976c86395f6b76091f1d6f44120d80..3d940e585b4e85c8365abeb2991ab7e38330106e 100644 (file)
@@ -426,19 +426,37 @@ func matchHostnames(pattern, host string) bool {
        return true
 }
 
-// IsValidForHost returns true iff c is a valid certificate for the given host.
-func (c *Certificate) IsValidForHost(h string) bool {
+type HostnameError struct {
+       Certificate *Certificate
+       Host        string
+}
+
+func (h *HostnameError) String() string {
+       var valid string
+       c := h.Certificate
+       if len(c.DNSNames) > 0 {
+               valid = strings.Join(c.DNSNames, ", ")
+       } else {
+               valid = c.Subject.CommonName
+       }
+       return "certificate is valid for " + valid + ", not " + h.Host
+}
+
+// VerifyHostname returns nil if c is a valid certificate for the named host.
+// Otherwise it returns an os.Error describing the mismatch.
+func (c *Certificate) VerifyHostname(h string) os.Error {
        if len(c.DNSNames) > 0 {
                for _, match := range c.DNSNames {
                        if matchHostnames(match, h) {
-                               return true
+                               return nil
                        }
                }
                // If Subject Alt Name is given, we ignore the common name.
-               return false
+       } else if matchHostnames(c.Subject.CommonName, h) {
+               return nil
        }
 
-       return matchHostnames(c.Subject.CommonName, h)
+       return &HostnameError{c, h}
 }
 
 type UnhandledCriticalExtension struct{}
index fa87fe26abcbe5bbef5092cc1a546f29ceebd4a6..12292c1b2e86249efb6b3c395350c899ed22232b 100644 (file)
@@ -96,8 +96,8 @@ func TestCertificateParse(t *testing.T) {
                t.Error(err)
        }
 
-       if !certs[0].IsValidForHost("mail.google.com") {
-               t.Errorf("cert not valid for host")
+       if err := certs[0].VerifyHostname("mail.google.com"); err != nil {
+               t.Error(err)
        }
 }
 
index d77bf0e7594e5c40d193b81f28107f67740f52c9..41e571c2c1b7233b6bc8dd40e562fe6e68be6efe 100644 (file)
@@ -59,11 +59,21 @@ func send(req *Request) (resp *Response, err os.Error) {
        var conn io.ReadWriteCloser
        if req.URL.Scheme == "http" {
                conn, err = net.Dial("tcp", "", addr)
+               if err != nil {
+                       return nil, err
+               }
        } else { // https
                conn, err = tls.Dial("tcp", "", addr)
-       }
-       if err != nil {
-               return nil, err
+               if err != nil {
+                       return nil, err
+               }
+               h := req.URL.Host
+               if hasPort(h) {
+                       h = h[0:strings.LastIndex(h, ":")]
+               }
+               if err := conn.(*tls.Conn).VerifyHostname(h); err != nil {
+                       return nil, err
+               }
        }
 
        err = req.Write(conn)