| // 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 "net/cert_net/nss_ocsp.h" | 
 |  | 
 | #include <string> | 
 | #include <utility> | 
 |  | 
 | #include "base/files/file_path.h" | 
 | #include "base/files/file_util.h" | 
 | #include "base/logging.h" | 
 | #include "base/macros.h" | 
 | #include "base/memory/ref_counted.h" | 
 | #include "net/base/net_errors.h" | 
 | #include "net/base/test_completion_callback.h" | 
 | #include "net/cert/cert_status_flags.h" | 
 | #include "net/cert/cert_verifier.h" | 
 | #include "net/cert/cert_verify_proc.h" | 
 | #include "net/cert/cert_verify_proc_nss.h" | 
 | #include "net/cert/cert_verify_result.h" | 
 | #include "net/cert/multi_threaded_cert_verifier.h" | 
 | #include "net/cert/test_root_certs.h" | 
 | #include "net/cert/x509_certificate.h" | 
 | #include "net/log/net_log_with_source.h" | 
 | #include "net/test/cert_test_util.h" | 
 | #include "net/test/gtest_util.h" | 
 | #include "net/test/test_data_directory.h" | 
 | #include "net/url_request/url_request_filter.h" | 
 | #include "net/url_request/url_request_interceptor.h" | 
 | #include "net/url_request/url_request_test_job.h" | 
 | #include "net/url_request/url_request_test_util.h" | 
 | #include "testing/gmock/include/gmock/gmock.h" | 
 | #include "testing/gtest/include/gtest/gtest.h" | 
 |  | 
 | using net::test::IsError; | 
 | using net::test::IsOk; | 
 |  | 
 | namespace net { | 
 |  | 
 | namespace { | 
 |  | 
 | // Matches the caIssuers hostname from the generated certificate. | 
 | const char kAiaHost[] = "aia-test.invalid"; | 
 | // Returning a single DER-encoded cert, so the mime-type must be | 
 | // application/pkix-cert per RFC 5280. | 
 | const char kAiaHeaders[] = "HTTP/1.1 200 OK\0" | 
 |                            "Content-type: application/pkix-cert\0" | 
 |                            "\0"; | 
 |  | 
 | class AiaResponseHandler : public URLRequestInterceptor { | 
 |  public: | 
 |   AiaResponseHandler(const std::string& headers, const std::string& cert_data) | 
 |       : headers_(headers), cert_data_(cert_data), request_count_(0) {} | 
 |   ~AiaResponseHandler() override {} | 
 |  | 
 |   // URLRequestInterceptor implementation: | 
 |   URLRequestJob* MaybeInterceptRequest( | 
 |       URLRequest* request, | 
 |       NetworkDelegate* network_delegate) const override { | 
 |     ++const_cast<AiaResponseHandler*>(this)->request_count_; | 
 |  | 
 |     return new URLRequestTestJob(request, network_delegate, headers_, | 
 |                                  cert_data_, true); | 
 |   } | 
 |  | 
 |   int request_count() const { return request_count_; } | 
 |  | 
 |  private: | 
 |   std::string headers_; | 
 |   std::string cert_data_; | 
 |   int request_count_; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(AiaResponseHandler); | 
 | }; | 
 |  | 
 | }  // namespace | 
 |  | 
 | class NssHttpTest : public ::testing::Test { | 
 |  public: | 
 |   NssHttpTest() | 
 |       : context_(false), | 
 |         handler_(NULL), | 
 |         verify_proc_(new CertVerifyProcNSS), | 
 |         verifier_(new MultiThreadedCertVerifier(verify_proc_.get())) {} | 
 |   ~NssHttpTest() override {} | 
 |  | 
 |   void SetUp() override { | 
 |     std::string file_contents; | 
 |     ASSERT_TRUE(base::ReadFileToString( | 
 |         GetTestCertsDirectory().AppendASCII("aia-intermediate.der"), | 
 |         &file_contents)); | 
 |     ASSERT_FALSE(file_contents.empty()); | 
 |  | 
 |     // Ownership of |handler| is transferred to the URLRequestFilter, but | 
 |     // hold onto the original pointer in order to access |request_count()|. | 
 |     std::unique_ptr<AiaResponseHandler> handler( | 
 |         new AiaResponseHandler(kAiaHeaders, file_contents)); | 
 |     handler_ = handler.get(); | 
 |  | 
 |     URLRequestFilter::GetInstance()->AddHostnameInterceptor("http", kAiaHost, | 
 |                                                             std::move(handler)); | 
 |  | 
 |     SetURLRequestContextForNSSHttpIO(&context_); | 
 |     EnsureNSSHttpIOInit(); | 
 |   } | 
 |  | 
 |   void TearDown() override { | 
 |     ShutdownNSSHttpIO(); | 
 |  | 
 |     if (handler_) | 
 |       URLRequestFilter::GetInstance()->RemoveHostnameHandler("http", kAiaHost); | 
 |   } | 
 |  | 
 |   CertVerifier* verifier() const { | 
 |     return verifier_.get(); | 
 |   } | 
 |  | 
 |   int request_count() const { | 
 |     return handler_->request_count(); | 
 |   } | 
 |  | 
 |  protected: | 
 |   const CertificateList empty_cert_list_; | 
 |  | 
 |  private: | 
 |   TestURLRequestContext context_; | 
 |   AiaResponseHandler* handler_; | 
 |   scoped_refptr<CertVerifyProc> verify_proc_; | 
 |   std::unique_ptr<CertVerifier> verifier_; | 
 | }; | 
 |  | 
 | // Tests that when using NSS to verify certificates, and IO is enabled, | 
 | // that a request to fetch missing intermediate certificates is | 
 | // made successfully. | 
 | TEST_F(NssHttpTest, TestAia) { | 
 |   scoped_refptr<X509Certificate> test_cert( | 
 |       ImportCertFromFile(GetTestCertsDirectory(), "aia-cert.pem")); | 
 |   ASSERT_TRUE(test_cert.get()); | 
 |  | 
 |   scoped_refptr<X509Certificate> test_root( | 
 |       ImportCertFromFile(GetTestCertsDirectory(), "aia-root.pem")); | 
 |   ASSERT_TRUE(test_root.get()); | 
 |  | 
 |   ScopedTestRoot scoped_root(test_root.get()); | 
 |  | 
 |   CertVerifyResult verify_result; | 
 |   TestCompletionCallback test_callback; | 
 |   std::unique_ptr<CertVerifier::Request> request; | 
 |  | 
 |   int flags = CertVerifier::VERIFY_CERT_IO_ENABLED; | 
 |   int error = verifier()->Verify( | 
 |       CertVerifier::RequestParams(test_cert, "aia-host.invalid", flags, | 
 |                                   std::string(), CertificateList()), | 
 |       nullptr, &verify_result, test_callback.callback(), &request, | 
 |       NetLogWithSource()); | 
 |   ASSERT_THAT(error, IsError(ERR_IO_PENDING)); | 
 |  | 
 |   error = test_callback.WaitForResult(); | 
 |  | 
 |   EXPECT_THAT(error, IsOk()); | 
 |  | 
 |   // Ensure that NSS made an AIA request for the missing intermediate. | 
 |   EXPECT_LT(0, request_count()); | 
 | } | 
 |  | 
 | }  // namespace net |