Skip to content

Instantly share code, notes, and snippets.

@shadyabhi
Last active December 18, 2022 06:11
Show Gist options
  • Select an option

  • Save shadyabhi/4014fc6e7acd1afc3e15c823d2565d49 to your computer and use it in GitHub Desktop.

Select an option

Save shadyabhi/4014fc6e7acd1afc3e15c823d2565d49 to your computer and use it in GitHub Desktop.
MINOR: ssl: add new sample ssl_c_r_dn
From 7b3263665f624333fc344e7153acd9937be9d740 Mon Sep 17 00:00:00 2001
From: Abhijeet Rastogi <[email protected]>
Date: Sat, 17 Dec 2022 16:10:38 -0800
Subject: [PATCH] MINOR: ssl: add new sample ssl_c_r_dn
This patch addresses #1514, adds the ability to fetch DN of the root
ca that was in the chain when client certificate was verified during SSL
handshake.
---
doc/configuration.txt | 15 ++++++++++
include/haproxy/ssl_utils.h | 1 +
src/ssl_sample.c | 57 +++++++++++++++++++++++++++++++++++++
src/ssl_utils.c | 18 ++++++++++++
4 files changed, 91 insertions(+)
diff --git a/doc/configuration.txt b/doc/configuration.txt
index 0cc2bdee3..9366334ba 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -19918,6 +19918,21 @@ ssl_c_notbefore : string
YYMMDDhhmmss[Z] when the incoming connection was made over an SSL/TLS
transport layer.
+ssl_c_r_dn([<entry>[,<occ>[,<format>]]]) : string
+ When the incoming connection was made over an SSL/TLS transport layer,
+ returns the full distinguished name of the root CA of the certificate
+ presented by the client when no <entry> 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_c_r_dn(OU,2)" the second organization unit, and
+ "ssl_c_r_dn(CN)" retrieves the common name.
+ The <format> 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_c_r_dn(,0,rfc2253)
+
ssl_c_s_dn([<entry>[,<occ>[,<format>]]]) : string
When the incoming connection was made over an SSL/TLS transport layer,
returns the full distinguished name of the subject of the certificate
diff --git a/include/haproxy/ssl_utils.h b/include/haproxy/ssl_utils.h
index b6bd9d6a4..badc52f41 100644
--- a/include/haproxy/ssl_utils.h
+++ b/include/haproxy/ssl_utils.h
@@ -39,6 +39,7 @@ int ssl_sock_get_dn_entry(X509_NAME *a, const struct buffer *entry, int pos,
int ssl_sock_get_dn_formatted(X509_NAME *a, const struct buffer *format, struct buffer *out);
int ssl_sock_get_dn_oneline(X509_NAME *a, struct buffer *out);
X509* ssl_sock_get_peer_certificate(SSL *ssl);
+X509* ssl_sock_get_verified_chain_root(SSL *ssl);
unsigned int openssl_version_parser(const char *version);
void exclude_tls_grease(char *input, int len, struct buffer *output);
int x509_v_err_str_to_int(const char *str);
diff --git a/src/ssl_sample.c b/src/ssl_sample.c
index 30a616253..4c7960295 100644
--- a/src/ssl_sample.c
+++ b/src/ssl_sample.c
@@ -527,6 +527,62 @@ smp_fetch_ssl_fc_has_crt(const struct arg *args, struct sample *smp, const char
return 1;
}
+/* string, returns a string of a formatted full dn \C=..\O=..\OU=.. \CN=.. of the
+ * client certificate's root CA.
+ */
+static int
+smp_fetch_ssl_r_dn(const struct arg *args, struct sample *smp, const char *kw, void *private)
+{
+ X509 *crt = NULL;
+ X509_NAME *name;
+ int ret = 0;
+ struct buffer *smp_trash;
+ struct connection *conn;
+ SSL *ssl;
+
+ conn = objt_conn(smp->sess->origin);
+ ssl = ssl_sock_get_ssl_object(conn);
+ if (!ssl)
+ return 0;
+
+ if (conn->flags & CO_FL_WAIT_XPRT && !conn->err_code) {
+ smp->flags |= SMP_F_MAY_CHANGE;
+ return 0;
+ }
+
+ crt = ssl_sock_get_verified_chain_root(ssl);
+ if (!crt)
+ goto out;
+
+ name = X509_get_subject_name(crt);
+ if (!name)
+ goto out;
+
+ smp_trash = get_trash_chunk();
+ if (args[0].type == ARGT_STR && args[0].data.str.data > 0) {
+ int pos = 1;
+
+ if (args[1].type == ARGT_SINT)
+ pos = args[1].data.sint;
+
+ if (ssl_sock_get_dn_entry(name, &args[0].data.str, pos, smp_trash) <= 0)
+ goto out;
+ }
+ else if (args[2].type == ARGT_STR && args[2].data.str.data > 0) {
+ if (ssl_sock_get_dn_formatted(name, &args[2].data.str, smp_trash) <= 0)
+ goto out;
+ }
+ else if (ssl_sock_get_dn_oneline(name, smp_trash) <= 0)
+ goto out;
+
+ smp->flags = SMP_F_VOL_SESS;
+ smp->data.type = SMP_T_STR;
+ smp->data.u.str = *smp_trash;
+ ret = 1;
+out:
+ return ret;
+}
+
/* binary, returns a certificate in a binary chunk (der/raw).
* The 5th keyword char is used to know if SSL_get_certificate or SSL_get_peer_certificate
* should be use.
@@ -2131,6 +2187,7 @@ static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, {
{ "ssl_c_key_alg", smp_fetch_ssl_x_key_alg, 0, NULL, SMP_T_STR, SMP_USE_L5CLI },
{ "ssl_c_notafter", smp_fetch_ssl_x_notafter, 0, NULL, SMP_T_STR, SMP_USE_L5CLI },
{ "ssl_c_notbefore", smp_fetch_ssl_x_notbefore, 0, NULL, SMP_T_STR, SMP_USE_L5CLI },
+ { "ssl_c_r_dn", smp_fetch_ssl_r_dn, ARG3(0,STR,SINT,STR),val_dnfmt, SMP_T_STR, SMP_USE_L5CLI },
{ "ssl_c_sig_alg", smp_fetch_ssl_x_sig_alg, 0, NULL, SMP_T_STR, SMP_USE_L5CLI },
{ "ssl_c_s_dn", smp_fetch_ssl_x_s_dn, ARG3(0,STR,SINT,STR),val_dnfmt, SMP_T_STR, SMP_USE_L5CLI },
{ "ssl_c_serial", smp_fetch_ssl_x_serial, 0, NULL, SMP_T_BIN, SMP_USE_L5CLI },
diff --git a/src/ssl_utils.c b/src/ssl_utils.c
index ed11e6c89..c9b627cf4 100644
--- a/src/ssl_utils.c
+++ b/src/ssl_utils.c
@@ -317,6 +317,24 @@ X509* ssl_sock_get_peer_certificate(SSL *ssl)
return cert;
}
+/*
+ * This function fetches the x509* for the root CA of client certificate.
+ * We use the SSL_get0_verified_chain and get the last certificate
+ * in the x509 stack.
+ * Returns NULL in case of failure.
+*/
+X509* ssl_sock_get_verified_chain_root(SSL *ssl)
+{
+ STACK_OF(X509) *certs = NULL;
+ X509 *crt = NULL;
+
+ certs = SSL_get0_verified_chain(ssl);
+ if (certs)
+ crt = sk_X509_value(certs, sk_X509_num(certs)-1);
+
+ return crt;
+}
+
/*
* Take an OpenSSL version in text format and return a numeric openssl version
* Return 0 if it failed to parse the version
--
2.27.0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment