MITM: Allow using local received SNI in the outgoing serverName & verifyPeerCertInNames

https://github.com/XTLS/Xray-core/issues/4348#issuecomment-2637370175

Local received SNI was sent by browser/app.

In freedom RAW's `tlsSettings`, set `"serverName": "fromMitm"` to forward it to the real website.

In freedom RAW's `tlsSettings`, set `"verifyPeerCertInNames": ["fromMitm"]` to use all possible names to verify the certificate.
This commit is contained in:
RPRX 2025-02-06 07:37:30 +00:00 committed by GitHub
parent 9b7841178a
commit c6a31f457c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 150 additions and 85 deletions

View File

@ -24,6 +24,7 @@ const (
allowedNetworkKey ctx.SessionKey = 9
handlerSessionKey ctx.SessionKey = 10
mitmAlpn11Key ctx.SessionKey = 11
mitmServerNameKey ctx.SessionKey = 12
)
func ContextWithInbound(ctx context.Context, inbound *Inbound) context.Context {
@ -174,3 +175,14 @@ func MitmAlpn11FromContext(ctx context.Context) bool {
}
return false
}
func ContextWithMitmServerName(ctx context.Context, serverName string) context.Context {
return context.WithValue(ctx, mitmServerNameKey, serverName)
}
func MitmServerNameFromContext(ctx context.Context) string {
if val, ok := ctx.Value(mitmServerNameKey).(string); ok {
return val
}
return ""
}

View File

@ -411,6 +411,7 @@ type TLSConfig struct {
CurvePreferences *StringList `json:"curvePreferences"`
MasterKeyLog string `json:"masterKeyLog"`
ServerNameToVerify string `json:"serverNameToVerify"`
VerifyPeerCertInNames []string `json:"verifyPeerCertInNames"`
}
// Build implements Buildable.
@ -469,10 +470,11 @@ func (c *TLSConfig) Build() (proto.Message, error) {
}
config.MasterKeyLog = c.MasterKeyLog
config.ServerNameToVerify = c.ServerNameToVerify
if config.ServerNameToVerify != "" && config.Fingerprint == "unsafe" {
return nil, errors.New(`serverNameToVerify only works with uTLS for now`)
if c.ServerNameToVerify != "" {
return nil, errors.PrintRemovedFeatureError("serverNameToVerify", "verifyPeerCertInNames")
}
config.VerifyPeerCertInNames = c.VerifyPeerCertInNames
return config, nil
}

View File

