Skip to content

Commit e9915b2

Browse files
committed
mod_ssl: Add SSLClientHelloVars directive which exposes various
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 apache#483 git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1921074 13f79535-47bb-0310-9956-ffa450edef68
1 parent cbadd66 commit e9915b2

File tree

7 files changed

+196
-1
lines changed

7 files changed

+196
-1
lines changed

changes-entries/ssl-hello-vars.txt

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
*) mod_ssl: Add SSLClientHelloVars directive to expose various
2+
ClientHello properties as SSL_CLIENTHELLO_* variables.
3+
[Charles Smutz <csmutz gmail.com>]

docs/manual/mod/mod_ssl.xml

+32
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,14 @@ compatibility variables.</p>
110110
<tr><td><code>SSL_SRP_USERINFO</code></td> <td>string</td> <td>SRP user info</td></tr>
111111
<tr><td><code>SSL_TLS_SNI</code></td> <td>string</td> <td>Contents of the SNI TLS extension (if supplied with ClientHello)</td></tr>
112112
<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>
113+
<tr><td><code>SSL_CLIENTHELLO_VERSION</code></td> <td>string</td> <td>Version field (legacy) from ClientHello as four hex encoded characters</td></tr>
114+
<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>
115+
<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>
116+
<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>
117+
<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>
118+
<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>
119+
<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>
120+
<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>
113121
</table>
114122

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

153+
<p>The <code>SSL_CLIENTHELLO_*</code> variables require the directive
154+
<directive module="mod_ssl">SSLClientHelloVars</directive> to be
155+
enabled or they will not be populated.</p>
156+
145157
<p>A number of additional environment variables can also be used
146158
in <directive>SSLRequire</directive> expressions, or in custom log
147159
formats:</p>
@@ -2858,6 +2870,26 @@ be protected with file permissions similar to those used for
28582870
</usage>
28592871
</directivesynopsis>
28602872

2873+
<directivesynopsis>
2874+
<name>SSLClientHelloVars</name>
2875+
<description>Enable collection of ClientHello variables</description>
2876+
<syntax>SSLClientHelloVars on|off</syntax>
2877+
<default>SSLClientHelloVars off</default>
2878+
<contextlist><context>server config</context>
2879+
<context>virtual host</context></contextlist>
2880+
<compatibility>Available in httpd 2.5.2 and later, requires OpenSSL 1.1.1 or later</compatibility>
2881+
2882+
<usage>
2883+
<p>This directive enables collection of ClientHello data during the handshake that is retained for
2884+
the length of the connection so it can be exposed as <code>SSL_CLIENTHELLLO_*</code> environment
2885+
variables for requests depending upon the <code>StdEnvVars</code> setting. The variables are
2886+
formatted as the hex-encoded raw buffers seen in the raw network protocol and as provided
2887+
by OpenSSL. GREASE (RFC 8701) values are filtered by OpenSSL when enumerating extension IDs, but
2888+
otherwise, are passed through unchanged for other variables. If this directive is not enabled or
2889+
if OpenSSL prior to version 1.1.1 is used, these variables will not have a value set.</p>
2890+
</usage>
2891+
</directivesynopsis>
2892+
28612893
<directivesynopsis>
28622894
<name>SSLCompression</name>
28632895
<description>Enable compression on the SSL level</description>

modules/ssl/mod_ssl.c

+3
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,9 @@ static const command_rec ssl_config_cmds[] = {
166166
"('[+-][" SSL_PROTOCOLS "] ...' - see manual)")
167167
SSL_CMD_SRV(HonorCipherOrder, FLAG,
168168
"Use the server's cipher ordering preference")
169+
SSL_CMD_SRV(ClientHelloVars, FLAG,
170+
"Enable SSL ClientHello variable collection "
171+
"(`on', `off')")
169172
SSL_CMD_SRV(Compression, FLAG,
170173
"Enable SSL level compression "
171174
"(`on', `off')")

modules/ssl/ssl_engine_config.c

+9
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ static SSLSrvConfigRec *ssl_config_server_new(apr_pool_t *p)
220220
#ifndef OPENSSL_NO_COMP
221221
sc->compression = UNSET;
222222
#endif
223+
sc->clienthello_vars = UNSET;
223224
sc->session_tickets = UNSET;
224225

225226
modssl_ctx_init_server(sc, p);
@@ -347,6 +348,7 @@ void *ssl_config_server_merge(apr_pool_t *p, void *basev, void *addv)
347348
cfgMerge(enabled, SSL_ENABLED_UNSET);
348349
cfgMergeInt(session_cache_timeout);
349350
cfgMergeBool(cipher_server_pref);
351+
cfgMergeBool(clienthello_vars);
350352
#ifdef HAVE_TLSEXT
351353
cfgMerge(strict_sni_vhost_check, SSL_ENABLED_UNSET);
352354
#endif
@@ -957,6 +959,13 @@ const char *ssl_cmd_SSLCompression(cmd_parms *cmd, void *dcfg, int flag)
957959
return NULL;
958960
}
959961

