blob: 750fcd963ec5d96db0aa0f76e148ca7c74c97239 [file] [log] [blame]
// Copyright 2017 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/ssl_error_assistant.h"
#include "base/memory/ptr_util.h"
#include "chrome/browser/ssl/ssl_error_assistant.pb.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "crypto/sha2.h"
#include "net/cert/asn1_util.h"
#include "net/test/cert_test_util.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/test_certificate_data.h"
#include "net/test/test_data_directory.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
const net::SHA256HashValue kCertPublicKeyHashValue = {{0x01, 0x02}};
const uint32_t kLargeVersionId = 0xFFFFFFu;
// These certificates are self signed certificates with relevant issuer common
// names generated using the following openssl command:
// openssl req -new -x509 -keyout server.pem -out server.pem -days 365 -nodes
// Common name: "Misconfig Software"
// Organization name: "Test Company"
const char kMisconfigSoftwareCert[] =
"-----BEGIN CERTIFICATE-----\n"
"MIIC5DCCAk2gAwIBAgIJAPYPMpr0AIDBMA0GCSqGSIb3DQEBBQUAMFYxCzAJBgNV\n"
"BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMRUwEwYDVQQKEwxUZXN0IENvbXBh\n"
"bnkxGzAZBgNVBAMTEk1pc2NvbmZpZyBTb2Z0d2FyZTAeFw0xNzEwMTcxOTQyMzFa\n"
"Fw0xODEwMTcxOTQyMzFaMFYxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0\n"
"YXRlMRUwEwYDVQQKEwxUZXN0IENvbXBhbnkxGzAZBgNVBAMTEk1pc2NvbmZpZyBT\n"
"b2Z0d2FyZTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAyZWnLvuD19iG5PSi\n"
"8dSVhLeuZDBtwcWBOMzga3hx7HDMd+395gstRLc1VhpMePmxUdyEpStHDiYjNF/k\n"
"GRsIXfXWpO82L7r+Fm6eym4BOw2sjX1aounljETYasREvXhEB/8WaLJfMcstUwsT\n"
"PoXgUWYkIBi/76EiWHXvYEiXV2kCAwEAAaOBuTCBtjAdBgNVHQ4EFgQUtakrb0wU\n"
"gZVXyus1vlj6T5aDEnYwgYYGA1UdIwR/MH2AFLWpK29MFIGVV8rrNb5Y+k+WgxJ2\n"
"oVqkWDBWMQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEVMBMGA1UE\n"
"ChMMVGVzdCBDb21wYW55MRswGQYDVQQDExJNaXNjb25maWcgU29mdHdhcmWCCQD2\n"
"DzKa9ACAwTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAFAFlPO3HEWQ\n"
"0XdRbeIQPVva72VFyF+ESFC6ky7GLDoaSAwRlE1i5qWfxnLbEA0b7CWjyO1tC8Uw\n"
"OMB5U9qmQouAqf5medr2pECSDimb7qBCz3kKjgZWt1Xv8w0PsW6lFVPmMsO4Zv7F\n"
"Podf1biETWgaYoT6PrUTtWG3jeSU2r9M\n"
"-----END CERTIFICATE-----";
// Common name: "ijklmn opqrs"
// Organization name: "abc defgh co"
const char kMisconfigSoftwareRegexCheckCert[] =
"-----BEGIN CERTIFICATE-----\n"
"MIIC0jCCAjugAwIBAgIJAOyyORCXGxvDMA0GCSqGSIb3DQEBBQUAMFAxCzAJBgNV\n"
"BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMRUwEwYDVQQKEwxhYmMgZGVmZ2gg\n"
"Y28xFTATBgNVBAMTDGlqa2xtbiBvcHFyczAeFw0xNzEwMTcyMjM4MzJaFw0xODEw\n"
"MTcyMjM4MzJaMFAxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMRUw\n"
"EwYDVQQKEwxhYmMgZGVmZ2ggY28xFTATBgNVBAMTDGlqa2xtbiBvcHFyczCBnzAN\n"
"BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAsnuBPW2k4+eFazC8lq7rLRNjpZ5yqEwX\n"
"LBE8fxbvjXSSZAaJz/iTn+Zg/UMJz9IpulbcA/xf36JuhFYv7aClFrtg5DHaqrPf\n"
"kt7g9AM3hEIjGsdHtyAqFp/+CpySGzVpTLyT1NtHkqtkiD6HCSpWqL+m/6ibpUhy\n"
"oy9y/ZV1vVUCAwEAAaOBszCBsDAdBgNVHQ4EFgQUBk+vtSjNipTcWh3NIbtsjVN0\n"
"uJswgYAGA1UdIwR5MHeAFAZPr7UozYqU3FodzSG7bI1TdLiboVSkUjBQMQswCQYD\n"
"VQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEVMBMGA1UEChMMYWJjIGRlZmdo\n"
"IGNvMRUwEwYDVQQDEwxpamtsbW4gb3BxcnOCCQDssjkQlxsbwzAMBgNVHRMEBTAD\n"
"AQH/MA0GCSqGSIb3DQEBBQUAA4GBACv8KnNmaOqHD8QsmvaD2Yvc7dAFkCgsdQb/\n"
"Tkyw0sJN8ZH+bummkgGZLw4gzdmhVg8kGIbiDvCYgOVaIg+2H3PtkdIrW2KhyXrN\n"
"2qIa9nBvuv8LC1TAdB65DDheLh0PuTGcIwfJ7kcKi+Eo8fPbYYdyHGRw+rVWXVPz\n"
"SgZO4ZYq\n"
"-----END CERTIFICATE-----";
} // namespace
class SSLErrorAssistantTest : public ChromeRenderViewHostTestHarness {
public:
void SetUp() override {
ChromeRenderViewHostTestHarness::SetUp();
error_assistant_.reset(new SSLErrorAssistant());
ssl_info_.cert = net::ImportCertFromFile(
net::GetTestCertsDirectory(), "subjectAltName_www_example_com.pem");
ssl_info_.cert_status = net::CERT_STATUS_COMMON_NAME_INVALID;
ssl_info_.public_key_hashes.push_back(
net::HashValue(kCertPublicKeyHashValue));
}
void TearDown() override {
error_assistant_.reset();
ChromeRenderViewHostTestHarness::TearDown();
}
void TestMITMSoftwareMatchFromString(const std::string& cert,
const std::string& match_result) {
net::CertificateList certs =
net::X509Certificate::CreateCertificateListFromBytes(
cert.data(), cert.size(), net::X509Certificate::FORMAT_AUTO);
ASSERT_FALSE(certs.empty());
EXPECT_EQ(match_result,
error_assistant()->MatchKnownMITMSoftware(certs[0]));
}
protected:
SSLErrorAssistantTest() {
embedded_test_server_ = base::MakeUnique<net::EmbeddedTestServer>();
}
~SSLErrorAssistantTest() override {}
SSLErrorAssistant* error_assistant() const { return error_assistant_.get(); }
net::EmbeddedTestServer* embedded_test_server() const {
return embedded_test_server_.get();
}
const net::SSLInfo& ssl_info() { return ssl_info_; }
private:
net::SSLInfo ssl_info_;
std::unique_ptr<SSLErrorAssistant> error_assistant_;
std::unique_ptr<net::EmbeddedTestServer> embedded_test_server_;
DISALLOW_COPY_AND_ASSIGN(SSLErrorAssistantTest);
};
// Test to see if IsKnownCaptivePortalCertificate() returns the correct value.
TEST_F(SSLErrorAssistantTest, CaptivePortalCertificateList) {
ASSERT_TRUE(embedded_test_server()->Start());
EXPECT_EQ(1u, ssl_info().public_key_hashes.size());
// Test without the known captive portal certificate in config_proto.
auto config_proto =
base::MakeUnique<chrome_browser_ssl::SSLErrorAssistantConfig>();
config_proto->set_version_id(kLargeVersionId);
config_proto->add_captive_portal_cert()->set_sha256_hash("sha256/boxfish");
config_proto->add_captive_portal_cert()->set_sha256_hash(
"sha256/treecreeper");
error_assistant()->SetErrorAssistantProto(std::move(config_proto));
EXPECT_FALSE(error_assistant()->IsKnownCaptivePortalCertificate(ssl_info()));
error_assistant()->ResetForTesting();
// Test with the known captive portal certificate in config_proto.
config_proto.reset(new chrome_browser_ssl::SSLErrorAssistantConfig());
config_proto->set_version_id(kLargeVersionId);
config_proto->add_captive_portal_cert()->set_sha256_hash("sha256/boxfish");
config_proto->add_captive_portal_cert()->set_sha256_hash(
ssl_info().public_key_hashes[0].ToString());
config_proto->add_captive_portal_cert()->set_sha256_hash(
"sha256/treecreeper");
error_assistant()->SetErrorAssistantProto(std::move(config_proto));
EXPECT_TRUE(error_assistant()->IsKnownCaptivePortalCertificate(ssl_info()));
}
// Test to see if the MitM Software gets matched correctly.
TEST_F(SSLErrorAssistantTest, MitMSoftwareMatching) {
ASSERT_TRUE(embedded_test_server()->Start());
auto config_proto =
base::MakeUnique<chrome_browser_ssl::SSLErrorAssistantConfig>();
config_proto->set_version_id(kLargeVersionId);
// Tests for a basic and more complex regex match.
chrome_browser_ssl::MITMSoftware* filter = config_proto->add_mitm_software();
filter->set_name("Basic Check");
filter->set_issuer_common_name_regex("Misconfig Software");
filter->set_issuer_organization_regex("Test Company");
filter = config_proto->add_mitm_software();
filter->set_name("Regex Check");
filter->set_issuer_common_name_regex("ij[a-z]+n opqrs");
filter->set_issuer_organization_regex("abc de[a-z0-9]gh [a-z]+");
error_assistant()->SetErrorAssistantProto(std::move(config_proto));
TestMITMSoftwareMatchFromString(kMisconfigSoftwareCert, "Basic Check");
TestMITMSoftwareMatchFromString(kMisconfigSoftwareRegexCheckCert,
"Regex Check");
error_assistant()->ResetForTesting();
// Tests for no matches.
config_proto.reset(new chrome_browser_ssl::SSLErrorAssistantConfig());
config_proto->set_version_id(kLargeVersionId);
filter = config_proto->add_mitm_software();
filter->set_name("Incorrect common name");
filter->set_issuer_common_name_regex("Misconfig Sotware");
filter->set_issuer_organization_regex("Test Company");
filter = config_proto->add_mitm_software();
filter->set_name("Incorrect company name");
filter->set_issuer_common_name_regex("Misconfig Software");
filter->set_issuer_organization_regex("Tst Company");
error_assistant()->SetErrorAssistantProto(std::move(config_proto));
TestMITMSoftwareMatchFromString(kMisconfigSoftwareCert, "");
}