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.
This commit is contained in:
Abhijeet Rastogi 2023-05-13 20:04:45 -07:00 committed by William Lallemand
parent 7f95469163
commit df97f472fa
5 changed files with 101 additions and 0 deletions

View File

@ -20768,6 +20768,20 @@ ssl_c_notbefore : string
YYMMDDhhmmss[Z] when the incoming connection was made over an SSL/TLS YYMMDDhhmmss[Z] when the incoming connection was made over an SSL/TLS
transport layer. transport layer.
ssl_c_r_dn([<entry>[,<occ>[,<format>]]]) : string
When the incoming connection was made over an SSL/TLS transport layer, and is
successfully validated with the configured ca-file, 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 ssl_c_s_dn([<entry>[,<occ>[,<format>]]]) : string
When the incoming connection was made over an SSL/TLS transport layer, When the incoming connection was made over an SSL/TLS transport layer,
returns the full distinguished name of the subject of the certificate returns the full distinguished name of the subject of the certificate

View File

@ -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_formatted(X509_NAME *a, const struct buffer *format, struct buffer *out);
int ssl_sock_get_dn_oneline(X509_NAME *a, 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_peer_certificate(SSL *ssl);
X509* ssl_sock_get_verified_chain_root(SSL *ssl);
unsigned int openssl_version_parser(const char *version); unsigned int openssl_version_parser(const char *version);
void exclude_tls_grease(char *input, int len, struct buffer *output); void exclude_tls_grease(char *input, int len, struct buffer *output);
int x509_v_err_str_to_int(const char *str); int x509_v_err_str_to_int(const char *str);

View File

@ -42,6 +42,7 @@ haproxy h1 -conf {
http-response add-header x-ssl-sig_alg %[ssl_c_sig_alg] http-response add-header x-ssl-sig_alg %[ssl_c_sig_alg]
http-response add-header x-ssl-i_dn %[ssl_c_i_dn] http-response add-header x-ssl-i_dn %[ssl_c_i_dn]
http-response add-header x-ssl-s_dn %[ssl_c_s_dn] http-response add-header x-ssl-s_dn %[ssl_c_s_dn]
http-response add-header x-ssl-r_dn %[ssl_c_r_dn]
http-response add-header x-ssl-s_serial %[ssl_c_serial,hex] http-response add-header x-ssl-s_serial %[ssl_c_serial,hex]
http-response add-header x-ssl-key_alg %[ssl_c_key_alg] http-response add-header x-ssl-key_alg %[ssl_c_key_alg]
http-response add-header x-ssl-version %[ssl_c_version] http-response add-header x-ssl-version %[ssl_c_version]
@ -64,6 +65,7 @@ client c1 -connect ${h1_clearlst_sock} {
expect resp.http.x-ssl-sig_alg == "RSA-SHA256" expect resp.http.x-ssl-sig_alg == "RSA-SHA256"
expect resp.http.x-ssl-i_dn == "/C=FR/ST=Some-State/O=HAProxy Technologies/CN=HAProxy Technologies CA Test Client Auth" expect resp.http.x-ssl-i_dn == "/C=FR/ST=Some-State/O=HAProxy Technologies/CN=HAProxy Technologies CA Test Client Auth"
expect resp.http.x-ssl-s_dn == "/C=FR/O=HAProxy Technologies Test/CN=client1" expect resp.http.x-ssl-s_dn == "/C=FR/O=HAProxy Technologies Test/CN=client1"
expect resp.http.x-ssl-r_dn == "/C=FR/ST=Some-State/O=HAProxy Technologies/CN=HAProxy Technologies CA Test Client Auth"
expect resp.http.x-ssl-s_serial == "02" expect resp.http.x-ssl-s_serial == "02"
expect resp.http.x-ssl-key_alg == "rsaEncryption" expect resp.http.x-ssl-key_alg == "rsaEncryption"
expect resp.http.x-ssl-version == "1" expect resp.http.x-ssl-version == "1"

View File

@ -538,6 +538,62 @@ smp_fetch_ssl_fc_has_crt(const struct arg *args, struct sample *smp, const char
return 1; 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). /* 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 * The 5th keyword char is used to know if SSL_get_certificate or SSL_get_peer_certificate
* should be use. * should be use.
@ -2142,6 +2198,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_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_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_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_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_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_serial", smp_fetch_ssl_x_serial, 0, NULL, SMP_T_BIN, SMP_USE_L5CLI },

View File

@ -317,6 +317,33 @@ X509* ssl_sock_get_peer_certificate(SSL *ssl)
return cert; return cert;
} }
/*
* This function fetches the x509* for the root CA of client certificate
* from the verified chain. 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) *chain = NULL;
X509 *crt = NULL;
int i;
chain = SSL_get0_verified_chain(ssl);
if (!chain)
return NULL;
for (i = 0; i < sk_X509_num(chain); i++) {
crt = sk_X509_value(chain, i);
if (X509_check_issued(crt, crt) == X509_V_OK)
break;
}
return crt;
}
/* /*
* Take an OpenSSL version in text format and return a numeric openssl version * Take an OpenSSL version in text format and return a numeric openssl version
* Return 0 if it failed to parse the version * Return 0 if it failed to parse the version