From adacb4f49c4ae391700c7e9d35163b830d750a60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rnar=20Ness?= Date: Wed, 3 Nov 2021 11:56:19 +0100 Subject: [PATCH 1/6] feature: ngx_http_lua_ffi_ssl_ciphers Add ngx_http_lua_ffi_ssl_ciphers that returns a uint16_t array of tls_protocol_id supported (and enabled) by both server and client --- src/ngx_http_lua_ssl_certby.c | 49 +++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/src/ngx_http_lua_ssl_certby.c b/src/ngx_http_lua_ssl_certby.c index b561122b13..3cc66fe48a 100644 --- a/src/ngx_http_lua_ssl_certby.c +++ b/src/ngx_http_lua_ssl_certby.c @@ -823,6 +823,55 @@ ngx_http_lua_ffi_ssl_raw_server_addr(ngx_http_request_t *r, char **addr, } +int +ngx_http_lua_ffi_ssl_ciphers(ngx_http_request_t *r, uint16_t *ciphers, + uint16_t *nciphers, char **err) +{ + ngx_ssl_conn_t *ssl_conn; + STACK_OF(SSL_CIPHER) *sk, *ck; + int sn, cn, i, n; + uint16_t tp; + + if (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; +} + + int ngx_http_lua_ffi_ssl_server_name(ngx_http_request_t *r, char **name, size_t *namelen, char **err) From cf482164534a32369ef8b2e4a520dd269ed045ab Mon Sep 17 00:00:00 2001 From: Sunny Chan Date: Wed, 4 Jun 2025 04:08:29 +0000 Subject: [PATCH 2/6] add: adapt boringssl and add Test --- src/ngx_http_lua_ssl_certby.c | 13 +- t/140-ssl-c-api.t | 248 ++++++++++++++++++++++++++++++++-- 2 files changed, 248 insertions(+), 13 deletions(-) diff --git a/src/ngx_http_lua_ssl_certby.c b/src/ngx_http_lua_ssl_certby.c index 8743bed119..7d170de802 100644 --- a/src/ngx_http_lua_ssl_certby.c +++ b/src/ngx_http_lua_ssl_certby.c @@ -837,12 +837,20 @@ int ngx_http_lua_ffi_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->connection == NULL || r->connection->ssl == NULL) { + if (r == NULL || r->connection == NULL || r->connection->ssl == NULL) { *err = "bad request"; return NGX_ERROR; } @@ -862,7 +870,6 @@ ngx_http_lua_ffi_ssl_ciphers(ngx_http_request_t *r, uint16_t *ciphers, *err = "buffer too small"; *nciphers = 0; sk_SSL_CIPHER_free(sk); - return NGX_ERROR; } @@ -879,6 +886,8 @@ ngx_http_lua_ffi_ssl_ciphers(ngx_http_request_t *r, uint16_t *ciphers, sk_SSL_CIPHER_free(sk); return NGX_OK; +#endif + } diff --git a/t/140-ssl-c-api.t b/t/140-ssl-c-api.t index 81d8375bb5..205df8e3b8 100644 --- a/t/140-ssl-c-api.t +++ b/t/140-ssl-c-api.t @@ -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(); @@ -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_ssl_ciphers(void *r, uint16_t *ciphers, + uint16_t *nciphers, char **err); ]] _EOC_ } @@ -229,7 +234,7 @@ connected: 1 ssl handshake: cdata sent http request: 56 bytes. received: HTTP/1.1 201 Created -received: Server: nginx +received: Server: openresty received: Content-Type: text/plain received: Content-Length: 4 received: Connection: close @@ -383,7 +388,7 @@ connected: 1 ssl handshake: cdata sent http request: 56 bytes. received: HTTP/1.1 201 Created -received: Server: nginx +received: Server: openresty received: Content-Type: text/plain received: Content-Length: 4 received: Connection: close @@ -511,7 +516,7 @@ connected: 1 ssl handshake: cdata sent http request: 56 bytes. received: HTTP/1.1 201 Created -received: Server: nginx +received: Server: openresty received: Content-Type: text/plain received: Content-Length: 4 received: Connection: close @@ -662,7 +667,7 @@ connected: 1 ssl handshake: cdata sent http request: 56 bytes. received: HTTP/1.1 201 Created -received: Server: nginx +received: Server: openresty received: Content-Type: text/plain received: Content-Length: 4 received: Connection: close @@ -812,7 +817,7 @@ connected: 1 ssl handshake: cdata sent http request: 56 bytes. received: HTTP/1.1 201 Created -received: Server: nginx +received: Server: openresty received: Content-Type: text/plain received: Content-Length: 4 received: Connection: close @@ -1194,7 +1199,7 @@ connected: 1 ssl handshake: cdata sent http request: 56 bytes. received: HTTP/1.1 201 Created -received: Server: nginx +received: Server: openresty received: Content-Type: text/plain received: Content-Length: 4 received: Connection: close @@ -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; @@ -1318,7 +1324,7 @@ connected: 1 ssl handshake: cdata sent http request: 56 bytes. received: HTTP/1.1 201 Created -received: Server: nginx +received: Server: openresty received: Content-Type: text/plain received: Content-Length: 4 received: Connection: close @@ -1468,7 +1474,7 @@ connected: 1 ssl handshake: cdata sent http request: 56 bytes. received: HTTP/1.1 201 Created -received: Server: nginx +received: Server: openresty received: Content-Type: text/plain received: Content-Length: 4 received: Connection: close @@ -1609,7 +1615,7 @@ connected: 1 ssl handshake: cdata sent http request: 56 bytes. received: HTTP/1.1 201 Created -received: Server: nginx +received: Server: openresty received: Content-Type: text/plain received: Content-Length: 4 received: Connection: close @@ -1777,3 +1783,223 @@ 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_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]") + + -- 使用无效的请求上下文 + local ret = ffi.C.ngx_http_lua_ffi_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_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_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] + + + + + + + + + + + From 43a4f9c7b09394ae3bc50d3c37d8c525de80d1ab Mon Sep 17 00:00:00 2001 From: Sunny Chan Date: Mon, 9 Jun 2025 10:14:50 +0000 Subject: [PATCH 3/6] refactor: rename and format --- src/ngx_http_lua_ssl_certby.c | 4 ++-- t/140-ssl-c-api.t | 22 +++++++++------------- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/src/ngx_http_lua_ssl_certby.c b/src/ngx_http_lua_ssl_certby.c index 7d170de802..76939e35f0 100644 --- a/src/ngx_http_lua_ssl_certby.c +++ b/src/ngx_http_lua_ssl_certby.c @@ -834,8 +834,8 @@ ngx_http_lua_ffi_ssl_raw_server_addr(ngx_http_request_t *r, char **addr, int -ngx_http_lua_ffi_ssl_ciphers(ngx_http_request_t *r, uint16_t *ciphers, - uint16_t *nciphers, char **err) +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 diff --git a/t/140-ssl-c-api.t b/t/140-ssl-c-api.t index 205df8e3b8..011c4f54d1 100644 --- a/t/140-ssl-c-api.t +++ b/t/140-ssl-c-api.t @@ -80,7 +80,7 @@ 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_ssl_ciphers(void *r, uint16_t *ciphers, + int ngx_http_lua_ffi_req_shared_ssl_ciphers(void *r, uint16_t *ciphers, uint16_t *nciphers, char **err); ]] _EOC_ @@ -1813,7 +1813,7 @@ SUCCESS local err = ffi.new("char*[1]") local r = get_request() - local ret = ffi.C.ngx_http_lua_ffi_ssl_ciphers(r, ciphers, nciphers, err) + 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])) @@ -1846,6 +1846,7 @@ GET /t 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 @@ -1858,8 +1859,8 @@ TLSv1.2, cipher: "ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM( local nciphers = ffi.new("uint16_t[1]", 64) local err = ffi.new("char*[1]") - -- 使用无效的请求上下文 - local ret = ffi.C.ngx_http_lua_ffi_ssl_ciphers(nil, ciphers, nciphers, err) + -- 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 @@ -1878,6 +1879,7 @@ err: bad request [alert] + === TEST 17: Buffer overflow handling --- skip_eval: 8:$ENV{TEST_NGINX_USE_BORINGSSL} --- http_config @@ -1906,7 +1908,7 @@ err: bad request local err = ffi.new("char*[1]") local r = get_request() - local ret = ffi.C.ngx_http_lua_ffi_ssl_ciphers(r, ciphers, nciphers, err) + 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])) @@ -1939,6 +1941,7 @@ GET /t 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 @@ -1966,7 +1969,7 @@ TLSv1.2, cipher: "ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM( local err = ffi.new("char*[1]") local r = get_request() - local ret = ffi.C.ngx_http_lua_ffi_ssl_ciphers(r, ciphers, nciphers, err) + 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])) @@ -1996,10 +1999,3 @@ Error: BoringSSL is not supported for SSL cipher operations - - - - - - - From 67e49674a08b7329d310dbca4c11101354201d1e Mon Sep 17 00:00:00 2001 From: Sunny Chan Date: Fri, 13 Jun 2025 08:18:33 +0000 Subject: [PATCH 4/6] revert: Server to nginx --- t/140-ssl-c-api.t | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/t/140-ssl-c-api.t b/t/140-ssl-c-api.t index 011c4f54d1..ce514f3cbf 100644 --- a/t/140-ssl-c-api.t +++ b/t/140-ssl-c-api.t @@ -234,7 +234,7 @@ connected: 1 ssl handshake: cdata sent http request: 56 bytes. received: HTTP/1.1 201 Created -received: Server: openresty +received: Server: nginx received: Content-Type: text/plain received: Content-Length: 4 received: Connection: close @@ -388,7 +388,7 @@ connected: 1 ssl handshake: cdata sent http request: 56 bytes. received: HTTP/1.1 201 Created -received: Server: openresty +received: Server: nginx received: Content-Type: text/plain received: Content-Length: 4 received: Connection: close @@ -516,7 +516,7 @@ connected: 1 ssl handshake: cdata sent http request: 56 bytes. received: HTTP/1.1 201 Created -received: Server: openresty +received: Server: nginx received: Content-Type: text/plain received: Content-Length: 4 received: Connection: close @@ -667,7 +667,7 @@ connected: 1 ssl handshake: cdata sent http request: 56 bytes. received: HTTP/1.1 201 Created -received: Server: openresty +received: Server: nginx received: Content-Type: text/plain received: Content-Length: 4 received: Connection: close @@ -817,7 +817,7 @@ connected: 1 ssl handshake: cdata sent http request: 56 bytes. received: HTTP/1.1 201 Created -received: Server: openresty +received: Server: nginx received: Content-Type: text/plain received: Content-Length: 4 received: Connection: close @@ -1199,7 +1199,7 @@ connected: 1 ssl handshake: cdata sent http request: 56 bytes. received: HTTP/1.1 201 Created -received: Server: openresty +received: Server: nginx received: Content-Type: text/plain received: Content-Length: 4 received: Connection: close @@ -1324,7 +1324,7 @@ connected: 1 ssl handshake: cdata sent http request: 56 bytes. received: HTTP/1.1 201 Created -received: Server: openresty +received: Server: nginx received: Content-Type: text/plain received: Content-Length: 4 received: Connection: close @@ -1474,7 +1474,7 @@ connected: 1 ssl handshake: cdata sent http request: 56 bytes. received: HTTP/1.1 201 Created -received: Server: openresty +received: Server: nginx received: Content-Type: text/plain received: Content-Length: 4 received: Connection: close @@ -1615,7 +1615,7 @@ connected: 1 ssl handshake: cdata sent http request: 56 bytes. received: HTTP/1.1 201 Created -received: Server: openresty +received: Server: nginx received: Content-Type: text/plain received: Content-Length: 4 received: Connection: close From 2ad0ac37d9c9a5b6d85371b130282a361a8c4307 Mon Sep 17 00:00:00 2001 From: Sunny Chan Date: Tue, 1 Jul 2025 07:47:37 +0000 Subject: [PATCH 5/6] add filter_grease option --- src/ngx_http_lua_ssl_certby.c | 25 ++++++++-- t/140-ssl-c-api.t | 91 ++++++++++++++++++++++++++++++++--- 2 files changed, 107 insertions(+), 9 deletions(-) diff --git a/src/ngx_http_lua_ssl_certby.c b/src/ngx_http_lua_ssl_certby.c index 76939e35f0..ae954e4f39 100644 --- a/src/ngx_http_lua_ssl_certby.c +++ b/src/ngx_http_lua_ssl_certby.c @@ -36,6 +36,7 @@ static u_char *ngx_http_lua_log_ssl_cert_error(ngx_log_t *log, u_char *buf, size_t len); static ngx_int_t ngx_http_lua_ssl_cert_by_chunk(lua_State *L, ngx_http_request_t *r); +static int ngx_http_lua_is_grease_cipher(uint16_t cipher_id); ngx_int_t @@ -446,6 +447,20 @@ ngx_http_lua_log_ssl_cert_error(ngx_log_t *log, u_char *buf, size_t len) } +static int +ngx_http_lua_is_grease_cipher(uint16_t cipher_id) +{ + /* GREASE values follow pattern: 0x?A?A where ? can be any hex digit */ + /* and both ? must be the same */ + uint8_t high_byte = (cipher_id >> 8) & 0xFF; + uint8_t low_byte = cipher_id & 0xFF; + /* Check if both bytes follow ?A pattern and high nibbles match */ + return ((high_byte & 0x0F) == 0x0A) && + ((low_byte & 0x0F) == 0x0A) && + ((high_byte & 0xF0) == (low_byte & 0xF0)); +} + + static ngx_int_t ngx_http_lua_ssl_cert_by_chunk(lua_State *L, ngx_http_request_t *r) { @@ -835,16 +850,14 @@ 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) + uint16_t *ciphers, uint16_t *nciphers, int filter_grease, 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; @@ -875,6 +888,12 @@ ngx_http_lua_ffi_req_shared_ssl_ciphers(ngx_http_request_t *r, for (*nciphers = 0, i = 0; i < sn; i++) { tp = SSL_CIPHER_get_protocol_id(sk_SSL_CIPHER_value(sk, i)); + + /* Skip GREASE ciphers if filtering is enabled */ + if (filter_grease && ngx_http_lua_is_grease_cipher(tp)) { + continue; + } + for (n = 0; n < cn; n++) { if (SSL_CIPHER_get_protocol_id(sk_SSL_CIPHER_value(ck, n)) == tp) { ciphers[(*nciphers)++] = tp; diff --git a/t/140-ssl-c-api.t b/t/140-ssl-c-api.t index ce514f3cbf..15ed8abfcf 100644 --- a/t/140-ssl-c-api.t +++ b/t/140-ssl-c-api.t @@ -14,7 +14,7 @@ if ($openssl_version =~ m/built with OpenSSL (0|1\.0\.(?:0|1[^\d]|2[a-d]).*)/) { $ENV{TEST_NGINX_USE_BORINGSSL} = 1; plan tests => repeat_each() * (blocks() * 6 - 8); } else { - plan tests => repeat_each() * (blocks() * 5 - 3); + plan tests => repeat_each() * (blocks() * 5 - 5); $ENV{TEST_NGINX_USE_OPENSSL} = 1; } @@ -81,7 +81,7 @@ ffi.cdef[[ 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); + uint16_t *nciphers, int filter_grease, char **err); ]] _EOC_ } @@ -1813,7 +1813,7 @@ SUCCESS 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) + local ret = ffi.C.ngx_http_lua_ffi_req_shared_ssl_ciphers(r, ciphers, nciphers, 0, err) if ret ~= 0 then ngx.log(ngx.ERR, "error: ", ffi.string(err[0])) @@ -1860,7 +1860,7 @@ TLSv1.2, cipher: "ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM( 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) + local ret = ffi.C.ngx_http_lua_ffi_req_shared_ssl_ciphers(nil, ciphers, nciphers, 0, err) ngx.say("ret: ", ret) if err[0] ~= nil then @@ -1908,7 +1908,7 @@ err: bad request 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) + local ret = ffi.C.ngx_http_lua_ffi_req_shared_ssl_ciphers(r, ciphers, nciphers, 0, err) if ret ~= 0 then ngx.log(ngx.ERR, "error: ", ffi.string(err[0])) @@ -1969,7 +1969,7 @@ TLSv1.2, cipher: "ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM( 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) + local ret = ffi.C.ngx_http_lua_ffi_req_shared_ssl_ciphers(r, ciphers, nciphers, 0, err) if ret ~= 0 then ngx.say("Error: ", ffi.string(err[0])) @@ -1998,4 +1998,83 @@ Error: BoringSSL is not supported for SSL cipher operations +=== TEST 19: Get supported ciphers with GREASE filtering +--- 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() + -- Test without GREASE filtering + local ret = ffi.C.ngx_http_lua_ffi_req_shared_ssl_ciphers(r, ciphers, nciphers, 0, err) + if ret ~= 0 then + ngx.log(ngx.ERR, "error without filtering: ", ffi.string(err[0])) + return + end + + local res_no_filter = {} + for i = 0, nciphers[0] - 1 do + local cipher_id = string.format("%04x", ciphers[i]) + table.insert(res_no_filter, cipher_id) + end + + -- Reset buffers + nciphers[0] = MAX_CIPHERS + + -- Test with GREASE filtering + local ret = ffi.C.ngx_http_lua_ffi_req_shared_ssl_ciphers(r, ciphers, nciphers, 1, err) + if ret ~= 0 then + ngx.log(ngx.ERR, "error with filtering: ", ffi.string(err[0])) + return + end + + local res_with_filter = {} + for i = 0, nciphers[0] - 1 do + local cipher_id = string.format("%04x", ciphers[i]) + table.insert(res_with_filter, cipher_id) + end + + ngx.say("without_filter:", cjson.encode(res_no_filter)) + ngx.say("with_filter:", cjson.encode(res_with_filter)) + } + } + } +--- 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 +without_filter:\[.*\] +with_filter:\[.*\] +--- error_log chomp +TLSv1.2, cipher: "ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(128) Mac=AEAD" + + + From 718dd45950b085e42d3d05f48bcc3a92843033a7 Mon Sep 17 00:00:00 2001 From: Sunny Chan Date: Wed, 2 Jul 2025 02:59:44 +0000 Subject: [PATCH 6/6] improve the code --- src/ngx_http_lua_ssl_certby.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/ngx_http_lua_ssl_certby.c b/src/ngx_http_lua_ssl_certby.c index cbba27ec33..c87b24e76e 100644 --- a/src/ngx_http_lua_ssl_certby.c +++ b/src/ngx_http_lua_ssl_certby.c @@ -456,12 +456,8 @@ ngx_http_lua_is_grease_cipher(uint16_t cipher_id) { /* GREASE values follow pattern: 0x?A?A where ? can be any hex digit */ /* and both ? must be the same */ - uint8_t high_byte = (cipher_id >> 8) & 0xFF; - uint8_t low_byte = cipher_id & 0xFF; /* Check if both bytes follow ?A pattern and high nibbles match */ - return ((high_byte & 0x0F) == 0x0A) && - ((low_byte & 0x0F) == 0x0A) && - ((high_byte & 0xF0) == (low_byte & 0xF0)); + return (cipher_id & 0x0F0F) == 0x0A0A; } @@ -865,7 +861,7 @@ ngx_http_lua_ffi_req_shared_ssl_ciphers(ngx_http_request_t *r, ngx_ssl_conn_t *ssl_conn; STACK_OF(SSL_CIPHER) *sk, *ck; int sn, cn, i, n; - uint16_t tp; + uint16_t cipher; if (r == NULL || r->connection == NULL || r->connection->ssl == NULL) { *err = "bad request"; @@ -891,16 +887,18 @@ ngx_http_lua_ffi_req_shared_ssl_ciphers(ngx_http_request_t *r, } for (*nciphers = 0, i = 0; i < sn; i++) { - tp = SSL_CIPHER_get_protocol_id(sk_SSL_CIPHER_value(sk, i)); - + cipher = SSL_CIPHER_get_protocol_id(sk_SSL_CIPHER_value(sk, i)); + /* Skip GREASE ciphers if filtering is enabled */ - if (filter_grease && ngx_http_lua_is_grease_cipher(tp)) { + if (filter_grease && ngx_http_lua_is_grease_cipher(cipher)) { continue; } - + for (n = 0; n < cn; n++) { - if (SSL_CIPHER_get_protocol_id(sk_SSL_CIPHER_value(ck, n)) == tp) { - ciphers[(*nciphers)++] = tp; + if (SSL_CIPHER_get_protocol_id(sk_SSL_CIPHER_value(ck, n)) + == cipher) + { + ciphers[(*nciphers)++] = cipher; break; } }