Hi,
The attached patch adds the ssl_sni_check converter which returns true
if the sample input string matches a loaded certificate's CN/SAN.
This can be useful to check for example if a host header matches a
loaded certificate CN/SAN before doing a redirect:
frontent fe_main
bind 127.0.0.1:80
bind 127.0.0.1:443 ssl crt /etc/haproxy/ssl/
http-request redirect scheme https if !{ ssl_fc } { hdr(host),ssl_sni_check()
}
This converter may be even more useful when certificates will be
added/removed at runtime.
++
--
Moemen MHEDHBI
>From 14ed628ab9badbb06c45bab324eb00f998de49af Mon Sep 17 00:00:00 2001
From: Moemen MHEDHBI <[email protected]>
Date: Sun, 23 Dec 2018 20:50:04 +0100
Subject: [PATCH] MINOR: sample: add ssl_sni_check converter
This adds the ssl_sni_check converter. The converter returns
true if the sample input string matches a loaded certificate's CN/SAN.
Lookup can be done through certificates of a specified bind line (by
<name>) otherwise the search will include all bind lines of the current
proxy.
---
doc/configuration.txt | 6 ++++++
src/ssl_sock.c | 43 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 49 insertions(+)
diff --git a/doc/configuration.txt b/doc/configuration.txt
index 6ca63d64a..0be043e73 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -13651,6 +13651,12 @@ sha1
Converts a binary input sample to a SHA1 digest. The result is a binary
sample with length of 20 bytes.
+ssl_sni_check(<name>)
+ Returns true if the sample input string matches a loaded certificate's CN/SAN.
+ Otherwise false is returned. When <name> is provided the lookup is done only
+ through the certificates of the bind line named <name>, if not all bind
+ lines of the current frontend will be searched.
+
strcmp(<var>)
Compares the contents of <var> with the input value of type string. Returns
the result as a signed integer compatible with strcmp(3): 0 if both strings
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index 282b85ddd..b24d78978 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -7276,6 +7276,41 @@ smp_fetch_ssl_c_verify(const struct arg *args, struct sample *smp, const char *k
return 1;
}
+/* boolean, returns true if input string matches a loaded certificate's CN/SAN. */
+/* The lookup is done only for the bind named <name> if the param is prvided. */
+static int smp_conv_ssl_sni_check(const struct arg *args, struct sample *smp, void *private)
+{
+ struct proxy *px = smp->px;
+ struct listener *l;
+ struct ebmb_node *node = NULL;
+ char *wildp = NULL;
+ int i;
+
+ for (i = 0; i < trash.size && i < smp->data.u.str.data; i++) {
+ trash.area[i] = tolower(smp->data.u.str.area[i]);
+ if (!wildp && (trash.area[i] == '.'))
+ wildp = &trash.area[i];
+ }
+ trash.area[i] = 0;
+
+ list_for_each_entry(l, &px->conf.listeners, by_fe) {
+ if ( args->type == ARGT_STR && l->name && (strcmp(args->data.str.area, l->name) != 0))
+ continue;
+ /* lookup in full qualified names */
+ node = ebst_lookup(&l->bind_conf->sni_ctx, trash.area);
+ /* lookup in wildcards names */
+ if (!node && wildp)
+ node = ebst_lookup(&l->bind_conf->sni_w_ctx, wildp);
+ if (node != NULL)
+ break;
+ }
+
+ smp->data.type = SMP_T_BOOL;
+ smp->data.u.sint = !!node;
+ smp->flags = SMP_F_VOL_TEST;
+ return 1;
+}
+
/* parse the "ca-file" bind keyword */
static int ssl_bind_parse_ca_file(char **args, int cur_arg, struct proxy *px, struct ssl_bind_conf *conf, char **err)
{
@@ -9047,6 +9082,14 @@ static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, {
INITCALL1(STG_REGISTER, sample_register_fetches, &sample_fetch_keywords);
+/* Note: must not be declared <const> as its list will be overwritten */
+static struct sample_conv_kw_list sample_conv_kws = {ILH, {
+ { "ssl_sni_check", smp_conv_ssl_sni_check, ARG1(0,STR), NULL, SMP_T_STR, SMP_T_BOOL },
+ { /* END */ },
+}};
+
+INITCALL1(STG_REGISTER, sample_register_convs, &sample_conv_kws);
+
/* Note: must not be declared <const> as its list will be overwritten.
* Please take care of keeping this list alphabetically sorted.
*/
--
2.19.2