| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
| /* vim: set ts=2 et sw=2 tw=80: */ |
| /* This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
| * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| |
| #include "ssl.h" |
| #include "ssl3prot.h" |
| #include "sslerr.h" |
| #include "sslproto.h" |
| #include "sslexp.h" |
| |
| #include <memory> |
| |
| #include "tls_connect.h" |
| |
| namespace nss_test { |
| |
| static void IncrementCounterArg(void *arg) { |
| if (arg) { |
| auto *called = reinterpret_cast<size_t *>(arg); |
| ++*called; |
| } |
| } |
| |
| PRBool NoopExtensionWriter(PRFileDesc *fd, SSLHandshakeType message, |
| PRUint8 *data, unsigned int *len, |
| unsigned int maxLen, void *arg) { |
| IncrementCounterArg(arg); |
| return PR_FALSE; |
| } |
| |
| PRBool EmptyExtensionWriter(PRFileDesc *fd, SSLHandshakeType message, |
| PRUint8 *data, unsigned int *len, |
| unsigned int maxLen, void *arg) { |
| IncrementCounterArg(arg); |
| return PR_TRUE; |
| } |
| |
| SECStatus NoopExtensionHandler(PRFileDesc *fd, SSLHandshakeType message, |
| const PRUint8 *data, unsigned int len, |
| SSLAlertDescription *alert, void *arg) { |
| return SECSuccess; |
| } |
| |
| // All of the (current) set of supported extensions, plus a few extra. |
| static const uint16_t kManyExtensions[] = { |
| ssl_server_name_xtn, |
| ssl_cert_status_xtn, |
| ssl_supported_groups_xtn, |
| ssl_ec_point_formats_xtn, |
| ssl_signature_algorithms_xtn, |
| ssl_signature_algorithms_cert_xtn, |
| ssl_use_srtp_xtn, |
| ssl_app_layer_protocol_xtn, |
| ssl_signed_cert_timestamp_xtn, |
| ssl_padding_xtn, |
| ssl_extended_master_secret_xtn, |
| ssl_session_ticket_xtn, |
| ssl_tls13_key_share_xtn, |
| ssl_tls13_pre_shared_key_xtn, |
| ssl_tls13_early_data_xtn, |
| ssl_tls13_supported_versions_xtn, |
| ssl_tls13_cookie_xtn, |
| ssl_tls13_psk_key_exchange_modes_xtn, |
| ssl_tls13_ticket_early_data_info_xtn, |
| ssl_tls13_certificate_authorities_xtn, |
| ssl_next_proto_nego_xtn, |
| ssl_renegotiation_info_xtn, |
| ssl_tls13_short_header_xtn, |
| ssl_record_size_limit_xtn, |
| 1, |
| 0xffff}; |
| // The list here includes all extensions we expect to use (SSL_MAX_EXTENSIONS), |
| // plus the deprecated values (see sslt.h), and two extra dummy values. |
| PR_STATIC_ASSERT((SSL_MAX_EXTENSIONS + 5) == PR_ARRAY_SIZE(kManyExtensions)); |
| |
| void InstallManyWriters(std::shared_ptr<TlsAgent> agent, |
| SSLExtensionWriter writer, size_t *installed = nullptr, |
| size_t *called = nullptr) { |
| for (size_t i = 0; i < PR_ARRAY_SIZE(kManyExtensions); ++i) { |
| SSLExtensionSupport support = ssl_ext_none; |
| SECStatus rv = SSL_GetExtensionSupport(kManyExtensions[i], &support); |
| ASSERT_EQ(SECSuccess, rv) << "SSL_GetExtensionSupport cannot fail"; |
| |
| rv = SSL_InstallExtensionHooks(agent->ssl_fd(), kManyExtensions[i], writer, |
| called, NoopExtensionHandler, nullptr); |
| if (support == ssl_ext_native_only) { |
| EXPECT_EQ(SECFailure, rv); |
| EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError()); |
| } else { |
| if (installed) { |
| ++*installed; |
| } |
| EXPECT_EQ(SECSuccess, rv); |
| } |
| } |
| } |
| |
| TEST_F(TlsConnectStreamTls13, CustomExtensionAllNoopClient) { |
| EnsureTlsSetup(); |
| size_t installed = 0; |
| size_t called = 0; |
| InstallManyWriters(client_, NoopExtensionWriter, &installed, &called); |
| EXPECT_LT(0U, installed); |
| Connect(); |
| EXPECT_EQ(installed, called); |
| } |
| |
| TEST_F(TlsConnectStreamTls13, CustomExtensionAllNoopServer) { |
| EnsureTlsSetup(); |
| size_t installed = 0; |
| size_t called = 0; |
| InstallManyWriters(server_, NoopExtensionWriter, &installed, &called); |
| EXPECT_LT(0U, installed); |
| Connect(); |
| // Extension writers are all called for each of ServerHello, |
| // EncryptedExtensions, and Certificate. |
| EXPECT_EQ(installed * 3, called); |
| } |
| |
| TEST_F(TlsConnectStreamTls13, CustomExtensionEmptyWriterClient) { |
| EnsureTlsSetup(); |
| InstallManyWriters(client_, EmptyExtensionWriter); |
| InstallManyWriters(server_, EmptyExtensionWriter); |
| Connect(); |
| } |
| |
| TEST_F(TlsConnectStreamTls13, CustomExtensionEmptyWriterServer) { |
| EnsureTlsSetup(); |
| InstallManyWriters(server_, EmptyExtensionWriter); |
| // Sending extensions that the client doesn't expect leads to extensions |
| // appearing even if the client didn't send one, or in the wrong messages. |
| client_->ExpectSendAlert(kTlsAlertUnsupportedExtension); |
| server_->ExpectSendAlert(kTlsAlertUnexpectedMessage); |
| ConnectExpectFail(); |
| } |
| |
| // Install an writer to disable sending of a natively-supported extension. |
| TEST_F(TlsConnectStreamTls13, CustomExtensionWriterDisable) { |
| EnsureTlsSetup(); |
| |
| // This option enables sending the extension via the native support. |
| SECStatus rv = SSL_OptionSet(client_->ssl_fd(), |
| SSL_ENABLE_SIGNED_CERT_TIMESTAMPS, PR_TRUE); |
| EXPECT_EQ(SECSuccess, rv); |
| |
| // This installs an override that doesn't do anything. You have to specify |
| // something; passing all nullptr values removes an existing handler. |
| rv = SSL_InstallExtensionHooks( |
| client_->ssl_fd(), ssl_signed_cert_timestamp_xtn, NoopExtensionWriter, |
| nullptr, NoopExtensionHandler, nullptr); |
| EXPECT_EQ(SECSuccess, rv); |
| auto capture = MakeTlsFilter<TlsExtensionCapture>( |
| client_, ssl_signed_cert_timestamp_xtn); |
| |
| Connect(); |
| // So nothing will be sent. |
| EXPECT_FALSE(capture->captured()); |
| } |
| |
| // An extension that is unlikely to be parsed as valid. |
| static uint8_t kNonsenseExtension[] = {91, 82, 73, 64, 55, 46, 37, 28, 19}; |
| |
| static PRBool NonsenseExtensionWriter(PRFileDesc *fd, SSLHandshakeType message, |
| PRUint8 *data, unsigned int *len, |
| unsigned int maxLen, void *arg) { |
| TlsAgent *agent = reinterpret_cast<TlsAgent *>(arg); |
| EXPECT_NE(nullptr, agent); |
| EXPECT_NE(nullptr, data); |
| EXPECT_NE(nullptr, len); |
| EXPECT_EQ(0U, *len); |
| EXPECT_LT(0U, maxLen); |
| EXPECT_EQ(agent->ssl_fd(), fd); |
| |
| if (message != ssl_hs_client_hello && message != ssl_hs_server_hello && |
| message != ssl_hs_encrypted_extensions) { |
| return PR_FALSE; |
| } |
| |
| *len = static_cast<unsigned int>(sizeof(kNonsenseExtension)); |
| EXPECT_GE(maxLen, *len); |
| if (maxLen < *len) { |
| return PR_FALSE; |
| } |
| PORT_Memcpy(data, kNonsenseExtension, *len); |
| return PR_TRUE; |
| } |
| |
| // Override the extension handler for an natively-supported and produce |
| // nonsense, which results in a handshake failure. |
| TEST_F(TlsConnectStreamTls13, CustomExtensionOverride) { |
| EnsureTlsSetup(); |
| |
| // This option enables sending the extension via the native support. |
| SECStatus rv = SSL_OptionSet(client_->ssl_fd(), |
| SSL_ENABLE_SIGNED_CERT_TIMESTAMPS, PR_TRUE); |
| EXPECT_EQ(SECSuccess, rv); |
| |
| // This installs an override that sends nonsense. |
| rv = SSL_InstallExtensionHooks( |
| client_->ssl_fd(), ssl_signed_cert_timestamp_xtn, NonsenseExtensionWriter, |
| client_.get(), NoopExtensionHandler, nullptr); |
| EXPECT_EQ(SECSuccess, rv); |
| |
| // Capture it to see what we got. |
| auto capture = MakeTlsFilter<TlsExtensionCapture>( |
| client_, ssl_signed_cert_timestamp_xtn); |
| |
| ConnectExpectAlert(server_, kTlsAlertDecodeError); |
| |
| EXPECT_TRUE(capture->captured()); |
| EXPECT_EQ(DataBuffer(kNonsenseExtension, sizeof(kNonsenseExtension)), |
| capture->extension()); |
| } |
| |
| static SECStatus NonsenseExtensionHandler(PRFileDesc *fd, |
| SSLHandshakeType message, |
| const PRUint8 *data, unsigned int len, |
| SSLAlertDescription *alert, |
| void *arg) { |
| TlsAgent *agent = reinterpret_cast<TlsAgent *>(arg); |
| EXPECT_EQ(agent->ssl_fd(), fd); |
| if (agent->role() == TlsAgent::SERVER) { |
| EXPECT_EQ(ssl_hs_client_hello, message); |
| } else { |
| EXPECT_TRUE(message == ssl_hs_server_hello || |
| message == ssl_hs_encrypted_extensions); |
| } |
| EXPECT_EQ(DataBuffer(kNonsenseExtension, sizeof(kNonsenseExtension)), |
| DataBuffer(data, len)); |
| EXPECT_NE(nullptr, alert); |
| return SECSuccess; |
| } |
| |
| // Send nonsense in an extension from client to server. |
| TEST_F(TlsConnectStreamTls13, CustomExtensionClientToServer) { |
| EnsureTlsSetup(); |
| |
| // This installs an override that sends nonsense. |
| const uint16_t extension_code = 0xffe5; |
| SECStatus rv = SSL_InstallExtensionHooks( |
| client_->ssl_fd(), extension_code, NonsenseExtensionWriter, client_.get(), |
| NoopExtensionHandler, nullptr); |
| EXPECT_EQ(SECSuccess, rv); |
| |
| // Capture it to see what we got. |
| auto capture = MakeTlsFilter<TlsExtensionCapture>(client_, extension_code); |
| |
| // Handle it so that the handshake completes. |
| rv = SSL_InstallExtensionHooks(server_->ssl_fd(), extension_code, |
| NoopExtensionWriter, nullptr, |
| NonsenseExtensionHandler, server_.get()); |
| EXPECT_EQ(SECSuccess, rv); |
| |
| Connect(); |
| |
| EXPECT_TRUE(capture->captured()); |
| EXPECT_EQ(DataBuffer(kNonsenseExtension, sizeof(kNonsenseExtension)), |
| capture->extension()); |
| } |
| |
| static PRBool NonsenseExtensionWriterSH(PRFileDesc *fd, |
| SSLHandshakeType message, PRUint8 *data, |
| unsigned int *len, unsigned int maxLen, |
| void *arg) { |
| if (message == ssl_hs_server_hello) { |
| return NonsenseExtensionWriter(fd, message, data, len, maxLen, arg); |
| } |
| return PR_FALSE; |
| } |
| |
| // Send nonsense in an extension from server to client, in ServerHello. |
| TEST_F(TlsConnectStreamTls13, CustomExtensionServerToClientSH) { |
| EnsureTlsSetup(); |
| |
| // This installs an override that sends nothing but expects nonsense. |
| const uint16_t extension_code = 0xff5e; |
| SECStatus rv = SSL_InstallExtensionHooks( |
| client_->ssl_fd(), extension_code, EmptyExtensionWriter, nullptr, |
| NonsenseExtensionHandler, client_.get()); |
| EXPECT_EQ(SECSuccess, rv); |
| |
| // Have the server send nonsense. |
| rv = SSL_InstallExtensionHooks(server_->ssl_fd(), extension_code, |
| NonsenseExtensionWriterSH, server_.get(), |
| NoopExtensionHandler, nullptr); |
| EXPECT_EQ(SECSuccess, rv); |
| |
| // Capture the extension from the ServerHello only and check it. |
| auto capture = MakeTlsFilter<TlsExtensionCapture>(server_, extension_code); |
| capture->SetHandshakeTypes({kTlsHandshakeServerHello}); |
| |
| Connect(); |
| |
| EXPECT_TRUE(capture->captured()); |
| EXPECT_EQ(DataBuffer(kNonsenseExtension, sizeof(kNonsenseExtension)), |
| capture->extension()); |
| } |
| |
| static PRBool NonsenseExtensionWriterEE(PRFileDesc *fd, |
| SSLHandshakeType message, PRUint8 *data, |
| unsigned int *len, unsigned int maxLen, |
| void *arg) { |
| if (message == ssl_hs_encrypted_extensions) { |
| return NonsenseExtensionWriter(fd, message, data, len, maxLen, arg); |
| } |
| return PR_FALSE; |
| } |
| |
| // Send nonsense in an extension from server to client, in EncryptedExtensions. |
| TEST_F(TlsConnectStreamTls13, CustomExtensionServerToClientEE) { |
| EnsureTlsSetup(); |
| |
| // This installs an override that sends nothing but expects nonsense. |
| const uint16_t extension_code = 0xff5e; |
| SECStatus rv = SSL_InstallExtensionHooks( |
| client_->ssl_fd(), extension_code, EmptyExtensionWriter, nullptr, |
| NonsenseExtensionHandler, client_.get()); |
| EXPECT_EQ(SECSuccess, rv); |
| |
| // Have the server send nonsense. |
| rv = SSL_InstallExtensionHooks(server_->ssl_fd(), extension_code, |
| NonsenseExtensionWriterEE, server_.get(), |
| NoopExtensionHandler, nullptr); |
| EXPECT_EQ(SECSuccess, rv); |
| |
| // Capture the extension from the EncryptedExtensions only and check it. |
| auto capture = MakeTlsFilter<TlsExtensionCapture>(server_, extension_code); |
| capture->SetHandshakeTypes({kTlsHandshakeEncryptedExtensions}); |
| capture->EnableDecryption(); |
| |
| Connect(); |
| |
| EXPECT_TRUE(capture->captured()); |
| EXPECT_EQ(DataBuffer(kNonsenseExtension, sizeof(kNonsenseExtension)), |
| capture->extension()); |
| } |
| |
| TEST_F(TlsConnectStreamTls13, CustomExtensionUnsolicitedServer) { |
| EnsureTlsSetup(); |
| |
| const uint16_t extension_code = 0xff5e; |
| SECStatus rv = SSL_InstallExtensionHooks( |
| server_->ssl_fd(), extension_code, NonsenseExtensionWriter, server_.get(), |
| NoopExtensionHandler, nullptr); |
| EXPECT_EQ(SECSuccess, rv); |
| |
| // Capture it to see what we got. |
| auto capture = MakeTlsFilter<TlsExtensionCapture>(server_, extension_code); |
| |
| client_->ExpectSendAlert(kTlsAlertUnsupportedExtension); |
| server_->ExpectSendAlert(kTlsAlertUnexpectedMessage); |
| ConnectExpectFail(); |
| |
| EXPECT_TRUE(capture->captured()); |
| EXPECT_EQ(DataBuffer(kNonsenseExtension, sizeof(kNonsenseExtension)), |
| capture->extension()); |
| } |
| |
| SECStatus RejectExtensionHandler(PRFileDesc *fd, SSLHandshakeType message, |
| const PRUint8 *data, unsigned int len, |
| SSLAlertDescription *alert, void *arg) { |
| return SECFailure; |
| } |
| |
| TEST_F(TlsConnectStreamTls13, CustomExtensionServerReject) { |
| EnsureTlsSetup(); |
| |
| // This installs an override that sends nonsense. |
| const uint16_t extension_code = 0xffe7; |
| SECStatus rv = SSL_InstallExtensionHooks(client_->ssl_fd(), extension_code, |
| EmptyExtensionWriter, nullptr, |
| NoopExtensionHandler, nullptr); |
| EXPECT_EQ(SECSuccess, rv); |
| |
| // Reject the extension for no good reason. |
| rv = SSL_InstallExtensionHooks(server_->ssl_fd(), extension_code, |
| NoopExtensionWriter, nullptr, |
| RejectExtensionHandler, nullptr); |
| EXPECT_EQ(SECSuccess, rv); |
| |
| ConnectExpectAlert(server_, kTlsAlertHandshakeFailure); |
| } |
| |
| // Send nonsense in an extension from client to server. |
| TEST_F(TlsConnectStreamTls13, CustomExtensionClientReject) { |
| EnsureTlsSetup(); |
| |
| // This installs an override that sends nothing but expects nonsense. |
| const uint16_t extension_code = 0xff58; |
| SECStatus rv = SSL_InstallExtensionHooks(client_->ssl_fd(), extension_code, |
| EmptyExtensionWriter, nullptr, |
| RejectExtensionHandler, nullptr); |
| EXPECT_EQ(SECSuccess, rv); |
| |
| // Have the server send nonsense. |
| rv = SSL_InstallExtensionHooks(server_->ssl_fd(), extension_code, |
| EmptyExtensionWriter, nullptr, |
| NoopExtensionHandler, nullptr); |
| EXPECT_EQ(SECSuccess, rv); |
| |
| client_->ExpectSendAlert(kTlsAlertHandshakeFailure); |
| server_->ExpectSendAlert(kTlsAlertUnexpectedMessage); |
| ConnectExpectFail(); |
| } |
| |
| static const uint8_t kCustomAlert = 0xf6; |
| |
| SECStatus AlertExtensionHandler(PRFileDesc *fd, SSLHandshakeType message, |
| const PRUint8 *data, unsigned int len, |
| SSLAlertDescription *alert, void *arg) { |
| *alert = kCustomAlert; |
| return SECFailure; |
| } |
| |
| TEST_F(TlsConnectStreamTls13, CustomExtensionServerRejectAlert) { |
| EnsureTlsSetup(); |
| |
| // This installs an override that sends nonsense. |
| const uint16_t extension_code = 0xffea; |
| SECStatus rv = SSL_InstallExtensionHooks(client_->ssl_fd(), extension_code, |
| EmptyExtensionWriter, nullptr, |
| NoopExtensionHandler, nullptr); |
| EXPECT_EQ(SECSuccess, rv); |
| |
| // Reject the extension for no good reason. |
| rv = SSL_InstallExtensionHooks(server_->ssl_fd(), extension_code, |
| NoopExtensionWriter, nullptr, |
| AlertExtensionHandler, nullptr); |
| EXPECT_EQ(SECSuccess, rv); |
| |
| ConnectExpectAlert(server_, kCustomAlert); |
| } |
| |
| // Send nonsense in an extension from client to server. |
| TEST_F(TlsConnectStreamTls13, CustomExtensionClientRejectAlert) { |
| EnsureTlsSetup(); |
| |
| // This installs an override that sends nothing but expects nonsense. |
| const uint16_t extension_code = 0xff5a; |
| SECStatus rv = SSL_InstallExtensionHooks(client_->ssl_fd(), extension_code, |
| EmptyExtensionWriter, nullptr, |
| AlertExtensionHandler, nullptr); |
| EXPECT_EQ(SECSuccess, rv); |
| |
| // Have the server send nonsense. |
| rv = SSL_InstallExtensionHooks(server_->ssl_fd(), extension_code, |
| EmptyExtensionWriter, nullptr, |
| NoopExtensionHandler, nullptr); |
| EXPECT_EQ(SECSuccess, rv); |
| |
| client_->ExpectSendAlert(kCustomAlert); |
| server_->ExpectSendAlert(kTlsAlertUnexpectedMessage); |
| ConnectExpectFail(); |
| } |
| |
| // Configure a custom extension hook badly. |
| TEST_F(TlsConnectStreamTls13, CustomExtensionOnlyWriter) { |
| EnsureTlsSetup(); |
| |
| // This installs an override that sends nothing but expects nonsense. |
| SECStatus rv = |
| SSL_InstallExtensionHooks(client_->ssl_fd(), 0xff6c, EmptyExtensionWriter, |
| nullptr, nullptr, nullptr); |
| EXPECT_EQ(SECFailure, rv); |
| EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError()); |
| } |
| |
| TEST_F(TlsConnectStreamTls13, CustomExtensionOnlyHandler) { |
| EnsureTlsSetup(); |
| |
| // This installs an override that sends nothing but expects nonsense. |
| SECStatus rv = |
| SSL_InstallExtensionHooks(client_->ssl_fd(), 0xff6d, nullptr, nullptr, |
| NoopExtensionHandler, nullptr); |
| EXPECT_EQ(SECFailure, rv); |
| EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError()); |
| } |
| |
| TEST_F(TlsConnectStreamTls13, CustomExtensionOverrunBuffer) { |
| EnsureTlsSetup(); |
| // This doesn't actually overrun the buffer, but it says that it does. |
| auto overrun_writer = [](PRFileDesc *fd, SSLHandshakeType message, |
| PRUint8 *data, unsigned int *len, |
| unsigned int maxLen, void *arg) -> PRBool { |
| *len = maxLen + 1; |
| return PR_TRUE; |
| }; |
| SECStatus rv = |
| SSL_InstallExtensionHooks(client_->ssl_fd(), 0xff71, overrun_writer, |
| nullptr, NoopExtensionHandler, nullptr); |
| EXPECT_EQ(SECSuccess, rv); |
| client_->StartConnect(); |
| client_->Handshake(); |
| client_->CheckErrorCode(SEC_ERROR_APPLICATION_CALLBACK_ERROR); |
| } |
| |
| } // namespace nss_test |