Skip to content

Commit

Permalink
feature: add ngx_http_lua_ffi_parse_der_cert and ngx_http_lua_ffi_par…
Browse files Browse the repository at this point in the history
…se_der_key functions.
  • Loading branch information
devicenull authored and zhuizhuhaomeng committed Jan 20, 2024
1 parent 0e769b7 commit 7598ff3
Show file tree
Hide file tree
Showing 6 changed files with 269 additions and 4 deletions.
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ env:
- LUAJIT_INC=$LUAJIT_PREFIX/include/luajit-2.1
- LUA_INCLUDE_DIR=$LUAJIT_INC
- PCRE_VER=8.45
- PCRE2_VER=10.37
- PCRE2_VER=10.42
- PCRE_PREFIX=/opt/pcre
- PCRE2_PREFIX=/opt/pcre2
- PCRE_LIB=$PCRE_PREFIX/lib
Expand Down Expand Up @@ -81,7 +81,7 @@ before_install:
install:
- if [ ! -f download-cache/drizzle7-$DRIZZLE_VER.tar.gz ]; then wget -P download-cache https://github.com/openresty/openresty-deps-prebuild/releases/download/v20230902/drizzle7-$DRIZZLE_VER.tar.gz; fi
- if [ "$USE_PCRE2" != "Y" ] && [ ! -f download-cache/pcre-$PCRE_VER.tar.gz ]; then wget -P download-cache https://downloads.sourceforge.net/project/pcre/pcre/${PCRE_VER}/pcre-${PCRE_VER}.tar.gz; fi
- if [ "$USE_PCRE2" = "Y" ] && [ ! -f download-cache/pcre2-$PCRE2_VER.tar.gz ]; then wget -P download-cache https://downloads.sourceforge.net/project/pcre/pcre2/${PCRE2_VER}/pcre2-${PCRE2_VER}.tar.gz; fi
- if [ "$USE_PCRE2" = "Y" ] && [ ! -f download-cache/pcre2-$PCRE2_VER.tar.gz ]; then wget -P download-cache https://github.com/PCRE2Project/pcre2/releases/download/pcre2-${PCRE2_VER}/pcre2-${PCRE2_VER}.tar.gz; fi
- if [ -n "$OPENSSL_VER" ] && [ ! -f download-cache/openssl-$OPENSSL_VER.tar.gz ]; then wget -P download-cache https://www.openssl.org/source/openssl-$OPENSSL_VER.tar.gz || wget -P download-cache https://www.openssl.org/source/old/${OPENSSL_VER//[a-z]/}/openssl-$OPENSSL_VER.tar.gz; fi
- if [ -n "$OPENSSL_VER" ] && [ ! -f download-cache/openssl-$OPENSSL_VER.tar.gz ]; then wget -P download-cache https://www.openssl.org/source/openssl-$OPENSSL_VER.tar.gz || wget -P download-cache https://www.openssl.org/source/old/${OPENSSL_VER//[a-z]/}/openssl-$OPENSSL_VER.tar.gz; fi
- wget https://github.com/openresty/openresty-deps-prebuild/releases/download/v20230902/boringssl-20230902-x64-focal.tar.gz
Expand Down
4 changes: 2 additions & 2 deletions README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -7916,11 +7916,11 @@ Set client certificate chain and corresponding private key to the TCP socket obj
The certificate chain and private key provided will be used later by the [tcpsock:sslhandshake](#tcpsocksslhandshake) method.

* `cert` specify a client certificate chain cdata object that will be used while handshaking with
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)
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) or [ngx.ssl.parse\_der\_cert](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl.md#parse_der_cert)
function provided by lua-resty-core. Note that specifying the `cert` option requires
corresponding `pkey` be provided too. See below.
* `pkey` specify a private key corresponds to the `cert` option above.
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)
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) or [ngx.ssl.parse\_der\_priv\_key](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl.md#parse_der_priv_key)
function provided by lua-resty-core.

If both of `cert` and `pkey` are `nil`, this method will clear any existing client certificate and private key
Expand Down
109 changes: 109 additions & 0 deletions src/ngx_http_lua_ssl_certby.c
Original file line number Diff line number Diff line change
Expand Up @@ -1172,6 +1172,81 @@ ngx_http_lua_ffi_parse_pem_cert(const u_char *pem, size_t pem_len,
}


void *
ngx_http_lua_ffi_parse_der_cert(const char *data, size_t len,
char **err)
{
BIO *bio;
X509 *x509;
STACK_OF(X509) *chain;

if (data == NULL || len == 0) {
*err = "invalid argument";
ERR_clear_error();
return NULL;
}

bio = BIO_new_mem_buf((char *) data, len);
if (bio == NULL) {
*err = "BIO_new_mem_buf() failed";
ERR_clear_error();
return NULL;
}

x509 = d2i_X509_bio(bio, NULL);
if (x509 == NULL) {
*err = "d2i_X509_bio() failed";
BIO_free(bio);
ERR_clear_error();
return NULL;
}

chain = sk_X509_new_null();
if (chain == NULL) {
*err = "sk_X509_new_null() failed";
X509_free(x509);
BIO_free(bio);
ERR_clear_error();
return NULL;
}

if (sk_X509_push(chain, x509) == 0) {
*err = "sk_X509_push() failed";
sk_X509_free(chain);
X509_free(x509);
BIO_free(bio);
ERR_clear_error();
return NULL;
}

/* read rest of the chain */

while (!BIO_eof(bio)) {
x509 = d2i_X509_bio(bio, NULL);
if (x509 == NULL) {
*err = "d2i_X509_bio() failed in rest of chain";
sk_X509_pop_free(chain, X509_free);
BIO_free(bio);
ERR_clear_error();
return NULL;
}

if (sk_X509_push(chain, x509) == 0) {
*err = "sk_X509_push() failed in rest of chain";
sk_X509_pop_free(chain, X509_free);
X509_free(x509);
BIO_free(bio);
ERR_clear_error();
return NULL;
}
}

BIO_free(bio);

return chain;
}


void
ngx_http_lua_ffi_free_cert(void *cdata)
{
Expand Down Expand Up @@ -1209,6 +1284,40 @@ ngx_http_lua_ffi_parse_pem_priv_key(const u_char *pem, size_t pem_len,
}


void *
ngx_http_lua_ffi_parse_der_priv_key(const char *data, size_t len,
char **err)
{
BIO *bio = NULL;
EVP_PKEY *pkey = NULL;

if (data == NULL || len == 0) {
*err = "invalid argument";
ERR_clear_error();
return NULL;
}

bio = BIO_new_mem_buf((char *) data, len);
if (bio == NULL) {
*err = "BIO_new_mem_buf() failed";
ERR_clear_error();
return NULL;
}

pkey = d2i_PrivateKey_bio(bio, NULL);
if (pkey == NULL) {
*err = "d2i_PrivateKey_bio() failed";
BIO_free(bio);
ERR_clear_error();
return NULL;
}

BIO_free(bio);

return pkey;
}


void
ngx_http_lua_ffi_free_priv_key(void *cdata)
{
Expand Down
156 changes: 156 additions & 0 deletions t/140-ssl-c-api.t
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ ffi.cdef[[
void *ngx_http_lua_ffi_parse_pem_priv_key(const unsigned char *pem,
size_t pem_len, char **err);

void *ngx_http_lua_ffi_parse_der_cert(const char *data, size_t len,
char **err);

void *ngx_http_lua_ffi_parse_der_priv_key(const char *data, size_t len,
char **err);

int ngx_http_lua_ffi_set_cert(void *r,
void *cdata, char **err);

Expand Down Expand Up @@ -1323,3 +1329,153 @@ SNI is test.com
--- no_error_log
[error]
[alert]



=== TEST 11: DER cert + private key cdata
--- http_config
server {
listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;
server_name test.com;

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 r == nil then
ngx.log(ngx.ERR, "no request found")
return
end

ffi.C.ngx_http_lua_ffi_ssl_clear_certs(r, errmsg)

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

local cert = ffi.C.ngx_http_lua_ffi_parse_der_cert(cert_data, #cert_data, errmsg)
if not cert then
ngx.log(ngx.ERR, "failed to parse DER cert: ",
ffi.string(errmsg[0]))
return
end

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

ffi.C.ngx_http_lua_ffi_free_cert(cert)

f = assert(io.open("t/cert/test_der.key", "rb"))
local pkey_data = f:read("*all")
f:close()

local pkey = ffi.C.ngx_http_lua_ffi_parse_der_priv_key(pkey_data, #pkey_data, errmsg)
if pkey == nil then
ngx.log(ngx.ERR, "failed to parse DER priv key: ",
ffi.string(errmsg[0]))
return
end

local rc = ffi.C.ngx_http_lua_ffi_set_priv_key(r, pkey, errmsg)
if rc ~= 0 then
ngx.log(ngx.ERR, "failed to set cdata priv key: ",
ffi.string(errmsg[0]))
return
end

ffi.C.ngx_http_lua_ffi_free_priv_key(pkey)
}

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

server_tokens off;
location /foo {
default_type 'text/plain';
content_by_lua_block { ngx.status = 201 ngx.say("foo") ngx.exit(201) }
more_clear_headers Date;
}
}
--- config
server_tokens off;
lua_ssl_trusted_certificate ../../cert/test.crt;

location /t {
content_by_lua_block {
do
local sock = ngx.socket.tcp()

sock:settimeout(2000)

local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock")
if not ok then
ngx.say("failed to connect: ", err)
return
end

ngx.say("connected: ", ok)

local sess, err = sock:sslhandshake(nil, "test.com", true)
if not sess then
ngx.say("failed to do SSL handshake: ", err)
return
end

ngx.say("ssl handshake: ", type(sess))

local req = "GET /foo HTTP/1.0\r\nHost: test.com\r\nConnection: close\r\n\r\n"
local bytes, err = sock:send(req)
if not bytes then
ngx.say("failed to send http request: ", err)
return
end

ngx.say("sent http request: ", bytes, " bytes.")

while true do
local line, err = sock:receive()
if not line then
-- ngx.say("failed to receive response status line: ", err)
break
end

ngx.say("received: ", line)
end

local ok, err = sock:close()
ngx.say("close: ", ok, " ", err)
end -- do
-- collectgarbage()
}
}

--- request
GET /t
--- response_body
connected: 1
ssl handshake: cdata
sent http request: 56 bytes.
received: HTTP/1.1 201 Created
received: Server: nginx
received: Content-Type: text/plain
received: Content-Length: 4
received: Connection: close
received:
received: foo
close: 1 nil

--- error_log
lua ssl server name: "test.com"

--- no_error_log
[error]
[alert]
Binary file added t/cert/test_der.crt
Binary file not shown.
Binary file added t/cert/test_der.key
Binary file not shown.

0 comments on commit 7598ff3

Please sign in to comment.