Skip to content

Commit 45c63cd

Browse files
authored
feature: add ssl trusted certificate.
1 parent 94f55f7 commit 45c63cd

File tree

2 files changed

+220
-44
lines changed

2 files changed

+220
-44
lines changed

src/ngx_http_lua_ssl_certby.c

+53-31
Original file line numberDiff line numberDiff line change
@@ -1468,8 +1468,8 @@ ngx_http_lua_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store)
14681468

14691469

14701470
int
1471-
ngx_http_lua_ffi_ssl_verify_client(ngx_http_request_t *r, void *ca_certs,
1472-
int depth, char **err)
1471+
ngx_http_lua_ffi_ssl_verify_client(ngx_http_request_t *r, void *client_certs,
1472+
void *trusted_certs, int depth, char **err)
14731473
{
14741474
#ifdef LIBRESSL_VERSION_NUMBER
14751475

@@ -1481,7 +1481,8 @@ ngx_http_lua_ffi_ssl_verify_client(ngx_http_request_t *r, void *ca_certs,
14811481
ngx_http_lua_ctx_t *ctx;
14821482
ngx_ssl_conn_t *ssl_conn;
14831483
ngx_http_ssl_srv_conf_t *sscf;
1484-
STACK_OF(X509) *chain = ca_certs;
1484+
STACK_OF(X509) *client_chain = client_certs;
1485+
STACK_OF(X509) *trusted_chain = trusted_certs;
14851486
STACK_OF(X509_NAME) *name_chain = NULL;
14861487
X509 *x509 = NULL;
14871488
X509_NAME *subject = NULL;
@@ -1535,54 +1536,75 @@ ngx_http_lua_ffi_ssl_verify_client(ngx_http_request_t *r, void *ca_certs,
15351536

15361537
/* set CA chain */
15371538

1538-
if (chain != NULL) {
1539+
if (client_chain != NULL || trusted_chain != NULL) {
1540+
15391541
ca_store = X509_STORE_new();
15401542
if (ca_store == NULL) {
15411543
*err = "X509_STORE_new() failed";
15421544
return NGX_ERROR;
15431545
}
15441546

1545-
/* construct name chain */
1547+
if (client_chain != NULL) {
15461548

1547-
name_chain = sk_X509_NAME_new_null();
1548-
if (name_chain == NULL) {
1549-
*err = "sk_X509_NAME_new_null() failed";
1550-
goto failed;
1551-
}
1552-
1553-
for (i = 0; i < sk_X509_num(chain); i++) {
1554-
x509 = sk_X509_value(chain, i);
1555-
if (x509 == NULL) {
1556-
*err = "sk_X509_value() failed";
1549+
/* construct name chain */
1550+
name_chain = sk_X509_NAME_new_null();
1551+
if (name_chain == NULL) {
1552+
*err = "sk_X509_NAME_new_null() failed";
15571553
goto failed;
15581554
}
15591555

1560-
/* add subject to name chain, which will be sent to client */
1561-
subject = X509_NAME_dup(X509_get_subject_name(x509));
1562-
if (subject == NULL) {
1563-
*err = "X509_get_subject_name() failed";
1564-
goto failed;
1556+
for (i = 0; i < sk_X509_num(client_chain); i++) {
1557+
x509 = sk_X509_value(client_chain, i);
1558+
if (x509 == NULL) {
1559+
*err = "sk_X509_value() failed";
1560+
goto failed;
1561+
}
1562+
1563+
/* add subject to name chain, which will be sent to client */
1564+
subject = X509_NAME_dup(X509_get_subject_name(x509));
1565+
if (subject == NULL) {
1566+
*err = "X509_get_subject_name() failed";
1567+
goto failed;
1568+
}
1569+
1570+
if (!sk_X509_NAME_push(name_chain, subject)) {
1571+
*err = "sk_X509_NAME_push() failed";
1572+
X509_NAME_free(subject);
1573+
goto failed;
1574+
}
1575+
1576+
/* add to trusted CA store */
1577+
if (X509_STORE_add_cert(ca_store, x509) == 0) {
1578+
*err = "X509_STORE_add_cert() failed";
1579+
goto failed;
1580+
}
15651581
}
15661582

1567-
if (!sk_X509_NAME_push(name_chain, subject)) {
1568-
*err = "sk_X509_NAME_push() failed";
1569-
X509_NAME_free(subject);
1570-
goto failed;
1571-
}
1583+
/* clean subject name list, and set it for send to client */
1584+
SSL_set_client_CA_list(ssl_conn, name_chain);
1585+
}
15721586

1573-
/* add to trusted CA store */
1574-
if (X509_STORE_add_cert(ca_store, x509) == 0) {
1575-
*err = "X509_STORE_add_cert() failed";
1576-
goto failed;
1587+
if (trusted_chain != NULL) {
1588+
for (i = 0; i < sk_X509_num(trusted_chain); i++) {
1589+
x509 = sk_X509_value(trusted_chain, i);
1590+
if (x509 == NULL) {
1591+
*err = "sk_X509_value() failed";
1592+
goto failed;
1593+
}
1594+
1595+
/* add to trusted CA store */
1596+
if (X509_STORE_add_cert(ca_store, x509) == 0) {
1597+
*err = "X509_STORE_add_cert() failed";
1598+
goto failed;
1599+
}
15771600
}
15781601
}
15791602

1603+
/* clean ca_store, and store new ca_store */
15801604
if (SSL_set0_verify_cert_store(ssl_conn, ca_store) == 0) {
15811605
*err = "SSL_set0_verify_cert_store() failed";
15821606
goto failed;
15831607
}
1584-
1585-
SSL_set_client_CA_list(ssl_conn, name_chain);
15861608
}
15871609

15881610
return NGX_OK;

t/140-ssl-c-api.t

+167-13
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ if ($openssl_version =~ m/built with OpenSSL (0|1\.0\.(?:0|1[^\d]|2[a-d]).*)/) {
1212
plan(skip_all => "too old OpenSSL, need 1.0.2e, was $1");
1313

1414
} else {
15-
plan tests => repeat_each() * (blocks() * 5 + 1);
15+
plan tests => repeat_each() * (blocks() * 5 - 1);
1616
}
1717

1818
$ENV{TEST_NGINX_HTML_DIR} ||= html_dir();
@@ -72,7 +72,7 @@ ffi.cdef[[
7272
void ngx_http_lua_ffi_free_priv_key(void *cdata);
7373
7474
int ngx_http_lua_ffi_ssl_verify_client(void *r, void *cdata,
75-
int depth, char **err);
75+
void *cdata, int depth, char **err);
7676
7777
int ngx_http_lua_ffi_ssl_client_random(ngx_http_request_t *r,
7878
unsigned char *out, size_t *outlen, char **err);
@@ -853,21 +853,21 @@ lua ssl server name: "test.com"
853853
local cert_data = f:read("*all")
854854
f:close()
855855
856-
local cert = ffi.C.ngx_http_lua_ffi_parse_pem_cert(cert_data, #cert_data, errmsg)
857-
if not cert then
858-
ngx.log(ngx.ERR, "failed to parse PEM cert: ",
856+
local client_cert = ffi.C.ngx_http_lua_ffi_parse_pem_cert(cert_data, #cert_data, errmsg)
857+
if not client_cert then
858+
ngx.log(ngx.ERR, "failed to parse PEM client cert: ",
859859
ffi.string(errmsg[0]))
860860
return
861861
end
862862
863-
local rc = ffi.C.ngx_http_lua_ffi_ssl_verify_client(r, cert, 1, errmsg)
863+
local rc = ffi.C.ngx_http_lua_ffi_ssl_verify_client(r, client_cert, nil, 1, errmsg)
864864
if rc ~= 0 then
865865
ngx.log(ngx.ERR, "failed to verify client: ",
866866
ffi.string(errmsg[0]))
867867
return
868868
end
869869
870-
ffi.C.ngx_http_lua_ffi_free_cert(cert)
870+
ffi.C.ngx_http_lua_ffi_free_cert(client_cert)
871871
}
872872
873873
ssl_certificate ../../cert/test2.crt;
@@ -924,7 +924,7 @@ client certificate subject: [email protected],CN=test.com
924924
return
925925
end
926926
927-
local rc = ffi.C.ngx_http_lua_ffi_ssl_verify_client(r, nil, -1, errmsg)
927+
local rc = ffi.C.ngx_http_lua_ffi_ssl_verify_client(r, nil, nil, -1, errmsg)
928928
if rc ~= 0 then
929929
ngx.log(ngx.ERR, "failed to verify client: ",
930930
ffi.string(errmsg[0]))
@@ -990,21 +990,21 @@ client certificate subject: [email protected],CN=test.com
990990
local cert_data = f:read("*all")
991991
f:close()
992992
993-
local cert = ffi.C.ngx_http_lua_ffi_parse_pem_cert(cert_data, #cert_data, errmsg)
994-
if not cert then
995-
ngx.log(ngx.ERR, "failed to parse PEM cert: ",
993+
local client_cert = ffi.C.ngx_http_lua_ffi_parse_pem_cert(cert_data, #cert_data, errmsg)
994+
if not client_cert then
995+
ngx.log(ngx.ERR, "failed to parse PEM client cert: ",
996996
ffi.string(errmsg[0]))
997997
return
998998
end
999999
1000-
local rc = ffi.C.ngx_http_lua_ffi_ssl_verify_client(r, cert, 1, errmsg)
1000+
local rc = ffi.C.ngx_http_lua_ffi_ssl_verify_client(r, client_cert, nil, 1, errmsg)
10011001
if rc ~= 0 then
10021002
ngx.log(ngx.ERR, "failed to verify client: ",
10031003
ffi.string(errmsg[0]))
10041004
return
10051005
end
10061006
1007-
ffi.C.ngx_http_lua_ffi_free_cert(cert)
1007+
ffi.C.ngx_http_lua_ffi_free_cert(client_cert)
10081008
}
10091009
10101010
ssl_certificate ../../cert/test2.crt;
@@ -1623,3 +1623,157 @@ lua ssl server name: "test.com"
16231623
--- no_error_log
16241624
[error]
16251625
[alert]
1626+
1627+
1628+
1629+
=== TEST 13: verify client, but server don't trust root ca
1630+
--- http_config
1631+
server {
1632+
listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;
1633+
server_name example.com;
1634+
1635+
ssl_certificate_by_lua_block {
1636+
collectgarbage()
1637+
1638+
require "defines"
1639+
local ffi = require "ffi"
1640+
1641+
local errmsg = ffi.new("char *[1]")
1642+
1643+
local r = require "resty.core.base" .get_request()
1644+
if r == nil then
1645+
ngx.log(ngx.ERR, "no request found")
1646+
return
1647+
end
1648+
1649+
local f = assert(io.open("t/cert/mtls_server.crt", "rb"))
1650+
local cert_data = f:read("*all")
1651+
f:close()
1652+
1653+
local client_cert = ffi.C.ngx_http_lua_ffi_parse_pem_cert(cert_data, #cert_data, errmsg)
1654+
if not client_cert then
1655+
ngx.log(ngx.ERR, "failed to parse PEM client cert: ",
1656+
ffi.string(errmsg[0]))
1657+
return
1658+
end
1659+
1660+
local rc = ffi.C.ngx_http_lua_ffi_ssl_verify_client(r, client_cert, nil, 2, errmsg)
1661+
if rc ~= 0 then
1662+
ngx.log(ngx.ERR, "failed to verify client: ",
1663+
ffi.string(errmsg[0]))
1664+
return
1665+
end
1666+
1667+
ffi.C.ngx_http_lua_ffi_free_cert(client_cert)
1668+
}
1669+
1670+
ssl_certificate ../../cert/mtls_server.crt;
1671+
ssl_certificate_key ../../cert/mtls_server.key;
1672+
1673+
location / {
1674+
default_type 'text/plain';
1675+
content_by_lua_block {
1676+
ngx.say(ngx.var.ssl_client_verify)
1677+
}
1678+
more_clear_headers Date;
1679+
}
1680+
}
1681+
--- config
1682+
location /t {
1683+
proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;
1684+
proxy_ssl_certificate ../../cert/mtls_client.crt;
1685+
proxy_ssl_certificate_key ../../cert/mtls_client.key;
1686+
proxy_ssl_session_reuse off;
1687+
}
1688+
1689+
--- request
1690+
GET /t
1691+
--- response_body
1692+
FAILED:unable to verify the first certificate
1693+
1694+
--- no_error_log
1695+
[error]
1696+
[alert]
1697+
1698+
1699+
1700+
=== TEST 14: verify client and server trust root ca
1701+
--- http_config
1702+
server {
1703+
listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;
1704+
server_name example.com;
1705+
1706+
ssl_certificate_by_lua_block {
1707+
collectgarbage()
1708+
1709+
require "defines"
1710+
local ffi = require "ffi"
1711+
1712+
local errmsg = ffi.new("char *[1]")
1713+
1714+
local r = require "resty.core.base" .get_request()
1715+
if r == nil then
1716+
ngx.log(ngx.ERR, "no request found")
1717+
return
1718+
end
1719+
1720+
local f = assert(io.open("t/cert/mtls_server.crt", "rb"))
1721+
local cert_data = f:read("*all")
1722+
f:close()
1723+
1724+
local client_cert = ffi.C.ngx_http_lua_ffi_parse_pem_cert(cert_data, #cert_data, errmsg)
1725+
if not client_cert then
1726+
ngx.log(ngx.ERR, "failed to parse PEM client cert: ",
1727+
ffi.string(errmsg[0]))
1728+
return
1729+
end
1730+
1731+
local f = assert(io.open("t/cert/mtls_ca.crt", "rb"))
1732+
local cert_data = f:read("*all")
1733+
f:close()
1734+
1735+
local trusted_cert = ffi.C.ngx_http_lua_ffi_parse_pem_cert(cert_data, #cert_data, errmsg)
1736+
if not trusted_cert then
1737+
ngx.log(ngx.ERR, "failed to parse PEM trusted cert: ",
1738+
ffi.string(errmsg[0]))
1739+
return
1740+
end
1741+
1742+
local rc = ffi.C.ngx_http_lua_ffi_ssl_verify_client(r, cert, trusted_cert, 2, errmsg)
1743+
if rc ~= 0 then
1744+
ngx.log(ngx.ERR, "failed to verify client: ",
1745+
ffi.string(errmsg[0]))
1746+
return
1747+
end
1748+
1749+
ffi.C.ngx_http_lua_ffi_free_cert(client_cert)
1750+
ffi.C.ngx_http_lua_ffi_free_cert(trusted_cert)
1751+
}
1752+
1753+
ssl_certificate ../../cert/mtls_server.crt;
1754+
ssl_certificate_key ../../cert/mtls_server.key;
1755+
1756+
location / {
1757+
default_type 'text/plain';
1758+
content_by_lua_block {
1759+
ngx.say(ngx.var.ssl_client_verify)
1760+
}
1761+
more_clear_headers Date;
1762+
}
1763+
}
1764+
--- config
1765+
location /t {
1766+
proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;
1767+
proxy_ssl_certificate ../../cert/mtls_client.crt;
1768+
proxy_ssl_certificate_key ../../cert/mtls_client.key;
1769+
proxy_ssl_session_reuse off;
1770+
}
1771+
1772+
--- request
1773+
GET /t
1774+
--- response_body
1775+
SUCCESS
1776+
1777+
--- no_error_log
1778+
[error]
1779+
[alert]

0 commit comments

Comments
 (0)