Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: add ssl trusted certificate #353

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 52 additions & 30 deletions src/ngx_stream_lua_ssl_certby.c
Original file line number Diff line number Diff line change
Expand Up @@ -1472,7 +1472,7 @@ ngx_stream_lua_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store)

int
ngx_stream_lua_ffi_ssl_verify_client(ngx_stream_lua_request_t *r,
void *ca_certs, int depth, char **err)
void *client_cert, void *trusted_certs, int depth, char **err)
{
#ifdef LIBRESSL_VERSION_NUMBER

Expand All @@ -1488,7 +1488,8 @@ ngx_stream_lua_ffi_ssl_verify_client(ngx_stream_lua_request_t *r,
#else
ngx_stream_ssl_conf_t *sscf;
#endif
STACK_OF(X509) *chain = ca_certs;
STACK_OF(X509) *client_chain = client_cert;
STACK_OF(X509) *trusted_chain = trusted_certs;
STACK_OF(X509_NAME) *name_chain = NULL;
X509 *x509 = NULL;
X509_NAME *subject = NULL;
Expand Down Expand Up @@ -1544,54 +1545,75 @@ ngx_stream_lua_ffi_ssl_verify_client(ngx_stream_lua_request_t *r,

/* set CA chain */

if (chain != NULL) {
if (client_chain != NULL || trusted_chain != NULL) {

ca_store = X509_STORE_new();
if (ca_store == NULL) {
*err = "X509_STORE_new() failed";
return NGX_ERROR;
}

/* construct name chain */
if (client_chain != NULL) {

name_chain = sk_X509_NAME_new_null();
if (name_chain == NULL) {
*err = "sk_X509_NAME_new_null() failed";
goto failed;
}

for (i = 0; i < sk_X509_num(chain); i++) {
x509 = sk_X509_value(chain, i);
if (x509 == NULL) {
*err = "sk_X509_value() failed";
/* construct name chain */
name_chain = sk_X509_NAME_new_null();
if (name_chain == NULL) {
*err = "sk_X509_NAME_new_null() failed";
goto failed;
}

/* add subject to name chain, which will be sent to client */
subject = X509_NAME_dup(X509_get_subject_name(x509));
if (subject == NULL) {
*err = "X509_get_subject_name() failed";
goto failed;
for (i = 0; i < sk_X509_num(client_chain); i++) {
x509 = sk_X509_value(client_chain, i);
if (x509 == NULL) {
*err = "sk_X509_value() failed";
goto failed;
}

/* add subject to name chain, which will be sent to client */
subject = X509_NAME_dup(X509_get_subject_name(x509));
if (subject == NULL) {
*err = "X509_get_subject_name() failed";
goto failed;
}

if (!sk_X509_NAME_push(name_chain, subject)) {
*err = "sk_X509_NAME_push() failed";
X509_NAME_free(subject);
goto failed;
}

/* add to trusted CA store */
if (X509_STORE_add_cert(ca_store, x509) == 0) {
*err = "X509_STORE_add_cert() failed";
goto failed;
}
}

if (!sk_X509_NAME_push(name_chain, subject)) {
*err = "sk_X509_NAME_push() failed";
X509_NAME_free(subject);
goto failed;
}
/* clean subject name list, and set it for send to client */
SSL_set_client_CA_list(ssl_conn, name_chain);
}

/* add to trusted CA store */
if (X509_STORE_add_cert(ca_store, x509) == 0) {
*err = "X509_STORE_add_cert() failed";
goto failed;
if (trusted_chain != NULL) {
for (i = 0; i < sk_X509_num(trusted_chain); i++) {
x509 = sk_X509_value(trusted_chain, i);
if (x509 == NULL) {
*err = "sk_X509_value() failed";
goto failed;
}

/* add to trusted CA store */
if (X509_STORE_add_cert(ca_store, x509) == 0) {
*err = "X509_STORE_add_cert() failed";
goto failed;
}
}
}

/* clean ca_store, and store new ca_store */
if (SSL_set0_verify_cert_store(ssl_conn, ca_store) == 0) {
*err = "SSL_set0_verify_cert_store() failed";
goto failed;
}

SSL_set_client_CA_list(ssl_conn, name_chain);
}

return NGX_OK;
Expand Down
148 changes: 143 additions & 5 deletions t/140-ssl-c-api.t
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ my $openssl_version = eval { `$NginxBinary -V 2>&1` };
if ($openssl_version =~ m/built with OpenSSL (0|1\.0\.(?:0|1[^\d]|2[a-d]).*)/) {
plan(skip_all => "too old OpenSSL, need 1.0.2e, was $1");
} else {
plan tests => repeat_each() * (blocks() * 5 + 1);
plan tests => repeat_each() * (blocks() * 5 - 1);
}

$ENV{TEST_NGINX_HTML_DIR} ||= html_dir();
Expand Down Expand Up @@ -67,7 +67,7 @@ ffi.cdef[[

void ngx_stream_lua_ffi_free_priv_key(void *cdata);

int ngx_stream_lua_ffi_ssl_verify_client(void *r, void *cdata, int depth, char **err);
int ngx_stream_lua_ffi_ssl_verify_client(void *r, void *cdata, void *cdata, int depth, char **err);

int ngx_stream_lua_ffi_ssl_client_random(ngx_stream_lua_request_t *r,
unsigned char *out, size_t *outlen, char **err);
Expand Down Expand Up @@ -722,7 +722,7 @@ lua ssl server name: "test.com"
return
end

local rc = ffi.C.ngx_stream_lua_ffi_ssl_verify_client(r, cert, -1, errmsg)
local rc = ffi.C.ngx_stream_lua_ffi_ssl_verify_client(r, cert, nil, -1, errmsg)
if rc ~= 0 then
ngx.log(ngx.ERR, "failed to set cdata cert: ",
ffi.string(errmsg[0]))
Expand Down Expand Up @@ -778,7 +778,7 @@ client certificate subject: [email protected],CN=test.com
return
end

local rc = ffi.C.ngx_stream_lua_ffi_ssl_verify_client(r, nil, -1, errmsg)
local rc = ffi.C.ngx_stream_lua_ffi_ssl_verify_client(r, nil, nil, -1, errmsg)
if rc ~= 0 then
ngx.log(ngx.ERR, "failed to set cdata cert: ",
ffi.string(errmsg[0]))
Expand Down Expand Up @@ -843,7 +843,7 @@ client certificate subject: [email protected],CN=test.com
return
end

local rc = ffi.C.ngx_stream_lua_ffi_ssl_verify_client(r, cert, 1, errmsg)
local rc = ffi.C.ngx_stream_lua_ffi_ssl_verify_client(r, cert, nil, 1, errmsg)
if rc ~= 0 then
ngx.log(ngx.ERR, "failed to set cdata cert: ",
ffi.string(errmsg[0]))
Expand Down Expand Up @@ -1236,3 +1236,141 @@ lua ssl server name: "test.com"
--- no_error_log
[error]
[alert]



=== TEST 12: verify client, but server don't trust root ca
--- stream_config
server {
listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;

ssl_certificate ../../cert/mtls_server.crt;
ssl_certificate_key ../../cert/mtls_server.key;

ssl_certificate_by_lua_block {
collectgarbage()

local ffi = require "ffi"
require "defines"

local errmsg = ffi.new("char *[1]")

local r = require "resty.core.base" .get_request()
if not r then
ngx.log(ngx.ERR, "no request found")
return
end

local f = assert(io.open("t/cert/mtls_server.crt", "rb"))
local cert_data = f:read("*all")
f:close()

local client_certs = ffi.C.ngx_stream_lua_ffi_parse_pem_cert(cert_data, #cert_data, errmsg)
if not client_certs then
ngx.log(ngx.ERR, "failed to parse PEM client certs: ",
ffi.string(errmsg[0]))
return
end

local rc = ffi.C.ngx_stream_lua_ffi_ssl_verify_client(r, client_certs, nil, 1, errmsg)
if rc ~= 0 then
ngx.log(ngx.ERR, "failed to set cdata cert: ",
ffi.string(errmsg[0]))
return
end

ffi.C.ngx_stream_lua_ffi_free_cert(client_certs)
}

content_by_lua_block {
ngx.say(ngx.var.ssl_client_verify)
}
}
--- stream_server_config
proxy_pass unix:$TEST_NGINX_HTML_DIR/nginx.sock;
proxy_ssl on;
proxy_ssl_certificate ../../cert/mtls_client.crt;
proxy_ssl_certificate_key ../../cert/mtls_client.key;
proxy_ssl_session_reuse off;

--- stream_response
FAILED:unable to verify the first certificate

--- no_error_log
[error]
[alert]



=== TEST 13: verify client and server trust root ca
--- stream_config
server {
listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;

ssl_certificate ../../cert/mtls_server.crt;
ssl_certificate_key ../../cert/mtls_server.key;

ssl_certificate_by_lua_block {
collectgarbage()

local ffi = require "ffi"
require "defines"

local errmsg = ffi.new("char *[1]")

local r = require "resty.core.base" .get_request()
if not r then
ngx.log(ngx.ERR, "no request found")
return
end

local f = assert(io.open("t/cert/mtls_server.crt", "rb"))
local cert_data = f:read("*all")
f:close()

local client_certs = ffi.C.ngx_stream_lua_ffi_parse_pem_cert(cert_data, #cert_data, errmsg)
if not client_certs then
ngx.log(ngx.ERR, "failed to parse PEM client certs: ",
ffi.string(errmsg[0]))
return
end

local f = assert(io.open("t/cert/mtls_ca.crt", "rb"))
local cert_data = f:read("*all")
f:close()

local trusted_certs = ffi.C.ngx_stream_lua_ffi_parse_pem_cert(cert_data, #cert_data, errmsg)
if not trusted_certs then
ngx.log(ngx.ERR, "failed to parse PEM trusted certs: ",
ffi.string(errmsg[0]))
return
end

local rc = ffi.C.ngx_stream_lua_ffi_ssl_verify_client(r, client_certs, trusted_certs, 1, errmsg)
if rc ~= 0 then
ngx.log(ngx.ERR, "failed to set cdata cert: ",
ffi.string(errmsg[0]))
return
end

ffi.C.ngx_stream_lua_ffi_free_cert(client_certs)
ffi.C.ngx_stream_lua_ffi_free_cert(trusted_certs)
}

content_by_lua_block {
ngx.say(ngx.var.ssl_client_verify)
}
}
--- stream_server_config
proxy_pass unix:$TEST_NGINX_HTML_DIR/nginx.sock;
proxy_ssl on;
proxy_ssl_certificate ../../cert/mtls_client.crt;
proxy_ssl_certificate_key ../../cert/mtls_client.key;
proxy_ssl_session_reuse off;

--- stream_response
SUCCESS

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