diff --git a/src/tls.c b/src/tls.c index 62118d0678b..388a2095b64 100644 --- a/src/tls.c +++ b/src/tls.c @@ -2026,6 +2026,7 @@ static int TLSX_ALPN_ParseAndSet(WOLFSSL *ssl, const byte *input, word16 length, word16 size = 0, offset = 0, wlen; int r = WC_NO_ERR_TRACE(BUFFER_ERROR); const byte *s; + word16 entryCount = 0; if (OPAQUE16_LEN > length) return BUFFER_ERROR; @@ -2042,6 +2043,15 @@ static int TLSX_ALPN_ParseAndSet(WOLFSSL *ssl, const byte *input, word16 length, wlen = *s++; if (wlen == 0 || (s + wlen - input) > length) return BUFFER_ERROR; + entryCount++; + } + + /* RFC 7301 Section 3.1: the server's ProtocolNameList in its ALPN + * response MUST contain exactly one ProtocolName. */ + if (!isRequest && entryCount != 1) { + SendAlert(ssl, alert_fatal, decode_error); + WOLFSSL_ERROR_VERBOSE(BUFFER_ERROR); + return BUFFER_ERROR; } if (isRequest) { diff --git a/tests/api.c b/tests/api.c index fe92bdee85e..1a77d179a2b 100644 --- a/tests/api.c +++ b/tests/api.c @@ -40753,6 +40753,7 @@ TEST_CASE testCases[] = { TEST_DECL(test_TLSX_SNI_GetSize_overflow), TEST_DECL(test_TLSX_ECH_msg_type_validation), TEST_DECL(test_TLSX_SRTP_msg_type_validation), + TEST_DECL(test_TLSX_ALPN_server_response_count), TEST_DECL(test_wolfSSL_wolfSSL_UseSecureRenegotiation), TEST_DECL(test_wolfSSL_clear_secure_renegotiation), TEST_DECL(test_wolfSSL_SCR_Reconnect), diff --git a/tests/api/test_tls_ext.c b/tests/api/test_tls_ext.c index 294fa9c903c..49edaf29f52 100644 --- a/tests/api/test_tls_ext.c +++ b/tests/api/test_tls_ext.c @@ -1033,3 +1033,41 @@ int test_TLSX_SRTP_msg_type_validation(void) #endif return EXPECT_RESULT(); } + +/* RFC 7301 Section 3.1: the server's ProtocolNameList in its ALPN response + * MUST contain exactly one ProtocolName. A ServerHello carrying two entries + * must be rejected rather than silently accepted. */ +int test_TLSX_ALPN_server_response_count(void) +{ + EXPECT_DECLS; +#if defined(HAVE_ALPN) && !defined(NO_WOLFSSL_CLIENT) && !defined(NO_TLS) && \ + !defined(WOLFSSL_NO_TLS12) + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + /* ServerHello-style ALPN extension whose ProtocolNameList contains + * two entries ("h2" and "http/1.1"). */ + static const byte extBytes[] = { + 0x00, 0x10, /* extension type = ALPN (16) */ + 0x00, 0x0E, /* extension length = 14 */ + 0x00, 0x0C, /* ProtocolNameList length */ + 0x02, 'h', '2', /* entry 1: "h2" */ + 0x08, 'h', 't', 't', 'p', '/', '1', '.', '1' /* entry 2 */ + }; + static char alpn_h2[] = "h2"; + + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method())); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + + ExpectIntEQ(wolfSSL_UseALPN(ssl, alpn_h2, (unsigned int)XSTRLEN(alpn_h2), + WOLFSSL_ALPN_FAILED_ON_MISMATCH), + WOLFSSL_SUCCESS); + + ExpectIntEQ(TLSX_Parse(ssl, extBytes, (word16)sizeof(extBytes), + server_hello, NULL), + WC_NO_ERR_TRACE(BUFFER_ERROR)); + + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} diff --git a/tests/api/test_tls_ext.h b/tests/api/test_tls_ext.h index e9d07d81ecd..dc617e2d62e 100644 --- a/tests/api/test_tls_ext.h +++ b/tests/api/test_tls_ext.h @@ -36,5 +36,6 @@ int test_TLSX_TCA_Find(void); int test_TLSX_SNI_GetSize_overflow(void); int test_TLSX_ECH_msg_type_validation(void); int test_TLSX_SRTP_msg_type_validation(void); +int test_TLSX_ALPN_server_response_count(void); #endif /* TESTS_API_TEST_TLS_EMS_H */