client: Remove hostname verification check
This commit is contained in:
		
							parent
							
								
									9f3564936e
								
							
						
					
					
						commit
						bfa3356d3a
					
				@ -175,10 +175,6 @@ func (c *Client) dialContext(ctx context.Context, network, addr string) (net.Con
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func (c *Client) verifyConnection(cs tls.ConnectionState, hostname string) error {
 | 
					func (c *Client) verifyConnection(cs tls.ConnectionState, hostname string) error {
 | 
				
			||||||
	cert := cs.PeerCertificates[0]
 | 
						cert := cs.PeerCertificates[0]
 | 
				
			||||||
	// Verify hostname
 | 
					 | 
				
			||||||
	if err := verifyHostname(cert, hostname); err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	// See if the client trusts the certificate
 | 
						// See if the client trusts the certificate
 | 
				
			||||||
	if c.TrustCertificate != nil {
 | 
						if c.TrustCertificate != nil {
 | 
				
			||||||
		return c.TrustCertificate(hostname, cert)
 | 
							return c.TrustCertificate(hostname, cert)
 | 
				
			||||||
 | 
				
			|||||||
@ -1,212 +0,0 @@
 | 
				
			|||||||
// Hostname verification code from the crypto/x509 package.
 | 
					 | 
				
			||||||
// Modified to allow Common Names in the short term, until new certificates
 | 
					 | 
				
			||||||
// can be issued with SANs.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Copyright 2011 The Go Authors. All rights reserved.
 | 
					 | 
				
			||||||
// Use of this source code is governed by a BSD-style
 | 
					 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package gemini
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"crypto/x509"
 | 
					 | 
				
			||||||
	"net"
 | 
					 | 
				
			||||||
	"strings"
 | 
					 | 
				
			||||||
	"unicode/utf8"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var oidExtensionSubjectAltName = []int{2, 5, 29, 17}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func hasSANExtension(c *x509.Certificate) bool {
 | 
					 | 
				
			||||||
	for _, e := range c.Extensions {
 | 
					 | 
				
			||||||
		if e.Id.Equal(oidExtensionSubjectAltName) {
 | 
					 | 
				
			||||||
			return true
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return false
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func validHostnamePattern(host string) bool { return validHostname(host, true) }
 | 
					 | 
				
			||||||
func validHostnameInput(host string) bool   { return validHostname(host, false) }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// validHostname reports whether host is a valid hostname that can be matched or
 | 
					 | 
				
			||||||
// matched against according to RFC 6125 2.2, with some leniency to accommodate
 | 
					 | 
				
			||||||
// legacy values.
 | 
					 | 
				
			||||||
func validHostname(host string, isPattern bool) bool {
 | 
					 | 
				
			||||||
	if !isPattern {
 | 
					 | 
				
			||||||
		host = strings.TrimSuffix(host, ".")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if len(host) == 0 {
 | 
					 | 
				
			||||||
		return false
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for i, part := range strings.Split(host, ".") {
 | 
					 | 
				
			||||||
		if part == "" {
 | 
					 | 
				
			||||||
			// Empty label.
 | 
					 | 
				
			||||||
			return false
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if isPattern && i == 0 && part == "*" {
 | 
					 | 
				
			||||||
			// Only allow full left-most wildcards, as those are the only ones
 | 
					 | 
				
			||||||
			// we match, and matching literal '*' characters is probably never
 | 
					 | 
				
			||||||
			// the expected behavior.
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		for j, c := range part {
 | 
					 | 
				
			||||||
			if 'a' <= c && c <= 'z' {
 | 
					 | 
				
			||||||
				continue
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if '0' <= c && c <= '9' {
 | 
					 | 
				
			||||||
				continue
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if 'A' <= c && c <= 'Z' {
 | 
					 | 
				
			||||||
				continue
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if c == '-' && j != 0 {
 | 
					 | 
				
			||||||
				continue
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if c == '_' {
 | 
					 | 
				
			||||||
				// Not a valid character in hostnames, but commonly
 | 
					 | 
				
			||||||
				// found in deployments outside the WebPKI.
 | 
					 | 
				
			||||||
				continue
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			return false
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return true
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// commonNameAsHostname reports whether the Common Name field should be
 | 
					 | 
				
			||||||
// considered the hostname that the certificate is valid for. This is a legacy
 | 
					 | 
				
			||||||
// behavior, disabled by default or if the Subject Alt Name extension is present.
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// It applies the strict validHostname check to the Common Name field, so that
 | 
					 | 
				
			||||||
// certificates without SANs can still be validated against CAs with name
 | 
					 | 
				
			||||||
// constraints if there is no risk the CN would be matched as a hostname.
 | 
					 | 
				
			||||||
// See NameConstraintsWithoutSANs and issue 24151.
 | 
					 | 
				
			||||||
func commonNameAsHostname(c *x509.Certificate) bool {
 | 
					 | 
				
			||||||
	return !hasSANExtension(c) && validHostnamePattern(c.Subject.CommonName)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func matchExactly(hostA, hostB string) bool {
 | 
					 | 
				
			||||||
	if hostA == "" || hostA == "." || hostB == "" || hostB == "." {
 | 
					 | 
				
			||||||
		return false
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return toLowerCaseASCII(hostA) == toLowerCaseASCII(hostB)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func matchHostnames(pattern, host string) bool {
 | 
					 | 
				
			||||||
	pattern = toLowerCaseASCII(pattern)
 | 
					 | 
				
			||||||
	host = toLowerCaseASCII(strings.TrimSuffix(host, "."))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if len(pattern) == 0 || len(host) == 0 {
 | 
					 | 
				
			||||||
		return false
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	patternParts := strings.Split(pattern, ".")
 | 
					 | 
				
			||||||
	hostParts := strings.Split(host, ".")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if len(patternParts) != len(hostParts) {
 | 
					 | 
				
			||||||
		return false
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for i, patternPart := range patternParts {
 | 
					 | 
				
			||||||
		if i == 0 && patternPart == "*" {
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if patternPart != hostParts[i] {
 | 
					 | 
				
			||||||
			return false
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return true
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// toLowerCaseASCII returns a lower-case version of in. See RFC 6125 6.4.1. We use
 | 
					 | 
				
			||||||
// an explicitly ASCII function to avoid any sharp corners resulting from
 | 
					 | 
				
			||||||
// performing Unicode operations on DNS labels.
 | 
					 | 
				
			||||||
func toLowerCaseASCII(in string) string {
 | 
					 | 
				
			||||||
	// If the string is already lower-case then there's nothing to do.
 | 
					 | 
				
			||||||
	isAlreadyLowerCase := true
 | 
					 | 
				
			||||||
	for _, c := range in {
 | 
					 | 
				
			||||||
		if c == utf8.RuneError {
 | 
					 | 
				
			||||||
			// If we get a UTF-8 error then there might be
 | 
					 | 
				
			||||||
			// upper-case ASCII bytes in the invalid sequence.
 | 
					 | 
				
			||||||
			isAlreadyLowerCase = false
 | 
					 | 
				
			||||||
			break
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if 'A' <= c && c <= 'Z' {
 | 
					 | 
				
			||||||
			isAlreadyLowerCase = false
 | 
					 | 
				
			||||||
			break
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if isAlreadyLowerCase {
 | 
					 | 
				
			||||||
		return in
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	out := []byte(in)
 | 
					 | 
				
			||||||
	for i, c := range out {
 | 
					 | 
				
			||||||
		if 'A' <= c && c <= 'Z' {
 | 
					 | 
				
			||||||
			out[i] += 'a' - 'A'
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return string(out)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// verifyHostname returns nil if c is a valid certificate for the named host.
 | 
					 | 
				
			||||||
// Otherwise it returns an error describing the mismatch.
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// IP addresses can be optionally enclosed in square brackets and are checked
 | 
					 | 
				
			||||||
// against the IPAddresses field. Other names are checked case insensitively
 | 
					 | 
				
			||||||
// against the DNSNames field. If the names are valid hostnames, the certificate
 | 
					 | 
				
			||||||
// fields can have a wildcard as the left-most label.
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// The legacy Common Name field is ignored unless it's a valid hostname, the
 | 
					 | 
				
			||||||
// certificate doesn't have any Subject Alternative Names, and the GODEBUG
 | 
					 | 
				
			||||||
// environment variable is set to "x509ignoreCN=0". Support for Common Name is
 | 
					 | 
				
			||||||
// deprecated will be entirely removed in the future.
 | 
					 | 
				
			||||||
func verifyHostname(c *x509.Certificate, h string) error {
 | 
					 | 
				
			||||||
	// IP addresses may be written in [ ].
 | 
					 | 
				
			||||||
	candidateIP := h
 | 
					 | 
				
			||||||
	if len(h) >= 3 && h[0] == '[' && h[len(h)-1] == ']' {
 | 
					 | 
				
			||||||
		candidateIP = h[1 : len(h)-1]
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if ip := net.ParseIP(candidateIP); ip != nil {
 | 
					 | 
				
			||||||
		// We only match IP addresses against IP SANs.
 | 
					 | 
				
			||||||
		// See RFC 6125, Appendix B.2.
 | 
					 | 
				
			||||||
		for _, candidate := range c.IPAddresses {
 | 
					 | 
				
			||||||
			if ip.Equal(candidate) {
 | 
					 | 
				
			||||||
				return nil
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return x509.HostnameError{c, candidateIP}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	names := c.DNSNames
 | 
					 | 
				
			||||||
	if commonNameAsHostname(c) {
 | 
					 | 
				
			||||||
		names = []string{c.Subject.CommonName}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	candidateName := toLowerCaseASCII(h) // Save allocations inside the loop.
 | 
					 | 
				
			||||||
	validCandidateName := validHostnameInput(candidateName)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for _, match := range names {
 | 
					 | 
				
			||||||
		// Ideally, we'd only match valid hostnames according to RFC 6125 like
 | 
					 | 
				
			||||||
		// browsers (more or less) do, but in practice Go is used in a wider
 | 
					 | 
				
			||||||
		// array of contexts and can't even assume DNS resolution. Instead,
 | 
					 | 
				
			||||||
		// always allow perfect matches, and only apply wildcard and trailing
 | 
					 | 
				
			||||||
		// dot processing to valid hostnames.
 | 
					 | 
				
			||||||
		if validCandidateName && validHostnamePattern(match) {
 | 
					 | 
				
			||||||
			if matchHostnames(match, candidateName) {
 | 
					 | 
				
			||||||
				return nil
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			if matchExactly(match, candidateName) {
 | 
					 | 
				
			||||||
				return nil
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return x509.HostnameError{c, h}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user