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