@@ -3,18 +3,26 @@ package pq
3
3
import (
4
4
"crypto/tls"
5
5
"crypto/x509"
6
+ "crypto/x509/pkix"
6
7
"io/ioutil"
7
8
"net"
8
9
"os"
9
10
"os/user"
10
11
"path/filepath"
12
+ "time"
11
13
)
12
14
15
+ type tlsConfWithCrl struct {
16
+ tls.Config
17
+
18
+ crl * pkix.CertificateList
19
+ }
20
+
13
21
// ssl generates a function to upgrade a net.Conn based on the "sslmode" and
14
22
// related settings. The function is nil when no upgrade should take place.
15
23
func ssl (o values ) (func (net.Conn ) (net.Conn , error ), error ) {
16
24
verifyCaOnly := false
17
- tlsConf := tls. Config {}
25
+ tlsConf := tlsConfWithCrl {}
18
26
switch mode := o ["sslmode" ]; mode {
19
27
// "require" is the default.
20
28
case "" , "require" :
@@ -67,12 +75,9 @@ func ssl(o values) (func(net.Conn) (net.Conn, error), error) {
67
75
tlsConf .Renegotiation = tls .RenegotiateFreelyAsClient
68
76
69
77
return func (conn net.Conn ) (net.Conn , error ) {
70
- client := tls .Client (conn , & tlsConf )
71
- if verifyCaOnly {
72
- err := sslVerifyCertificateAuthority (client , & tlsConf )
73
- if err != nil {
74
- return nil , err
75
- }
78
+ client := tls .Client (conn , & tlsConf .Config )
79
+ if err := sslVerifyExtra (client , & tlsConf , verifyCaOnly ); err != nil {
80
+ return nil , err
76
81
}
77
82
return client , nil
78
83
}, nil
@@ -82,7 +87,7 @@ func ssl(o values) (func(net.Conn) (net.Conn, error), error) {
82
87
// "sslkey" settings, or if they aren't set, from the .postgresql directory
83
88
// in the user's home directory. The configured files must exist and have
84
89
// the correct permissions.
85
- func sslClientCertificates (tlsConf * tls. Config , o values ) error {
90
+ func sslClientCertificates (tlsConf * tlsConfWithCrl , o values ) error {
86
91
sslinline := o ["sslinline" ]
87
92
if sslinline == "true" {
88
93
cert , err := tls .X509KeyPair ([]byte (o ["sslcert" ]), []byte (o ["sslkey" ]))
@@ -98,6 +103,23 @@ func sslClientCertificates(tlsConf *tls.Config, o values) error {
98
103
// know from where to load them.
99
104
user , _ := user .Current ()
100
105
106
+ // Load CRL, https://github.com/postgres/postgres/blob/REL9_6_2/src/interfaces/libpq/fe-secure-openssl.c#L974
107
+ sslcrl := o ["sslcrl" ]
108
+ if len (sslcrl ) == 0 && user != nil {
109
+ sslcrl = filepath .Join (user .HomeDir , ".postgresql" , "root.crl" )
110
+ }
111
+ if len (sslcrl ) > 0 {
112
+ crlcontent , err := ioutil .ReadFile (sslcrl )
113
+ if err != nil && ! os .IsNotExist (err ) { // https://github.com/postgres/postgres/blob/REL9_6_2/src/interfaces/libpq/fe-secure-openssl.c#L1002
114
+ return err
115
+ } else if err == nil {
116
+ tlsConf .crl , err = x509 .ParseCRL (crlcontent )
117
+ if err != nil {
118
+ return err
119
+ }
120
+ }
121
+ }
122
+
101
123
// In libpq, the client certificate is only loaded if the setting is not blank.
102
124
//
103
125
// https://github.com/postgres/postgres/blob/REL9_6_2/src/interfaces/libpq/fe-secure-openssl.c#L1036-L1037
@@ -140,7 +162,7 @@ func sslClientCertificates(tlsConf *tls.Config, o values) error {
140
162
}
141
163
142
164
// sslCertificateAuthority adds the RootCA specified in the "sslrootcert" setting.
143
- func sslCertificateAuthority (tlsConf * tls. Config , o values ) error {
165
+ func sslCertificateAuthority (tlsConf * tlsConfWithCrl , o values ) error {
144
166
// In libpq, the root certificate is only loaded if the setting is not blank.
145
167
//
146
168
// https://github.com/postgres/postgres/blob/REL9_6_2/src/interfaces/libpq/fe-secure-openssl.c#L950-L951
@@ -168,26 +190,73 @@ func sslCertificateAuthority(tlsConf *tls.Config, o values) error {
168
190
return nil
169
191
}
170
192
171
- // sslVerifyCertificateAuthority carries out a TLS handshake to the server and
172
- // verifies the presented certificate against the CA, i.e. the one specified in
173
- // sslrootcert or the system CA if sslrootcert was not specified.
174
- func sslVerifyCertificateAuthority (client * tls.Conn , tlsConf * tls.Config ) error {
193
+ // sslVerifyExtra carries out a TLS handshake to the server and
194
+ // carries out extra verification that Go's TLS package doesn't do:
195
+ // * if verifyCaOnly is true, verifies the presented certificate against the CA,
196
+ // i.e. the one specified in sslrootcert or the system CA if sslrootcert was not specified.
197
+ // * verifies the PeerCertificates against CRL if sslcrl was specified
198
+ func sslVerifyExtra (client * tls.Conn , tlsConf * tlsConfWithCrl , verifyCaOnly bool ) error {
175
199
err := client .Handshake ()
176
200
if err != nil {
177
201
return err
178
202
}
179
- certs := client .ConnectionState ().PeerCertificates
180
- opts := x509.VerifyOptions {
181
- DNSName : client .ConnectionState ().ServerName ,
182
- Intermediates : x509 .NewCertPool (),
183
- Roots : tlsConf .RootCAs ,
203
+
204
+ state := client .ConnectionState ()
205
+ if verifyCaOnly {
206
+ opts := x509.VerifyOptions {
207
+ DNSName : client .ConnectionState ().ServerName ,
208
+ Intermediates : x509 .NewCertPool (),
209
+ Roots : tlsConf .RootCAs ,
210
+ }
211
+ for i , cert := range state .PeerCertificates {
212
+ if i == 0 {
213
+ continue
214
+ }
215
+ opts .Intermediates .AddCert (cert )
216
+ }
217
+
218
+ state .VerifiedChains , err = state .PeerCertificates [0 ].Verify (opts )
219
+ if err != nil {
220
+ return err
221
+ }
184
222
}
185
- for i , cert := range certs {
186
- if i == 0 {
187
- continue
223
+
224
+ if crl := tlsConf .crl ; crl != nil {
225
+ if crl .HasExpired (time .Now ()) {
226
+ return fmterrorf ("sslcrl has expired on %v" , crl .TBSCertList .NextUpdate )
227
+ }
228
+
229
+ crlVerified := false
230
+ crlIssuer := crl .TBSCertList .Issuer .String ()
231
+
232
+ VerifiedChainLoop:
233
+ for _ , chain := range state .VerifiedChains {
234
+ for i := len (chain ) - 1 ; i >= 0 ; i -- {
235
+ cert := chain [i ]
236
+ if cert .Subject .ToRDNSequence ().String () != crlIssuer {
237
+ continue
238
+ }
239
+
240
+ if err := cert .CheckCRLSignature (crl ); err != nil {
241
+ return fmterrorf ("sslcrl failed to verify with cert subject %s: %w" , cert .Subject .String (), err )
242
+ }
243
+ crlVerified = true
244
+ break VerifiedChainLoop
245
+ }
246
+ }
247
+
248
+ if ! crlVerified {
249
+ return fmterrorf ("sslcrl failed to verify with all root certificates." )
250
+ }
251
+
252
+ for _ , cert := range state .PeerCertificates {
253
+ for _ , revoked := range crl .TBSCertList .RevokedCertificates {
254
+ if cert .SerialNumber .Cmp (revoked .SerialNumber ) == 0 {
255
+ return fmterrorf ("certificate %s was revoked at %v" , cert .SerialNumber .String (), revoked .RevocationTime )
256
+ }
257
+ }
188
258
}
189
- opts .Intermediates .AddCert (cert )
190
259
}
191
- _ , err = certs [ 0 ]. Verify ( opts )
192
- return err
260
+
261
+ return nil
193
262
}
0 commit comments