Skip to content

Commit

Permalink
mod_ssl: Add SSLClientHelloVars directive which exposes various
Browse files Browse the repository at this point in the history
ClientHello properties in new SSL_CLIENTHELLO_* variables.

* modules/ssl/ssl_engine_kernel.c (ssl_hook_Fixup_vars): Add
  SSL_CLIENTHELLO_* vars.
  (copy_clienthello_vars): New function.
  (ssl_callback_ClientHello): Call it when needed.

* modules/ssl/ssl_engine_vars.c (ssl_var_lookup_ssl_clienthello): New
  function.
  (ssl_var_lookup_ssl): Call it for SSL_CLIENTHELLO_*.

* modules/ssl/ssl_private.h (modssl_clienthello_vars): Add type.
  (SSLConnRec): Add clienthello_vars pointer.

* modules/ssl/ssl_engine_config.c, modules/ssl/mod_ssl.c: Add handling
  of new SSLClientHelloVars directive.

Submitted by: Charles Smutz <csmutz gmail.com>
Github: closes #483


git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1921074 13f79535-47bb-0310-9956-ffa450edef68
  • Loading branch information
notroj committed Oct 1, 2024
1 parent cbadd66 commit e9915b2
Show file tree
Hide file tree
Showing 7 changed files with 196 additions and 1 deletion.
3 changes: 3 additions & 0 deletions changes-entries/ssl-hello-vars.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
*) mod_ssl: Add SSLClientHelloVars directive to expose various
ClientHello properties as SSL_CLIENTHELLO_* variables.
[Charles Smutz <csmutz gmail.com>]
32 changes: 32 additions & 0 deletions docs/manual/mod/mod_ssl.xml
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,14 @@ compatibility variables.</p>
<tr><td><code>SSL_SRP_USERINFO</code></td> <td>string</td> <td>SRP user info</td></tr>
<tr><td><code>SSL_TLS_SNI</code></td> <td>string</td> <td>Contents of the SNI TLS extension (if supplied with ClientHello)</td></tr>
<tr><td><code>SSL_HANDSHAKE_RTT</code></td> <td>number</td> <td>Round-trip time of TLS handshake in microseconds including endpoint processing (set to empty string if OpenSSL version prior to 3.2 or if round-trip time can not be determined)</td></tr>
<tr><td><code>SSL_CLIENTHELLO_VERSION</code></td> <td>string</td> <td>Version field (legacy) from ClientHello as four hex encoded characters</td></tr>
<tr><td><code>SSL_CLIENTHELLO_CIPHERS</code></td> <td>string</td> <td>Cipher Suites from ClientHello as four hex encoded characters per item</td></tr>
<tr><td><code>SSL_CLIENTHELLO_EXTENSIONS</code></td> <td>string</td> <td>Extension IDs from ClientHello as four hex encoded characters per item</td></tr>
<tr><td><code>SSL_CLIENTHELLO_GROUPS</code></td> <td>string</td> <td>Value of Supported Groups extension (10) from ClientHello as four hex encoded characters per item</td></tr>
<tr><td><code>SSL_CLIENTHELLO_EC_FORMATS</code></td> <td>string</td> <td>Value of EC Point Formats extension (11) from ClientHello as two hex encoded characters per item</td></tr>
<tr><td><code>SSL_CLIENTHELLO_SIG_ALGOS</code></td> <td>string</td> <td>Value of Signature Algorithms extension (13) from ClientHello as four hex encoded characters per item</td></tr>
<tr><td><code>SSL_CLIENTHELLO_ALPN</code></td> <td>string</td> <td>Value of ALPN extension (16) from ClientHello as hex encoded string including leading string lengths</td></tr>
<tr><td><code>SSL_CLIENTHELLO_VERSIONS</code></td> <td>string</td> <td>Value of Supported Versions extension (43) from ClientHello as four hex encoded characters per item</td></tr>
</table>

