Skip to content

Commit a4514c4

Browse files
committed
http: check https certificate against host name
Fixes #1093. R=agl, agl1 CC=golang-dev https://golang.org/cl/2115045
1 parent eddddf0 commit a4514c4

File tree

4 files changed

+45
-10
lines changed

4 files changed

+45
-10
lines changed

src/pkg/crypto/tls/conn.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -670,3 +670,10 @@ func (c *Conn) PeerCertificates() []*x509.Certificate {
670670

671671
return c.peerCertificates
672672
}
673+
674+
// VerifyHostname checks that the peer certificate chain is valid for
675+
// connecting to host. If so, it returns nil; if not, it returns an os.Error
676+
// describing the problem.
677+
func (c *Conn) VerifyHostname(host string) os.Error {
678+
return c.PeerCertificates()[0].VerifyHostname(host)
679+
}

src/pkg/crypto/x509/x509.go

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -426,19 +426,37 @@ func matchHostnames(pattern, host string) bool {
426426
return true
427427
}
428428

429-
// IsValidForHost returns true iff c is a valid certificate for the given host.
430-
func (c *Certificate) IsValidForHost(h string) bool {
429+
type HostnameError struct {
430+
Certificate *Certificate
431+
Host string
432+
}
433+
434+
func (h *HostnameError) String() string {
435+
var valid string
436+
c := h.Certificate
437+
if len(c.DNSNames) > 0 {
438+
valid = strings.Join(c.DNSNames, ", ")
439+
} else {
440+
valid = c.Subject.CommonName
441+
}
442+
return "certificate is valid for " + valid + ", not " + h.Host
443+
}
444+
445+
// VerifyHostname returns nil if c is a valid certificate for the named host.
446+
// Otherwise it returns an os.Error describing the mismatch.
447+
func (c *Certificate) VerifyHostname(h string) os.Error {
431448
if len(c.DNSNames) > 0 {
432449
for _, match := range c.DNSNames {
433450
if matchHostnames(match, h) {
434-
return true
451+
return nil
435452
}
436453
}
437454
// If Subject Alt Name is given, we ignore the common name.
438-
return false
455+
} else if matchHostnames(c.Subject.CommonName, h) {
456+
return nil
439457
}
440458

441-
return matchHostnames(c.Subject.CommonName, h)
459+
return &HostnameError{c, h}
442460
}
443461

444462
type UnhandledCriticalExtension struct{}

src/pkg/crypto/x509/x509_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,8 @@ func TestCertificateParse(t *testing.T) {
9696
t.Error(err)
9797
}
9898

99-
if !certs[0].IsValidForHost("mail.google.com") {
100-
t.Errorf("cert not valid for host")
99+
if err := certs[0].VerifyHostname("mail.google.com"); err != nil {
100+
t.Error(err)
101101
}
102102
}
103103

src/pkg/http/client.go

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,21 @@ func send(req *Request) (resp *Response, err os.Error) {
5959
var conn io.ReadWriteCloser
6060
if req.URL.Scheme == "http" {
6161
conn, err = net.Dial("tcp", "", addr)
62+
if err != nil {
63+
return nil, err
64+
}
6265
} else { // https
6366
conn, err = tls.Dial("tcp", "", addr)
64-
}
65-
if err != nil {
66-
return nil, err
67+
if err != nil {
68+
return nil, err
69+
}
70+
h := req.URL.Host
71+
if hasPort(h) {
72+
h = h[0:strings.LastIndex(h, ":")]
73+
}
74+
if err := conn.(*tls.Conn).VerifyHostname(h); err != nil {
75+
return nil, err
76+
}
6777
}
6878

6979
err = req.Write(conn)

0 commit comments

Comments
 (0)