962+
const char *ssl_cmd_SSLClientHelloVars(cmd_parms *cmd, void *dcfg, int flag)
963+
{
964+
SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
965+
sc->clienthello_vars = flag ? TRUE : FALSE;
966+
return NULL;
967+
}
968+
960969
const char *ssl_cmd_SSLHonorCipherOrder(cmd_parms *cmd, void *dcfg, int flag)
961970
{
962971
#ifdef SSL_OP_CIPHER_SERVER_PREFERENCE

modules/ssl/ssl_engine_kernel.c

+59
Original file line numberDiff line numberDiff line change
@@ -1547,6 +1547,14 @@ static const char *const ssl_hook_Fixup_vars[] = {
15471547
"SSL_SRP_USERINFO",
15481548
#endif
15491549
"SSL_HANDSHAKE_RTT",
1550+
"SSL_CLIENTHELLO_VERSION",
1551+
"SSL_CLIENTHELLO_CIPHERS",
1552+
"SSL_CLIENTHELLO_EXTENSIONS",
1553+
"SSL_CLIENTHELLO_GROUPS",
1554+
"SSL_CLIENTHELLO_EC_FORMATS",
1555+
"SSL_CLIENTHELLO_SIG_ALGOS",
1556+
"SSL_CLIENTHELLO_ALPN",
1557+
"SSL_CLIENTHELLO_VERSIONS",
15501558
NULL
15511559
};
15521560

@@ -2465,6 +2473,53 @@ int ssl_callback_ServerNameIndication(SSL *ssl, int *al, modssl_ctx_t *mctx)
24652473
}
24662474

24672475
#if OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined(LIBRESSL_VERSION_NUMBER)
2476+
/*
2477+
* Copy data from clienthello for env vars use later
2478+
*/
2479+
static void copy_clienthello_vars(conn_rec *c, SSL *ssl)
2480+
{
2481+
SSLConnRec *sslcon;
2482+
modssl_clienthello_vars *clienthello_vars;
2483+
const unsigned char *data;
2484+
int *ids;
2485+
2486+
sslcon = myConnConfig(c);
2487+
2488+
sslcon->clienthello_vars = apr_pcalloc(c->pool, sizeof(*clienthello_vars));
2489+
clienthello_vars = sslcon->clienthello_vars;
2490+
2491+
clienthello_vars->version = SSL_client_hello_get0_legacy_version(ssl);
2492+
clienthello_vars->ciphers_len = SSL_client_hello_get0_ciphers(ssl, &data);
2493+
if (clienthello_vars->ciphers_len > 0) {
2494+
clienthello_vars->ciphers_data = apr_pmemdup(c->pool, data, clienthello_vars->ciphers_len);
2495+
}
2496+
if (SSL_client_hello_get1_extensions_present(ssl, &ids, &clienthello_vars->extids_len) == 1) {
2497+
if (clienthello_vars->extids_len > 0)
2498+
clienthello_vars->extids_data = apr_pmemdup(c->pool, ids, clienthello_vars->extids_len * sizeof(int));
2499+
OPENSSL_free(ids);
2500+
}
2501+
if (SSL_client_hello_get0_ext(ssl, TLSEXT_TYPE_supported_groups, &data, &clienthello_vars->ecgroups_len) == 1) {
2502+
if (clienthello_vars->ecgroups_len > 0)
2503+
clienthello_vars->ecgroups_data = apr_pmemdup(c->pool, data, clienthello_vars->ecgroups_len);
2504+
}
2505+
if (SSL_client_hello_get0_ext(ssl, TLSEXT_TYPE_ec_point_formats, &data, &clienthello_vars->ecformats_len) == 1) {
2506+
if (clienthello_vars->ecformats_len > 0)
2507+
clienthello_vars->ecformats_data = apr_pmemdup(c->pool, data, clienthello_vars->ecformats_len);
2508+
}
2509+
if (SSL_client_hello_get0_ext(ssl, TLSEXT_TYPE_signature_algorithms, &data, &clienthello_vars->sigalgos_len) == 1) {
2510+
if (clienthello_vars->sigalgos_len > 0)
2511+
clienthello_vars->sigalgos_data = apr_pmemdup(c->pool, data, clienthello_vars->sigalgos_len);
2512+
}
2513+
if (SSL_client_hello_get0_ext(ssl, TLSEXT_TYPE_application_layer_protocol_negotiation, &data, &clienthello_vars->alpn_len) == 1) {
2514+
if (clienthello_vars->alpn_len > 0)
2515+
clienthello_vars->alpn_data = apr_pmemdup(c->pool, data, clienthello_vars->alpn_len);
2516+
}
2517+
if (SSL_client_hello_get0_ext(ssl, TLSEXT_TYPE_supported_versions, &data, &clienthello_vars->versions_len) == 1) {
2518+
if (clienthello_vars->versions_len > 0)
2519+
clienthello_vars->versions_data = apr_pmemdup(c->pool, data, clienthello_vars->versions_len);
2520+
}
2521+
}
2522+
24682523
/*
24692524
* This callback function is called when the ClientHello is received.
24702525
*/
@@ -2520,6 +2575,10 @@ int ssl_callback_ClientHello(SSL *ssl, int *al, void *arg)
25202575

