]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: ssl: add the ssl_fc_crtname sample fetch
authorWilliam Lallemand <wlallemand@irq6.net>
Thu, 2 Apr 2026 21:34:50 +0000 (23:34 +0200)
committerWilliam Lallemand <wlallemand@haproxy.com>
Fri, 3 Apr 2026 08:41:00 +0000 (10:41 +0200)
This new sample fetch returns the name of the certificate selected for
an incoming SSL/TLS connection, as it would appear in "show ssl cert".
It may be a filename with its relative or absolute path, or an alias,
depending on how the certificate was declared in the configuration.

The certificate name is stored as ex_data on the SSL_CTX at load time
in ckch_inst_new_load_store(), and freed via a dedicated free callback.

doc/configuration.txt
include/haproxy/ssl_sock.h
src/ssl_sample.c
src/ssl_sock.c

index 9f4d1ef6d44220619de9e312d855d77ffd943c97..d323c33aeac93e918c152f0a4d22e13f022f747b 100644 (file)
@@ -25659,6 +25659,7 @@ ssl_fc_client_early_traffic_secret                 string
 ssl_fc_client_handshake_traffic_secret             string
 ssl_fc_client_random                               binary
 ssl_fc_client_traffic_secret_0                     string
+ssl_fc_crtname                                     string
 ssl_fc_curve                                       string
 ssl_fc_early_exporter_secret                       string
 ssl_fc_ecformats_bin                               binary
@@ -26283,6 +26284,21 @@ ssl_fc_client_traffic_secret_0 : string
   activated with "tune.ssl.keylog on" in the global section. See also
   "tune.ssl.keylog"
 
+ssl_fc_crtname : string
+  Returns the name of the certificate that was selected for the incoming
+  SSL/TLS connection. This is the name as it appears in "show ssl cert": it
+  may be the filename with its relative or absolute path, or an alias,
+  depending on how the certificate was declared in the configuration.
+
+  Example :
+    crt-store example
+        load crt "example.com.pem"
+
+    frontend www
+        bind *:443 ssl crt "@example/example.com.pem"
+        acl match_certificate ssl_fc_crtname -m beg -i "@example/"
+        http-request set-header X-Cert-Name %[ssl_fc_crtname] if match_certificate
+
 ssl_fc_curve : string
   Returns the name of the curve used in the key agreement when the incoming
   connection was made over an SSL/TLS transport layer. This requires
index e8edad0e9f1b42460b8d1a20fcb7fc7a3c0763b5..d6058439763ce895417febf17e61f4f0d598b93f 100644 (file)
@@ -54,6 +54,7 @@ extern struct xprt_ops ssl_sock;
 extern int ssl_capture_ptr_index;
 extern int ssl_keylog_index;
 extern int ssl_client_sni_index;
+extern int ssl_crtname_index;
 extern struct pool_head *pool_head_ssl_keylog;
 extern struct pool_head *pool_head_ssl_keylog_str;
 extern struct list openssl_providers;
index fdbbdd3e878219af5a7d4b60b35fda7be593f8ec..35850c29e45983f8d631f50cb0b52f8546af5d6d 100644 (file)
@@ -2034,6 +2034,39 @@ smp_fetch_ssl_fc_sni(const struct arg *args, struct sample *smp, const char *kw,
 #endif
 }
 
