Skip to content

Commit 7b0d59c

Browse files
authored
bugfix: reject settrustedstore on already-handshaked cosocket (#2505)
Calling tcpsock:settrustedstore() after sslhandshake() has completed silently stashed the new X509_STORE into u->ssl_trusted_store, but the next sslhandshake() short-circuits via the c->ssl->handshaked check and never reaches the SSL_set1_verify_cert_store() block, so peer verification kept using the original trust anchor. Reject the call at the FFI entry when the underlying TLS connection is already established so callers get a clear error instead of a silently ineffective trust-anchor swap. The fix is contained to the FFI argument validation path and does not touch the handshake state machine.
1 parent f673343 commit 7b0d59c

2 files changed

Lines changed: 73 additions & 0 deletions

File tree

src/ngx_http_lua_socket_tcp.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2283,6 +2283,14 @@ ngx_http_lua_ffi_socket_tcp_settrustedstore(ngx_http_request_t *r,
22832283
return NGX_ERROR;
22842284
}
22852285

2286+
if (u->peer.connection->ssl
2287+
&& u->peer.connection->ssl->handshaked)
2288+
{
2289+
*errmsg = "ssl handshake already done; trusted store cannot be "
2290+
"changed on an established TLS connection";
2291+
return NGX_ERROR;
2292+
}
2293+
22862294
u->ssl_trusted_store = store;
22872295

22882296
return NGX_OK;

t/193-ssl-trusted-store.t

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,3 +352,68 @@ settrustedstore: true nil
352352
[alert]
353353
[crit]
354354
[emerg]
355+
356+
357+
358+
=== TEST 6: settrustedstore is rejected after the TLS handshake has completed
359+
--- http_config eval: $::tls_http_config
360+
--- config eval
361+
"
362+
location /t {
363+
content_by_lua_block {
364+
local f = assert(io.open('$::HtmlDir/mtls_ca.crt'))
365+
local ca_pem = f:read('*a')
366+
f:close()
367+
368+
local store1 = assert(load_store_from_pem(ca_pem))
369+
370+
local sock = ngx.socket.tcp()
371+
assert(sock:connect('unix:$::HtmlDir/tls.sock'))
372+
373+
assert(settrustedstore(sock, store1))
374+
375+
local sess, err = sock:sslhandshake(nil, 'example.com', true)
376+
if not sess then
377+
ngx.say('failed to do SSL handshake: ', err)
378+
return
379+
end
380+
381+
-- second settrustedstore on an already-handshaked cosocket
382+
-- must fail with a clear error rather than silently no-op.
383+
local f2 = assert(io.open('$::HtmlDir/unrelated_ca.crt'))
384+
local ca_pem2 = f2:read('*a')
385+
f2:close()
386+
local store2 = assert(load_store_from_pem(ca_pem2))
387+
388+
local ok, err = settrustedstore(sock, store2)
389+
ngx.say('settrustedstore after handshake: ', ok, ' ', err)
390+
391+
-- connection must still be usable after the rejected call.
392+
local bytes, err = sock:send('GET / HTTP/1.0\\r\\nHost: example.com\\r\\n\\r\\n')
393+
if not bytes then
394+
ngx.say('failed to send: ', err)
395+
return
396+
end
397+
398+
local line, err = sock:receive('*l')
399+
if not line then
400+
ngx.say('failed to receive: ', err)
401+
return
402+
end
403+
404+
ngx.say('received: ', line)
405+
sock:close()
406+
}
407+
}
408+
"
409+
--- user_files eval: $::tls_user_files
410+
--- request
411+
GET /t
412+
--- response_body_like
413+
^settrustedstore after handshake: nil ssl handshake already done; trusted store cannot be changed on an established TLS connection
414+
received: HTTP/1\.[01] 200 OK$
415+
--- no_error_log
416+
[error]
417+
[alert]
418+
[crit]
419+
[emerg]

0 commit comments

Comments
 (0)