From 8e03315607ba40b307d23fb654460eb76d2ac8f3 Mon Sep 17 00:00:00 2001 From: phuslu Date: Sun, 26 Jan 2025 23:58:17 +0800 Subject: [PATCH 01/11] fix ci --- .github/workflows/nginx.yml | 3 +++ .github/workflows/performance.yml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/nginx.yml b/.github/workflows/nginx.yml index a3b38d4..6c60805 100644 --- a/.github/workflows/nginx.yml +++ b/.github/workflows/nginx.yml @@ -16,6 +16,9 @@ jobs: openssl: [openssl-3.2, openssl-3.1, openssl-3.0, OpenSSL_1_1_1-stable] nginx: [release-1.25.3, release-1.24.0, release-1.23.4, release-1.22.1, release-1.21.6, release-1.20.2] steps: + - name: Dependency + run: | + sudo apt update -y && sudo apt-get install -y libpcre3 libpcre3-dev - name: Clone run: | git clone -b ${{ matrix.openssl }} --depth=1 https://github.com/openssl/openssl diff --git a/.github/workflows/performance.yml b/.github/workflows/performance.yml index 14d8a5b..9fb0d36 100644 --- a/.github/workflows/performance.yml +++ b/.github/workflows/performance.yml @@ -16,7 +16,7 @@ jobs: nginx: [release-1.25.3] steps: - name: Install packages - run: sudo apt update -y && sudo apt-get install -y wrk + run: sudo apt update -y && sudo apt-get install -y wrk libpcre3 libpcre3-dev - name: Config run: | cat < nginx.conf From d27041f194e2eaec82d7a77578890926e405167c Mon Sep 17 00:00:00 2001 From: phuslu Date: Mon, 27 Jan 2025 00:53:16 +0800 Subject: [PATCH 02/11] fix PACKET_remaining usage --- patches/openssl.OpenSSL_1_1_1-stable.patch | 33 +++++++++++----------- patches/openssl.openssl-3.0.patch | 29 +++++++++---------- patches/openssl.openssl-3.1.patch | 29 +++++++++---------- patches/openssl.openssl-3.2.patch | 33 +++++++++++----------- 4 files changed, 60 insertions(+), 64 deletions(-) diff --git a/patches/openssl.OpenSSL_1_1_1-stable.patch b/patches/openssl.OpenSSL_1_1_1-stable.patch index a8f08c3..1b1bd60 100644 --- a/patches/openssl.OpenSSL_1_1_1-stable.patch +++ b/patches/openssl.OpenSSL_1_1_1-stable.patch @@ -1,5 +1,5 @@ diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h -index 9af0c899..c77a04cd 100644 +index 9af0c8995e..c77a04cd90 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -1818,6 +1818,7 @@ size_t SSL_client_hello_get0_ciphers(SSL *s, const unsigned char **out); @@ -11,7 +11,7 @@ index 9af0c899..c77a04cd 100644 const unsigned char **out, size_t *outlen); diff --git a/include/openssl/tls1.h b/include/openssl/tls1.h -index 76d9fda4..a29114f2 100644 +index 76d9fda46e..a29114f215 100644 --- a/include/openssl/tls1.h +++ b/include/openssl/tls1.h @@ -131,6 +131,15 @@ extern "C" { @@ -31,10 +31,10 @@ index 76d9fda4..a29114f2 100644 # define TLSEXT_TYPE_session_ticket 35 diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c -index 47adc321..635b23b3 100644 +index 47adc3211c..be20746b98 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c -@@ -5219,6 +5219,95 @@ int SSL_client_hello_get1_extensions_present(SSL *s, int **out, size_t *outlen) +@@ -5219,6 +5219,94 @@ int SSL_client_hello_get1_extensions_present(SSL *s, int **out, size_t *outlen) return 0; } @@ -74,11 +74,12 @@ index 47adc321..635b23b3 100644 + ptr += 2; + + /* ciphers */ -+ num = PACKET_remaining(&s->clienthello->ciphersuites); -+ *(uint16_t*)ptr = (uint16_t)num; -+ ptr += 2; -+ memcpy(ptr, PACKET_data(&s->clienthello->ciphersuites), num); -+ ptr += num; ++ if (num = PACKET_remaining(&s->clienthello->ciphersuites)) { ++ *(uint16_t*)ptr = (uint16_t)num; ++ ptr += 2; ++ memcpy(ptr, PACKET_data(&s->clienthello->ciphersuites), num); ++ ptr += num; ++ } + + /* extensions */ + num = 0; @@ -104,19 +105,17 @@ index 47adc321..635b23b3 100644 + ptr += num*2; + + /* groups */ -+ if (groups) { -+ num = PACKET_remaining(groups); ++ if (groups && (num = PACKET_remaining(groups))) { + memcpy(ptr, PACKET_data(groups), num); + *(uint16_t*)ptr = (uint16_t)num; + ptr += num; + } else { -+ *(uint16_t*)ptr = 0; -+ ptr += 2; ++ *(uint16_t*)ptr = 0; ++ ptr += 2; + } + + /* formats */ -+ if (formats) { -+ num = PACKET_remaining(formats); ++ if (formats && (num = PACKET_remaining(formats))) { + memcpy(ptr, PACKET_data(formats), num); + *ptr = (uint8_t)num; + ptr += num; @@ -131,7 +130,7 @@ index 47adc321..635b23b3 100644 size_t *outlen) { diff --git a/ssl/ssl_local.h b/ssl/ssl_local.h -index 5c792154..a3038c79 100644 +index 5c79215423..a3038c79ea 100644 --- a/ssl/ssl_local.h +++ b/ssl/ssl_local.h @@ -714,6 +714,10 @@ typedef enum tlsext_index_en { @@ -146,7 +145,7 @@ index 5c792154..a3038c79 100644 TLSEXT_IDX_psk, /* Dummy index - must always be the last entry */ diff --git a/ssl/statem/extensions.c b/ssl/statem/extensions.c -index 0f39275b..7cb1e622 100644 +index 0f39275baa..7cb1e62287 100644 --- a/ssl/statem/extensions.c +++ b/ssl/statem/extensions.c @@ -377,6 +377,38 @@ static const EXTENSION_DEFINITION ext_defs[] = { diff --git a/patches/openssl.openssl-3.0.patch b/patches/openssl.openssl-3.0.patch index a961abc..46bc0e9 100644 --- a/patches/openssl.openssl-3.0.patch +++ b/patches/openssl.openssl-3.0.patch @@ -11,7 +11,7 @@ index 105b4a4a3c..6c7eb4643a 100644 const unsigned char **out, size_t *outlen); diff --git a/include/openssl/tls1.h b/include/openssl/tls1.h -index d6e9331fa1..b62b4e380d 100644 +index 91558fa8d1..b04e6ec4fa 100644 --- a/include/openssl/tls1.h +++ b/include/openssl/tls1.h @@ -134,6 +134,15 @@ extern "C" { @@ -31,10 +31,10 @@ index d6e9331fa1..b62b4e380d 100644 # define TLSEXT_TYPE_session_ticket 35 diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c -index 2c8479eb5f..4c62687258 100644 +index e628140dfa..512e5a552c 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c -@@ -5463,6 +5463,95 @@ int SSL_client_hello_get1_extensions_present(SSL *s, int **out, size_t *outlen) +@@ -5481,6 +5481,94 @@ int SSL_client_hello_get1_extensions_present(SSL *s, int **out, size_t *outlen) return 0; } @@ -74,11 +74,12 @@ index 2c8479eb5f..4c62687258 100644 + ptr += 2; + + /* ciphers */ -+ num = PACKET_remaining(&s->clienthello->ciphersuites); -+ *(uint16_t*)ptr = (uint16_t)num; -+ ptr += 2; -+ memcpy(ptr, PACKET_data(&s->clienthello->ciphersuites), num); -+ ptr += num; ++ if (num = PACKET_remaining(&s->clienthello->ciphersuites)) { ++ *(uint16_t*)ptr = (uint16_t)num; ++ ptr += 2; ++ memcpy(ptr, PACKET_data(&s->clienthello->ciphersuites), num); ++ ptr += num; ++ } + + /* extensions */ + num = 0; @@ -104,19 +105,17 @@ index 2c8479eb5f..4c62687258 100644 + ptr += num*2; + + /* groups */ -+ if (groups) { -+ num = PACKET_remaining(groups); ++ if (groups && (num = PACKET_remaining(groups))) { + memcpy(ptr, PACKET_data(groups), num); + *(uint16_t*)ptr = (uint16_t)num; + ptr += num; + } else { -+ *(uint16_t*)ptr = 0; -+ ptr += 2; ++ *(uint16_t*)ptr = 0; ++ ptr += 2; + } + + /* formats */ -+ if (formats) { -+ num = PACKET_remaining(formats); ++ if (formats && (num = PACKET_remaining(formats))) { + memcpy(ptr, PACKET_data(formats), num); + *ptr = (uint8_t)num; + ptr += num; @@ -146,7 +145,7 @@ index 5fb1feb801..99f1370ea3 100644 TLSEXT_IDX_psk, /* Dummy index - must always be the last entry */ diff --git a/ssl/statem/extensions.c b/ssl/statem/extensions.c -index 1518ca7f4e..66a83bcb16 100644 +index f8157389b7..fb5e1a8453 100644 --- a/ssl/statem/extensions.c +++ b/ssl/statem/extensions.c @@ -370,6 +370,38 @@ static const EXTENSION_DEFINITION ext_defs[] = { diff --git a/patches/openssl.openssl-3.1.patch b/patches/openssl.openssl-3.1.patch index 3c37f92..3621805 100644 --- a/patches/openssl.openssl-3.1.patch +++ b/patches/openssl.openssl-3.1.patch @@ -11,7 +11,7 @@ index f03f52fbd8..3140c3c5c5 100644 const unsigned char **out, size_t *outlen); diff --git a/include/openssl/tls1.h b/include/openssl/tls1.h -index 793155e186..ef1f187b15 100644 +index 03f43744b1..a54c78e1fe 100644 --- a/include/openssl/tls1.h +++ b/include/openssl/tls1.h @@ -134,6 +134,15 @@ extern "C" { @@ -31,10 +31,10 @@ index 793155e186..ef1f187b15 100644 # define TLSEXT_TYPE_session_ticket 35 diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c -index b5cc4af2f0..578598e664 100644 +index f218dcf1db..8bf43c4c58 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c -@@ -5464,6 +5464,95 @@ int SSL_client_hello_get1_extensions_present(SSL *s, int **out, size_t *outlen) +@@ -5482,6 +5482,94 @@ int SSL_client_hello_get1_extensions_present(SSL *s, int **out, size_t *outlen) return 0; } @@ -74,11 +74,12 @@ index b5cc4af2f0..578598e664 100644 + ptr += 2; + + /* ciphers */ -+ num = PACKET_remaining(&s->clienthello->ciphersuites); -+ *(uint16_t*)ptr = (uint16_t)num; -+ ptr += 2; -+ memcpy(ptr, PACKET_data(&s->clienthello->ciphersuites), num); -+ ptr += num; ++ if (num = PACKET_remaining(&s->clienthello->ciphersuites)) { ++ *(uint16_t*)ptr = (uint16_t)num; ++ ptr += 2; ++ memcpy(ptr, PACKET_data(&s->clienthello->ciphersuites), num); ++ ptr += num; ++ } + + /* extensions */ + num = 0; @@ -104,19 +105,17 @@ index b5cc4af2f0..578598e664 100644 + ptr += num*2; + + /* groups */ -+ if (groups) { -+ num = PACKET_remaining(groups); ++ if (groups && (num = PACKET_remaining(groups))) { + memcpy(ptr, PACKET_data(groups), num); + *(uint16_t*)ptr = (uint16_t)num; + ptr += num; + } else { -+ *(uint16_t*)ptr = 0; -+ ptr += 2; ++ *(uint16_t*)ptr = 0; ++ ptr += 2; + } + + /* formats */ -+ if (formats) { -+ num = PACKET_remaining(formats); ++ if (formats && (num = PACKET_remaining(formats))) { + memcpy(ptr, PACKET_data(formats), num); + *ptr = (uint8_t)num; + ptr += num; @@ -146,7 +145,7 @@ index 845329a809..8fa0619feb 100644 TLSEXT_IDX_psk, /* Dummy index - must always be the last entry */ diff --git a/ssl/statem/extensions.c b/ssl/statem/extensions.c -index e182b5abac..7d5adbf845 100644 +index 2ff809bc0f..fd730c09b8 100644 --- a/ssl/statem/extensions.c +++ b/ssl/statem/extensions.c @@ -369,6 +369,38 @@ static const EXTENSION_DEFINITION ext_defs[] = { diff --git a/patches/openssl.openssl-3.2.patch b/patches/openssl.openssl-3.2.patch index 54b905d..1b4fda5 100644 --- a/patches/openssl.openssl-3.2.patch +++ b/patches/openssl.openssl-3.2.patch @@ -11,7 +11,7 @@ index 9f91039f8a..81b9c51892 100644 size_t *num_exts); int SSL_client_hello_get0_ext(SSL *s, unsigned int type, diff --git a/include/openssl/tls1.h b/include/openssl/tls1.h -index 7e3d1a725b..ecee15e29f 100644 +index 5329338efa..9755b5f44c 100644 --- a/include/openssl/tls1.h +++ b/include/openssl/tls1.h @@ -142,6 +142,13 @@ extern "C" { @@ -29,10 +29,10 @@ index 7e3d1a725b..ecee15e29f 100644 # define TLSEXT_TYPE_compress_certificate 27 diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c -index 26cae27dae..f5d1d8013a 100644 +index a6695ca4a0..4af995af35 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c -@@ -6572,6 +6572,99 @@ int SSL_client_hello_get1_extensions_present(SSL *s, int **out, size_t *outlen) +@@ -6595,6 +6595,98 @@ int SSL_client_hello_get1_extensions_present(SSL *s, int **out, size_t *outlen) return 0; } @@ -76,11 +76,12 @@ index 26cae27dae..f5d1d8013a 100644 + ptr += 2; + + /* ciphers */ -+ num = PACKET_remaining(&sc->clienthello->ciphersuites); -+ *(uint16_t*)ptr = (uint16_t)num; -+ ptr += 2; -+ memcpy(ptr, PACKET_data(&sc->clienthello->ciphersuites), num); -+ ptr += num; ++ if (num = PACKET_remaining(&sc->clienthello->ciphersuites)) { ++ *(uint16_t*)ptr = (uint16_t)num; ++ ptr += 2; ++ memcpy(ptr, PACKET_data(&sc->clienthello->ciphersuites), num); ++ ptr += num; ++ } + + /* extensions */ + num = 0; @@ -106,19 +107,17 @@ index 26cae27dae..f5d1d8013a 100644 + ptr += num*2; + + /* groups */ -+ if (groups) { -+ num = PACKET_remaining(groups); ++ if (groups && (num = PACKET_remaining(groups))) { + memcpy(ptr, PACKET_data(groups), num); + *(uint16_t*)ptr = (uint16_t)num; + ptr += num; + } else { -+ *(uint16_t*)ptr = 0; -+ ptr += 2; ++ *(uint16_t*)ptr = 0; ++ ptr += 2; + } + + /* formats */ -+ if (formats) { -+ num = PACKET_remaining(formats); ++ if (formats && (num = PACKET_remaining(formats))) { + memcpy(ptr, PACKET_data(formats), num); + *ptr = (uint8_t)num; + ptr += num; @@ -133,10 +132,10 @@ index 26cae27dae..f5d1d8013a 100644 { RAW_EXTENSION *ext; diff --git a/ssl/ssl_local.h b/ssl/ssl_local.h -index 0d3acfbe66..01ceec6897 100644 +index 5f19e679fc..a5241e163d 100644 --- a/ssl/ssl_local.h +++ b/ssl/ssl_local.h -@@ -707,6 +707,9 @@ typedef enum tlsext_index_en { +@@ -708,6 +708,9 @@ typedef enum tlsext_index_en { TLSEXT_IDX_compress_certificate, TLSEXT_IDX_early_data, TLSEXT_IDX_certificate_authorities, @@ -147,7 +146,7 @@ index 0d3acfbe66..01ceec6897 100644 TLSEXT_IDX_psk, /* Dummy index - must always be the last entry */ diff --git a/ssl/statem/extensions.c b/ssl/statem/extensions.c -index 0a64ca2246..9460207d1f 100644 +index c35c2ccd33..55c04a9937 100644 --- a/ssl/statem/extensions.c +++ b/ssl/statem/extensions.c @@ -411,6 +411,30 @@ static const EXTENSION_DEFINITION ext_defs[] = { From ed7c6db27d8971ba4fadd0518071c16d4b1ab3b3 Mon Sep 17 00:00:00 2001 From: Vasiliy Soshnikov Date: Sun, 2 Feb 2025 16:03:51 +0300 Subject: [PATCH 03/11] Supporing the laters versions of: nginx v1.27 patch and openssl v3.4 patch; Fix segfault; speedup sulution by reducting number of allocations per requests; --- patches/nginx-1.27.patch | 180 +++++++++++++++++++++++++++++ patches/openssl.openssl-3.4.patch | 186 ++++++++++++++++++++++++++++++ src/nginx_ssl_fingerprint.c | 14 +++ 3 files changed, 380 insertions(+) create mode 100644 patches/nginx-1.27.patch create mode 100644 patches/openssl.openssl-3.4.patch diff --git a/patches/nginx-1.27.patch b/patches/nginx-1.27.patch new file mode 100644 index 0000000..0cc2aae --- /dev/null +++ b/patches/nginx-1.27.patch @@ -0,0 +1,180 @@ +diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c +index 2f68a55de..d069b72ea 100644 +--- a/src/event/ngx_event_openssl.c ++++ b/src/event/ngx_event_openssl.c +@@ -11,6 +11,7 @@ + #include + + ++#define NGX_SSL_JA3_BUFFER_SIZE 256 /* = (sizeof(uint16_t) * 128) */ + #define NGX_SSL_PASSWORD_BUFFER_SIZE 4096 + + #if (NGX_HAVE_NTLS) +@@ -1971,6 +1972,40 @@ ngx_ssl_set_session(ngx_connection_t *c, ngx_ssl_session_t *session) + return NGX_OK; + } + ++int ++ngx_ssl_client_hello_ja3_cb(SSL *s, int *al, void *arg) ++{ ++ ngx_connection_t *c = arg; ++ ngx_str_t *ja; ++ ++ if (c == NULL) { ++ return SSL_CLIENT_HELLO_SUCCESS; ++ } ++ ++ if (c->ssl == NULL) { ++ return SSL_CLIENT_HELLO_SUCCESS; ++ } ++ ++ ja = &c->ssl->fp_ja3_data; ++ ja->len = NGX_SSL_JA3_BUFFER_SIZE; ++ ++ ja->data = ngx_pnalloc(c->pool, c->ssl->fp_ja3_data.len); ++ if (ja->data == NULL) { ++ ja->len = 0; ++ return SSL_CLIENT_HELLO_SUCCESS; ++ } ++ ++ ja->len = SSL_client_hello_get_ja3_data(c->ssl->connection, ja->data, ++ NGX_SSL_JA3_BUFFER_SIZE); ++ if (ja->len == 0) { ++ ngx_log_error(NGX_LOG_WARN, c->log, 0, "SSL_client_hello_get_ja3_data " ++ "seems the buffer size is less that number of fileds; " ++ "for this SSL connection can't get a ja3 hash string"); ++ ja->data = NULL; ++ } ++ ++ return SSL_CLIENT_HELLO_SUCCESS; ++} + + ngx_int_t + ngx_ssl_handshake(ngx_connection_t *c) +@@ -1991,6 +2026,9 @@ ngx_ssl_handshake(ngx_connection_t *c) + + ngx_ssl_clear_error(c->log); + ++ (void) SSL_CTX_set_client_hello_cb(c->ssl->session_ctx, ++ ngx_ssl_client_hello_ja3_cb, c); ++ + n = SSL_do_handshake(c->ssl->connection); + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_do_handshake: %d", n); +diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h +index 42a01ce62..c4adc270d 100644 +--- a/src/event/ngx_event_openssl.h ++++ b/src/event/ngx_event_openssl.h +@@ -129,6 +129,11 @@ struct ngx_ssl_connection_s { + unsigned in_ocsp:1; + unsigned early_preread:1; + unsigned write_blocked:1; ++ ++ ngx_str_t fp_ja3_data; ++ ngx_str_t fp_ja3_str; ++ ngx_str_t fp_ja3_hash; ++ uint16_t fp_tls_greased; + }; + + +diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c +index 0f5bd3de8..1b85e378d 100644 +--- a/src/http/v2/ngx_http_v2.c ++++ b/src/http/v2/ngx_http_v2.c +@@ -301,6 +301,8 @@ ngx_http_v2_init(ngx_event_t *rev) + ngx_add_timer(rev, cscf->client_header_timeout); + } + ++ h2c->fp_fingerprinted = 0; ++ + c->idle = 1; + ngx_reusable_connection(c, 0); + +@@ -1352,6 +1354,14 @@ ngx_http_v2_state_headers(ngx_http_v2_connection_t *h2c, u_char *pos, + } + } + ++ if (!h2c->fp_fingerprinted && h2c->fp_priorities.len < 32) { ++ h2c->fp_priorities.data[h2c->fp_priorities.len] = (uint8_t)stream->node->id; ++ h2c->fp_priorities.data[h2c->fp_priorities.len+1] = (uint8_t)excl; ++ h2c->fp_priorities.data[h2c->fp_priorities.len+2] = (uint8_t)depend; ++ h2c->fp_priorities.data[h2c->fp_priorities.len+3] = (uint8_t)(weight-1); ++ h2c->fp_priorities.len += 4; ++ } ++ + return ngx_http_v2_state_header_block(h2c, pos, end); + + rst_stream: +@@ -1775,6 +1785,9 @@ ngx_http_v2_state_process_header(ngx_http_v2_connection_t *h2c, u_char *pos, + } + + if (header->name.data[0] == ':') { ++ if (!h2c->fp_fingerprinted && h2c->fp_pseudoheaders.len < 32 && header->name.len > 1) ++ h2c->fp_pseudoheaders.data[h2c->fp_pseudoheaders.len++] = header->name.data[1]; ++ + rc = ngx_http_v2_pseudo_header(r, header); + + if (rc == NGX_OK) { +@@ -2194,6 +2207,12 @@ ngx_http_v2_state_settings_params(ngx_http_v2_connection_t *h2c, u_char *pos, + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 setting %ui:%ui", id, value); + ++ if (!h2c->fp_fingerprinted && h2c->fp_settings.len < 32) { ++ h2c->fp_settings.data[h2c->fp_settings.len] = (uint8_t)id; ++ *(uint32_t*)(h2c->fp_settings.data + h2c->fp_settings.len + 1) = (uint32_t)value; ++ h2c->fp_settings.len += 5; ++ } ++ + switch (id) { + + case NGX_HTTP_V2_INIT_WINDOW_SIZE_SETTING: +@@ -2478,6 +2497,9 @@ ngx_http_v2_state_window_update(ngx_http_v2_connection_t *h2c, u_char *pos, + } + + h2c->send_window += window; ++ if (!h2c->fp_fingerprinted) { ++ h2c->fp_windowupdate = window; ++ } + + while (!ngx_queue_empty(&h2c->waiting)) { + q = ngx_queue_head(&h2c->waiting); +diff --git a/src/http/v2/ngx_http_v2.h b/src/http/v2/ngx_http_v2.h +index 6751b3026..60a68a0fd 100644 +--- a/src/http/v2/ngx_http_v2.h ++++ b/src/http/v2/ngx_http_v2.h +@@ -17,6 +17,8 @@ + + #define NGX_HTTP_V2_STATE_BUFFER_SIZE 16 + ++#define NGX_FP_V2_BUFFER_SIZE 32 ++ + #define NGX_HTTP_V2_DEFAULT_FRAME_SIZE (1 << 14) + #define NGX_HTTP_V2_MAX_FRAME_SIZE ((1 << 24) - 1) + +@@ -121,6 +123,12 @@ typedef struct { + } ngx_http_v2_hpack_t; + + ++typedef struct { ++ u_char data[NGX_FP_V2_BUFFER_SIZE]; ++ size_t len; ++} ngx_http_v2_fp_fixed_str_t; ++ ++ + struct ngx_http_v2_connection_s { + ngx_connection_t *connection; + ngx_http_connection_t *http_connection; +@@ -168,6 +176,13 @@ struct ngx_http_v2_connection_s { + unsigned table_update:1; + unsigned blocked:1; + unsigned goaway:1; ++ ++ unsigned fp_fingerprinted:1; ++ ngx_http_v2_fp_fixed_str_t fp_settings, ++ fp_priorities, ++ fp_pseudoheaders; ++ ngx_uint_t fp_windowupdate; ++ ngx_str_t fp_str; + }; + + diff --git a/patches/openssl.openssl-3.4.patch b/patches/openssl.openssl-3.4.patch new file mode 100644 index 0000000..c8e4143 --- /dev/null +++ b/patches/openssl.openssl-3.4.patch @@ -0,0 +1,186 @@ +diff --git a/include/openssl/ssl.h.in b/include/openssl/ssl.h.in +index 4bab2ac767..9732ece423 100644 +--- a/include/openssl/ssl.h.in ++++ b/include/openssl/ssl.h.in +@@ -1905,6 +1905,8 @@ size_t SSL_client_hello_get0_ciphers(SSL *s, const unsigned char **out); + size_t SSL_client_hello_get0_compression_methods(SSL *s, + const unsigned char **out); + int SSL_client_hello_get1_extensions_present(SSL *s, int **out, size_t *outlen); ++size_t SSL_client_hello_get_ja3_data(SSL *s, unsigned char *data, ++ size_t data_size); + int SSL_client_hello_get_extension_order(SSL *s, uint16_t *exts, + size_t *num_exts); + int SSL_client_hello_get0_ext(SSL *s, unsigned int type, +diff --git a/include/openssl/tls1.h b/include/openssl/tls1.h +index 8e9b110bb3..3a2407b0e4 100644 +--- a/include/openssl/tls1.h ++++ b/include/openssl/tls1.h +@@ -142,6 +142,13 @@ extern "C" { + /* ExtensionType value from RFC7627 */ + # define TLSEXT_TYPE_extended_master_secret 23 + ++/* ExtensionType value from RFC6961 */ ++# define TLSEXT_TYPE_status_request_v2 17 ++/* ExtensionType value from RFC8449 */ ++# define TLSEXT_TYPE_record_size_limit 28 ++/* ExtensionType value from RFC7639 */ ++# define TLSEXT_TYPE_application_settings 17513 ++ + /* ExtensionType value from RFC8879 */ + # define TLSEXT_TYPE_compress_certificate 27 + +diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c +index 295b719ff2..429d710fa2 100644 +--- a/ssl/ssl_lib.c ++++ b/ssl/ssl_lib.c +@@ -6641,6 +6641,101 @@ int SSL_client_hello_get1_extensions_present(SSL *s, int **out, size_t *outlen) + return 0; + } + ++size_t SSL_client_hello_get_ja3_data(SSL *s, unsigned char *data, ++ size_t data_size) ++{ ++ RAW_EXTENSION *ext; ++ PACKET *groups = NULL, *formats = NULL; ++ size_t num = 0, i; ++ unsigned char *ptr = data, ++ *end = data + data_size; ++ const SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(s); ++ ++ if (sc == NULL) ++ return 0; ++ ++ if (ossl_unlikely(sc->clienthello == NULL || data == NULL ++ /** just checking that we have at least this */ ++ || data_size < 128)) ++ return 0; ++ ++ /* version */ ++ *(uint16_t *) ptr = (uint16_t) sc->clienthello->legacy_version; ++ ptr += sizeof(uint16_t); ++ ++ num = PACKET_remaining(&sc->clienthello->ciphersuites); ++ *(uint16_t *) ptr = (uint16_t) num; ++ ptr += sizeof(uint16_t); ++ ++ if (ossl_unlikely(ptr + num > end)) ++ return 0; ++ ++ memcpy(ptr, PACKET_data(&sc->clienthello->ciphersuites), num); ++ ptr += num; ++ ++ /* extensions */ ++ num = 0; ++ for (i = 0; i < sc->clienthello->pre_proc_exts_len; i++) { ++ ext = sc->clienthello->pre_proc_exts + i; ++ if (ext->present) ++ num++; ++ } ++ ++ num = num * 2; ++ if (ossl_unlikely((ptr + num + sizeof(uint16_t)) > end)) ++ return 0; ++ *(uint16_t*) ptr = (uint16_t) num; ++ ptr += sizeof(uint16_t); ++ ++ for (i = 0; i < sc->clienthello->pre_proc_exts_len; i++) { ++ ext = sc->clienthello->pre_proc_exts + i; ++ if (ext->present) { ++ if (ext->received_order >= num) ++ break; ++ if (ext->type == TLSEXT_TYPE_supported_groups) ++ groups = &ext->data; ++ if (ext->type == TLSEXT_TYPE_ec_point_formats) ++ formats = &ext->data; ++ if (ossl_likely(ext->received_order < data_size)) { ++ *(uint16_t*)(ptr + ext->received_order) = (uint16_t) ext->type; ++ } ++ } ++ } ++ ++ ptr += num; ++ /* groups */ ++ if (groups) { ++ num = PACKET_remaining(groups); ++ if (ossl_unlikely((ptr + num + sizeof(uint16_t)) > end)) ++ return 0; ++ memcpy(ptr, PACKET_data(groups), num); ++ *(uint16_t*) ptr = (uint16_t) num; ++ ptr += num; ++ } else { ++ if (ossl_unlikely(ptr + sizeof(uint16_t) > end)) ++ return 0; ++ *(uint16_t *) ptr = (uint16_t) 0; ++ ptr += sizeof(uint16_t); ++ } ++ ++ /* formats */ ++ if (formats) { ++ num = PACKET_remaining(formats); ++ if (ossl_unlikely((ptr + num + sizeof(uint8_t)) > end)) ++ return 0; ++ memcpy(ptr, PACKET_data(formats), num); ++ *(uint8_t *) ptr = (uint8_t) num; ++ ptr += num; ++ } else { ++ if (ossl_unlikely(ptr + sizeof(uint8_t) > end)) ++ return 0; ++ *(uint8_t *) ptr = (uint8_t) 0; ++ ptr += sizeof(uint8_t); ++ } ++ ++ return ptr - data; ++} ++ + int SSL_client_hello_get_extension_order(SSL *s, uint16_t *exts, size_t *num_exts) + { + RAW_EXTENSION *ext; +diff --git a/ssl/ssl_local.h b/ssl/ssl_local.h +index 277be3084d..6b3821737f 100644 +--- a/ssl/ssl_local.h ++++ b/ssl/ssl_local.h +@@ -701,6 +701,9 @@ typedef enum tlsext_index_en { + TLSEXT_IDX_compress_certificate, + TLSEXT_IDX_early_data, + TLSEXT_IDX_certificate_authorities, ++ TLSEXT_IDX_status_request_v2, ++ TLSEXT_IDX_record_size_limit, ++ TLSEXT_IDX_application_settings, + TLSEXT_IDX_padding, + TLSEXT_IDX_psk, + /* Dummy index - must always be the last entry */ +diff --git a/ssl/statem/extensions.c b/ssl/statem/extensions.c +index 762c7ac0d4..56d2f5ba60 100644 +--- a/ssl/statem/extensions.c ++++ b/ssl/statem/extensions.c +@@ -413,6 +413,30 @@ static const EXTENSION_DEFINITION ext_defs[] = { + tls_construct_certificate_authorities, + tls_construct_certificate_authorities, NULL, + }, ++ { ++ TLSEXT_TYPE_status_request_v2, ++ SSL_EXT_CLIENT_HELLO, ++ NULL, ++ NULL, NULL, ++ NULL, ++ NULL, NULL, ++ }, ++ { ++ TLSEXT_TYPE_record_size_limit, ++ SSL_EXT_CLIENT_HELLO, ++ NULL, ++ NULL, NULL, ++ NULL, ++ NULL, NULL, ++ }, ++ { ++ TLSEXT_TYPE_application_settings, ++ SSL_EXT_CLIENT_HELLO, ++ NULL, ++ NULL, NULL, ++ NULL, ++ NULL, NULL, ++ }, + { + /* Must be immediately before pre_shared_key */ + TLSEXT_TYPE_padding, diff --git a/src/nginx_ssl_fingerprint.c b/src/nginx_ssl_fingerprint.c index 950c670..49771f0 100755 --- a/src/nginx_ssl_fingerprint.c +++ b/src/nginx_ssl_fingerprint.c @@ -211,6 +211,11 @@ int ngx_ssl_ja3(ngx_connection_t *c) c->ssl->fp_ja3_str.len = c->ssl->fp_ja3_data.len * 3; c->ssl->fp_ja3_str.data = ngx_pnalloc(c->pool, c->ssl->fp_ja3_str.len); + if (c->ssl->fp_ja3_str.data == NULL) { + /** Else we break a data stream */ + c->ssl->fp_ja3_str.len = 0; + return NGX_DECLINED; + } ngx_log_debug(NGX_LOG_DEBUG_EVENT, c->log, 0, "ngx_ssl_ja3: alloc bytes: [%d]\n", c->ssl->fp_ja3_str.len); @@ -313,6 +318,11 @@ int ngx_ssl_ja3_hash(ngx_connection_t *c) c->ssl->fp_ja3_hash.len = 32; c->ssl->fp_ja3_hash.data = ngx_pnalloc(c->pool, c->ssl->fp_ja3_hash.len); + if (c->ssl->fp_ja3_hash.data == NULL) { + /** Else we break a stream */ + c->ssl->fp_ja3_hash.len = 0; + return NGX_DECLINED; + } ngx_log_debug(NGX_LOG_DEBUG_EVENT, c->log, 0, "ngx_ssl_ja3_hash: alloc bytes: [%d]\n", c->ssl->fp_ja3_hash.len); @@ -343,6 +353,10 @@ int ngx_http2_fingerprint(ngx_connection_t *c, ngx_http_v2_connection_t *h2c) n = 4 + h2c->fp_settings.len * 3 + 10 + h2c->fp_priorities.len * 2 + h2c->fp_pseudoheaders.len * 2; h2c->fp_str.data = ngx_pnalloc(c->pool, n); + if (h2c->fp_str.data == NULL) { + /** Else we break a stream */ + return NGX_DECLINED; + } pstr = h2c->fp_str.data; ngx_log_debug(NGX_LOG_DEBUG_EVENT, c->log, 0, "ngx_http2_fingerprint: alloc bytes: [%d]\n", n); From b0cbf965e3aa7cc6a4b73c18c11fc41fa4b627a0 Mon Sep 17 00:00:00 2001 From: Vasiliy Soshnikov Date: Sun, 2 Feb 2025 16:52:00 +0300 Subject: [PATCH 04/11] Test PACKET_remaing before do memcpy --- patches/openssl.openssl-3.4.patch | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/patches/openssl.openssl-3.4.patch b/patches/openssl.openssl-3.4.patch index c8e4143..dcb4b43 100644 --- a/patches/openssl.openssl-3.4.patch +++ b/patches/openssl.openssl-3.4.patch @@ -30,10 +30,10 @@ index 8e9b110bb3..3a2407b0e4 100644 # define TLSEXT_TYPE_compress_certificate 27 diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c -index 295b719ff2..429d710fa2 100644 +index 295b719ff2..3d92df0db1 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c -@@ -6641,6 +6641,101 @@ int SSL_client_hello_get1_extensions_present(SSL *s, int **out, size_t *outlen) +@@ -6641,6 +6641,106 @@ int SSL_client_hello_get1_extensions_present(SSL *s, int **out, size_t *outlen) return 0; } @@ -59,15 +59,18 @@ index 295b719ff2..429d710fa2 100644 + *(uint16_t *) ptr = (uint16_t) sc->clienthello->legacy_version; + ptr += sizeof(uint16_t); + ++ /* ciphers */ + num = PACKET_remaining(&sc->clienthello->ciphersuites); -+ *(uint16_t *) ptr = (uint16_t) num; -+ ptr += sizeof(uint16_t); ++ if (ossl_likely(num > 0)) { ++ *(uint16_t *) ptr = (uint16_t) num; ++ ptr += sizeof(uint16_t); + -+ if (ossl_unlikely(ptr + num > end)) -+ return 0; ++ if (ossl_unlikely(ptr + num > end)) ++ return 0; + -+ memcpy(ptr, PACKET_data(&sc->clienthello->ciphersuites), num); -+ ptr += num; ++ memcpy(ptr, PACKET_data(&sc->clienthello->ciphersuites), num); ++ ptr += num; ++ } + + /* extensions */ + num = 0; @@ -99,9 +102,11 @@ index 295b719ff2..429d710fa2 100644 + } + + ptr += num; -+ /* groups */ -+ if (groups) { -+ num = PACKET_remaining(groups); ++ ++ /* groups */ ++ num = PACKET_remaining(groups); ++ if (groups && num > 0) { ++ + if (ossl_unlikely((ptr + num + sizeof(uint16_t)) > end)) + return 0; + memcpy(ptr, PACKET_data(groups), num); @@ -115,8 +120,8 @@ index 295b719ff2..429d710fa2 100644 + } + + /* formats */ -+ if (formats) { -+ num = PACKET_remaining(formats); ++ num = PACKET_remaining(formats); ++ if (formats && num > 0) { + if (ossl_unlikely((ptr + num + sizeof(uint8_t)) > end)) + return 0; + memcpy(ptr, PACKET_data(formats), num); From ccfe8f10791e8e331fd7e04bea344f92d0195895 Mon Sep 17 00:00:00 2001 From: Vasiliy Soshnikov Date: Sun, 2 Feb 2025 19:43:13 +0300 Subject: [PATCH 05/11] Fix add_variable, since it called at postconfiguration, and also overwrite v; make possibe working with map ''{} and other nginx.conf releated features by adding no_found; small refactoring of the code to make it more close to official nginx's style and logic --- config | 11 +- src/nginx_ssl_fingerprint.c | 33 ++--- src/nginx_ssl_fingerprint.h | 19 +++ src/ngx_http_ssl_fingerprint_module.c | 174 ++++++++++---------------- 4 files changed, 111 insertions(+), 126 deletions(-) create mode 100644 src/nginx_ssl_fingerprint.h diff --git a/config b/config index 4e99c56..d469314 100644 --- a/config +++ b/config @@ -1,3 +1,8 @@ + +# +# JA3 module conf +# + ngx_addon_name=ngx_ssl_fingerprint_module CORE_LIBS="$CORE_LIBS" @@ -8,9 +13,13 @@ STREAM_MODULES="ngx_stream_ssl_fingerprint_preread_module $STREAM_MODULES" HTTP_MODULES="$HTTP_MODULES ngx_http_ssl_fingerprint_module" -NGX_ADDON_SRCS="$NGX_ADDON_SRCS \ +NGX_ADDON_SRCS="$NGX_ADDON_SRCS \ $ngx_addon_dir/src/nginx_ssl_fingerprint.c \ $ngx_addon_dir/src/ngx_stream_ssl_fingerprint_preread_module.c \ $ngx_addon_dir/src/ngx_http_ssl_fingerprint_module.c " +CFLAGS="$CFLAGS -I$ngx_addon_dir" + +have=NGX_JA3_FINGERPRING_MODULE . auto/have + diff --git a/src/nginx_ssl_fingerprint.c b/src/nginx_ssl_fingerprint.c index 49771f0..84e9478 100755 --- a/src/nginx_ssl_fingerprint.c +++ b/src/nginx_ssl_fingerprint.c @@ -5,6 +5,8 @@ #include #include +#include + #define IS_GREASE_CODE(code) (((code)&0x0f0f) == 0x0a0a && ((code)&0xff) == ((code)>>8)) static inline @@ -192,20 +194,16 @@ int ngx_ssl_ja3(ngx_connection_t *c) size_t num = 0, i; uint16_t n, greased = 0; - if (c == NULL) { - return NGX_DECLINED; - } - - if (c->ssl == NULL) { + if (c == NULL || c->ssl == NULL) { return NGX_DECLINED; } data = c->ssl->fp_ja3_data.data; - if (!data) { + if (data == NULL) { return NGX_DECLINED; } - if (c->ssl->fp_ja3_str.len > 0) { + if (c->ssl->fp_ja3_str.data != NULL) { return NGX_OK; } @@ -214,7 +212,7 @@ int ngx_ssl_ja3(ngx_connection_t *c) if (c->ssl->fp_ja3_str.data == NULL) { /** Else we break a data stream */ c->ssl->fp_ja3_str.len = 0; - return NGX_DECLINED; + return NGX_DECLINED /** NGX_ERROR? */; } ngx_log_debug(NGX_LOG_DEBUG_EVENT, c->log, 0, "ngx_ssl_ja3: alloc bytes: [%d]\n", c->ssl->fp_ja3_str.len); @@ -300,18 +298,16 @@ int ngx_ssl_ja3(ngx_connection_t *c) int ngx_ssl_ja3_hash(ngx_connection_t *c) { - if (c == NULL) { - return NGX_DECLINED; - } + ngx_md5_t ctx; + u_char hash_buf[16]; - if (c->ssl == NULL) { + if (c == NULL + || c->ssl == NULL + || c->ssl->fp_ja3_hash.len > 0) + { return NGX_DECLINED; } - if (c->ssl->fp_ja3_hash.len > 0) { - return NGX_OK; - } - if (ngx_ssl_ja3(c) == NGX_DECLINED) { return NGX_DECLINED; } @@ -326,9 +322,6 @@ int ngx_ssl_ja3_hash(ngx_connection_t *c) ngx_log_debug(NGX_LOG_DEBUG_EVENT, c->log, 0, "ngx_ssl_ja3_hash: alloc bytes: [%d]\n", c->ssl->fp_ja3_hash.len); - ngx_md5_t ctx; - u_char hash_buf[16]; - ngx_md5_init(&ctx); ngx_md5_update(&ctx, c->ssl->fp_ja3_str.data, c->ssl->fp_ja3_str.len); ngx_md5_final(hash_buf, &ctx); @@ -343,7 +336,7 @@ int ngx_http2_fingerprint(ngx_connection_t *c, ngx_http_v2_connection_t *h2c) unsigned short n = 0; size_t i; - if (!h2c) { + if (c == NULL || h2c == NULL) { return NGX_DECLINED; } diff --git a/src/nginx_ssl_fingerprint.h b/src/nginx_ssl_fingerprint.h new file mode 100644 index 0000000..28ccca6 --- /dev/null +++ b/src/nginx_ssl_fingerprint.h @@ -0,0 +1,19 @@ + +/* + * Obj: nginx_ssl_fingerprint.c + */ + +#ifndef NGINX_SSL_FINGERPRINT_H_ +#define NGINX_SSL_FINGERPRINT_H_ 1 + + +#include +#include +#include + +int ngx_ssl_ja3(ngx_connection_t *c); +int ngx_ssl_ja3_hash(ngx_connection_t *c); +int ngx_http2_fingerprint(ngx_connection_t *c, ngx_http_v2_connection_t *h2c); + +#endif /** NGINX_SSL_FINGERPRINT_H_ */ + diff --git a/src/ngx_http_ssl_fingerprint_module.c b/src/ngx_http_ssl_fingerprint_module.c index 5166096..23510ee 100644 --- a/src/ngx_http_ssl_fingerprint_module.c +++ b/src/ngx_http_ssl_fingerprint_module.c @@ -1,61 +1,71 @@ + #include #include #include -extern int ngx_ssl_ja3(ngx_connection_t *c); -extern int ngx_ssl_ja3_hash(ngx_connection_t *c); -extern int ngx_http2_fingerprint(ngx_connection_t *c, ngx_http_v2_connection_t *h2c); +#include static ngx_int_t ngx_http_ssl_fingerprint_init(ngx_conf_t *cf); +static ngx_int_t ngx_http_ssl_greased(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_ssl_fingerprint(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_ssl_fingerprint_hash(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_http2_fingerprint(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); static ngx_http_module_t ngx_http_ssl_fingerprint_module_ctx = { - NULL, /* preconfiguration */ - ngx_http_ssl_fingerprint_init, /* postconfiguration */ - NULL, /* create main configuration */ - NULL, /* init main configuration */ - NULL, /* create server configuration */ - NULL, /* merge server configuration */ - NULL, /* create location configuration */ - NULL /* merge location configuration */ + ngx_http_ssl_fingerprint_init, /* preconfiguration */ + NULL, /* postconfiguration */ + NULL, /* create main configuration */ + NULL, /* init main configuration */ + NULL, /* create server configuration */ + NULL, /* merge server configuration */ + NULL, /* create location configuration */ + NULL /* merge location configuration */ }; ngx_module_t ngx_http_ssl_fingerprint_module = { NGX_MODULE_V1, &ngx_http_ssl_fingerprint_module_ctx, /* module context */ - NULL, /* module directives */ - NGX_HTTP_MODULE, /* module type */ - NULL, /* init master */ - NULL, /* init module */ - NULL, /* init process */ - NULL, /* init thread */ - NULL, /* exit thread */ - NULL, /* exit process */ - NULL, /* exit master */ + NULL, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ NGX_MODULE_V1_PADDING}; +static ngx_http_variable_t ngx_http_ssl_fingerprint_variables_list[] = { + {ngx_string("http_ssl_greased"), NULL, ngx_http_ssl_greased, + 0, NGX_HTTP_VAR_NOCACHEABLE, 0}, + {ngx_string("http_ssl_ja3"), NULL, ngx_http_ssl_fingerprint, + 0, NGX_HTTP_VAR_NOCACHEABLE, 0}, + {ngx_string("http_ssl_ja3_hash"), NULL, ngx_http_ssl_fingerprint_hash, + 0, NGX_HTTP_VAR_NOCACHEABLE, 0}, + {ngx_string("http2_fingerprint"), NULL, ngx_http_http2_fingerprint, + 0, NGX_HTTP_VAR_NOCACHEABLE, 0}, + ngx_http_null_variable +}; static ngx_int_t ngx_http_ssl_greased(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { - if (r->connection == NULL) - { - return NGX_OK; - } + /* For access.log's map $http2_fingerpring {}: + * if it's not found, then user could add a defined string */ + v->not_found = 1; - if (r->connection->ssl == NULL) - { + if (ngx_ssl_ja3(r->connection) != NGX_OK) { return NGX_OK; } - if (ngx_ssl_ja3(r->connection) == NGX_DECLINED) - { - return NGX_ERROR; - } - v->len = 1; - v->data = (u_char*)(r->connection->ssl->fp_tls_greased ? "1" : "0"); - + v->data = (u_char*) (r->connection->ssl->fp_tls_greased ? "1" : "0"); v->valid = 1; v->no_cacheable = 1; v->not_found = 0; @@ -67,26 +77,19 @@ static ngx_int_t ngx_http_ssl_fingerprint(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { - if (r->connection == NULL) - { - return NGX_OK; - } + /* For access.log's map $http2_fingerpring {}: + * if it's not found, then user could add a defined string */ + v->not_found = 1; - if (r->connection->ssl == NULL) - { + if (ngx_ssl_ja3(r->connection) != NGX_OK) { return NGX_OK; } - if (ngx_ssl_ja3(r->connection) == NGX_DECLINED) - { - return NGX_ERROR; - } - v->data = r->connection->ssl->fp_ja3_str.data; v->len = r->connection->ssl->fp_ja3_str.len; - v->valid = 1; v->no_cacheable = 1; v->not_found = 0; + v->valid = 1; return NGX_OK; } @@ -95,26 +98,19 @@ static ngx_int_t ngx_http_ssl_fingerprint_hash(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { - if (r->connection == NULL) - { - return NGX_OK; - } + /* For access.log's map $http2_fingerpring {}: + * if it's not found, then user could add a defined string */ + v->not_found = 1; - if (r->connection->ssl == NULL) - { + if (ngx_ssl_ja3_hash(r->connection) != NGX_OK) { return NGX_OK; } - if (ngx_ssl_ja3_hash(r->connection) == NGX_DECLINED) - { - return NGX_ERROR; - } - v->data = r->connection->ssl->fp_ja3_hash.data; v->len = r->connection->ssl->fp_ja3_hash.len; - v->valid = 1; v->no_cacheable = 1; v->not_found = 0; + v->valid = 1; return NGX_OK; } @@ -123,77 +119,45 @@ static ngx_int_t ngx_http_http2_fingerprint(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { - if (r->connection == NULL) - { - return NGX_OK; - } + /* For access.log's map $http2_fingerpring {}: + * if it's not found, then user could add a defined string */ + v->not_found = 1; - if (r->stream == NULL) - { + if (r->stream == NULL) { return NGX_OK; } - if (r->stream->connection == NULL) + if (ngx_http2_fingerprint(r->connection, r->stream->connection) + != NGX_OK) { return NGX_OK; } - if (ngx_http2_fingerprint(r->connection, r->stream->connection) == NGX_DECLINED) - { - return NGX_ERROR; - } - v->data = r->stream->connection->fp_str.data; v->len = r->stream->connection->fp_str.len; v->valid = 1; - v->no_cacheable = 1; v->not_found = 0; + v->no_cacheable = 1; return NGX_OK; } -static ngx_http_variable_t ngx_http_ssl_fingerprint_variables_list[] = { - {ngx_string("http_ssl_greased"), - NULL, - ngx_http_ssl_greased, - 0, 0, 0}, - {ngx_string("http_ssl_ja3"), - NULL, - ngx_http_ssl_fingerprint, - 0, 0, 0}, - {ngx_string("http_ssl_ja3_hash"), - NULL, - ngx_http_ssl_fingerprint_hash, - 0, 0, 0}, - {ngx_string("http2_fingerprint"), - NULL, - ngx_http_http2_fingerprint, - 0, 0, 0}, -}; - static ngx_int_t ngx_http_ssl_fingerprint_init(ngx_conf_t *cf) { + ngx_http_variable_t *var, *v; - ngx_http_variable_t *v; - size_t l = 0; - size_t vars_len; - - vars_len = (sizeof(ngx_http_ssl_fingerprint_variables_list) / - sizeof(ngx_http_ssl_fingerprint_variables_list[0])); + for (v = ngx_http_ssl_fingerprint_variables_list; v->name.len; v++) { - /* Register variables */ - for (l = 0; l < vars_len; ++l) - { - v = ngx_http_add_variable(cf, - &ngx_http_ssl_fingerprint_variables_list[l].name, - ngx_http_ssl_fingerprint_variables_list[l].flags); - if (v == NULL) - { - continue; + var = ngx_http_add_variable(cf, &v->name, v->flags); + if (var == NULL) { + return NGX_ERROR; } - *v = ngx_http_ssl_fingerprint_variables_list[l]; + /** NOTE: update it, if set_handler will be needed */ + var->get_handler = v->get_handler; + var->data = v->data; } return NGX_OK; } + From 90c1aef0f3692fbc41bfff6c51e5d85c73c92bae Mon Sep 17 00:00:00 2001 From: Vasiliy Soshnikov Date: Mon, 3 Feb 2025 19:30:17 +0300 Subject: [PATCH 06/11] Fix setting ssl extensions since it was broken by me for openssl-3.4 --- patches/openssl.openssl-3.4.patch | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/patches/openssl.openssl-3.4.patch b/patches/openssl.openssl-3.4.patch index dcb4b43..90c962f 100644 --- a/patches/openssl.openssl-3.4.patch +++ b/patches/openssl.openssl-3.4.patch @@ -30,10 +30,10 @@ index 8e9b110bb3..3a2407b0e4 100644 # define TLSEXT_TYPE_compress_certificate 27 diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c -index 295b719ff2..3d92df0db1 100644 +index 295b719ff2..7bfc9e2eb0 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c -@@ -6641,6 +6641,106 @@ int SSL_client_hello_get1_extensions_present(SSL *s, int **out, size_t *outlen) +@@ -6641,6 +6641,107 @@ int SSL_client_hello_get1_extensions_present(SSL *s, int **out, size_t *outlen) return 0; } @@ -83,6 +83,7 @@ index 295b719ff2..3d92df0db1 100644 + num = num * 2; + if (ossl_unlikely((ptr + num + sizeof(uint16_t)) > end)) + return 0; ++ + *(uint16_t*) ptr = (uint16_t) num; + ptr += sizeof(uint16_t); + @@ -96,7 +97,7 @@ index 295b719ff2..3d92df0db1 100644 + if (ext->type == TLSEXT_TYPE_ec_point_formats) + formats = &ext->data; + if (ossl_likely(ext->received_order < data_size)) { -+ *(uint16_t*)(ptr + ext->received_order) = (uint16_t) ext->type; ++ ((uint16_t*)(ptr))[ext->received_order] = (uint16_t) ext->type; + } + } + } From 6000f93bf3e579778d9f2c4623faf9e6214827b2 Mon Sep 17 00:00:00 2001 From: Vasiliy Soshnikov Date: Mon, 3 Feb 2025 22:24:33 +0300 Subject: [PATCH 07/11] Disable ngx_stream_ssl_fingerprint_preread_module.c, if nginx stream ssl module is off --- src/ngx_stream_ssl_fingerprint_preread_module.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/ngx_stream_ssl_fingerprint_preread_module.c b/src/ngx_stream_ssl_fingerprint_preread_module.c index 46b5186..616133c 100644 --- a/src/ngx_stream_ssl_fingerprint_preread_module.c +++ b/src/ngx_stream_ssl_fingerprint_preread_module.c @@ -1,3 +1,10 @@ + +/* + * Please make sure that you have been incuded --with-streams before --add-module + * Else this module won't be compiled + */ +#if (NGX_STREAM_SSL) + #include #include #include @@ -162,3 +169,10 @@ ngx_stream_ssl_fingerprint_preread_init(ngx_conf_t *cf) return NGX_OK; } + +#else +# pragma message "nginx-ssl-fingerprint: " +# pragma message "disabled for ngx_stream or ngx_ssl_stream. Please add --with-streams with ssl support, " +# pragma message "if you wish to enable it" +#endif /** NGX_STREAM_SSL */ + From 509a4e8f34a4cfad1ea486ced7f6b053ec0fbceb Mon Sep 17 00:00:00 2001 From: Vasiliy Soshnikov Date: Tue, 4 Feb 2025 00:27:26 +0300 Subject: [PATCH 08/11] Specified variable behavior after typical functional tests. Also off preread module from nginx bootstrap, else error appear --- config | 19 ++++---- src/nginx_ssl_fingerprint.c | 68 ++++++++++++++++++--------- src/ngx_http_ssl_fingerprint_module.c | 34 ++++++++------ 3 files changed, 76 insertions(+), 45 deletions(-) diff --git a/config b/config index d469314..1d61bda 100644 --- a/config +++ b/config @@ -6,20 +6,21 @@ ngx_addon_name=ngx_ssl_fingerprint_module CORE_LIBS="$CORE_LIBS" - CORE_INCS="$CORE_INCS $ngx_addon_dir/src" -STREAM_MODULES="ngx_stream_ssl_fingerprint_preread_module $STREAM_MODULES" - HTTP_MODULES="$HTTP_MODULES ngx_http_ssl_fingerprint_module" -NGX_ADDON_SRCS="$NGX_ADDON_SRCS \ - $ngx_addon_dir/src/nginx_ssl_fingerprint.c \ - $ngx_addon_dir/src/ngx_stream_ssl_fingerprint_preread_module.c \ - $ngx_addon_dir/src/ngx_http_ssl_fingerprint_module.c - " +stream_module="" -CFLAGS="$CFLAGS -I$ngx_addon_dir" +if [ $STREAM_SSL_PREREAD = YES ]; then + STREAM_MODULES="ngx_stream_ssl_fingerprint_preread_module $STREAM_MODULES" + stream_module="$ngx_addon_dir/src/ngx_stream_ssl_fingerprint_preread_module.c" +fi + +NGX_ADDON_SRCS="$NGX_ADDON_SRCS \ + $ngx_addon_dir/src/nginx_ssl_fingerprint.c \ + $ngx_addon_dir/src/ngx_http_ssl_fingerprint_module.c \ + $stream_module" have=NGX_JA3_FINGERPRING_MODULE . auto/have diff --git a/src/nginx_ssl_fingerprint.c b/src/nginx_ssl_fingerprint.c index 84e9478..c73a8c1 100755 --- a/src/nginx_ssl_fingerprint.c +++ b/src/nginx_ssl_fingerprint.c @@ -188,19 +188,33 @@ unsigned char *append_uint32(unsigned char* dst, uint32_t n) return dst; } + +/** + * Params: + * c and c->ssl should be valid pointers + * + * Returns: + * NGX_OK - c->ssl->fp_ja3_str is already set + * NGX_ERROR - something went wrong + */ int ngx_ssl_ja3(ngx_connection_t *c) { u_char *ptr = NULL, *data = NULL; size_t num = 0, i; uint16_t n, greased = 0; - if (c == NULL || c->ssl == NULL) { - return NGX_DECLINED; - } - data = c->ssl->fp_ja3_data.data; if (data == NULL) { - return NGX_DECLINED; + /** + * NOTE: + * If we can't set it in OpenSSL, + * then something defenetly something went wrong. + * Typical production configuration has log level set to error, + * this would help to debug this case, if it happened. + */ + ngx_log_error(NGX_LOG_WARN, c->log, 0, + "ngx_ssl_ja3: fp_ja_data == NULL"); + return NGX_ERROR; } if (c->ssl->fp_ja3_str.data != NULL) { @@ -212,7 +226,7 @@ int ngx_ssl_ja3(ngx_connection_t *c) if (c->ssl->fp_ja3_str.data == NULL) { /** Else we break a data stream */ c->ssl->fp_ja3_str.len = 0; - return NGX_DECLINED /** NGX_ERROR? */; + return NGX_ERROR; } ngx_log_debug(NGX_LOG_DEBUG_EVENT, c->log, 0, "ngx_ssl_ja3: alloc bytes: [%d]\n", c->ssl->fp_ja3_str.len); @@ -296,28 +310,33 @@ int ngx_ssl_ja3(ngx_connection_t *c) return NGX_OK; } +/** + * Params: + * c and c->ssl should be valid pointers and tested before. + * + * Returns: + * NGX_OK - c->ssl->fp_ja3_hash is alread set + * NGX_ERROR - something went wrong + */ int ngx_ssl_ja3_hash(ngx_connection_t *c) { ngx_md5_t ctx; u_char hash_buf[16]; - if (c == NULL - || c->ssl == NULL - || c->ssl->fp_ja3_hash.len > 0) - { - return NGX_DECLINED; + if (c->ssl->fp_ja3_hash.len > 0) { + return NGX_OK; } - if (ngx_ssl_ja3(c) == NGX_DECLINED) { - return NGX_DECLINED; + if (ngx_ssl_ja3(c) != NGX_OK) { + return NGX_ERROR; } c->ssl->fp_ja3_hash.len = 32; c->ssl->fp_ja3_hash.data = ngx_pnalloc(c->pool, c->ssl->fp_ja3_hash.len); if (c->ssl->fp_ja3_hash.data == NULL) { - /** Else we break a stream */ + /** Else we can break a stream */ c->ssl->fp_ja3_hash.len = 0; - return NGX_DECLINED; + return NGX_ERROR; } ngx_log_debug(NGX_LOG_DEBUG_EVENT, c->log, 0, "ngx_ssl_ja3_hash: alloc bytes: [%d]\n", c->ssl->fp_ja3_hash.len); @@ -330,25 +349,32 @@ int ngx_ssl_ja3_hash(ngx_connection_t *c) return NGX_OK; } +/** + * Params: + * c and h2c should be a valid pointers + * + * Returns: + * NGX_OK -- h2c->fp_str is set + * NGX_ERROR -- something went wrong + */ int ngx_http2_fingerprint(ngx_connection_t *c, ngx_http_v2_connection_t *h2c) { unsigned char *pstr = NULL; unsigned short n = 0; size_t i; - if (c == NULL || h2c == NULL) { - return NGX_DECLINED; - } - if (h2c->fp_str.len > 0) { return NGX_OK; } - n = 4 + h2c->fp_settings.len * 3 + 10 + h2c->fp_priorities.len * 2 + h2c->fp_pseudoheaders.len * 2; + n = 4 + h2c->fp_settings.len * 3 + + 10 + h2c->fp_priorities.len * 2 + + h2c->fp_pseudoheaders.len * 2; + h2c->fp_str.data = ngx_pnalloc(c->pool, n); if (h2c->fp_str.data == NULL) { /** Else we break a stream */ - return NGX_DECLINED; + return NGX_ERROR; } pstr = h2c->fp_str.data; diff --git a/src/ngx_http_ssl_fingerprint_module.c b/src/ngx_http_ssl_fingerprint_module.c index 23510ee..e2b0afd 100644 --- a/src/ngx_http_ssl_fingerprint_module.c +++ b/src/ngx_http_ssl_fingerprint_module.c @@ -56,18 +56,20 @@ static ngx_int_t ngx_http_ssl_greased(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { - /* For access.log's map $http2_fingerpring {}: + /* For access.log's map $http2_VAR {}: * if it's not found, then user could add a defined string */ v->not_found = 1; - if (ngx_ssl_ja3(r->connection) != NGX_OK) { + if (r->connection->ssl == NULL) { return NGX_OK; } + if (ngx_ssl_ja3(r->connection) != NGX_OK) { + return NGX_ERROR; + } + v->len = 1; v->data = (u_char*) (r->connection->ssl->fp_tls_greased ? "1" : "0"); - v->valid = 1; - v->no_cacheable = 1; v->not_found = 0; return NGX_OK; @@ -77,19 +79,21 @@ static ngx_int_t ngx_http_ssl_fingerprint(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { - /* For access.log's map $http2_fingerpring {}: + /* For access.log's map $VAR {}: * if it's not found, then user could add a defined string */ v->not_found = 1; - if (ngx_ssl_ja3(r->connection) != NGX_OK) { + if (r->connection->ssl == NULL) { return NGX_OK; } + if (ngx_ssl_ja3(r->connection) != NGX_OK) { + return NGX_ERROR; + } + v->data = r->connection->ssl->fp_ja3_str.data; v->len = r->connection->ssl->fp_ja3_str.len; - v->no_cacheable = 1; v->not_found = 0; - v->valid = 1; return NGX_OK; } @@ -98,19 +102,21 @@ static ngx_int_t ngx_http_ssl_fingerprint_hash(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { - /* For access.log's map $http2_fingerpring {}: + /* For access.log's map $VAR {}: * if it's not found, then user could add a defined string */ v->not_found = 1; + if (r->connection->ssl == NULL) { + return NGX_OK; + } + if (ngx_ssl_ja3_hash(r->connection) != NGX_OK) { return NGX_OK; } v->data = r->connection->ssl->fp_ja3_hash.data; v->len = r->connection->ssl->fp_ja3_hash.len; - v->no_cacheable = 1; v->not_found = 0; - v->valid = 1; return NGX_OK; } @@ -119,7 +125,7 @@ static ngx_int_t ngx_http_http2_fingerprint(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { - /* For access.log's map $http2_fingerpring {}: + /* For access.log's map $VAR {}: * if it's not found, then user could add a defined string */ v->not_found = 1; @@ -130,14 +136,12 @@ ngx_http_http2_fingerprint(ngx_http_request_t *r, if (ngx_http2_fingerprint(r->connection, r->stream->connection) != NGX_OK) { - return NGX_OK; + return NGX_ERROR; } v->data = r->stream->connection->fp_str.data; v->len = r->stream->connection->fp_str.len; - v->valid = 1; v->not_found = 0; - v->no_cacheable = 1; return NGX_OK; } From 6e95027358d9180d3c12dd1fd94a77946b9094b3 Mon Sep 17 00:00:00 2001 From: Vasiliy Soshnikov Date: Thu, 6 Feb 2025 12:43:14 +0300 Subject: [PATCH 09/11] Add: build stream module, only if ssl stream is enabled. Update: readme.md's support versions --- README.md | 4 ++- config | 5 +++- ...gx_stream_ssl_fingerprint_preread_module.c | 29 +++++++------------ 3 files changed, 18 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 8b8e1a5..70ec704 100644 --- a/README.md +++ b/README.md @@ -8,14 +8,16 @@ A high performance nginx module for ja3 and http2 fingerprint. ### Support Matrix -| | OpenSSL_1_1_1 | openssl-3.0 | openssl-3.1 | openssl-3.2 | +| | OpenSSL_1_1_1 | openssl-3.0 | openssl-3.1 | openssl-3.2 | openssl-3.4 | | -----------| -------------------- | ----------- | ----------- | ----------- | +-----| | nginx-1.20 | ✅ | ✅ | ✅ | ✅ | | nginx-1.21 | ✅ | ✅ | ✅ | ✅ | | nginx-1.22 | ✅ | ✅ | ✅ | ✅ | | nginx-1.23 | ✅ | ✅ | ✅ | ✅ | | nginx-1.24 | ✅ | ✅ | ✅ | ✅ | | nginx-1.25 | ✅ | ✅ | ✅ | ✅ | +| nginx-1.27 | | | | | | ✅ | ## Configuration diff --git a/config b/config index 1d61bda..f5c142d 100644 --- a/config +++ b/config @@ -12,9 +12,12 @@ HTTP_MODULES="$HTTP_MODULES ngx_http_ssl_fingerprint_module" stream_module="" -if [ $STREAM_SSL_PREREAD = YES ]; then +if [ $STREAM_SSL = YES ]; then + echo "[+] ngx_stream_ssl_fingerprint_preread_module included" STREAM_MODULES="ngx_stream_ssl_fingerprint_preread_module $STREAM_MODULES" stream_module="$ngx_addon_dir/src/ngx_stream_ssl_fingerprint_preread_module.c" +else + echo "[-] ngx_stream_ssl_fingerprint_preread_module will no be compiled" fi NGX_ADDON_SRCS="$NGX_ADDON_SRCS \ diff --git a/src/ngx_stream_ssl_fingerprint_preread_module.c b/src/ngx_stream_ssl_fingerprint_preread_module.c index 616133c..a2629e2 100644 --- a/src/ngx_stream_ssl_fingerprint_preread_module.c +++ b/src/ngx_stream_ssl_fingerprint_preread_module.c @@ -1,9 +1,8 @@ /* - * Please make sure that you have been incuded --with-streams before --add-module - * Else this module won't be compiled + * Please make sure that you have been incuded --with-streams_ssl_moduke + * before --add-module Else this module won't be compiled */ -#if (NGX_STREAM_SSL) #include #include @@ -27,15 +26,15 @@ static ngx_stream_module_t ngx_stream_ssl_fingerprint_preread_module_ctx = { ngx_module_t ngx_stream_ssl_fingerprint_preread_module = { NGX_MODULE_V1, &ngx_stream_ssl_fingerprint_preread_module_ctx, /* module context */ - NULL, /* module directives */ - NGX_STREAM_MODULE, /* module type */ - NULL, /* init master */ - NULL, /* init module */ - NULL, /* init process */ - NULL, /* init thread */ - NULL, /* exit thread */ - NULL, /* exit process */ - NULL, /* exit master */ + NULL, /* module directives */ + NGX_STREAM_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ NGX_MODULE_V1_PADDING }; @@ -170,9 +169,3 @@ ngx_stream_ssl_fingerprint_preread_init(ngx_conf_t *cf) return NGX_OK; } -#else -# pragma message "nginx-ssl-fingerprint: " -# pragma message "disabled for ngx_stream or ngx_ssl_stream. Please add --with-streams with ssl support, " -# pragma message "if you wish to enable it" -#endif /** NGX_STREAM_SSL */ - From f3910421b50c7885842f2189d1833e2bb5627a9a Mon Sep 17 00:00:00 2001 From: Vas Soshnikov Date: Thu, 6 Feb 2025 12:45:52 +0300 Subject: [PATCH 10/11] Update README.md Fix MD --- README.md | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 70ec704..15923bf 100644 --- a/README.md +++ b/README.md @@ -9,15 +9,14 @@ A high performance nginx module for ja3 and http2 fingerprint. ### Support Matrix | | OpenSSL_1_1_1 | openssl-3.0 | openssl-3.1 | openssl-3.2 | openssl-3.4 | -| -----------| -------------------- | ----------- | ----------- | ----------- | ------| -| nginx-1.20 | ✅ | ✅ | ✅ | ✅ | -| nginx-1.21 | ✅ | ✅ | ✅ | ✅ | -| nginx-1.22 | ✅ | ✅ | ✅ | ✅ | -| nginx-1.23 | ✅ | ✅ | ✅ | ✅ | -| nginx-1.24 | ✅ | ✅ | ✅ | ✅ | -| nginx-1.25 | ✅ | ✅ | ✅ | ✅ | -| nginx-1.27 | | | | | | ✅ | +| -----------| -------------------- | ----------- | ----------- | ----------- | ----------- +| nginx-1.20 | ✅ | ✅ | ✅ | ✅ | | +| nginx-1.21 | ✅ | ✅ | ✅ | ✅ | | +| nginx-1.22 | ✅ | ✅ | ✅ | ✅ | | +| nginx-1.23 | ✅ | ✅ | ✅ | ✅ | | +| nginx-1.24 | ✅ | ✅ | ✅ | ✅ | | +| nginx-1.25 | ✅ | ✅ | ✅ | ✅ | | +| nginx-1.27 | | | | | ✅ | ## Configuration From bd882b328d0999f07035ee77afdd958a4e813b79 Mon Sep 17 00:00:00 2001 From: Vas Soshnikov Date: Tue, 13 May 2025 20:23:17 +0300 Subject: [PATCH 11/11] Fix of issues/63 (#64) Possible fix of the issue. Root cause: groups & formats or/and eq to NULL for some old .Net clients --- patches/openssl.openssl-3.4.patch | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/patches/openssl.openssl-3.4.patch b/patches/openssl.openssl-3.4.patch index 90c962f..45d4ac8 100644 --- a/patches/openssl.openssl-3.4.patch +++ b/patches/openssl.openssl-3.4.patch @@ -30,10 +30,10 @@ index 8e9b110bb3..3a2407b0e4 100644 # define TLSEXT_TYPE_compress_certificate 27 diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c -index 295b719ff2..7bfc9e2eb0 100644 +index 295b719ff2..c3df123253 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c -@@ -6641,6 +6641,107 @@ int SSL_client_hello_get1_extensions_present(SSL *s, int **out, size_t *outlen) +@@ -6641,6 +6641,108 @@ int SSL_client_hello_get1_extensions_present(SSL *s, int **out, size_t *outlen) return 0; } @@ -105,9 +105,9 @@ index 295b719ff2..7bfc9e2eb0 100644 + ptr += num; + + /* groups */ -+ num = PACKET_remaining(groups); -+ if (groups && num > 0) { -+ ++ if (groups ++ && (num = PACKET_remaining(groups)) > 0) ++ { + if (ossl_unlikely((ptr + num + sizeof(uint16_t)) > end)) + return 0; + memcpy(ptr, PACKET_data(groups), num); @@ -121,8 +121,9 @@ index 295b719ff2..7bfc9e2eb0 100644 + } + + /* formats */ -+ num = PACKET_remaining(formats); -+ if (formats && num > 0) { ++ if (formats ++ && (num = PACKET_remaining(formats)) > 0) ++ { + if (ossl_unlikely((ptr + num + sizeof(uint8_t)) > end)) + return 0; + memcpy(ptr, PACKET_data(formats), num);