@ -64,10 +64,6 @@ func (d *DokodemoDoor) policy() policy.Session {
return p
}
type hasHandshakeAddressContext interface {
HandshakeAddressContext(ctx context.Context) net.Address
}
// Process implements proxy.Inbound.
func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn stat.Connection, dispatcher routing.Dispatcher) error {
errors.LogDebug(ctx, "processing connection from: ", conn.RemoteAddr())
@ -87,14 +83,14 @@ func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn st
destinationOverridden = true
}
}
if handshake, ok := conn.(hasHandshakeAddressContext); ok && !destinationOverridden {
addr := handshake.HandshakeAddressContext(ctx)
if addr != nil {
dest.Address = addr
if conn.(*tls.Conn).ConnectionState().NegotiatedProtocol == "http/1.1" {
ctx = session.ContextWithMitmAlpn11(ctx, true)
}
if tlsConn, ok := conn.(tls.Interface); ok && !destinationOverridden {
if serverName := tlsConn.HandshakeContextServerName(ctx); serverName != "" {
dest.Address = net.DomainAddress(serverName)
destinationOverridden = true
ctx = session.ContextWithMitmServerName(ctx, serverName)
}
if tlsConn.NegotiatedProtocol() == "http/1.1" {
ctx = session.ContextWithMitmAlpn11(ctx, true)
}
}
}

View File

@ -14,6 +14,10 @@ import (
"github.com/xtls/xray-core/transport/internet/tls"
)
func IsFromMitm(str string) bool {
return strings.ToLower(str) == "frommitm"
}
// Dial dials a new TCP connection to the given destination.
func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (stat.Connection, error) {
errors.LogInfo(ctx, "dialing TCP to ", dest)
@ -23,11 +27,28 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
}
if config := tls.ConfigFromStreamSettings(streamSettings); config != nil {
mitmServerName := session.MitmServerNameFromContext(ctx)
mitmAlpn11 := session.MitmAlpn11FromContext(ctx)
tlsConfig := config.GetTLSConfig(tls.WithDestination(dest))
if IsFromMitm(tlsConfig.ServerName) {
tlsConfig.ServerName = mitmServerName
}
if r, ok := tlsConfig.Rand.(*tls.RandCarrier); ok && len(r.VerifyPeerCertInNames) > 0 && IsFromMitm(r.VerifyPeerCertInNames[0]) {
r.VerifyPeerCertInNames = r.VerifyPeerCertInNames[1:]
after := mitmServerName
for {
if len(after) > 0 {
r.VerifyPeerCertInNames = append(r.VerifyPeerCertInNames, after)
}
_, after, _ = strings.Cut(after, ".")
if !strings.Contains(after, ".") {
break
}
}
}
if fingerprint := tls.GetFingerprint(config.Fingerprint); fingerprint != nil {
conn = tls.UClient(conn, tlsConfig, fingerprint)
if len(tlsConfig.NextProtos) == 1 && (tlsConfig.NextProtos[0] == "http/1.1" ||
(strings.ToLower(tlsConfig.NextProtos[0]) == "frommitm" && session.MitmAlpn11FromContext(ctx))) {
if len(tlsConfig.NextProtos) == 1 && (tlsConfig.NextProtos[0] == "http/1.1" || (IsFromMitm(tlsConfig.NextProtos[0]) && mitmAlpn11)) {
if err := conn.(*tls.UConn).WebsocketHandshakeContext(ctx); err != nil {
return nil, err
}
@ -37,14 +58,17 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
}
}
} else {
if len(tlsConfig.NextProtos) == 1 && strings.ToLower(tlsConfig.NextProtos[0]) == "frommitm" {
if session.MitmAlpn11FromContext(ctx) {
tlsConfig.NextProtos = []string{"http/1.1"} // new slice
if len(tlsConfig.NextProtos) == 1 && IsFromMitm(tlsConfig.NextProtos[0]) {
if mitmAlpn11 {
tlsConfig.NextProtos[0] = "http/1.1"
} else {
tlsConfig.NextProtos = nil
}
}
conn = tls.Client(conn, tlsConfig)
if err := conn.(*tls.Conn).HandshakeContext(ctx); err != nil {
return nil, err
}
}
} else if config := reality.ConfigFromStreamSettings(streamSettings); config != nil {
if conn, err = reality.UClient(conn, config, ctx, dest); err != nil {

View File

@ -9,6 +9,7 @@ import (
"crypto/x509"
"encoding/base64"
"os"
"slices"
"strings"
"sync"
"time"
@ -277,10 +278,35 @@ func (c *Config) parseServerName() string {
return c.ServerName
}
func (c *Config) verifyPeerCert(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
if c.PinnedPeerCertificateChainSha256 != nil {
func (r *RandCarrier) verifyPeerCert(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
if r.VerifyPeerCertInNames != nil {
if len(r.VerifyPeerCertInNames) > 0 {
certs := make([]*x509.Certificate, len(rawCerts))
for i, asn1Data := range rawCerts {
certs[i], _ = x509.ParseCertificate(asn1Data)
}
opts := x509.VerifyOptions{
Roots: r.RootCAs,
CurrentTime: time.Now(),
Intermediates: x509.NewCertPool(),
}
for _, cert := range certs[1:] {
opts.Intermediates.AddCert(cert)
}
for _, opts.DNSName = range r.VerifyPeerCertInNames {
if _, err := certs[0].Verify(opts); err == nil {
return nil
}
}
}
if r.PinnedPeerCertificateChainSha256 == nil {
errors.New("peer cert is invalid.")
}
}
if r.PinnedPeerCertificateChainSha256 != nil {
hashValue := GenerateCertChainHash(rawCerts)
for _, v := range c.PinnedPeerCertificateChainSha256 {
for _, v := range r.PinnedPeerCertificateChainSha256 {
if hmac.Equal(hashValue, v) {
return nil
}
@ -288,11 +314,11 @@ func (c *Config) verifyPeerCert(rawCerts [][]byte, verifiedChains [][]*x509.Cert
return errors.New("peer cert is unrecognized: ", base64.StdEncoding.EncodeToString(hashValue))
}
if c.PinnedPeerCertificatePublicKeySha256 != nil {
if r.PinnedPeerCertificatePublicKeySha256 != nil {
for _, v := range verifiedChains {
for _, cert := range v {
publicHash := GenerateCertPublicKeyHash(cert)
for _, c := range c.PinnedPeerCertificatePublicKeySha256 {
for _, c := range r.PinnedPeerCertificatePublicKeySha256 {
if hmac.Equal(publicHash, c) {
return nil
}
@ -305,7 +331,10 @@ func (c *Config) verifyPeerCert(rawCerts [][]byte, verifiedChains [][]*x509.Cert
}
type RandCarrier struct {
ServerNameToVerify string
RootCAs *x509.CertPool
VerifyPeerCertInNames []string
PinnedPeerCertificateChainSha256 [][]byte
PinnedPeerCertificatePublicKeySha256 [][]byte
}
func (r *RandCarrier) Read(p []byte) (n int, err error) {
@ -329,16 +358,25 @@ func (c *Config) GetTLSConfig(opts ...Option) *tls.Config {
}
}
randCarrier := &RandCarrier{
RootCAs: root,
VerifyPeerCertInNames: slices.Clone(c.VerifyPeerCertInNames),
PinnedPeerCertificateChainSha256: c.PinnedPeerCertificateChainSha256,
PinnedPeerCertificatePublicKeySha256: c.PinnedPeerCertificatePublicKeySha256,
}
config := &tls.Config{
Rand: &RandCarrier{
ServerNameToVerify: c.ServerNameToVerify,
},
Rand: randCarrier,
ClientSessionCache: globalSessionCache,
RootCAs: root,
InsecureSkipVerify: c.AllowInsecure,
NextProtos: c.NextProtocol,
NextProtos: slices.Clone(c.NextProtocol),
SessionTicketsDisabled: !c.EnableSessionResumption,
VerifyPeerCertificate: c.verifyPeerCert,
VerifyPeerCertificate: randCarrier.verifyPeerCert,
}
if len(c.VerifyPeerCertInNames) > 0 {
config.InsecureSkipVerify = true
} else {
randCarrier.VerifyPeerCertInNames = nil
}
for _, opt := range opts {

View File

@ -202,20 +202,21 @@ type Config struct {
// TLS Client Hello fingerprint (uTLS).
Fingerprint string `protobuf:"bytes,11,opt,name=fingerprint,proto3" json:"fingerprint,omitempty"`
RejectUnknownSni bool `protobuf:"varint,12,opt,name=reject_unknown_sni,json=rejectUnknownSni,proto3" json:"reject_unknown_sni,omitempty"`
// @Document A pinned certificate chain sha256 hash.
// @Document If the server's hash does not match this value, the connection will be aborted.
// @Document This value replace allow_insecure.
// @Document Some certificate chain sha256 hashes.
// @Document After normal validation or allow_insecure, if the server's cert chain hash does not match any of these values, the connection will be aborted.
// @Critical
PinnedPeerCertificateChainSha256 [][]byte `protobuf:"bytes,13,rep,name=pinned_peer_certificate_chain_sha256,json=pinnedPeerCertificateChainSha256,proto3" json:"pinned_peer_certificate_chain_sha256,omitempty"`
// @Document A pinned certificate public key sha256 hash.
// @Document If the server's public key hash does not match this value, the connection will be aborted.
// @Document This value replace allow_insecure.
// @Document Some certificate public key sha256 hashes.
// @Document After normal validation (required), if the verified cert's public key hash does not match any of these values, the connection will be aborted.
// @Critical
PinnedPeerCertificatePublicKeySha256 [][]byte `protobuf:"bytes,14,rep,name=pinned_peer_certificate_public_key_sha256,json=pinnedPeerCertificatePublicKeySha256,proto3" json:"pinned_peer_certificate_public_key_sha256,omitempty"`
MasterKeyLog string `protobuf:"bytes,15,opt,name=master_key_log,json=masterKeyLog,proto3" json:"master_key_log,omitempty"`
// Lists of string as CurvePreferences values.
CurvePreferences []string `protobuf:"bytes,16,rep,name=curve_preferences,json=curvePreferences,proto3" json:"curve_preferences,omitempty"`
ServerNameToVerify string `protobuf:"bytes,17,opt,name=server_name_to_verify,json=serverNameToVerify,proto3" json:"server_name_to_verify,omitempty"`
CurvePreferences []string `protobuf:"bytes,16,rep,name=curve_preferences,json=curvePreferences,proto3" json:"curve_preferences,omitempty"`
// @Document Replaces server_name to verify the peer cert.
// @Document After allow_insecure (automatically), if the server's cert can't be verified by any of these names, pinned_peer_certificate_chain_sha256 will be tried.
// @Critical
VerifyPeerCertInNames []string `protobuf:"bytes,17,rep,name=verify_peer_cert_in_names,json=verifyPeerCertInNames,proto3" json:"verify_peer_cert_in_names,omitempty"`
}
func (x *Config) Reset() {
@ -353,11 +354,11 @@ func (x *Config) GetCurvePreferences() []string {
return nil
}
func (x *Config) GetServerNameToVerify() string {
func (x *Config) GetVerifyPeerCertInNames() []string {
if x != nil {
return x.ServerNameToVerify
return x.VerifyPeerCertInNames
}
return ""
return nil
}
var File_transport_internet_tls_config_proto protoreflect.FileDescriptor
@ -391,7 +392,7 @@ var file_transport_internet_tls_config_proto_rawDesc = []byte{
0x4e, 0x43, 0x49, 0x50, 0x48, 0x45, 0x52, 0x4d, 0x45, 0x4e, 0x54, 0x10, 0x00, 0x12, 0x14, 0x0a,
0x10, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x56, 0x45, 0x52, 0x49, 0x46,
0x59, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x54, 0x59,
0x5f, 0x49, 0x53, 0x53, 0x55, 0x45, 0x10, 0x02, 0x22, 0x93, 0x06, 0x0a, 0x06, 0x43, 0x6f, 0x6e,
0x5f, 0x49, 0x53, 0x53, 0x55, 0x45, 0x10, 0x02, 0x22, 0x9a, 0x06, 0x0a, 0x06, 0x43, 0x6f, 0x6e,
0x66, 0x69, 0x67, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x69, 0x6e, 0x73,
0x65, 0x63, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x61, 0x6c, 0x6c,
0x6f, 0x77, 0x49, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x12, 0x4a, 0x0a, 0x0b, 0x63, 0x65,
@ -437,18 +438,19 @@ var file_transport_internet_tls_config_proto_rawDesc = []byte{
0x52, 0x0c, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x4c, 0x6f, 0x67, 0x12, 0x2b,
0x0a, 0x11, 0x63, 0x75, 0x72, 0x76, 0x65, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e,
0x63, 0x65, 0x73, 0x18, 0x10, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x63, 0x75, 0x72, 0x76, 0x65,
0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x31, 0x0a, 0x15, 0x73,
0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x74, 0x6f, 0x5f, 0x76, 0x65,
0x72, 0x69, 0x66, 0x79, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x73, 0x65, 0x72, 0x76,
0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x54, 0x6f, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x42, 0x73,
0x0a, 0x1f, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73,
0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x74, 0x6c,
0x73, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x74,
0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65,
0x74, 0x2f, 0x74, 0x6c, 0x73, 0xaa, 0x02, 0x1b, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61,
0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e,
0x54, 0x6c, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x38, 0x0a, 0x19, 0x76,
0x65, 0x72, 0x69, 0x66, 0x79, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x5f,
0x69, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x11, 0x20, 0x03, 0x28, 0x09, 0x52, 0x15,
0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x50, 0x65, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74, 0x49, 0x6e,
0x4e, 0x61, 0x6d, 0x65, 0x73, 0x42, 0x73, 0x0a, 0x1f, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61,
0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65,
0x72, 0x6e, 0x65, 0x74, 0x2e, 0x74, 0x6c, 0x73, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68,
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79,
0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f,
0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x74, 0x6c, 0x73, 0xaa, 0x02, 0x1b, 0x58,
0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e,
0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x6c, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x33,
}
var (

View File

@ -69,16 +69,14 @@ message Config {
bool reject_unknown_sni = 12;
/* @Document A pinned certificate chain sha256 hash.
@Document If the server's hash does not match this value, the connection will be aborted.
@Document This value replace allow_insecure.
/* @Document Some certificate chain sha256 hashes.
@Document After normal validation or allow_insecure, if the server's cert chain hash does not match any of these values, the connection will be aborted.
@Critical
*/
repeated bytes pinned_peer_certificate_chain_sha256 = 13;
/* @Document A pinned certificate public key sha256 hash.
@Document If the server's public key hash does not match this value, the connection will be aborted.
@Document This value replace allow_insecure.
/* @Document Some certificate public key sha256 hashes.
@Document After normal validation (required), if the verified cert's public key hash does not match any of these values, the connection will be aborted.
@Critical
*/
repeated bytes pinned_peer_certificate_public_key_sha256 = 14;
@ -88,5 +86,9 @@ message Config {
// Lists of string as CurvePreferences values.
repeated string curve_preferences = 16;
string server_name_to_verify = 17;
/* @Document Replaces server_name to verify the peer cert.
@Document After allow_insecure (automatically), if the server's cert can't be verified by any of these names, pinned_peer_certificate_chain_sha256 will be tried.
@Critical
*/
repeated string verify_peer_cert_in_names = 17;
}

View File

@ -16,6 +16,7 @@ type Interface interface {
net.Conn
HandshakeContext(ctx context.Context) error
VerifyHostname(host string) error
HandshakeContextServerName(ctx context.Context) string
NegotiatedProtocol() string
}
@ -43,15 +44,11 @@ func (c *Conn) WriteMultiBuffer(mb buf.MultiBuffer) error {
return err
}
func (c *Conn) HandshakeAddressContext(ctx context.Context) net.Address {
func (c *Conn) HandshakeContextServerName(ctx context.Context) string {
if err := c.HandshakeContext(ctx); err != nil {
return nil
return ""
}
state := c.ConnectionState()
if state.ServerName == "" {
return nil
}
return net.ParseAddress(state.ServerName)
return c.ConnectionState().ServerName
}
func (c *Conn) NegotiatedProtocol() string {
@ -85,15 +82,11 @@ func (c *UConn) Close() error {
return c.Conn.Close()
}
func (c *UConn) HandshakeAddressContext(ctx context.Context) net.Address {
func (c *UConn) HandshakeContextServerName(ctx context.Context) string {
if err := c.HandshakeContext(ctx); err != nil {
return nil
return ""
}
state := c.ConnectionState()
if state.ServerName == "" {
return nil
}
return net.ParseAddress(state.ServerName)
return c.ConnectionState().ServerName
}
// WebsocketHandshake basically calls UConn.Handshake inside it but it will only send
@ -134,17 +127,13 @@ func UClient(c net.Conn, config *tls.Config, fingerprint *utls.ClientHelloID) ne
}
func copyConfig(c *tls.Config) *utls.Config {
serverNameToVerify := ""
if r, ok := c.Rand.(*RandCarrier); ok {
serverNameToVerify = r.ServerNameToVerify
}
return &utls.Config{
RootCAs: c.RootCAs,
ServerName: c.ServerName,
InsecureSkipVerify: c.InsecureSkipVerify,
VerifyPeerCertificate: c.VerifyPeerCertificate,
KeyLogWriter: c.KeyLogWriter,
InsecureServerNameToVerify: serverNameToVerify,
Rand: c.Rand,
RootCAs: c.RootCAs,
ServerName: c.ServerName,
InsecureSkipVerify: c.InsecureSkipVerify,
VerifyPeerCertificate: c.VerifyPeerCertificate,
KeyLogWriter: c.KeyLogWriter,
}
}