|
1 | 1 | package pq |
2 | 2 |
|
3 | 3 | import ( |
| 4 | + "bytes" |
4 | 5 | "crypto/tls" |
5 | 6 | "crypto/x509" |
| 7 | + "encoding/pem" |
6 | 8 | "errors" |
7 | 9 | "fmt" |
8 | 10 | "net" |
@@ -123,6 +125,7 @@ func ssl(cfg Config) (func(net.Conn) (net.Conn, error), error) { |
123 | 125 | if err != nil { |
124 | 126 | return nil, err |
125 | 127 | } |
| 128 | + sslAppendIntermediates(tlsConf, cfg) |
126 | 129 |
|
127 | 130 | // Accept renegotiation requests initiated by the backend. |
128 | 131 | // |
@@ -222,6 +225,46 @@ func sslClientCertificates(tlsConf *tls.Config, cfg Config) error { |
222 | 225 | return nil |
223 | 226 | } |
224 | 227 |
|
| 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 | + |
225 | 268 | // sslCertificateAuthority adds the RootCA specified in the "sslrootcert" setting. |
226 | 269 | func sslCertificateAuthority(tlsConf *tls.Config, cfg Config) error { |
227 | 270 | // In libpq, the root certificate is only loaded if the setting is not blank. |
|
0 commit comments