From: Willy Tarreau Date: Mon, 10 Sep 2012 06:20:03 +0000 (+0200) Subject: MEDIUM: ssl: add sample fetches for is_ssl, ssl_has_sni, ssl_sni_* X-Git-Tag: v1.5-dev12~5 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7875d0967ff030410588a680ffb5dfd92ca920ef;p=thirdparty%2Fhaproxy.git MEDIUM: ssl: add sample fetches for is_ssl, ssl_has_sni, ssl_sni_* This allows SNI presence and value to be checked on incoming SSL connections. It is usable both for ACLs and stick tables. --- diff --git a/doc/configuration.txt b/doc/configuration.txt index 5501444668..33cb530320 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -7901,11 +7901,18 @@ during analysis. This requires that some data has been buffered, for instance through TCP request content inspection. Please see the "tcp-request content" keyword for more detailed information on the subject. +is_ssl + Returns true when the incoming connection was made via an SSL/TLS data layer + and is locally deciphered. This means it has matched a socket declared with + a "bind" line having the "ssl" option. + rep_ssl_hello_type Returns true when data in the response buffer looks like a complete SSL (v3 or superior) hello message and handshake type is equal to . This test was designed to be used with TCP response content inspection: a - SSL session ID may be fetched. + SSL session ID may be fetched. 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_len Returns true when the length of the data in the request buffer matches the @@ -7946,7 +7953,9 @@ req_ssl_hello_type Returns true when data in the request buffer looks like a complete SSL (v3 or superior) hello message and handshake type is equal to . This test was designed to be used with TCP request content inspection: an - SSL session ID may be fetched. + SSL session ID may be fetched. 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_sni Returns true when data in the request buffer looks like a complete SSL (v3 @@ -7956,7 +7965,10 @@ req_ssl_sni or denying access to certain hosts when SSL/TLS is used by the client. This test was designed to be used with TCP request content inspection. If content switching is needed, it is recommended to first wait for a complete client - hello (type 1), like in the example below. + hello (type 1), like in the example below. 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. See also "ssl_sni" below. Examples : # Wait for a client hello for at most 5 seconds @@ -7972,7 +7984,53 @@ req_ssl_ver easily fooled. In particular, it waits for as many bytes as announced in the message header if this header looks valid (bound to the buffer size). Note that TLSv1 is announced as SSL version 3.1. This test was designed to be used - with TCP request content inspection. + with TCP request content inspection. 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. + +ssl_sni + Returns true when the incoming connection was made over an SSL/TLS data layer + which deciphered it and found a Server Name Indication TLS extension sent by + the client, matching the specified string. In HTTPS, the SNI field (when + present) is equal to the requested host name. This match is different from + req_ssl_sni above in that it applies to the connection being deciphered by + haproxy and not to SSL contents being blindly forwarded. This requires that + the SSL library is build with support for TLS extensions (check haproxy -vv). + +ssl_has_sni + This is used to check for presence of a Server Name Indication TLS extension + in an incoming connection was made over an SSL/TLS data layer. Returns true + when the incoming connection presents a TLS SNI field. This requires that + the SSL library is build with support for TLS extensions (check haproxy -vv). + +ssl_sni + Returns true when the incoming connection was made over an SSL/TLS data layer + which deciphered it and found a Server Name Indication TLS extension sent by + the client, matching the specified string. In HTTPS, the SNI field (when + present) is equal to the requested host name. This match is different from + req_ssl_sni above in that it applies to the connection being deciphered by + haproxy and not to SSL contents being blindly forwarded. See also ssl_sni_end + and ssl_sni_req below. This requires that the SSL library is build with + support for TLS extensions (check haproxy -vv). + +ssl_sni_end + Returns true when the incoming connection was made over an SSL/TLS data layer + which deciphered it and found a Server Name Indication TLS extension sent by + the client, ending like the specified string. In HTTPS, the SNI field (when + present) is equal to the requested host name. This match is different from + req_ssl_sni above in that it applies to the connection being deciphered by + haproxy and not to SSL contents being blindly forwarded. This requires that + the SSL library is build with support for TLS extensions (check haproxy -vv). + +ssl_sni_req + Returns true when the incoming connection was made over an SSL/TLS data layer + which deciphered it and found a Server Name Indication TLS extension sent by + the client, matching the specified regex. In HTTPS, the SNI field (when + present) is equal to the requested host name. This match is different from + req_ssl_sni above in that it applies to the connection being deciphered by + haproxy and not to SSL contents being blindly forwarded. This requires that + the SSL library is build with support for TLS extensions (check haproxy -vv). wait_end Waits for the end of the analysis period to return true. This may be used in @@ -8542,6 +8600,10 @@ The list of currently supported pattern fetch functions is the following : last one. A typical use is with the X-Forwarded-For header once converted to IP, associated with an IP stick-table. + is_ssl This checks the data layer used by incoming connection, and + returns 1 if the connection was made via an SSL/TLS data layer, + otherwise zero. + path This extracts the request's URL path (without the host part). A typical use is with prefetch-capable caches, and with portals which need to aggregate multiple information from databases and @@ -8569,6 +8631,18 @@ The list of currently supported pattern fetch functions is the following : that this function will be useful but it's available at no cost. It is of type integer and only works with such tables. + ssl_has_sni This checks the data layer used by incoming connection, and + returns 1 if the connection was made via an SSL/TLS data layer + and the client sent a Server Name Indication TLS extension, + otherwise zero. This requires that the SSL library is build with + support for TLS extensions (check haproxy -vv). + + ssl_sni This extracts the Server Name Indication field from an incoming + connection made via an SSL/TLS data layer and locally deciphered + by haproxy. The result typically is a string matching the HTTPS + host name (253 chars or less). The SSL library must have been + built with support for TLS extensions (check haproxy -vv). + url This extracts the request's URL as presented in the request. A typical use is with prefetch-capable caches, and with portals which need to aggregate multiple information from databases and diff --git a/src/ssl_sock.c b/src/ssl_sock.c index 531d56b499..8410ab0eef 100644 --- a/src/ssl_sock.c +++ b/src/ssl_sock.c @@ -45,6 +45,8 @@ #include #include +#include +#include #include #include #include @@ -748,6 +750,75 @@ static void ssl_sock_shutw(struct connection *conn, int clean) SSL_set_shutdown(conn->data_ctx, SSL_SENT_SHUTDOWN); } +/***** Below are some sample fetching functions for ACL/patterns *****/ + +/* boolean, returns true if data layer is SSL */ +static int +smp_fetch_is_ssl(struct proxy *px, struct session *l4, void *l7, unsigned int opt, + const struct arg *args, struct sample *smp) +{ + smp->type = SMP_T_BOOL; + smp->data.uint = (l4->si[0].conn.data == &ssl_sock); + return 1; +} + +/* boolean, returns true if data layer is SSL */ +static int +smp_fetch_has_sni(struct proxy *px, struct session *l4, void *l7, unsigned int opt, + const struct arg *args, struct sample *smp) +{ +#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME + smp->type = SMP_T_BOOL; + smp->data.uint = (l4->si[0].conn.data == &ssl_sock) && + SSL_get_servername(l4->si[0].conn.data_ctx, TLSEXT_NAMETYPE_host_name) != NULL; + return 1; +#else + return 0; +#endif +} + +static int +smp_fetch_ssl_sni(struct proxy *px, struct session *l4, void *l7, unsigned int opt, + const struct arg *args, struct sample *smp) +{ +#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME + smp->flags = 0; + smp->type = SMP_T_CSTR; + + if (!l4 || l4->si[0].conn.data != &ssl_sock) + return 0; + + /* data points to cookie value */ + smp->data.str.str = (char *)SSL_get_servername(l4->si[0].conn.data_ctx, TLSEXT_NAMETYPE_host_name); + smp->data.str.len = strlen(smp->data.str.str); + return 1; +#else + return 0; +#endif +} + +/* Note: must not be declared as its list will be overwritten. + * Please take care of keeping this list alphabetically sorted. + */ +static struct sample_fetch_kw_list sample_fetch_keywords = {{ },{ + { "is_ssl", smp_fetch_is_ssl, 0, NULL, SMP_T_BOOL, SMP_CAP_REQ|SMP_CAP_RES }, + { "ssl_has_sni", smp_fetch_has_sni, 0, NULL, SMP_T_BOOL, SMP_CAP_REQ|SMP_CAP_RES }, + { "ssl_sni", smp_fetch_ssl_sni, 0, NULL, SMP_T_CSTR, SMP_CAP_REQ|SMP_CAP_RES }, + { NULL, NULL, 0, 0, 0 }, +}}; + +/* Note: must not be declared as its list will be overwritten. + * Please take care of keeping this list alphabetically sorted. + */ +static struct acl_kw_list acl_kws = {{ },{ + { "is_ssl", acl_parse_int, smp_fetch_is_ssl, acl_match_nothing, ACL_USE_L6REQ_PERMANENT, 0 }, + { "ssl_has_sni", acl_parse_int, smp_fetch_has_sni, acl_match_nothing, ACL_USE_L6REQ_PERMANENT, 0 }, + { "ssl_sni", acl_parse_str, smp_fetch_ssl_sni, acl_match_str, ACL_USE_L6REQ_PERMANENT|ACL_MAY_LOOKUP, 0 }, + { "ssl_sni_end", acl_parse_str, smp_fetch_ssl_sni, acl_match_end, ACL_USE_L6REQ_PERMANENT|ACL_MAY_LOOKUP, 0 }, + { "ssl_sni_reg", acl_parse_str, smp_fetch_ssl_sni, acl_match_reg, ACL_USE_L6REQ_PERMANENT|ACL_MAY_LOOKUP, 0 }, + { NULL, NULL, NULL, NULL }, +}}; + /* data-layer operations for SSL sockets */ struct data_ops ssl_sock = { @@ -768,6 +839,8 @@ static void __ssl_sock_init(void) { SSL_library_init(); cm = SSL_COMP_get_compression_methods(); sk_SSL_COMP_zero(cm); + sample_register_fetches(&sample_fetch_keywords); + acl_register_keywords(&acl_kws); } /*