Last active
December 18, 2022 06:11
-
-
Save shadyabhi/4014fc6e7acd1afc3e15c823d2565d49 to your computer and use it in GitHub Desktop.
MINOR: ssl: add new sample ssl_c_r_dn
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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