diff options
Diffstat (limited to 'net/socket')
-rw-r--r-- | net/socket/ssl_client_socket.cc | 12 | ||||
-rw-r--r-- | net/socket/ssl_client_socket.h | 20 | ||||
-rw-r--r-- | net/socket/ssl_client_socket_nss.cc | 111 | ||||
-rw-r--r-- | net/socket/ssl_client_socket_unittest.cc | 75 |
4 files changed, 152 insertions, 66 deletions
diff --git a/net/socket/ssl_client_socket.cc b/net/socket/ssl_client_socket.cc index 588b701..d96a720 100644 --- a/net/socket/ssl_client_socket.cc +++ b/net/socket/ssl_client_socket.cc @@ -17,7 +17,8 @@ SSLClientSocket::SSLClientSocket() was_spdy_negotiated_(false), protocol_negotiated_(kProtoUnknown), channel_id_sent_(false), - signed_cert_timestamps_received_(false) { + signed_cert_timestamps_received_(false), + stapled_ocsp_response_received_(false) { } // static @@ -145,15 +146,16 @@ void SSLClientSocket::set_channel_id_sent(bool channel_id_sent) { channel_id_sent_ = channel_id_sent; } -bool SSLClientSocket::WereSignedCertTimestampsReceived() const { - return signed_cert_timestamps_received_; -} - void SSLClientSocket::set_signed_cert_timestamps_received( bool signed_cert_timestamps_received) { signed_cert_timestamps_received_ = signed_cert_timestamps_received; } +void SSLClientSocket::set_stapled_ocsp_response_received( + bool stapled_ocsp_response_received) { + stapled_ocsp_response_received_ = stapled_ocsp_response_received; +} + // static void SSLClientSocket::RecordChannelIDSupport( ServerBoundCertService* server_bound_cert_service, diff --git a/net/socket/ssl_client_socket.h b/net/socket/ssl_client_socket.h index 40840e4..410062d 100644 --- a/net/socket/ssl_client_socket.h +++ b/net/socket/ssl_client_socket.h @@ -7,6 +7,7 @@ #include <string> +#include "base/gtest_prod_util.h" #include "net/base/completion_callback.h" #include "net/base/load_flags.h" #include "net/base/net_errors.h" @@ -131,18 +132,15 @@ class NET_EXPORT SSLClientSocket : public SSLSocket { // Public for ssl_client_socket_openssl_unittest.cc. virtual bool WasChannelIDSent() const; - // Returns true if the server sent Certificate Transparency SCTs - // via a TLS extension. - // Temporary glue for testing while the CT code hasn't landed. - // TODO(ekasper): expose received SCTs via SSLInfo instead. - virtual bool WereSignedCertTimestampsReceived() const; - protected: virtual void set_channel_id_sent(bool channel_id_sent); virtual void set_signed_cert_timestamps_received( bool signed_cert_timestamps_received); + virtual void set_stapled_ocsp_response_received( + bool stapled_ocsp_response_received); + // Records histograms for channel id support during full handshakes - resumed // handshakes are ignored. static void RecordChannelIDSupport( @@ -157,6 +155,14 @@ class NET_EXPORT SSLClientSocket : public SSLSocket { ServerBoundCertService* server_bound_cert_service); private: + // For signed_cert_timestamps_received_ and stapled_ocsp_response_received_. + FRIEND_TEST_ALL_PREFIXES(SSLClientSocketTest, + ConnectSignedCertTimestampsEnabledTLSExtension); + FRIEND_TEST_ALL_PREFIXES(SSLClientSocketTest, + ConnectSignedCertTimestampsEnabledOCSP); + FRIEND_TEST_ALL_PREFIXES(SSLClientSocketTest, + ConnectSignedCertTimestampsDisabled); + // True if NPN was responded to, independent of selecting SPDY or HTTP. bool was_npn_negotiated_; // True if NPN successfully negotiated SPDY. @@ -167,6 +173,8 @@ class NET_EXPORT SSLClientSocket : public SSLSocket { bool channel_id_sent_; // True if SCTs were received via a TLS extension. bool signed_cert_timestamps_received_; + // True if a stapled OCSP response was received. + bool stapled_ocsp_response_received_; }; } // namespace net diff --git a/net/socket/ssl_client_socket_nss.cc b/net/socket/ssl_client_socket_nss.cc index 0815962..dbf64af 100644 --- a/net/socket/ssl_client_socket_nss.cc +++ b/net/socket/ssl_client_socket_nss.cc @@ -93,6 +93,7 @@ #include "net/cert/asn1_util.h" #include "net/cert/cert_status_flags.h" #include "net/cert/cert_verifier.h" +#include "net/cert/ct_objects_extractor.h" #include "net/cert/ct_verifier.h" #include "net/cert/ct_verify_result.h" #include "net/cert/scoped_nss_types.h" @@ -418,6 +419,7 @@ struct HandshakeState { server_cert_chain.Reset(NULL); server_cert = NULL; sct_list_from_tls_extension.clear(); + stapled_ocsp_response.clear(); resumed_handshake = false; ssl_connection_status = 0; } @@ -449,6 +451,8 @@ struct HandshakeState { scoped_refptr<X509Certificate> server_cert; // SignedCertificateTimestampList received via TLS extension (RFC 6962). std::string sct_list_from_tls_extension; + // Stapled OCSP response received. + std::string stapled_ocsp_response; // True if the current handshake was the result of TLS session resumption. bool resumed_handshake; @@ -760,10 +764,13 @@ class SSLClientSocketNSS::Core : public base::RefCountedThreadSafe<Core> { // Updates the NSS and platform specific certificates. void UpdateServerCert(); - // Update the nss_handshake_state_ with SignedCertificateTimestampLists - // received in the handshake, via a TLS extension or (to be implemented) - // OCSP stapling. + // Update the nss_handshake_state_ with the SignedCertificateTimestampList + // received in the handshake via a TLS extension. void UpdateSignedCertTimestamps(); + // Update the OCSP response cache with the stapled response received in the + // handshake, and update nss_handshake_state_ with + // the SignedCertificateTimestampList received in the stapled OCSP response. + void UpdateStapledOCSPResponse(); // Updates the nss_handshake_state_ with the negotiated security parameters. void UpdateConnectionStatus(); // Record histograms for channel id support during full handshakes - resumed @@ -1663,6 +1670,7 @@ void SSLClientSocketNSS::Core::HandshakeSucceeded() { RecordChannelIDSupportOnNSSTaskRunner(); UpdateServerCert(); UpdateSignedCertTimestamps(); + UpdateStapledOCSPResponse(); UpdateConnectionStatus(); UpdateNextProto(); @@ -1834,43 +1842,6 @@ int SSLClientSocketNSS::Core::DoHandshake() { false_started_ = true; HandshakeSucceeded(); } - - // TODO(wtc): move this block of code to OwnAuthCertHandler. - #if defined(SSL_ENABLE_OCSP_STAPLING) - // TODO(agl): figure out how to plumb an OCSP response into the Mac - // system library and update IsOCSPStaplingSupported for Mac. - if (IsOCSPStaplingSupported()) { - const SECItemArray* ocsp_responses = - SSL_PeerStapledOCSPResponses(nss_fd_); - if (ocsp_responses->len) { - #if defined(OS_WIN) - if (nss_handshake_state_.server_cert) { - CRYPT_DATA_BLOB ocsp_response_blob; - ocsp_response_blob.cbData = ocsp_responses->items[0].len; - ocsp_response_blob.pbData = ocsp_responses->items[0].data; - BOOL ok = CertSetCertificateContextProperty( - nss_handshake_state_.server_cert->os_cert_handle(), - CERT_OCSP_RESPONSE_PROP_ID, - CERT_SET_PROPERTY_IGNORE_PERSIST_ERROR_FLAG, - &ocsp_response_blob); - if (!ok) { - VLOG(1) << "Failed to set OCSP response property: " - << GetLastError(); - } - } - #elif defined(USE_NSS) - CacheOCSPResponseFromSideChannelFunction cache_ocsp_response = - GetCacheOCSPResponseFromSideChannelFunction(); - - cache_ocsp_response( - CERT_GetDefaultCertDB(), - nss_handshake_state_.server_cert_chain[0], PR_Now(), - &ocsp_responses->items[0], NULL); - #endif - } - } - #endif - // Done! } else { PRErrorCode prerr = PR_GetError(); net_error = HandleNSSError(prerr, true); @@ -2436,6 +2407,46 @@ void SSLClientSocketNSS::Core::UpdateSignedCertTimestamps() { signed_cert_timestamps->len); } +void SSLClientSocketNSS::Core::UpdateStapledOCSPResponse() { + const SECItemArray* ocsp_responses = + SSL_PeerStapledOCSPResponses(nss_fd_); + if (!ocsp_responses || !ocsp_responses->len) + return; + + nss_handshake_state_.stapled_ocsp_response = std::string( + reinterpret_cast<char*>(ocsp_responses->items[0].data), + ocsp_responses->items[0].len); + + // TODO(agl): figure out how to plumb an OCSP response into the Mac + // system library and update IsOCSPStaplingSupported for Mac. + if (IsOCSPStaplingSupported()) { + #if defined(OS_WIN) + if (nss_handshake_state_.server_cert) { + CRYPT_DATA_BLOB ocsp_response_blob; + ocsp_response_blob.cbData = ocsp_responses->items[0].len; + ocsp_response_blob.pbData = ocsp_responses->items[0].data; + BOOL ok = CertSetCertificateContextProperty( + nss_handshake_state_.server_cert->os_cert_handle(), + CERT_OCSP_RESPONSE_PROP_ID, + CERT_SET_PROPERTY_IGNORE_PERSIST_ERROR_FLAG, + &ocsp_response_blob); + if (!ok) { + VLOG(1) << "Failed to set OCSP response property: " + << GetLastError(); + } + } + #elif defined(USE_NSS) + CacheOCSPResponseFromSideChannelFunction cache_ocsp_response = + GetCacheOCSPResponseFromSideChannelFunction(); + + cache_ocsp_response( + CERT_GetDefaultCertDB(), + nss_handshake_state_.server_cert_chain[0], PR_Now(), + &ocsp_responses->items[0], NULL); + #endif + } // IsOCSPStaplingSupported() +} + void SSLClientSocketNSS::Core::UpdateConnectionStatus() { SSLChannelInfo channel_info; SECStatus ok = SSL_GetChannelInfo(nss_fd_, @@ -3202,12 +3213,14 @@ int SSLClientSocketNSS::InitializeSSLOptions() { // Added in NSS 3.15 #ifdef SSL_ENABLE_OCSP_STAPLING - if (IsOCSPStaplingSupported()) { - rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_OCSP_STAPLING, PR_TRUE); - if (rv != SECSuccess) { - LogFailedNSSFunction(net_log_, "SSL_OptionSet", - "SSL_ENABLE_OCSP_STAPLING"); - } + // Request OCSP stapling even on platforms that don't support it, in + // order to extract Certificate Transparency information. + rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_OCSP_STAPLING, + (IsOCSPStaplingSupported() || + ssl_config_.signed_cert_timestamps_enabled)); + if (rv != SECSuccess) { + LogFailedNSSFunction(net_log_, "SSL_OptionSet", + "SSL_ENABLE_OCSP_STAPLING"); } #endif @@ -3365,6 +3378,8 @@ int SSLClientSocketNSS::DoHandshakeComplete(int result) { set_channel_id_sent(core_->state().channel_id_sent); set_signed_cert_timestamps_received( !core_->state().sct_list_from_tls_extension.empty()); + set_stapled_ocsp_response_received( + !core_->state().stapled_ocsp_response.empty()); LeaveFunction(result); return result; @@ -3522,10 +3537,12 @@ void SSLClientSocketNSS::VerifyCT() { // external communication. int result = cert_transparency_verifier_->Verify( server_cert_verify_result_.verified_cert, - std::string(), // SCT list from OCSP response + core_->state().stapled_ocsp_response, core_->state().sct_list_from_tls_extension, &ct_verify_result_, net_log_); + // TODO(ekasper): wipe stapled_ocsp_response and sct_list_from_tls_extension + // from the state after verification is complete, to conserve memory. VLOG(1) << "CT Verification complete: result " << result << " Invalid scts: " << ct_verify_result_.invalid_scts.size() diff --git a/net/socket/ssl_client_socket_unittest.cc b/net/socket/ssl_client_socket_unittest.cc index 0e667c6..6d4b118 100644 --- a/net/socket/ssl_client_socket_unittest.cc +++ b/net/socket/ssl_client_socket_unittest.cc @@ -1793,9 +1793,11 @@ TEST_F(SSLClientSocketCertRequestInfoTest, TwoAuthorities) { request_info->cert_authorities[1]); } -TEST_F(SSLClientSocketTest, ConnectSignedCertTimestampsEnabled) { +} // namespace + +TEST_F(SSLClientSocketTest, ConnectSignedCertTimestampsEnabledTLSExtension) { SpawnedTestServer::SSLOptions ssl_options; - ssl_options.signed_cert_timestamps = "test"; + ssl_options.signed_cert_timestamps_tls_ext = "test"; SpawnedTestServer test_server(SpawnedTestServer::TYPE_HTTPS, ssl_options, @@ -1835,10 +1837,69 @@ TEST_F(SSLClientSocketTest, ConnectSignedCertTimestampsEnabled) { EXPECT_TRUE(LogContainsSSLConnectEndEvent(entries, -1)); #if !defined(USE_OPENSSL) - EXPECT_TRUE(sock->WereSignedCertTimestampsReceived()); + EXPECT_TRUE(sock->signed_cert_timestamps_received_); #else // Enabling CT for OpenSSL is currently a noop. - EXPECT_FALSE(sock->WereSignedCertTimestampsReceived()); + EXPECT_FALSE(sock->signed_cert_timestamps_received_); +#endif + + sock->Disconnect(); + EXPECT_FALSE(sock->IsConnected()); +} + +// Test that enabling Signed Certificate Timestamps enables OCSP stapling. +TEST_F(SSLClientSocketTest, ConnectSignedCertTimestampsEnabledOCSP) { + SpawnedTestServer::SSLOptions ssl_options; + ssl_options.staple_ocsp_response = true; + // The test server currently only knows how to generate OCSP responses + // for a freshly minted certificate. + ssl_options.server_certificate = SpawnedTestServer::SSLOptions::CERT_AUTO; + + SpawnedTestServer test_server(SpawnedTestServer::TYPE_HTTPS, + ssl_options, + base::FilePath()); + ASSERT_TRUE(test_server.Start()); + + AddressList addr; + ASSERT_TRUE(test_server.GetAddressList(&addr)); + + TestCompletionCallback callback; + CapturingNetLog log; + scoped_ptr<StreamSocket> transport( + new TCPClientSocket(addr, &log, NetLog::Source())); + int rv = transport->Connect(callback.callback()); + if (rv == ERR_IO_PENDING) + rv = callback.WaitForResult(); + EXPECT_EQ(OK, rv); + + SSLConfig ssl_config; + // Enabling Signed Cert Timestamps ensures we request OCSP stapling for + // Certificate Transparency verification regardless of whether the platform + // is able to process the OCSP status itself. + ssl_config.signed_cert_timestamps_enabled = true; + + scoped_ptr<SSLClientSocket> sock(CreateSSLClientSocket( + transport.Pass(), test_server.host_port_pair(), ssl_config)); + + EXPECT_FALSE(sock->IsConnected()); + + rv = sock->Connect(callback.callback()); + + CapturingNetLog::CapturedEntryList entries; + log.GetEntries(&entries); + EXPECT_TRUE(LogContainsBeginEvent(entries, 5, NetLog::TYPE_SSL_CONNECT)); + if (rv == ERR_IO_PENDING) + rv = callback.WaitForResult(); + EXPECT_EQ(OK, rv); + EXPECT_TRUE(sock->IsConnected()); + log.GetEntries(&entries); + EXPECT_TRUE(LogContainsSSLConnectEndEvent(entries, -1)); + +#if !defined(USE_OPENSSL) + EXPECT_TRUE(sock->stapled_ocsp_response_received_); +#else + // OCSP stapling isn't currently supported in the OpenSSL socket. + EXPECT_FALSE(sock->stapled_ocsp_response_received_); #endif sock->Disconnect(); @@ -1847,7 +1908,7 @@ TEST_F(SSLClientSocketTest, ConnectSignedCertTimestampsEnabled) { TEST_F(SSLClientSocketTest, ConnectSignedCertTimestampsDisabled) { SpawnedTestServer::SSLOptions ssl_options; - ssl_options.signed_cert_timestamps = "test"; + ssl_options.signed_cert_timestamps_tls_ext = "test"; SpawnedTestServer test_server(SpawnedTestServer::TYPE_HTTPS, ssl_options, @@ -1886,12 +1947,10 @@ TEST_F(SSLClientSocketTest, ConnectSignedCertTimestampsDisabled) { log.GetEntries(&entries); EXPECT_TRUE(LogContainsSSLConnectEndEvent(entries, -1)); - EXPECT_FALSE(sock->WereSignedCertTimestampsReceived()); + EXPECT_FALSE(sock->signed_cert_timestamps_received_); sock->Disconnect(); EXPECT_FALSE(sock->IsConnected()); } -} // namespace - } // namespace net |