]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: ssl: Add support for returning the dn samples from ssl_(c|f)_(i|s)_dn in LDAP...
authorElliot Otchet <degroens@yahoo.com>
Wed, 15 Jan 2020 13:12:14 +0000 (08:12 -0500)
committerWilly Tarreau <w@1wt.eu>
Sat, 18 Jan 2020 05:42:30 +0000 (06:42 +0100)
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 <degroens@yahoo.com>
Reviewed-by: Tim Duesterhus <tim@bastelstu.be>
doc/configuration.txt
src/ssl_sock.c

index 9ac898517539c25938c2fd9814d2652ebee07b3c..1d9b3d1cdc38ed6be73c8123a72914881d016715 100644 (file)
@@ -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
index 2dcceac0cb5999d6d892bdb29bf1457a3b859976..95dbe4c891384d79aa5ed6f3bf50f54f755b069b 100644 (file)
@@ -7117,6 +7117,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.
  */
@@ -7596,7 +7629,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)
@@ -7605,6 +7638,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;
 
@@ -7700,7 +7737,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)
@@ -7709,6 +7746,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;
 
@@ -11076,6 +11117,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)
@@ -11121,24 +11177,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 },