15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/net/chrome_fraudulent_certificate_reporter.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <string>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/bind.h"
102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/files/file_path.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/memory/scoped_ptr.h"
129ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch#include "base/message_loop/message_loop.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/synchronization/waitable_event.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/threading/thread.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/test/test_browser_thread.h"
160f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)#include "net/base/request_priority.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/test_data_directory.h"
18c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "net/cert/x509_certificate.h"
192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "net/http/transport_security_state.h"
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "net/ssl/ssl_info.h"
21c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "net/test/cert_test_util.h"
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/url_request/fraudulent_certificate_reporter.h"
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/url_request/url_request.h"
2403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)#include "net/url_request/url_request_context.h"
25f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#include "net/url_request/url_request_test_util.h"
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "testing/gtest/include/gtest/gtest.h"
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using content::BrowserThread;
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using net::SSLInfo;
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace chrome_browser_net {
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Builds an SSLInfo from an invalid cert chain. In this case, the cert is
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// expired; what matters is that the cert would not pass even a normal
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// sanity check. We test that we DO NOT send a fraudulent certificate report
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// in this case.
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static SSLInfo GetBadSSLInfo() {
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SSLInfo info;
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  info.cert = net::ImportCertFromFile(net::GetTestCertsDirectory(),
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                      "expired_cert.pem");
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  info.cert_status = net::CERT_STATUS_DATE_INVALID;
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  info.is_issued_by_known_root = false;
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return info;
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Builds an SSLInfo from a "good" cert chain, as defined by IsGoodSSLInfo,
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// but which does not pass DomainState::IsChainOfPublicKeysPermitted. In this
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// case, the certificate is for mail.google.com, signed by our Chrome test
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// CA. During testing, Chrome believes this CA is part of the root system
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// store. But, this CA is not in the pin list; we test that we DO send a
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// fraudulent certicate report in this case.
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static SSLInfo GetGoodSSLInfo() {
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SSLInfo info;
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  info.cert = net::ImportCertFromFile(net::GetTestCertsDirectory(),
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                      "test_mail_google_com.pem");
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  info.is_issued_by_known_root = true;
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return info;
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Checks that |info| is good as required by the SSL checks performed in
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// URLRequestHttpJob::OnStartCompleted, which are enough to trigger pin
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// checking but not sufficient to pass
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// DomainState::IsChainOfPublicKeysPermitted.
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static bool IsGoodSSLInfo(const SSLInfo& info) {
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return info.is_valid() && info.is_issued_by_known_root;
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class TestReporter : public ChromeFraudulentCertificateReporter {
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  explicit TestReporter(net::URLRequestContext* request_context)
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      : ChromeFraudulentCertificateReporter(request_context) {}
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class SendingTestReporter : public TestReporter {
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  explicit SendingTestReporter(net::URLRequestContext* request_context)
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      : TestReporter(request_context), passed_(false) {}
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Passes if invoked with a good SSLInfo and for a hostname that is a Google
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // pinned property.
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual void SendReport(const std::string& hostname,
861320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                          const SSLInfo& ssl_info) OVERRIDE {
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    EXPECT_TRUE(IsGoodSSLInfo(ssl_info));
881320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    EXPECT_TRUE(net::TransportSecurityState::IsGooglePinnedProperty(hostname));
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    passed_ = true;
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual ~SendingTestReporter() {
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // If the object is destroyed without having its SendReport method invoked,
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // we failed.
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    EXPECT_TRUE(passed_);
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool passed_;
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class NotSendingTestReporter : public TestReporter {
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  explicit NotSendingTestReporter(net::URLRequestContext* request_context)
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      : TestReporter(request_context) {}
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Passes if invoked with a bad SSLInfo and for a hostname that is not a
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Google pinned property.
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual void SendReport(const std::string& hostname,
1091320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                          const SSLInfo& ssl_info) OVERRIDE {
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    EXPECT_FALSE(IsGoodSSLInfo(ssl_info));
1111320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    EXPECT_FALSE(net::TransportSecurityState::IsGooglePinnedProperty(hostname));
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// A ChromeFraudulentCertificateReporter that uses a MockURLRequest, but is
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// otherwise normal: reports are constructed and sent in the usual way.
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class MockReporter : public ChromeFraudulentCertificateReporter {
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  explicit MockReporter(net::URLRequestContext* request_context)
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : ChromeFraudulentCertificateReporter(request_context) {}
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1220f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  virtual scoped_ptr<net::URLRequest> CreateURLRequest(
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      net::URLRequestContext* context) OVERRIDE {
12403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    return context->CreateRequest(GURL(std::string()),
12503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)                                  net::DEFAULT_PRIORITY,
12603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)                                  NULL,
12703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)                                  NULL);
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual void SendReport(
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      const std::string& hostname,
1321320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      const net::SSLInfo& ssl_info) OVERRIDE {
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(!hostname.empty());
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(ssl_info.is_valid());
1351320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    ChromeFraudulentCertificateReporter::SendReport(hostname, ssl_info);
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static void DoReportIsSent() {
140f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  net::TestURLRequestContext context;
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SendingTestReporter reporter(&context);
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SSLInfo info = GetGoodSSLInfo();
1431320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  reporter.SendReport("mail.google.com", info);
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static void DoReportIsNotSent() {
147f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  net::TestURLRequestContext context;
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  NotSendingTestReporter reporter(&context);
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SSLInfo info = GetBadSSLInfo();
1501320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  reporter.SendReport("www.example.com", info);
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static void DoMockReportIsSent() {
154f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  net::TestURLRequestContext context;
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MockReporter reporter(&context);
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SSLInfo info = GetGoodSSLInfo();
1571320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  reporter.SendReport("mail.google.com", info);
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEST(ChromeFraudulentCertificateReporterTest, GoodBadInfo) {
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SSLInfo good = GetGoodSSLInfo();
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(IsGoodSSLInfo(good));
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SSLInfo bad = GetBadSSLInfo();
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_FALSE(IsGoodSSLInfo(bad));
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEST(ChromeFraudulentCertificateReporterTest, ReportIsSent) {
1695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::MessageLoopForIO loop;
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  content::TestBrowserThread io_thread(BrowserThread::IO, &loop);
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  loop.PostTask(FROM_HERE, base::Bind(&DoReportIsSent));
1722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  loop.RunUntilIdle();
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEST(ChromeFraudulentCertificateReporterTest, MockReportIsSent) {
1765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::MessageLoopForIO loop;
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  content::TestBrowserThread io_thread(BrowserThread::IO, &loop);
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  loop.PostTask(FROM_HERE, base::Bind(&DoMockReportIsSent));
1792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  loop.RunUntilIdle();
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEST(ChromeFraudulentCertificateReporterTest, ReportIsNotSent) {
1835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::MessageLoopForIO loop;
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  content::TestBrowserThread io_thread(BrowserThread::IO, &loop);
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  loop.PostTask(FROM_HERE, base::Bind(&DoReportIsNotSent));
1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  loop.RunUntilIdle();
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace chrome_browser_net
190