Skip to content

feature: get_client_hello_ciphers() #498

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
72 changes: 72 additions & 0 deletions lib/ngx/ssl/clienthello.lua
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@ local lshift = bit.lshift
local table_insert = table.insert
local table_new = require "table.new"
local intp = ffi.new("int*[1]")
local usp = ffi.new("unsigned short*[1]")


local ngx_lua_ffi_ssl_get_client_hello_server_name
local ngx_lua_ffi_ssl_get_client_hello_ext
local ngx_lua_ffi_ssl_set_protocols
local ngx_lua_ffi_ssl_get_client_hello_ext_present
local ngx_lua_ffi_ssl_get_client_hello_ciphers


if subsystem == 'http' then
Expand All @@ -41,8 +43,13 @@ if subsystem == 'http' then

int ngx_http_lua_ffi_ssl_set_protocols(ngx_http_request_t *r,
int protocols, char **err);

int ngx_http_lua_ffi_ssl_get_client_hello_ext_present(ngx_http_request_t *r,
int **extensions, size_t *extensions_len, char **err);
/* Undefined for the stream subsystem */
int ngx_http_lua_ffi_ssl_get_client_hello_ciphers(ngx_http_request_t *r,
int **ciphers, size_t *cipherslen, char **err);
/* Undefined for the stream subsystem */
]]

ngx_lua_ffi_ssl_get_client_hello_server_name =
Expand All @@ -52,6 +59,9 @@ if subsystem == 'http' then
ngx_lua_ffi_ssl_set_protocols = C.ngx_http_lua_ffi_ssl_set_protocols
ngx_lua_ffi_ssl_get_client_hello_ext_present =
C.ngx_http_lua_ffi_ssl_get_client_hello_ext_present
ngx_lua_ffi_ssl_get_client_hello_ciphers =
C.ngx_http_lua_ffi_ssl_get_client_hello_ciphers



elseif subsystem == 'stream' then
Expand Down Expand Up @@ -83,6 +93,27 @@ local ccharpp = ffi.new("const char*[1]")
local cucharpp = ffi.new("const unsigned char*[1]")


--https://datatracker.ietf.org/doc/html/rfc8701
local TLS_GREASE = {
[2570] = true,
[6682] = true,
[10794] = true,
[14906] = true,
[19018] = true,
[23130] = true,
[27242] = true,
[31354] = true,
[35466] = true,
[39578] = true,
[43690] = true,
[47802] = true,
[51914] = true,
[56026] = true,
[60138] = true,
[64250] = true
}


-- return server_name, err
function _M.get_client_hello_server_name()
local r = get_request()
Expand Down Expand Up @@ -125,6 +156,8 @@ function _M.get_client_hello_ext_present()

local rc = ngx_lua_ffi_ssl_get_client_hello_ext_present(r, intp,
sizep, errmsg)
-- the function used under the hood, SSL_client_hello_get1_extensions_present,
-- already excludes GREASE, thank G*d
if rc == FFI_OK then -- Convert C array to Lua table
local array = intp[0]
local size = tonumber(sizep[0])
Expand All @@ -144,6 +177,45 @@ function _M.get_client_hello_ext_present()
return nil, ffi_str(errmsg[0])
end

-- return ciphers_table, err
-- excluding GREASE ciphers
function _M.get_client_hello_ciphers()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

local r = get_request()
if not r then
error("no request found")
end

if ngx_phase() ~= "ssl_client_hello" then
error("API disabled in the current context")
end

local sizep = get_size_ptr()

local rc = ngx_lua_ffi_ssl_get_client_hello_ciphers(r, usp,
sizep, errmsg)
if rc == FFI_OK then
local ciphers_table = table_new(16, 0)
local array = usp[0]
local size = tonumber(sizep[0])
local y = 1
for i=0, size-1, 1 do
if not TLS_GREASE[array[i]] then
ciphers_table[y] = array[i]
y = y + 1
end
end

return ciphers_table
end

-- NGX_DECLINED
if rc == -5 then
return nil
end

return nil, ffi_str(errmsg[0])
end

-- return ext, err
function _M.get_client_hello_ext(ext_type)
local r = get_request()
Expand Down
98 changes: 98 additions & 0 deletions t/ssl-client-hello.t
Original file line number Diff line number Diff line change
Expand Up @@ -1065,3 +1065,101 @@ qr/1: TLS EXT \d+, context: ssl_client_hello_by_lua/
[alert]
[crit]
[placeholder]


=== TEST 11: log ciphers in the clienthello packet
--- http_config
lua_package_path "$TEST_NGINX_LUA_PACKAGE_PATH";

server {
listen 127.0.0.2:$TEST_NGINX_RAND_PORT_1 ssl;
server_name test.com;
ssl_client_hello_by_lua_block {
local ssl_clt = require "ngx.ssl.clienthello"
local all_extensions, err = ssl_clt.get_client_hello_ciphers()
if not err and ciphers then
for i, cipher in ipairs(ciphers) do
ngx.log(ngx.INFO, i, ": CIPHER ", cipher)
end
else
ngx.log(ngx.ERR, "failed to get ciphers")
end
ngx.exit(ngx.ERROR)
}

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_certificate ../../cert/test.crt;
ssl_certificate_key ../../cert/test.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;
lua_ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

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

sock:settimeout(3000)

local ok, err = sock:connect("127.0.0.2", $TEST_NGINX_RAND_PORT_1)
if not ok then
ngx.say("failed to connect: ", err)
return
end

ngx.say("connected: ", ok)

local sess, err = sock:sslhandshake(nil, nil, 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
failed to do SSL handshake: handshake failed
--- error_log eval
qr/1: CIPHER \d+, context: ssl_client_hello_by_lua/
--- no_error_log
[alert]
[crit]
[placeholder]