summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authormattm@chromium.org <mattm@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-12-17 00:09:00 +0000
committermattm@chromium.org <mattm@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-12-17 00:09:00 +0000
commite53c02329933022491988cb58f58423f7a18ba61 (patch)
treef87bb5be9cabe9d03a370496ab71419e3a43fe88 /net
parent61e2b8591c6940552fdac1bed5c8787e99f30d97 (diff)
downloadchromium_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.cc106
-rw-r--r--net/cert/nss_profile_filter_chromeos.h60
-rw-r--r--net/net.gyp5
-rw-r--r--net/ssl/client_cert_store.h3
-rw-r--r--net/ssl/client_cert_store_chromeos.cc94
-rw-r--r--net/ssl/client_cert_store_chromeos.h66
-rw-r--r--net/ssl/client_cert_store_chromeos_unittest.cc193
-rw-r--r--net/ssl/client_cert_store_nss.cc88
-rw-r--r--net/ssl/client_cert_store_nss.h19
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