<p><em>x509</em> specifies a component of an X.509 DN; one of
Expand Down Expand Up @@ -142,6 +150,10 @@ suffix (if any). For example, <code>SSL_SERVER_S_DN_OU_RAW</code> or
<p><code>SSL_CLIENT_V_REMAIN</code> is only available in version 2.1
and later.</p>

<p>The <code>SSL_CLIENTHELLO_*</code> variables require the directive
<directive module="mod_ssl">SSLClientHelloVars</directive> to be
enabled or they will not be populated.</p>

<p>A number of additional environment variables can also be used
in <directive>SSLRequire</directive> expressions, or in custom log
formats:</p>
Expand Down Expand Up @@ -2858,6 +2870,26 @@ be protected with file permissions similar to those used for
</usage>
</directivesynopsis>

<directivesynopsis>
<name>SSLClientHelloVars</name>
<description>Enable collection of ClientHello variables</description>
<syntax>SSLClientHelloVars on|off</syntax>
<default>SSLClientHelloVars off</default>
<contextlist><context>server config</context>
<context>virtual host</context></contextlist>
<compatibility>Available in httpd 2.5.2 and later, requires OpenSSL 1.1.1 or later</compatibility>

<usage>
<p>This directive enables collection of ClientHello data during the handshake that is retained for
the length of the connection so it can be exposed as <code>SSL_CLIENTHELLLO_*</code> environment
variables for requests depending upon the <code>StdEnvVars</code> setting. The variables are
formatted as the hex-encoded raw buffers seen in the raw network protocol and as provided
by OpenSSL. GREASE (RFC 8701) values are filtered by OpenSSL when enumerating extension IDs, but
otherwise, are passed through unchanged for other variables. If this directive is not enabled or
if OpenSSL prior to version 1.1.1 is used, these variables will not have a value set.</p>
</usage>
</directivesynopsis>

<directivesynopsis>
<name>SSLCompression</name>
<description>Enable compression on the SSL level</description>
Expand Down
3 changes: 3 additions & 0 deletions modules/ssl/mod_ssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,9 @@ static const command_rec ssl_config_cmds[] = {
"('[+-][" SSL_PROTOCOLS "] ...' - see manual)")
SSL_CMD_SRV(HonorCipherOrder, FLAG,
"Use the server's cipher ordering preference")
SSL_CMD_SRV(ClientHelloVars, FLAG,
"Enable SSL ClientHello variable collection "
"(`on', `off')")
SSL_CMD_SRV(Compression, FLAG,
"Enable SSL level compression "
"(`on', `off')")
Expand Down
9 changes: 9 additions & 0 deletions modules/ssl/ssl_engine_config.c
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ static SSLSrvConfigRec *ssl_config_server_new(apr_pool_t *p)
#ifndef OPENSSL_NO_COMP
sc->compression = UNSET;
#endif
sc->clienthello_vars = UNSET;
sc->session_tickets = UNSET;

modssl_ctx_init_server(sc, p);
Expand Down Expand Up @@ -347,6 +348,7 @@ void *ssl_config_server_merge(apr_pool_t *p, void *basev, void *addv)
cfgMerge(enabled, SSL_ENABLED_UNSET);
cfgMergeInt(session_cache_timeout);
cfgMergeBool(cipher_server_pref);
cfgMergeBool(clienthello_vars);
#ifdef HAVE_TLSEXT
cfgMerge(strict_sni_vhost_check, SSL_ENABLED_UNSET);
#endif
Expand Down Expand Up @@ -957,6 +959,13 @@ const char *ssl_cmd_SSLCompression(cmd_parms *cmd, void *dcfg, int flag)
return NULL;
}

const char *ssl_cmd_SSLClientHelloVars(cmd_parms *cmd, void *dcfg, int flag)
{
SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
sc->clienthello_vars = flag ? TRUE : FALSE;
return NULL;
}

