Skip to content

Commit b247286

Browse files
committed
bugfix: check sslhandshake result on immediate OK
This patch fixes the immediate `FFI_OK` path in `tcpsock:sslhandshake()`. Currently `sslhandshake()` treats the initial `FFI_OK` return from `ngx_http_lua_ffi_socket_tcp_sslhandshake()` as the final successful result when `reused_session == false`: ```lua rc = ngx_http_lua_ffi_socket_tcp_sslhandshake(...) if rc == FFI_OK then if reused_session == false then return true end rc = ngx_http_lua_ffi_socket_tcp_get_sslhandshake_result(...) end ``` This skips `ngx_http_lua_ffi_socket_tcp_get_sslhandshake_result()` for the `reused_session == false` case. That is unsafe because the first FFI call result and the final SSL handshake result are not the same thing. The first return value tells Lua how the handshake operation progressed: ```text FFI_ERROR -> immediate call failed FFI_DONE -> reused session FFI_OK -> handshake operation completed immediately FFI_AGAIN -> handshake needs to yield and resume later ``` The final result must still be read from: ```text ngx_http_lua_ffi_socket_tcp_get_sslhandshake_result(...) ``` This matters when the C side completes the handshake path immediately but records an SSL error in the cosocket state. For example, certificate verification can fail inside the handshake handler and set: ```text u->error_ret u->openssl_error_code_ret ``` The old Lua code can miss that state. Old flow: ```text Lua sslhandshake() | v C: ngx_http_lua_ffi_socket_tcp_sslhandshake(...) | v returns FFI_OK | v reused_session == false? | v return true | v final handshake result is never checked ``` So callers may believe TLS succeeded even though the C cosocket state already has a handshake error. The next socket operation can then fail with a misleading error, for example a write failure, instead of returning the original SSL handshake error. The fixed flow always reads the final result for both immediate and async completion paths: ```text Lua sslhandshake() | v C: ngx_http_lua_ffi_socket_tcp_sslhandshake(...) | +-----------------------------+ | | v v FFI_OK FFI_AGAIN immediate async handshake completion | | v | co_yield() | | +------------+-------------+ | v get_sslhandshake_result() | v final result | +--------+--------+ | | v v FFI_ERROR FFI_OK | | v v return SSL error return true/session ``` This patch separates the two meanings by using: ```text rc -> result of ngx_http_lua_ffi_socket_tcp_sslhandshake() res -> result of ngx_http_lua_ffi_socket_tcp_get_sslhandshake_result() ``` The new control flow is: ```lua rc = ngx_http_lua_ffi_socket_tcp_sslhandshake(...) if rc == FFI_ERROR then return nil, err end if rc == FFI_DONE then return reused_session end local res if rc == FFI_OK then res = ngx_http_lua_ffi_socket_tcp_get_sslhandshake_result(...) else assert(rc == FFI_AGAIN) co_yield() res = ngx_http_lua_ffi_socket_tcp_get_sslhandshake_result(...) end if res == FFI_ERROR then return nil, ssl_err end assert(res == FFI_OK) return true or session ``` This keeps the existing async behavior, but makes the immediate `FFI_OK` path follow the same final-result check as the resumed `FFI_AGAIN` path.
1 parent e68e857 commit b247286

1 file changed

Lines changed: 30 additions & 32 deletions

File tree

lib/resty/core/socket.lua

Lines changed: 30 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -424,52 +424,50 @@ local function sslhandshake(cosocket, reused_session, server_name, ssl_verify,
424424
error("no request ctx found", 2)
425425
end
426426

427-
if rc == FFI_OK then
428-
if reused_session == false then
429-
return true
427+
if rc == FFI_ERROR then
428+
if openssl_error_code[0] ~= 0 then
429+
return nil, openssl_error_code[0] .. ": " .. ffi_str(errmsg[0])
430430
end
431431

432-
rc = C.ngx_http_lua_ffi_socket_tcp_get_sslhandshake_result(r, u,
433-
session_ptr, errmsg, openssl_error_code)
432+
return nil, ffi_str(errmsg[0])
434433
end
435434

436-
while true do
437-
if rc == FFI_ERROR then
438-
if openssl_error_code[0] ~= 0 then
439-
return nil, openssl_error_code[0] .. ": " .. ffi_str(errmsg[0])
440-
end
441-
442-
return nil, ffi_str(errmsg[0])
443-
end
444-
445-
if rc == FFI_DONE then
446-
return reused_session
447-
end
435+
if rc == FFI_DONE then
436+
return reused_session
437+
end
448438

449-
if rc == FFI_OK then
450-
if reused_session == false then
451-
return true
452-
end
439+
local res
453440

454-
rc = C.ngx_http_lua_ffi_socket_tcp_get_sslhandshake_result(r, u,
455-
session_ptr, errmsg, openssl_error_code)
441+
if rc == FFI_OK then
442+
res = C.ngx_http_lua_ffi_socket_tcp_get_sslhandshake_result(r, u,
443+
session_ptr, errmsg, openssl_error_code)
444+
else
445+
assert(rc == FFI_AGAIN)
456446

457-
assert(rc == FFI_OK)
447+
co_yield()
458448

459-
if session_ptr[0] == nil then
460-
return session_ptr[0]
461-
end
449+
res = C.ngx_http_lua_ffi_socket_tcp_get_sslhandshake_result(r, u,
450+
session_ptr, errmsg, openssl_error_code)
451+
end
462452

463-
return ffi_gc(session_ptr[0], C.ngx_http_lua_ffi_ssl_free_session)
453+
if res == FFI_ERROR then
454+
if openssl_error_code[0] ~= 0 then
455+
return nil, openssl_error_code[0] .. ": " .. ffi_str(errmsg[0])
464456
end
457+
return nil, ffi_str(errmsg[0])
458+
end
465459

466-
assert(rc == FFI_AGAIN)
460+
assert(res == FFI_OK)
467461

468-
co_yield()
462+
if reused_session == false then
463+
return true
464+
end
469465

470-
rc = C.ngx_http_lua_ffi_socket_tcp_get_sslhandshake_result(r, u,
471-
session_ptr, errmsg, openssl_error_code)
466+
if session_ptr[0] == nil then
467+
return session_ptr[0]
472468
end
469+
470+
return ffi_gc(session_ptr[0], C.ngx_http_lua_ffi_ssl_free_session)
473471
end
474472

475473

0 commit comments

Comments
 (0)