Commit a4514c42 authored by Russ Cox's avatar Russ Cox

http: check https certificate against host name

Fixes #1093.

R=agl, agl1
CC=golang-dev
https://golang.org/cl/2115045
parent eddddf04
......@@ -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)
}
......@@ -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{}
......
......@@ -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)
}
}
......
......@@ -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)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment