Tim, Willy,
Thanks again. Update provided: - Fixes the issue Tim points out by using
strcmp. - Corrects the argument spacing - Updates to include my name in the
patch submission. Yes, you can use my name.
Thanks!
-Elliot
On Friday, January 17, 2020, 5:58:43 AM EST, Willy Tarreau <[email protected]>
wrote:
On Fri, Jan 17, 2020 at 11:45:17AM +0100, Tim Düsterhus wrote:
> I did not test, but if I am not mistaken this would allow "rfc2253XXX"
> as the format parameter, no? Meaning: It allows one to append arbitrary
> garbage to the rfc2253 value.
> Judging from `smp_check_const_bool` in sample.c a simple strcmp should
> be safe to use or you can just use the chunk_strcmp you already use in
> `ssl_sock_get_dn_formatted`, I guess. Does Willy have any strong opinion
> about this?
I noticed it as well but didn't have enough time to assign to this to
look for alternatives, and considered that it was not critical. But if
other places already work with an strcmp() that's definitely better
indeed.
Similarly, we usually prefer to avoid running strcmp() at runtime, so
sometimes what's done is that the argument keyword, once parsed, is
replaced by an integer that's cheaper to test. I didn't jump on the
code to find one example, but I wouldn't be surprized that the json
converter or anything else taking a small set of words already does
something like this. But here we're dealing with TLS stuff and usually
the call rate is very low so I considered that it was not important
enough to require another round trip.
In general, especially for first submissions, my acceptance level is
lower than what some submitters are willing to do, and I prefer not
to harrass them and take the patch once it's good enough. But if some
want to provide top-most quality patches, they're absolutely welcome
to do so!
Thanks!
Willy
From 460a76a48a5885fd0c0c2586d8c03c4329b2b163 Mon Sep 17 00:00:00 2001
From: Elliot Otchet <[email protected]>
Date: Wed, 15 Jan 2020 08:12:14 -0500
Subject: [PATCH] MINOR: ssl: Add support for returning the dn samples from
ssl_(c|f)_(i|s)_dn in LDAP v3 (RFC2253) format.
Modifies the existing sample extraction methods (smp_fetch_ssl_x_i_dn, smp_fetch_ssl_x_s_dn) to accommodate a third argument that indicates the DN should be returned in LDAP v3 format. When the third argument is present, the new function (ssl_sock_get_dn_formatted) is called with three parameters including the X509_NAME, a buffer containing the format argument, and a buffer for the output. If the supplied format matches the supported format string (currently only "rfc2253" is supported), the formatted value is extracted into the supplied output buffer using OpenSSL's X509_NAME_print_ex and BIO_s_mem. 1 is returned when a dn value is retrieved. 0 is returned when a value is not retrieved.
Argument validation is added to each of the related sample configurations to ensure the third argument passed is either blank or "rfc2253" using strcmp. An error is returned if the third argument is present with any other value.
Documentation was updated in configuration.txt and it was noted during preliminary reviews that a CLEANUP patch should follow that adjusts the documentation. Currently, this patch and the existing documentation are copied with some minor revisions for each sample configuration. It might be better to have one entry for all of the samples or entries for each that reference back to a primary entry that explains the sample in detail.
Special thanks to Chris, Willy, Tim and Aleks for the feedback.
Author: Elliot Otchet <[email protected]>
---
doc/configuration.txt | 28 ++++++++++++++++++---
src/ssl_sock.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++-----
2 files changed, 86 insertions(+), 10 deletions(-)
diff --git a/doc/configuration.txt b/doc/configuration.txt
index 9ac8985..1d9b3d1 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -15246,7 +15246,7 @@ ssl_c_err : integer
to your SSL library's documentation to find the exhaustive list of error
codes.
-ssl_c_i_dn([<entry>[,<occ>]]) : string
+ssl_c_i_dn([<entry>[,<occ>[,<format>]]]) : string
When the incoming connection was made over an SSL/TLS transport layer,
returns the full distinguished name of the issuer of the certificate
presented by the client when no <entry> is specified, or the value of the
@@ -15255,6 +15255,11 @@ ssl_c_i_dn([<entry>[,<occ>]]) : string
the value of the nth given entry value from the beginning/end of the DN.
For instance, "ssl_c_i_dn(OU,2)" the second organization unit, and
"ssl_c_i_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_i_dn(,0,rfc2253)
ssl_c_key_alg : string
Returns the name of the algorithm used to generate the key of the certificate
@@ -15271,7 +15276,7 @@ ssl_c_notbefore : string
YYMMDDhhmmss[Z] when the incoming connection was made over an SSL/TLS
transport layer.
-ssl_c_s_dn([<entry>[,<occ>]]) : string
+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
presented by the client when no <entry> is specified, or the value of the
@@ -15280,6 +15285,11 @@ ssl_c_s_dn([<entry>[,<occ>]]) : string
the value of the nth given entry value from the beginning/end of the DN.
For instance, "ssl_c_s_dn(OU,2)" the second organization unit, and
"ssl_c_s_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_s_dn(,0,rfc2253)
ssl_c_serial : binary
Returns the serial of the certificate presented by the client when the
@@ -15320,7 +15330,7 @@ ssl_f_der : binary
incoming 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_f_i_dn([<entry>[,<occ>]]) : string
+ssl_f_i_dn([<entry>[,<occ>[,<format>]]]) : string
When the incoming connection was made over an SSL/TLS transport layer,
returns the full distinguished name of the issuer of the certificate
presented by the frontend when no <entry> is specified, or the value of the
@@ -15329,6 +15339,11 @@ ssl_f_i_dn([<entry>[,<occ>]]) : string
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 <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_f_i_dn(,0,rfc2253)
ssl_f_key_alg : string
Returns the name of the algorithm used to generate the key of the certificate
@@ -15345,7 +15360,7 @@ ssl_f_notbefore : string
YYMMDDhhmmss[Z] when the incoming connection was made over an SSL/TLS
transport layer.
-ssl_f_s_dn([<entry>[,<occ>]]) : string
+ssl_f_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
presented by the frontend when no <entry> is specified, or the value of the
@@ -15354,6 +15369,11 @@ ssl_f_s_dn([<entry>[,<occ>]]) : string
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 <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_f_s_dn(,0,rfc2253)
ssl_f_serial : binary
Returns the serial of the certificate presented by the frontend when the
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index ff8af23..7f1491a 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -7144,6 +7144,39 @@ static int ssl_sock_get_san_oneline(X509 *cert, struct buffer *out)
}
#endif
+/*
+ * Extract the DN in the specified format from the X509_NAME and copy result to a chunk.
+ * Currently supports rfc2253 for returning LDAP V3 DNs.
+ * Returns 1 if dn entries exist, 0 if no dn entry was found.
+ */
+static int
+ssl_sock_get_dn_formatted(X509_NAME *a, const struct buffer *format, struct buffer *out)
+{
+ BIO *bio = NULL;
+ int ret = 0;
+ int data_len = 0;
+
+ if (chunk_strcmp(format, "rfc2253") == 0) {
+ bio = BIO_new(BIO_s_mem());
+ if (bio == NULL)
+ goto out;
+
+ if (X509_NAME_print_ex(bio, a, 0, XN_FLAG_RFC2253) < 0)
+ goto out;
+
+ if ((data_len = BIO_read(bio, out->area, out->size)) <= 0)
+ goto out;
+
+ out->data = data_len;
+
+ ret = 1;
+ }
+out:
+ if (bio)
+ BIO_free(bio);
+ return ret;
+}
+
/* Extract and format full DN from a X509_NAME and copy result into a chunk
* Returns 1 if dn entries exits, 0 if no dn entry found or -1 if output is not large enough.
*/
@@ -7623,7 +7656,7 @@ smp_fetch_ssl_x_i_dn(const struct arg *args, struct sample *smp, const char *kw,
goto out;
smp_trash = get_trash_chunk();
- if (args && args[0].type == ARGT_STR) {
+ if (args && args[0].type == ARGT_STR && args[0].data.str.data > 0) {
int pos = 1;
if (args[1].type == ARGT_SINT)
@@ -7632,6 +7665,10 @@ smp_fetch_ssl_x_i_dn(const struct arg *args, struct sample *smp, const char *kw,
if (ssl_sock_get_dn_entry(name, &args[0].data.str, pos, smp_trash) <= 0)
goto out;
}
+ else if (args && 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;
@@ -7727,7 +7764,7 @@ smp_fetch_ssl_x_s_dn(const struct arg *args, struct sample *smp, const char *kw,
goto out;
smp_trash = get_trash_chunk();
- if (args && args[0].type == ARGT_STR) {
+ if (args && args[0].type == ARGT_STR && args[0].data.str.data > 0) {
int pos = 1;
if (args[1].type == ARGT_SINT)
@@ -7736,6 +7773,10 @@ smp_fetch_ssl_x_s_dn(const struct arg *args, struct sample *smp, const char *kw,
if (ssl_sock_get_dn_entry(name, &args[0].data.str, pos, smp_trash) <= 0)
goto out;
}
+ else if (args && 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;
@@ -11103,6 +11144,21 @@ err:
}
# endif
+/* Argument validation functions */
+
+/* This function is used to validate the arguments passed to any "x_dn" ssl
+ * keywords. These keywords support specifying a third parameter that must be
+ * either empty or the value "rfc2253". Returns 0 on error, non-zero if OK.
+ */
+int val_dnfmt(struct arg *arg, char **err_msg)
+{
+ if (arg && arg[2].type == ARGT_STR && arg[2].data.str.data > 0 && (strcmp(arg[2].data.str.area, "rfc2253") != 0)) {
+ memprintf(err_msg, "only rfc2253 or a blank value are currently supported as the format argument.");
+ return 0;
+ }
+ return 1;
+}
+
/* register cli keywords */
static struct cli_kw_list cli_kws = {{ },{
#if (defined SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB && TLS_TICKETS_NO > 0)
@@ -11148,24 +11204,24 @@ static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, {
{ "ssl_c_ca_err_depth", smp_fetch_ssl_c_ca_err_depth, 0, NULL, SMP_T_SINT, SMP_USE_L5CLI },
{ "ssl_c_der", smp_fetch_ssl_x_der, 0, NULL, SMP_T_BIN, SMP_USE_L5CLI },
{ "ssl_c_err", smp_fetch_ssl_c_err, 0, NULL, SMP_T_SINT, SMP_USE_L5CLI },
- { "ssl_c_i_dn", smp_fetch_ssl_x_i_dn, ARG2(0,STR,SINT), NULL, SMP_T_STR, SMP_USE_L5CLI },
+ { "ssl_c_i_dn", smp_fetch_ssl_x_i_dn, ARG3(0,STR,SINT,STR),val_dnfmt, SMP_T_STR, SMP_USE_L5CLI },
{ "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_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, ARG2(0,STR,SINT), 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 },
{ "ssl_c_sha1", smp_fetch_ssl_x_sha1, 0, NULL, SMP_T_BIN, SMP_USE_L5CLI },
{ "ssl_c_used", smp_fetch_ssl_c_used, 0, NULL, SMP_T_BOOL, SMP_USE_L5CLI },
{ "ssl_c_verify", smp_fetch_ssl_c_verify, 0, NULL, SMP_T_SINT, SMP_USE_L5CLI },
{ "ssl_c_version", smp_fetch_ssl_x_version, 0, NULL, SMP_T_SINT, SMP_USE_L5CLI },
{ "ssl_f_der", smp_fetch_ssl_x_der, 0, NULL, SMP_T_BIN, SMP_USE_L5CLI },
- { "ssl_f_i_dn", smp_fetch_ssl_x_i_dn, ARG2(0,STR,SINT), NULL, SMP_T_STR, SMP_USE_L5CLI },
+ { "ssl_f_i_dn", smp_fetch_ssl_x_i_dn, ARG3(0,STR,SINT,STR),val_dnfmt, SMP_T_STR, SMP_USE_L5CLI },
{ "ssl_f_key_alg", smp_fetch_ssl_x_key_alg, 0, NULL, SMP_T_STR, SMP_USE_L5CLI },
{ "ssl_f_notafter", smp_fetch_ssl_x_notafter, 0, NULL, SMP_T_STR, SMP_USE_L5CLI },
{ "ssl_f_notbefore", smp_fetch_ssl_x_notbefore, 0, NULL, SMP_T_STR, SMP_USE_L5CLI },
{ "ssl_f_sig_alg", smp_fetch_ssl_x_sig_alg, 0, NULL, SMP_T_STR, SMP_USE_L5CLI },
- { "ssl_f_s_dn", smp_fetch_ssl_x_s_dn, ARG2(0,STR,SINT), NULL, SMP_T_STR, SMP_USE_L5CLI },
+ { "ssl_f_s_dn", smp_fetch_ssl_x_s_dn, ARG3(0,STR,SINT,STR),val_dnfmt, SMP_T_STR, SMP_USE_L5CLI },
{ "ssl_f_serial", smp_fetch_ssl_x_serial, 0, NULL, SMP_T_BIN, SMP_USE_L5CLI },
{ "ssl_f_sha1", smp_fetch_ssl_x_sha1, 0, NULL, SMP_T_BIN, SMP_USE_L5CLI },
{ "ssl_f_version", smp_fetch_ssl_x_version, 0, NULL, SMP_T_SINT, SMP_USE_L5CLI },
--
1.8.3.1