Skip to content

feature: ngx_http_lua_ffi_ssl_ciphers #2424

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

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
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
58 changes: 58 additions & 0 deletions src/ngx_http_lua_ssl_certby.c
Original file line number Diff line number Diff line change
Expand Up @@ -833,6 +833,64 @@ ngx_http_lua_ffi_ssl_raw_server_addr(ngx_http_request_t *r, char **addr,
}


int
ngx_http_lua_ffi_req_shared_ssl_ciphers(ngx_http_request_t *r,
uint16_t *ciphers, uint16_t *nciphers, char **err)
{

#ifdef OPENSSL_IS_BORINGSSL

*err = "BoringSSL is not supported for SSL cipher operations";
return NGX_ERROR;

#else

ngx_ssl_conn_t *ssl_conn;
STACK_OF(SSL_CIPHER) *sk, *ck;
int sn, cn, i, n;
uint16_t tp;

if (r == NULL || r->connection == NULL || r->connection->ssl == NULL) {
*err = "bad request";
return NGX_ERROR;
}

ssl_conn = r->connection->ssl->connection;
if (ssl_conn == NULL) {
*err = "bad ssl conn";
return NGX_ERROR;
}

sk = SSL_get1_supported_ciphers(ssl_conn);
ck = SSL_get_client_ciphers(ssl_conn);
sn = sk_SSL_CIPHER_num(sk);
cn = sk_SSL_CIPHER_num(ck);

if (sn > *nciphers) {
*err = "buffer too small";
*nciphers = 0;
sk_SSL_CIPHER_free(sk);
return NGX_ERROR;
}

for (*nciphers = 0, i = 0; i < sn; i++) {
tp = SSL_CIPHER_get_protocol_id(sk_SSL_CIPHER_value(sk, i));
for (n = 0; n < cn; n++) {
if (SSL_CIPHER_get_protocol_id(sk_SSL_CIPHER_value(ck, n)) == tp) {
ciphers[(*nciphers)++] = tp;
break;
}
}
}

sk_SSL_CIPHER_free(sk);

return NGX_OK;
#endif

}


int
ngx_http_lua_ffi_ssl_server_name(ngx_http_request_t *r, char **name,
size_t *namelen, char **err)
Expand Down
226 changes: 224 additions & 2 deletions t/140-ssl-c-api.t
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@ 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");

} elsif ($openssl_version =~ m/BoringSSL/) {
$ENV{TEST_NGINX_USE_BORINGSSL} = 1;
plan tests => repeat_each() * (blocks() * 6 - 8);
} else {
plan tests => repeat_each() * (blocks() * 5 - 1);
plan tests => repeat_each() * (blocks() * 5 - 3);
$ENV{TEST_NGINX_USE_OPENSSL} = 1;
}

$ENV{TEST_NGINX_HTML_DIR} ||= html_dir();
Expand Down Expand Up @@ -77,6 +80,8 @@ ffi.cdef[[
int ngx_http_lua_ffi_ssl_client_random(ngx_http_request_t *r,
unsigned char *out, size_t *outlen, char **err);

int ngx_http_lua_ffi_req_shared_ssl_ciphers(void *r, uint16_t *ciphers,
uint16_t *nciphers, char **err);
]]
_EOC_
}
Expand Down Expand Up @@ -1212,6 +1217,7 @@ lua ssl server name: "test.com"


