From a20eb4660bb364bd2078b5db78f09d9de1bf915e Mon Sep 17 00:00:00 2001 From: luyuhuang Date: Sat, 5 Sep 2020 11:52:33 +0800 Subject: [PATCH] certificate pinning for servers without domain --- cmd/gost/route.go | 27 +++++++++++++++++++++ tls.go | 61 +++++++++++++++++++++++++---------------------- 2 files changed, 59 insertions(+), 29 deletions(-) diff --git a/cmd/gost/route.go b/cmd/gost/route.go index f64056b..0db5b2e 100644 --- a/cmd/gost/route.go +++ b/cmd/gost/route.go @@ -3,12 +3,14 @@ package main import ( "crypto/sha256" "crypto/tls" + "crypto/x509" "encoding/base64" "fmt" "net" "net/url" "os" "strings" + "time" "github.com/ginuerzh/gost" "github.com/go-log/log" @@ -128,6 +130,31 @@ func parseChainNode(ns string) (nodes []gost.Node, err error) { InsecureSkipVerify: !node.GetBool("secure"), RootCAs: rootCAs, } + + // If the argument `ca` is given, but not open `secure`, we verify the + // certificate manually. + if rootCAs != nil && !node.GetBool("secure") { + tlsCfg.VerifyConnection = func(state tls.ConnectionState) error { + opts := x509.VerifyOptions{ + Roots: rootCAs, + CurrentTime: time.Now(), + DNSName: "", + Intermediates: x509.NewCertPool(), + } + + certs := state.PeerCertificates + for i, cert := range certs { + if i == 0 { + continue + } + opts.Intermediates.AddCert(cert) + } + + _, err = certs[0].Verify(opts) + return err + } + } + if cert, err := tls.LoadX509KeyPair(node.Get("cert"), node.Get("key")); err == nil { tlsCfg.Certificates = []tls.Certificate{cert} } diff --git a/tls.go b/tls.go index 44faff6..c1b9684 100644 --- a/tls.go +++ b/tls.go @@ -2,7 +2,6 @@ package gost import ( "crypto/tls" - "crypto/x509" "errors" "net" "sync" @@ -290,36 +289,40 @@ func wrapTLSClient(conn net.Conn, tlsConfig *tls.Config, timeout time.Duration) return nil, err } - // If crypto/tls is doing verification, there's no need to do our own. - if tlsConfig.InsecureSkipVerify == false { - return tlsConn, nil - } - - // Similarly if we use host's CA, we can do full handshake - if tlsConfig.RootCAs == nil { - return tlsConn, nil - } - - opts := x509.VerifyOptions{ - Roots: tlsConfig.RootCAs, - CurrentTime: time.Now(), - DNSName: "", - Intermediates: x509.NewCertPool(), - } - - certs := tlsConn.ConnectionState().PeerCertificates - for i, cert := range certs { - if i == 0 { - continue + // We can do this in `tls.Config.VerifyConnection`, which effective for + // other TLS protocols such as WebSocket. See `route.go:parseChainNode` + /* + // If crypto/tls is doing verification, there's no need to do our own. + if tlsConfig.InsecureSkipVerify == false { + return tlsConn, nil } - opts.Intermediates.AddCert(cert) - } - _, err = certs[0].Verify(opts) - if err != nil { - tlsConn.Close() - return nil, err - } + // Similarly if we use host's CA, we can do full handshake + if tlsConfig.RootCAs == nil { + return tlsConn, nil + } + + opts := x509.VerifyOptions{ + Roots: tlsConfig.RootCAs, + CurrentTime: time.Now(), + DNSName: "", + Intermediates: x509.NewCertPool(), + } + + certs := tlsConn.ConnectionState().PeerCertificates + for i, cert := range certs { + if i == 0 { + continue + } + opts.Intermediates.AddCert(cert) + } + + _, err = certs[0].Verify(opts) + if err != nil { + tlsConn.Close() + return nil, err + } + */ return tlsConn, err }