@@ -3,18 +3,26 @@ package pq
33import (
44 "crypto/tls"
55 "crypto/x509"
6+ "crypto/x509/pkix"
67 "io/ioutil"
78 "net"
89 "os"
910 "os/user"
1011 "path/filepath"
12+ "time"
1113)
1214
15+ type tlsConfWithCrl struct {
16+ tls.Config
17+
18+ crl * pkix.CertificateList
19+ }
20+
1321// ssl generates a function to upgrade a net.Conn based on the "sslmode" and
1422// related settings. The function is nil when no upgrade should take place.
1523func ssl (o values ) (func (net.Conn ) (net.Conn , error ), error ) {
1624 verifyCaOnly := false
17- tlsConf := tls. Config {}
25+ tlsConf := tlsConfWithCrl {}
1826 switch mode := o ["sslmode" ]; mode {
1927 // "require" is the default.
2028 case "" , "require" :
@@ -67,12 +75,9 @@ func ssl(o values) (func(net.Conn) (net.Conn, error), error) {
6775 tlsConf .Renegotiation = tls .RenegotiateFreelyAsClient
6876
6977 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
7681 }
7782 return client , nil
7883 }, nil
@@ -82,7 +87,7 @@ func ssl(o values) (func(net.Conn) (net.Conn, error), error) {
8287// "sslkey" settings, or if they aren't set, from the .postgresql directory
8388// in the user's home directory. The configured files must exist and have
8489// the correct permissions.
85- func sslClientCertificates (tlsConf * tls. Config , o values ) error {
90+ func sslClientCertificates (tlsConf * tlsConfWithCrl , o values ) error {
8691 sslinline := o ["sslinline" ]
8792 if sslinline == "true" {
8893 cert , err := tls .X509KeyPair ([]byte (o ["sslcert" ]), []byte (o ["sslkey" ]))
@@ -98,6 +103,23 @@ func sslClientCertificates(tlsConf *tls.Config, o values) error {
98103 // know from where to load them.
99104 user , _ := user .Current ()
100105
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+
101123 // In libpq, the client certificate is only loaded if the setting is not blank.
102124 //
103125 // 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 {
140162}
141163
142164// 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 {
144166 // In libpq, the root certificate is only loaded if the setting is not blank.
145167 //
146168 // 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 {
168190 return nil
169191}
170192
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 {
175199 err := client .Handshake ()
176200 if err != nil {
177201 return err
178202 }
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+ }
184222 }
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+ }
188258 }
189- opts .Intermediates .AddCert (cert )
190259 }
191- _ , err = certs [ 0 ]. Verify ( opts )
192- return err
260+
261+ return nil
193262}
0 commit comments