From: Elliot Otchet Date: Wed, 15 Jan 2020 13:12:14 +0000 (-0500) Subject: MINOR: ssl: Add support for returning the dn samples from ssl_(c|f)_(i|s)_dn in LDAP... X-Git-Tag: v2.2-dev1~83 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=71f829767d3a5f8e2f309862b1e606bb03323878;p=thirdparty%2Fhaproxy.git 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 Reviewed-by: Tim Duesterhus --- diff --git a/doc/configuration.txt b/doc/configuration.txt index 9ac8985175..1d9b3d1cdc 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([[,]]) : string +ssl_c_i_dn([[,[,]]]) : 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 is specified, or the value of the @@ -15255,6 +15255,11 @@ ssl_c_i_dn([[,]]) : 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 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([[,]]) : string +ssl_c_s_dn([[,[,]]]) : 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 is specified, or the value of the @@ -15280,6 +15285,11 @@ ssl_c_s_dn([[,]]) : 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 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([[,]]) : string +ssl_f_i_dn([[,[,]]]) : 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 is specified, or the value of the @@ -15329,6 +15339,11 @@ ssl_f_i_dn([[,]]) : 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 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([[,]]) : string +ssl_f_s_dn([[,[,]]]) : 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 is specified, or the value of the @@ -15354,6 +15369,11 @@ ssl_f_s_dn([[,]]) : 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 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 2dcceac0cb..95dbe4c891 100644 --- a/src/ssl_sock.c +++ b/src/ssl_sock.c @@ -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 },