Skip to content

Commit 08f8950

Browse files
committed
Allow for client certs signed by an intermediate CA.
Before this change, client certs signed by an intermediate CA, when the server only trusts the root CA, were not handled correctly. Add a unit test for this situation (which now passes) as well as tests for other intermediate CA situations (which passed before this change and continue to pass afterwards.) Fixes #1266.
1 parent 89e7b23 commit 08f8950

File tree

2 files changed

+466
-0
lines changed

2 files changed

+466
-0
lines changed

ssl.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package pq
22

33
import (
4+
"bytes"
45
"crypto/tls"
56
"crypto/x509"
7+
"encoding/pem"
68
"errors"
79
"fmt"
810
"net"
@@ -123,6 +125,7 @@ func ssl(cfg Config) (func(net.Conn) (net.Conn, error), error) {
123125
if err != nil {
124126
return nil, err
125127
}
128+
sslAppendIntermediates(tlsConf, cfg)
126129

127130
// Accept renegotiation requests initiated by the backend.
128131
//
@@ -222,6 +225,46 @@ func sslClientCertificates(tlsConf *tls.Config, cfg Config) error {
222225
return nil
223226
}
224227

228+
// sslAppendIntermediates appends intermediate CA certificates from sslrootcert
229+
// to the client certificate chain. This is needed so the server can verify the
230+
// client cert when it was signed by an intermediate CA — without this, the TLS
231+
// handshake only sends the leaf client cert.
232+
func sslAppendIntermediates(tlsConf *tls.Config, cfg Config) {
233+
if len(tlsConf.Certificates) == 0 || cfg.SSLRootCert == "" {
234+
return
235+
}
236+
237+
var pemData []byte
238+
if cfg.SSLInline {
239+
pemData = []byte(cfg.SSLRootCert)
240+
} else {
241+
var err error
242+
pemData, err = os.ReadFile(cfg.SSLRootCert)
243+
if err != nil {
244+
return
245+
}
246+
}
247+
248+
for {
249+
var block *pem.Block
250+
block, pemData = pem.Decode(pemData)
251+
if block == nil {
252+
break
253+
}
254+
if block.Type != "CERTIFICATE" {
255+
continue
256+
}
257+
cert, err := x509.ParseCertificate(block.Bytes)
258+
if err != nil {
259+
continue
260+
}
261+
// Skip self-signed root CAs; only append intermediates.
262+
if cert.IsCA && !bytes.Equal(cert.RawIssuer, cert.RawSubject) {
263+
tlsConf.Certificates[0].Certificate = append(tlsConf.Certificates[0].Certificate, block.Bytes)
264+
}
265+
}
266+
}
267+
225268
// sslCertificateAuthority adds the RootCA specified in the "sslrootcert" setting.
226269
func sslCertificateAuthority(tlsConf *tls.Config, cfg Config) error {
227270
// In libpq, the root certificate is only loaded if the setting is not blank.

0 commit comments

Comments
 (0)