MINOR: Add sample fetch to detect Supported Elliptic Curves Extension

Clients that support ECC cipher suites SHOULD send the specified extension
within the SSL ClientHello message according to RFC4492, section 5.1. We
can use this extension to chain-proxy requests so that, on the same IP
address, a ECC compatible clients gets an EC certificate and a non-ECC
compatible client gets a regular RSA certificate. The main advantage of this
approach compared to the one presented by Dave Zhu on the mailing list
is that we can make it work with OpenSSL versions before 1.0.2.

Example:
frontend ssl-relay
        mode tcp
        bind 0.0.0.0:443
        use_backend ssl-ecc if { req.ssl_ec_ext 1 }
        default_backend ssl-rsa

backend ssl-ecc
        mode tcp
        server ecc unix@/var/run/haproxy_ssl_ecc.sock send-proxy-v2 check

backend ssl-rsa
        mode tcp
        server rsa unix@/var/run/haproxy_ssl_rsa.sock send-proxy-v2 check

listen  all-ssl
        bind unix@/var/run/haproxy_ssl_ecc.sock accept-proxy ssl crt /usr/local/haproxy/ecc.foo.com.pem user nobody
        bind unix@/var/run/haproxy_ssl_rsa.sock accept-proxy ssl crt /usr/local/haproxy/www.foo.com.pem user nobody

Signed-off-by: Nenad Merdanovic <nmerdan@anine.io>
This commit is contained in:
Nenad Merdanovic 2015-07-07 22:00:17 +02:00 committed by Willy Tarreau
parent fc017fec48
commit 5fc7d7e8ce
2 changed files with 131 additions and 0 deletions

View File

@ -12631,6 +12631,15 @@ rdp_cookie_cnt([name]) : integer (deprecated)
ACL derivatives :
req_rdp_cookie_cnt([<name>]) : integer match
req.ssl_ec_ext : boolean
Returns a boolean identifying if client sent the Supported Elliptic Curves
Extension as defined in RFC4492, section 5.1. within the SSL ClientHello
message. This can be used to present ECC compatible clients with EC certificate
and to use RSA for all others, on the same IP address. Note that this only
applies to raw contents found in the request buffer and not to contents
deciphered via an SSL data layer, so this will not work with "bind" lines
having the "ssl" option.
req.ssl_hello_type : integer
req_ssl_hello_type : integer (deprecated)
Returns an integer value containing the type of the SSL hello message found

View File

@ -56,6 +56,127 @@ smp_fetch_len(const struct arg *args, struct sample *smp, const char *kw, void *
return 1;
}
/* Returns TRUE if the client sent Supported Elliptic Curves Extension (0x000a)
* Mainly used to detect if client supports ECC cipher suites.
*/
static int
smp_fetch_req_ssl_ec_ext(const struct arg *args, struct sample *smp, const char *kw, void *private)
{
int hs_len, ext_len, bleft;
struct channel *chn;
unsigned char *data;
chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
if (!chn->buf)
goto not_ssl_hello;
bleft = chn->buf->i;
data = (unsigned char *)chn->buf->p;
/* Check for SSL/TLS Handshake */
if (!bleft)
goto too_short;
if (*data != 0x16)
goto not_ssl_hello;
/* Check for SSLv3 or later (SSL version >= 3.0) in the record layer*/
if (bleft < 3)
goto too_short;
if (data[1] < 0x03)
goto not_ssl_hello;
if (bleft < 5)
goto too_short;
hs_len = (data[3] << 8) + data[4];
if (hs_len < 1 + 3 + 2 + 32 + 1 + 2 + 2 + 1 + 1 + 2 + 2)
goto not_ssl_hello; /* too short to have an extension */
data += 5; /* enter TLS handshake */
bleft -= 5;
/* Check for a complete client hello starting at <data> */
if (bleft < 1)
goto too_short;
if (data[0] != 0x01) /* msg_type = Client Hello */
goto not_ssl_hello;
/* Check the Hello's length */
if (bleft < 4)
goto too_short;
hs_len = (data[1] << 16) + (data[2] << 8) + data[3];
if (hs_len < 2 + 32 + 1 + 2 + 2 + 1 + 1 + 2 + 2)
goto not_ssl_hello; /* too short to have an extension */
/* We want the full handshake here */
if (bleft < hs_len)
goto too_short;
data += 4;
/* Start of the ClientHello message */
if (data[0] < 0x03 || data[1] < 0x01) /* TLSv1 minimum */
goto not_ssl_hello;
ext_len = data[34]; /* session_id_len */
if (ext_len > 32 || ext_len > (hs_len - 35)) /* check for correct session_id len */
goto not_ssl_hello;
/* Jump to cipher suite */
hs_len -= 35 + ext_len;
data += 35 + ext_len;
if (hs_len < 4 || /* minimum one cipher */
(ext_len = (data[0] << 8) + data[1]) < 2 || /* minimum 2 bytes for a cipher */
ext_len > hs_len)
goto not_ssl_hello;
/* Jump to the compression methods */
hs_len -= 2 + ext_len;
data += 2 + ext_len;
if (hs_len < 2 || /* minimum one compression method */
data[0] < 1 || data[0] > hs_len) /* minimum 1 bytes for a method */
goto not_ssl_hello;
/* Jump to the extensions */
hs_len -= 1 + data[0];
data += 1 + data[0];
if (hs_len < 2 || /* minimum one extension list length */
(ext_len = (data[0] << 8) + data[1]) > hs_len - 2) /* list too long */
goto not_ssl_hello;
hs_len = ext_len; /* limit ourselves to the extension length */
data += 2;
while (hs_len >= 4) {
int ext_type, ext_len;
ext_type = (data[0] << 8) + data[1];
ext_len = (data[2] << 8) + data[3];
if (ext_len > hs_len - 4) /* Extension too long */
goto not_ssl_hello;
/* Elliptic curves extension */
if (ext_type == 10) {
smp->type = SMP_T_BOOL;
smp->data.uint = 1;
return 1;
}
hs_len -= 4 + ext_len;
data += 4 + ext_len;
}
/* server name not found */
goto not_ssl_hello;
too_short:
smp->flags = SMP_F_MAY_CHANGE;
not_ssl_hello:
return 0;
}
/* returns the type of SSL hello message (mainly used to detect an SSL hello) */
static int
smp_fetch_ssl_hello_type(const struct arg *args, struct sample *smp, const char *kw, void *private)
@ -648,6 +769,7 @@ static struct sample_fetch_kw_list smp_kws = {ILH, {
{ "req.payload_lv", smp_fetch_payload_lv, ARG3(2,UINT,UINT,SINT), val_payload_lv, SMP_T_BIN, SMP_USE_L6REQ },
{ "req.rdp_cookie", smp_fetch_rdp_cookie, ARG1(0,STR), NULL, SMP_T_STR, SMP_USE_L6REQ },
{ "req.rdp_cookie_cnt", smp_fetch_rdp_cookie_cnt, ARG1(0,STR), NULL, SMP_T_UINT, SMP_USE_L6REQ },
{ "req.ssl_ec_ext", smp_fetch_req_ssl_ec_ext, 0, NULL, SMP_T_BOOL, SMP_USE_L6REQ },
{ "req.ssl_hello_type", smp_fetch_ssl_hello_type, 0, NULL, SMP_T_UINT, SMP_USE_L6REQ },
{ "req.ssl_sni", smp_fetch_ssl_hello_sni, 0, NULL, SMP_T_STR, SMP_USE_L6REQ },
{ "req.ssl_ver", smp_fetch_req_ssl_ver, 0, NULL, SMP_T_UINT, SMP_USE_L6REQ },