diff --git a/doc/configuration.txt b/doc/configuration.txt index 780d27fd9..6612d3d43 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -17077,6 +17077,74 @@ ssl_fc_use_keysize : integer Returns the symmetric cipher key size used in bits when the incoming connection was made over an SSL/TLS transport layer. +ssl_s_der : binary + Returns the DER formatted certificate presented by the server when the + outgoing connection was made over an SSL/TLS transport layer. When used for + an ACL, the value(s) to match against can be passed in hexadecimal form. + +ssl_s_key_alg : string + Returns the name of the algorithm used to generate the key of the certificate + presented by the server when the outgoing connection was made over an + SSL/TLS transport layer. + +ssl_s_notafter : string + Returns the end date presented by the server as a formatted string + YYMMDDhhmmss[Z] when the outgoing connection was made over an SSL/TLS + transport layer. + +ssl_s_notbefore : string + Returns the start date presented by the server as a formatted string + YYMMDDhhmmss[Z] when the outgoing connection was made over an SSL/TLS + transport layer. + +ssl_s_i_dn([[,[,]]]) : string + When the outgoing connection was made over an SSL/TLS transport layer, + returns the full distinguished name of the issuer of the certificate + presented by the server when no is specified, or the value of the + first given entry found from the beginning of the DN. If a positive/negative + occurrence number is specified as the optional second argument, it returns + the value of the nth given entry value from the beginning/end of the DN. + For instance, "ssl_f_i_dn(OU,2)" the second organization unit, and + "ssl_f_i_dn(CN)" retrieves the common name. + The parameter allows you to receive the DN suitable for + consumption by different protocols. Currently supported is rfc2253 for + LDAP v3. + If you'd like to modify the format only you can specify an empty string + and zero for the first two parameters. Example: ssl_s_i_dn(,0,rfc2253) + +ssl_s_s_dn([[,[,]]]) : string + When the outgoing connection was made over an SSL/TLS transport layer, + returns the full distinguished name of the subject of the certificate + presented by the server when no is specified, or the value of the + first given entry found from the beginning of the DN. If a positive/negative + occurrence number is specified as the optional second argument, it returns + the value of the nth given entry value from the beginning/end of the DN. + For instance, "ssl_f_s_dn(OU,2)" the second organization unit, and + "ssl_f_s_dn(CN)" retrieves the common name. + The parameter allows you to receive the DN suitable for + consumption by different protocols. Currently supported is rfc2253 for + LDAP v3. + If you'd like to modify the format only you can specify an empty string + and zero for the first two parameters. Example: ssl_s_s_dn(,0,rfc2253) + +ssl_s_serial : binary + Returns the serial of the certificate presented by the server when the + outgoing connection was made over an SSL/TLS transport layer. When used for + an ACL, the value(s) to match against can be passed in hexadecimal form. + +ssl_s_sha1 : binary + Returns the SHA-1 fingerprint of the certificate presented by the server + when the outgoing connection was made over an SSL/TLS transport layer. This + can be used to know which certificate was chosen using SNI. + +ssl_s_sig_alg : string + Returns the name of the algorithm used to sign the certificate presented by + the server when the outgoing connection was made over an SSL/TLS transport + layer. + +ssl_s_version : integer + Returns the version of the certificate presented by the server when the + outgoing connection was made over an SSL/TLS transport layer. 7.3.5. Fetching samples from buffer contents (Layer 6) ------------------------------------------------------ diff --git a/reg-tests/ssl/ssl_server_samples.vtc b/reg-tests/ssl/ssl_server_samples.vtc new file mode 100644 index 000000000..0ee2998b6 --- /dev/null +++ b/reg-tests/ssl/ssl_server_samples.vtc @@ -0,0 +1,73 @@ +#REGTEST_TYPE=devel + +varnishtest "Test the ssl_s_* sample fetches" +#REQUIRE_VERSION=2.2 +#REQUIRE_OPTIONS=OPENSSL +feature ignore_unknown_macro + +server s1 -repeat 3 { + rxreq + txresp +} -start + +haproxy h1 -conf { + global + tune.ssl.default-dh-param 2048 + tune.ssl.capture-cipherlist-size 1 + crt-base ${testdir} + stats socket "${tmpdir}/h1/stats" level admin + + defaults + mode http + option httplog + ${no-htx} option http-use-htx + log stderr local0 debug err + option logasap + timeout connect 1s + timeout client 1s + timeout server 1s + + + listen clear-lst + bind "fd@${clearlst}" + balance roundrobin + http-response add-header x-ssl-sha1 %[ssl_s_sha1,hex] + http-response add-header x-ssl-notafter %[ssl_s_notafter] + http-response add-header x-ssl-notbefore %[ssl_s_notbefore] + http-response add-header x-ssl-sig_alg %[ssl_s_sig_alg] + http-response add-header x-ssl-i_dn %[ssl_s_i_dn] + http-response add-header x-ssl-s_dn %[ssl_s_s_dn] + http-response add-header x-ssl-s_serial %[ssl_s_serial,hex] + http-response add-header x-ssl-key_alg %[ssl_s_key_alg] + http-response add-header x-ssl-der %[ssl_s_der,hex] + http-response add-header x-ssl-version %[ssl_s_version] + + server s1 "${tmpdir}/ssl.sock" ssl verify none sni str(www.test1.com) + + listen ssl-lst + mode http + ${no-htx} option http-use-htx + + bind "${tmpdir}/ssl.sock" ssl strict-sni crt-list ${testdir}/localhost.crt-list + + server s1 ${s1_addr}:${s1_port} +} -start + + +client c1 -connect ${h1_clearlst_sock} { + txreq + rxresp + expect resp.status == 200 + expect resp.http.x-ssl-sha1 == "2195C9F0FD58470313013FC27C1B9CF9864BD1C6" + expect resp.http.x-ssl-notafter == "180116230238Z" + expect resp.http.x-ssl-notbefore == "160117230238Z" + expect resp.http.x-ssl-sig_alg == "RSA-SHA256" + expect resp.http.x-ssl-i_dn == "/C=FR/ST=Ile-de-France/L=Paris/O=ozon.io/CN=Ozon Test CA/emailAddress=support@ozon.io" + expect resp.http.x-ssl-s_dn == "/C=FR/ST=Ile-de-France/L=Neuilly-sur-Seine/O=TOAD Consulting/OU=eParapher Team/CN=www.test1.com/emailAddress=arnault.michel@toad-consulting.fr" + expect resp.http.x-ssl-s_serial == "02" + expect resp.http.x-ssl-key_alg == "rsaEncryption" + expect resp.http.x-ssl-version == "3" + expect resp.http.x-ssl-der ~ 3082067930820461A0030201020201.* +} -run + + diff --git a/src/ssl_sample.c b/src/ssl_sample.c index cc6f53728..4c7ecccdf 100644 --- a/src/ssl_sample.c +++ b/src/ssl_sample.c @@ -91,14 +91,20 @@ smp_fetch_ssl_fc_has_crt(const struct arg *args, struct sample *smp, const char static int smp_fetch_ssl_x_der(const struct arg *args, struct sample *smp, const char *kw, void *private) { - int cert_peer = (kw[4] == 'c') ? 1 : 0; + int cert_peer = (kw[4] == 'c' || kw[4] == 's') ? 1 : 0; + int conn_server = (kw[4] == 's') ? 1 : 0; + X509 *crt = NULL; int ret = 0; struct buffer *smp_trash; struct connection *conn; SSL *ssl; - conn = objt_conn(smp->sess->origin); + if (conn_server) + conn = cs_conn(objt_cs(smp->strm->si[1].end)); + else + conn = objt_conn(smp->sess->origin); + ssl = ssl_sock_get_ssl_object(conn); if (!ssl) return 0; @@ -137,14 +143,18 @@ smp_fetch_ssl_x_der(const struct arg *args, struct sample *smp, const char *kw, static int smp_fetch_ssl_x_serial(const struct arg *args, struct sample *smp, const char *kw, void *private) { - int cert_peer = (kw[4] == 'c') ? 1 : 0; + int cert_peer = (kw[4] == 'c' || kw[4] == 's') ? 1 : 0; + int conn_server = (kw[4] == 's') ? 1 : 0; X509 *crt = NULL; int ret = 0; struct buffer *smp_trash; struct connection *conn; SSL *ssl; - conn = objt_conn(smp->sess->origin); + if (conn_server) + conn = cs_conn(objt_cs(smp->strm->si[1].end)); + else + conn = objt_conn(smp->sess->origin); ssl = ssl_sock_get_ssl_object(conn); if (!ssl) return 0; @@ -183,7 +193,8 @@ smp_fetch_ssl_x_serial(const struct arg *args, struct sample *smp, const char *k static int smp_fetch_ssl_x_sha1(const struct arg *args, struct sample *smp, const char *kw, void *private) { - int cert_peer = (kw[4] == 'c') ? 1 : 0; + int cert_peer = (kw[4] == 'c' || kw[4] == 's') ? 1 : 0; + int conn_server = (kw[4] == 's') ? 1 : 0; X509 *crt = NULL; const EVP_MD *digest; int ret = 0; @@ -192,7 +203,11 @@ smp_fetch_ssl_x_sha1(const struct arg *args, struct sample *smp, const char *kw, struct connection *conn; SSL *ssl; - conn = objt_conn(smp->sess->origin); + if (conn_server) + conn = cs_conn(objt_cs(smp->strm->si[1].end)); + else + conn = objt_conn(smp->sess->origin); + ssl = ssl_sock_get_ssl_object(conn); if (!ssl) return 0; @@ -230,14 +245,19 @@ smp_fetch_ssl_x_sha1(const struct arg *args, struct sample *smp, const char *kw, static int smp_fetch_ssl_x_notafter(const struct arg *args, struct sample *smp, const char *kw, void *private) { - int cert_peer = (kw[4] == 'c') ? 1 : 0; + int cert_peer = (kw[4] == 'c' || kw[4] == 's') ? 1 : 0; + int conn_server = (kw[4] == 's') ? 1 : 0; X509 *crt = NULL; int ret = 0; struct buffer *smp_trash; struct connection *conn; SSL *ssl; - conn = objt_conn(smp->sess->origin); + if (conn_server) + conn = cs_conn(objt_cs(smp->strm->si[1].end)); + else + conn = objt_conn(smp->sess->origin); + ssl = ssl_sock_get_ssl_object(conn); if (!ssl) return 0; @@ -275,7 +295,8 @@ smp_fetch_ssl_x_notafter(const struct arg *args, struct sample *smp, const char static int smp_fetch_ssl_x_i_dn(const struct arg *args, struct sample *smp, const char *kw, void *private) { - int cert_peer = (kw[4] == 'c') ? 1 : 0; + int cert_peer = (kw[4] == 'c' || kw[4] == 's') ? 1 : 0; + int conn_server = (kw[4] == 's') ? 1 : 0; X509 *crt = NULL; X509_NAME *name; int ret = 0; @@ -283,7 +304,11 @@ smp_fetch_ssl_x_i_dn(const struct arg *args, struct sample *smp, const char *kw, struct connection *conn; SSL *ssl; - conn = objt_conn(smp->sess->origin); + if (conn_server) + conn = cs_conn(objt_cs(smp->strm->si[1].end)); + else + conn = objt_conn(smp->sess->origin); + ssl = ssl_sock_get_ssl_object(conn); if (!ssl) return 0; @@ -338,14 +363,19 @@ smp_fetch_ssl_x_i_dn(const struct arg *args, struct sample *smp, const char *kw, static int smp_fetch_ssl_x_notbefore(const struct arg *args, struct sample *smp, const char *kw, void *private) { - int cert_peer = (kw[4] == 'c') ? 1 : 0; + int cert_peer = (kw[4] == 'c' || kw[4] == 's') ? 1 : 0; + int conn_server = (kw[4] == 's') ? 1 : 0; X509 *crt = NULL; int ret = 0; struct buffer *smp_trash; struct connection *conn; SSL *ssl; - conn = objt_conn(smp->sess->origin); + if (conn_server) + conn = cs_conn(objt_cs(smp->strm->si[1].end)); + else + conn = objt_conn(smp->sess->origin); + ssl = ssl_sock_get_ssl_object(conn); if (!ssl) return 0; @@ -383,7 +413,8 @@ smp_fetch_ssl_x_notbefore(const struct arg *args, struct sample *smp, const char static int smp_fetch_ssl_x_s_dn(const struct arg *args, struct sample *smp, const char *kw, void *private) { - int cert_peer = (kw[4] == 'c') ? 1 : 0; + int cert_peer = (kw[4] == 'c' || kw[4] == 's') ? 1 : 0; + int conn_server = (kw[4] == 's') ? 1 : 0; X509 *crt = NULL; X509_NAME *name; int ret = 0; @@ -391,7 +422,11 @@ smp_fetch_ssl_x_s_dn(const struct arg *args, struct sample *smp, const char *kw, struct connection *conn; SSL *ssl; - conn = objt_conn(smp->sess->origin); + if (conn_server) + conn = cs_conn(objt_cs(smp->strm->si[1].end)); + else + conn = objt_conn(smp->sess->origin); + ssl = ssl_sock_get_ssl_object(conn); if (!ssl) return 0; @@ -475,12 +510,17 @@ smp_fetch_ssl_c_used(const struct arg *args, struct sample *smp, const char *kw, static int smp_fetch_ssl_x_version(const struct arg *args, struct sample *smp, const char *kw, void *private) { - int cert_peer = (kw[4] == 'c') ? 1 : 0; + int cert_peer = (kw[4] == 'c' || kw[4] == 's') ? 1 : 0; + int conn_server = (kw[4] == 's') ? 1 : 0; + X509 *crt; struct connection *conn; SSL *ssl; - conn = objt_conn(smp->sess->origin); + if (conn_server) + conn = cs_conn(objt_cs(smp->strm->si[1].end)); + else + conn = objt_conn(smp->sess->origin); ssl = ssl_sock_get_ssl_object(conn); if (!ssl) return 0; @@ -513,14 +553,19 @@ smp_fetch_ssl_x_version(const struct arg *args, struct sample *smp, const char * static int smp_fetch_ssl_x_sig_alg(const struct arg *args, struct sample *smp, const char *kw, void *private) { - int cert_peer = (kw[4] == 'c') ? 1 : 0; + int cert_peer = (kw[4] == 'c' || kw[4] == 's') ? 1 : 0; + int conn_server = (kw[4] == 's') ? 1 : 0; X509 *crt; __OPENSSL_110_CONST__ ASN1_OBJECT *algorithm; int nid; struct connection *conn; SSL *ssl; - conn = objt_conn(smp->sess->origin); + if (conn_server) + conn = cs_conn(objt_cs(smp->strm->si[1].end)); + else + conn = objt_conn(smp->sess->origin); + ssl = ssl_sock_get_ssl_object(conn); if (!ssl) return 0; @@ -565,14 +610,18 @@ smp_fetch_ssl_x_sig_alg(const struct arg *args, struct sample *smp, const char * static int smp_fetch_ssl_x_key_alg(const struct arg *args, struct sample *smp, const char *kw, void *private) { - int cert_peer = (kw[4] == 'c') ? 1 : 0; + int cert_peer = (kw[4] == 'c' || kw[4] == 's') ? 1 : 0; + int conn_server = (kw[4] == 's') ? 1 : 0; X509 *crt; ASN1_OBJECT *algorithm; int nid; struct connection *conn; SSL *ssl; - conn = objt_conn(smp->sess->origin); + if (conn_server) + conn = cs_conn(objt_cs(smp->strm->si[1].end)); + else + conn = objt_conn(smp->sess->origin); ssl = ssl_sock_get_ssl_object(conn); if (!ssl) return 0; @@ -1337,6 +1386,18 @@ static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, { { "ssl_fc_cipherlist_hex", smp_fetch_ssl_fc_cl_hex, 0, NULL, SMP_T_BIN, SMP_USE_L5CLI }, { "ssl_fc_cipherlist_str", smp_fetch_ssl_fc_cl_str, 0, NULL, SMP_T_STR, SMP_USE_L5CLI }, { "ssl_fc_cipherlist_xxh", smp_fetch_ssl_fc_cl_xxh64, 0, NULL, SMP_T_SINT, SMP_USE_L5CLI }, + +/* SSL server certificate fetches */ + { "ssl_s_der", smp_fetch_ssl_x_der, 0, NULL, SMP_T_BIN, SMP_USE_L5CLI }, + { "ssl_s_key_alg", smp_fetch_ssl_x_key_alg, 0, NULL, SMP_T_STR, SMP_USE_L5CLI }, + { "ssl_s_notafter", smp_fetch_ssl_x_notafter, 0, NULL, SMP_T_STR, SMP_USE_L5CLI }, + { "ssl_s_notbefore", smp_fetch_ssl_x_notbefore, 0, NULL, SMP_T_STR, SMP_USE_L5CLI }, + { "ssl_s_sig_alg", smp_fetch_ssl_x_sig_alg, 0, NULL, SMP_T_STR, SMP_USE_L5CLI }, + { "ssl_s_s_dn", smp_fetch_ssl_x_s_dn, ARG3(0,STR,SINT,STR),val_dnfmt, SMP_T_STR, SMP_USE_L5CLI }, + { "ssl_s_i_dn", smp_fetch_ssl_x_i_dn, ARG3(0,STR,SINT,STR),val_dnfmt, SMP_T_STR, SMP_USE_L5CLI }, + { "ssl_s_serial", smp_fetch_ssl_x_serial, 0, NULL, SMP_T_BIN, SMP_USE_L5CLI }, + { "ssl_s_sha1", smp_fetch_ssl_x_sha1, 0, NULL, SMP_T_BIN, SMP_USE_L5CLI }, + { "ssl_s_version", smp_fetch_ssl_x_version, 0, NULL, SMP_T_SINT, SMP_USE_L5CLI }, { NULL, NULL, 0, 0, 0 }, }};