Skip to content

Commit

Permalink
cosocket: add function tcpsock:setclientcert, reimplemented `tcpsock:…
Browse files Browse the repository at this point in the history
…sslhandshake` with FFI

This adds support for setting client certificate/private key that will be used later
for mutual TLS handshake with a server. Also, the `tcpsock:sslhandshake`
implementation has been rewritten to use FFI C API to be more performant
and easier to maintain.

Also see: openresty/lua-nginx-module#1602

Co-authored-by: Chrono Law <chrono.law@konghq.com>
  • Loading branch information
dndx and chronolaw committed Mar 7, 2022
1 parent c7951b2 commit 68bf4bf
Show file tree
Hide file tree
Showing 8 changed files with 206 additions and 56 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ install:
- git clone https://github.com/openresty/openresty.git ../openresty
- git clone https://github.com/openresty/openresty-devel-utils.git
- git clone https://github.com/simpl/ngx_devel_kit.git ../ndk-nginx-module
- git clone https://github.com/openresty/lua-nginx-module.git ../lua-nginx-module
- git clone -b feat/cosocket_tlshandshake https://github.com/dndx/lua-nginx-module.git ../lua-nginx-module
- git clone https://github.com/openresty/no-pool-nginx.git ../no-pool-nginx
- git clone https://github.com/openresty/echo-nginx-module.git ../echo-nginx-module
- git clone https://github.com/openresty/lua-resty-lrucache.git
Expand Down
2 changes: 2 additions & 0 deletions README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,8 @@ in the current request before you reusing the `ctx` table in some other place.
## resty.core.socket

