Skip to content

Commit 68bf4bf

Browse files
dndxchronolaw
andcommitted
cosocket: add function tcpsock:setclientcert, reimplemented tcpsock: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 <[email protected]>
1 parent c7951b2 commit 68bf4bf

File tree

8 files changed

+206
-56
lines changed

8 files changed

+206
-56
lines changed

.travis.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ install:
6565
- git clone https://github.com/openresty/openresty.git ../openresty
6666
- git clone https://github.com/openresty/openresty-devel-utils.git
6767
- git clone https://github.com/simpl/ngx_devel_kit.git ../ndk-nginx-module
68-
- git clone https://github.com/openresty/lua-nginx-module.git ../lua-nginx-module
68+
- git clone -b feat/cosocket_tlshandshake https://github.com/dndx/lua-nginx-module.git ../lua-nginx-module
6969
- git clone https://github.com/openresty/no-pool-nginx.git ../no-pool-nginx
7070
- git clone https://github.com/openresty/echo-nginx-module.git ../echo-nginx-module
7171
- git clone https://github.com/openresty/lua-resty-lrucache.git

README.markdown

+2
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,8 @@ in the current request before you reusing the `ctx` table in some other place.
271271
## resty.core.socket
272272

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

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

lib/resty/core/socket.lua

+160-12
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,27 @@
11
local base = require "resty.core.base"
2-
base.allows_subsystem('http')
3-
local debug = require 'debug'
4-
local ffi = require 'ffi'
2+
base.allows_subsystem("http")
3+
local debug = require "debug"
4+
local ffi = require "ffi"
55

66

7-
local error = error
7+
local error = error
8+
local assert = assert
89
local tonumber = tonumber
10+
local tostring = tostring
11+
local type = type
12+
local select = select
913
local registry = debug.getregistry()
14+
15+
local C = ffi.C
1016
local ffi_new = ffi.new
11-
local ffi_string = ffi.string
12-
local C = ffi.C
17+
local ffi_str = ffi.string
18+
local ffi_gc = ffi.gc
19+
1320
local get_string_buf = base.get_string_buf
14-
local get_size_ptr = base.get_size_ptr
15-
local tostring = tostring
21+
local get_size_ptr = base.get_size_ptr
22+
local get_request = base.get_request
23+
24+
local co_yield = coroutine._yield
1625

1726

1827
local option_index = {
@@ -35,14 +44,36 @@ ngx_http_lua_ffi_socket_tcp_getoption(ngx_http_lua_socket_tcp_upstream_t *u,
3544
int
3645
ngx_http_lua_ffi_socket_tcp_setoption(ngx_http_lua_socket_tcp_upstream_t *u,
3746
int opt, int val, unsigned char *err, size_t *errlen);
47+
48+
int
49+
ngx_http_lua_ffi_socket_tcp_sslhandshake(ngx_http_request_t *r,
50+
ngx_http_lua_socket_tcp_upstream_t *u, void *sess,
51+
int enable_session_reuse, ngx_str_t *server_name, int verify,
52+
int ocsp_status_req, void *chain, void *pkey, char **errmsg);
53+
54+
int
55+
ngx_http_lua_ffi_socket_tcp_get_sslhandshake_result(ngx_http_request_t *r,
56+
ngx_http_lua_socket_tcp_upstream_t *u, void **sess, char **errmsg,
57+
int *openssl_error_code);
58+
59+
void
60+
ngx_http_lua_ffi_ssl_free_session(void *sess);
3861
]]
3962

4063

4164
local output_value_buf = ffi_new("int[1]")
42-
local FFI_OK = base.FFI_OK
43-
local SOCKET_CTX_INDEX = 1
4465
local ERR_BUF_SIZE = 4096
4566

67+
local FFI_OK = base.FFI_OK
68+
local FFI_ERROR = base.FFI_ERROR
69+
local FFI_DONE = base.FFI_DONE
70+
local FFI_AGAIN = base.FFI_AGAIN
71+
local FFI_NO_REQ_CTX = base.FFI_NO_REQ_CTX
72+
73+
local SOCKET_CTX_INDEX = 1
74+
local SOCKET_CLIENT_CERT_INDEX = 6
75+
local SOCKET_CLIENT_PKEY_INDEX = 7
76+
4677

4778
local function get_tcp_socket(cosocket)
4879
local tcp_socket = cosocket[SOCKET_CTX_INDEX]
@@ -75,7 +106,7 @@ local function getoption(cosocket, option)
75106
err,
76107
errlen)
77108
if rc ~= FFI_OK then
78-
return nil, ffi_string(err, errlen[0])
109+
return nil, ffi_str(err, errlen[0])
79110
end
80111

