Index: chrome/browser/net/nss_slot_factory.cc |
diff --git a/chrome/browser/net/nss_slot_factory.cc b/chrome/browser/net/nss_slot_factory.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..2fbb04a3e4ee6cd3990cb4dd96b2cb0859fb721e |
--- /dev/null |
+++ b/chrome/browser/net/nss_slot_factory.cc |
@@ -0,0 +1,305 @@ |
+// 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 "chrome/browser/net/nss_slot_factory.h" |
+ |
+#include <map> |
+ |
+#include "base/bind.h" |
+#include "base/file_util.h" |
+#include "base/files/file_path.h" |
+#include "base/lazy_instance.h" |
+#include "base/location.h" |
+#include "base/logging.h" |
+#include "base/memory/weak_ptr.h" |
+#include "base/stl_util.h" |
+#include "chrome/browser/profiles/profile_io_data.h" |
+#include "content/public/browser/browser_thread.h" |
+#include "content/public/browser/resource_context.h" |
+#include "crypto/nss_util.h" |
+#include "crypto/nss_util_internal.h" |
+#include "net/cert/nss_cert_database.h" |
+ |
+#if defined(OS_CHROMEOS) |
+#include "chromeos/dbus/cryptohome_client.h" |
+#include "chromeos/dbus/dbus_thread_manager.h" |
+#include "chrome/browser/chromeos/net/nss_cert_database_cros.h" |
+#endif |
+ |
+using content::BrowserThread; |
+ |
+namespace { |
+ |
+#if defined(OS_CHROMEOS) |
+void OnPkcs11GetTpmTokenInfoOnUIThread( |
+ const base::Callback<void(int)>& callback, |
+ chromeos::DBusMethodCallStatus call_status, |
+ const std::string& label, |
+ const std::string& user_pin, |
+ int slot) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ LOG(WARNING) << __func__ << " " << label << " " << slot; |
+ BrowserThread::PostTask( |
+ BrowserThread::IO, FROM_HERE, base::Bind(callback, slot)); |
+} |
+ |
+void GetTpmInfoForUserOnUIThread(const std::string& user_name, |
+ const base::Callback<void(int)>& callback) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ LOG(WARNING) << __func__ << " " << user_name; |
+ chromeos::DBusThreadManager::Get() |
+ ->GetCryptohomeClient() |
+ ->Pkcs11GetTpmTokenInfoForUser( |
+ user_name, |
+ base::Bind(&OnPkcs11GetTpmTokenInfoOnUIThread, callback)); |
+} |
+ |
+// NSSSlotService initializes and holds the public and private key slots for a |
+// given profile. |
+class NSSSlotService { |
+ public: |
+ NSSSlotService(const base::FilePath& user_dir, |
+ const std::string& user_name); |
+ virtual ~NSSSlotService(); |
+ |
+ crypto::ScopedPK11Slot GetPublicNSSKeySlot(); |
+ crypto::ScopedPK11Slot GetPrivateNSSKeySlot(); |
+ void OnPrivateNSSKeySlotForResourceContextReady( |
+ const base::Callback<void(crypto::ScopedPK11Slot)>& callback); |
+ void OnNSSCertDatabaseForResourceContextReady( |
+ const base::Callback<void(net::NSSCertDatabase*)>& callback); |
+ |
+ private: |
+ void OnGotTpmSlot(int slot); |
+ void StartTpmSlotInitializion(); |
+ |
+ base::FilePath user_dir_; |
+ std::string user_name_; |
+ crypto::ScopedPK11Slot public_slot_; |
+ crypto::ScopedPK11Slot private_slot_; |
+ scoped_ptr<chromeos::NSSCertDatabaseCros> cert_db_; |
+ |
+ std::vector<base::Callback<void(crypto::ScopedPK11Slot)> > |
+ tpm_ready_callback_list_; |
+ std::vector<base::Callback<void(net::NSSCertDatabase*)> > |
+ cert_db_ready_callback_list_; |
+ base::WeakPtrFactory<NSSSlotService> weak_ptr_factory_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(NSSSlotService); |
+}; |
+ |
+// XXX |
+// If the profile directory is /home/chronos/u-xxxxxx, the nss db will be at |
+// /home/chronos/u-xxxxxx/.pki/nssdb. You can get the profile directory for a |
+// given username if you need it by calling libchromeos' GetUserPath(). |
+// |
+NSSSlotService::NSSSlotService(const base::FilePath& user_dir, |
+ const std::string& user_name) |
+ : user_dir_(user_dir), |
+ user_name_(user_name), |
+ weak_ptr_factory_(this) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
+ // XXX should we do something to check that we aren't reopening the same |
+ // database used by the default public slot? |
+ public_slot_.reset(crypto::OpenPersistentNSSDBForPath(user_dir_)); |
+ LOG(WARNING) << "pub: " << public_slot_.get(); |
+ if (crypto::IsTPMTokenEnabledForNSS()) { |
+ if (crypto::IsTPMTokenReady()) { |
+ StartTpmSlotInitializion(); |
+ } else { |
+ LOG(WARNING) << "waiting for tpm ready ..."; |
+ crypto::OnTPMReady(base::Bind(&NSSSlotService::StartTpmSlotInitializion, |
+ weak_ptr_factory_.GetWeakPtr())); |
+ } |
+ } else { |
+ LOG(WARNING) << "using software private slot"; |
+ private_slot_ = GetPublicNSSKeySlot(); |
+ } |
+ crypto::DumpNSSSlotInfos(); |
+} |
+ |
+NSSSlotService::~NSSSlotService() { |
+ LOG(WARNING) << __func__; |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
+ // Don't close when NSS is < 3.15.1, because it would require an additional |
+ // sleep for 1 second after closing the database, due to |
+ // http://bugzil.la/875601. |
+ if (NSS_VersionCheck("3.15.1")) { |
+ if (public_slot_) { |
+ LOG(WARNING) << "closing nssdb"; |
+ SECStatus status = SECMOD_CloseUserDB(public_slot_.get()); |
+ if (status != SECSuccess) |
+ PLOG(ERROR) << "SECMOD_CloseUserDB failed: " << PORT_GetError(); |
+ } |
+ } |
+} |
+ |
+crypto::ScopedPK11Slot NSSSlotService::GetPublicNSSKeySlot() { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
+ return crypto::ScopedPK11Slot( |
+ public_slot_ ? PK11_ReferenceSlot(public_slot_.get()) : NULL); |
+} |
+ |
+crypto::ScopedPK11Slot NSSSlotService::GetPrivateNSSKeySlot() { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
+ return crypto::ScopedPK11Slot( |
+ private_slot_ ? PK11_ReferenceSlot(private_slot_.get()) : NULL); |
+} |
+ |
+void NSSSlotService::OnPrivateNSSKeySlotForResourceContextReady( |
+ const base::Callback<void(crypto::ScopedPK11Slot)>& callback) { |
+ VLOG(1) << __func__; |
+ if (private_slot_) |
+ callback.Run(GetPrivateNSSKeySlot()); |
+ else |
+ tpm_ready_callback_list_.push_back(callback); |
+} |
+ |
+void NSSSlotService::OnNSSCertDatabaseForResourceContextReady( |
+ const base::Callback<void(net::NSSCertDatabase*)>& callback) { |
+ if (cert_db_) |
+ callback.Run(cert_db_.get()); |
+ else |
+ cert_db_ready_callback_list_.push_back(callback); |
+} |
+ |
+void NSSSlotService::OnGotTpmSlot(int slot) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
+ LOG(WARNING) << __func__ << " " << slot; |
+ if (slot >= 0) { |
+ private_slot_.reset(crypto::GetTPMSlotForId(slot)); |
+ LOG(WARNING) << " private_slot_ " << private_slot_.get(); |
+ cert_db_.reset(new chromeos::NSSCertDatabaseCros(GetPublicNSSKeySlot(), |
+ GetPrivateNSSKeySlot())); |
+ |
+ for (std::vector<base::Callback<void(crypto::ScopedPK11Slot)> >::iterator |
+ i = tpm_ready_callback_list_.begin(); |
+ i != tpm_ready_callback_list_.end(); |
+ ++i) { |
+ (*i).Run(GetPrivateNSSKeySlot()); |
+ } |
+ tpm_ready_callback_list_.clear(); |
+ |
+ for (std::vector<base::Callback<void(net::NSSCertDatabase*)> >::iterator |
+ i = cert_db_ready_callback_list_.begin(); |
+ i != cert_db_ready_callback_list_.end(); |
+ ++i) { |
+ (*i).Run(cert_db_.get()); |
+ } |
+ cert_db_ready_callback_list_.clear(); |
+ } |
+ crypto::DumpNSSSlotInfos(); |
+} |
+ |
+void NSSSlotService::StartTpmSlotInitializion() { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
+ LOG(WARNING) << __func__; |
+ |
+ BrowserThread::PostTask( |
+ BrowserThread::UI, |
+ FROM_HERE, |
+ base::Bind(&GetTpmInfoForUserOnUIThread, |
+ user_name_, |
+ base::Bind(&NSSSlotService::OnGotTpmSlot, |
+ weak_ptr_factory_.GetWeakPtr()))); |
+} |
+ |
+class NSSSlotFactory { |
+ public: |
+ NSSSlotService* GetService(content::ResourceContext* context); |
+ |
+ ~NSSSlotFactory(); |
+ |
+ private: |
+ friend struct base::DefaultLazyInstanceTraits<NSSSlotFactory>; |
+ |
+ std::map<std::string, NSSSlotService*> service_map_; |
+}; |
+ |
+base::LazyInstance<NSSSlotFactory>::Leaky |
+ g_nss_slot_factory = LAZY_INSTANCE_INITIALIZER; |
+ |
+NSSSlotService* NSSSlotFactory::GetService(content::ResourceContext* context) { |
+ ProfileIOData* io_data = ProfileIOData::FromResourceContext(context); |
+ std::string user_name = io_data->user_name(); |
+ if (user_name.empty()) |
+ return NULL; |
+ |
+ std::map<std::string, NSSSlotService*>::iterator i = |
+ service_map_.find(user_name); |
+ if (i != service_map_.end()) |
+ return (*i).second; |
+ |
+ NSSSlotService* service = |
+ new NSSSlotService(io_data->profile_path(), io_data->user_name()); |
+ service_map_[user_name] = service; |
+ return service; |
+} |
+ |
+// XXX this will be run on UI thread due to lazyinstance atexit thingy? |
+// XXX made lazyinstance leaky ... so it won't be run at all, like nss_util |
+NSSSlotFactory::~NSSSlotFactory() { |
+ LOG(WARNING) << __func__; |
+ STLDeleteValues(&service_map_); |
+} |
+#endif // defined(OS_CHROMEOS) |
+ |
+void PassTPMToCallback( |
+ const base::Callback<void(crypto::ScopedPK11Slot)>& callback) { |
+ callback.Run(crypto::ScopedPK11Slot(crypto::GetPrivateNSSKeySlot())); |
+} |
+ |
+} // namespace |
+ |
+crypto::ScopedPK11Slot GetPublicNSSKeySlotForResourceContext( |
+ content::ResourceContext* context) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
+#if defined(OS_CHROMEOS) |
+ NSSSlotService* service = g_nss_slot_factory.Get().GetService(context); |
+ if (service) |
+ return service->GetPublicNSSKeySlot(); |
+#endif |
+ return crypto::ScopedPK11Slot(crypto::GetPublicNSSKeySlot()); |
+} |
+ |
+crypto::ScopedPK11Slot GetPrivateNSSKeySlotForResourceContext( |
+ content::ResourceContext* context) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
+#if defined(OS_CHROMEOS) |
+ NSSSlotService* service = g_nss_slot_factory.Get().GetService(context); |
+ if (service) |
+ return service->GetPrivateNSSKeySlot(); |
+#endif |
+ return crypto::ScopedPK11Slot(crypto::GetPrivateNSSKeySlot()); |
+} |
+ |
+void OnPrivateNSSKeySlotForResourceContextReady( |
+ content::ResourceContext* context, |
+ const base::Callback<void(crypto::ScopedPK11Slot)>& callback) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
+#if defined(OS_CHROMEOS) |
+ NSSSlotService* service = g_nss_slot_factory.Get().GetService(context); |
+ if (service) { |
+ service->OnPrivateNSSKeySlotForResourceContextReady(callback); |
+ return; |
+ } |
+ crypto::OnTPMReady(base::Bind(&PassTPMToCallback, callback)); |
+#else |
+ PassTPMToCallback(callback); |
+#endif |
+} |
+ |
+void GetNSSCertDatabaseForResourceContext( |
+ content::ResourceContext* context, |
+ const base::Callback<void(net::NSSCertDatabase*)>& callback) { |
+#if defined(OS_CHROMEOS) |
+ NSSSlotService* service = g_nss_slot_factory.Get().GetService(context); |
+ if (service) { |
+ service->OnNSSCertDatabaseForResourceContextReady(callback); |
+ return; |
+ } |
+#else |
+ callback.Run(NSSCertDatabase::GetInstance()); |
+#endif |
+} |