This repository has been archived by the owner on Jun 14, 2022. It is now read-only.
/
ssl.go
163 lines (142 loc) · 4.68 KB
/
ssl.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
package ftauth
import (
"crypto/tls"
"crypto/x509"
"errors"
"strings"
"sync"
)
var (
errInvalidCertificate = errors.New("invalid certificate")
)
// The main certificate repository.
var (
certRepo = &CertificateRepository{}
defaultConfiguration = &SecurityConfiguration{
TrustPublicPKI: true,
}
defaultConfigurationKey = "default"
)
// CertificateRepository holds a map of hosts to certificate pools
// for use with TLS handshake verification (i.e. certificate pinning).
type CertificateRepository struct {
m sync.Map
}
// GetCertificateRepository returns the main certificate repo for adding/removing
// security configurations.
func GetCertificateRepository() *CertificateRepository {
return certRepo
}
// GetDefaultConfiguration returns the default security configuration, i.e. the configuration
// used when a server's configuration has not been explicitly set.
func (cr *CertificateRepository) GetDefaultConfiguration() *SecurityConfiguration {
sc := cr.GetSecurityConfiguration(defaultConfigurationKey)
if sc == nil {
return defaultConfiguration
}
return sc
}
// SetDefaultConfiguration sets the default security configuration, i.e. the configuration
// used when a server's configuration has not been explicitly set.
func (cr *CertificateRepository) SetDefaultConfiguration(sc *SecurityConfiguration) {
cr.m.Store(defaultConfigurationKey, sc)
}
// AddSecurityConfiguration configures the TLS client for request to the specified host.
func (cr *CertificateRepository) AddSecurityConfiguration(sc *SecurityConfiguration) {
cr.m.Store(sc.Host, sc)
}
// GetSecurityConfiguration returns the stored configuration for the given host, returning
// nil if not found.
func (cr *CertificateRepository) GetSecurityConfiguration(host string) *SecurityConfiguration {
if secConf, loaded := cr.m.Load(host); loaded {
switch secConf.(type) {
case *SecurityConfiguration:
return secConf.(*SecurityConfiguration)
}
}
return nil
}
// RemoveSecurityConfiguration resets the security configuration for the host, using
// the default security configuration instead.
func (cr *CertificateRepository) RemoveSecurityConfiguration(host string) {
cr.m.Delete(host)
}
// SecurityConfiguration holds a host-specific configuration for the
// rules to use when verifying a TLS handshake.
type SecurityConfiguration struct {
Host string // e.g. google.com
TrustPublicPKI bool
intermediates *x509.CertPool
}
// NewSecurityConfiguration creates a new configuration object for the given host.
// Must call CertficateRepository.AddSecurityConfiguration() for it to take effect.
func NewSecurityConfiguration(host string, trustPublicPKI bool) *SecurityConfiguration {
secConf := &SecurityConfiguration{
Host: host,
TrustPublicPKI: trustPublicPKI,
intermediates: x509.NewCertPool(),
}
return secConf
}
// AddIntermediatePEM pins the intermediate certificate(s) (in PEM format),
// adding them to the list of verified certificates for the host in this
// configuration.
func (sc *SecurityConfiguration) AddIntermediatePEM(pem []byte) error {
ok := sc.intermediates.AppendCertsFromPEM(pem)
if !ok {
return errInvalidCertificate
}
return nil
}
// AddIntermediateASN1 pins the intermediate certificate (in ASN1 DER format),
// adding it to the list of verified certificates for the host in this
// configuration.
func (sc *SecurityConfiguration) AddIntermediateASN1(asn1 []byte) error {
cert, err := x509.ParseCertificate(asn1)
if err != nil {
return err
}
sc.intermediates.AddCert(cert)
return nil
}
// ResetPinning removes all intermediate certs and resets TrustSystemRoots to true.
func (sc *SecurityConfiguration) ResetPinning() {
sc.intermediates = x509.NewCertPool()
sc.TrustPublicPKI = true
}
func createTLSConfig() *tls.Config {
return &tls.Config{
VerifyConnection: func(cs tls.ConnectionState) error {
opts := x509.VerifyOptions{
DNSName: cs.ServerName,
Intermediates: x509.NewCertPool(),
}
host := getHostnameFromDNSName(cs.ServerName)
sc := certRepo.GetSecurityConfiguration(host)
if sc == nil {
sc = certRepo.GetDefaultConfiguration()
}
if sc.TrustPublicPKI || len(sc.intermediates.Subjects()) == 0 {
for _, cert := range cs.PeerCertificates[1:] {
opts.Intermediates.AddCert(cert)
}
} else {
opts.Intermediates = sc.intermediates
}
_, err := cs.PeerCertificates[0].Verify(opts)
return err
},
}
}
// getHostnameFromDNSName returns the host name used as the key
// in the certificate repo.
func getHostnameFromDNSName(dnsName string) string {
fields := strings.Split(dnsName, ".")
if len(fields) == 0 {
return dnsName
}
if fields[0] == "www" || fields[0] == "*" {
return strings.Join(fields[1:], ".")
}
return dnsName
}