81112
return tonumber(output_value_buf[0])
@@ -107,17 +138,134 @@ local function setoption(cosocket, option, value)
107138
err,
108139
errlen)
109140
if rc ~= FFI_OK then
110-
return nil, ffi_string(err, errlen[0])
141+
return nil, ffi_str(err, errlen[0])
111142
end
112143

113144
return true
114145
end
115146

116147

148+
local errmsg = base.get_errmsg_ptr()
149+
local session_ptr = ffi_new("void *[1]")
150+
local server_name_str = ffi_new("ngx_str_t[1]")
151+
local openssl_error_code = ffi_new("int[1]")
152+
153+
154+
local function setclientcert(cosocket, cert, pkey)
155+
if not cert and not pkey then
156+
cosocket[SOCKET_CLIENT_CERT_INDEX] = nil
157+
cosocket[SOCKET_CLIENT_PKEY_INDEX] = nil
158+
return true
159+
end
160+
161+
if not cert or not pkey then
162+
return nil,
163+
"client certificate must be supplied with corresponding " ..
164+
"private key"
165+
end
166+
167+
if type(cert) ~= "cdata" then
168+
return nil, "bad cert arg: cdata expected, got " .. type(cert)
169+
end
170+
171+
if type(pkey) ~= "cdata" then
172+
return nil, "bad pkey arg: cdata expected, got " .. type(pkey)
173+
end
174+
175+
cosocket[SOCKET_CLIENT_CERT_INDEX] = cert
176+
cosocket[SOCKET_CLIENT_PKEY_INDEX] = pkey
177+
178+
return true
179+
end
180+
181+
182+
local function sslhandshake(cosocket, reused_session, server_name, ssl_verify,
183+
send_status_req, ...)
184+
185+
local n = select("#", ...)
186+
if not cosocket or n > 0 then
187+
error("ngx.socket sslhandshake: expecting 1 ~ 5 arguments " ..
188+
"(including the object), but seen " .. (cosocket and 5 + n or 0))
189+
end
190+
191+
local r = get_request()
192+
if not r then
193+
error("no request found", 2)
194+
end
195+
196+
session_ptr[0] = type(reused_session) == "cdata" and reused_session or nil
197+
198+
if server_name then
199+
server_name_str[0].data = server_name
200+
server_name_str[0].len = #server_name
201+
202+
else
203+
server_name_str[0].data = nil
204+
server_name_str[0].len = 0
205+
end
206+
207+
local u = get_tcp_socket(cosocket)
208+
209+
local rc = C.ngx_http_lua_ffi_socket_tcp_sslhandshake(r, u,
210+
session_ptr[0],
211+
reused_session ~= false,
212+
server_name_str,
213+
ssl_verify and 1 or 0,
214+
send_status_req and 1 or 0,
215+
cosocket[SOCKET_CLIENT_CERT_INDEX],
216+
cosocket[SOCKET_CLIENT_PKEY_INDEX],
217+
errmsg)
218+
219+
if rc == FFI_NO_REQ_CTX then
220+
error("no request ctx found", 2)
221+
end
222+
223+
while true do
224+
if rc == FFI_ERROR then
225+
if openssl_error_code[0] ~= 0 then
226+
return nil, openssl_error_code[0] .. ": " .. ffi_str(errmsg[0])
227+
end
228+
229+
return nil, ffi_str(errmsg[0])
230+
end
231+
232+
if rc == FFI_DONE then
233+
return reused_session
234+
end
235+
236+
if rc == FFI_OK then
237+
if reused_session == false then
238+
return true
239+
end
240+
241+
rc = C.ngx_http_lua_ffi_socket_tcp_get_sslhandshake_result(r, u,
242+
session_ptr, errmsg, openssl_error_code)
243+
244+
assert(rc == FFI_OK)
245+
246+
if session_ptr[0] == nil then
247+
return session_ptr[0]
248+
end
249+
250+
return ffi_gc(session_ptr[0], C.ngx_http_lua_ffi_ssl_free_session)
251+
end
252+
253+
assert(rc == FFI_AGAIN)
254+
255+
co_yield()
256+
257+
rc = C.ngx_http_lua_ffi_socket_tcp_get_sslhandshake_result(r, u,
258+
session_ptr, errmsg, openssl_error_code)
259+
end
260+
end
261+
262+
117263
do
118264
local method_table = registry.__tcp_cosocket_mt
119265
method_table.getoption = getoption
120266
method_table.setoption = setoption
267+
method_table.setclientcert = setclientcert
268+
method_table.sslhandshake = sslhandshake
121269
end
122270