const char *ssl_cmd_SSLHonorCipherOrder(cmd_parms *cmd, void *dcfg, int flag)
{
#ifdef SSL_OP_CIPHER_SERVER_PREFERENCE
Expand Down
59 changes: 59 additions & 0 deletions modules/ssl/ssl_engine_kernel.c
Original file line number Diff line number Diff line change
Expand Up @@ -1547,6 +1547,14 @@ static const char *const ssl_hook_Fixup_vars[] = {
"SSL_SRP_USERINFO",
#endif
"SSL_HANDSHAKE_RTT",
"SSL_CLIENTHELLO_VERSION",
"SSL_CLIENTHELLO_CIPHERS",
"SSL_CLIENTHELLO_EXTENSIONS",
"SSL_CLIENTHELLO_GROUPS",
"SSL_CLIENTHELLO_EC_FORMATS",
"SSL_CLIENTHELLO_SIG_ALGOS",
"SSL_CLIENTHELLO_ALPN",
"SSL_CLIENTHELLO_VERSIONS",
NULL
};

Expand Down Expand Up @@ -2465,6 +2473,53 @@ int ssl_callback_ServerNameIndication(SSL *ssl, int *al, modssl_ctx_t *mctx)
}

#if OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined(LIBRESSL_VERSION_NUMBER)
/*
* Copy data from clienthello for env vars use later
*/
static void copy_clienthello_vars(conn_rec *c, SSL *ssl)
{
SSLConnRec *sslcon;
modssl_clienthello_vars *clienthello_vars;
const unsigned char *data;
int *ids;

sslcon = myConnConfig(c);

sslcon->clienthello_vars = apr_pcalloc(c->pool, sizeof(*clienthello_vars));
clienthello_vars = sslcon->clienthello_vars;

clienthello_vars->version = SSL_client_hello_get0_legacy_version(ssl);
clienthello_vars->ciphers_len = SSL_client_hello_get0_ciphers(ssl, &data);
if (clienthello_vars->ciphers_len > 0) {
clienthello_vars->ciphers_data = apr_pmemdup(c->pool, data, clienthello_vars->ciphers_len);
}
if (SSL_client_hello_get1_extensions_present(ssl, &ids, &clienthello_vars->extids_len) == 1) {
if (clienthello_vars->extids_len > 0)
clienthello_vars->extids_data = apr_pmemdup(c->pool, ids, clienthello_vars->extids_len * sizeof(int));
OPENSSL_free(ids);
}
if (SSL_client_hello_get0_ext(ssl, TLSEXT_TYPE_supported_groups, &data, &clienthello_vars->ecgroups_len) == 1) {
if (clienthello_vars->ecgroups_len > 0)
clienthello_vars->ecgroups_data = apr_pmemdup(c->pool, data, clienthello_vars->ecgroups_len);
}
if (SSL_client_hello_get0_ext(ssl, TLSEXT_TYPE_ec_point_formats, &data, &clienthello_vars->ecformats_len) == 1) {
if (clienthello_vars->ecformats_len > 0)
clienthello_vars->ecformats_data = apr_pmemdup(c->pool, data, clienthello_vars->ecformats_len);
}
if (SSL_client_hello_get0_ext(ssl, TLSEXT_TYPE_signature_algorithms, &data, &clienthello_vars->sigalgos_len) == 1) {
if (clienthello_vars->sigalgos_len > 0)
clienthello_vars->sigalgos_data = apr_pmemdup(c->pool, data, clienthello_vars->sigalgos_len);
}
if (SSL_client_hello_get0_ext(ssl, TLSEXT_TYPE_application_layer_protocol_negotiation, &data, &clienthello_vars->alpn_len) == 1) {
if (clienthello_vars->alpn_len > 0)
clienthello_vars->alpn_data = apr_pmemdup(c->pool, data, clienthello_vars->alpn_len);
}
if (SSL_client_hello_get0_ext(ssl, TLSEXT_TYPE_supported_versions, &data, &clienthello_vars->versions_len) == 1) {
if (clienthello_vars->versions_len > 0)
clienthello_vars->versions_data = apr_pmemdup(c->pool, data, clienthello_vars->versions_len);
}
}

