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);
+X509* ssl_sock_get_peer_certificate(SSL *ssl);
+
#endif /* _HAPROXY_SSL_UTILS_H */
#endif /* USE_OPENSSL */
syslog Slg_cust_fmt -level info {
recv
- expect ~ ".*conn_status:\"0:Success\" hsk_err:\"0:-\""
+ expect ~ ".*conn_status:\"0:Success\" hsk_err:\"0:-\" CN=\"/C=FR/O=HAProxy Technologies/CN=Client\",serial=1007,hash=063DCC2E6A9159E66994B325D6D2EF3D17A75B6F"
barrier b1 sync
recv
- expect ~ ".*conn_status:\"30:SSL client CA chain cannot be verified\" hsk_err:\"337100934:error:1417C086:SSL routines:tls_process_client_certificate:certificate verify failed\""
+ expect ~ ".*conn_status:\"30:SSL client CA chain cannot be verified\" hsk_err:\"337100934:error:1417C086:SSL routines:tls_process_client_certificate:certificate verify failed\" CN=\"/C=FR/O=HAProxy Technologies/CN=Client\",serial=1007,hash=063DCC2E6A9159E66994B325D6D2EF3D17A75B6F"
barrier b1 sync
recv
- expect ~ ".*conn_status:\"31:SSL client certificate not trusted\" hsk_err:\"337100934:error:1417C086:SSL routines:tls_process_client_certificate:certificate verify failed\""
+ expect ~ ".*conn_status:\"31:SSL client certificate not trusted\" hsk_err:\"337100934:error:1417C086:SSL routines:tls_process_client_certificate:certificate verify failed\" CN=\"/C=FR/O=HAProxy Technologies/CN=Client\",serial=1007,hash=063DCC2E6A9159E66994B325D6D2EF3D17A75B6F"
barrier b1 sync
+ # In case of an error occuring before the certificate verification process,
+ # the client certificate chain is never parsed and verified so we can't
+ # have information about the client's certificate.
recv
- expect ~ ".*conn_status:\"34:SSL handshake failure\" hsk_err:\"337678529:error:142090C1:SSL routines:tls_early_post_process_client_hello:no shared cipher\""
+ expect ~ ".*conn_status:\"34:SSL handshake failure\" hsk_err:\"337678529:error:142090C1:SSL routines:tls_early_post_process_client_hello:no shared cipher\" CN=\"\",serial=-,hash=-"
} -start
syslog Slg_https_fmt -level info {
log ${Slg_cust_fmt_addr}:${Slg_cust_fmt_port} local0
option log-error-via-logformat
mode http
- log-format "conn_status:\"%[fc_conn_err]:%[fc_conn_err_str]\" hsk_err:\"%[ssl_fc_hsk_err]:%[ssl_fc_hsk_err_str]\""
+ log-format "conn_status:\"%[fc_conn_err]:%[fc_conn_err_str]\" hsk_err:\"%[ssl_fc_hsk_err]:%[ssl_fc_hsk_err_str]\" CN=%{+Q}[ssl_c_s_dn],serial=%[ssl_c_serial,hex],hash=%[ssl_c_sha1,hex]"
bind "${tmpdir}/cust_logfmt_ssl.sock" ssl crt ${testdir}/set_cafile_server.pem ca-verify-file ${testdir}/set_cafile_rootCA.crt ca-file ${testdir}/set_cafile_interCA1.crt verify required ciphersuites "TLS_AES_256_GCM_SHA384"
server s1 ${s1_addr}:${s1_port}
if (!ssl)
return 0;
- if (conn->flags & CO_FL_WAIT_XPRT) {
+ if (conn->flags & CO_FL_WAIT_XPRT && !conn->err_code) {
smp->flags |= SMP_F_MAY_CHANGE;
return 0;
}
if (cert_peer)
- crt = SSL_get_peer_certificate(ssl);
+ crt = ssl_sock_get_peer_certificate(ssl);
else
crt = SSL_get_certificate(ssl);
if (!ssl)
return 0;
- if (conn->flags & CO_FL_WAIT_XPRT) {
+ if (conn->flags & CO_FL_WAIT_XPRT && !conn->err_code) {
smp->flags |= SMP_F_MAY_CHANGE;
return 0;
}
if (cert_peer)
- crt = SSL_get_peer_certificate(ssl);
+ crt = ssl_sock_get_peer_certificate(ssl);
else
crt = SSL_get_certificate(ssl);
if (!ssl)
return 0;
- if (conn->flags & CO_FL_WAIT_XPRT) {
+ if (conn->flags & CO_FL_WAIT_XPRT && !conn->err_code) {
smp->flags |= SMP_F_MAY_CHANGE;
return 0;
}
if (cert_peer)
- crt = SSL_get_peer_certificate(ssl);
+ crt = ssl_sock_get_peer_certificate(ssl);
else
crt = SSL_get_certificate(ssl);
if (!crt)
if (!ssl)
return 0;
- if (conn->flags & CO_FL_WAIT_XPRT) {
+ if (conn->flags & CO_FL_WAIT_XPRT && !conn->err_code) {
smp->flags |= SMP_F_MAY_CHANGE;
return 0;
}
if (cert_peer)
- crt = SSL_get_peer_certificate(ssl);
+ crt = ssl_sock_get_peer_certificate(ssl);
else
crt = SSL_get_certificate(ssl);
if (!crt)
if (!ssl)
return 0;
- if (conn->flags & CO_FL_WAIT_XPRT) {
+ if (conn->flags & CO_FL_WAIT_XPRT && !conn->err_code) {
smp->flags |= SMP_F_MAY_CHANGE;
return 0;
}
if (cert_peer)
- crt = SSL_get_peer_certificate(ssl);
+ crt = ssl_sock_get_peer_certificate(ssl);
else
crt = SSL_get_certificate(ssl);
if (!crt)
if (!ssl)
return 0;
- if (conn->flags & CO_FL_WAIT_XPRT) {
+ if (conn->flags & CO_FL_WAIT_XPRT && !conn->err_code) {
smp->flags |= SMP_F_MAY_CHANGE;
return 0;
}
if (cert_peer)
- crt = SSL_get_peer_certificate(ssl);
+ crt = ssl_sock_get_peer_certificate(ssl);
else
crt = SSL_get_certificate(ssl);
if (!crt)
if (!ssl)
return 0;
- if (conn->flags & CO_FL_WAIT_XPRT) {
+ if (conn->flags & CO_FL_WAIT_XPRT && !conn->err_code) {
smp->flags |= SMP_F_MAY_CHANGE;
return 0;
}
if (cert_peer)
- crt = SSL_get_peer_certificate(ssl);
+ crt = ssl_sock_get_peer_certificate(ssl);
else
crt = SSL_get_certificate(ssl);
if (!crt)
if (!ssl)
return 0;
- if (conn->flags & CO_FL_WAIT_XPRT) {
+ if (conn->flags & CO_FL_WAIT_XPRT && !conn->err_code) {
smp->flags |= SMP_F_MAY_CHANGE;
return 0;
}
/* SSL_get_peer_certificate returns a ptr on allocated X509 struct */
- crt = SSL_get_peer_certificate(ssl);
+ crt = ssl_sock_get_peer_certificate(ssl);
if (crt) {
X509_free(crt);
}
if (!ssl)
return 0;
- if (conn->flags & CO_FL_WAIT_XPRT) {
+ if (conn->flags & CO_FL_WAIT_XPRT && !conn->err_code) {
smp->flags |= SMP_F_MAY_CHANGE;
return 0;
}
if (cert_peer)
- crt = SSL_get_peer_certificate(ssl);
+ crt = ssl_sock_get_peer_certificate(ssl);
else
crt = SSL_get_certificate(ssl);
if (!crt)
if (!ssl)
return 0;
- if (conn->flags & CO_FL_WAIT_XPRT) {
+ if (conn->flags & CO_FL_WAIT_XPRT && !conn->err_code) {
smp->flags |= SMP_F_MAY_CHANGE;
return 0;
}
if (cert_peer)
- crt = SSL_get_peer_certificate(ssl);
+ crt = ssl_sock_get_peer_certificate(ssl);
else
crt = SSL_get_certificate(ssl);
if (!crt)
if (!ssl)
return 0;
- if (conn->flags & CO_FL_WAIT_XPRT) {
+ if (conn->flags & CO_FL_WAIT_XPRT && !conn->err_code) {
smp->flags |= SMP_F_MAY_CHANGE;
return 0;
}
if (cert_peer)
- crt = SSL_get_peer_certificate(ssl);
+ crt = ssl_sock_get_peer_certificate(ssl);
else
crt = SSL_get_certificate(ssl);
if (!crt)
struct pool_head *pool_head_ssl_keylog_str __read_mostly = NULL;
#endif
+int ssl_client_crt_ref_index = -1;
+
#if (defined SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB && TLS_TICKETS_NO > 0)
struct list tlskeys_reference = LIST_HEAD_INIT(tlskeys_reference);
#endif
struct connection *conn;
struct ssl_sock_ctx *ctx;
int err, depth;
+ X509 *client_crt;
+ STACK_OF(X509) *certs;
ssl = X509_STORE_CTX_get_ex_data(x_store, SSL_get_ex_data_X509_STORE_CTX_idx());
conn = SSL_get_ex_data(ssl, ssl_app_data_index);
+ client_crt = SSL_get_ex_data(ssl, ssl_client_crt_ref_index);
ctx = conn->xprt_ctx;
ctx->xprt_st |= SSL_SOCK_ST_FL_VERIFY_DONE;
+ depth = X509_STORE_CTX_get_error_depth(x_store);
+ err = X509_STORE_CTX_get_error(x_store);
+
if (ok) /* no errors */
return ok;
- depth = X509_STORE_CTX_get_error_depth(x_store);
- err = X509_STORE_CTX_get_error(x_store);
+ /* Keep a reference to the client's certificate in order to be able to
+ * dump some fetches values in a log even when the verification process
+ * fails. */
+ if (depth == 0) {
+ X509_free(client_crt);
+ client_crt = X509_STORE_CTX_get0_cert(x_store);
+ if (client_crt) {
+ X509_up_ref(client_crt);
+ SSL_set_ex_data(ssl, ssl_client_crt_ref_index, client_crt);
+ }
+ }
+ else {
+ /* An error occurred on a CA certificate of the certificate
+ * chain, we might never call this verify callback on the client
+ * certificate's depth (which is 0) so we try to store the
+ * reference right now. */
+ if (X509_STORE_CTX_get0_chain(x_store) != NULL) {
+ certs = X509_STORE_CTX_get1_chain(x_store);
+ if (certs) {
+ client_crt = sk_X509_value(certs, 0);
+ if (client_crt) {
+ X509_up_ref(client_crt);
+ SSL_set_ex_data(ssl, ssl_client_crt_ref_index, client_crt);
+ }
+ }
+ sk_X509_pop_free(certs, X509_free);
+ }
+ }
/* check if CA error needs to be ignored */
if (depth > 0) {
}
#endif
+static void ssl_sock_clt_crt_free_func(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx, long argl, void *argp)
+{
+ if (!ptr)
+ return;
+
+ X509_free((X509*)ptr);
+}
+
__attribute__((constructor))
static void __ssl_sock_init(void)
{
#ifdef HAVE_SSL_KEYLOG
ssl_keylog_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, ssl_sock_keylog_free_func);
#endif
+ ssl_client_crt_ref_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, ssl_sock_clt_crt_free_func);
#ifndef OPENSSL_NO_ENGINE
ENGINE_load_builtin_engines();
hap_register_post_check(ssl_check_async_engine_count);
return 1;
}
+
+extern int ssl_client_crt_ref_index;
+
+/*
+ * This function fetches the SSL certificate for a specific connection (either
+ * client certificate or server certificate depending on the cert_peer
+ * parameter).
+ * When trying to get the peer certificate from the server side, we first try to
+ * use the dedicated SSL_get_peer_certificate function, but we fall back to
+ * trying to get the client certificate reference that might have been stored in
+ * the SSL structure's ex_data during the verification process.
+ * Returns NULL in case of failure.
+ */
+X509* ssl_sock_get_peer_certificate(SSL *ssl)
+{
+ X509* cert;
+
+ cert = SSL_get_peer_certificate(ssl);
+ /* Get the client certificate reference stored in the SSL
+ * structure's ex_data during the verification process. */
+ if (!cert) {
+ cert = SSL_get_ex_data(ssl, ssl_client_crt_ref_index);
+ if (cert)
+ X509_up_ref(cert);
+ }
+
+ return cert;
+}