Skip to content

Commit cba2bc5

Browse files
committed
GetClientCertificate cannot return nil cert in TLS 1.3
1 parent 19e2fc1 commit cba2bc5

File tree

4 files changed

+52
-9
lines changed

4 files changed

+52
-9
lines changed

tls/client/client.go

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,22 @@ func NewTLSClientConfigFunc(logger *slog.Logger, src source.ClientCertsSource, o
2020
if err != nil {
2121
return nil, err
2222
}
23+
var getClientCertificateFunc func(info *tls.CertificateRequestInfo) (*tls.Certificate, error)
24+
if store.LoadClientCerts().Certificate != nil {
25+
// Set function only when client certificate is available.
26+
// TLS 1.3 checks if GetClientCertificate function is nil, if it is not nil,
27+
// it assumes client certificate is available which call cause the panic if nil is returned.
28+
// nolint:unparam
29+
getClientCertificateFunc = func(_ *tls.CertificateRequestInfo) (*tls.Certificate, error) {
30+
return store.LoadClientCerts().Certificate, nil
31+
}
32+
}
2333
return func() *tls.Config {
2434
cs := store.LoadClientCerts()
2535
x := &tls.Config{
26-
RootCAs: cs.RootCAs,
27-
InsecureSkipVerify: cs.InsecureSkipVerify,
28-
GetClientCertificate: func(info *tls.CertificateRequestInfo) (*tls.Certificate, error) {
29-
return store.LoadClientCerts().Certificate, nil
30-
},
36+
RootCAs: cs.RootCAs,
37+
InsecureSkipVerify: cs.InsecureSkipVerify,
38+
GetClientCertificate: getClientCertificateFunc,
3139
}
3240
for _, opt := range opts {
3341
opt(x)

tls/client/config/config_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,37 @@ func TestGetClientTLSConfig(t *testing.T) {
3131
require.NoError(t, err)
3232
require.NotNil(t, clientCert)
3333
}
34+
35+
func TestGetClientTLSConfigNoConfig(t *testing.T) {
36+
bundle := testutil.NewCertsBundle()
37+
defer bundle.Close()
38+
tlsConfigFunc, err := GetTLSClientConfigFunc(slog.Default(), &config.TLSClientConfig{
39+
Enable: true,
40+
Refresh: 0,
41+
File: config.TLSClientFiles{},
42+
})
43+
require.NoError(t, err)
44+
tlsConfig := tlsConfigFunc()
45+
require.Nil(t, tlsConfig.RootCAs)
46+
require.Nil(t, tlsConfig.GetClientCertificate)
47+
}
48+
49+
func TestGetClientTLSConfigSkipVerify(t *testing.T) {
50+
bundle := testutil.NewCertsBundle()
51+
defer bundle.Close()
52+
tlsConfigFunc, err := GetTLSClientConfigFunc(slog.Default(), &config.TLSClientConfig{
53+
Enable: true,
54+
Refresh: 0,
55+
File: config.TLSClientFiles{
56+
Key: bundle.ClientKey.Name(),
57+
Cert: bundle.ClientCert.Name(),
58+
},
59+
})
60+
require.NoError(t, err)
61+
tlsConfig := tlsConfigFunc()
62+
require.Nil(t, tlsConfig.RootCAs)
63+
64+
clientCert, err := tlsConfig.GetClientCertificate(nil)
65+
require.NoError(t, err)
66+
require.NotNil(t, clientCert)
67+
}

tls/server/config/config_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ func TestGetServerTLSConfig(t *testing.T) {
2929
})
3030
require.NoError(t, err)
3131
require.NotNil(t, tlsConfig.ClientCAs)
32+
systemPool, err := x509.SystemCertPool()
33+
require.NoError(t, err)
34+
// not system pool
35+
require.False(t, tlsConfig.ClientCAs.Equal(systemPool))
3236
require.Equal(t, tlsConfig.ClientAuth, tls.RequireAndVerifyClientCert)
3337
require.NotEmpty(t, tlsConfig.Certificates)
3438
// clientCRL verification

tls/server/source/pems.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,7 @@ func (s ServerPEMs) ClientCAs() (*x509.CertPool, error) {
4040
if len(s.ClientAuthPEMBlock) == 0 {
4141
return nil, nil
4242
}
43-
certPool, err := x509.SystemCertPool()
44-
if err != nil {
45-
certPool = x509.NewCertPool()
46-
}
43+
certPool := x509.NewCertPool()
4744
if !certPool.AppendCertsFromPEM(s.ClientAuthPEMBlock) {
4845
return nil, errors.New("server PEMs: building client CAs failed")
4946
}

0 commit comments

Comments
 (0)