diff options
author | rch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-08-14 01:00:11 +0000 |
---|---|---|
committer | rch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-08-14 01:00:11 +0000 |
commit | 0b620f4b8d4e0b4f0bb5d76a78b0aca431b9eb75 (patch) | |
tree | 7a728b838a7028eca227949722fc66b9e9e1ce3b /net | |
parent | 9d363dd55297354292489000b2bdf5623bca8d63 (diff) | |
download | chromium_src-0b620f4b8d4e0b4f0bb5d76a78b0aca431b9eb75.zip chromium_src-0b620f4b8d4e0b4f0bb5d76a78b0aca431b9eb75.tar.gz chromium_src-0b620f4b8d4e0b4f0bb5d76a78b0aca431b9eb75.tar.bz2 |
Refactor pooling logic into a helper method
Disable pooling when there are cert errors.
Disable pooling when pinning does not match for the new host.
BUG=398925
Review URL: https://codereview.chromium.org/425803014
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@289433 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r-- | net/http/http_network_session.cc | 1 | ||||
-rw-r--r-- | net/quic/quic_client_session.cc | 28 | ||||
-rw-r--r-- | net/quic/quic_client_session.h | 3 | ||||
-rw-r--r-- | net/quic/quic_client_session_test.cc | 64 | ||||
-rw-r--r-- | net/quic/quic_http_stream_test.cc | 3 | ||||
-rw-r--r-- | net/quic/quic_stream_factory.cc | 4 | ||||
-rw-r--r-- | net/quic/quic_stream_factory.h | 1 | ||||
-rw-r--r-- | net/quic/quic_stream_factory_test.cc | 6 | ||||
-rw-r--r-- | net/socket/ssl_client_socket_pool_unittest.cc | 3 | ||||
-rw-r--r-- | net/spdy/spdy_session.cc | 55 | ||||
-rw-r--r-- | net/spdy/spdy_session.h | 11 | ||||
-rw-r--r-- | net/spdy/spdy_session_pool.cc | 3 | ||||
-rw-r--r-- | net/spdy/spdy_session_pool.h | 4 | ||||
-rw-r--r-- | net/spdy/spdy_session_unittest.cc | 110 | ||||
-rw-r--r-- | net/spdy/spdy_test_utils.cc | 33 | ||||
-rw-r--r-- | net/spdy/spdy_test_utils.h | 18 |
16 files changed, 292 insertions, 55 deletions
diff --git a/net/http/http_network_session.cc b/net/http/http_network_session.cc index b48d660..d1a7236 100644 --- a/net/http/http_network_session.cc +++ b/net/http/http_network_session.cc @@ -135,6 +135,7 @@ HttpNetworkSession::HttpNetworkSession(const Params& params) spdy_session_pool_(params.host_resolver, params.ssl_config_service, params.http_server_properties, + params.transport_security_state, params.force_spdy_single_domain, params.enable_spdy_compression, params.enable_spdy_ping_based_connection_checking, diff --git a/net/quic/quic_client_session.cc b/net/quic/quic_client_session.cc index c6699f1..b136724 100644 --- a/net/quic/quic_client_session.cc +++ b/net/quic/quic_client_session.cc @@ -13,6 +13,7 @@ #include "base/values.h" #include "net/base/io_buffer.h" #include "net/base/net_errors.h" +#include "net/http/transport_security_state.h" #include "net/quic/crypto/proof_verifier_chromium.h" #include "net/quic/crypto/quic_server_info.h" #include "net/quic/quic_connection_helper.h" @@ -20,6 +21,7 @@ #include "net/quic/quic_default_packet_writer.h" #include "net/quic/quic_server_id.h" #include "net/quic/quic_stream_factory.h" +#include "net/spdy/spdy_session.h" #include "net/ssl/channel_id_service.h" #include "net/ssl/ssl_connection_status_flags.h" #include "net/ssl/ssl_info.h" @@ -138,6 +140,7 @@ QuicClientSession::QuicClientSession( scoped_ptr<QuicDefaultPacketWriter> writer, QuicStreamFactory* stream_factory, QuicCryptoClientStreamFactory* crypto_client_stream_factory, + TransportSecurityState* transport_security_state, scoped_ptr<QuicServerInfo> server_info, const QuicServerId& server_id, const QuicConfig& config, @@ -151,6 +154,7 @@ QuicClientSession::QuicClientSession( socket_(socket.Pass()), writer_(writer.Pass()), read_buffer_(new IOBufferWithSize(kMaxPacketSize)), + transport_security_state_(transport_security_state), server_info_(server_info.Pass()), read_pending_(false), num_total_streams_(0), @@ -489,28 +493,8 @@ bool QuicClientSession::CanPool(const std::string& hostname) const { return true; } - // Disable pooling for secure sessions. - // TODO(rch): re-enable this. - return false; -#if 0 - bool unused = false; - // Pooling is prohibited if the server cert is not valid for the new domain, - // and for connections on which client certs were sent. It is also prohibited - // when channel ID was sent if the hosts are from different eTLDs+1. - if (!ssl_info.cert->VerifyNameMatch(hostname, &unused)) - return false; - - if (ssl_info.client_cert_sent) - return false; - - if (ssl_info.channel_id_sent && - ChannelIDService::GetDomainForHost(hostname) != - ChannelIDService::GetDomainForHost(server_host_port_.host())) { - return false; - } - - return true; -#endif + return SpdySession::CanPool(transport_security_state_, ssl_info, + server_host_port_.host(), hostname); } QuicDataStream* QuicClientSession::CreateIncomingDataStream( diff --git a/net/quic/quic_client_session.h b/net/quic/quic_client_session.h index 6112e1e..9808cac 100644 --- a/net/quic/quic_client_session.h +++ b/net/quic/quic_client_session.h @@ -35,6 +35,7 @@ class QuicServerId; class QuicServerInfo; class QuicStreamFactory; class SSLInfo; +class TransportSecurityState; namespace test { class QuicClientSessionPeer; @@ -95,6 +96,7 @@ class NET_EXPORT_PRIVATE QuicClientSession : public QuicClientSessionBase { scoped_ptr<QuicDefaultPacketWriter> writer, QuicStreamFactory* stream_factory, QuicCryptoClientStreamFactory* crypto_client_stream_factory, + TransportSecurityState* transport_security_state, scoped_ptr<QuicServerInfo> server_info, const QuicServerId& server_id, const QuicConfig& config, @@ -226,6 +228,7 @@ class NET_EXPORT_PRIVATE QuicClientSession : public QuicClientSessionBase { scoped_ptr<DatagramClientSocket> socket_; scoped_ptr<QuicDefaultPacketWriter> writer_; scoped_refptr<IOBufferWithSize> read_buffer_; + TransportSecurityState* transport_security_state_; scoped_ptr<QuicServerInfo> server_info_; scoped_ptr<CertVerifyResult> cert_verify_result_; std::string pinning_failure_log_; diff --git a/net/quic/quic_client_session_test.cc b/net/quic/quic_client_session_test.cc index ae1aea6..1e13e89 100644 --- a/net/quic/quic_client_session_test.cc +++ b/net/quic/quic_client_session_test.cc @@ -6,12 +6,14 @@ #include <vector> +#include "base/base64.h" #include "base/files/file_path.h" #include "base/rand_util.h" #include "net/base/capturing_net_log.h" #include "net/base/test_completion_callback.h" #include "net/base/test_data_directory.h" #include "net/cert/cert_verify_result.h" +#include "net/http/transport_security_state.h" #include "net/quic/crypto/aes_128_gcm_12_encrypter.h" #include "net/quic/crypto/crypto_protocol.h" #include "net/quic/crypto/proof_verifier_chromium.h" @@ -24,6 +26,7 @@ #include "net/quic/test_tools/quic_test_utils.h" #include "net/quic/test_tools/simple_quic_framer.h" #include "net/socket/socket_test_util.h" +#include "net/spdy/spdy_test_utils.h" #include "net/test/cert_test_util.h" #include "net/udp/datagram_client_socket.h" @@ -73,6 +76,7 @@ class QuicClientSessionTest : public ::testing::TestWithParam<QuicVersion> { connection_( new PacketSavingConnection(false, SupportedVersions(GetParam()))), session_(connection_, GetSocket().Pass(), writer_.Pass(), NULL, NULL, + &transport_security_state_, make_scoped_ptr((QuicServerInfo*)NULL), QuicServerId(kServerHostname, kServerPort, false, PRIVACY_MODE_DISABLED), @@ -108,6 +112,7 @@ class QuicClientSessionTest : public ::testing::TestWithParam<QuicVersion> { CapturingNetLog net_log_; MockClientSocketFactory socket_factory_; StaticSocketDataProvider socket_data_; + TransportSecurityState transport_security_state_; QuicClientSession session_; MockClock clock_; MockRandom random_; @@ -172,18 +177,15 @@ TEST_P(QuicClientSessionTest, GoAwayReceived) { EXPECT_EQ(NULL, session_.CreateOutgoingDataStream()); } -// TODO(rch): re-enable this. -TEST_P(QuicClientSessionTest, DISABLED_CanPool) { +TEST_P(QuicClientSessionTest, CanPool) { // Load a cert that is valid for: // www.example.org // mail.example.org // www.example.com - base::FilePath certs_dir = GetTestCertsDirectory(); - CertVerifyResult result; ProofVerifyDetailsChromium details; details.cert_verify_result.verified_cert = - ImportCertFromFile(certs_dir, "spdy_pooling.pem"); + ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem"); ASSERT_TRUE(details.cert_verify_result.verified_cert); session_.OnProofVerifyDetailsAvailable(details); @@ -196,18 +198,15 @@ TEST_P(QuicClientSessionTest, DISABLED_CanPool) { EXPECT_FALSE(session_.CanPool("mail.google.com")); } -// TODO(rch): re-enable this. -TEST_P(QuicClientSessionTest, DISABLED_ConnectionPooledWithTlsChannelId) { +TEST_P(QuicClientSessionTest, ConnectionPooledWithTlsChannelId) { // Load a cert that is valid for: // www.example.org // mail.example.org // www.example.com - base::FilePath certs_dir = GetTestCertsDirectory(); - CertVerifyResult result; ProofVerifyDetailsChromium details; details.cert_verify_result.verified_cert = - ImportCertFromFile(certs_dir, "spdy_pooling.pem"); + ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem"); ASSERT_TRUE(details.cert_verify_result.verified_cert); session_.OnProofVerifyDetailsAvailable(details); @@ -220,6 +219,51 @@ TEST_P(QuicClientSessionTest, DISABLED_ConnectionPooledWithTlsChannelId) { EXPECT_FALSE(session_.CanPool("mail.google.com")); } +TEST_P(QuicClientSessionTest, ConnectionNotPooledWithDifferentPin) { + uint8 primary_pin = 1; + uint8 backup_pin = 2; + uint8 bad_pin = 3; + AddPin(&transport_security_state_, "mail.example.org", primary_pin, + backup_pin); + + ProofVerifyDetailsChromium details; + details.cert_verify_result.verified_cert = + ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem"); + details.cert_verify_result.is_issued_by_known_root = true; + details.cert_verify_result.public_key_hashes.push_back( + GetTestHashValue(bad_pin)); + + ASSERT_TRUE(details.cert_verify_result.verified_cert); + + session_.OnProofVerifyDetailsAvailable(details); + CompleteCryptoHandshake(); + QuicClientSessionPeer::SetChannelIDSent(&session_, true); + + EXPECT_FALSE(session_.CanPool("mail.example.org")); +} + +TEST_P(QuicClientSessionTest, ConnectionPooledWithMatchingPin) { + uint8 primary_pin = 1; + uint8 backup_pin = 2; + AddPin(&transport_security_state_, "mail.example.org", primary_pin, + backup_pin); + + ProofVerifyDetailsChromium details; + details.cert_verify_result.verified_cert = + ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem"); + details.cert_verify_result.is_issued_by_known_root = true; + details.cert_verify_result.public_key_hashes.push_back( + GetTestHashValue(primary_pin)); + + ASSERT_TRUE(details.cert_verify_result.verified_cert); + + session_.OnProofVerifyDetailsAvailable(details); + CompleteCryptoHandshake(); + QuicClientSessionPeer::SetChannelIDSent(&session_, true); + + EXPECT_TRUE(session_.CanPool("mail.example.org")); +} + } // namespace } // namespace test } // namespace net diff --git a/net/quic/quic_http_stream_test.cc b/net/quic/quic_http_stream_test.cc index 5e46258..932f566 100644 --- a/net/quic/quic_http_stream_test.cc +++ b/net/quic/quic_http_stream_test.cc @@ -11,6 +11,7 @@ #include "net/base/upload_bytes_element_reader.h" #include "net/base/upload_data_stream.h" #include "net/http/http_response_headers.h" +#include "net/http/transport_security_state.h" #include "net/quic/congestion_control/receive_algorithm_interface.h" #include "net/quic/congestion_control/send_algorithm_interface.h" #include "net/quic/crypto/crypto_protocol.h" @@ -214,6 +215,7 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<QuicVersion> { scoped_ptr<DatagramClientSocket>(socket), writer_.Pass(), NULL, &crypto_client_stream_factory_, + &transport_security_state_, make_scoped_ptr((QuicServerInfo*)NULL), QuicServerId(kServerHostname, kServerPort, false, PRIVACY_MODE_DISABLED), @@ -299,6 +301,7 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<QuicVersion> { testing::StrictMock<MockConnectionVisitor> visitor_; scoped_ptr<QuicHttpStream> stream_; scoped_ptr<QuicDefaultPacketWriter> writer_; + TransportSecurityState transport_security_state_; scoped_ptr<QuicClientSession> session_; QuicCryptoClientConfig crypto_config_; TestCompletionCallback callback_; diff --git a/net/quic/quic_stream_factory.cc b/net/quic/quic_stream_factory.cc index ea30b2e..1baf4c8 100644 --- a/net/quic/quic_stream_factory.cc +++ b/net/quic/quic_stream_factory.cc @@ -861,8 +861,8 @@ int QuicStreamFactory::CreateSession( *session = new QuicClientSession( connection, socket.Pass(), writer.Pass(), this, - quic_crypto_client_stream_factory_, server_info.Pass(), server_id, - config, &crypto_config_, + quic_crypto_client_stream_factory_, transport_security_state_, + server_info.Pass(), server_id, config, &crypto_config_, base::MessageLoop::current()->message_loop_proxy().get(), net_log.net_log()); (*session)->InitializeSession(); diff --git a/net/quic/quic_stream_factory.h b/net/quic/quic_stream_factory.h index 574bf6f..262e276 100644 --- a/net/quic/quic_stream_factory.h +++ b/net/quic/quic_stream_factory.h @@ -238,6 +238,7 @@ class NET_EXPORT_PRIVATE QuicStreamFactory HostResolver* host_resolver_; ClientSocketFactory* client_socket_factory_; base::WeakPtr<HttpServerProperties> http_server_properties_; + TransportSecurityState* transport_security_state_; QuicServerInfoFactory* quic_server_info_factory_; QuicCryptoClientStreamFactory* quic_crypto_client_stream_factory_; QuicRandom* random_generator_; diff --git a/net/quic/quic_stream_factory_test.cc b/net/quic/quic_stream_factory_test.cc index df66134..59f97f5 100644 --- a/net/quic/quic_stream_factory_test.cc +++ b/net/quic/quic_stream_factory_test.cc @@ -360,8 +360,7 @@ TEST_P(QuicStreamFactoryTest, CreateHttpVsHttps) { EXPECT_TRUE(socket_data2.at_write_eof()); } -// TODO(rch): re-enable this. -TEST_P(QuicStreamFactoryTest, DISABLED_Pooling) { +TEST_P(QuicStreamFactoryTest, Pooling) { MockRead reads[] = { MockRead(ASYNC, OK, 0) // EOF }; @@ -477,8 +476,7 @@ TEST_P(QuicStreamFactoryTest, NoPoolingAfterGoAway) { EXPECT_TRUE(socket_data2.at_write_eof()); } -// TODO(rch): re-enable this. -TEST_P(QuicStreamFactoryTest, DISABLED_HttpsPooling) { +TEST_P(QuicStreamFactoryTest, HttpsPooling) { MockRead reads[] = { MockRead(ASYNC, OK, 0) // EOF }; diff --git a/net/socket/ssl_client_socket_pool_unittest.cc b/net/socket/ssl_client_socket_pool_unittest.cc index 14b0662..ad59889 100644 --- a/net/socket/ssl_client_socket_pool_unittest.cc +++ b/net/socket/ssl_client_socket_pool_unittest.cc @@ -1257,8 +1257,7 @@ TEST_P(SSLClientSocketPoolTest, NeedProxyAuth) { EXPECT_FALSE(tunnel_handle->socket()->IsConnected()); } -// TODO(rch): re-enable this. -TEST_P(SSLClientSocketPoolTest, DISABLED_IPPooling) { +TEST_P(SSLClientSocketPoolTest, IPPooling) { const int kTestPort = 80; struct TestHosts { std::string name; diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc index 64e2c04..51c6ee7 100644 --- a/net/spdy/spdy_session.cc +++ b/net/spdy/spdy_session.cc @@ -29,10 +29,12 @@ #include "net/base/net_log.h" #include "net/base/net_util.h" #include "net/cert/asn1_util.h" +#include "net/cert/cert_verify_result.h" #include "net/http/http_log_util.h" #include "net/http/http_network_session.h" #include "net/http/http_server_properties.h" #include "net/http/http_util.h" +#include "net/http/transport_security_state.h" #include "net/spdy/spdy_buffer_producer.h" #include "net/spdy/spdy_frame_builder.h" #include "net/spdy/spdy_http_utils.h" @@ -529,9 +531,47 @@ SpdySession::PushedStreamInfo::PushedStreamInfo( SpdySession::PushedStreamInfo::~PushedStreamInfo() {} +// static +bool SpdySession::CanPool(TransportSecurityState* transport_security_state, + const SSLInfo& ssl_info, + const std::string& old_hostname, + const std::string& new_hostname) { + // Pooling is prohibited if the server cert is not valid for the new domain, + // and for connections on which client certs were sent. It is also prohibited + // when channel ID was sent if the hosts are from different eTLDs+1. + if (IsCertStatusError(ssl_info.cert_status)) + return false; + + if (ssl_info.client_cert_sent) + return false; + + if (ssl_info.channel_id_sent && + ChannelIDService::GetDomainForHost(new_hostname) != + ChannelIDService::GetDomainForHost(old_hostname)) { + return false; + } + + bool unused = false; + if (!ssl_info.cert->VerifyNameMatch(new_hostname, &unused)) + return false; + + std::string pinning_failure_log; + if (!transport_security_state->CheckPublicKeyPins( + new_hostname, + true, /* sni_available */ + ssl_info.is_issued_by_known_root, + ssl_info.public_key_hashes, + &pinning_failure_log)) { + return false; + } + + return true; +} + SpdySession::SpdySession( const SpdySessionKey& spdy_session_key, const base::WeakPtr<HttpServerProperties>& http_server_properties, + TransportSecurityState* transport_security_state, bool verify_domain_authentication, bool enable_sending_initial_data, bool enable_compression, @@ -547,6 +587,7 @@ SpdySession::SpdySession( spdy_session_key_(spdy_session_key), pool_(NULL), http_server_properties_(http_server_properties), + transport_security_state_(transport_security_state), read_buffer_(new IOBuffer(kReadBufferSize)), stream_hi_water_mark_(kFirstStreamId), num_pushed_streams_(0u), @@ -714,18 +755,8 @@ bool SpdySession::VerifyDomainAuthentication(const std::string& domain) { if (!GetSSLInfo(&ssl_info, &was_npn_negotiated, &protocol_negotiated)) return true; // This is not a secure session, so all domains are okay. - // Disable pooling for secure sessions. - // TODO(rch): re-enable this. - return false; -#if 0 - bool unused = false; - return - !ssl_info.client_cert_sent && - (!ssl_info.channel_id_sent || - (ChannelIDService::GetDomainForHost(domain) == - ChannelIDService::GetDomainForHost(host_port_pair().host()))) && - ssl_info.cert->VerifyNameMatch(domain, &unused); -#endif + return CanPool(transport_security_state_, ssl_info, + host_port_pair().host(), domain); } int SpdySession::GetPushStream( diff --git a/net/spdy/spdy_session.h b/net/spdy/spdy_session.h index 4037e03..d2da863 100644 --- a/net/spdy/spdy_session.h +++ b/net/spdy/spdy_session.h @@ -70,6 +70,7 @@ class BoundNetLog; struct LoadTimingInfo; class SpdyStream; class SSLInfo; +class TransportSecurityState; // NOTE: There's an enum of the same name (also with numeric suffixes) // in histograms.xml. Be sure to add new values there also. @@ -222,6 +223,13 @@ class NET_EXPORT SpdySession : public BufferedSpdyFramerVisitorInterface, FLOW_CONTROL_STREAM_AND_SESSION }; + // Returns true if |hostname| can be pooled into an existing connection + // associated with |ssl_info|. + static bool CanPool(TransportSecurityState* transport_security_state, + const SSLInfo& ssl_info, + const std::string& old_hostname, + const std::string& new_hostname); + // Create a new SpdySession. // |spdy_session_key| is the host/port that this session connects to, privacy // and proxy configuration settings that it's using. @@ -229,6 +237,7 @@ class NET_EXPORT SpdySession : public BufferedSpdyFramerVisitorInterface, // network events to. SpdySession(const SpdySessionKey& spdy_session_key, const base::WeakPtr<HttpServerProperties>& http_server_properties, + TransportSecurityState* transport_security_state, bool verify_domain_authentication, bool enable_sending_initial_data, bool enable_compression, @@ -963,6 +972,8 @@ class NET_EXPORT SpdySession : public BufferedSpdyFramerVisitorInterface, SpdySessionPool* pool_; const base::WeakPtr<HttpServerProperties> http_server_properties_; + TransportSecurityState* transport_security_state_; + // The socket handle for this session. scoped_ptr<ClientSocketHandle> connection_; diff --git a/net/spdy/spdy_session_pool.cc b/net/spdy/spdy_session_pool.cc index 189c945..d839785 100644 --- a/net/spdy/spdy_session_pool.cc +++ b/net/spdy/spdy_session_pool.cc @@ -31,6 +31,7 @@ SpdySessionPool::SpdySessionPool( HostResolver* resolver, SSLConfigService* ssl_config_service, const base::WeakPtr<HttpServerProperties>& http_server_properties, + TransportSecurityState* transport_security_state, bool force_single_domain, bool enable_compression, bool enable_ping_based_connection_checking, @@ -41,6 +42,7 @@ SpdySessionPool::SpdySessionPool( SpdySessionPool::TimeFunc time_func, const std::string& trusted_spdy_proxy) : http_server_properties_(http_server_properties), + transport_security_state_(transport_security_state), ssl_config_service_(ssl_config_service), resolver_(resolver), verify_domain_authentication_(true), @@ -98,6 +100,7 @@ base::WeakPtr<SpdySession> SpdySessionPool::CreateAvailableSessionFromSocket( scoped_ptr<SpdySession> new_session( new SpdySession(key, http_server_properties_, + transport_security_state_, verify_domain_authentication_, enable_sending_initial_data_, enable_compression_, diff --git a/net/spdy/spdy_session_pool.h b/net/spdy/spdy_session_pool.h index 0fad5d2..2fbb030 100644 --- a/net/spdy/spdy_session_pool.h +++ b/net/spdy/spdy_session_pool.h @@ -34,6 +34,7 @@ class ClientSocketHandle; class HostResolver; class HttpServerProperties; class SpdySession; +class TransportSecurityState; // This is a very simple pool for open SpdySessions. class NET_EXPORT SpdySessionPool @@ -50,6 +51,7 @@ class NET_EXPORT SpdySessionPool HostResolver* host_resolver, SSLConfigService* ssl_config_service, const base::WeakPtr<HttpServerProperties>& http_server_properties, + TransportSecurityState* transport_security_state, bool force_single_domain, bool enable_compression, bool enable_ping_based_connection_checking, @@ -191,6 +193,8 @@ class NET_EXPORT SpdySessionPool const base::WeakPtr<HttpServerProperties> http_server_properties_; + TransportSecurityState* transport_security_state_; + // The set of all sessions. This is a superset of the sessions in // |available_sessions_|. // diff --git a/net/spdy/spdy_session_unittest.cc b/net/spdy/spdy_session_unittest.cc index 1fa5f2e..8194fe7 100644 --- a/net/spdy/spdy_session_unittest.cc +++ b/net/spdy/spdy_session_unittest.cc @@ -4,6 +4,7 @@ #include "net/spdy/spdy_session.h" +#include "base/base64.h" #include "base/bind.h" #include "base/callback.h" #include "base/memory/scoped_ptr.h" @@ -2375,7 +2376,7 @@ TEST_P(SpdySessionTest, CloseActivatedStreamThatClosesSession) { EXPECT_TRUE(session == NULL); } -TEST_P(SpdySessionTest, DISABLED_VerifyDomainAuthentication) { +TEST_P(SpdySessionTest, VerifyDomainAuthentication) { session_deps_.host_resolver->set_synchronous_mode(true); MockConnect connect_data(SYNCHRONOUS, OK); @@ -2417,8 +2418,7 @@ TEST_P(SpdySessionTest, DISABLED_VerifyDomainAuthentication) { EXPECT_FALSE(session->VerifyDomainAuthentication("mail.google.com")); } -// TODO(rch): re-enable this. -TEST_P(SpdySessionTest, DISABLED_ConnectionPooledWithTlsChannelId) { +TEST_P(SpdySessionTest, ConnectionPooledWithTlsChannelId) { session_deps_.host_resolver->set_synchronous_mode(true); MockConnect connect_data(SYNCHRONOUS, OK); @@ -5001,4 +5001,108 @@ TEST(MapNetErrorToGoAwayStatus, MapsValue) { CHECK_EQ(GOAWAY_PROTOCOL_ERROR, MapNetErrorToGoAwayStatus(ERR_UNEXPECTED)); } +TEST(CanPoolTest, CanPool) { + // Load a cert that is valid for: + // www.example.org + // mail.example.org + // www.example.com + + TransportSecurityState tss; + SSLInfo ssl_info; + ssl_info.cert = ImportCertFromFile(GetTestCertsDirectory(), + "spdy_pooling.pem"); + + EXPECT_TRUE(SpdySession::CanPool( + &tss, ssl_info, "www.example.org", "www.example.org")); + EXPECT_TRUE(SpdySession::CanPool( + &tss, ssl_info, "www.example.org", "mail.example.org")); + EXPECT_TRUE(SpdySession::CanPool( + &tss, ssl_info, "www.example.org", "mail.example.com")); + EXPECT_FALSE(SpdySession::CanPool( + &tss, ssl_info, "www.example.org", "mail.google.com")); +} + +TEST(CanPoolTest, CanNotPoolWithCertErrors) { + // Load a cert that is valid for: + // www.example.org + // mail.example.org + // www.example.com + + TransportSecurityState tss; + SSLInfo ssl_info; + ssl_info.cert = ImportCertFromFile(GetTestCertsDirectory(), + "spdy_pooling.pem"); + ssl_info.cert_status = CERT_STATUS_REVOKED; + + EXPECT_FALSE(SpdySession::CanPool( + &tss, ssl_info, "www.example.org", "mail.example.org")); +} + +TEST(CanPoolTest, CanNotPoolWithClientCerts) { + // Load a cert that is valid for: + // www.example.org + // mail.example.org + // www.example.com + + TransportSecurityState tss; + SSLInfo ssl_info; + ssl_info.cert = ImportCertFromFile(GetTestCertsDirectory(), + "spdy_pooling.pem"); + ssl_info.client_cert_sent = true; + + EXPECT_FALSE(SpdySession::CanPool( + &tss, ssl_info, "www.example.org", "mail.example.org")); +} + +TEST(CanPoolTest, CanNotPoolAcrossETLDsWithChannelID) { + // Load a cert that is valid for: + // www.example.org + // mail.example.org + // www.example.com + + TransportSecurityState tss; + SSLInfo ssl_info; + ssl_info.cert = ImportCertFromFile(GetTestCertsDirectory(), + "spdy_pooling.pem"); + ssl_info.channel_id_sent = true; + + EXPECT_TRUE(SpdySession::CanPool( + &tss, ssl_info, "www.example.org", "mail.example.org")); + EXPECT_FALSE(SpdySession::CanPool( + &tss, ssl_info, "www.example.org", "www.example.com")); +} + +TEST(CanPoolTest, CanNotPoolWithBadPins) { + uint8 primary_pin = 1; + uint8 backup_pin = 2; + uint8 bad_pin = 3; + TransportSecurityState tss; + test::AddPin(&tss, "mail.example.org", primary_pin, backup_pin); + + SSLInfo ssl_info; + ssl_info.cert = ImportCertFromFile(GetTestCertsDirectory(), + "spdy_pooling.pem"); + ssl_info.is_issued_by_known_root = true; + ssl_info.public_key_hashes.push_back(test::GetTestHashValue(bad_pin)); + + EXPECT_FALSE(SpdySession::CanPool( + &tss, ssl_info, "www.example.org", "mail.example.org")); +} + +TEST(CanPoolTest, CanPoolWithAcceptablePins) { + uint8 primary_pin = 1; + uint8 backup_pin = 2; + TransportSecurityState tss; + test::AddPin(&tss, "mail.example.org", primary_pin, backup_pin); + + SSLInfo ssl_info; + ssl_info.cert = ImportCertFromFile(GetTestCertsDirectory(), + "spdy_pooling.pem"); + ssl_info.is_issued_by_known_root = true; + ssl_info.public_key_hashes.push_back(test::GetTestHashValue(primary_pin)); + + EXPECT_TRUE(SpdySession::CanPool( + &tss, ssl_info, "www.example.org", "mail.example.org")); +} + } // namespace net diff --git a/net/spdy/spdy_test_utils.cc b/net/spdy/spdy_test_utils.cc index e33a08b..7627abe 100644 --- a/net/spdy/spdy_test_utils.cc +++ b/net/spdy/spdy_test_utils.cc @@ -7,10 +7,13 @@ #include <cstring> #include <vector> +#include "base/base64.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/strings/string_number_conversions.h" #include "base/sys_byteorder.h" +#include "net/http/transport_security_state.h" +#include "net/ssl/ssl_info.h" #include "testing/gtest/include/gtest/gtest.h" namespace net { @@ -139,6 +142,36 @@ std::string a2b_hex(const char* hex_data) { return result; } +HashValue GetTestHashValue(uint8_t label) { + HashValue hash_value(HASH_VALUE_SHA256); + memset(hash_value.data(), label, hash_value.size()); + return hash_value; +} + +std::string GetTestPin(uint8_t label) { + HashValue hash_value = GetTestHashValue(label); + std::string base64; + base::Base64Encode(base::StringPiece( + reinterpret_cast<char*>(hash_value.data()), hash_value.size()), &base64); + + return std::string("pin-sha256=\"") + base64 + "\""; +} + +void AddPin(TransportSecurityState* state, + const std::string& host, + uint8_t primary_label, + uint8_t backup_label) { + std::string primary_pin = GetTestPin(primary_label); + std::string backup_pin = GetTestPin(backup_label); + std::string header = "max-age = 10000; " + primary_pin + "; " + backup_pin; + + // Construct a fake SSLInfo that will pass AddHPKPHeader's checks. + SSLInfo ssl_info; + ssl_info.is_issued_by_known_root = true; + ssl_info.public_key_hashes.push_back(GetTestHashValue(primary_label)); + EXPECT_TRUE(state->AddHPKPHeader(host, header, ssl_info)); +} + } // namespace test } // namespace net diff --git a/net/spdy/spdy_test_utils.h b/net/spdy/spdy_test_utils.h index 439311e..14bc8ef 100644 --- a/net/spdy/spdy_test_utils.h +++ b/net/spdy/spdy_test_utils.h @@ -5,12 +5,17 @@ #ifndef NET_SPDY_TEST_UTILS_H_ #define NET_SPDY_TEST_UTILS_H_ +#include <stdint.h> + #include <string> #include "net/spdy/spdy_protocol.h" namespace net { +class HashValue; +class TransportSecurityState; + namespace test { std::string HexDumpWithMarks(const unsigned char* data, int length, @@ -33,6 +38,19 @@ void SetFrameLength(SpdyFrame* frame, std::string a2b_hex(const char* hex_data); +// Returns a SHA1 HashValue in which each byte has the value |label|. +HashValue GetTestHashValue(uint8_t label); + +// Returns SHA1 pinning header for the of the base64 encoding of +// GetTestHashValue(|label|). +std::string GetTestPin(uint8_t label); + +// Adds a pin for |host| to |state|. +void AddPin(TransportSecurityState* state, + const std::string& host, + uint8_t primary_label, + uint8_t backup_label); + } // namespace test } // namespace net |