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) <noreply@anthropic.com>
This commit is contained in:
Vadim Zhestikov 2026-05-22 14:56:16 -07:00
parent d44205284f
commit a3ef7dea8a
4 changed files with 111 additions and 0 deletions

View file

@ -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)
{

View file

@ -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,

View file

@ -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 },

View file

@ -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 },