blob: d0e2061fa0b06a20fc21c1d56212745d670126b4 [file] [log] [blame]
// Copyright 2014 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 "services/network/cert_verify_proc_chromeos.h"
#include <stddef.h>
#include "crypto/nss_util_internal.h"
#include "crypto/scoped_test_nss_chromeos_user.h"
#include "net/base/net_errors.h"
#include "net/cert/cert_verify_proc.h"
#include "net/cert/cert_verify_result.h"
#include "net/cert/nss_cert_database_chromeos.h"
#include "net/cert/x509_util.h"
#include "net/cert/x509_util_nss.h"
#include "net/test/cert_test_util.h"
#include "net/test/test_data_directory.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace network {
namespace {
std::string GetSubjectCN(CRYPTO_BUFFER* cert_handle) {
scoped_refptr<net::X509Certificate> cert =
net::X509Certificate::CreateFromBuffer(bssl::UpRef(cert_handle), {});
if (!cert)
return std::string();
return "CN=" + cert->subject().common_name;
}
} // namespace
class CertVerifyProcChromeOSTest : public testing::Test {
public:
CertVerifyProcChromeOSTest() : user_1_("user1"), user_2_("user2") {}
void SetUp() override {
// Initialize nss_util slots.
ASSERT_TRUE(user_1_.constructed_successfully());
ASSERT_TRUE(user_2_.constructed_successfully());
user_1_.FinishInit();
user_2_.FinishInit();
// Create NSSCertDatabaseChromeOS for each user.
db_1_.reset(new net::NSSCertDatabaseChromeOS(
crypto::GetPublicSlotForChromeOSUser(user_1_.username_hash()),
crypto::GetPrivateSlotForChromeOSUser(
user_1_.username_hash(),
base::Callback<void(crypto::ScopedPK11Slot)>())));
db_2_.reset(new net::NSSCertDatabaseChromeOS(
crypto::GetPublicSlotForChromeOSUser(user_2_.username_hash()),
crypto::GetPrivateSlotForChromeOSUser(
user_2_.username_hash(),
base::Callback<void(crypto::ScopedPK11Slot)>())));
// Create default verifier and for each user.
verify_proc_default_ = new CertVerifyProcChromeOS();
verify_proc_1_ = new CertVerifyProcChromeOS(db_1_->GetPublicSlot());
verify_proc_2_ = new CertVerifyProcChromeOS(db_2_->GetPublicSlot());
// Load test cert chains from disk.
certs_1_ = net::CreateCERTCertificateListFromFile(
net::GetTestCertsDirectory(), "multi-root-chain1.pem",
net::X509Certificate::FORMAT_AUTO);
ASSERT_EQ(4U, certs_1_.size());
certs_2_ = net::CreateCERTCertificateListFromFile(
net::GetTestCertsDirectory(), "multi-root-chain2.pem",
net::X509Certificate::FORMAT_AUTO);
ASSERT_EQ(4U, certs_2_.size());
// The chains:
// 1. A (end-entity) -> B -> C -> D (self-signed root)
// 2. A (end-entity) -> B -> C2 -> E (self-signed root)
ASSERT_TRUE(net::x509_util::IsSameCertificate(certs_1_[0].get(),
certs_2_[0].get()));
ASSERT_TRUE(net::x509_util::IsSameCertificate(certs_1_[1].get(),
certs_2_[1].get()));
ASSERT_FALSE(net::x509_util::IsSameCertificate(certs_1_[2].get(),
certs_2_[2].get()));
ASSERT_STREQ("CN=C CA - Multi-root", certs_1_[2]->subjectName);
ASSERT_STREQ("CN=C CA - Multi-root", certs_2_[2]->subjectName);
ASSERT_STREQ("CN=D Root CA - Multi-root", certs_1_.back()->subjectName);
ASSERT_STREQ("CN=E Root CA - Multi-root", certs_2_.back()->subjectName);
root_1_.push_back(
net::x509_util::DupCERTCertificate(certs_1_.back().get()));
ASSERT_TRUE(root_1_.back());
root_2_.push_back(
net::x509_util::DupCERTCertificate(certs_2_.back().get()));
ASSERT_TRUE(root_2_.back());
ASSERT_STREQ("CN=D Root CA - Multi-root", root_1_.back()->subjectName);
ASSERT_STREQ("CN=E Root CA - Multi-root", root_2_.back()->subjectName);
server_ = net::x509_util::CreateX509CertificateFromCERTCertificate(
certs_1_[0].get());
ASSERT_TRUE(server_);
}
int VerifyWithAdditionalTrustAnchors(
net::CertVerifyProc* verify_proc,
const net::CertificateList& additional_trust_anchors,
net::X509Certificate* cert,
std::string* root_subject_name) {
int flags = 0;
net::CertVerifyResult verify_result;
int error =
verify_proc->Verify(cert, "127.0.0.1", std::string(), flags, NULL,
additional_trust_anchors, &verify_result);
if (!verify_result.verified_cert->intermediate_buffers().empty()) {
root_subject_name->assign(GetSubjectCN(
verify_result.verified_cert->intermediate_buffers().back().get()));
} else {
root_subject_name->clear();
}
return error;
}
int Verify(net::CertVerifyProc* verify_proc,
net::X509Certificate* cert,
std::string* root_subject_name) {
net::CertificateList additional_trust_anchors;
return VerifyWithAdditionalTrustAnchors(
verify_proc, additional_trust_anchors, cert, root_subject_name);
}
protected:
crypto::ScopedTestNSSChromeOSUser user_1_;
crypto::ScopedTestNSSChromeOSUser user_2_;
std::unique_ptr<net::NSSCertDatabaseChromeOS> db_1_;
std::unique_ptr<net::NSSCertDatabaseChromeOS> db_2_;
scoped_refptr<net::CertVerifyProc> verify_proc_default_;
scoped_refptr<net::CertVerifyProc> verify_proc_1_;
scoped_refptr<net::CertVerifyProc> verify_proc_2_;
net::ScopedCERTCertificateList certs_1_;
net::ScopedCERTCertificateList certs_2_;
net::ScopedCERTCertificateList root_1_;
net::ScopedCERTCertificateList root_2_;
scoped_refptr<net::X509Certificate> server_;
};
// Test that the CertVerifyProcChromeOS doesn't trusts roots that are in other
// user's slots or that have been deleted, and that verifying done by one user
// doesn't affect verifications done by others.
TEST_F(CertVerifyProcChromeOSTest, TestChainVerify) {
std::string verify_root;
// Before either of the root certs have been trusted, all verifications should
// fail with CERT_AUTHORITY_INVALID.
EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
Verify(verify_proc_default_.get(), server_.get(), &verify_root));
EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
Verify(verify_proc_1_.get(), server_.get(), &verify_root));
EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
Verify(verify_proc_2_.get(), server_.get(), &verify_root));
// Import and trust the D root for user 1.
net::NSSCertDatabase::ImportCertFailureList failed;
EXPECT_TRUE(db_1_->ImportCACerts(root_1_, net::NSSCertDatabase::TRUSTED_SSL,
&failed));
EXPECT_EQ(0U, failed.size());
// Imported CA certs are not trusted by default verifier.
EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
Verify(verify_proc_default_.get(), server_.get(), &verify_root));
// User 1 should now verify successfully through the D root.
EXPECT_EQ(net::OK, Verify(verify_proc_1_.get(), server_.get(), &verify_root));
EXPECT_EQ("CN=D Root CA - Multi-root", verify_root);
// User 2 should still fail.
EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
Verify(verify_proc_2_.get(), server_.get(), &verify_root));
// Import and trust the E root for user 2.
failed.clear();
EXPECT_TRUE(db_2_->ImportCACerts(root_2_, net::NSSCertDatabase::TRUSTED_SSL,
&failed));
EXPECT_EQ(0U, failed.size());
// Imported CA certs are not trusted by default verifier.
EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
Verify(verify_proc_default_.get(), server_.get(), &verify_root));
// User 1 should still verify successfully through the D root.
EXPECT_EQ(net::OK, Verify(verify_proc_1_.get(), server_.get(), &verify_root));
EXPECT_EQ("CN=D Root CA - Multi-root", verify_root);
// User 2 should now verify successfully through the E root.
EXPECT_EQ(net::OK, Verify(verify_proc_2_.get(), server_.get(), &verify_root));
EXPECT_EQ("CN=E Root CA - Multi-root", verify_root);
// Delete D root.
EXPECT_TRUE(db_1_->DeleteCertAndKey(root_1_[0].get()));
EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
Verify(verify_proc_default_.get(), server_.get(), &verify_root));
// User 1 should now fail to verify.
EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
Verify(verify_proc_1_.get(), server_.get(), &verify_root));
// User 2 should still verify successfully through the E root.
EXPECT_EQ(net::OK, Verify(verify_proc_2_.get(), server_.get(), &verify_root));
EXPECT_EQ("CN=E Root CA - Multi-root", verify_root);
// Delete E root.
EXPECT_TRUE(db_2_->DeleteCertAndKey(root_2_[0].get()));
EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
Verify(verify_proc_default_.get(), server_.get(), &verify_root));
// User 1 should still fail to verify.
EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
Verify(verify_proc_1_.get(), server_.get(), &verify_root));
// User 2 should now fail to verify.
EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
Verify(verify_proc_2_.get(), server_.get(), &verify_root));
}
// Test that roots specified through additional_trust_anchors are trusted for
// that verification, and that there is not any caching that affects later
// verifications.
TEST_F(CertVerifyProcChromeOSTest, TestAdditionalTrustAnchors) {
EXPECT_TRUE(verify_proc_default_->SupportsAdditionalTrustAnchors());
EXPECT_TRUE(verify_proc_1_->SupportsAdditionalTrustAnchors());
std::string verify_root;
net::CertificateList additional_trust_anchors;
scoped_refptr<net::X509Certificate> d_root_ca =
net::x509_util::CreateX509CertificateFromCERTCertificate(
certs_1_.back().get());
ASSERT_TRUE(d_root_ca);
// Before either of the root certs have been trusted, all verifications should
// fail with CERT_AUTHORITY_INVALID.
EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
VerifyWithAdditionalTrustAnchors(verify_proc_default_.get(),
additional_trust_anchors,
server_.get(), &verify_root));
EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
VerifyWithAdditionalTrustAnchors(verify_proc_1_.get(),
additional_trust_anchors,
server_.get(), &verify_root));
// Use D Root CA as additional trust anchor. Verifications should succeed now.
additional_trust_anchors.push_back(d_root_ca);
EXPECT_EQ(net::OK, VerifyWithAdditionalTrustAnchors(
verify_proc_default_.get(), additional_trust_anchors,
server_.get(), &verify_root));
EXPECT_EQ("CN=D Root CA - Multi-root", verify_root);
EXPECT_EQ(net::OK, VerifyWithAdditionalTrustAnchors(
verify_proc_1_.get(), additional_trust_anchors,
server_.get(), &verify_root));
EXPECT_EQ("CN=D Root CA - Multi-root", verify_root);
// User 2 should still fail.
EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
VerifyWithAdditionalTrustAnchors(verify_proc_2_.get(),
net::CertificateList(),
server_.get(), &verify_root));
// Without additional trust anchors, verification should fail again.
additional_trust_anchors.clear();
EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
VerifyWithAdditionalTrustAnchors(verify_proc_default_.get(),
additional_trust_anchors,
server_.get(), &verify_root));
EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
VerifyWithAdditionalTrustAnchors(verify_proc_1_.get(),
additional_trust_anchors,
server_.get(), &verify_root));
// Import and trust the D Root CA for user 2.
net::NSSCertDatabase::ImportCertFailureList failed;
EXPECT_TRUE(db_2_->ImportCACerts(root_1_, net::NSSCertDatabase::TRUSTED_SSL,
&failed));
EXPECT_EQ(0U, failed.size());
// Use D Root CA as additional trust anchor. Verifications should still
// succeed even if the cert is trusted by a different profile.
additional_trust_anchors.push_back(d_root_ca);
EXPECT_EQ(net::OK, VerifyWithAdditionalTrustAnchors(
verify_proc_default_.get(), additional_trust_anchors,
server_.get(), &verify_root));
EXPECT_EQ("CN=D Root CA - Multi-root", verify_root);
EXPECT_EQ(net::OK, VerifyWithAdditionalTrustAnchors(
verify_proc_1_.get(), additional_trust_anchors,
server_.get(), &verify_root));
EXPECT_EQ("CN=D Root CA - Multi-root", verify_root);
EXPECT_EQ(net::OK, VerifyWithAdditionalTrustAnchors(
verify_proc_2_.get(), additional_trust_anchors,
server_.get(), &verify_root));
EXPECT_EQ("CN=D Root CA - Multi-root", verify_root);
}
class CertVerifyProcChromeOSOrderingTest
: public CertVerifyProcChromeOSTest,
public ::testing::WithParamInterface<std::tuple<bool, int, std::string>> {
};
// Test a variety of different combinations of (maybe) verifying / (maybe)
// importing / verifying again, to try to find any cases where caching might
// affect the results.
// http://crbug.com/396501
TEST_P(CertVerifyProcChromeOSOrderingTest, DISABLED_TrustThenVerify) {
const ParamType& param = GetParam();
const bool verify_first = std::get<0>(param);
const int trust_bitmask = std::get<1>(param);
const std::string test_order = std::get<2>(param);
DVLOG(1) << "verify_first: " << verify_first
<< " trust_bitmask: " << trust_bitmask
<< " test_order: " << test_order;
std::string verify_root;
if (verify_first) {
// Before either of the root certs have been trusted, all verifications
// should fail with CERT_AUTHORITY_INVALID.
EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
Verify(verify_proc_default_.get(), server_.get(), &verify_root));
EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
Verify(verify_proc_1_.get(), server_.get(), &verify_root));
EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
Verify(verify_proc_2_.get(), server_.get(), &verify_root));
}
int expected_user1_result = net::ERR_CERT_AUTHORITY_INVALID;
int expected_user2_result = net::ERR_CERT_AUTHORITY_INVALID;
if (trust_bitmask & 1) {
expected_user1_result = net::OK;
// Import and trust the D root for user 1.
net::NSSCertDatabase::ImportCertFailureList failed;
EXPECT_TRUE(db_1_->ImportCACerts(root_1_, net::NSSCertDatabase::TRUSTED_SSL,
&failed));
EXPECT_EQ(0U, failed.size());
for (size_t i = 0; i < failed.size(); ++i) {
LOG(ERROR) << "import fail " << failed[i].net_error << " for "
<< failed[i].certificate->subjectName;
}
}
if (trust_bitmask & 2) {
expected_user2_result = net::OK;
// Import and trust the E root for user 2.
net::NSSCertDatabase::ImportCertFailureList failed;
EXPECT_TRUE(db_2_->ImportCACerts(root_2_, net::NSSCertDatabase::TRUSTED_SSL,
&failed));
EXPECT_EQ(0U, failed.size());
for (size_t i = 0; i < failed.size(); ++i) {
LOG(ERROR) << "import fail " << failed[i].net_error << " for "
<< failed[i].certificate->subjectName;
}
}
// Repeat the tests twice, they should return the same each time.
for (int i = 0; i < 2; ++i) {
SCOPED_TRACE(i);
for (std::string::const_iterator j = test_order.begin();
j != test_order.end(); ++j) {
switch (*j) {
case 'd':
// Default verifier should always fail.
EXPECT_EQ(
net::ERR_CERT_AUTHORITY_INVALID,
Verify(verify_proc_default_.get(), server_.get(), &verify_root));
break;
case '1':
EXPECT_EQ(expected_user1_result,
Verify(verify_proc_1_.get(), server_.get(), &verify_root));
if (expected_user1_result == net::OK)
EXPECT_EQ("CN=D Root CA - Multi-root", verify_root);
break;
case '2':
EXPECT_EQ(expected_user2_result,
Verify(verify_proc_2_.get(), server_.get(), &verify_root));
if (expected_user2_result == net::OK)
EXPECT_EQ("CN=E Root CA - Multi-root", verify_root);
break;
default:
FAIL();
}
}
}
}
INSTANTIATE_TEST_CASE_P(
Variations,
CertVerifyProcChromeOSOrderingTest,
::testing::Combine(
::testing::Bool(),
::testing::Range(0, 1 << 2),
::testing::Values("d12", "d21", "1d2", "12d", "2d1", "21d")));
} // namespace network