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