Skip to content

Commit 97937e2

Browse files
authored
feature: add ssl trusted certificate.
1 parent e974e57 commit 97937e2

14 files changed

+635
-35
lines changed

src/ngx_stream_lua_ssl_certby.c

Lines changed: 52 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1472,7 +1472,7 @@ ngx_stream_lua_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store)
14721472

14731473
int
14741474
ngx_stream_lua_ffi_ssl_verify_client(ngx_stream_lua_request_t *r,
1475-
void *ca_certs, int depth, char **err)
1475+
void *client_cert, void *trusted_certs, int depth, char **err)
14761476
{
14771477
#ifdef LIBRESSL_VERSION_NUMBER
14781478

@@ -1488,7 +1488,8 @@ ngx_stream_lua_ffi_ssl_verify_client(ngx_stream_lua_request_t *r,
14881488
#else
14891489
ngx_stream_ssl_conf_t *sscf;
14901490
#endif
1491-
STACK_OF(X509) *chain = ca_certs;
1491+
STACK_OF(X509) *client_chain = client_cert;
1492+
STACK_OF(X509) *trusted_chain = trusted_certs;
14921493
STACK_OF(X509_NAME) *name_chain = NULL;
14931494
X509 *x509 = NULL;
14941495
X509_NAME *subject = NULL;
@@ -1544,54 +1545,75 @@ ngx_stream_lua_ffi_ssl_verify_client(ngx_stream_lua_request_t *r,
15441545

15451546
/* set CA chain */
15461547

1547-
if (chain != NULL) {
1548+
if (client_chain != NULL || trusted_chain != NULL) {
1549+
15481550
ca_store = X509_STORE_new();
15491551
if (ca_store == NULL) {
15501552
*err = "X509_STORE_new() failed";
15511553
return NGX_ERROR;
15521554
}
15531555

1554-
/* construct name chain */
1556+
if (client_chain != NULL) {
15551557

1556-
name_chain = sk_X509_NAME_new_null();
1557-
if (name_chain == NULL) {
1558-
*err = "sk_X509_NAME_new_null() failed";
1559-
goto failed;
1560-
}
1561-
1562-
for (i = 0; i < sk_X509_num(chain); i++) {
1563-
x509 = sk_X509_value(chain, i);
1564-
if (x509 == NULL) {
1565-
*err = "sk_X509_value() failed";
1558+
/* construct name chain */
1559+
name_chain = sk_X509_NAME_new_null();
1560+
if (name_chain == NULL) {
1561+
*err = "sk_X509_NAME_new_null() failed";
15661562
goto failed;
15671563
}
15681564

1569-
/* add subject to name chain, which will be sent to client */
1570-
subject = X509_NAME_dup(X509_get_subject_name(x509));
1571-
if (subject == NULL) {
1572-
*err = "X509_get_subject_name() failed";
1573-
goto failed;
1565+
for (i = 0; i < sk_X509_num(client_chain); i++) {
1566+
x509 = sk_X509_value(client_chain, i);
1567+
if (x509 == NULL) {
1568+
*err = "sk_X509_value() failed";
1569+
goto failed;
1570+
}
1571+
1572+
/* add subject to name chain, which will be sent to client */
1573+
subject = X509_NAME_dup(X509_get_subject_name(x509));
1574+
if (subject == NULL) {
1575+
*err = "X509_get_subject_name() failed";
1576+
goto failed;
1577+
}
1578+
1579+
if (!sk_X509_NAME_push(name_chain, subject)) {
1580+
*err = "sk_X509_NAME_push() failed";
1581+
X509_NAME_free(subject);
1582+
goto failed;
1583+
}
1584+
1585+
/* add to trusted CA store */
1586+
if (X509_STORE_add_cert(ca_store, x509) == 0) {
1587+
*err = "X509_STORE_add_cert() failed";
1588+
goto failed;
1589+
}
15741590
}
15751591

1576-
if (!sk_X509_NAME_push(name_chain, subject)) {
1577-
*err = "sk_X509_NAME_push() failed";
1578-
X509_NAME_free(subject);
1579-
goto failed;
1580-
}
1592+
/* clean subject name list, and set it for send to client */
1593+
SSL_set_client_CA_list(ssl_conn, name_chain);
1594+
}
15811595

1582-
/* add to trusted CA store */
1583-
if (X509_STORE_add_cert(ca_store, x509) == 0) {
1584-
*err = "X509_STORE_add_cert() failed";
1585-
goto failed;
1596+
if (trusted_chain != NULL) {
1597+
for (i = 0; i < sk_X509_num(trusted_chain); i++) {
1598+
x509 = sk_X509_value(trusted_chain, i);
1599+
if (x509 == NULL) {
1600+
*err = "sk_X509_value() failed";
1601+
goto failed;
1602+
}
1603+
1604+
/* add to trusted CA store */
1605+
if (X509_STORE_add_cert(ca_store, x509) == 0) {
1606+
*err = "X509_STORE_add_cert() failed";
1607+
goto failed;
1608+
}
15861609
}
15871610
}
15881611

1612+
/* clean ca_store, and store new ca_store */
15891613
if (SSL_set0_verify_cert_store(ssl_conn, ca_store) == 0) {
15901614
*err = "SSL_set0_verify_cert_store() failed";
15911615
goto failed;
15921616
}
1593-
1594-
SSL_set_client_CA_list(ssl_conn, name_chain);
15951617
}
15961618

15971619
return NGX_OK;

t/140-ssl-c-api.t

Lines changed: 143 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ my $openssl_version = eval { `$NginxBinary -V 2>&1` };
1010
if ($openssl_version =~ m/built with OpenSSL (0|1\.0\.(?:0|1[^\d]|2[a-d]).*)/) {
1111
plan(skip_all => "too old OpenSSL, need 1.0.2e, was $1");
1212
} else {
13-
plan tests => repeat_each() * (blocks() * 5 + 1);
13+
plan tests => repeat_each() * (blocks() * 5 - 1);
1414
}
1515

1616
$ENV{TEST_NGINX_HTML_DIR} ||= html_dir();
@@ -67,7 +67,7 @@ ffi.cdef[[
6767
6868
void ngx_stream_lua_ffi_free_priv_key(void *cdata);
6969
70-
int ngx_stream_lua_ffi_ssl_verify_client(void *r, void *cdata, int depth, char **err);
70+
int ngx_stream_lua_ffi_ssl_verify_client(void *r, void *cdata, void *cdata, int depth, char **err);
7171
7272
int ngx_stream_lua_ffi_ssl_client_random(ngx_stream_lua_request_t *r,
7373
unsigned char *out, size_t *outlen, char **err);
@@ -722,7 +722,7 @@ lua ssl server name: "test.com"
722722
return
723723
end
724724
725-
local rc = ffi.C.ngx_stream_lua_ffi_ssl_verify_client(r, cert, -1, errmsg)
725+
local rc = ffi.C.ngx_stream_lua_ffi_ssl_verify_client(r, cert, nil, -1, errmsg)
726726
if rc ~= 0 then
727727
ngx.log(ngx.ERR, "failed to set cdata cert: ",
728728
ffi.string(errmsg[0]))
@@ -778,7 +778,7 @@ client certificate subject: [email protected],CN=test.com
778778
return
779779
end
780780
781-
local rc = ffi.C.ngx_stream_lua_ffi_ssl_verify_client(r, nil, -1, errmsg)
781+
local rc = ffi.C.ngx_stream_lua_ffi_ssl_verify_client(r, nil, nil, -1, errmsg)
782782
if rc ~= 0 then
783783
ngx.log(ngx.ERR, "failed to set cdata cert: ",
784784
ffi.string(errmsg[0]))
@@ -843,7 +843,7 @@ client certificate subject: [email protected],CN=test.com
843843
return
844844
end
845845
846-
local rc = ffi.C.ngx_stream_lua_ffi_ssl_verify_client(r, cert, 1, errmsg)
846+
local rc = ffi.C.ngx_stream_lua_ffi_ssl_verify_client(r, cert, nil, 1, errmsg)
847847
if rc ~= 0 then
848848
ngx.log(ngx.ERR, "failed to set cdata cert: ",
849849
ffi.string(errmsg[0]))
@@ -1236,3 +1236,141 @@ lua ssl server name: "test.com"
12361236
--- no_error_log
12371237
[error]
12381238
[alert]
1239+
1240+
1241+
1242+
=== TEST 12: verify client, but server don't trust root ca
1243+
--- stream_config
1244+
server {
1245+
listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;
1246+
1247+
ssl_certificate ../../cert/mtls_server.crt;
1248+
ssl_certificate_key ../../cert/mtls_server.key;
1249+
1250+
ssl_certificate_by_lua_block {
1251+
collectgarbage()
1252+
1253+
local ffi = require "ffi"
1254+
require "defines"
1255+
1256+
local errmsg = ffi.new("char *[1]")
1257+
1258+
local r = require "resty.core.base" .get_request()
1259+
if not r then
1260+
ngx.log(ngx.ERR, "no request found")
1261+
return
1262+
end
1263+
1264+
local f = assert(io.open("t/cert/mtls_server.crt", "rb"))
1265+
local cert_data = f:read("*all")
1266+
f:close()
1267+
1268+
local client_certs = ffi.C.ngx_stream_lua_ffi_parse_pem_cert(cert_data, #cert_data, errmsg)
1269+
if not client_certs then
1270+
ngx.log(ngx.ERR, "failed to parse PEM client certs: ",
1271+
ffi.string(errmsg[0]))
1272+
return
1273+
end
1274+
1275+
local rc = ffi.C.ngx_stream_lua_ffi_ssl_verify_client(r, client_certs, nil, 1, errmsg)
1276+
if rc ~= 0 then
1277+
ngx.log(ngx.ERR, "failed to set cdata cert: ",
1278+
ffi.string(errmsg[0]))
1279+
return
1280+
end
1281+
1282+
ffi.C.ngx_stream_lua_ffi_free_cert(client_certs)
1283+
}
1284+
1285+
content_by_lua_block {
1286+
ngx.say(ngx.var.ssl_client_verify)
1287+
}
1288+
}
1289+
--- stream_server_config
1290+
proxy_pass unix:$TEST_NGINX_HTML_DIR/nginx.sock;
1291+
proxy_ssl on;
1292+
proxy_ssl_certificate ../../cert/mtls_client.crt;
1293+
proxy_ssl_certificate_key ../../cert/mtls_client.key;
1294+
proxy_ssl_session_reuse off;
1295+
1296+
--- stream_response
1297+
FAILED:unable to verify the first certificate
1298+
1299+
--- no_error_log
1300+
[error]
1301+
[alert]
1302+
1303+
1304+
1305+
=== TEST 13: verify client and server trust root ca
1306+
--- stream_config
1307+
server {
1308+
listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;
1309+
1310+
ssl_certificate ../../cert/mtls_server.crt;
1311+
ssl_certificate_key ../../cert/mtls_server.key;
1312+
1313+
ssl_certificate_by_lua_block {
1314+
collectgarbage()
1315+
1316+
local ffi = require "ffi"
1317+
require "defines"
1318+
1319+
local errmsg = ffi.new("char *[1]")
1320+
1321+
local r = require "resty.core.base" .get_request()
1322+
if not r then
1323+
ngx.log(ngx.ERR, "no request found")
1324+
return
1325+
end
1326+
1327+
local f = assert(io.open("t/cert/mtls_server.crt", "rb"))
1328+
local cert_data = f:read("*all")
1329+
f:close()
1330+
1331+
local client_certs = ffi.C.ngx_stream_lua_ffi_parse_pem_cert(cert_data, #cert_data, errmsg)
1332+
if not client_certs then
1333+
ngx.log(ngx.ERR, "failed to parse PEM client certs: ",
1334+
ffi.string(errmsg[0]))
1335+
return
1336+
end
1337+
1338+
local f = assert(io.open("t/cert/mtls_ca.crt", "rb"))
1339+
local cert_data = f:read("*all")
1340+
f:close()
1341+
1342+
local trusted_certs = ffi.C.ngx_stream_lua_ffi_parse_pem_cert(cert_data, #cert_data, errmsg)
1343+
if not trusted_certs then
1344+
ngx.log(ngx.ERR, "failed to parse PEM trusted certs: ",
1345+
ffi.string(errmsg[0]))
1346+
return
1347+
end
1348+
1349+
local rc = ffi.C.ngx_stream_lua_ffi_ssl_verify_client(r, client_certs, trusted_certs, 1, errmsg)
1350+
if rc ~= 0 then
1351+
ngx.log(ngx.ERR, "failed to set cdata cert: ",
1352+
ffi.string(errmsg[0]))
1353+
return
1354+
end
1355+
1356+
ffi.C.ngx_stream_lua_ffi_free_cert(client_certs)
1357+
ffi.C.ngx_stream_lua_ffi_free_cert(trusted_certs)
1358+
}
1359+
1360+
content_by_lua_block {
1361+
ngx.say(ngx.var.ssl_client_verify)
1362+
}
1363+
}
1364+
--- stream_server_config
1365+
proxy_pass unix:$TEST_NGINX_HTML_DIR/nginx.sock;
1366+
proxy_ssl on;
1367+
proxy_ssl_certificate ../../cert/mtls_client.crt;
1368+
proxy_ssl_certificate_key ../../cert/mtls_client.key;
1369+
proxy_ssl_session_reuse off;
1370+
1371+
--- stream_response
1372+
SUCCESS
1373+
1374+
--- no_error_log
1375+
[error]
1376+
[alert]

t/cert/mtls_ca.crt

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
Certificate:
2+
Data:
3+
Version: 3 (0x2)
4+
Serial Number:
5+
32:ed:21:56:d8:4e:aa:03:89:a9:4a:a4:e2:85:2d:8a:3b:2b:89:22
6+
Signature Algorithm: sha256WithRSAEncryption
7+
Issuer: C = US, ST = California, O = OpenResty, CN = OpenResty Testing Root CA
8+
Validity
9+
Not Before: Mar 13 15:49:00 2022 GMT
10+
Not After : Mar 8 15:49:00 2042 GMT
11+
Subject: C = US, ST = California, O = OpenResty, CN = OpenResty Testing Root CA
12+
Subject Public Key Info:
13+
Public Key Algorithm: rsaEncryption
14+
RSA Public-Key: (2048 bit)
15+
Modulus:
16+
00:e6:37:d2:c6:17:36:c7:b2:7f:7d:cf:d0:62:87:
17+
99:d9:21:b8:de:ff:d8:e2:3a:1c:68:90:8f:ce:17:
18+
68:22:b0:60:30:cc:29:e8:34:ee:ff:b2:25:de:6e:
19+
1a:d4:df:10:19:11:4b:40:61:d3:a9:4d:80:ed:97:
20+
81:4e:c5:74:e8:4d:63:e3:5f:21:bc:5a:6e:22:a0:
21+
17:91:c1:cb:25:53:9b:9d:4e:e1:51:5b:f6:52:e7:
22+
0a:27:f6:16:c2:31:cb:6c:47:f4:89:51:15:cc:06:
23+
be:31:3e:1c:ea:ee:81:9b:c4:97:96:fd:e5:1c:95:
24+
9e:c0:65:cd:a9:9a:cb:68:67:f2:62:a0:21:eb:5a:
25+
c5:a1:92:ed:32:41:28:f9:47:34:eb:44:ae:d6:e7:
26+
76:71:11:98:c9:2e:ce:6c:7c:10:1b:c7:4c:c3:14:
27+
89:4e:d9:4c:d9:c7:43:e9:3c:29:ca:62:a9:91:b3:
28+
87:e7:d7:b4:18:ab:65:f9:6b:ed:82:ca:a1:36:35:
29+
18:05:cb:5c:24:26:13:13:f8:99:ac:99:be:9b:a6:
30+
73:df:0d:16:95:b1:dc:be:fe:7a:c2:b6:dc:c8:93:
31+
cf:10:e0:29:03:0e:28:78:18:84:ee:14:92:ab:be:
32+
5a:a0:14:a2:4a:2f:d3:d0:b8:0e:00:d2:5a:cd:e4:
33+
bd:a1
34+
Exponent: 65537 (0x10001)
35+
X509v3 extensions:
36+
X509v3 Key Usage: critical
37+
Certificate Sign, CRL Sign
38+
X509v3 Basic Constraints: critical
39+
CA:TRUE
40+
X509v3 Subject Key Identifier:
41+
F0:D7:4B:14:73:E1:67:00:6B:54:B4:19:20:76:12:9F:9D:8E:C8:09
42+
Signature Algorithm: sha256WithRSAEncryption
43+
6d:52:21:6d:6e:8c:e5:4a:28:07:65:6d:d8:7c:23:2e:c6:c1:
44+
d0:ec:27:b3:b0:c3:d3:e8:fa:72:b9:de:32:4e:ff:97:8d:86:
45+
a9:6d:b3:a9:b4:2d:77:ca:28:97:6a:3d:7b:a2:15:ed:34:dc:
46+
72:9f:6f:e7:01:0c:d3:28:6a:80:1b:50:09:fd:d7:2c:d8:92:
47+
d5:10:c4:73:15:20:7d:99:dc:de:30:7b:3c:6e:e9:66:b2:0e:
48+
4e:1a:c1:51:57:6e:5b:b0:a9:f6:ff:0b:8f:07:67:31:40:5b:
49+
11:a9:06:d3:d3:76:c5:d2:56:95:9a:9e:4a:16:44:4b:32:e5:
50+
af:dd:4b:4d:5d:57:b8:85:69:36:93:2a:c6:0c:8f:e1:42:35:
51+
be:8e:f3:e7:35:d3:2c:3a:03:31:40:75:8e:e8:dd:57:35:20:
52+
5e:18:a9:76:ce:85:be:7e:3a:cf:6e:08:58:5b:47:d5:e9:c4:
53+
ec:0e:e9:8e:3c:2d:5c:7b:59:20:5b:24:92:a0:e0:1e:a3:5a:
54+
67:d8:ff:7f:a5:82:f1:df:db:05:65:79:88:b1:3c:e6:01:d1:
55+
5a:c7:d2:6e:9a:e6:a2:da:4a:c7:19:78:d9:14:71:6e:1f:70:
56+
f3:41:e5:b3:78:31:d5:22:0e:7c:1a:b2:43:d9:86:ff:53:ea:
57+
2b:ba:d2:27
58+
-----BEGIN CERTIFICATE-----
59+
MIIDhDCCAmygAwIBAgIUMu0hVthOqgOJqUqk4oUtijsriSIwDQYJKoZIhvcNAQEL
60+
BQAwWjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExEjAQBgNVBAoT
61+
CU9wZW5SZXN0eTEiMCAGA1UEAxMZT3BlblJlc3R5IFRlc3RpbmcgUm9vdCBDQTAe
62+
Fw0yMjAzMTMxNTQ5MDBaFw00MjAzMDgxNTQ5MDBaMFoxCzAJBgNVBAYTAlVTMRMw
63+
EQYDVQQIEwpDYWxpZm9ybmlhMRIwEAYDVQQKEwlPcGVuUmVzdHkxIjAgBgNVBAMT
64+
GU9wZW5SZXN0eSBUZXN0aW5nIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IB
65+
DwAwggEKAoIBAQDmN9LGFzbHsn99z9Bih5nZIbje/9jiOhxokI/OF2gisGAwzCno
66+
NO7/siXebhrU3xAZEUtAYdOpTYDtl4FOxXToTWPjXyG8Wm4ioBeRwcslU5udTuFR
67+
W/ZS5won9hbCMctsR/SJURXMBr4xPhzq7oGbxJeW/eUclZ7AZc2pmstoZ/JioCHr
68+
WsWhku0yQSj5RzTrRK7W53ZxEZjJLs5sfBAbx0zDFIlO2UzZx0PpPCnKYqmRs4fn
69+
17QYq2X5a+2CyqE2NRgFy1wkJhMT+Jmsmb6bpnPfDRaVsdy+/nrCttzIk88Q4CkD
70+
Dih4GITuFJKrvlqgFKJKL9PQuA4A0lrN5L2hAgMBAAGjQjBAMA4GA1UdDwEB/wQE
71+
AwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTw10sUc+FnAGtUtBkgdhKf
72+
nY7ICTANBgkqhkiG9w0BAQsFAAOCAQEAbVIhbW6M5UooB2Vt2HwjLsbB0Owns7DD
73+
0+j6crneMk7/l42GqW2zqbQtd8ool2o9e6IV7TTccp9v5wEM0yhqgBtQCf3XLNiS
74+
1RDEcxUgfZnc3jB7PG7pZrIOThrBUVduW7Cp9v8LjwdnMUBbEakG09N2xdJWlZqe
75+
ShZESzLlr91LTV1XuIVpNpMqxgyP4UI1vo7z5zXTLDoDMUB1jujdVzUgXhipds6F
76+
vn46z24IWFtH1enE7A7pjjwtXHtZIFskkqDgHqNaZ9j/f6WC8d/bBWV5iLE85gHR
77+
WsfSbprmotpKxxl42RRxbh9w80Hls3gx1SIOfBqyQ9mG/1PqK7rSJw==
78+
-----END CERTIFICATE-----

0 commit comments

Comments
 (0)