123271

t/ocsp.t

+15-15
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ __DATA__
9797
GET /t
9898
--- response_body
9999
connected: 1
100-
ssl handshake: userdata
100+
ssl handshake: cdata
101101
102102
--- error_log
103103
lua ssl server name: "test.com"
@@ -189,7 +189,7 @@ OCSP url found: http://127.0.0.1:8888/ocsp?foo=1,
189189
GET /t
190190
--- response_body
191191
connected: 1
192-
ssl handshake: userdata
192+
ssl handshake: cdata
193193
194194
--- error_log
195195
lua ssl server name: "test.com"
@@ -281,7 +281,7 @@ OCSP responder not found
281281
GET /t
282282
--- response_body
283283
connected: 1
284-
ssl handshake: userdata
284+
ssl handshake: cdata
285285
286286
--- error_log
287287
lua ssl server name: "test.com"
@@ -372,7 +372,7 @@ failed to get OCSP responder: no issuer certificate in chain
372372
GET /t
373373
--- response_body
374374
connected: 1
375-
ssl handshake: userdata
375+
ssl handshake: cdata
376376
377377
--- error_log
378378
lua ssl server name: "test.com"
@@ -467,7 +467,7 @@ failed to get OCSP responder: issuer certificate not next to leaf
467467
GET /t
468468
--- response_body
469469
connected: 1
470-
ssl handshake: userdata
470+
ssl handshake: cdata
471471
472472
--- error_log
473473
lua ssl server name: "test.com"
@@ -564,7 +564,7 @@ still get an error: truncated
564564
GET /t
565565
--- response_body
566566
connected: 1
567-
ssl handshake: userdata
567+
ssl handshake: cdata
568568
569569
--- error_log
570570
lua ssl server name: "test.com"
@@ -657,7 +657,7 @@ OCSP request created with length 68
657657
GET /t
658658
--- response_body
659659
connected: 1
660-
ssl handshake: userdata
660+
ssl handshake: cdata
661661
662662
--- error_log
663663
lua ssl server name: "test.com"
@@ -831,7 +831,7 @@ failed to create OCSP request: d2i_X509_bio() failed
831831
GET /t
832832
--- response_body
833833
connected: 1
834-
ssl handshake: userdata
834+
ssl handshake: cdata
835835
836836
--- error_log
837837
lua ssl server name: "test.com"
@@ -922,7 +922,7 @@ failed to create OCSP request: no issuer certificate in chain
922922
GET /t
923923
--- response_body
924924
connected: 1
925-
ssl handshake: userdata
925+
ssl handshake: cdata
926926
927927
--- error_log
928928
lua ssl server name: "test.com"
@@ -1015,7 +1015,7 @@ OCSP response validation ok
10151015
GET /t
10161016
--- response_body
10171017
connected: 1
1018-
ssl handshake: userdata
1018+
ssl handshake: cdata
10191019
10201020
--- error_log
10211021
lua ssl server name: "test.com"
@@ -1107,7 +1107,7 @@ OCSP response validation ok
11071107
GET /t
11081108
--- response_body
11091109
connected: 1
1110-
ssl handshake: userdata
1110+
ssl handshake: cdata
11111111
11121112
--- error_log
11131113
lua ssl server name: "test.com"
@@ -1202,7 +1202,7 @@ FIXME: we should complain in this case.
12021202
GET /t
12031203
--- response_body
12041204
connected: 1
1205-
ssl handshake: userdata
1205+
ssl handshake: cdata
12061206
12071207
--- error_log
12081208
lua ssl server name: "test.com"
@@ -1296,7 +1296,7 @@ OCSP response validation ok
12961296
GET /t
12971297
--- response_body
12981298
connected: 1
1299-
ssl handshake: userdata
1299+
ssl handshake: cdata
13001300
13011301
--- error_log
13021302
lua ssl server name: "test.com"
@@ -1472,7 +1472,7 @@ FIXME: check the OCSP staple actually received by the ssl client
14721472
GET /t
14731473
--- response_body
14741474
connected: 1
1475-
ssl handshake: userdata
1475+
ssl handshake: cdata
14761476
14771477
--- error_log
14781478
lua ssl server name: "test.com"
@@ -1554,7 +1554,7 @@ ocsp status resp set ok: nil,
15541554
GET /t
15551555
--- response_body
15561556
connected: 1
1557-
ssl handshake: userdata
1557+
ssl handshake: cdata
15581558
15591559
--- error_log
15601560
lua ssl server name: "test.com"

0 commit comments

Comments
 (0)