25212576
give_up:
25222577
init_vhost(c, ssl, servername);
2578+
2579+
if (mySrvConfigFromConn(c)->clienthello_vars == TRUE)
2580+
copy_clienthello_vars(c, ssl);
2581+
25232582
return SSL_CLIENT_HELLO_SUCCESS;
25242583
}
25252584
#endif /* OPENSSL_VERSION_NUMBER < 0x10101000L */

modules/ssl/ssl_engine_vars.c

+60
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ static const char *ssl_var_lookup_ssl_cert_verify(apr_pool_t *p, const SSLConnRe
5252
static const char *ssl_var_lookup_ssl_cipher(apr_pool_t *p, const SSLConnRec *sslconn, const char *var);
5353
static void ssl_var_lookup_ssl_cipher_bits(SSL *ssl, int *usekeysize, int *algkeysize);
5454
static const char *ssl_var_lookup_ssl_handshake_rtt(apr_pool_t *p, SSL *ssl);
55+
static const char *ssl_var_lookup_ssl_clienthello(apr_pool_t *p, const SSLConnRec *sslconn, const char *var);
5556
static const char *ssl_var_lookup_ssl_version(const char *var);
5657
static const char *ssl_var_lookup_ssl_compress_meth(SSL *ssl);
5758

@@ -476,6 +477,9 @@ static const char *ssl_var_lookup_ssl(apr_pool_t *p, const SSLConnRec *sslconn,
476477
else if (ssl != NULL && strcEQ(var, "HANDSHAKE_RTT")) {
477478
result = ssl_var_lookup_ssl_handshake_rtt(p, ssl);
478479
}
480+
else if (ssl != NULL && strlen(var) >= 12 && strcEQn(var, "CLIENTHELLO_", 12)) {
481+
result = ssl_var_lookup_ssl_clienthello(p, sslconn, var+12);
482+
}
479483
else if (ssl != NULL && strlen(var) > 18 && strcEQn(var, "CLIENT_CERT_CHAIN_", 18)) {
480484
sk = SSL_get_peer_cert_chain(ssl);
481485
result = ssl_var_lookup_ssl_cert_chain(p, sk, var+18, 1);
@@ -975,6 +979,62 @@ static const char *ssl_var_lookup_ssl_handshake_rtt(apr_pool_t *p, SSL *ssl)
975979
return NULL;
976980
}
977981

982+
static const char *ssl_var_lookup_ssl_clienthello(apr_pool_t *p, const SSLConnRec *sslconn, const char *var)
983+
{
984+
#if OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined(LIBRESSL_VERSION_NUMBER)
985+
char *value;
986+
modssl_clienthello_vars *clienthello_vars;
987+
apr_size_t i;
988+
989+
clienthello_vars = sslconn->clienthello_vars;
990+
991+
if (!clienthello_vars)
992+
return NULL;
993+
994+
if (strEQ(var, "VERSION")) {
995+
return apr_psprintf(p, "%04x", (uint16_t) clienthello_vars->version);
996+
}
997+
else if (strEQ(var, "CIPHERS") && (clienthello_vars->ciphers_len > 0)) {
998+
value = apr_palloc(p, clienthello_vars->ciphers_len * 2 + 1);
999+
ap_bin2hex(clienthello_vars->ciphers_data, clienthello_vars->ciphers_len, value);
1000+
return value;
1001+
}
1002+
else if (strEQ(var, "EXTENSIONS") && (clienthello_vars->extids_len > 0)) {
1003+
value = apr_palloc(p, clienthello_vars->extids_len * 4 + 1);
1004+
for (i = 0; i < clienthello_vars->extids_len; i++) {
1005+
apr_snprintf(value + i * 4, 5, "%04x", (uint16_t) clienthello_vars->extids_data[i]);
1006+
}
1007+
return value;
1008+
}
1009+
else if (strEQ(var, "GROUPS") && (clienthello_vars->ecgroups_len > 2)) {
1010+
value = apr_palloc(p, clienthello_vars->ecgroups_len * 2 + 1 - 2);
1011+
ap_bin2hex(clienthello_vars->ecgroups_data + 2, clienthello_vars->ecgroups_len - 2, value);
1012+
return value;
1013+
}
1014+
else if (strEQ(var, "EC_FORMATS") && (clienthello_vars->ecformats_len > 1)) {
1015+
value = apr_palloc(p, clienthello_vars->ecformats_len * 2 + 1 - 1);
1016+
ap_bin2hex(clienthello_vars->ecformats_data + 1, clienthello_vars->ecformats_len - 1, value);
1017+
return value;
1018+
}
1019+
else if (strEQ(var, "SIG_ALGOS") && (clienthello_vars->sigalgos_len > 2)) {
1020+
value = apr_palloc(p, clienthello_vars->sigalgos_len * 2 + 1 - 2);
1021+
ap_bin2hex(clienthello_vars->sigalgos_data + 2, clienthello_vars->sigalgos_len - 2, value);
1022+
return value;
1023+
}
1024+
else if (strEQ(var, "ALPN") && (clienthello_vars->alpn_len > 2)) {
1025+
value = apr_palloc(p, clienthello_vars->alpn_len * 2 + 1 - 2);
1026+
ap_bin2hex(clienthello_vars->alpn_data + 2, clienthello_vars->alpn_len - 2, value);
1027+
return value;
1028+
}
1029+
else if (strEQ(var, "VERSIONS") && (clienthello_vars->versions_len > 1)) {
1030+
value = apr_palloc(p, clienthello_vars->versions_len * 2 + 1 - 1);
1031+
ap_bin2hex(clienthello_vars->versions_data + 1, clienthello_vars->versions_len - 1, value);
1032+
return value;
1033+
}
1034+
#endif
1035+
return NULL;
1036+
}
1037+
9781038
static const char *ssl_var_lookup_ssl_version(const char *var)
9791039
{
9801040
if (strEQ(var, "INTERFACE")) {

modules/ssl/ssl_private.h

+30-1
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,30 @@ typedef enum {
574574
SSL_SHUTDOWN_TYPE_ACCURATE
575575
} ssl_shutdown_type_e;
576576

577+
/**
578+
* Define the structure to hold clienthello variables
579+
* (later exposed as environment vars)
580+
*/
581+
#if OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined(LIBRESSL_VERSION_NUMBER)
582+
typedef struct {
583+
unsigned int version;
584+
apr_size_t ciphers_len;
585+
const unsigned char *ciphers_data;
586+
apr_size_t extids_len;
587+
const int *extids_data;
588+
apr_size_t ecgroups_len;
589+
const unsigned char *ecgroups_data;
590+
apr_size_t ecformats_len;
591+
const unsigned char *ecformats_data;
592+
apr_size_t sigalgos_len;
593+
const unsigned char *sigalgos_data;
594+
apr_size_t alpn_len;
595+
const unsigned char *alpn_data;
596+
apr_size_t versions_len;
597+
const unsigned char *versions_data;
598+
} modssl_clienthello_vars;
599+
#endif
600+
577601
typedef struct {
578602
SSL *ssl;
579603
const char *client_dn;
@@ -604,6 +628,10 @@ typedef struct {
604628
const char *cipher_suite; /* cipher suite used in last reneg */
605629
int service_unavailable; /* thouugh we negotiate SSL, no requests will be served */
606630
int vhost_found; /* whether we found vhost from SNI already */
631+
632+
#if OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined(LIBRESSL_VERSION_NUMBER)
633+
modssl_clienthello_vars *clienthello_vars; /* info from clienthello callback */
634+
#endif
607635
} SSLConnRec;
608636

609637
/* Private keys are retained across reloads, since decryption
@@ -833,7 +861,7 @@ struct SSLSrvConfigRec {
833861
BOOL compression;
834862
#endif
835863
BOOL session_tickets;
836-
864+
BOOL clienthello_vars;
837865
};
838866

839867
/**
@@ -893,6 +921,7 @@ const char *ssl_cmd_SSLCARevocationPath(cmd_parms *, void *, const char *);
893921
const char *ssl_cmd_SSLCARevocationFile(cmd_parms *, void *, const char *);
894922
const char *ssl_cmd_SSLCARevocationCheck(cmd_parms *, void *, const char *);
895923
const char *ssl_cmd_SSLHonorCipherOrder(cmd_parms *cmd, void *dcfg, int flag);
924+
const char *ssl_cmd_SSLClientHelloVars(cmd_parms *, void *, int flag);
896925
const char *ssl_cmd_SSLCompression(cmd_parms *, void *, int flag);
897926
const char *ssl_cmd_SSLSessionTickets(cmd_parms *, void *, int flag);
898927
const char *ssl_cmd_SSLVerifyClient(cmd_parms *, void *, const char *);

0 commit comments

Comments
 (0)