+/* ssl_fc_crtname */
+static int smp_fetch_ssl_fc_crtname(const struct arg *args, struct sample *smp, const char *kw, void *private)
+{
+       struct connection *conn;
+       SSL *ssl;
+       SSL_CTX *ctx;
+
+       smp->flags = SMP_F_VOL_SESS | SMP_F_CONST;
+       smp->data.type = SMP_T_STR;
+
+       if (obj_type(smp->sess->origin) == OBJ_TYPE_CHECK)
+               conn = (kw[4] == 'b') ? sc_conn(__objt_check(smp->sess->origin)->sc) : NULL;
+       else
+               conn = (kw[4] != 'b') ? objt_conn(smp->sess->origin) :
+                       smp->strm ? sc_conn(smp->strm->scb) : NULL;
+
+       ssl = ssl_sock_get_ssl_object(conn);
+       if (!ssl)
+               return 0;
+
+       ctx = SSL_get_SSL_CTX(ssl);
+       if (!ctx)
+               return 0;
+
+       smp->data.u.str.area = SSL_CTX_get_ex_data(ctx, ssl_crtname_index);
+       if (!smp->data.u.str.area)
+               return 0;
+       smp->data.u.str.data = strlen(smp->data.u.str.area);
+
+       return 1;
+}
+
+
 #ifdef USE_ECH
 static int
 smp_fetch_ssl_fc_ech_status(const struct arg *args, struct sample *smp,
@@ -2768,6 +2801,7 @@ static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, {
 #endif
 
        { "ssl_fc_sni",             smp_fetch_ssl_fc_sni,         0,                   NULL,    SMP_T_STR,  SMP_USE_L5CLI },
+       { "ssl_fc_crtname",         smp_fetch_ssl_fc_crtname,     0,                   NULL,    SMP_T_STR,  SMP_USE_L5CLI },
 #ifdef USE_ECH
        { "ssl_fc_ech_status",      smp_fetch_ssl_fc_ech_status,  0,                   NULL,    SMP_T_STR,  SMP_USE_L5CLI },
        { "ssl_fc_ech_outer_sni",   smp_fetch_ssl_fc_ech_outer_sni, 0,                 NULL,    SMP_T_STR,  SMP_USE_L5CLI },
index 9544deffc8b47cefb4e6e66fb39a4c3d443ecdbb..5c410f2a8da4336dbf3339d287242616e2394e6c 100644 (file)
@@ -764,6 +764,8 @@ int ssl_client_crt_ref_index = -1;
 
 /* Used to store the client's SNI in case of ClientHello callback error */
 int ssl_client_sni_index = -1;
+/* store the name of the certificate */
+int ssl_crtname_index = -1;
 
 #if (defined SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB && TLS_TICKETS_NO > 0)
 struct list tlskeys_reference = LIST_HEAD_INIT(tlskeys_reference);
@@ -3160,6 +3162,7 @@ int ckch_inst_new_load_store(const char *path, struct ckch_store *ckchs, struct
 #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
        STACK_OF(GENERAL_NAME) *names;
 #endif
+       char *crtname = NULL;
        struct ckch_data *data;
        struct ckch_inst *ckch_inst = NULL;
        int errcode = 0;
@@ -3179,6 +3182,16 @@ int ckch_inst_new_load_store(const char *path, struct ckch_store *ckchs, struct
                goto error;
        }
 
+       crtname = strdup(path);
+       if (!crtname) {
+               memprintf(err, "%sunable to allocate SSL context for cert '%s'.\n",
+                         err && *err ? *err : "", path);
+               errcode |= ERR_ALERT | ERR_FATAL;
+               goto error;
+       }
+
+       SSL_CTX_set_ex_data(ctx, ssl_crtname_index, crtname);
+
        if (global_ssl.security_level > -1)
                SSL_CTX_set_security_level(ctx, global_ssl.security_level);
 
@@ -8361,6 +8374,11 @@ static void ssl_sock_capture_free_func(void *parent, void *ptr, CRYPTO_EX_DATA *
        pool_free(pool_head_ssl_capture, ptr);
 }
 
+static void ssl_sock_free_crtname(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx, long argl, void *argp)
+{
+       free(ptr);
+}
+
 #ifdef HAVE_SSL_KEYLOG
 static void ssl_sock_keylog_free_func(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx, long argl, void *argp)
 {
@@ -8488,6 +8506,8 @@ static void __ssl_sock_init(void)
 #endif
        ssl_client_crt_ref_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, ssl_sock_clt_crt_free_func);
        ssl_client_sni_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, ssl_sock_clt_sni_free_func);
+       ssl_crtname_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, ssl_sock_free_crtname);
+
 #if defined(USE_ENGINE) && !defined(OPENSSL_NO_ENGINE)
        ENGINE_load_builtin_engines();
        hap_register_post_check(ssl_check_async_engine_count);