diff --git a/doc/dox_comments/header_files/ssl.h b/doc/dox_comments/header_files/ssl.h index a58c6b4f202..2d720937852 100644 --- a/doc/dox_comments/header_files/ssl.h +++ b/doc/dox_comments/header_files/ssl.h @@ -6348,6 +6348,59 @@ long wolfSSL_get_options(const WOLFSSL *s); */ long wolfSSL_set_tlsext_debug_arg(WOLFSSL *s, void *arg); +/*! + \ingroup Setup + + \brief Callback type for the TLS extension debug callback. + + Invoked once for every TLS extension received during the handshake, + in wire order, before the extension is processed. + + \param ssl The WOLFSSL object receiving the extension. + \param client_server 1 if the WOLFSSL object is a client, 0 if a server. + \param type The extension type, e.g. TLSX_SERVER_NAME. + \param data The raw extension content (data after the 2-byte length). + \param len Length of the extension content in bytes. + \param arg The argument set with wolfSSL_set_tlsext_debug_arg(). + + Note that, unlike OpenSSL 3.x, the callback also reports unknown + (unregistered) extension types. +*/ +typedef void (*WOLFSSL_TLSEXT_DEBUG_CB)(WOLFSSL* ssl, int client_server, + int type, const byte* data, int len, void* arg); + +/*! + \ingroup Setup + + \brief This is used to set the TLS extension debug callback on the + object. + + The callback (type WOLFSSL_TLSEXT_DEBUG_CB) is invoked once for every + TLS extension received during the handshake, in wire order, before the + extension is processed. It reports the side of the connection, the + extension type, the raw extension content and the argument set with + wolfSSL_set_tlsext_debug_arg(). Passing a NULL callback disables it. + + \return SSL_SUCCESS On successful setting of the callback. + \return SSL_FAILURE If a NULL ssl is passed in. + + \param s WOLFSSL structure to set the callback in. + \param cb Callback to invoke for each received TLS extension, or NULL + to disable it. + + _Example_ + \code + WOLFSSL* ssl; + long ret; + // create ssl object + ret = wolfSSL_set_tlsext_debug_callback(ssl, my_tlsext_debug_cb); + // check ret value + \endcode + + \sa wolfSSL_set_tlsext_debug_arg +*/ +long wolfSSL_set_tlsext_debug_callback(WOLFSSL *s, WOLFSSL_TLSEXT_DEBUG_CB cb); + /*! \ingroup openSSL diff --git a/src/ssl_api_ext.c b/src/ssl_api_ext.c index 2be0da03757..e536b6d8d6a 100644 --- a/src/ssl_api_ext.c +++ b/src/ssl_api_ext.c @@ -1634,6 +1634,29 @@ long wolfSSL_set_tlsext_debug_arg(WOLFSSL* ssl, void *arg) return ret; } + +/* Set the callback invoked for each TLS extension received during the + * handshake. + * + * @param [in, out] ssl SSL/TLS object. + * @param [in] cb Debug callback, or NULL to disable. + * @return WOLFSSL_SUCCESS on success. + * @return WOLFSSL_FAILURE when ssl is NULL. + */ +long wolfSSL_set_tlsext_debug_callback(WOLFSSL* ssl, + WOLFSSL_TLSEXT_DEBUG_CB cb) +{ + long ret = WOLFSSL_SUCCESS; + + if (ssl == NULL) { + ret = WOLFSSL_FAILURE; + } + else { + ssl->tlsextDebugCb = cb; + } + + return ret; +} #endif /* HAVE_PK_CALLBACKS */ #ifndef NO_WOLFSSL_STUB diff --git a/src/tls.c b/src/tls.c index 7b3e0d5bc59..b9a2067fafb 100644 --- a/src/tls.c +++ b/src/tls.c @@ -18612,6 +18612,17 @@ WOLFSSL_TEST_VIS int TLSX_Parse(WOLFSSL* ssl, const byte* input, word16 length, if (length - offset < size) return BUFFER_ERROR; +#if defined(OPENSSL_EXTRA) && defined(HAVE_PK_CALLBACKS) + /* Report the extension to the debug callback, like OpenSSL does in + * tls1_handle_extensions(). client_server is 1 when this SSL object + * is a client. */ + if (ssl->tlsextDebugCb != NULL) { + ssl->tlsextDebugCb(ssl, + (int)(ssl->options.side == WOLFSSL_CLIENT_END), (int)type, + input + offset, (int)size, ssl->loggingCtx); + } +#endif + /* Check minimum size required for TLSX, even if disabled */ switch (msgType) { #ifndef NO_WOLFSSL_SERVER diff --git a/tests/api/test_ssl_ext.c b/tests/api/test_ssl_ext.c index 132485c2be9..63ae701e6b8 100644 --- a/tests/api/test_ssl_ext.c +++ b/tests/api/test_ssl_ext.c @@ -528,6 +528,149 @@ int test_wolfSSL_set_tlsext_debug_arg_ext(void) return EXPECT_RESULT(); } +#if defined(OPENSSL_EXTRA) && defined(HAVE_PK_CALLBACKS) && \ + !defined(NO_TLS) && \ + (!defined(NO_WOLFSSL_CLIENT) || \ + (defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + defined(HAVE_TLS_EXTENSIONS))) +/* State recorded by test_tlsext_debug_cb(). */ +struct test_tlsext_debug_data { + int count; /* number of extensions reported */ + int client_server; /* client_server value reported */ + int types[32]; /* extension types reported, in order */ + int lens[32]; /* lengths of the reported extensions */ +}; + +static void test_tlsext_debug_cb(WOLFSSL *ssl, int client_server, int type, + const byte *data, int len, void *arg) +{ + struct test_tlsext_debug_data *d = (struct test_tlsext_debug_data *)arg; + (void)ssl; + (void)data; + + d->count++; + d->client_server = client_server; + if (d->count - 1 < (int)(sizeof(d->types) / sizeof(d->types[0]))) { + d->types[d->count - 1] = type; + d->lens[d->count - 1] = len; + } +} + +/* Find an extension type in the recorded list; returns its length, -1 if + * not reported. */ +static int test_tlsext_debug_find_len(const struct test_tlsext_debug_data *d, + int type) +{ + int i; + + for (i = 0; i < d->count && + i < (int)(sizeof(d->types) / sizeof(d->types[0])); i++) { + if (d->types[i] == type) + return d->lens[i]; + } + return -1; +} +#endif /* helpers for the TLS ext debug callback tests */ + +/* Test installing the TLS extension debug callback. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_set_tlsext_debug_callback_ext(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && defined(HAVE_PK_CALLBACKS) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_TLS) + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + + /* NULL object is rejected. */ + ExpectIntEQ(wolfSSL_set_tlsext_debug_callback(NULL, + test_tlsext_debug_cb), WOLFSSL_FAILURE); + + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_client_method())); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + ExpectIntEQ(wolfSSL_set_tlsext_debug_callback(ssl, test_tlsext_debug_cb), + WOLFSSL_SUCCESS); + /* Setting NULL disables the callback. */ + ExpectIntEQ(wolfSSL_set_tlsext_debug_callback(ssl, NULL), + WOLFSSL_SUCCESS); + + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} + +/* Test that the TLS extension debug callback reports the extensions + * received during a handshake on both sides. + * + * client_server identifies the side of the connection and the argument set + * with wolfSSL_set_tlsext_debug_arg() is passed through to the callback. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_set_tlsext_debug_callback_handshake_ext(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && defined(HAVE_PK_CALLBACKS) && \ + defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + defined(HAVE_TLS_EXTENSIONS) && !defined(NO_TLS) + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + struct test_memio_ctx test_ctx; + struct test_tlsext_debug_data cData, sData; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + XMEMSET(&cData, 0, sizeof(cData)); + XMEMSET(&sData, 0, sizeof(sData)); + + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfSSLv23_client_method, wolfSSLv23_server_method), 0); + + ExpectIntEQ(wolfSSL_set_tlsext_debug_callback(ssl_c, test_tlsext_debug_cb), + WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_set_tlsext_debug_arg(ssl_c, &cData), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_set_tlsext_debug_callback(ssl_s, test_tlsext_debug_cb), + WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_set_tlsext_debug_arg(ssl_s, &sData), WOLFSSL_SUCCESS); + + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + /* Each side saw extensions from the peer, tagged with its own side. + * The server always sees the client hello's core extensions (e.g. + * supported groups); the client sees the server hello's, which carries + * supported versions in TLS 1.3 or the echoed extended master secret in + * TLS 1.2. */ + ExpectTrue(sData.count > 0); +#if defined(HAVE_EXTENDED_MASTER) || defined(WOLFSSL_TLS13) + ExpectTrue(cData.count > 0); +#endif + ExpectIntEQ(cData.client_server, 1); + ExpectIntEQ(sData.client_server, 0); + + /* Known extensions are reported with the expected content. */ +#if defined(WOLFSSL_TLS13) + /* TLS 1.3: both sides see supported versions (a list of 2-byte + * versions, so at least 2 bytes). */ + ExpectTrue(test_tlsext_debug_find_len(&cData, + TLSX_SUPPORTED_VERSIONS) >= 2); + ExpectTrue(test_tlsext_debug_find_len(&sData, + TLSX_SUPPORTED_VERSIONS) >= 2); +#elif defined(HAVE_EXTENDED_MASTER) + /* TLS 1.2: the client offers extended master secret (empty content). */ + ExpectIntEQ(test_tlsext_debug_find_len(&sData, + TLSX_EXTENDED_MASTER_SECRET), 0); +#endif + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + /* Test installing the session ticket callback and its context. * * @return TEST_SUCCESS on success. diff --git a/tests/api/test_ssl_ext.h b/tests/api/test_ssl_ext.h index ee3da3a2108..bec0e79d2cc 100644 --- a/tests/api/test_ssl_ext.h +++ b/tests/api/test_ssl_ext.h @@ -34,6 +34,8 @@ int test_wolfSSL_DisableExtendedMasterSecret_ext(void); int test_wolfSSL_set_tlsext_host_name_ext(void); int test_wolfSSL_CTX_set_tlsext_servername_callback_ext(void); int test_wolfSSL_set_tlsext_debug_arg_ext(void); +int test_wolfSSL_set_tlsext_debug_callback_ext(void); +int test_wolfSSL_set_tlsext_debug_callback_handshake_ext(void); int test_wolfSSL_set_SessionTicket_cb_ext(void); int test_wolfSSL_set1_curves_list_ext(void); int test_wolfSSL_SecureResume_ext(void); @@ -74,6 +76,9 @@ int test_wolfSSL_ticket_key_cb_renew_ext(void); TEST_DECL_GROUP("ssl_ext", \ test_wolfSSL_CTX_set_tlsext_servername_callback_ext), \ TEST_DECL_GROUP("ssl_ext", test_wolfSSL_set_tlsext_debug_arg_ext), \ + TEST_DECL_GROUP("ssl_ext", test_wolfSSL_set_tlsext_debug_callback_ext), \ + TEST_DECL_GROUP("ssl_ext", \ + test_wolfSSL_set_tlsext_debug_callback_handshake_ext), \ TEST_DECL_GROUP("ssl_ext", test_wolfSSL_set_SessionTicket_cb_ext), \ TEST_DECL_GROUP("ssl_ext", test_wolfSSL_set1_curves_list_ext), \ TEST_DECL_GROUP("ssl_ext", test_wolfSSL_SecureResume_ext), \ diff --git a/wolfssl/internal.h b/wolfssl/internal.h index ebc0e7b5245..3506c790f68 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -6572,6 +6572,7 @@ struct WOLFSSL { #ifdef OPENSSL_EXTRA #ifdef HAVE_PK_CALLBACKS void* loggingCtx; /* logging callback argument */ + WOLFSSL_TLSEXT_DEBUG_CB tlsextDebugCb; /* TLS ext debug callback */ #endif #endif /* OPENSSL_EXTRA */ #ifndef NO_RSA diff --git a/wolfssl/openssl/ssl.h b/wolfssl/openssl/ssl.h index 91d61cfdaf2..4dcb9f1f6a8 100644 --- a/wolfssl/openssl/ssl.h +++ b/wolfssl/openssl/ssl.h @@ -1449,6 +1449,8 @@ typedef WOLFSSL_SRTP_PROTECTION_PROFILE SRTP_PROTECTION_PROFILE; #define SSL_get_secure_renegotiation_support wolfSSL_SSL_get_secure_renegotiation_support #define SSL_renegotiate_pending wolfSSL_SSL_renegotiate_pending #define SSL_set_tlsext_debug_arg wolfSSL_set_tlsext_debug_arg +#define SSL_set_tlsext_debug_callback(ssl, cb) \ + wolfSSL_set_tlsext_debug_callback((ssl), (WOLFSSL_TLSEXT_DEBUG_CB)(cb)) #define SSL_set_tlsext_status_type wolfSSL_set_tlsext_status_type #define SSL_get_tlsext_status_type wolfSSL_get_tlsext_status_type #define SSL_set_tlsext_status_exts wolfSSL_set_tlsext_status_exts diff --git a/wolfssl/ssl.h b/wolfssl/ssl.h index dd35a5f9291..ea5a7a7c4a9 100644 --- a/wolfssl/ssl.h +++ b/wolfssl/ssl.h @@ -2775,7 +2775,11 @@ WOLFSSL_API char* wolfSSL_get_srp_username(WOLFSSL *ssl); WOLFSSL_API long wolfSSL_clear_options(WOLFSSL *s, long op); WOLFSSL_API long wolfSSL_set_tmp_dh(WOLFSSL *s, WOLFSSL_DH *dh); +typedef void (*WOLFSSL_TLSEXT_DEBUG_CB)(WOLFSSL* ssl, int client_server, + int type, const byte* data, int len, void* arg); WOLFSSL_API long wolfSSL_set_tlsext_debug_arg(WOLFSSL *s, void *arg); +WOLFSSL_API long wolfSSL_set_tlsext_debug_callback(WOLFSSL *s, + WOLFSSL_TLSEXT_DEBUG_CB cb); WOLFSSL_API long wolfSSL_set_tlsext_status_type(WOLFSSL *s, int type); WOLFSSL_API long wolfSSL_get_tlsext_status_type(WOLFSSL *s); WOLFSSL_API long wolfSSL_set_tlsext_status_exts(WOLFSSL *s, void *arg);