diff --git a/doc/configuration.txt b/doc/configuration.txt index b3ac72be6..cd40d1cde 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -18925,6 +18925,25 @@ ssl_fc_has_sni : boolean that the SSL library is built with support for TLS extensions enabled (check haproxy -vv). +ssl_fc_hsk_err : integer + When the incoming connection was made over an SSL/TLS transport layer, + returns the ID of the latest error that happened during the handshake on the + frontend side, or 0 if no error was encountered. Any error happening during + the client's certificate verification process will not be raised through this + fetch but via the existing "ssl_c_err", "ssl_c_ca_err" and + "ssl_c_ca_err_depth" fetches. In order to get a text description of this + error code, you can either use the "ssl_fc_hsk_err_str" sample fetch or use + the "openssl errstr" command (which takes an error code in hexadecimal + representation as parameter). Please refer to your SSL library's + documentation to find the exhaustive list of error codes. + +ssl_fc_hsk_err_str : string + When the incoming connection was made over an SSL/TLS transport layer, + returns a string representation of the latest error that happened during the + handshake on the frontend side. Any error happening during the client's + certificate verification process will not be raised through this fetch. See + also "ssl_fc_hsk_err". + ssl_fc_is_resumed : boolean Returns true if the SSL/TLS session has been resumed through the use of SSL session cache or TLS tickets on an incoming connection over an SSL/TLS diff --git a/include/haproxy/ssl_sock-t.h b/include/haproxy/ssl_sock-t.h index 98390113f..5acedcfc5 100644 --- a/include/haproxy/ssl_sock-t.h +++ b/include/haproxy/ssl_sock-t.h @@ -235,6 +235,7 @@ struct ssl_sock_ctx { struct wait_event wait_event; struct wait_event *subs; int xprt_st; /* transport layer state, initialized to zero */ + unsigned long hsk_error_code; /* last handshake error code of the error stack */ struct buffer early_buf; /* buffer to store the early data received */ int sent_early_data; /* Amount of early data we sent so far */ diff --git a/src/ssl_sample.c b/src/ssl_sample.c index d6315267f..8bfea07f0 100644 --- a/src/ssl_sample.c +++ b/src/ssl_sample.c @@ -1188,6 +1188,61 @@ smp_fetch_ssl_fc_cl_xxh64(const struct arg *args, struct sample *smp, const char return 1; } +static int +smp_fetch_ssl_fc_hsk_err(const struct arg *args, struct sample *smp, const char *kw, void *private) +{ + struct connection *conn; + struct ssl_sock_ctx *ctx; + + conn = objt_conn(smp->sess->origin); + if (!conn || conn->xprt != &ssl_sock) + return 0; + ctx = conn->xprt_ctx; + + if (conn->flags & CO_FL_WAIT_XPRT && !conn->err_code) { + smp->flags = SMP_F_MAY_CHANGE; + return 0; + } + + if (!ctx) + return 0; + + smp->flags = SMP_F_VOL_SESS; + smp->data.type = SMP_T_SINT; + smp->data.u.sint = ctx->hsk_error_code; + return 1; +} + +static int +smp_fetch_ssl_fc_hsk_err_str(const struct arg *args, struct sample *smp, const char *kw, void *private) +{ + struct connection *conn; + struct ssl_sock_ctx *ctx; + const char *err_code_str; + + conn = objt_conn(smp->sess->origin); + if (!conn || conn->xprt != &ssl_sock) + return 0; + ctx = conn->xprt_ctx; + + if (conn->flags & CO_FL_WAIT_XPRT && !conn->err_code) { + smp->flags = SMP_F_MAY_CHANGE; + return 0; + } + + if (!ctx || !ctx->hsk_error_code) + return 0; + + err_code_str = ERR_error_string(ctx->hsk_error_code, NULL); + + smp->flags = SMP_F_VOL_SESS; + smp->data.type = SMP_T_STR; + smp->data.u.str.area = (char*)err_code_str; + smp->data.u.str.data = strlen(err_code_str); + + return 1; +} + /* Dump the SSL keylog, it only works with "tune.ssl.keylog 1" */ #ifdef HAVE_SSL_KEYLOG static int smp_fetch_ssl_x_keylog(const struct arg *args, struct sample *smp, const char *kw, void *private) @@ -1546,6 +1601,8 @@ static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, { { "ssl_fc_cipherlist_hex", smp_fetch_ssl_fc_cl_hex, 0, NULL, SMP_T_BIN, SMP_USE_L5CLI }, { "ssl_fc_cipherlist_str", smp_fetch_ssl_fc_cl_str, 0, NULL, SMP_T_STR, SMP_USE_L5CLI }, { "ssl_fc_cipherlist_xxh", smp_fetch_ssl_fc_cl_xxh64, 0, NULL, SMP_T_SINT, SMP_USE_L5CLI }, + { "ssl_fc_hsk_err", smp_fetch_ssl_fc_hsk_err, 0, NULL, SMP_T_SINT, SMP_USE_L5CLI }, + { "ssl_fc_hsk_err_str", smp_fetch_ssl_fc_hsk_err_str, 0, NULL, SMP_T_STR, SMP_USE_L5CLI }, /* SSL server certificate fetches */ { "ssl_s_der", smp_fetch_ssl_x_der, 0, NULL, SMP_T_BIN, SMP_USE_L5CLI }, diff --git a/src/ssl_sock.c b/src/ssl_sock.c index 997ab8de4..ba61243ac 100644 --- a/src/ssl_sock.c +++ b/src/ssl_sock.c @@ -5278,6 +5278,7 @@ static int ssl_sock_init(struct connection *conn, void **xprt_ctx) ctx->subs = NULL; ctx->xprt_st = 0; ctx->xprt_ctx = NULL; + ctx->hsk_error_code = 0; /* Only work with sockets for now, this should be adapted when we'll * add QUIC support. @@ -5556,6 +5557,9 @@ static int ssl_sock_handshake(struct connection *conn, unsigned int flag) /* handshake did not complete, let's find why */ ret = SSL_get_error(ctx->ssl, ret); + if (!ctx->hsk_error_code) + ctx->hsk_error_code = ERR_peek_error(); + if (ret == SSL_ERROR_WANT_WRITE) { /* SSL handshake needs to write, L4 connection may not be ready */ if (!(ctx->wait_event.events & SUB_RETRY_SEND))