From a3ef7dea8a43c4cba1939660ed78257563d42667 Mon Sep 17 00:00:00 2001 From: Vadim Zhestikov Date: Fri, 22 May 2026 14:56:16 -0700 Subject: [PATCH] SSL: add $ssl_sigalgs variable. The new $ssl_sigalgs variable lists all signature algorithms advertised by the client in its ClientHello message, colon-separated, in analogy to $ssl_ciphers and $ssl_curves. On OpenSSL 4.0+, SSL_get0_sigalg() is used, which returns proper TLS scheme names (e.g. "rsa_pkcs1_sha256", "ecdsa_secp256r1_sha256") and falls back to "0xHHHH" hex for schemes without a registered name. On older OpenSSL (1.0.2+, not BoringSSL, not LibreSSL), SSL_get_sigalgs() is used. Since OBJ_nid2sn() on the combined psignhash NID is not injective across TLS SignatureScheme codes (e.g. 0x0403 and 0x081a both yield "ecdsa-with-SHA256"), raw SignatureScheme codes are reported to avoid ambiguity and to match the hex fallback format of SSL_get0_sigalg(). Co-Authored-By: Claude Sonnet 4.6 (1M context) --- src/event/ngx_event_openssl.c | 103 +++++++++++++++++++++++++ src/event/ngx_event_openssl.h | 2 + src/http/modules/ngx_http_ssl_module.c | 3 + src/stream/ngx_stream_ssl_module.c | 3 + 4 files changed, 111 insertions(+) diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c index 1653be0c3..bdca69768 100644 --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -5669,6 +5669,109 @@ ngx_ssl_get_sigalg(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s) } +ngx_int_t +ngx_ssl_get_sigalgs(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s) +{ +#if (OPENSSL_VERSION_NUMBER >= 0x40000000L) + + int n, i; + size_t len; + u_char *p; + const char *name; + unsigned int codepoint; + + n = SSL_get0_sigalg(c->ssl->connection, -1, NULL, NULL); + + if (n <= 0) { + s->len = 0; + return NGX_OK; + } + + len = 0; + + for (i = 0; i < n; i++) { + SSL_get0_sigalg(c->ssl->connection, i, &codepoint, &name); + len += name ? ngx_strlen(name) : sizeof("0x0000") - 1; + len += sizeof(":") - 1; + } + + s->data = ngx_pnalloc(pool, len); + if (s->data == NULL) { + return NGX_ERROR; + } + + p = s->data; + + for (i = 0; i < n; i++) { + SSL_get0_sigalg(c->ssl->connection, i, &codepoint, &name); + + if (name) { + p = ngx_cpymem(p, name, ngx_strlen(name)); + + } else { + p = ngx_sprintf(p, "0x%04xd", codepoint); + } + + *p++ = ':'; + } + + p--; + + s->len = p - s->data; + +#elif (OPENSSL_VERSION_NUMBER >= 0x10002000L \ + && !defined OPENSSL_IS_BORINGSSL \ + && !defined LIBRESSL_VERSION_NUMBER) + + /* + * SSL_get_sigalgs() suits TLS 1.2; OBJ_nid2sn() on the combined + * psignhash NID is not injective across TLS SignatureScheme codes + * (e.g. 0x0403 and 0x081a both yield "ecdsa-with-SHA256"), so raw + * SignatureScheme codes are used to avoid ambiguity and to match + * the hex fallback format of SSL_get0_sigalg() above. + */ + + int n, i; + size_t len; + u_char *p; + unsigned char rsig, rhash; + + n = SSL_get_sigalgs(c->ssl->connection, -1, NULL, NULL, NULL, NULL, NULL); + + if (n <= 0) { + s->len = 0; + return NGX_OK; + } + + len = n * (sizeof("0x0000") - 1 + sizeof(":") - 1); + + s->data = ngx_pnalloc(pool, len); + if (s->data == NULL) { + return NGX_ERROR; + } + + p = s->data; + + for (i = 0; i < n; i++) { + SSL_get_sigalgs(c->ssl->connection, i, NULL, NULL, NULL, &rsig, &rhash); + p = ngx_sprintf(p, "0x%04xd", rhash << 8 | rsig); + *p++ = ':'; + } + + p--; + + s->len = p - s->data; + +#else + + s->len = 0; + +#endif + + return NGX_OK; +} + + ngx_int_t ngx_ssl_get_session_id(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s) { diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h index 79ae39503..12ac01b3c 100644 --- a/src/event/ngx_event_openssl.h +++ b/src/event/ngx_event_openssl.h @@ -337,6 +337,8 @@ ngx_int_t ngx_ssl_get_curves(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s); ngx_int_t ngx_ssl_get_sigalg(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s); +ngx_int_t ngx_ssl_get_sigalgs(ngx_connection_t *c, ngx_pool_t *pool, + ngx_str_t *s); ngx_int_t ngx_ssl_get_session_id(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s); ngx_int_t ngx_ssl_get_session_reused(ngx_connection_t *c, ngx_pool_t *pool, diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c index 43fcafd50..0afcc9dd1 100644 --- a/src/http/modules/ngx_http_ssl_module.c +++ b/src/http/modules/ngx_http_ssl_module.c @@ -368,6 +368,9 @@ static ngx_http_variable_t ngx_http_ssl_vars[] = { { ngx_string("ssl_sigalg"), NULL, ngx_http_ssl_variable, (uintptr_t) ngx_ssl_get_sigalg, NGX_HTTP_VAR_CHANGEABLE, 0 }, + { ngx_string("ssl_sigalgs"), NULL, ngx_http_ssl_variable, + (uintptr_t) ngx_ssl_get_sigalgs, NGX_HTTP_VAR_CHANGEABLE, 0 }, + { ngx_string("ssl_session_id"), NULL, ngx_http_ssl_variable, (uintptr_t) ngx_ssl_get_session_id, NGX_HTTP_VAR_CHANGEABLE, 0 }, diff --git a/src/stream/ngx_stream_ssl_module.c b/src/stream/ngx_stream_ssl_module.c index 0e17cff4d..c68333a1c 100644 --- a/src/stream/ngx_stream_ssl_module.c +++ b/src/stream/ngx_stream_ssl_module.c @@ -367,6 +367,9 @@ static ngx_stream_variable_t ngx_stream_ssl_vars[] = { { ngx_string("ssl_sigalg"), NULL, ngx_stream_ssl_variable, (uintptr_t) ngx_ssl_get_sigalg, NGX_STREAM_VAR_CHANGEABLE, 0 }, + { ngx_string("ssl_sigalgs"), NULL, ngx_stream_ssl_variable, + (uintptr_t) ngx_ssl_get_sigalgs, NGX_STREAM_VAR_CHANGEABLE, 0 }, + { ngx_string("ssl_session_id"), NULL, ngx_stream_ssl_variable, (uintptr_t) ngx_ssl_get_session_id, NGX_STREAM_VAR_CHANGEABLE, 0 },