=== TEST 10: Raw SSL pointer
--- skip_eval: 8:$ENV{TEST_NGINX_USE_BORINGSSL}
--- http_config
server {
listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;
Expand Down Expand Up @@ -1777,3 +1783,219 @@ SUCCESS
--- no_error_log
[error]
[alert]



=== TEST 15: Get supported ciphers
--- skip_eval: 8:$ENV{TEST_NGINX_USE_BORINGSSL}
--- http_config
server {
listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;
server_name test.com;
ssl_certificate ../../cert/test.crt;
ssl_certificate_key ../../cert/test.key;
ssl_protocols TLSv1.2;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384;

server_tokens off;

location /ciphers {
content_by_lua_block {
require "defines"
local ffi = require "ffi"
local cjson = require "cjson.safe"
local base = require "resty.core.base"
local get_request = base.get_request

local MAX_CIPHERS = 64
local ciphers = ffi.new("uint16_t[?]", MAX_CIPHERS)
local nciphers = ffi.new("uint16_t[1]", MAX_CIPHERS)
local err = ffi.new("char*[1]")

local r = get_request()
local ret = ffi.C.ngx_http_lua_ffi_req_shared_ssl_ciphers(r, ciphers, nciphers, err)

if ret ~= 0 then
ngx.log(ngx.ERR, "error: ", ffi.string(err[0]))
return
end

local res = {}
for i = 0, nciphers[0] - 1 do
local cipher_id = string.format("%04x", ciphers[i])
table.insert(res, cipher_id)
end

ngx.say(cjson.encode(res))
}
}
}
--- config
server_tokens off;
location /t {
proxy_ssl_protocols TLSv1.2;
proxy_ssl_session_reuse off;
proxy_ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256;
proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock:/ciphers;
}
--- request
GET /t
--- response_body_like
\["c02f","c02b"\]
--- error_log chomp
TLSv1.2, cipher: "ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(128) Mac=AEAD"



=== TEST 16: SSL cipher API error handling (no SSL)
--- skip_eval: 8:$ENV{TEST_NGINX_USE_BORINGSSL}
--- config
location /t {
content_by_lua_block {
require "defines"
local ffi = require "ffi"

local ciphers = ffi.new("uint16_t[64]")
local nciphers = ffi.new("uint16_t[1]", 64)
local err = ffi.new("char*[1]")

-- use nil request to trigger error
local ret = ffi.C.ngx_http_lua_ffi_req_shared_ssl_ciphers(nil, ciphers, nciphers, err)

ngx.say("ret: ", ret)
if err[0] ~= nil then
ngx.say("err: ", ffi.string(err[0]))
end
}
}
--- request
GET /t
--- response_body
ret: -1
err: bad request

--- no_error_log
[error]
[alert]



=== TEST 17: Buffer overflow handling
--- skip_eval: 8:$ENV{TEST_NGINX_USE_BORINGSSL}
--- http_config
server {
listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;
server_name test.com;
ssl_certificate ../../cert/test.crt;
ssl_certificate_key ../../cert/test.key;
ssl_protocols TLSv1.2;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384;

server_tokens off;


location /ciphers {
content_by_lua_block {
require "defines"
local ffi = require "ffi"
local base = require "resty.core.base"
local get_request = base.get_request
local cjson = require "cjson.safe"

local MAX_CIPHERS = 64
local ciphers = ffi.new("uint16_t[?]", MAX_CIPHERS)
local nciphers = ffi.new("uint16_t[1]", MAX_CIPHERS)
local err = ffi.new("char*[1]")

local r = get_request()
local ret = ffi.C.ngx_http_lua_ffi_req_shared_ssl_ciphers(r, ciphers, nciphers, err)

if ret ~= 0 then
ngx.log(ngx.ERR, "error: ", ffi.string(err[0]))
return
end
local res = {}
for i = 0, nciphers[0] - 1 do
local cipher_id = string.format("%04x", ciphers[i])

table.insert(res, cipher_id)
end
ngx.say(cjson.encode(res))
}
}
}
--- config
server_tokens off;
location /t {
proxy_ssl_protocols TLSv1.2;
proxy_ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256;
proxy_ssl_session_reuse off;
proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock:/ciphers;
}
--- request
GET /t
--- response_body_like
\["c02f"\]
--- error_code: 200
--- error_log chomp
TLSv1.2, cipher: "ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(128) Mac=AEAD"



=== TEST 18: BORINGSSL error handling
--- skip_eval: 8:$ENV{TEST_NGINX_USE_OPENSSL}
--- http_config
server {
listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;
server_name test.com;
ssl_certificate ../../cert/test.crt;
ssl_certificate_key ../../cert/test.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384;

server_tokens off;


location /ciphers {
content_by_lua_block {
require "defines"
local ffi = require "ffi"
local base = require "resty.core.base"
local get_request = base.get_request

local MAX_CIPHERS = 64
local ciphers = ffi.new("uint16_t[?]", MAX_CIPHERS)
local nciphers = ffi.new("uint16_t[1]", MAX_CIPHERS)
local err = ffi.new("char*[1]")

local r = get_request()
local ret = ffi.C.ngx_http_lua_ffi_req_shared_ssl_ciphers(r, ciphers, nciphers, err)

if ret ~= 0 then
ngx.say("Error: ", ffi.string(err[0]))
return
end

}
}
}
--- config
server_tokens off;
location /t {
proxy_ssl_protocols TLSv1.2 TLSv1.3;
proxy_ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256;
proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock:/ciphers;
}
--- request
GET /t
--- response_body_like chomp
Error: BoringSSL is not supported for SSL cipher operations
--- error_code: 200

--- no_error_log
[error]
[alert]