1// Copyright (c) 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/chromeos/policy/policy_cert_verifier.h" 6 7#include "base/bind.h" 8#include "base/bind_helpers.h" 9#include "base/callback.h" 10#include "base/memory/ref_counted.h" 11#include "base/memory/scoped_ptr.h" 12#include "base/run_loop.h" 13#include "content/public/browser/browser_thread.h" 14#include "content/public/test/test_browser_thread_bundle.h" 15#include "crypto/nss_util.h" 16#include "net/base/net_log.h" 17#include "net/base/test_completion_callback.h" 18#include "net/base/test_data_directory.h" 19#include "net/cert/cert_trust_anchor_provider.h" 20#include "net/cert/cert_verify_proc.h" 21#include "net/cert/cert_verify_result.h" 22#include "net/cert/nss_cert_database.h" 23#include "net/cert/x509_certificate.h" 24#include "net/test/cert_test_util.h" 25#include "testing/gtest/include/gtest/gtest.h" 26 27namespace policy { 28 29// This is actually a unit test, but is linked with browser_tests because 30// importing a certificate into the NSS test database persists for the duration 31// of a process; since each browser_test runs in a separate process then this 32// won't affect subsequent tests. 33// This can be moved to the unittests target once the TODO in ~ScopedTestNSSDB 34// is fixed. 35class PolicyCertVerifierTest : public testing::Test { 36 public: 37 PolicyCertVerifierTest() : cert_db_(NULL), trust_anchor_used_(false) {} 38 39 virtual ~PolicyCertVerifierTest() {} 40 41 virtual void SetUp() OVERRIDE { 42 ASSERT_TRUE(test_nssdb_.is_open()); 43 cert_db_ = net::NSSCertDatabase::GetInstance(); 44 45 cert_verifier_.reset(new PolicyCertVerifier(base::Bind( 46 &PolicyCertVerifierTest::OnTrustAnchorUsed, base::Unretained(this)))); 47 cert_verifier_->InitializeOnIOThread(); 48 49 test_ca_cert_ = LoadCertificate("root_ca_cert.pem", net::CA_CERT); 50 ASSERT_TRUE(test_ca_cert_); 51 test_server_cert_ = LoadCertificate("ok_cert.pem", net::SERVER_CERT); 52 ASSERT_TRUE(test_server_cert_); 53 test_ca_cert_list_.push_back(test_ca_cert_); 54 } 55 56 virtual void TearDown() OVERRIDE { 57 // Destroy |cert_verifier_| before destroying the ThreadBundle, otherwise 58 // BrowserThread::CurrentlyOn checks fail. 59 cert_verifier_.reset(); 60 } 61 62 protected: 63 int VerifyTestServerCert(const net::TestCompletionCallback& test_callback, 64 net::CertVerifyResult* verify_result, 65 net::CertVerifier::RequestHandle* request_handle) { 66 return cert_verifier_->Verify(test_server_cert_.get(), 67 "127.0.0.1", 68 0, 69 NULL, 70 verify_result, 71 test_callback.callback(), 72 request_handle, 73 net::BoundNetLog()); 74 } 75 76 bool SupportsAdditionalTrustAnchors() { 77 scoped_refptr<net::CertVerifyProc> proc = 78 net::CertVerifyProc::CreateDefault(); 79 return proc->SupportsAdditionalTrustAnchors(); 80 } 81 82 // Returns whether |cert_verifier| signalled usage of one of the additional 83 // trust anchors (i.e. of |test_ca_cert_|) for the first time or since the 84 // last call of this function. 85 bool WasTrustAnchorUsedAndReset() { 86 base::RunLoop().RunUntilIdle(); 87 bool result = trust_anchor_used_; 88 trust_anchor_used_ = false; 89 return result; 90 } 91 92 // |test_ca_cert_| is the issuer of |test_server_cert_|. 93 scoped_refptr<net::X509Certificate> test_ca_cert_; 94 scoped_refptr<net::X509Certificate> test_server_cert_; 95 net::CertificateList test_ca_cert_list_; 96 net::NSSCertDatabase* cert_db_; 97 scoped_ptr<PolicyCertVerifier> cert_verifier_; 98 99 private: 100 void OnTrustAnchorUsed() { 101 trust_anchor_used_ = true; 102 } 103 104 scoped_refptr<net::X509Certificate> LoadCertificate(const std::string& name, 105 net::CertType type) { 106 scoped_refptr<net::X509Certificate> cert = 107 net::ImportCertFromFile(net::GetTestCertsDirectory(), name); 108 109 // No certificate is trusted right after it's loaded. 110 net::NSSCertDatabase::TrustBits trust = 111 cert_db_->GetCertTrust(cert.get(), type); 112 EXPECT_EQ(net::NSSCertDatabase::TRUST_DEFAULT, trust); 113 114 return cert; 115 } 116 117 bool trust_anchor_used_; 118 crypto::ScopedTestNSSDB test_nssdb_; 119 content::TestBrowserThreadBundle thread_bundle_; 120}; 121 122TEST_F(PolicyCertVerifierTest, VerifyUntrustedCert) { 123 // |test_server_cert_| is untrusted, so Verify() fails. 124 { 125 net::CertVerifyResult verify_result; 126 net::TestCompletionCallback callback; 127 net::CertVerifier::RequestHandle request_handle = NULL; 128 int error = VerifyTestServerCert(callback, &verify_result, &request_handle); 129 ASSERT_EQ(net::ERR_IO_PENDING, error); 130 EXPECT_TRUE(request_handle); 131 error = callback.WaitForResult(); 132 EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID, error); 133 } 134 135 // Issuing the same request again hits the cache. This tests the synchronous 136 // path. 137 { 138 net::CertVerifyResult verify_result; 139 net::TestCompletionCallback callback; 140 net::CertVerifier::RequestHandle request_handle = NULL; 141 int error = VerifyTestServerCert(callback, &verify_result, &request_handle); 142 EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID, error); 143 } 144 145 EXPECT_FALSE(WasTrustAnchorUsedAndReset()); 146} 147 148TEST_F(PolicyCertVerifierTest, VerifyTrustedCert) { 149 // Make the database trust |test_ca_cert_|. 150 net::NSSCertDatabase::ImportCertFailureList failure_list; 151 ASSERT_TRUE(cert_db_->ImportCACerts( 152 test_ca_cert_list_, net::NSSCertDatabase::TRUSTED_SSL, &failure_list)); 153 ASSERT_TRUE(failure_list.empty()); 154 155 // Verify that it is now trusted. 156 net::NSSCertDatabase::TrustBits trust = 157 cert_db_->GetCertTrust(test_ca_cert_.get(), net::CA_CERT); 158 EXPECT_EQ(net::NSSCertDatabase::TRUSTED_SSL, trust); 159 160 // Verify() successfully verifies |test_server_cert_| after it was imported. 161 net::CertVerifyResult verify_result; 162 net::TestCompletionCallback callback; 163 net::CertVerifier::RequestHandle request_handle = NULL; 164 int error = VerifyTestServerCert(callback, &verify_result, &request_handle); 165 ASSERT_EQ(net::ERR_IO_PENDING, error); 166 EXPECT_TRUE(request_handle); 167 error = callback.WaitForResult(); 168 EXPECT_EQ(net::OK, error); 169 170 // The additional trust anchors were not used, since the certificate is 171 // trusted from the database. 172 EXPECT_FALSE(WasTrustAnchorUsedAndReset()); 173} 174 175TEST_F(PolicyCertVerifierTest, VerifyUsingAdditionalTrustAnchor) { 176 if (!SupportsAdditionalTrustAnchors()) { 177 LOG(INFO) << "Test skipped on this platform. NSS >= 3.14.2 required."; 178 return; 179 } 180 181 // |test_server_cert_| is untrusted, so Verify() fails. 182 { 183 net::CertVerifyResult verify_result; 184 net::TestCompletionCallback callback; 185 net::CertVerifier::RequestHandle request_handle = NULL; 186 int error = VerifyTestServerCert(callback, &verify_result, &request_handle); 187 ASSERT_EQ(net::ERR_IO_PENDING, error); 188 EXPECT_TRUE(request_handle); 189 error = callback.WaitForResult(); 190 EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID, error); 191 } 192 EXPECT_FALSE(WasTrustAnchorUsedAndReset()); 193 194 // Verify() again with the additional trust anchors. 195 cert_verifier_->SetTrustAnchors(test_ca_cert_list_); 196 { 197 net::CertVerifyResult verify_result; 198 net::TestCompletionCallback callback; 199 net::CertVerifier::RequestHandle request_handle = NULL; 200 int error = VerifyTestServerCert(callback, &verify_result, &request_handle); 201 ASSERT_EQ(net::ERR_IO_PENDING, error); 202 EXPECT_TRUE(request_handle); 203 error = callback.WaitForResult(); 204 EXPECT_EQ(net::OK, error); 205 } 206 EXPECT_TRUE(WasTrustAnchorUsedAndReset()); 207 208 // Verify() again with the additional trust anchors will hit the cache. 209 cert_verifier_->SetTrustAnchors(test_ca_cert_list_); 210 { 211 net::CertVerifyResult verify_result; 212 net::TestCompletionCallback callback; 213 net::CertVerifier::RequestHandle request_handle = NULL; 214 int error = VerifyTestServerCert(callback, &verify_result, &request_handle); 215 EXPECT_EQ(net::OK, error); 216 } 217 EXPECT_TRUE(WasTrustAnchorUsedAndReset()); 218 219 // Verifying after removing the trust anchors should now fail. 220 cert_verifier_->SetTrustAnchors(net::CertificateList()); 221 { 222 net::CertVerifyResult verify_result; 223 net::TestCompletionCallback callback; 224 net::CertVerifier::RequestHandle request_handle = NULL; 225 int error = VerifyTestServerCert(callback, &verify_result, &request_handle); 226 // Note: this hits the cached result from the first Verify() in this test. 227 EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID, error); 228 } 229 // The additional trust anchors were reset, thus |cert_verifier_| should not 230 // signal it's usage anymore. 231 EXPECT_FALSE(WasTrustAnchorUsedAndReset()); 232} 233 234} // namespace policy 235