Index: chrome/browser/chromeos/policy/policy_cert_verifier_browsertest.cc |
diff --git a/chrome/browser/chromeos/policy/policy_cert_verifier_browsertest.cc b/chrome/browser/chromeos/policy/policy_cert_verifier_browsertest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..c39c460d718353e548446f3ca9a6402d6366ba97 |
--- /dev/null |
+++ b/chrome/browser/chromeos/policy/policy_cert_verifier_browsertest.cc |
@@ -0,0 +1,297 @@ |
+// Copyright (c) 2013 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/chromeos/policy/policy_cert_verifier.h" |
+ |
+#include "base/memory/ref_counted.h" |
+#include "base/memory/scoped_ptr.h" |
+#include "base/message_loop.h" |
+#include "base/run_loop.h" |
+#include "chrome/common/pref_names.h" |
+#include "chrome/test/base/testing_browser_process.h" |
+#include "chrome/test/base/testing_profile.h" |
+#include "chrome/test/base/testing_profile_manager.h" |
+#include "content/public/browser/browser_thread.h" |
+#include "content/public/test/test_browser_thread.h" |
+#include "crypto/nss_util.h" |
+#include "net/base/net_log.h" |
+#include "net/base/test_completion_callback.h" |
+#include "net/base/test_data_directory.h" |
+#include "net/cert/cert_trust_anchor_provider.h" |
+#include "net/cert/cert_verify_proc.h" |
+#include "net/cert/cert_verify_result.h" |
+#include "net/cert/nss_cert_database.h" |
+#include "net/cert/x509_certificate.h" |
+#include "net/test/cert_test_util.h" |
+#include "testing/gmock/include/gmock/gmock.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+ |
+using testing::Mock; |
+using testing::ReturnRef; |
+ |
+namespace policy { |
+ |
+namespace { |
+ |
+class MockCertTrustAnchorProvider : public net::CertTrustAnchorProvider { |
+ public: |
+ MockCertTrustAnchorProvider() {} |
+ virtual ~MockCertTrustAnchorProvider() {} |
+ |
+ MOCK_METHOD0(GetAdditionalTrustAnchors, const net::CertificateList&()); |
+}; |
+ |
+} // namespace |
+ |
+// This is actually a unit test, but is linked with browser_tests because |
+// importing a certificate into the NSS test database persists for the duration |
+// of a process; since each browser_test runs in a separate process then this |
+// won't affect subsequent tests. |
+// This can be moved to the unittests target once the TODO in ~ScopedTestNSSDB |
+// is fixed. |
+class PolicyCertVerifierTest : public testing::Test { |
+ public: |
+ PolicyCertVerifierTest() |
+ : cert_db_(NULL), |
+ ui_thread_(content::BrowserThread::UI, &loop_), |
+ io_thread_(content::BrowserThread::IO, &loop_), |
+ profile_manager_(TestingBrowserProcess::GetGlobal()), |
+ profile_(NULL) {} |
+ |
+ virtual ~PolicyCertVerifierTest() {} |
+ |
+ virtual void SetUp() OVERRIDE { |
+ ASSERT_TRUE(test_nssdb_.is_open()); |
+ cert_db_ = net::NSSCertDatabase::GetInstance(); |
+ |
+ ASSERT_TRUE(profile_manager_.SetUp()); |
+ profile_ = profile_manager_.CreateTestingProfile("profile"); |
+ |
+ cert_verifier_.reset(new PolicyCertVerifier(profile_, &trust_provider_)); |
+ } |
+ |
+ bool SupportsAdditionalTrustAnchors() { |
+ scoped_refptr<net::CertVerifyProc> proc = |
pneubeck (no reviews)
2013/04/04 11:22:22
nit: you might drop the local variable.
Joao da Silva
2013/04/04 11:25:35
RefCounted objects have 0 refcounts when they are
|
+ net::CertVerifyProc::CreateDefault(); |
+ return proc->SupportsAdditionalTrustAnchors(); |
+ } |
+ |
+ scoped_refptr<net::X509Certificate> LoadCertificate(const std::string& name, |
+ net::CertType type) { |
+ scoped_refptr<net::X509Certificate> cert = |
+ net::ImportCertFromFile(net::GetTestCertsDirectory(), name); |
+ |
+ // No certificate is trusted right after it's loaded. |
+ net::NSSCertDatabase::TrustBits trust = |
+ cert_db_->GetCertTrust(cert.get(), type); |
+ EXPECT_EQ(net::NSSCertDatabase::TRUST_DEFAULT, trust); |
+ |
+ return cert; |
+ } |
+ |
+ protected: |
+ crypto::ScopedTestNSSDB test_nssdb_; |
+ net::NSSCertDatabase* cert_db_; |
+ MessageLoop loop_; |
+ content::TestBrowserThread ui_thread_; |
+ content::TestBrowserThread io_thread_; |
+ TestingProfileManager profile_manager_; |
+ TestingProfile* profile_; |
+ MockCertTrustAnchorProvider trust_provider_; |
+ scoped_ptr<PolicyCertVerifier> cert_verifier_; |
+ const net::CertificateList empty_cert_list_; |
+}; |
+ |
+TEST_F(PolicyCertVerifierTest, VerifyUntrustedCert) { |
+ scoped_refptr<net::X509Certificate> cert = |
+ LoadCertificate("ok_cert.pem", net::SERVER_CERT); |
+ ASSERT_TRUE(cert); |
+ |
+ // |cert| is untrusted, so Verify() fails. |
+ net::CertVerifyResult verify_result; |
+ net::TestCompletionCallback callback; |
+ net::CertVerifier::RequestHandle request_handle; |
+ EXPECT_CALL(trust_provider_, GetAdditionalTrustAnchors()) |
+ .WillOnce(ReturnRef(empty_cert_list_)); |
+ int error = cert_verifier_->Verify(cert, "127.0.0.1", 0, NULL, |
+ &verify_result, callback.callback(), |
+ &request_handle, net::BoundNetLog()); |
+ Mock::VerifyAndClearExpectations(&trust_provider_); |
+ ASSERT_EQ(net::ERR_IO_PENDING, error); |
+ ASSERT_TRUE(request_handle); |
+ error = callback.WaitForResult(); |
+ EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID, error); |
+ |
+ // Issuing the same request again hits the cache. This tests the synchronous |
+ // path. |
+ EXPECT_CALL(trust_provider_, GetAdditionalTrustAnchors()) |
+ .WillOnce(ReturnRef(empty_cert_list_)); |
+ error = cert_verifier_->Verify(cert, "127.0.0.1", 0, NULL, |
+ &verify_result, callback.callback(), |
+ &request_handle, net::BoundNetLog()); |
+ Mock::VerifyAndClearExpectations(&trust_provider_); |
+ EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID, error); |
+ |
+ // The profile is not tainted. |
+ base::RunLoop().RunUntilIdle(); |
+ EXPECT_FALSE( |
+ profile_->GetPrefs()->GetBoolean(prefs::kUsedPolicyCertificatesOnce)); |
+} |
+ |
+TEST_F(PolicyCertVerifierTest, VerifyTrustedCert) { |
+ // |ca_cert| is the issuer of |cert|. |
+ scoped_refptr<net::X509Certificate> ca_cert = |
+ LoadCertificate("root_ca_cert.crt", net::CA_CERT); |
+ ASSERT_TRUE(ca_cert); |
+ scoped_refptr<net::X509Certificate> cert = |
+ LoadCertificate("ok_cert.pem", net::SERVER_CERT); |
+ ASSERT_TRUE(cert); |
+ |
+ // Make the database trust |ca_cert|. |
+ net::CertificateList import_list; |
+ import_list.push_back(ca_cert); |
+ net::NSSCertDatabase::ImportCertFailureList failure_list; |
+ ASSERT_TRUE(cert_db_->ImportCACerts( |
+ import_list, net::NSSCertDatabase::TRUSTED_SSL, &failure_list)); |
+ ASSERT_TRUE(failure_list.empty()); |
+ |
+ // Verify that it is now trusted. |
+ net::NSSCertDatabase::TrustBits trust = |
+ cert_db_->GetCertTrust(ca_cert.get(), net::CA_CERT); |
+ EXPECT_EQ(net::NSSCertDatabase::TRUSTED_SSL, trust); |
+ |
+ // Verify() successfully verifies |cert| after it was imported. |
+ net::CertVerifyResult verify_result; |
+ net::TestCompletionCallback callback; |
+ net::CertVerifier::RequestHandle request_handle; |
+ EXPECT_CALL(trust_provider_, GetAdditionalTrustAnchors()) |
+ .WillOnce(ReturnRef(empty_cert_list_)); |
+ int error = cert_verifier_->Verify(cert, "127.0.0.1", 0, NULL, |
+ &verify_result, callback.callback(), |
+ &request_handle, net::BoundNetLog()); |
+ Mock::VerifyAndClearExpectations(&trust_provider_); |
+ ASSERT_EQ(net::ERR_IO_PENDING, error); |
+ ASSERT_TRUE(request_handle); |
+ error = callback.WaitForResult(); |
+ EXPECT_EQ(net::OK, error); |
+ |
+ // The profile is not tainted, since the certificate is trusted from the |
+ // database. |
+ base::RunLoop().RunUntilIdle(); |
+ EXPECT_FALSE( |
+ profile_->GetPrefs()->GetBoolean(prefs::kUsedPolicyCertificatesOnce)); |
+} |
+ |
+TEST_F(PolicyCertVerifierTest, VerifyUsingAdditionalTrustAnchor) { |
+ if (!SupportsAdditionalTrustAnchors()) { |
+ LOG(INFO) << "Test skipped on this platform. NSS >= 3.14.2 required."; |
+ return; |
+ } |
+ |
+ // |ca_cert| is the issuer of |cert|. |
+ scoped_refptr<net::X509Certificate> ca_cert = |
+ LoadCertificate("root_ca_cert.crt", net::CA_CERT); |
+ ASSERT_TRUE(ca_cert); |
+ scoped_refptr<net::X509Certificate> cert = |
+ LoadCertificate("ok_cert.pem", net::SERVER_CERT); |
+ ASSERT_TRUE(cert); |
+ |
+ net::CertificateList additional_trust_anchors; |
+ additional_trust_anchors.push_back(ca_cert); |
+ |
+ // Verify() successfully verifies |cert|, using |ca_cert| from the list of |
+ // |additional_trust_anchors|. |
+ net::CertVerifyResult verify_result; |
+ net::TestCompletionCallback callback; |
+ net::CertVerifier::RequestHandle request_handle; |
+ EXPECT_CALL(trust_provider_, GetAdditionalTrustAnchors()) |
+ .WillOnce(ReturnRef(additional_trust_anchors)); |
+ int error = cert_verifier_->Verify(cert, "127.0.0.1", 0, NULL, |
+ &verify_result, callback.callback(), |
+ &request_handle, net::BoundNetLog()); |
+ Mock::VerifyAndClearExpectations(&trust_provider_); |
+ ASSERT_EQ(net::ERR_IO_PENDING, error); |
+ ASSERT_TRUE(request_handle); |
+ error = callback.WaitForResult(); |
+ EXPECT_EQ(net::OK, error); |
+ |
+ // The profile becomes tainted after using the trust anchors that came from |
+ // the policy configuration. |
+ base::RunLoop().RunUntilIdle(); |
+ EXPECT_TRUE( |
+ profile_->GetPrefs()->GetBoolean(prefs::kUsedPolicyCertificatesOnce)); |
+} |
+ |
+TEST_F(PolicyCertVerifierTest, ProfileRemainsTainted) { |
+ if (!SupportsAdditionalTrustAnchors()) { |
+ LOG(INFO) << "Test skipped on this platform. NSS >= 3.14.2 required."; |
+ return; |
+ } |
+ |
+ // |ca_cert| is the issuer of |cert|. |
+ scoped_refptr<net::X509Certificate> ca_cert = |
+ LoadCertificate("root_ca_cert.crt", net::CA_CERT); |
+ ASSERT_TRUE(ca_cert); |
+ scoped_refptr<net::X509Certificate> cert = |
+ LoadCertificate("ok_cert.pem", net::SERVER_CERT); |
+ ASSERT_TRUE(cert); |
+ |
+ net::CertificateList additional_trust_anchors; |
+ additional_trust_anchors.push_back(ca_cert); |
+ |
+ // |cert| is untrusted, so Verify() fails. |
+ net::CertVerifyResult verify_result; |
+ net::TestCompletionCallback callback; |
+ net::CertVerifier::RequestHandle request_handle; |
+ EXPECT_CALL(trust_provider_, GetAdditionalTrustAnchors()) |
+ .WillOnce(ReturnRef(empty_cert_list_)); |
+ int error = cert_verifier_->Verify(cert, "127.0.0.1", 0, NULL, |
+ &verify_result, callback.callback(), |
+ &request_handle, net::BoundNetLog()); |
+ Mock::VerifyAndClearExpectations(&trust_provider_); |
+ ASSERT_EQ(net::ERR_IO_PENDING, error); |
+ ASSERT_TRUE(request_handle); |
+ error = callback.WaitForResult(); |
+ EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID, error); |
+ |
+ // The profile is not tainted. |
+ base::RunLoop().RunUntilIdle(); |
+ EXPECT_FALSE( |
+ profile_->GetPrefs()->GetBoolean(prefs::kUsedPolicyCertificatesOnce)); |
+ |
+ // Verify() again with the additional trust anchors. |
+ EXPECT_CALL(trust_provider_, GetAdditionalTrustAnchors()) |
+ .WillOnce(ReturnRef(additional_trust_anchors)); |
+ error = cert_verifier_->Verify(cert, "127.0.0.1", 0, NULL, |
+ &verify_result, callback.callback(), |
+ &request_handle, net::BoundNetLog()); |
+ Mock::VerifyAndClearExpectations(&trust_provider_); |
+ ASSERT_EQ(net::ERR_IO_PENDING, error); |
+ ASSERT_TRUE(request_handle); |
+ error = callback.WaitForResult(); |
+ EXPECT_EQ(net::OK, error); |
+ |
+ // The profile becomes tainted after using the trust anchors that came from |
+ // the policy configuration. |
+ base::RunLoop().RunUntilIdle(); |
+ EXPECT_TRUE( |
+ profile_->GetPrefs()->GetBoolean(prefs::kUsedPolicyCertificatesOnce)); |
+ |
+ // Verifying after removing the trust anchors should now fail. |
+ EXPECT_CALL(trust_provider_, GetAdditionalTrustAnchors()) |
+ .WillOnce(ReturnRef(empty_cert_list_)); |
+ error = cert_verifier_->Verify(cert, "127.0.0.1", 0, NULL, |
+ &verify_result, callback.callback(), |
+ &request_handle, net::BoundNetLog()); |
+ Mock::VerifyAndClearExpectations(&trust_provider_); |
+ // Note: this hits the cached result from the first Verify() in this test. |
+ EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID, error); |
+ |
+ // The profile is still tainted. |
+ base::RunLoop().RunUntilIdle(); |
+ EXPECT_TRUE( |
+ profile_->GetPrefs()->GetBoolean(prefs::kUsedPolicyCertificatesOnce)); |
+} |
+ |
+} // namespace policy |