Skip to content

Commit 483a41c

Browse files
committed
tls: copy client CAs and cert store on CertCb
Copy client CA certs and cert store when asynchronously selecting `SecureContext` during `SNICallback`. We already copy private key, certificate, and certificate chain, but the client CA certs were missing. Fix: #2772 PR-URL: #3537 Reviewed-By: Ben Noordhuis <[email protected]>
1 parent a5cce79 commit 483a41c

File tree

4 files changed

+56
-11
lines changed

4 files changed

+56
-11
lines changed

src/node_crypto.cc

+29-2
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,8 @@ template class SSLWrap<TLSWrap>;
137137
template void SSLWrap<TLSWrap>::AddMethods(Environment* env,
138138
Local<FunctionTemplate> t);
139139
template void SSLWrap<TLSWrap>::InitNPN(SecureContext* sc);
140+
template void SSLWrap<TLSWrap>::SetSNIContext(SecureContext* sc);
141+
template int SSLWrap<TLSWrap>::SetCACerts(SecureContext* sc);
140142
template SSL_SESSION* SSLWrap<TLSWrap>::GetSessionCallback(
141143
SSL* s,
142144
unsigned char* key,
@@ -2264,6 +2266,8 @@ void SSLWrap<Base>::CertCbDone(const FunctionCallbackInfo<Value>& args) {
22642266
rv = SSL_use_PrivateKey(w->ssl_, pkey);
22652267
if (rv && chain != nullptr)
22662268
rv = SSL_set1_chain(w->ssl_, chain);
2269+
if (rv)
2270+
rv = w->SetCACerts(sc);
22672271
if (!rv) {
22682272
unsigned long err = ERR_get_error();
22692273
if (!err)
@@ -2314,6 +2318,30 @@ void SSLWrap<Base>::DestroySSL() {
23142318
}
23152319

23162320

2321+
template <class Base>
2322+
void SSLWrap<Base>::SetSNIContext(SecureContext* sc) {
2323+
InitNPN(sc);
2324+
CHECK_EQ(SSL_set_SSL_CTX(ssl_, sc->ctx_), sc->ctx_);
2325+
2326+
SetCACerts(sc);
2327+
}
2328+
2329+
2330+
template <class Base>
2331+
int SSLWrap<Base>::SetCACerts(SecureContext* sc) {
2332+
int err = SSL_set1_verify_cert_store(ssl_, SSL_CTX_get_cert_store(sc->ctx_));
2333+
if (err != 1)
2334+
return err;
2335+
2336+
STACK_OF(X509_NAME)* list = SSL_dup_CA_list(
2337+
SSL_CTX_get_client_CA_list(sc->ctx_));
2338+
2339+
// NOTE: `SSL_set_client_CA_list` takes the ownership of `list`
2340+
SSL_set_client_CA_list(ssl_, list);
2341+
return 1;
2342+
}
2343+
2344+
23172345
void Connection::OnClientHelloParseEnd(void* arg) {
23182346
Connection* conn = static_cast<Connection*>(arg);
23192347

@@ -2627,8 +2655,7 @@ int Connection::SelectSNIContextCallback_(SSL *s, int *ad, void* arg) {
26272655
if (secure_context_constructor_template->HasInstance(ret)) {
26282656
conn->sni_context_.Reset(env->isolate(), ret);
26292657
SecureContext* sc = Unwrap<SecureContext>(ret.As<Object>());
2630-
InitNPN(sc);
2631-
SSL_set_SSL_CTX(s, sc->ctx_);
2658+
conn->SetSNIContext(sc);
26322659
} else {
26332660
return SSL_TLSEXT_ERR_NOACK;
26342661
}

src/node_crypto.h

+2
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,8 @@ class SSLWrap {
271271

272272
void DestroySSL();
273273
void WaitForCertCb(CertCb cb, void* arg);
274+
void SetSNIContext(SecureContext* sc);
275+
int SetCACerts(SecureContext* sc);
274276

275277
inline Environment* ssl_env() const {
276278
return env_;

src/tls_wrap.cc

+1-2
Original file line numberDiff line numberDiff line change
@@ -857,8 +857,7 @@ int TLSWrap::SelectSNIContextCallback(SSL* s, int* ad, void* arg) {
857857
p->sni_context_.Reset(env->isolate(), ctx);
858858

859859
SecureContext* sc = Unwrap<SecureContext>(ctx.As<Object>());
860-
InitNPN(sc);
861-
SSL_set_SSL_CTX(s, sc->ctx_);
860+
p->SetSNIContext(sc);
862861
return SSL_TLSEXT_ERR_OK;
863862
}
864863
#endif // SSL_CTRL_SET_TLSEXT_SERVERNAME_CB

test/parallel/test-tls-sni-option.js

+24-7
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ function loadPEM(n) {
2626
var serverOptions = {
2727
key: loadPEM('agent2-key'),
2828
cert: loadPEM('agent2-cert'),
29+
requestCert: true,
30+
rejectUnauthorized: false,
2931
SNICallback: function(servername, callback) {
3032
var context = SNIContexts[servername];
3133

@@ -46,7 +48,8 @@ var serverOptions = {
4648
var SNIContexts = {
4749
'a.example.com': {
4850
key: loadPEM('agent1-key'),
49-
cert: loadPEM('agent1-cert')
51+
cert: loadPEM('agent1-cert'),
52+
ca: [ loadPEM('ca2-cert') ]
5053
},
5154
'b.example.com': {
5255
key: loadPEM('agent3-key'),
@@ -66,6 +69,13 @@ var clientsOptions = [{
6669
ca: [loadPEM('ca1-cert')],
6770
servername: 'a.example.com',
6871
rejectUnauthorized: false
72+
}, {
73+
port: serverPort,
74+
key: loadPEM('agent4-key'),
75+
cert: loadPEM('agent4-cert'),
76+
ca: [loadPEM('ca1-cert')],
77+
servername: 'a.example.com',
78+
rejectUnauthorized: false
6979
}, {
7080
port: serverPort,
7181
key: loadPEM('agent2-key'),
@@ -97,7 +107,7 @@ var serverResults = [],
97107
clientError;
98108

99109
var server = tls.createServer(serverOptions, function(c) {
100-
serverResults.push(c.servername);
110+
serverResults.push({ sni: c.servername, authorized: c.authorized });
101111
});
102112

103113
server.on('clientError', function(err) {
@@ -144,9 +154,16 @@ function startTest() {
144154
}
145155

146156
process.on('exit', function() {
147-
assert.deepEqual(serverResults, ['a.example.com', 'b.example.com',
148-
'c.wrong.com', null]);
149-
assert.deepEqual(clientResults, [true, true, false, false]);
150-
assert.deepEqual(clientErrors, [null, null, null, 'socket hang up']);
151-
assert.deepEqual(serverErrors, [null, null, null, 'Invalid SNI context']);
157+
assert.deepEqual(serverResults, [
158+
{ sni: 'a.example.com', authorized: false },
159+
{ sni: 'a.example.com', authorized: true },
160+
{ sni: 'b.example.com', authorized: false },
161+
{ sni: 'c.wrong.com', authorized: false },
162+
null
163+
]);
164+
assert.deepEqual(clientResults, [true, true, true, false, false]);
165+
assert.deepEqual(clientErrors, [null, null, null, null, 'socket hang up']);
166+
assert.deepEqual(serverErrors, [
167+
null, null, null, null, 'Invalid SNI context'
168+
]);
152169
});

0 commit comments

Comments
 (0)