diff options
author | estark <estark@chromium.org> | 2015-05-13 15:01:55 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-05-13 22:02:50 +0000 |
commit | 4282f117db4ddb6b307efad849ec6635095e9cd1 (patch) | |
tree | 16e84773eda4a7e4568f1ef234e71f2482054fee | |
parent | 8a9efca9b5451c75da244f62f073206c97ef6dd4 (diff) | |
download | chromium_src-4282f117db4ddb6b307efad849ec6635095e9cd1.zip chromium_src-4282f117db4ddb6b307efad849ec6635095e9cd1.tar.gz chromium_src-4282f117db4ddb6b307efad849ec6635095e9cd1.tar.bz2 |
Split cert reporter class into report building/serializing and sending
The pre-existing |CertificateErrorReporter| class (in
//chrome/browser/net) is now only in charge of sending reports over the
network. A new class (|CertificateErrorReport| in //chrome/browser/ssl)
is in charge of building and serializing the reports.
The motivation for this change is to allow reports to include
interstitial-specific information (such as the type of interstitial that
was shown, whether the user clicked through, etc.). So as to avoid
introducing interstitial knowledge into //c/b/net, all the report
building and serializing knowledge (including the report protobuf) has
been moved into //c/b/ssl. |SSLBlockingPage| now sends a serialized report
through |ChromeContentBrowserClient| to the SafeBrowsing UIManager to be
sent over the network.
|ChromeFraudulentCertificateReporter| (responsible for reporting
Google-property pinning violations) has also been moved into //c/b/ssl
so that it can use the new |CertificateErrorReport| class to build
reports before sending them with a |CertificateErrorReporter|.
BUG=462713,461588
Review URL: https://codereview.chromium.org/1117173004
Cr-Commit-Position: refs/heads/master@{#329723}
29 files changed, 464 insertions, 328 deletions
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn index eabb36b..7a7efe9 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn @@ -76,8 +76,9 @@ source_set("browser") { "//chrome/app/resources:platform_locale_settings", "//chrome/app/theme:theme_resources", "//chrome/browser/autocomplete:in_memory_url_index_cache_proto", - "//chrome/browser/net:cert_logger_proto", + "//chrome/browser/net:encrypted_cert_logger_proto", "//chrome/browser/net:probe_message_proto", + "//chrome/browser/ssl:cert_logger_proto", "//chrome/browser/ui", "//chrome/common", "//chrome/common/net", diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc index acd08e10..e0e9bb9 100644 --- a/chrome/browser/chrome_content_browser_client.cc +++ b/chrome/browser/chrome_content_browser_client.cc @@ -530,11 +530,11 @@ class SafeBrowsingSSLCertReporter : public SSLCertReporter { ~SafeBrowsingSSLCertReporter() override {} // SSLCertReporter implementation - void ReportInvalidCertificateChain(const std::string& hostname, - const net::SSLInfo& ssl_info) override { + void ReportInvalidCertificateChain( + const std::string& serialized_report) override { if (safe_browsing_ui_manager_) { safe_browsing_ui_manager_->ReportInvalidCertificateChain( - hostname, ssl_info, base::Bind(&base::DoNothing)); + serialized_report, base::Bind(&base::DoNothing)); } } diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn index 04c3b81..9f5360f 100644 --- a/chrome/browser/chromeos/BUILD.gn +++ b/chrome/browser/chromeos/BUILD.gn @@ -47,10 +47,11 @@ source_set("chromeos") { "//components/wifi_sync", "//chrome/browser/devtools", "//chrome/browser/extensions", - "//chrome/browser/net:cert_logger_proto", + "//chrome/browser/net:encrypted_cert_logger_proto", "//chrome/browser/safe_browsing:chunk_proto", "//chrome/browser/safe_browsing:metadata_proto", "//chrome/browser/safe_browsing:report_proto", + "//chrome/browser/ssl:cert_logger_proto", "//chrome/common", "//chrome/common/extensions/api", "//chrome/common/extensions/api:api_registration", diff --git a/chrome/browser/net/BUILD.gn b/chrome/browser/net/BUILD.gn index 5941ca6..51aa200 100644 --- a/chrome/browser/net/BUILD.gn +++ b/chrome/browser/net/BUILD.gn @@ -11,9 +11,9 @@ proto_library("probe_message_proto") { ] } -# GYP version: chrome/chrome_browser.gypi:cert_logger_proto -proto_library("cert_logger_proto") { +# GYP version: chrome/chrome_browser.gypi:encrypted_cert_logger_proto +proto_library("encrypted_cert_logger_proto") { sources = [ - "cert_logger.proto", + "encrypted_cert_logger.proto", ] } diff --git a/chrome/browser/net/certificate_error_reporter.cc b/chrome/browser/net/certificate_error_reporter.cc index bb37884..c1f1591 100644 --- a/chrome/browser/net/certificate_error_reporter.cc +++ b/chrome/browser/net/certificate_error_reporter.cc @@ -7,9 +7,7 @@ #include <set> #include "base/logging.h" -#include "base/stl_util.h" -#include "base/time/time.h" -#include "chrome/browser/net/cert_logger.pb.h" +#include "chrome/browser/net/encrypted_cert_logger.pb.h" #if defined(USE_OPENSSL) #include "crypto/aead_openssl.h" @@ -22,14 +20,10 @@ #include "net/base/load_flags.h" #include "net/base/request_priority.h" #include "net/base/upload_bytes_element_reader.h" -#include "net/cert/x509_certificate.h" -#include "net/ssl/ssl_info.h" #include "net/url_request/url_request_context.h" namespace { -using chrome_browser_net::CertLoggerRequest; - // Constants used for crypto static const uint8 kServerPublicKey[] = { 0x51, 0xcc, 0x52, 0x67, 0x42, 0x47, 0x3b, 0x10, 0xe8, 0x63, 0x18, @@ -56,7 +50,8 @@ bool EncryptSerializedReport( crypto::curve25519::ScalarMult(private_key, server_public_key, shared_secret); crypto::Aead aead(crypto::Aead::AES_128_CTR_HMAC_SHA256); - crypto::HKDF hkdf(std::string((char*)shared_secret, sizeof(shared_secret)), + crypto::HKDF hkdf(std::string(reinterpret_cast<char*>(shared_secret), + sizeof(shared_secret)), std::string(), base::StringPiece(kHkdfLabel, sizeof(kHkdfLabel)), 0, 0, aead.KeyLength()); @@ -69,7 +64,7 @@ bool EncryptSerializedReport( std::string nonce(aead.NonceLength(), 0); std::string ciphertext; - if (!aead.Seal(report, nonce, "", &ciphertext)) { + if (!aead.Seal(report, nonce, std::string(), &ciphertext)) { LOG(ERROR) << "Error sealing certificate report."; return false; } @@ -77,7 +72,7 @@ bool EncryptSerializedReport( encrypted_report->set_encrypted_report(ciphertext); encrypted_report->set_server_public_key_version(server_public_key_version); encrypted_report->set_client_public_key( - std::string((char*)public_key, sizeof(public_key))); + std::string(reinterpret_cast<char*>(public_key), sizeof(public_key))); encrypted_report->set_algorithm( chrome_browser_net::EncryptedCertLoggerRequest:: AEAD_ECDH_AES_128_CTR_HMAC_SHA256); @@ -85,41 +80,6 @@ bool EncryptSerializedReport( } #endif -void AddCertStatusToReportErrors( - net::CertStatus cert_status, - CertLoggerRequest* report) { - if (cert_status & net::CERT_STATUS_REVOKED) - report->add_cert_error(CertLoggerRequest::ERR_CERT_REVOKED); - if (cert_status & net::CERT_STATUS_INVALID) - report->add_cert_error(CertLoggerRequest::ERR_CERT_INVALID); - if (cert_status & net::CERT_STATUS_PINNED_KEY_MISSING) - report->add_cert_error( - CertLoggerRequest::ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN); - if (cert_status & net::CERT_STATUS_AUTHORITY_INVALID) - report->add_cert_error(CertLoggerRequest::ERR_CERT_AUTHORITY_INVALID); - if (cert_status & net::CERT_STATUS_COMMON_NAME_INVALID) - report->add_cert_error(CertLoggerRequest::ERR_CERT_COMMON_NAME_INVALID); - if (cert_status & net::CERT_STATUS_NON_UNIQUE_NAME) - report->add_cert_error(CertLoggerRequest::ERR_CERT_NON_UNIQUE_NAME); - if (cert_status & net::CERT_STATUS_NAME_CONSTRAINT_VIOLATION) - report->add_cert_error( - CertLoggerRequest::ERR_CERT_NAME_CONSTRAINT_VIOLATION); - if (cert_status & net::CERT_STATUS_WEAK_SIGNATURE_ALGORITHM) - report->add_cert_error( - CertLoggerRequest::ERR_CERT_WEAK_SIGNATURE_ALGORITHM); - if (cert_status & net::CERT_STATUS_WEAK_KEY) - report->add_cert_error(CertLoggerRequest::ERR_CERT_WEAK_KEY); - if (cert_status & net::CERT_STATUS_DATE_INVALID) - report->add_cert_error(CertLoggerRequest::ERR_CERT_DATE_INVALID); - if (cert_status & net::CERT_STATUS_VALIDITY_TOO_LONG) - report->add_cert_error(CertLoggerRequest::ERR_CERT_VALIDITY_TOO_LONG); - if (cert_status & net::CERT_STATUS_UNABLE_TO_CHECK_REVOCATION) - report->add_cert_error( - CertLoggerRequest::ERR_CERT_UNABLE_TO_CHECK_REVOCATION); - if (cert_status & net::CERT_STATUS_NO_REVOCATION_MECHANISM) - report->add_cert_error(CertLoggerRequest::ERR_CERT_NO_REVOCATION_MECHANISM); -} - } // namespace namespace chrome_browser_net { @@ -153,25 +113,20 @@ CertificateErrorReporter::~CertificateErrorReporter() { STLDeleteElements(&inflight_requests_); } -void CertificateErrorReporter::SendReport(ReportType type, - const std::string& hostname, - const net::SSLInfo& ssl_info) { - CertLoggerRequest request; - BuildReport(hostname, ssl_info, &request); - +void CertificateErrorReporter::SendReport( + ReportType type, + const std::string& serialized_report) { switch (type) { case REPORT_TYPE_PINNING_VIOLATION: - SendCertLoggerRequest(request); + SendSerializedRequest(serialized_report); break; case REPORT_TYPE_EXTENDED_REPORTING: if (upload_url_.SchemeIsCryptographic()) { - SendCertLoggerRequest(request); + SendSerializedRequest(serialized_report); } else { DCHECK(IsHttpUploadUrlSupported()); #if defined(USE_OPENSSL) EncryptedCertLoggerRequest encrypted_report; - std::string serialized_report; - request.SerializeToString(&serialized_report); if (!EncryptSerializedReport(server_public_key_, server_public_key_version_, serialized_report, &encrypted_report)) { @@ -230,14 +185,16 @@ bool CertificateErrorReporter::IsHttpUploadUrlSupported() { bool CertificateErrorReporter::DecryptCertificateErrorReport( const uint8 server_private_key[32], const EncryptedCertLoggerRequest& encrypted_report, - CertLoggerRequest* decrypted_report) { + std::string* decrypted_serialized_report) { uint8 shared_secret[crypto::curve25519::kBytes]; crypto::curve25519::ScalarMult( - server_private_key, (uint8*)encrypted_report.client_public_key().data(), + server_private_key, reinterpret_cast<const uint8*>( + encrypted_report.client_public_key().data()), shared_secret); crypto::Aead aead(crypto::Aead::AES_128_CTR_HMAC_SHA256); - crypto::HKDF hkdf(std::string((char*)shared_secret, sizeof(shared_secret)), + crypto::HKDF hkdf(std::string(reinterpret_cast<char*>(shared_secret), + sizeof(shared_secret)), std::string(), base::StringPiece(kHkdfLabel, sizeof(kHkdfLabel)), 0, 0, aead.KeyLength()); @@ -249,23 +206,11 @@ bool CertificateErrorReporter::DecryptCertificateErrorReport( // Use an all-zero nonce because the key is random per-message. std::string nonce(aead.NonceLength(), 0); - std::string plaintext; - if (!aead.Open(encrypted_report.encrypted_report(), nonce, "", &plaintext)) { - LOG(ERROR) << "Error opening certificate report"; - return false; - } - - return decrypted_report->ParseFromString(plaintext); + return aead.Open(encrypted_report.encrypted_report(), nonce, std::string(), + decrypted_serialized_report); } #endif -void CertificateErrorReporter::SendCertLoggerRequest( - const CertLoggerRequest& request) { - std::string serialized_request; - request.SerializeToString(&serialized_request); - SendSerializedRequest(serialized_request); -} - void CertificateErrorReporter::SendSerializedRequest( const std::string& serialized_request) { scoped_ptr<net::URLRequest> url_request = CreateURLRequest(request_context_); @@ -286,26 +231,6 @@ void CertificateErrorReporter::SendSerializedRequest( raw_url_request->Start(); } -void CertificateErrorReporter::BuildReport(const std::string& hostname, - const net::SSLInfo& ssl_info, - CertLoggerRequest* out_request) { - base::Time now = base::Time::Now(); - out_request->set_time_usec(now.ToInternalValue()); - out_request->set_hostname(hostname); - - std::vector<std::string> pem_encoded_chain; - if (!ssl_info.cert->GetPEMEncodedChain(&pem_encoded_chain)) - LOG(ERROR) << "Could not get PEM encoded chain."; - - std::string* cert_chain = out_request->mutable_cert_chain(); - for (size_t i = 0; i < pem_encoded_chain.size(); ++i) - *cert_chain += pem_encoded_chain[i]; - - out_request->add_pin(ssl_info.pinning_failure_log); - - AddCertStatusToReportErrors(ssl_info.cert_status, out_request); -} - void CertificateErrorReporter::RequestComplete(net::URLRequest* request) { std::set<net::URLRequest*>::iterator i = inflight_requests_.find(request); DCHECK(i != inflight_requests_.end()); diff --git a/chrome/browser/net/certificate_error_reporter.h b/chrome/browser/net/certificate_error_reporter.h index cba431f..6b60ef8 100644 --- a/chrome/browser/net/certificate_error_reporter.h +++ b/chrome/browser/net/certificate_error_reporter.h @@ -20,7 +20,6 @@ class SSLInfo; namespace chrome_browser_net { -class CertLoggerRequest; class EncryptedCertLoggerRequest; // Provides functionality for sending reports about invalid SSL @@ -41,7 +40,7 @@ class CertificateErrorReporter : public net::URLRequest::Delegate { // to the server. enum CookiesPreference { SEND_COOKIES, DO_NOT_SEND_COOKIES }; - // Create a certificate error reporter that will send certificate + // Creates a certificate error reporter that will send certificate // error reports to |upload_url|, using |request_context| as the // context for the reports. |cookies_preference| controls whether // cookies will be sent along with the reports. @@ -58,11 +57,12 @@ class CertificateErrorReporter : public net::URLRequest::Delegate { ~CertificateErrorReporter() override; - // Construct, serialize, and send a certificate report to the report - // collection server containing the |ssl_info| associated with a - // connection to |hostname|. + // Sends a certificate report to the report collection server. The + // |serialized_report| is expected to be a serialized protobuf + // containing information about the hostname, certificate chain, and + // certificate errors encountered when validating the chain. // - // SendReport actually sends the report over the network; callers are + // |SendReport| actually sends the report over the network; callers are // responsible for enforcing any preconditions (such as obtaining user // opt-in, only sending reports for certain hostnames, checking for // incognito mode, etc.). @@ -72,8 +72,7 @@ class CertificateErrorReporter : public net::URLRequest::Delegate { // unsupported platforms, callers must send extended reporting reports // over SSL. virtual void SendReport(ReportType type, - const std::string& hostname, - const net::SSLInfo& ssl_info); + const std::string& serialized_report); // net::URLRequest::Delegate void OnResponseStarted(net::URLRequest* request) override; @@ -87,25 +86,18 @@ class CertificateErrorReporter : public net::URLRequest::Delegate { static bool DecryptCertificateErrorReport( const uint8 server_private_key[32], const EncryptedCertLoggerRequest& encrypted_report, - CertLoggerRequest* decrypted_report); + std::string* decrypted_serialized_report); private: - // Create a URLRequest with which to send a certificate report to the + // Creates a URLRequest with which to send a certificate report to the // server. virtual scoped_ptr<net::URLRequest> CreateURLRequest( net::URLRequestContext* context); - // Serialize and send a CertLoggerRequest protobuf to the report + // Sends a serialized report (encrypted or not) to the report // collection server. - void SendCertLoggerRequest(const CertLoggerRequest& request); - void SendSerializedRequest(const std::string& serialized_request); - // Populate the CertLoggerRequest for a report. - static void BuildReport(const std::string& hostname, - const net::SSLInfo& ssl_info, - CertLoggerRequest* out_request); - // Performs post-report cleanup. void RequestComplete(net::URLRequest* request); diff --git a/chrome/browser/net/certificate_error_reporter_unittest.cc b/chrome/browser/net/certificate_error_reporter_unittest.cc index fbf4c81..8641e5e 100644 --- a/chrome/browser/net/certificate_error_reporter_unittest.cc +++ b/chrome/browser/net/certificate_error_reporter_unittest.cc @@ -9,25 +9,19 @@ #include "base/bind.h" #include "base/bind_helpers.h" -#include "base/files/file_path.h" -#include "base/files/file_util.h" #include "base/macros.h" #include "base/message_loop/message_loop.h" -#include "base/path_service.h" #include "base/run_loop.h" #include "base/thread_task_runner_handle.h" -#include "chrome/browser/net/cert_logger.pb.h" +#include "chrome/browser/net/encrypted_cert_logger.pb.h" #include "chrome/common/chrome_paths.h" #include "content/public/browser/browser_thread.h" #include "crypto/curve25519.h" #include "net/base/load_flags.h" #include "net/base/network_delegate_impl.h" -#include "net/base/test_data_directory.h" #include "net/base/upload_bytes_element_reader.h" #include "net/base/upload_data_stream.h" #include "net/base/upload_element_reader.h" -#include "net/cert/cert_status_flags.h" -#include "net/test/cert_test_util.h" #include "net/test/url_request/url_request_failed_job.h" #include "net/test/url_request/url_request_mock_data_job.h" #include "net/url_request/url_request_filter.h" @@ -38,44 +32,15 @@ using chrome_browser_net::CertificateErrorReporter; using content::BrowserThread; using net::CertStatus; using net::CompletionCallback; -using net::SSLInfo; using net::NetworkDelegateImpl; using net::TestURLRequestContext; using net::URLRequest; namespace { -const char kHostname[] = "test.mail.google.com"; -const char kSecondRequestHostname[] = "test2.mail.google.com"; -const char kDummyFailureLog[] = "dummy failure log"; -const char kTestCertFilename[] = "test_mail_google_com.pem"; +const char kDummyReport[] = "test.mail.google.com"; +const char kSecondDummyReport[] = "test2.mail.google.com"; const uint32 kServerPublicKeyVersion = 1; -const CertStatus kCertStatus = - net::CERT_STATUS_COMMON_NAME_INVALID | net::CERT_STATUS_REVOKED; -const size_t kNumCertErrors = 2; -const chrome_browser_net::CertLoggerRequest::CertError kFirstReportedCertError = - chrome_browser_net::CertLoggerRequest::ERR_CERT_COMMON_NAME_INVALID; -const chrome_browser_net::CertLoggerRequest::CertError - kSecondReportedCertError = - chrome_browser_net::CertLoggerRequest::ERR_CERT_REVOKED; - -SSLInfo GetTestSSLInfo() { - SSLInfo info; - info.cert = - net::ImportCertFromFile(net::GetTestCertsDirectory(), kTestCertFilename); - info.is_issued_by_known_root = true; - info.cert_status = kCertStatus; - info.pinning_failure_log = kDummyFailureLog; - return info; -} - -std::string GetPEMEncodedChain() { - base::FilePath cert_path = - net::GetTestCertsDirectory().AppendASCII(kTestCertFilename); - std::string cert_data; - EXPECT_TRUE(base::ReadFileToString(cert_path, &cert_data)); - return cert_data; -} void EnableUrlRequestMocks(bool enable) { net::URLRequestFilter::GetInstance()->ClearHandlers(); @@ -86,12 +51,10 @@ void EnableUrlRequestMocks(bool enable) { net::URLRequestMockDataJob::AddUrlHandler(); } -// Check that data uploaded in the request matches the test data (an SSL -// report for one of the given hostnames, with the info returned by -// |GetTestSSLInfo()|). The hostname sent in the report will be erased -// from |expect_hostnames|. +// Check that data uploaded in the request matches the test report +// data. The sent reports will be erased from |expect_reports|. void CheckUploadData(URLRequest* request, - std::set<std::string>* expect_hostnames, + std::set<std::string>* expect_reports, bool encrypted, const uint8* server_private_key) { const net::UploadDataStream* upload = request->get_upload(); @@ -104,7 +67,7 @@ void CheckUploadData(URLRequest* request, ASSERT_TRUE(reader); std::string upload_data(reader->bytes(), reader->length()); - chrome_browser_net::CertLoggerRequest uploaded_request; + std::string uploaded_report; #if defined(USE_OPENSSL) if (encrypted) { chrome_browser_net::EncryptedCertLoggerRequest encrypted_request; @@ -114,42 +77,23 @@ void CheckUploadData(URLRequest* request, EXPECT_EQ(chrome_browser_net::EncryptedCertLoggerRequest:: AEAD_ECDH_AES_128_CTR_HMAC_SHA256, encrypted_request.algorithm()); - ASSERT_TRUE( - chrome_browser_net::CertificateErrorReporter:: - DecryptCertificateErrorReport(server_private_key, encrypted_request, - &uploaded_request)); + ASSERT_TRUE(CertificateErrorReporter::DecryptCertificateErrorReport( + server_private_key, encrypted_request, &uploaded_report)); } else { - ASSERT_TRUE(uploaded_request.ParseFromString(upload_data)); + uploaded_report = upload_data; } #else - ASSERT_TRUE(uploaded_request.ParseFromString(upload_data)); + uploaded_report = upload_data; #endif - EXPECT_EQ(1u, expect_hostnames->count(uploaded_request.hostname())); - expect_hostnames->erase(uploaded_request.hostname()); - - EXPECT_EQ(GetPEMEncodedChain(), uploaded_request.cert_chain()); - EXPECT_EQ(1, uploaded_request.pin().size()); - EXPECT_EQ(kDummyFailureLog, uploaded_request.pin().Get(0)); - EXPECT_EQ(2, uploaded_request.cert_error().size()); - - std::set<chrome_browser_net::CertLoggerRequest::CertError> reported_errors; - reported_errors.insert( - static_cast<chrome_browser_net::CertLoggerRequest::CertError>( - uploaded_request.cert_error().Get(0))); - reported_errors.insert( - static_cast<chrome_browser_net::CertLoggerRequest::CertError>( - uploaded_request.cert_error().Get(1))); - EXPECT_EQ(kNumCertErrors, reported_errors.size()); - EXPECT_EQ(1u, reported_errors.count(kFirstReportedCertError)); - EXPECT_EQ(1u, reported_errors.count(kSecondReportedCertError)); + EXPECT_EQ(1u, expect_reports->count(uploaded_report)); + expect_reports->erase(uploaded_report); } // A network delegate that lets tests check that a certificate error // report was sent. It counts the number of requests and lets tests // register a callback to run when the request is destroyed. It also -// checks that the uploaded data is as expected (a report for -// |kHostname| and |GetTestSSLInfo()|). +// checks that the uploaded data is as expected. class TestCertificateErrorReporterNetworkDelegate : public NetworkDelegateImpl { public: TestCertificateErrorReporterNetworkDelegate() @@ -164,8 +108,8 @@ class TestCertificateErrorReporterNetworkDelegate : public NetworkDelegateImpl { ~TestCertificateErrorReporterNetworkDelegate() override {} - void ExpectHostname(const std::string& hostname) { - expect_hostnames_.insert(hostname); + void ExpectReport(const std::string& report) { + expect_reports_.insert(report); } void set_all_url_requests_destroyed_callback( @@ -209,16 +153,14 @@ class TestCertificateErrorReporterNetworkDelegate : public NetworkDelegateImpl { EXPECT_TRUE(request->load_flags() & net::LOAD_DO_NOT_SAVE_COOKIES); } - std::string uploaded_request_hostname; - CheckUploadData(request, &expect_hostnames_, expect_request_encrypted_, + CheckUploadData(request, &expect_reports_, expect_request_encrypted_, server_private_key_); - expect_hostnames_.erase(uploaded_request_hostname); return net::OK; } void OnURLRequestDestroyed(URLRequest* request) override { url_request_destroyed_callback_.Run(); - if (expect_hostnames_.empty()) + if (expect_reports_.empty()) all_url_requests_destroyed_callback_.Run(); } @@ -230,7 +172,7 @@ class TestCertificateErrorReporterNetworkDelegate : public NetworkDelegateImpl { base::Closure all_url_requests_destroyed_callback_; int num_requests_; GURL expect_url_; - std::set<std::string> expect_hostnames_; + std::set<std::string> expect_reports_; bool expect_cookies_; bool expect_request_encrypted_; @@ -264,7 +206,7 @@ class CertificateErrorReporterTest : public ::testing::Test { void SendReport(CertificateErrorReporter* reporter, TestCertificateErrorReporterNetworkDelegate* network_delegate, - const std::string& report_hostname, + const std::string& report, const GURL& url, int request_sequence_number, CertificateErrorReporter::ReportType type) { @@ -272,11 +214,11 @@ void SendReport(CertificateErrorReporter* reporter, network_delegate->set_url_request_destroyed_callback(run_loop.QuitClosure()); network_delegate->set_expect_url(url); - network_delegate->ExpectHostname(report_hostname); + network_delegate->ExpectReport(report); EXPECT_EQ(request_sequence_number, network_delegate->num_requests()); - reporter->SendReport(type, report_hostname, GetTestSSLInfo()); + reporter->SendReport(type, report); run_loop.Run(); EXPECT_EQ(request_sequence_number + 1, network_delegate->num_requests()); @@ -288,7 +230,7 @@ TEST_F(CertificateErrorReporterTest, PinningViolationSendReportSendsRequest) { GURL url = net::URLRequestMockDataJob::GetMockHttpsUrl("dummy data", 1); CertificateErrorReporter reporter( context(), url, CertificateErrorReporter::DO_NOT_SEND_COOKIES); - SendReport(&reporter, network_delegate(), kHostname, url, 0, + SendReport(&reporter, network_delegate(), kDummyReport, url, 0, CertificateErrorReporter::REPORT_TYPE_PINNING_VIOLATION); } @@ -298,7 +240,7 @@ TEST_F(CertificateErrorReporterTest, ExtendedReportingSendReportSendsRequest) { CertificateErrorReporter https_reporter( context(), https_url, CertificateErrorReporter::DO_NOT_SEND_COOKIES); network_delegate()->set_expect_request_encrypted(false); - SendReport(&https_reporter, network_delegate(), kHostname, https_url, 0, + SendReport(&https_reporter, network_delegate(), kDummyReport, https_url, 0, CertificateErrorReporter::REPORT_TYPE_EXTENDED_REPORTING); // Data should be encrypted when sent to an HTTP URL. @@ -308,7 +250,7 @@ TEST_F(CertificateErrorReporterTest, ExtendedReportingSendReportSendsRequest) { context(), http_url, CertificateErrorReporter::DO_NOT_SEND_COOKIES, network_delegate()->server_public_key(), kServerPublicKeyVersion); network_delegate()->set_expect_request_encrypted(true); - SendReport(&http_reporter, network_delegate(), kHostname, http_url, 1, + SendReport(&http_reporter, network_delegate(), kDummyReport, http_url, 1, CertificateErrorReporter::REPORT_TYPE_EXTENDED_REPORTING); } } @@ -317,9 +259,9 @@ TEST_F(CertificateErrorReporterTest, SendMultipleReportsSequentially) { GURL url = net::URLRequestMockDataJob::GetMockHttpsUrl("dummy data", 1); CertificateErrorReporter reporter( context(), url, CertificateErrorReporter::DO_NOT_SEND_COOKIES); - SendReport(&reporter, network_delegate(), kHostname, url, 0, + SendReport(&reporter, network_delegate(), kDummyReport, url, 0, CertificateErrorReporter::REPORT_TYPE_PINNING_VIOLATION); - SendReport(&reporter, network_delegate(), kSecondRequestHostname, url, 1, + SendReport(&reporter, network_delegate(), kDummyReport, url, 1, CertificateErrorReporter::REPORT_TYPE_PINNING_VIOLATION); } @@ -330,8 +272,8 @@ TEST_F(CertificateErrorReporterTest, SendMultipleReportsSimultaneously) { GURL url = net::URLRequestMockDataJob::GetMockHttpsUrl("dummy data", 1); network_delegate()->set_expect_url(url); - network_delegate()->ExpectHostname(kHostname); - network_delegate()->ExpectHostname(kSecondRequestHostname); + network_delegate()->ExpectReport(kDummyReport); + network_delegate()->ExpectReport(kSecondDummyReport); CertificateErrorReporter reporter( context(), url, CertificateErrorReporter::DO_NOT_SEND_COOKIES); @@ -339,9 +281,9 @@ TEST_F(CertificateErrorReporterTest, SendMultipleReportsSimultaneously) { EXPECT_EQ(0, network_delegate()->num_requests()); reporter.SendReport(CertificateErrorReporter::REPORT_TYPE_PINNING_VIOLATION, - kHostname, GetTestSSLInfo()); + kDummyReport); reporter.SendReport(CertificateErrorReporter::REPORT_TYPE_PINNING_VIOLATION, - kSecondRequestHostname, GetTestSSLInfo()); + kSecondDummyReport); run_loop.Run(); @@ -358,14 +300,14 @@ TEST_F(CertificateErrorReporterTest, PendingRequestGetsDeleted) { GURL url = net::URLRequestFailedJob::GetMockHttpUrlWithFailurePhase( net::URLRequestFailedJob::START, net::ERR_IO_PENDING); network_delegate()->set_expect_url(url); - network_delegate()->ExpectHostname(kHostname); + network_delegate()->ExpectReport(kDummyReport); EXPECT_EQ(0, network_delegate()->num_requests()); scoped_ptr<CertificateErrorReporter> reporter(new CertificateErrorReporter( context(), url, CertificateErrorReporter::DO_NOT_SEND_COOKIES)); reporter->SendReport(CertificateErrorReporter::REPORT_TYPE_PINNING_VIOLATION, - kHostname, GetTestSSLInfo()); + kDummyReport); reporter.reset(); run_loop.Run(); @@ -378,7 +320,7 @@ TEST_F(CertificateErrorReporterTest, ErroredRequestGetsDeleted) { GURL url = net::URLRequestFailedJob::GetMockHttpsUrl(net::ERR_FAILED); CertificateErrorReporter reporter( context(), url, CertificateErrorReporter::DO_NOT_SEND_COOKIES); - SendReport(&reporter, network_delegate(), kHostname, url, 0, + SendReport(&reporter, network_delegate(), kDummyReport, url, 0, CertificateErrorReporter::REPORT_TYPE_PINNING_VIOLATION); } @@ -391,7 +333,7 @@ TEST_F(CertificateErrorReporterTest, SendCookiesPreference) { CertificateErrorReporter::SEND_COOKIES); network_delegate()->set_expect_cookies(true); - SendReport(&reporter, network_delegate(), kHostname, url, 0, + SendReport(&reporter, network_delegate(), kDummyReport, url, 0, CertificateErrorReporter::REPORT_TYPE_PINNING_VIOLATION); } @@ -401,7 +343,7 @@ TEST_F(CertificateErrorReporterTest, DoNotSendCookiesPreference) { context(), url, CertificateErrorReporter::DO_NOT_SEND_COOKIES); network_delegate()->set_expect_cookies(false); - SendReport(&reporter, network_delegate(), kHostname, url, 0, + SendReport(&reporter, network_delegate(), kDummyReport, url, 0, CertificateErrorReporter::REPORT_TYPE_PINNING_VIOLATION); } @@ -558,13 +500,14 @@ TEST_F(CertificateErrorReporterTest, DecryptExampleReport) { 0x1A, 0x7D, 0x19, 0x81, 0xF0, 0x4D, 0x20, 0x01}; chrome_browser_net::EncryptedCertLoggerRequest encrypted_request; - chrome_browser_net::CertLoggerRequest request; - ASSERT_TRUE(encrypted_request.ParseFromString(std::string( - (char*) kSerializedEncryptedReport, sizeof(kSerializedEncryptedReport)))); + std::string decrypted_serialized_report; + ASSERT_TRUE(encrypted_request.ParseFromString( + std::string(reinterpret_cast<const char*>(kSerializedEncryptedReport), + sizeof(kSerializedEncryptedReport)))); ASSERT_TRUE(chrome_browser_net::CertificateErrorReporter:: DecryptCertificateErrorReport( network_delegate()->server_private_key(), - encrypted_request, &request)); + encrypted_request, &decrypted_serialized_report)); } #endif diff --git a/chrome/browser/net/encrypted_cert_logger.proto b/chrome/browser/net/encrypted_cert_logger.proto new file mode 100644 index 0000000..6c6274e --- /dev/null +++ b/chrome/browser/net/encrypted_cert_logger.proto @@ -0,0 +1,30 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// + +syntax = "proto2"; + +package chrome_browser_net; + +// Chrome requires this. +option optimize_for = LITE_RUNTIME; + +// This protobuffer is intended to store an encrypted report of an +// invalid certificate chain. +message EncryptedCertLoggerRequest { + // An encrypted, serialized CertLoggerRequest + required bytes encrypted_report = 1; + // The server public key version that was used to derive the shared secret. + required uint32 server_public_key_version = 2; + // The client public key that corresponds to the private key that was used + // to derive the shared secret. + required bytes client_public_key = 3; + // The encryption algorithm used to encrypt the report. + enum Algorithm { + UNKNOWN_ALGORITHM = 0; + AEAD_ECDH_AES_128_CTR_HMAC_SHA256 = 1; + } + optional Algorithm algorithm = 4 + [default = AEAD_ECDH_AES_128_CTR_HMAC_SHA256]; +};
\ No newline at end of file diff --git a/chrome/browser/profiles/profile_io_data.cc b/chrome/browser/profiles/profile_io_data.cc index 512fc15..a680930 100644 --- a/chrome/browser/profiles/profile_io_data.cc +++ b/chrome/browser/profiles/profile_io_data.cc @@ -34,7 +34,6 @@ #include "chrome/browser/io_thread.h" #include "chrome/browser/media/media_device_id_salt.h" #include "chrome/browser/net/about_protocol_handler.h" -#include "chrome/browser/net/chrome_fraudulent_certificate_reporter.h" #include "chrome/browser/net/chrome_http_user_agent_settings.h" #include "chrome/browser/net/chrome_net_log.h" #include "chrome/browser/net/chrome_network_delegate.h" @@ -46,6 +45,7 @@ #include "chrome/browser/predictors/resource_prefetch_predictor_factory.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_manager.h" +#include "chrome/browser/ssl/chrome_fraudulent_certificate_reporter.h" #include "chrome/browser/ui/search/new_tab_page_interceptor_service.h" #include "chrome/browser/ui/search/new_tab_page_interceptor_service_factory.h" #include "chrome/common/chrome_paths.h" @@ -1025,8 +1025,7 @@ void ProfileIOData::Init( network_delegate->set_force_google_safe_search(&force_google_safesearch_); network_delegate->set_force_youtube_safety_mode(&force_youtube_safety_mode_); fraudulent_certificate_reporter_.reset( - new chrome_browser_net::ChromeFraudulentCertificateReporter( - main_request_context_.get())); + new ChromeFraudulentCertificateReporter(main_request_context_.get())); // NOTE: Proxy service uses the default io thread network delegate, not the // delegate just created. diff --git a/chrome/browser/safe_browsing/ping_manager.cc b/chrome/browser/safe_browsing/ping_manager.cc index 07fc0ab..92228ca 100644 --- a/chrome/browser/safe_browsing/ping_manager.cc +++ b/chrome/browser/safe_browsing/ping_manager.cc @@ -134,12 +134,11 @@ void SafeBrowsingPingManager::ReportMalwareDetails( } void SafeBrowsingPingManager::ReportInvalidCertificateChain( - const std::string& hostname, - const net::SSLInfo& ssl_info) { + const std::string& serialized_report) { DCHECK(certificate_error_reporter_); certificate_error_reporter_->SendReport( - CertificateErrorReporter::REPORT_TYPE_EXTENDED_REPORTING, hostname, - ssl_info); + CertificateErrorReporter::REPORT_TYPE_EXTENDED_REPORTING, + serialized_report); } void SafeBrowsingPingManager::SetCertificateErrorReporterForTesting( diff --git a/chrome/browser/safe_browsing/ping_manager.h b/chrome/browser/safe_browsing/ping_manager.h index 28c536c..7a62b23 100644 --- a/chrome/browser/safe_browsing/ping_manager.h +++ b/chrome/browser/safe_browsing/ping_manager.h @@ -56,8 +56,7 @@ class SafeBrowsingPingManager : public net::URLFetcherDelegate { // Users can opt-in on the SSL interstitial to send reports of invalid // certificate chains. - void ReportInvalidCertificateChain(const std::string& hostname, - const net::SSLInfo& ssl_info); + void ReportInvalidCertificateChain(const std::string& serialized_report); void SetCertificateErrorReporterForTesting(scoped_ptr< chrome_browser_net::CertificateErrorReporter> certificate_error_reporter); diff --git a/chrome/browser/safe_browsing/ui_manager.cc b/chrome/browser/safe_browsing/ui_manager.cc index b50e3e3..db59f3b 100644 --- a/chrome/browser/safe_browsing/ui_manager.cc +++ b/chrome/browser/safe_browsing/ui_manager.cc @@ -215,15 +215,14 @@ void SafeBrowsingUIManager::ReportSafeBrowsingHit( } void SafeBrowsingUIManager::ReportInvalidCertificateChain( - const std::string& hostname, - const net::SSLInfo& ssl_info, + const std::string& serialized_report, const base::Closure& callback) { DCHECK_CURRENTLY_ON(BrowserThread::UI); BrowserThread::PostTaskAndReply( BrowserThread::IO, FROM_HERE, base::Bind( &SafeBrowsingUIManager::ReportInvalidCertificateChainOnIOThread, this, - hostname, ssl_info), + serialized_report), callback); } @@ -261,8 +260,7 @@ void SafeBrowsingUIManager::ReportSafeBrowsingHitOnIOThread( } void SafeBrowsingUIManager::ReportInvalidCertificateChainOnIOThread( - const std::string& hostname, - const net::SSLInfo& ssl_info) { + const std::string& serialized_report) { DCHECK_CURRENTLY_ON(BrowserThread::IO); // The service may delete the ping manager (i.e. when user disabling service, @@ -270,8 +268,7 @@ void SafeBrowsingUIManager::ReportInvalidCertificateChainOnIOThread( if (!sb_service_ || !sb_service_->ping_manager()) return; - sb_service_->ping_manager()->ReportInvalidCertificateChain(hostname, - ssl_info); + sb_service_->ping_manager()->ReportInvalidCertificateChain(serialized_report); } // If the user had opted-in to send MalwareDetails, this gets called diff --git a/chrome/browser/safe_browsing/ui_manager.h b/chrome/browser/safe_browsing/ui_manager.h index 42be145..cdef1be 100644 --- a/chrome/browser/safe_browsing/ui_manager.h +++ b/chrome/browser/safe_browsing/ui_manager.h @@ -130,8 +130,7 @@ class SafeBrowsingUIManager // Report an invalid TLS/SSL certificate chain to the server. Can only // be called on UI thread. - void ReportInvalidCertificateChain(const std::string& hostname, - const net::SSLInfo& ssl_info, + void ReportInvalidCertificateChain(const std::string& serialized_report, const base::Closure& callback); // Add and remove observers. These methods must be invoked on the UI thread. @@ -156,8 +155,8 @@ class SafeBrowsingUIManager const std::string& post_data); // Sends an invalid certificate chain report over the network. - void ReportInvalidCertificateChainOnIOThread(const std::string& hostname, - const net::SSLInfo& ssl_info); + void ReportInvalidCertificateChainOnIOThread( + const std::string& serialized_report); // Adds the given entry to the whitelist. Called on the UI thread. void UpdateWhitelist(const UnsafeResource& resource); diff --git a/chrome/browser/ssl/BUILD.gn b/chrome/browser/ssl/BUILD.gn new file mode 100644 index 0000000..88f00288 --- /dev/null +++ b/chrome/browser/ssl/BUILD.gn @@ -0,0 +1,12 @@ +# Copyright 2015 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//third_party/protobuf/proto_library.gni") + +# GYP version: chrome/chrome_browser.gypi:cert_logger_proto +proto_library("cert_logger_proto") { + sources = [ + "cert_logger.proto", + ] +} diff --git a/chrome/browser/net/cert_logger.proto b/chrome/browser/ssl/cert_logger.proto index 3824c9f..72549fd 100644 --- a/chrome/browser/net/cert_logger.proto +++ b/chrome/browser/ssl/cert_logger.proto @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright 2015 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // @@ -14,11 +14,8 @@ // generated by the frontend and logged, including validation and error checking // done on the client's input data. - syntax = "proto2"; -package chrome_browser_net; - // Chrome requires this. option optimize_for = LITE_RUNTIME; @@ -59,35 +56,4 @@ message CertLoggerRequest { // Certificate errors encountered (if any) when validating this // certificate chain. repeated CertError cert_error = 6; -}; - -// A wrapper proto containing an encrypted CertLoggerRequest -message EncryptedCertLoggerRequest { - // An encrypted, serialized CertLoggerRequest - required bytes encrypted_report = 1; - // The server public key version that was used to derive the shared secret. - required uint32 server_public_key_version = 2; - // The client public key that corresponds to the private key that was used - // to derive the shared secret. - required bytes client_public_key = 3; - // The encryption algorithm used to encrypt the report. - enum Algorithm { - UNKNOWN_ALGORITHM = 0; - AEAD_ECDH_AES_128_CTR_HMAC_SHA256 = 1; - } - optional Algorithm algorithm = 4 - [default = AEAD_ECDH_AES_128_CTR_HMAC_SHA256]; -}; - -// The response sent back to the user. -message CertLoggerResponse { - enum ResponseCode { - OK = 1; - MALFORMED_CERT_DATA = 2; - HOST_CERT_DONT_MATCH = 3; - ROOT_NOT_RECOGNIZED = 4; - ROOT_NOT_UNEXPECTED = 5; - OTHER_ERROR = 6; - }; - required ResponseCode response = 1; -}; +};
\ No newline at end of file diff --git a/chrome/browser/ssl/certificate_error_report.cc b/chrome/browser/ssl/certificate_error_report.cc new file mode 100644 index 0000000..3862621 --- /dev/null +++ b/chrome/browser/ssl/certificate_error_report.cc @@ -0,0 +1,92 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ssl/certificate_error_report.h" + +#include <vector> + +#include "base/stl_util.h" +#include "base/time/time.h" +#include "chrome/browser/ssl/cert_logger.pb.h" +#include "net/cert/cert_status_flags.h" +#include "net/cert/x509_certificate.h" +#include "net/ssl/ssl_info.h" + +namespace { + +void AddCertStatusToReportErrors(net::CertStatus cert_status, + CertLoggerRequest* report) { + if (cert_status & net::CERT_STATUS_REVOKED) + report->add_cert_error(CertLoggerRequest::ERR_CERT_REVOKED); + if (cert_status & net::CERT_STATUS_INVALID) + report->add_cert_error(CertLoggerRequest::ERR_CERT_INVALID); + if (cert_status & net::CERT_STATUS_PINNED_KEY_MISSING) + report->add_cert_error( + CertLoggerRequest::ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN); + if (cert_status & net::CERT_STATUS_AUTHORITY_INVALID) + report->add_cert_error(CertLoggerRequest::ERR_CERT_AUTHORITY_INVALID); + if (cert_status & net::CERT_STATUS_COMMON_NAME_INVALID) + report->add_cert_error(CertLoggerRequest::ERR_CERT_COMMON_NAME_INVALID); + if (cert_status & net::CERT_STATUS_NON_UNIQUE_NAME) + report->add_cert_error(CertLoggerRequest::ERR_CERT_NON_UNIQUE_NAME); + if (cert_status & net::CERT_STATUS_NAME_CONSTRAINT_VIOLATION) + report->add_cert_error( + CertLoggerRequest::ERR_CERT_NAME_CONSTRAINT_VIOLATION); + if (cert_status & net::CERT_STATUS_WEAK_SIGNATURE_ALGORITHM) + report->add_cert_error( + CertLoggerRequest::ERR_CERT_WEAK_SIGNATURE_ALGORITHM); + if (cert_status & net::CERT_STATUS_WEAK_KEY) + report->add_cert_error(CertLoggerRequest::ERR_CERT_WEAK_KEY); + if (cert_status & net::CERT_STATUS_DATE_INVALID) + report->add_cert_error(CertLoggerRequest::ERR_CERT_DATE_INVALID); + if (cert_status & net::CERT_STATUS_VALIDITY_TOO_LONG) + report->add_cert_error(CertLoggerRequest::ERR_CERT_VALIDITY_TOO_LONG); + if (cert_status & net::CERT_STATUS_UNABLE_TO_CHECK_REVOCATION) + report->add_cert_error( + CertLoggerRequest::ERR_CERT_UNABLE_TO_CHECK_REVOCATION); + if (cert_status & net::CERT_STATUS_NO_REVOCATION_MECHANISM) + report->add_cert_error(CertLoggerRequest::ERR_CERT_NO_REVOCATION_MECHANISM); +} +} // namespace + +CertificateErrorReport::CertificateErrorReport() + : cert_report_(new CertLoggerRequest()) { +} + +CertificateErrorReport::CertificateErrorReport(const std::string& hostname, + const net::SSLInfo& ssl_info) + : cert_report_(new CertLoggerRequest()) { + base::Time now = base::Time::Now(); + cert_report_->set_time_usec(now.ToInternalValue()); + cert_report_->set_hostname(hostname); + + std::vector<std::string> pem_encoded_chain; + if (!ssl_info.cert->GetPEMEncodedChain(&pem_encoded_chain)) { + LOG(ERROR) << "Could not get PEM encoded chain."; + } + + std::string* cert_chain = cert_report_->mutable_cert_chain(); + for (size_t i = 0; i < pem_encoded_chain.size(); ++i) + cert_chain->append(pem_encoded_chain[i]); + + cert_report_->add_pin(ssl_info.pinning_failure_log); + + AddCertStatusToReportErrors(ssl_info.cert_status, cert_report_.get()); +} + +CertificateErrorReport::~CertificateErrorReport() { +} + +bool CertificateErrorReport::InitializeFromString( + const std::string& serialized_report) { + return cert_report_->ParseFromString(serialized_report); +} + +bool CertificateErrorReport::Serialize(std::string* output) const { + return cert_report_->SerializeToString(output); +} + +const std::string& CertificateErrorReport::hostname() const { + return cert_report_->hostname(); +} diff --git a/chrome/browser/ssl/certificate_error_report.h b/chrome/browser/ssl/certificate_error_report.h new file mode 100644 index 0000000..9bc9b71 --- /dev/null +++ b/chrome/browser/ssl/certificate_error_report.h @@ -0,0 +1,51 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_SSL_CERTIFICATE_ERROR_REPORT_H_ +#define CHROME_BROWSER_SSL_CERTIFICATE_ERROR_REPORT_H_ + +#include <string> + +#include "base/memory/scoped_ptr.h" + +namespace net { +class SSLInfo; +} // namespace net + +class CertLoggerRequest; + +// This class builds and serializes reports for invalid SSL certificate +// chains, intended to be sent with +// chrome_browser_net::CertificateErrorReporter. +class CertificateErrorReport { + public: + // Constructs an empty report. + CertificateErrorReport(); + + // Constructs a report for the given |hostname| using the SSL + // properties in |ssl_info|. + CertificateErrorReport(const std::string& hostname, + const net::SSLInfo& ssl_info); + + ~CertificateErrorReport(); + + // Initializes an empty report by parsing the given serialized + // report. |serialized_report| should be a serialized + // CertLoggerRequest protobuf. Returns true if the report could be + // successfully parsed and false otherwise. + bool InitializeFromString(const std::string& serialized_report); + + // Serializes the report. The output will be a serialized + // CertLoggerRequest protobuf. Returns true if the serialization was + // successful and false otherwise. + bool Serialize(std::string* output) const; + + // Gets the hostname to which this report corresponds. + const std::string& hostname() const; + + private: + scoped_ptr<CertLoggerRequest> cert_report_; +}; + +#endif // CHROME_BROWSER_SSL_CERTIFICATE_ERROR_REPORT_H_ diff --git a/chrome/browser/ssl/certificate_error_report_unittest.cc b/chrome/browser/ssl/certificate_error_report_unittest.cc new file mode 100644 index 0000000..ff9b1ba --- /dev/null +++ b/chrome/browser/ssl/certificate_error_report_unittest.cc @@ -0,0 +1,97 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ssl/certificate_error_report.h" + +#include <set> +#include <string> + +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/path_service.h" +#include "chrome/browser/ssl/cert_logger.pb.h" +#include "chrome/common/chrome_paths.h" +#include "net/base/test_data_directory.h" +#include "net/cert/cert_status_flags.h" +#include "net/ssl/ssl_info.h" +#include "net/test/cert_test_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +using net::SSLInfo; + +namespace { + +const char kDummyHostname[] = "dummy.hostname.com"; +const char kDummyFailureLog[] = "dummy failure log"; +const char kTestCertFilename[] = "test_mail_google_com.pem"; + +const net::CertStatus kCertStatus = + net::CERT_STATUS_COMMON_NAME_INVALID | net::CERT_STATUS_REVOKED; +const size_t kNumCertErrors = 2; + +const CertLoggerRequest::CertError kFirstReportedCertError = + CertLoggerRequest::ERR_CERT_COMMON_NAME_INVALID; +const CertLoggerRequest::CertError kSecondReportedCertError = + CertLoggerRequest::ERR_CERT_REVOKED; + +SSLInfo GetTestSSLInfo() { + SSLInfo info; + info.cert = + net::ImportCertFromFile(net::GetTestCertsDirectory(), kTestCertFilename); + info.is_issued_by_known_root = true; + info.cert_status = kCertStatus; + info.pinning_failure_log = kDummyFailureLog; + return info; +} + +std::string GetPEMEncodedChain() { + base::FilePath cert_path = + net::GetTestCertsDirectory().AppendASCII(kTestCertFilename); + std::string cert_data; + EXPECT_TRUE(base::ReadFileToString(cert_path, &cert_data)); + return cert_data; +} + +// Test that a serialized CertificateErrorReport can be deserialized as +// a CertLoggerRequest protobuf (which is the format that the receiving +// server expects it in) with the right data in it. +TEST(CertificateErrorReportTest, SerializedReportAsProtobuf) { + SSLInfo ssl_info = GetTestSSLInfo(); + + std::string serialized_report; + CertificateErrorReport report(kDummyHostname, ssl_info); + report.Serialize(&serialized_report); + + CertLoggerRequest deserialized_report; + ASSERT_TRUE(deserialized_report.ParseFromString(serialized_report)); + EXPECT_EQ(kDummyHostname, deserialized_report.hostname()); + EXPECT_EQ(GetPEMEncodedChain(), deserialized_report.cert_chain()); + EXPECT_EQ(1, deserialized_report.pin().size()); + EXPECT_EQ(kDummyFailureLog, deserialized_report.pin().Get(0)); + + std::set<CertLoggerRequest::CertError> reported_errors; + reported_errors.insert(static_cast<CertLoggerRequest::CertError>( + deserialized_report.cert_error().Get(0))); + reported_errors.insert(static_cast<CertLoggerRequest::CertError>( + deserialized_report.cert_error().Get(1))); + EXPECT_EQ(kNumCertErrors, reported_errors.size()); + EXPECT_EQ(1u, reported_errors.count(kFirstReportedCertError)); + EXPECT_EQ(1u, reported_errors.count(kSecondReportedCertError)); +} + +// Test that a serialized report can be parsed. +TEST(CertificateErrorReportTest, ParseSerializedReport) { + SSLInfo ssl_info = GetTestSSLInfo(); + + std::string serialized_report; + CertificateErrorReport report(kDummyHostname, ssl_info); + EXPECT_EQ(kDummyHostname, report.hostname()); + report.Serialize(&serialized_report); + + CertificateErrorReport parsed; + ASSERT_TRUE(parsed.InitializeFromString(serialized_report)); + EXPECT_EQ(report.hostname(), parsed.hostname()); +} + +} // namespace diff --git a/chrome/browser/net/chrome_fraudulent_certificate_reporter.cc b/chrome/browser/ssl/chrome_fraudulent_certificate_reporter.cc index 1c6dd48..e24d71a 100644 --- a/chrome/browser/net/chrome_fraudulent_certificate_reporter.cc +++ b/chrome/browser/ssl/chrome_fraudulent_certificate_reporter.cc @@ -1,11 +1,12 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2015 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/net/chrome_fraudulent_certificate_reporter.h" +#include "chrome/browser/ssl/chrome_fraudulent_certificate_reporter.h" #include "base/profiler/scoped_tracker.h" #include "chrome/browser/net/certificate_error_reporter.h" +#include "chrome/browser/ssl/certificate_error_report.h" #include "net/ssl/ssl_info.h" #include "net/url_request/url_request_context.h" #include "url/gurl.h" @@ -19,18 +20,17 @@ const char kFraudulentCertificateUploadEndpoint[] = } // namespace -namespace chrome_browser_net { - ChromeFraudulentCertificateReporter::ChromeFraudulentCertificateReporter( net::URLRequestContext* request_context) - : certificate_reporter_(new CertificateErrorReporter( + : certificate_reporter_(new chrome_browser_net::CertificateErrorReporter( request_context, GURL(kFraudulentCertificateUploadEndpoint), - CertificateErrorReporter::DO_NOT_SEND_COOKIES)) { + chrome_browser_net::CertificateErrorReporter::DO_NOT_SEND_COOKIES)) { } ChromeFraudulentCertificateReporter::ChromeFraudulentCertificateReporter( - scoped_ptr<CertificateErrorReporter> certificate_reporter) + scoped_ptr<chrome_browser_net::CertificateErrorReporter> + certificate_reporter) : certificate_reporter_(certificate_reporter.Pass()) { } @@ -45,9 +45,15 @@ void ChromeFraudulentCertificateReporter::SendReport( if (!net::TransportSecurityState::IsGooglePinnedProperty(hostname)) return; + CertificateErrorReport report(hostname, ssl_info); + std::string serialized_report; + if (!report.Serialize(&serialized_report)) { + LOG(ERROR) << "Failed to serialize pinning violation report."; + return; + } + certificate_reporter_->SendReport( - CertificateErrorReporter::REPORT_TYPE_PINNING_VIOLATION, hostname, - ssl_info); + chrome_browser_net::CertificateErrorReporter:: + REPORT_TYPE_PINNING_VIOLATION, + serialized_report); } - -} // namespace chrome_browser_net diff --git a/chrome/browser/net/chrome_fraudulent_certificate_reporter.h b/chrome/browser/ssl/chrome_fraudulent_certificate_reporter.h index 86575ea..3c4d086 100644 --- a/chrome/browser/net/chrome_fraudulent_certificate_reporter.h +++ b/chrome/browser/ssl/chrome_fraudulent_certificate_reporter.h @@ -1,9 +1,9 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright 2015 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROME_BROWSER_NET_CHROME_FRAUDULENT_CERTIFICATE_REPORTER_H_ -#define CHROME_BROWSER_NET_CHROME_FRAUDULENT_CERTIFICATE_REPORTER_H_ +#ifndef CHROME_BROWSER_SSL_CHROME_FRAUDULENT_CERTIFICATE_REPORTER_H_ +#define CHROME_BROWSER_SSL_CHROME_FRAUDULENT_CERTIFICATE_REPORTER_H_ #include <set> #include <string> @@ -11,13 +11,13 @@ #include "base/memory/scoped_ptr.h" #include "net/url_request/fraudulent_certificate_reporter.h" -namespace net { -class URLRequestContext; -} - namespace chrome_browser_net { - class CertificateErrorReporter; +} // namespace chrome_browser_net + +namespace net { +class URLRequestContext; +} // namespace net class ChromeFraudulentCertificateReporter : public net::FraudulentCertificateReporter { @@ -26,8 +26,8 @@ class ChromeFraudulentCertificateReporter net::URLRequestContext* request_context); // Useful for tests to use a mock reporter. - explicit ChromeFraudulentCertificateReporter( - scoped_ptr<CertificateErrorReporter> certificate_reporter); + explicit ChromeFraudulentCertificateReporter(scoped_ptr< + chrome_browser_net::CertificateErrorReporter> certificate_reporter); ~ChromeFraudulentCertificateReporter() override; @@ -36,11 +36,10 @@ class ChromeFraudulentCertificateReporter const net::SSLInfo& ssl_info) override; private: - scoped_ptr<CertificateErrorReporter> certificate_reporter_; + scoped_ptr<chrome_browser_net::CertificateErrorReporter> + certificate_reporter_; DISALLOW_COPY_AND_ASSIGN(ChromeFraudulentCertificateReporter); }; -} // namespace chrome_browser_net - -#endif // CHROME_BROWSER_NET_CHROME_FRAUDULENT_CERTIFICATE_REPORTER_H_ +#endif // CHROME_BROWSER_SSL_CHROME_FRAUDULENT_CERTIFICATE_REPORTER_H_ diff --git a/chrome/browser/net/chrome_fraudulent_certificate_reporter_unittest.cc b/chrome/browser/ssl/chrome_fraudulent_certificate_reporter_unittest.cc index f3ecf48..a321388 100644 --- a/chrome/browser/net/chrome_fraudulent_certificate_reporter_unittest.cc +++ b/chrome/browser/ssl/chrome_fraudulent_certificate_reporter_unittest.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/net/chrome_fraudulent_certificate_reporter.h" +#include "chrome/browser/ssl/chrome_fraudulent_certificate_reporter.h" #include <string> @@ -26,10 +26,11 @@ #include "net/url_request/url_request_test_util.h" #include "testing/gtest/include/gtest/gtest.h" +using chrome_browser_net::CertificateErrorReporter; using content::BrowserThread; using net::SSLInfo; -namespace chrome_browser_net { +namespace { // Builds an SSLInfo from an invalid cert chain. In this case, the cert is // expired; what matters is that the cert would not pass even a normal @@ -38,8 +39,8 @@ namespace chrome_browser_net { static SSLInfo GetBadSSLInfo() { SSLInfo info; - info.cert = net::ImportCertFromFile(net::GetTestCertsDirectory(), - "expired_cert.pem"); + info.cert = + net::ImportCertFromFile(net::GetTestCertsDirectory(), "expired_cert.pem"); info.cert_status = net::CERT_STATUS_DATE_INVALID; info.is_issued_by_known_root = false; @@ -124,19 +125,16 @@ class MockReporter : public CertificateErrorReporter { CertificateErrorReporter::DO_NOT_SEND_COOKIES) {} void SendReport(ReportType type, - const std::string& hostname, - const net::SSLInfo& ssl_info) override { + const std::string& serialized_report) override { EXPECT_EQ(type, REPORT_TYPE_PINNING_VIOLATION); - EXPECT_FALSE(hostname.empty()); - EXPECT_TRUE(ssl_info.is_valid()); - CertificateErrorReporter::SendReport(type, hostname, ssl_info); + EXPECT_FALSE(serialized_report.empty()); + CertificateErrorReporter::SendReport(type, serialized_report); } private: scoped_ptr<net::URLRequest> CreateURLRequest( net::URLRequestContext* context) override { - return context->CreateRequest(GURL(std::string()), - net::DEFAULT_PRIORITY, + return context->CreateRequest(GURL(std::string()), net::DEFAULT_PRIORITY, NULL); } }; @@ -192,4 +190,4 @@ TEST(ChromeFraudulentCertificateReporterTest, ReportIsNotSent) { loop.RunUntilIdle(); } -} // namespace chrome_browser_net +} // namespace diff --git a/chrome/browser/ssl/ssl_blocking_page.cc b/chrome/browser/ssl/ssl_blocking_page.cc index 46ad971..c81e436 100644 --- a/chrome/browser/ssl/ssl_blocking_page.cc +++ b/chrome/browser/ssl/ssl_blocking_page.cc @@ -28,6 +28,7 @@ #include "chrome/browser/interstitials/security_interstitial_metrics_helper.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/renderer_preferences_util.h" +#include "chrome/browser/ssl/certificate_error_report.h" #include "chrome/browser/ssl/ssl_cert_reporter.h" #include "chrome/browser/ssl/ssl_error_classification.h" #include "chrome/browser/ssl/ssl_error_info.h" @@ -663,8 +664,15 @@ void SSLBlockingPage::FinishCertCollection() { SecurityInterstitialMetricsHelper::EXTENDED_REPORTING_IS_ENABLED); if (ShouldReportCertificateError()) { - ssl_cert_reporter_->ReportInvalidCertificateChain(request_url().host(), - ssl_info_); + std::string serialized_report; + CertificateErrorReport report(request_url().host(), ssl_info_); + + if (!report.Serialize(&serialized_report)) { + LOG(ERROR) << "Failed to serialize certificate report."; + return; + } + + ssl_cert_reporter_->ReportInvalidCertificateChain(serialized_report); } } diff --git a/chrome/browser/ssl/ssl_browser_tests.cc b/chrome/browser/ssl/ssl_browser_tests.cc index d536049..bbdd300 100644 --- a/chrome/browser/ssl/ssl_browser_tests.cc +++ b/chrome/browser/ssl/ssl_browser_tests.cc @@ -21,6 +21,8 @@ #include "chrome/browser/safe_browsing/ping_manager.h" #include "chrome/browser/safe_browsing/safe_browsing_service.h" #include "chrome/browser/safe_browsing/ui_manager.h" +#include "chrome/browser/ssl/cert_logger.pb.h" +#include "chrome/browser/ssl/certificate_error_report.h" #include "chrome/browser/ssl/chrome_ssl_host_state_delegate.h" #include "chrome/browser/ssl/ssl_blocking_page.h" #include "chrome/browser/ui/browser.h" @@ -211,10 +213,11 @@ class MockReporter : public CertificateErrorReporter { cookies_preference) {} void SendReport(CertificateErrorReporter::ReportType type, - const std::string& hostname, - const net::SSLInfo& ssl_info) override { + const std::string& serialized_report) override { + CertificateErrorReport report; + ASSERT_TRUE(report.InitializeFromString(serialized_report)); EXPECT_EQ(CertificateErrorReporter::REPORT_TYPE_EXTENDED_REPORTING, type); - latest_hostname_reported_ = hostname; + latest_hostname_reported_ = report.hostname(); } const std::string& latest_hostname_reported() { @@ -247,12 +250,12 @@ class MockSSLCertReporter : public SSLCertReporter { ~MockSSLCertReporter() override { EXPECT_EQ(expect_report_, reported_); } // SSLCertReporter implementation - void ReportInvalidCertificateChain(const std::string& hostname, - const net::SSLInfo& ssl_info) override { + void ReportInvalidCertificateChain( + const std::string& serialized_report) override { reported_ = true; if (expect_report_) { safe_browsing_ui_manager_->ReportInvalidCertificateChain( - hostname, ssl_info, report_sent_callback_); + serialized_report, report_sent_callback_); } } diff --git a/chrome/browser/ssl/ssl_cert_reporter.h b/chrome/browser/ssl/ssl_cert_reporter.h index d31c080..37c8d87 100644 --- a/chrome/browser/ssl/ssl_cert_reporter.h +++ b/chrome/browser/ssl/ssl_cert_reporter.h @@ -17,11 +17,10 @@ class SSLCertReporter { public: virtual ~SSLCertReporter() {} - // Send a report for |hostname| with the given |ssl_info| to the - // report collection endpoint. |callback| will be run when the report has - // been sent off (not necessarily after a response has been received). - virtual void ReportInvalidCertificateChain(const std::string& hostname, - const net::SSLInfo& ssl_info) = 0; + // Sends a serialized certificate report to the report collection + // endpoint. + virtual void ReportInvalidCertificateChain( + const std::string& serialized_report) = 0; }; #endif // CHROME_BROWSER_SSL_SSL_CERT_REPORTER_H_ diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn index 1d15e1d..ea7487c 100644 --- a/chrome/browser/ui/BUILD.gn +++ b/chrome/browser/ui/BUILD.gn @@ -42,7 +42,8 @@ source_set("ui") { "//chrome:strings", "//chrome/app/resources:platform_locale_settings", "//chrome/app/theme:theme_resources", - "//chrome/browser/net:cert_logger_proto", + "//chrome/browser/net:encrypted_cert_logger_proto", + "//chrome/browser/ssl:cert_logger_proto", "//chrome/common", "//chrome/common/net", "//components/app_modal", diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 910fda19..cc9fd91 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -1822,8 +1822,6 @@ 'browser/net/chrome_cookie_notification_details.h', 'browser/net/chrome_extensions_network_delegate.cc', 'browser/net/chrome_extensions_network_delegate.h', - 'browser/net/chrome_fraudulent_certificate_reporter.cc', - 'browser/net/chrome_fraudulent_certificate_reporter.h', 'browser/net/chrome_http_user_agent_settings.cc', 'browser/net/chrome_http_user_agent_settings.h', 'browser/net/chrome_net_log.cc', @@ -2672,6 +2670,10 @@ 'browser/spellchecker/word_trimmer.h', ], 'chrome_browser_ssl_sources': [ + 'browser/ssl/certificate_error_report.cc', + 'browser/ssl/certificate_error_report.h', + 'browser/ssl/chrome_fraudulent_certificate_reporter.cc', + 'browser/ssl/chrome_fraudulent_certificate_reporter.h', 'browser/ssl/chrome_ssl_host_state_delegate.cc', 'browser/ssl/chrome_ssl_host_state_delegate.h', 'browser/ssl/chrome_ssl_host_state_delegate_factory.cc', @@ -3011,6 +3013,7 @@ 'chrome_resources.gyp:theme_resources', 'common', 'common_net', + 'encrypted_cert_logger_proto', 'in_memory_url_index_cache_proto', 'probe_message_proto', '../components/components.gyp:autofill_core_browser', @@ -3723,10 +3726,23 @@ { # Protobuf compiler / generator for the fraudulent certificate reporting # protocol buffer. - # GN version: //chrome/browser/net:cert_logger_proto + # GN version: //chrome/browser/ssl:cert_logger_proto 'target_name': 'cert_logger_proto', 'type': 'static_library', - 'sources': [ 'browser/net/cert_logger.proto', ], + 'sources': [ 'browser/ssl/cert_logger.proto', ], + 'variables': { + 'proto_in_dir': 'browser/ssl', + 'proto_out_dir': 'chrome/browser/ssl', + }, + 'includes': [ '../build/protoc.gypi', ], + }, + { + # Protobuf compiler / generator for the encrypted certificate + # reports protocol buffer. + # GN version: //chrome/browser/net:encrypted_cert_logger_proto + 'target_name': 'encrypted_cert_logger_proto', + 'type': 'static_library', + 'sources': [ 'browser/net/encrypted_cert_logger.proto', ], 'variables': { 'proto_in_dir': 'browser/net', 'proto_out_dir': 'chrome/browser/net', diff --git a/chrome/chrome_browser_chromeos.gypi b/chrome/chrome_browser_chromeos.gypi index 8a27199..aa01ca0 100644 --- a/chrome/chrome_browser_chromeos.gypi +++ b/chrome/chrome_browser_chromeos.gypi @@ -1127,6 +1127,7 @@ 'debugger', 'device_policy_proto', 'drive_proto', + 'encrypted_cert_logger_proto', 'installer_util', 'safe_browsing_chunk_proto', 'safe_browsing_proto', diff --git a/chrome/chrome_browser_ui.gypi b/chrome/chrome_browser_ui.gypi index 78528dd..c59917b 100644 --- a/chrome/chrome_browser_ui.gypi +++ b/chrome/chrome_browser_ui.gypi @@ -2746,6 +2746,7 @@ 'chrome_resources.gyp:theme_resources', 'common', 'common_net', + 'encrypted_cert_logger_proto', '../components/components.gyp:auto_login_parser', '../components/components.gyp:device_event_log_component', '../components/components.gyp:dom_distiller_core', diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi index e20b202..4ecceaa 100644 --- a/chrome/chrome_tests_unit.gypi +++ b/chrome/chrome_tests_unit.gypi @@ -142,7 +142,6 @@ 'browser/metrics/variations/variations_service_unittest.cc', 'browser/mod_pagespeed/mod_pagespeed_metrics_unittest.cc', 'browser/net/certificate_error_reporter_unittest.cc', - 'browser/net/chrome_fraudulent_certificate_reporter_unittest.cc', 'browser/net/chrome_network_delegate_unittest.cc', 'browser/net/dns_probe_runner_unittest.cc', 'browser/net/dns_probe_service_unittest.cc', @@ -248,6 +247,8 @@ 'browser/signin/signin_manager_unittest.cc', 'browser/signin/signin_tracker_unittest.cc', 'browser/signin/test_signin_client_builder.cc', + 'browser/ssl/certificate_error_report_unittest.cc', + 'browser/ssl/chrome_fraudulent_certificate_reporter_unittest.cc', 'browser/ssl/ssl_error_classification_unittest.cc', 'browser/ssl/ssl_error_handler_unittest.cc', 'browser/status_icons/status_icon_menu_model_unittest.cc', |