diff options
author | mattm@chromium.org <mattm@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-12-17 00:09:00 +0000 |
---|---|---|
committer | mattm@chromium.org <mattm@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-12-17 00:09:00 +0000 |
commit | e53c02329933022491988cb58f58423f7a18ba61 (patch) | |
tree | f87bb5be9cabe9d03a370496ab71419e3a43fe88 /net | |
parent | 61e2b8591c6940552fdac1bed5c8787e99f30d97 (diff) | |
download | chromium_src-e53c02329933022491988cb58f58423f7a18ba61.zip chromium_src-e53c02329933022491988cb58f58423f7a18ba61.tar.gz chromium_src-e53c02329933022491988cb58f58423f7a18ba61.tar.bz2 |
Add ClientCertStoreChromeOS which only returns the certs for a given user.
BUG=302125
Review URL: https://codereview.chromium.org/112533002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@241080 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r-- | net/cert/nss_profile_filter_chromeos.cc | 106 | ||||
-rw-r--r-- | net/cert/nss_profile_filter_chromeos.h | 60 | ||||
-rw-r--r-- | net/net.gyp | 5 | ||||
-rw-r--r-- | net/ssl/client_cert_store.h | 3 | ||||
-rw-r--r-- | net/ssl/client_cert_store_chromeos.cc | 94 | ||||
-rw-r--r-- | net/ssl/client_cert_store_chromeos.h | 66 | ||||
-rw-r--r-- | net/ssl/client_cert_store_chromeos_unittest.cc | 193 | ||||
-rw-r--r-- | net/ssl/client_cert_store_nss.cc | 88 | ||||
-rw-r--r-- | net/ssl/client_cert_store_nss.h | 19 |
9 files changed, 592 insertions, 42 deletions
diff --git a/net/cert/nss_profile_filter_chromeos.cc b/net/cert/nss_profile_filter_chromeos.cc new file mode 100644 index 0000000..6fe8eba --- /dev/null +++ b/net/cert/nss_profile_filter_chromeos.cc @@ -0,0 +1,106 @@ +// Copyright 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/nss_profile_filter_chromeos.h" + +#include "base/bind.h" +#include "base/callback.h" +#include "base/strings/stringprintf.h" + +namespace net { + +namespace { + +std::string CertSlotsString(const scoped_refptr<X509Certificate>& cert) { + std::string result; + crypto::ScopedPK11SlotList slots_for_cert( + PK11_GetAllSlotsForCert(cert->os_cert_handle(), NULL)); + for (PK11SlotListElement* slot_element = + PK11_GetFirstSafe(slots_for_cert.get()); + slot_element; + slot_element = + PK11_GetNextSafe(slots_for_cert.get(), slot_element, PR_FALSE)) { + if (!result.empty()) + result += ','; + base::StringAppendF(&result, + "%lu:%lu", + PK11_GetModuleID(slot_element->slot), + PK11_GetSlotID(slot_element->slot)); + } + return result; +} + +} // namespace + +NSSProfileFilterChromeOS::NSSProfileFilterChromeOS() {} + +NSSProfileFilterChromeOS::~NSSProfileFilterChromeOS() {} + +void NSSProfileFilterChromeOS::Init(crypto::ScopedPK11Slot public_slot, + crypto::ScopedPK11Slot private_slot) { + public_slot_ = public_slot.Pass(); + private_slot_ = private_slot.Pass(); +} + +bool NSSProfileFilterChromeOS::IsModuleAllowed(PK11SlotInfo* slot) const { + // If this is one of the public/private slots for this profile, allow it. + if (slot == public_slot_.get() || slot == private_slot_.get()) + return true; + // If it's from the read-only slot, allow it. + if (slot == PK11_GetInternalKeySlot()) + return true; + // If this is not the internal (file-system) module or the TPM module, allow + // it. + SECMODModule* module_for_slot = PK11_GetModule(slot); + if (module_for_slot != PK11_GetModule(public_slot_.get()) && + module_for_slot != PK11_GetModule(private_slot_.get())) + return true; + return false; +} + +bool NSSProfileFilterChromeOS::IsCertAllowed( + const scoped_refptr<X509Certificate>& cert) const { + crypto::ScopedPK11SlotList slots_for_cert( + PK11_GetAllSlotsForCert(cert->os_cert_handle(), NULL)); + if (!slots_for_cert) { + DVLOG(2) << "cert no slots: " << cert->subject().GetDisplayName(); + return true; + } + + for (PK11SlotListElement* slot_element = + PK11_GetFirstSafe(slots_for_cert.get()); + slot_element; + slot_element = + PK11_GetNextSafe(slots_for_cert.get(), slot_element, PR_FALSE)) { + if (IsModuleAllowed(slot_element->slot)) { + DVLOG(3) << "cert from " << CertSlotsString(cert) + << " allowed: " << cert->subject().GetDisplayName(); + return true; + } + } + DVLOG(2) << "cert from " << CertSlotsString(cert) + << " filtered: " << cert->subject().GetDisplayName(); + return false; +} + +NSSProfileFilterChromeOS::CertNotAllowedForProfilePredicate:: + CertNotAllowedForProfilePredicate(const NSSProfileFilterChromeOS& filter) + : filter_(filter) {} + +bool NSSProfileFilterChromeOS::CertNotAllowedForProfilePredicate::operator()( + const scoped_refptr<X509Certificate>& cert) const { + return !filter_.IsCertAllowed(cert); +} + +NSSProfileFilterChromeOS::ModuleNotAllowedForProfilePredicate:: + ModuleNotAllowedForProfilePredicate(const NSSProfileFilterChromeOS& filter) + : filter_(filter) {} + +bool NSSProfileFilterChromeOS::ModuleNotAllowedForProfilePredicate::operator()( + const scoped_refptr<CryptoModule>& module) const { + return !filter_.IsModuleAllowed(module->os_module_handle()); +} + +} // namespace net + diff --git a/net/cert/nss_profile_filter_chromeos.h b/net/cert/nss_profile_filter_chromeos.h new file mode 100644 index 0000000..d5ff818 --- /dev/null +++ b/net/cert/nss_profile_filter_chromeos.h @@ -0,0 +1,60 @@ +// Copyright 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. + +#ifndef NET_CERT_NSS_PROFILE_FILTER_CHROMEOS +#define NET_CERT_NSS_PROFILE_FILTER_CHROMEOS + +#include "base/callback_forward.h" +#include "crypto/scoped_nss_types.h" +#include "net/base/crypto_module.h" +#include "net/cert/x509_certificate.h" + +namespace content { +class ResourceContext; +} // namespace content + +namespace net { + +class NET_EXPORT NSSProfileFilterChromeOS { + public: + NSSProfileFilterChromeOS(); + ~NSSProfileFilterChromeOS(); + + // Initialize with slot handles. + void Init(crypto::ScopedPK11Slot public_slot, + crypto::ScopedPK11Slot private_slot); + + bool IsModuleAllowed(PK11SlotInfo* slot) const; + bool IsCertAllowed(const scoped_refptr<X509Certificate>& cert) const; + + class CertNotAllowedForProfilePredicate { + public: + explicit CertNotAllowedForProfilePredicate( + const NSSProfileFilterChromeOS& filter); + bool operator()(const scoped_refptr<X509Certificate>& cert) const; + + private: + const NSSProfileFilterChromeOS& filter_; + }; + + class ModuleNotAllowedForProfilePredicate { + public: + explicit ModuleNotAllowedForProfilePredicate( + const NSSProfileFilterChromeOS& filter); + bool operator()(const scoped_refptr<CryptoModule>& module) const; + + private: + const NSSProfileFilterChromeOS& filter_; + }; + + private: + crypto::ScopedPK11Slot public_slot_; + crypto::ScopedPK11Slot private_slot_; + + DISALLOW_COPY_AND_ASSIGN(NSSProfileFilterChromeOS); +}; + +} // namespace net + +#endif // NET_CERT_NSS_PROFILE_FILTER_CHROMEOS diff --git a/net/net.gyp b/net/net.gyp index 29aad0f..c308a8a7 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -297,6 +297,8 @@ 'cert/multi_threaded_cert_verifier.h', 'cert/nss_cert_database.cc', 'cert/nss_cert_database.h', + 'cert/nss_profile_filter_chromeos.cc', + 'cert/nss_profile_filter_chromeos.h', 'cert/pem_tokenizer.cc', 'cert/pem_tokenizer.h', 'cert/scoped_nss_types.h', @@ -1025,6 +1027,8 @@ 'spdy/spdy_write_queue.h', 'spdy/write_blocked_list.h', 'ssl/client_cert_store.h', + 'ssl/client_cert_store_chromeos.cc', + 'ssl/client_cert_store_chromeos.h', 'ssl/client_cert_store_mac.cc', 'ssl/client_cert_store_mac.h', 'ssl/client_cert_store_nss.cc', @@ -1934,6 +1938,7 @@ 'spdy/spdy_websocket_test_util.h', 'spdy/spdy_write_queue_unittest.cc', 'spdy/write_blocked_list_test.cc', + 'ssl/client_cert_store_chromeos_unittest.cc', 'ssl/client_cert_store_mac_unittest.cc', 'ssl/client_cert_store_nss_unittest.cc', 'ssl/client_cert_store_unittest-inl.h', diff --git a/net/ssl/client_cert_store.h b/net/ssl/client_cert_store.h index 394d774..fe050e5 100644 --- a/net/ssl/client_cert_store.h +++ b/net/ssl/client_cert_store.h @@ -23,7 +23,8 @@ class NET_EXPORT ClientCertStore { // Get client certs matching the |cert_request_info|. On completion, the // results will be stored in |selected_certs| and the |callback| will be run. // The |callback| may be called sychronously. The caller must ensure the - // |selected_certs| object remains alive until the callback has been run. + // ClientCertStore and the |selected_certs| object remain alive until the + // callback has been run. virtual void GetClientCerts(const SSLCertRequestInfo& cert_request_info, CertificateList* selected_certs, const base::Closure& callback) = 0; diff --git a/net/ssl/client_cert_store_chromeos.cc b/net/ssl/client_cert_store_chromeos.cc new file mode 100644 index 0000000..f357797 --- /dev/null +++ b/net/ssl/client_cert_store_chromeos.cc @@ -0,0 +1,94 @@ +// Copyright 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/ssl/client_cert_store_chromeos.h" + +#include <cert.h> + +#include "base/bind.h" +#include "crypto/nss_crypto_module_delegate.h" +#include "crypto/nss_util_internal.h" + +namespace net { + +ClientCertStoreChromeOS::ClientCertStoreChromeOS( + const std::string& username_hash, + const PasswordDelegateFactory& password_delegate_factory) + : ClientCertStoreNSS(password_delegate_factory), + username_hash_(username_hash) {} + +ClientCertStoreChromeOS::~ClientCertStoreChromeOS() {} + +void ClientCertStoreChromeOS::GetClientCerts( + const SSLCertRequestInfo& cert_request_info, + CertificateList* selected_certs, + const base::Closure& callback) { + crypto::ScopedPK11Slot private_slot(crypto::GetPrivateSlotForChromeOSUser( + username_hash_, + base::Bind(&ClientCertStoreChromeOS::DidGetPrivateSlot, + // Caller is responsible for keeping the ClientCertStore alive + // until the callback is run. + base::Unretained(this), + &cert_request_info, + selected_certs, + callback))); + if (private_slot) + DidGetPrivateSlot( + &cert_request_info, selected_certs, callback, private_slot.Pass()); +} + +void ClientCertStoreChromeOS::GetClientCertsImpl(CERTCertList* cert_list, + const SSLCertRequestInfo& request, + bool query_nssdb, + CertificateList* selected_certs) { + ClientCertStoreNSS::GetClientCertsImpl( + cert_list, request, query_nssdb, selected_certs); + + size_t pre_size = selected_certs->size(); + selected_certs->erase( + std::remove_if( + selected_certs->begin(), + selected_certs->end(), + NSSProfileFilterChromeOS::CertNotAllowedForProfilePredicate( + profile_filter_)), + selected_certs->end()); + DVLOG(1) << "filtered " << pre_size - selected_certs->size() << " of " + << pre_size << " certs"; +} + +void ClientCertStoreChromeOS::DidGetPrivateSlot( + const SSLCertRequestInfo* request, + CertificateList* selected_certs, + const base::Closure& callback, + crypto::ScopedPK11Slot private_slot) { + profile_filter_.Init(crypto::GetPublicSlotForChromeOSUser(username_hash_), + private_slot.Pass()); + ClientCertStoreNSS::GetClientCerts(*request, selected_certs, callback); +} + +void ClientCertStoreChromeOS::InitForTesting( + crypto::ScopedPK11Slot public_slot, + crypto::ScopedPK11Slot private_slot) { + profile_filter_.Init(public_slot.Pass(), private_slot.Pass()); +} + +bool ClientCertStoreChromeOS::SelectClientCertsForTesting( + const CertificateList& input_certs, + const SSLCertRequestInfo& request, + CertificateList* selected_certs) { + CERTCertList* cert_list = CERT_NewCertList(); + if (!cert_list) + return false; + for (size_t i = 0; i < input_certs.size(); ++i) { + CERT_AddCertToListTail( + cert_list, CERT_DupCertificate(input_certs[i]->os_cert_handle())); + } + + GetClientCertsImpl(cert_list, request, false, selected_certs); + CERT_DestroyCertList(cert_list); + return true; +} + + +} // namespace net diff --git a/net/ssl/client_cert_store_chromeos.h b/net/ssl/client_cert_store_chromeos.h new file mode 100644 index 0000000..41339f1 --- /dev/null +++ b/net/ssl/client_cert_store_chromeos.h @@ -0,0 +1,66 @@ +// Copyright 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. + +#ifndef NET_SSL_CLIENT_CERT_STORE_CHROMEOS_H_ +#define NET_SSL_CLIENT_CERT_STORE_CHROMEOS_H_ + +#include <string> + +#include "crypto/scoped_nss_types.h" +#include "net/cert/nss_profile_filter_chromeos.h" +#include "net/ssl/client_cert_store_nss.h" + +namespace net { + +class NET_EXPORT ClientCertStoreChromeOS : public ClientCertStoreNSS { + public: + ClientCertStoreChromeOS( + const std::string& username_hash, + const PasswordDelegateFactory& password_delegate_factory); + virtual ~ClientCertStoreChromeOS(); + + // ClientCertStoreNSS: + virtual void GetClientCerts(const SSLCertRequestInfo& cert_request_info, + CertificateList* selected_certs, + const base::Closure& callback) OVERRIDE; + + protected: + // ClientCertStoreNSS: + virtual void GetClientCertsImpl(CERTCertList* cert_list, + const SSLCertRequestInfo& request, + bool query_nssdb, + CertificateList* selected_certs) OVERRIDE; + + private: + friend class ClientCertStoreChromeOSTestDelegate; + + void DidGetPrivateSlot(const SSLCertRequestInfo* request, + CertificateList* selected_certs, + const base::Closure& callback, + crypto::ScopedPK11Slot private_slot); + + // Allows tests to initialize the cert store with the given slots. + // Must be called before SelectClientCertsForTesting. + void InitForTesting(crypto::ScopedPK11Slot public_slot, + crypto::ScopedPK11Slot private_slot); + + // A hook for testing. Filters |input_certs| using the logic being used to + // filter the system store when GetClientCerts() is called. + // Implemented by creating a list of certificates that otherwise would be + // extracted from the system store and filtering it using the common logic + // (less adequate than the approach used on Windows). + bool SelectClientCertsForTesting(const CertificateList& input_certs, + const SSLCertRequestInfo& cert_request_info, + CertificateList* selected_certs); + + + std::string username_hash_; + NSSProfileFilterChromeOS profile_filter_; + + DISALLOW_COPY_AND_ASSIGN(ClientCertStoreChromeOS); +}; + +} // namespace net + +#endif // NET_SSL_CLIENT_CERT_STORE_CHROMEOS_H_ diff --git a/net/ssl/client_cert_store_chromeos_unittest.cc b/net/ssl/client_cert_store_chromeos_unittest.cc new file mode 100644 index 0000000..73e0326 --- /dev/null +++ b/net/ssl/client_cert_store_chromeos_unittest.cc @@ -0,0 +1,193 @@ +// Copyright 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/ssl/client_cert_store_chromeos.h" + +#include "base/bind.h" +#include "base/callback.h" +#include "base/file_util.h" +#include "base/run_loop.h" +#include "base/strings/utf_string_conversions.h" +#include "crypto/nss_util.h" +#include "crypto/nss_util_internal.h" +#include "net/cert/nss_cert_database.h" +#include "net/ssl/client_cert_store_unittest-inl.h" + +namespace net { + +class ClientCertStoreChromeOSTestDelegate { + public: + ClientCertStoreChromeOSTestDelegate() + : store_("usernamehash", + ClientCertStoreChromeOS::PasswordDelegateFactory()) { + store_.InitForTesting( + crypto::ScopedPK11Slot(crypto::GetPublicNSSKeySlot()), + crypto::ScopedPK11Slot(crypto::GetPrivateNSSKeySlot())); + } + + bool SelectClientCerts(const CertificateList& input_certs, + const SSLCertRequestInfo& cert_request_info, + CertificateList* selected_certs) { + return store_.SelectClientCertsForTesting( + input_certs, cert_request_info, selected_certs); + } + + private: + ClientCertStoreChromeOS store_; +}; + +INSTANTIATE_TYPED_TEST_CASE_P(ChromeOS, + ClientCertStoreTest, + ClientCertStoreChromeOSTestDelegate); + +class ClientCertStoreChromeOSTest : public ::testing::Test { + public: + scoped_refptr<X509Certificate> ImportCertForUser( + const std::string& username_hash, + const std::string& filename, + const std::string& password) { + crypto::ScopedPK11Slot slot( + crypto::GetPublicSlotForChromeOSUser(username_hash)); + EXPECT_TRUE(slot.get()); + if (!slot.get()) + return NULL; + + net::CertificateList cert_list; + + base::FilePath p12_path = GetTestCertsDirectory().AppendASCII(filename); + std::string p12_data; + if (!base::ReadFileToString(p12_path, &p12_data)) { + EXPECT_TRUE(false); + return NULL; + } + + scoped_refptr<net::CryptoModule> module( + net::CryptoModule::CreateFromHandle(slot.get())); + int rv = NSSCertDatabase::GetInstance()->ImportFromPKCS12( + module.get(), p12_data, base::UTF8ToUTF16(password), false, &cert_list); + + EXPECT_EQ(0, rv); + EXPECT_EQ(1U, cert_list.size()); + if (rv || cert_list.size() != 1) + return NULL; + + return cert_list[0]; + } +}; + +// TODO(mattm): Do better testing of cert_authorities matching below. Update +// net/data/ssl/scripts/generate-client-certificates.sh so that it actually +// saves the .p12 files, and regenerate them. + +TEST_F(ClientCertStoreChromeOSTest, WaitForNSSInit) { + crypto::ScopedTestNSSChromeOSUser user("scopeduser"); + ASSERT_TRUE(user.constructed_successfully()); + ClientCertStoreChromeOS store( + user.username_hash(), ClientCertStoreChromeOS::PasswordDelegateFactory()); + scoped_refptr<X509Certificate> cert_1( + ImportCertForUser(user.username_hash(), "client.p12", "12345")); + scoped_refptr<X509Certificate> cert_2( + ImportCertForUser(user.username_hash(), "websocket_client_cert.p12", "")); + + std::vector<std::string> authority_1( + 1, + std::string(reinterpret_cast<const char*>(kAuthority1DN), + sizeof(kAuthority1DN))); + scoped_refptr<SSLCertRequestInfo> request_1(new SSLCertRequestInfo()); + request_1->cert_authorities = authority_1; + + scoped_refptr<SSLCertRequestInfo> request_all(new SSLCertRequestInfo()); + + base::RunLoop run_loop_1; + base::RunLoop run_loop_all; + store.GetClientCerts( + *request_1, &request_1->client_certs, run_loop_1.QuitClosure()); + store.GetClientCerts( + *request_all, &request_all->client_certs, run_loop_all.QuitClosure()); + + // Callbacks won't be run until nss_util init finishes for the user. + user.FinishInit(); + + run_loop_1.Run(); + run_loop_all.Run(); + + ASSERT_EQ(0u, request_1->client_certs.size()); + ASSERT_EQ(2u, request_all->client_certs.size()); +} + +TEST_F(ClientCertStoreChromeOSTest, NSSAlreadyInitialized) { + crypto::ScopedTestNSSChromeOSUser user("scopeduser"); + ASSERT_TRUE(user.constructed_successfully()); + user.FinishInit(); + + ClientCertStoreChromeOS store( + user.username_hash(), ClientCertStoreChromeOS::PasswordDelegateFactory()); + scoped_refptr<X509Certificate> cert_1( + ImportCertForUser(user.username_hash(), "client.p12", "12345")); + scoped_refptr<X509Certificate> cert_2( + ImportCertForUser(user.username_hash(), "websocket_client_cert.p12", "")); + + std::vector<std::string> authority_1( + 1, + std::string(reinterpret_cast<const char*>(kAuthority1DN), + sizeof(kAuthority1DN))); + scoped_refptr<SSLCertRequestInfo> request_1(new SSLCertRequestInfo()); + request_1->cert_authorities = authority_1; + + scoped_refptr<SSLCertRequestInfo> request_all(new SSLCertRequestInfo()); + + base::RunLoop run_loop_1; + base::RunLoop run_loop_all; + store.GetClientCerts( + *request_1, &request_1->client_certs, run_loop_1.QuitClosure()); + store.GetClientCerts( + *request_all, &request_all->client_certs, run_loop_all.QuitClosure()); + + run_loop_1.Run(); + run_loop_all.Run(); + + ASSERT_EQ(0u, request_1->client_certs.size()); + ASSERT_EQ(2u, request_all->client_certs.size()); +} + +TEST_F(ClientCertStoreChromeOSTest, TwoUsers) { + crypto::ScopedTestNSSChromeOSUser user1("scopeduser1"); + ASSERT_TRUE(user1.constructed_successfully()); + crypto::ScopedTestNSSChromeOSUser user2("scopeduser2"); + ASSERT_TRUE(user2.constructed_successfully()); + ClientCertStoreChromeOS store1( + user1.username_hash(), + ClientCertStoreChromeOS::PasswordDelegateFactory()); + ClientCertStoreChromeOS store2( + user2.username_hash(), + ClientCertStoreChromeOS::PasswordDelegateFactory()); + scoped_refptr<X509Certificate> cert_1( + ImportCertForUser(user1.username_hash(), "client.p12", "12345")); + scoped_refptr<X509Certificate> cert_2(ImportCertForUser( + user2.username_hash(), "websocket_client_cert.p12", "")); + + scoped_refptr<SSLCertRequestInfo> request_1(new SSLCertRequestInfo()); + scoped_refptr<SSLCertRequestInfo> request_2(new SSLCertRequestInfo()); + + base::RunLoop run_loop_1; + base::RunLoop run_loop_2; + store1.GetClientCerts( + *request_1, &request_1->client_certs, run_loop_1.QuitClosure()); + store2.GetClientCerts( + *request_2, &request_2->client_certs, run_loop_2.QuitClosure()); + + // Callbacks won't be run until nss_util init finishes for the user. + user1.FinishInit(); + user2.FinishInit(); + + run_loop_1.Run(); + run_loop_2.Run(); + + ASSERT_EQ(1u, request_1->client_certs.size()); + EXPECT_TRUE(cert_1->Equals(request_1->client_certs[0])); + // TODO(mattm): Request for second user will have zero results due to + // crbug.com/315285. Update the test once that is fixed. +} + +} // namespace net diff --git a/net/ssl/client_cert_store_nss.cc b/net/ssl/client_cert_store_nss.cc index 9df5612..1b44bce 100644 --- a/net/ssl/client_cert_store_nss.cc +++ b/net/ssl/client_cert_store_nss.cc @@ -11,23 +11,47 @@ #include "base/location.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" +#include "base/strings/string_piece.h" #include "base/threading/worker_pool.h" #include "crypto/nss_crypto_module_delegate.h" #include "net/cert/x509_util.h" namespace net { -namespace { - -// Examines the certificates in |cert_list| to find all certificates that match -// the client certificate request in |request|, storing the matching -// certificates in |selected_certs|. -// If |query_nssdb| is true, NSS will be queried to construct full certificate -// chains. If it is false, only the certificate will be considered. -void GetClientCertsImpl(CERTCertList* cert_list, - const SSLCertRequestInfo& request, - bool query_nssdb, - CertificateList* selected_certs) { +ClientCertStoreNSS::ClientCertStoreNSS( + const PasswordDelegateFactory& password_delegate_factory) + : password_delegate_factory_(password_delegate_factory) {} + +ClientCertStoreNSS::~ClientCertStoreNSS() {} + +void ClientCertStoreNSS::GetClientCerts(const SSLCertRequestInfo& request, + CertificateList* selected_certs, + const base::Closure& callback) { + scoped_ptr<crypto::CryptoModuleBlockingPasswordDelegate> password_delegate; + if (!password_delegate_factory_.is_null()) { + password_delegate.reset( + password_delegate_factory_.Run(request.host_and_port)); + } + if (base::WorkerPool::PostTaskAndReply( + FROM_HERE, + base::Bind(&ClientCertStoreNSS::GetClientCertsOnWorkerThread, + // Caller is responsible for keeping the ClientCertStore + // alive until the callback is run. + base::Unretained(this), + base::Passed(&password_delegate), + &request, + selected_certs), + callback, + true)) + return; + selected_certs->clear(); + callback.Run(); +} + +void ClientCertStoreNSS::GetClientCertsImpl(CERTCertList* cert_list, + const SSLCertRequestInfo& request, + bool query_nssdb, + CertificateList* selected_certs) { DCHECK(cert_list); DCHECK(selected_certs); @@ -53,12 +77,16 @@ void GetClientCertsImpl(CERTCertList* cert_list, if (!ca_names_items.empty()) ca_names.names = &ca_names_items[0]; + size_t num_raw = 0; for (CERTCertListNode* node = CERT_LIST_HEAD(cert_list); !CERT_LIST_END(node, cert_list); node = CERT_LIST_NEXT(node)) { + ++num_raw; // Only offer unexpired certificates. if (CERT_CheckCertValidTimes(node->cert, PR_Now(), PR_TRUE) != secCertTimeValid) { + DVLOG(2) << "skipped expired cert: " + << base::StringPiece(node->cert->nickname); continue; } @@ -71,15 +99,21 @@ void GetClientCertsImpl(CERTCertList* cert_list, cert->IsIssuedByEncoded(request.cert_authorities)) || (query_nssdb && NSS_CmpCertChainWCANames(node->cert, &ca_names) == SECSuccess)) { + DVLOG(2) << "matched cert: " << base::StringPiece(node->cert->nickname); selected_certs->push_back(cert); } + else + DVLOG(2) << "skipped non-matching cert: " + << base::StringPiece(node->cert->nickname); } + DVLOG(2) << "num_raw:" << num_raw + << " num_selected:" << selected_certs->size(); std::sort(selected_certs->begin(), selected_certs->end(), x509_util::ClientCertSorter()); } -void GetClientCertsOnWorkerThread( +void ClientCertStoreNSS::GetClientCertsOnWorkerThread( scoped_ptr<crypto::CryptoModuleBlockingPasswordDelegate> password_delegate, const SSLCertRequestInfo* request, CertificateList* selected_certs) { @@ -91,6 +125,7 @@ void GetClientCertsOnWorkerThread( password_delegate.get()); // It is ok for a user not to have any client certs. if (!client_certs) { + DVLOG(2) << "No client certs found."; selected_certs->clear(); return; } @@ -99,35 +134,6 @@ void GetClientCertsOnWorkerThread( CERT_DestroyCertList(client_certs); } -} // namespace - -ClientCertStoreNSS::ClientCertStoreNSS( - const PasswordDelegateFactory& password_delegate_factory) - : password_delegate_factory_(password_delegate_factory) {} - -ClientCertStoreNSS::~ClientCertStoreNSS() {} - -void ClientCertStoreNSS::GetClientCerts(const SSLCertRequestInfo& request, - CertificateList* selected_certs, - const base::Closure& callback) { - scoped_ptr<crypto::CryptoModuleBlockingPasswordDelegate> password_delegate; - if (!password_delegate_factory_.is_null()) { - password_delegate.reset( - password_delegate_factory_.Run(request.host_and_port)); - } - if (!base::WorkerPool::PostTaskAndReply( - FROM_HERE, - base::Bind(&GetClientCertsOnWorkerThread, - base::Passed(&password_delegate), - &request, - selected_certs), - callback, - true)) { - selected_certs->clear(); - callback.Run(); - } -} - bool ClientCertStoreNSS::SelectClientCertsForTesting( const CertificateList& input_certs, const SSLCertRequestInfo& request, diff --git a/net/ssl/client_cert_store_nss.h b/net/ssl/client_cert_store_nss.h index 53b7c60..c2a9611 100644 --- a/net/ssl/client_cert_store_nss.h +++ b/net/ssl/client_cert_store_nss.h @@ -12,6 +12,8 @@ #include "net/ssl/client_cert_store.h" #include "net/ssl/ssl_cert_request_info.h" +typedef struct CERTCertListStr CERTCertList; + namespace crypto { class CryptoModuleBlockingPasswordDelegate; } @@ -32,9 +34,26 @@ class NET_EXPORT ClientCertStoreNSS : public ClientCertStore { CertificateList* selected_certs, const base::Closure& callback) OVERRIDE; + protected: + // Examines the certificates in |cert_list| to find all certificates that + // match the client certificate request in |request|, storing the matching + // certificates in |selected_certs|. + // If |query_nssdb| is true, NSS will be queried to construct full certificate + // chains. If it is false, only the certificate will be considered. + virtual void GetClientCertsImpl(CERTCertList* cert_list, + const SSLCertRequestInfo& request, + bool query_nssdb, + CertificateList* selected_certs); + private: friend class ClientCertStoreNSSTestDelegate; + void GetClientCertsOnWorkerThread( + scoped_ptr<crypto::CryptoModuleBlockingPasswordDelegate> + password_delegate, + const SSLCertRequestInfo* request, + CertificateList* selected_certs); + // A hook for testing. Filters |input_certs| using the logic being used to // filter the system store when GetClientCerts() is called. // Implemented by creating a list of certificates that otherwise would be |