* [socket.setoption](https://github.com/openresty/lua-nginx-module#tcpsocksetoption)
* [socket.setclientcert](https://github.com/openresty/lua-nginx-module#tcpsocksetclientcert)
* [socket.sslhandshake](https://github.com/openresty/lua-nginx-module#tcpsocksslhandshake)

[Back to TOC](#table-of-contents)

Expand Down
172 changes: 160 additions & 12 deletions lib/resty/core/socket.lua
Original file line number Diff line number Diff line change
@@ -1,18 +1,27 @@
local base = require "resty.core.base"
base.allows_subsystem('http')
local debug = require 'debug'
local ffi = require 'ffi'
base.allows_subsystem("http")
local debug = require "debug"
local ffi = require "ffi"


local error = error
local error = error
local assert = assert
local tonumber = tonumber
local tostring = tostring
local type = type
local select = select
local registry = debug.getregistry()

local C = ffi.C
local ffi_new = ffi.new
local ffi_string = ffi.string
local C = ffi.C
local ffi_str = ffi.string
local ffi_gc = ffi.gc

local get_string_buf = base.get_string_buf
local get_size_ptr = base.get_size_ptr
local tostring = tostring
local get_size_ptr = base.get_size_ptr
local get_request = base.get_request

local co_yield = coroutine._yield


local option_index = {
Expand All @@ -35,14 +44,36 @@ ngx_http_lua_ffi_socket_tcp_getoption(ngx_http_lua_socket_tcp_upstream_t *u,
int
ngx_http_lua_ffi_socket_tcp_setoption(ngx_http_lua_socket_tcp_upstream_t *u,
int opt, int val, unsigned char *err, size_t *errlen);

int
ngx_http_lua_ffi_socket_tcp_sslhandshake(ngx_http_request_t *r,
ngx_http_lua_socket_tcp_upstream_t *u, void *sess,
int enable_session_reuse, ngx_str_t *server_name, int verify,
int ocsp_status_req, void *chain, void *pkey, char **errmsg);

int
ngx_http_lua_ffi_socket_tcp_get_sslhandshake_result(ngx_http_request_t *r,
ngx_http_lua_socket_tcp_upstream_t *u, void **sess, char **errmsg,
int *openssl_error_code);

void
ngx_http_lua_ffi_ssl_free_session(void *sess);
]]


local output_value_buf = ffi_new("int[1]")
local FFI_OK = base.FFI_OK
local SOCKET_CTX_INDEX = 1
local ERR_BUF_SIZE = 4096

local FFI_OK = base.FFI_OK
local FFI_ERROR = base.FFI_ERROR
local FFI_DONE = base.FFI_DONE
local FFI_AGAIN = base.FFI_AGAIN
local FFI_NO_REQ_CTX = base.FFI_NO_REQ_CTX

local SOCKET_CTX_INDEX = 1
local SOCKET_CLIENT_CERT_INDEX = 6
local SOCKET_CLIENT_PKEY_INDEX = 7


local function get_tcp_socket(cosocket)
local tcp_socket = cosocket[SOCKET_CTX_INDEX]
Expand Down Expand Up @@ -75,7 +106,7 @@ local function getoption(cosocket, option)
err,
errlen)
if rc ~= FFI_OK then
return nil, ffi_string(err, errlen[0])
return nil, ffi_str(err, errlen[0])
end

return tonumber(output_value_buf[0])
Expand Down Expand Up @@ -107,17 +138,134 @@ local function setoption(cosocket, option, value)
err,
errlen)
if rc ~= FFI_OK then
return nil, ffi_string(err, errlen[0])
return nil, ffi_str(err, errlen[0])
end

return true
end


local errmsg = base.get_errmsg_ptr()
local session_ptr = ffi_new("void *[1]")
local server_name_str = ffi_new("ngx_str_t[1]")
local openssl_error_code = ffi_new("int[1]")


local function setclientcert(cosocket, cert, pkey)
if not cert and not pkey then
cosocket[SOCKET_CLIENT_CERT_INDEX] = nil
cosocket[SOCKET_CLIENT_PKEY_INDEX] = nil
return true
end

if not cert or not pkey then
return nil,
"client certificate must be supplied with corresponding " ..
"private key"
end

if type(cert) ~= "cdata" then
return nil, "bad cert arg: cdata expected, got " .. type(cert)
end

if type(pkey) ~= "cdata" then
return nil, "bad pkey arg: cdata expected, got " .. type(pkey)
end

cosocket[SOCKET_CLIENT_CERT_INDEX] = cert
cosocket[SOCKET_CLIENT_PKEY_INDEX] = pkey

return true
end


local function sslhandshake(cosocket, reused_session, server_name, ssl_verify,
send_status_req, ...)

local n = select("#", ...)
if not cosocket or n > 0 then
error("ngx.socket sslhandshake: expecting 1 ~ 5 arguments " ..
"(including the object), but seen " .. (cosocket and 5 + n or 0))
end

local r = get_request()
if not r then
error("no request found", 2)
end

session_ptr[0] = type(reused_session) == "cdata" and reused_session or nil

if server_name then
server_name_str[0].data = server_name
server_name_str[0].len = #server_name

else
server_name_str[0].data = nil
server_name_str[0].len = 0
end

local u = get_tcp_socket(cosocket)

local rc = C.ngx_http_lua_ffi_socket_tcp_sslhandshake(r, u,
session_ptr[0],
reused_session ~= false,
server_name_str,
ssl_verify and 1 or 0,
send_status_req and 1 or 0,
cosocket[SOCKET_CLIENT_CERT_INDEX],
cosocket[SOCKET_CLIENT_PKEY_INDEX],
errmsg)

if rc == FFI_NO_REQ_CTX then
error("no request ctx found", 2)
end

while true do
if rc == FFI_ERROR then
if openssl_error_code[0] ~= 0 then
return nil, openssl_error_code[0] .. ": " .. ffi_str(errmsg[0])
end

return nil, ffi_str(errmsg[0])
end

if rc == FFI_DONE then
return reused_session
end

if rc == FFI_OK then
if reused_session == false then
return true
end

rc = C.ngx_http_lua_ffi_socket_tcp_get_sslhandshake_result(r, u,
session_ptr, errmsg, openssl_error_code)

assert(rc == FFI_OK)

if session_ptr[0] == nil then
return session_ptr[0]
end

return ffi_gc(session_ptr[0], C.ngx_http_lua_ffi_ssl_free_session)
end

assert(rc == FFI_AGAIN)

co_yield()

rc = C.ngx_http_lua_ffi_socket_tcp_get_sslhandshake_result(r, u,
session_ptr, errmsg, openssl_error_code)
end
end


do
local method_table = registry.__tcp_cosocket_mt
method_table.getoption = getoption
method_table.setoption = setoption
method_table.setclientcert = setclientcert
method_table.sslhandshake = sslhandshake
end


Expand Down
30 changes: 15 additions & 15 deletions t/ocsp.t
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ __DATA__
GET /t
--- response_body
connected: 1
ssl handshake: userdata
ssl handshake: cdata

--- error_log
lua ssl server name: "test.com"
Expand Down Expand Up @@ -189,7 +189,7 @@ OCSP url found: http://127.0.0.1:8888/ocsp?foo=1,
GET /t
--- response_body
connected: 1
ssl handshake: userdata
ssl handshake: cdata

--- error_log
lua ssl server name: "test.com"
Expand Down Expand Up @@ -281,7 +281,7 @@ OCSP responder not found
GET /t
--- response_body
connected: 1
ssl handshake: userdata
ssl handshake: cdata

--- error_log
lua ssl server name: "test.com"
Expand Down Expand Up @@ -372,7 +372,7 @@ failed to get OCSP responder: no issuer certificate in chain
GET /t
--- response_body
connected: 1
ssl handshake: userdata
ssl handshake: cdata

--- error_log
lua ssl server name: "test.com"
Expand Down Expand Up @@ -467,7 +467,7 @@ failed to get OCSP responder: issuer certificate not next to leaf
GET /t
--- response_body
connected: 1
ssl handshake: userdata
ssl handshake: cdata

--- error_log
lua ssl server name: "test.com"
Expand Down Expand Up @@ -564,7 +564,7 @@ still get an error: truncated
GET /t
--- response_body
connected: 1
ssl handshake: userdata
ssl handshake: cdata

--- error_log
lua ssl server name: "test.com"
Expand Down Expand Up @@ -657,7 +657,7 @@ OCSP request created with length 68
GET /t
--- response_body
connected: 1
ssl handshake: userdata
ssl handshake: cdata

--- error_log
lua ssl server name: "test.com"
Expand Down Expand Up @@ -831,7 +831,7 @@ failed to create OCSP request: d2i_X509_bio() failed
GET /t
--- response_body
connected: 1
ssl handshake: userdata
ssl handshake: cdata

--- error_log
lua ssl server name: "test.com"
Expand Down Expand Up @@ -922,7 +922,7 @@ failed to create OCSP request: no issuer certificate in chain
GET /t
--- response_body
connected: 1
ssl handshake: userdata
ssl handshake: cdata

--- error_log
lua ssl server name: "test.com"
Expand Down Expand Up @@ -1015,7 +1015,7 @@ OCSP response validation ok
GET /t
--- response_body
connected: 1
ssl handshake: userdata
ssl handshake: cdata

--- error_log
lua ssl server name: "test.com"
Expand Down Expand Up @@ -1107,7 +1107,7 @@ OCSP response validation ok
GET /t
--- response_body
connected: 1
ssl handshake: userdata
ssl handshake: cdata

--- error_log
lua ssl server name: "test.com"
Expand Down Expand Up @@ -1202,7 +1202,7 @@ FIXME: we should complain in this case.
GET /t
--- response_body
connected: 1
ssl handshake: userdata
ssl handshake: cdata

--- error_log
lua ssl server name: "test.com"
Expand Down Expand Up @@ -1296,7 +1296,7 @@ OCSP response validation ok
GET /t
--- response_body
connected: 1
ssl handshake: userdata
ssl handshake: cdata

--- error_log
lua ssl server name: "test.com"
Expand Down Expand Up @@ -1472,7 +1472,7 @@ FIXME: check the OCSP staple actually received by the ssl client
GET /t
--- response_body
connected: 1
ssl handshake: userdata
ssl handshake: cdata

--- error_log
lua ssl server name: "test.com"
Expand Down Expand Up @@ -1554,7 +1554,7 @@ ocsp status resp set ok: nil,
GET /t
--- response_body
connected: 1
ssl handshake: userdata
ssl handshake: cdata

--- error_log
lua ssl server name: "test.com"
Expand Down
Loading

0 comments on commit 68bf4bf

Please sign in to comment.