2020-04-25 20:24:53 +00:00
|
|
|
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
2020-04-27 15:17:18 +00:00
|
|
|
// Package tlsdial originally existed to set up a tls.Config for x509
|
|
|
|
// validation, using a memory-optimized path for iOS, but then we
|
|
|
|
// moved that to the tailscale/go tree instead, so now this package
|
|
|
|
// does very little. But for now we keep it as a unified point where
|
|
|
|
// we might want to add shared policy on outgoing TLS connections from
|
|
|
|
// the 3 places in the client that connect to Tailscale (logs,
|
|
|
|
// control, DERP).
|
2020-04-25 20:24:53 +00:00
|
|
|
package tlsdial
|
|
|
|
|
2020-06-01 16:01:37 +00:00
|
|
|
import (
|
|
|
|
"crypto/tls"
|
|
|
|
"crypto/x509"
|
|
|
|
"errors"
|
|
|
|
"time"
|
|
|
|
)
|
2020-04-25 20:24:53 +00:00
|
|
|
|
2020-06-01 16:01:37 +00:00
|
|
|
// Config returns a tls.Config for connecting to a server.
|
2020-04-25 20:24:53 +00:00
|
|
|
// If base is non-nil, it's cloned as the base config before
|
|
|
|
// being configured and returned.
|
|
|
|
func Config(host string, base *tls.Config) *tls.Config {
|
|
|
|
var conf *tls.Config
|
|
|
|
if base == nil {
|
|
|
|
conf = new(tls.Config)
|
|
|
|
} else {
|
|
|
|
conf = base.Clone()
|
|
|
|
}
|
|
|
|
conf.ServerName = host
|
|
|
|
|
|
|
|
return conf
|
|
|
|
}
|
2020-06-01 16:01:37 +00:00
|
|
|
|
|
|
|
// SetConfigExpectedCert modifies c to expect and verify that the server returns
|
|
|
|
// a certificate for the provided certDNSName.
|
|
|
|
func SetConfigExpectedCert(c *tls.Config, certDNSName string) {
|
|
|
|
if c.ServerName == certDNSName {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if c.ServerName == "" {
|
|
|
|
c.ServerName = certDNSName
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if c.VerifyPeerCertificate != nil {
|
|
|
|
panic("refusing to override tls.Config.VerifyPeerCertificate")
|
|
|
|
}
|
|
|
|
// Set InsecureSkipVerify to prevent crypto/tls from doing its
|
|
|
|
// own cert verification, but do the same work that it'd do
|
|
|
|
// (but using certDNSName) in the VerifyPeerCertificate hook.
|
|
|
|
c.InsecureSkipVerify = true
|
|
|
|
c.VerifyPeerCertificate = func(rawCerts [][]byte, _ [][]*x509.Certificate) error {
|
|
|
|
if len(rawCerts) == 0 {
|
|
|
|
return errors.New("no certs presented")
|
|
|
|
}
|
|
|
|
certs := make([]*x509.Certificate, len(rawCerts))
|
|
|
|
for i, asn1Data := range rawCerts {
|
|
|
|
cert, err := x509.ParseCertificate(asn1Data)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
certs[i] = cert
|
|
|
|
}
|
|
|
|
opts := x509.VerifyOptions{
|
|
|
|
CurrentTime: time.Now(),
|
|
|
|
DNSName: certDNSName,
|
|
|
|
Intermediates: x509.NewCertPool(),
|
|
|
|
}
|
|
|
|
for _, cert := range certs[1:] {
|
|
|
|
opts.Intermediates.AddCert(cert)
|
|
|
|
}
|
|
|
|
_, err := certs[0].Verify(opts)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|