@marcel91200 / caddy Mirror · read-only / commits / 6c675e29
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 11, 11:00 UTC

caddytls: Fix client auth (fix #7724) (#7727)

The peer certificates should be loaded even if existingVerifyPeerCert is nil. Patched with the assistance of Copilot, as an experiment.

Matt Holt committed May 14, 2026 at 10:05 UTC 6c675e29f87cbe7326983ddb6d739175119d394c
2 files changed +65 -5
modules/caddytls/connpolicy.go
+6 -5
@@ -896,18 +896,19 @@ func (clientauth *ClientAuthentication) ConfigureTLSConfig(cfg *tls.Config) erro
896 // Unlike VerifyPeerCertificate, VerifyConnection is called on every
897 // connection including resumed sessions, preventing session-resumption bypass.
898 func (clientauth *ClientAuthentication) verifyConnection(cs tls.ConnectionState) error {
899 + rawCerts := make([][]byte, len(cs.PeerCertificates))
900 + for i, cert := range cs.PeerCertificates {
901 + rawCerts[i] = cert.Raw
902 + }
903 +
904 // first use any pre-existing custom verification function
905 if clientauth.existingVerifyPeerCert != nil {
901 - rawCerts := make([][]byte, len(cs.PeerCertificates))
902 - for i, cert := range cs.PeerCertificates {
903 - rawCerts[i] = cert.Raw
904 - }
906 if err := clientauth.existingVerifyPeerCert(rawCerts, cs.VerifiedChains); err != nil {
907 return err
908 }
909 }
910 for _, verifier := range clientauth.verifiers {
910 - if err := verifier.VerifyClientCertificate(nil, cs.VerifiedChains); err != nil {
911 + if err := verifier.VerifyClientCertificate(rawCerts, cs.VerifiedChains); err != nil {
912 return err
913 }
914 }
modules/caddytls/connpolicy_verifyconnection_test.go new
+59
@@ -0,0 +1,59 @@
1 +package caddytls
2 +
3 +import (
4 + "crypto/tls"
5 + "crypto/x509"
6 + "errors"
7 + "reflect"
8 + "testing"
9 +)
10 +
11 +type testClientCertificateVerifier struct {
12 + rawCerts [][]byte
13 + verifiedChains [][]*x509.Certificate
14 + err error
15 +}
16 +
17 +func (v *testClientCertificateVerifier) VerifyClientCertificate(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
18 + v.rawCerts = rawCerts
19 + v.verifiedChains = verifiedChains
20 + return v.err
21 +}
22 +
23 +func TestClientAuthenticationVerifyConnectionPassesRawCertsToVerifiers(t *testing.T) {
24 + verifier := &testClientCertificateVerifier{}
25 + clientauth := &ClientAuthentication{
26 + verifiers: []ClientCertificateVerifier{verifier},
27 + }
28 +
29 + peerCert := &x509.Certificate{Raw: []byte("peer-cert-raw")}
30 + verifiedChains := [][]*x509.Certificate{{peerCert}}
31 + connState := tls.ConnectionState{
32 + PeerCertificates: []*x509.Certificate{peerCert},
33 + VerifiedChains: verifiedChains,
34 + }
35 +
36 + if err := clientauth.verifyConnection(connState); err != nil {
37 + t.Fatalf("verifyConnection failed: %v", err)
38 + }
39 +
40 + if !reflect.DeepEqual(verifier.rawCerts, [][]byte{[]byte("peer-cert-raw")}) {
41 + t.Fatalf("unexpected raw certs: got %#v", verifier.rawCerts)
42 + }
43 + if !reflect.DeepEqual(verifier.verifiedChains, verifiedChains) {
44 + t.Fatalf("unexpected verified chains: got %#v", verifier.verifiedChains)
45 + }
46 +}
47 +
48 +func TestClientAuthenticationVerifyConnectionReturnsVerifierError(t *testing.T) {
49 + wantErr := errors.New("verify failed")
50 + verifier := &testClientCertificateVerifier{err: wantErr}
51 + clientauth := &ClientAuthentication{
52 + verifiers: []ClientCertificateVerifier{verifier},
53 + }
54 +
55 + err := clientauth.verifyConnection(tls.ConnectionState{})
56 + if !errors.Is(err, wantErr) {
57 + t.Fatalf("expected error %v, got %v", wantErr, err)
58 + }
59 +}