Skip to content

Commit f6ba1df

Browse files
committed
feature: TCP cosocket client certificate support. closes #534
1 parent fd1e0f8 commit f6ba1df

9 files changed

+858
-8
lines changed

README.markdown

+13-2
Original file line numberDiff line numberDiff line change
@@ -927,7 +927,6 @@ TODO
927927
* add `ignore_resp_headers`, `ignore_resp_body`, and `ignore_resp` options to [ngx.location.capture](#ngxlocationcapture) and [ngx.location.capture_multi](#ngxlocationcapture_multi) methods, to allow micro performance tuning on the user side.
928928
* add automatic Lua code time slicing support by yielding and resuming the Lua VM actively via Lua's debug hooks.
929929
* add `stat` mode similar to [mod_lua](https://httpd.apache.org/docs/trunk/mod/mod_lua.html).
930-
* cosocket: add client SSL certificate support.
931930

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

@@ -7346,7 +7345,7 @@ This method was first introduced in the `v0.5.0rc1` release.
73467345
tcpsock:sslhandshake
73477346
--------------------
73487347

7349-
**syntax:** *session, err = tcpsock:sslhandshake(reused_session?, server_name?, ssl_verify?, send_status_req?)*
7348+
**syntax:** *session, err = tcpsock:sslhandshake(reused_session?, server_name?, ssl_verify?, send_status_req?, options_table?)*
73507349

73517350
**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua**
73527351

@@ -7382,6 +7381,18 @@ to validate the server name in the server certificate.
73827381
The optional `send_status_req` argument takes a boolean that controls whether to send
73837382
the OCSP status request in the SSL handshake request (which is for requesting OCSP stapling).
73847383

7384+
An optional Lua table can be specified as the last argument to this method to specify various handshake options:
7385+
7386+
* `client_cert` specify a client certificate chain cdata object that will be used while handshaking with
7387+
remote server. These objects can be created using [ngx.ssl.parse\_pem\_cert](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl.md#parse_pem_cert)
7388+
function provided by lua-resty-core. Note that specifying the `client_cert` option requires
7389+
corresponding `client_priv_key` be provided too. See below.
7390+
* `client_priv_key` specify a private key corresponds to the `client_cert` option above.
7391+
These objects can be created using [ngx.ssl.parse\_pem\_priv\_key](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl.md#parse_pem_priv_key)
7392+
function provided by lua-resty-core.
7393+
7394+
The support for the options table argument was first introduced in the v0.10.16 release.
7395+
73857396
For connections that have already done SSL/TLS handshake, this method returns
73867397
immediately.
73877398

src/ngx_http_lua_socket_tcp.c

+99-4
Original file line numberDiff line numberDiff line change
@@ -1534,23 +1534,27 @@ ngx_http_lua_socket_conn_error_retval_handler(ngx_http_request_t *r,
15341534
static int
15351535
ngx_http_lua_socket_tcp_sslhandshake(lua_State *L)
15361536
{
1537-
int n, top;
1537+
int n, top, i;
15381538
ngx_int_t rc;
15391539
ngx_str_t name = ngx_null_string;
15401540
ngx_connection_t *c;
15411541
ngx_ssl_session_t **psession;
15421542
ngx_http_request_t *r;
15431543
ngx_http_lua_ctx_t *ctx;
15441544
ngx_http_lua_co_ctx_t *coctx;
1545+
STACK_OF(X509) *chain = NULL;
1546+
X509 *x509;
1547+
EVP_PKEY *pkey;
1548+
ngx_ssl_conn_t *ssl_conn;
15451549

15461550
ngx_http_lua_socket_tcp_upstream_t *u;
15471551

15481552
/* Lua function arguments: self [,session] [,host] [,verify]
1549-
[,send_status_req] */
1553+
[,send_status_req] [, opts] */
15501554

15511555
n = lua_gettop(L);
1552-
if (n < 1 || n > 5) {
1553-
return luaL_error(L, "ngx.socket sslhandshake: expecting 1 ~ 5 "
1556+
if (n < 1 || n > 6) {
1557+
return luaL_error(L, "ngx.socket sslhandshake: expecting 1 ~ 6 "
15541558
"arguments (including the object), but seen %d", n);
15551559
}
15561560

@@ -1626,6 +1630,8 @@ ngx_http_lua_socket_tcp_sslhandshake(lua_State *L)
16261630
return 2;
16271631
}
16281632

1633+
ssl_conn = c->ssl->connection;
1634+
16291635
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
16301636
if (ctx == NULL) {
16311637
return luaL_error(L, "no ctx found");
@@ -1695,6 +1701,95 @@ ngx_http_lua_socket_tcp_sslhandshake(lua_State *L)
16951701
#endif
16961702
}
16971703
}
1704+
1705+
if (n >= 6) {
1706+
if (lua_type(L, 6) == LUA_TTABLE) {
1707+
lua_getfield(L, 6, "client_cert");
1708+
1709+
if (!lua_isnil(L, -1)) {
1710+
chain = luaL_checkcdataptr(L, -1);
1711+
if (chain == NULL) {
1712+
return luaL_error(L, "\"client_cert\" can "
1713+
"not be NULL");
1714+
}
1715+
1716+
/* chain != NULL */
1717+
1718+
lua_pop(L, 1);
1719+
1720+
lua_getfield(L, 6, "client_priv_key");
1721+
1722+
pkey = luaL_checkcdataptr(L, -1);
1723+
if (pkey == NULL) {
1724+
return luaL_error(L, "\"client_priv_key\" can "
1725+
"not be NULL");
1726+
}
1727+
1728+
if (sk_X509_num(chain) < 1) {
1729+
ERR_clear_error();
1730+
return luaL_error(L, "invalid client "
1731+
"certificate chain");
1732+
}
1733+
1734+
x509 = sk_X509_value(chain, 0);
1735+
if (x509 == NULL) {
1736+
ERR_clear_error();
1737+
lua_pushnil(L);
1738+
lua_pushliteral(L, "lua ssl fetch client "
1739+
"certificate from chain "
1740+
"failed");
1741+
return 2;
1742+
}
1743+
1744+
if (SSL_use_certificate(ssl_conn, x509) == 0) {
1745+
ERR_clear_error();
1746+
lua_pushnil(L);
1747+
lua_pushliteral(L, "lua ssl set client "
1748+
"certificate failed");
1749+
return 2;
1750+
}
1751+
1752+
/* read rest of the chain */
1753+
1754+
for (i = 1; i < sk_X509_num(chain); i++) {
1755+
x509 = sk_X509_value(chain, i);
1756+
if (x509 == NULL) {
1757+
ERR_clear_error();
1758+
lua_pushnil(L);
1759+
lua_pushliteral(L, "lua ssl fetch client "
1760+
"intermediate certificate "
1761+
"from chain failed");
1762+
return 2;
1763+
}
1764+
1765+
if (SSL_add1_chain_cert(ssl_conn, x509) == 0) {
1766+
ERR_clear_error();
1767+
lua_pushnil(L);
1768+
lua_pushliteral(L, "lua ssl set client "
1769+
"intermediate certificate "
1770+
"failed");
1771+
return 2;
1772+
}
1773+
}
1774+
1775+
if (SSL_use_PrivateKey(ssl_conn, pkey) == 0) {
1776+
ERR_clear_error();
1777+
lua_pushnil(L);
1778+
lua_pushliteral(L, "lua ssl set client "
1779+
"private key failed");
1780+
return 2;
1781+
}
1782+
}
1783+
1784+
lua_pop(L, 1);
1785+
1786+
} else if (!lua_isnil(L, 6)) {
1787+
return luaL_error(L, "ngx.socket sslhandshake: bad "
1788+
"options table type, expecting a "
1789+
"table but seen %s",
1790+
lua_typename(L, lua_type(L, 6)));
1791+
}
1792+
}
16981793
}
16991794
}
17001795
}

0 commit comments

Comments
 (0)