/*
* This callback function is called when the ClientHello is received.
*/
Expand Down Expand Up @@ -2520,6 +2575,10 @@ int ssl_callback_ClientHello(SSL *ssl, int *al, void *arg)

give_up:
init_vhost(c, ssl, servername);

if (mySrvConfigFromConn(c)->clienthello_vars == TRUE)
copy_clienthello_vars(c, ssl);

return SSL_CLIENT_HELLO_SUCCESS;
}
#endif /* OPENSSL_VERSION_NUMBER < 0x10101000L */
Expand Down
60 changes: 60 additions & 0 deletions modules/ssl/ssl_engine_vars.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ static const char *ssl_var_lookup_ssl_cert_verify(apr_pool_t *p, const SSLConnRe
static const char *ssl_var_lookup_ssl_cipher(apr_pool_t *p, const SSLConnRec *sslconn, const char *var);
static void ssl_var_lookup_ssl_cipher_bits(SSL *ssl, int *usekeysize, int *algkeysize);
static const char *ssl_var_lookup_ssl_handshake_rtt(apr_pool_t *p, SSL *ssl);
static const char *ssl_var_lookup_ssl_clienthello(apr_pool_t *p, const SSLConnRec *sslconn, const char *var);
static const char *ssl_var_lookup_ssl_version(const char *var);
static const char *ssl_var_lookup_ssl_compress_meth(SSL *ssl);

Expand Down Expand Up @@ -476,6 +477,9 @@ static const char *ssl_var_lookup_ssl(apr_pool_t *p, const SSLConnRec *sslconn,
else if (ssl != NULL && strcEQ(var, "HANDSHAKE_RTT")) {
result = ssl_var_lookup_ssl_handshake_rtt(p, ssl);
}
else if (ssl != NULL && strlen(var) >= 12 && strcEQn(var, "CLIENTHELLO_", 12)) {
result = ssl_var_lookup_ssl_clienthello(p, sslconn, var+12);
}
else if (ssl != NULL && strlen(var) > 18 && strcEQn(var, "CLIENT_CERT_CHAIN_", 18)) {
sk = SSL_get_peer_cert_chain(ssl);
result = ssl_var_lookup_ssl_cert_chain(p, sk, var+18, 1);
Expand Down Expand Up @@ -975,6 +979,62 @@ static const char *ssl_var_lookup_ssl_handshake_rtt(apr_pool_t *p, SSL *ssl)
return NULL;
}

static const char *ssl_var_lookup_ssl_clienthello(apr_pool_t *p, const SSLConnRec *sslconn, const char *var)
{
#if OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined(LIBRESSL_VERSION_NUMBER)
char *value;
modssl_clienthello_vars *clienthello_vars;
apr_size_t i;

clienthello_vars = sslconn->clienthello_vars;

if (!clienthello_vars)
return NULL;

if (strEQ(var, "VERSION")) {
return apr_psprintf(p, "%04x", (uint16_t) clienthello_vars->version);
}
else if (strEQ(var, "CIPHERS") && (clienthello_vars->ciphers_len > 0)) {
value = apr_palloc(p, clienthello_vars->ciphers_len * 2 + 1);
ap_bin2hex(clienthello_vars->ciphers_data, clienthello_vars->ciphers_len, value);
return value;
}
else if (strEQ(var, "EXTENSIONS") && (clienthello_vars->extids_len > 0)) {
value = apr_palloc(p, clienthello_vars->extids_len * 4 + 1);
for (i = 0; i < clienthello_vars->extids_len; i++) {
apr_snprintf(value + i * 4, 5, "%04x", (uint16_t) clienthello_vars->extids_data[i]);
}
return value;
}
else if (strEQ(var, "GROUPS") && (clienthello_vars->ecgroups_len > 2)) {
value = apr_palloc(p, clienthello_vars->ecgroups_len * 2 + 1 - 2);
ap_bin2hex(clienthello_vars->ecgroups_data + 2, clienthello_vars->ecgroups_len - 2, value);
return value;
}
else if (strEQ(var, "EC_FORMATS") && (clienthello_vars->ecformats_len > 1)) {
value = apr_palloc(p, clienthello_vars->ecformats_len * 2 + 1 - 1);
ap_bin2hex(clienthello_vars->ecformats_data + 1, clienthello_vars->ecformats_len - 1, value);
return value;
}
else if (strEQ(var, "SIG_ALGOS") && (clienthello_vars->sigalgos_len > 2)) {
value = apr_palloc(p, clienthello_vars->sigalgos_len * 2 + 1 - 2);
ap_bin2hex(clienthello_vars->sigalgos_data + 2, clienthello_vars->sigalgos_len - 2, value);
return value;
}
else if (strEQ(var, "ALPN") && (clienthello_vars->alpn_len > 2)) {
value = apr_palloc(p, clienthello_vars->alpn_len * 2 + 1 - 2);
ap_bin2hex(clienthello_vars->alpn_data + 2, clienthello_vars->alpn_len - 2, value);
return value;
}
else if (strEQ(var, "VERSIONS") && (clienthello_vars->versions_len > 1)) {
value = apr_palloc(p, clienthello_vars->versions_len * 2 + 1 - 1);
ap_bin2hex(clienthello_vars->versions_data + 1, clienthello_vars->versions_len - 1, value);
return value;
}
#endif
return NULL;
}

static const char *ssl_var_lookup_ssl_version(const char *var)
{
if (strEQ(var, "INTERFACE")) {
Expand Down
31 changes: 30 additions & 1 deletion modules/ssl/ssl_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,30 @@ typedef enum {
SSL_SHUTDOWN_TYPE_ACCURATE
} ssl_shutdown_type_e;

/**
* Define the structure to hold clienthello variables
* (later exposed as environment vars)
*/
#if OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined(LIBRESSL_VERSION_NUMBER)
typedef struct {
unsigned int version;
apr_size_t ciphers_len;
const unsigned char *ciphers_data;
apr_size_t extids_len;
const int *extids_data;
apr_size_t ecgroups_len;
const unsigned char *ecgroups_data;
apr_size_t ecformats_len;
const unsigned char *ecformats_data;
apr_size_t sigalgos_len;
const unsigned char *sigalgos_data;
apr_size_t alpn_len;
const unsigned char *alpn_data;
apr_size_t versions_len;
const unsigned char *versions_data;
} modssl_clienthello_vars;
#endif

typedef struct {
SSL *ssl;
const char *client_dn;
Expand Down Expand Up @@ -604,6 +628,10 @@ typedef struct {
const char *cipher_suite; /* cipher suite used in last reneg */
int service_unavailable; /* thouugh we negotiate SSL, no requests will be served */
int vhost_found; /* whether we found vhost from SNI already */

#if OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined(LIBRESSL_VERSION_NUMBER)
modssl_clienthello_vars *clienthello_vars; /* info from clienthello callback */
#endif
} SSLConnRec;

/* Private keys are retained across reloads, since decryption
Expand Down Expand Up @@ -833,7 +861,7 @@ struct SSLSrvConfigRec {
BOOL compression;
#endif
BOOL session_tickets;

BOOL clienthello_vars;
};

/**
Expand Down Expand Up @@ -893,6 +921,7 @@ const char *ssl_cmd_SSLCARevocationPath(cmd_parms *, void *, const char *);
const char *ssl_cmd_SSLCARevocationFile(cmd_parms *, void *, const char *);
const char *ssl_cmd_SSLCARevocationCheck(cmd_parms *, void *, const char *);
const char *ssl_cmd_SSLHonorCipherOrder(cmd_parms *cmd, void *dcfg, int flag);
const char *ssl_cmd_SSLClientHelloVars(cmd_parms *, void *, int flag);
const char *ssl_cmd_SSLCompression(cmd_parms *, void *, int flag);
const char *ssl_cmd_SSLSessionTickets(cmd_parms *, void *, int flag);
const char *ssl_cmd_SSLVerifyClient(cmd_parms *, void *, const char *);
Expand Down

0 comments on commit e9915b2

Please sign in to comment.