mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-08-06 07:07:04 +02:00
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:
parent
7f95469163
commit
df97f472fa
@ -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
|
||||||
|
@ -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);
|
||||||
|
@ -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"
|
||||||
|
@ -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 },
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user