diff options
-rw-r--r-- | chrome/browser/net/nss_context.h | 32 | ||||
-rw-r--r-- | chrome/browser/net/nss_context_chromeos.cc | 29 | ||||
-rw-r--r-- | chrome/browser/net/nss_context_linux.cc | 21 | ||||
-rw-r--r-- | chrome/browser/profiles/profile_io_data.cc | 132 | ||||
-rw-r--r-- | chrome/browser/profiles/profile_io_data.h | 11 | ||||
-rw-r--r-- | chrome/browser/ui/webui/chromeos/cryptohome_web_ui_handler.cc | 2 | ||||
-rw-r--r-- | chrome/browser/ui/webui/options/certificate_manager_browsertest.cc | 28 | ||||
-rw-r--r-- | chrome/chrome_browser.gypi | 6 | ||||
-rw-r--r-- | crypto/nss_util.cc | 236 | ||||
-rw-r--r-- | crypto/nss_util.h | 11 | ||||
-rw-r--r-- | crypto/nss_util_internal.h | 43 |
11 files changed, 537 insertions, 14 deletions
diff --git a/chrome/browser/net/nss_context.h b/chrome/browser/net/nss_context.h new file mode 100644 index 0000000..0735df2 --- /dev/null +++ b/chrome/browser/net/nss_context.h @@ -0,0 +1,32 @@ +// 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. + +#ifndef CHROME_BROWSER_NET_NSS_CONTEXT_H_ +#define CHROME_BROWSER_NET_NSS_CONTEXT_H_ + +#include <string> + +#include "base/callback.h" +#include "base/compiler_specific.h" +#include "crypto/scoped_nss_types.h" + +namespace content { +class ResourceContext; +} // namespace content + +// Returns a reference to the public slot for the user associated with +// |context|. Should be called only on the IO thread. +crypto::ScopedPK11Slot GetPublicNSSKeySlotForResourceContext( + content::ResourceContext* context); + +// Returns a reference to the private slot for the user associated with +// |context|, if it is loaded. If it is not loaded and |callback| is non-null, +// the |callback| will be run once the slot is loaded. +// Should be called only on the IO thread. +crypto::ScopedPK11Slot GetPrivateNSSKeySlotForResourceContext( + content::ResourceContext* context, + const base::Callback<void(crypto::ScopedPK11Slot)>& callback) + WARN_UNUSED_RESULT; + +#endif // CHROME_BROWSER_NET_NSS_CONTEXT_H_ diff --git a/chrome/browser/net/nss_context_chromeos.cc b/chrome/browser/net/nss_context_chromeos.cc new file mode 100644 index 0000000..b789f4e --- /dev/null +++ b/chrome/browser/net/nss_context_chromeos.cc @@ -0,0 +1,29 @@ +// 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_context.h" + +#include "chrome/browser/profiles/profile_io_data.h" +#include "content/public/browser/browser_thread.h" +#include "crypto/nss_util_internal.h" + +namespace { +std::string GetUsername(content::ResourceContext* context) { + return ProfileIOData::FromResourceContext(context)->username_hash(); +} +} // namespace + +crypto::ScopedPK11Slot GetPublicNSSKeySlotForResourceContext( + content::ResourceContext* context) { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + return crypto::GetPublicSlotForChromeOSUser(GetUsername(context)); +} + +crypto::ScopedPK11Slot GetPrivateNSSKeySlotForResourceContext( + content::ResourceContext* context, + const base::Callback<void(crypto::ScopedPK11Slot)>& callback) { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + return crypto::GetPrivateSlotForChromeOSUser(GetUsername(context), callback); +} + diff --git a/chrome/browser/net/nss_context_linux.cc b/chrome/browser/net/nss_context_linux.cc new file mode 100644 index 0000000..d737387 --- /dev/null +++ b/chrome/browser/net/nss_context_linux.cc @@ -0,0 +1,21 @@ +// 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_context.h" + +#include "content/public/browser/browser_thread.h" +#include "crypto/nss_util_internal.h" + +crypto::ScopedPK11Slot GetPublicNSSKeySlotForResourceContext( + content::ResourceContext* context) { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + return crypto::ScopedPK11Slot(crypto::GetPublicNSSKeySlot()); +} + +crypto::ScopedPK11Slot GetPrivateNSSKeySlotForResourceContext( + content::ResourceContext* context, + const base::Callback<void(crypto::ScopedPK11Slot)>& callback) { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + return crypto::ScopedPK11Slot(crypto::GetPrivateNSSKeySlot()); +} diff --git a/chrome/browser/profiles/profile_io_data.cc b/chrome/browser/profiles/profile_io_data.cc index 46129e6..e50779c 100644 --- a/chrome/browser/profiles/profile_io_data.cc +++ b/chrome/browser/profiles/profile_io_data.cc @@ -89,11 +89,17 @@ #if defined(OS_CHROMEOS) #include "chrome/browser/chromeos/drive/drive_protocol_handler.h" +#include "chrome/browser/chromeos/login/user.h" +#include "chrome/browser/chromeos/login/user_manager.h" #include "chrome/browser/chromeos/policy/policy_cert_service.h" #include "chrome/browser/chromeos/policy/policy_cert_service_factory.h" #include "chrome/browser/chromeos/policy/policy_cert_verifier.h" #include "chrome/browser/chromeos/settings/cros_settings.h" +#include "chromeos/dbus/cryptohome_client.h" +#include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/settings/cros_settings_names.h" +#include "crypto/nss_util.h" +#include "crypto/nss_util_internal.h" #endif // defined(OS_CHROMEOS) #if defined(USE_NSS) @@ -228,6 +234,112 @@ class DebugDevToolsInterceptor }; #endif // defined(DEBUG_DEVTOOLS) +#if defined(OS_CHROMEOS) +// The following four functions are responsible for initializing NSS for each +// profile on ChromeOS, which has a separate NSS database and TPM slot +// per-profile. +// +// Initialization basically follows these steps: +// 1) Get some info from chromeos::UserManager about the User for this profile. +// 2) Tell nss_util to initialize the software slot for this profile. +// 3) Wait for the TPM module to be loaded by nss_util if it isn't already. +// 4) Ask CryptohomeClient which TPM slot id corresponds to this profile. +// 5) Tell nss_util to use that slot id on the TPM module. +// +// Some of these steps must happen on the UI thread, others must happen on the +// IO thread: +// UI thread IO Thread +// +// ProfileIOData::InitializeOnUIThread +// | +// chromeos::UserManager::GetUserByProfile +// \---------------------------------------v +// StartNSSInitOnIOThread +// | +// crypto::InitializeNSSForChromeOSUser +// | +// crypto::IsTPMTokenReady +// | +// StartTPMSlotInitializationOnIOThread +// v---------------------------------------/ +// GetTPMInfoForUserOnUIThread +// | +// CryptohomeClient::Pkcs11GetTpmTokenInfoForUser +// | +// DidGetTPMInfoForUserOnUIThread +// \---------------------------------------v +// crypto::InitializeTPMForChromeOSUser + +void DidGetTPMInfoForUserOnUIThread(const std::string& username_hash, + chromeos::DBusMethodCallStatus call_status, + const std::string& label, + const std::string& user_pin, + int slot_id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + if (call_status == chromeos::DBUS_METHOD_CALL_FAILURE) { + NOTREACHED() << "dbus error getting TPM info for " << username_hash; + return; + } + DVLOG(1) << "Got TPM slot for " << username_hash << ": " << slot_id; + BrowserThread::PostTask( + BrowserThread::IO, + FROM_HERE, + base::Bind( + &crypto::InitializeTPMForChromeOSUser, username_hash, slot_id)); +} + +void GetTPMInfoForUserOnUIThread(const std::string& username, + const std::string& username_hash) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DVLOG(1) << "Getting TPM info from cryptohome for " + << " " << username << " " << username_hash; + chromeos::DBusThreadManager::Get() + ->GetCryptohomeClient() + ->Pkcs11GetTpmTokenInfoForUser( + username, + base::Bind(&DidGetTPMInfoForUserOnUIThread, username_hash)); +} + +void StartTPMSlotInitializationOnIOThread(const std::string& username, + const std::string& username_hash) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + BrowserThread::PostTask( + BrowserThread::UI, + FROM_HERE, + base::Bind(&GetTPMInfoForUserOnUIThread, username, username_hash)); +} + +void StartNSSInitOnIOThread(const std::string& username, + const std::string& username_hash, + const base::FilePath& path, + bool is_primary_user) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DVLOG(1) << "Starting NSS init for " << username + << " hash:" << username_hash + << " is_primary_user:" << is_primary_user; + + if (!crypto::InitializeNSSForChromeOSUser( + username, username_hash, is_primary_user, path)) { + // If the user already exists in nss_util's map, it is already initialized + // or in the process of being initialized. In either case, there's no need + // to do anything. + return; + } + + if (crypto::IsTPMTokenEnabledForNSS()) { + if (crypto::IsTPMTokenReady(base::Bind( + &StartTPMSlotInitializationOnIOThread, username, username_hash))) { + StartTPMSlotInitializationOnIOThread(username, username_hash); + } else { + DVLOG(1) << "Waiting for tpm ready ..."; + } + } else { + crypto::InitializePrivateSoftwareSlotForChromeOSUser(username_hash); + } +} +#endif // defined(OS_CHROMEOS) + } // namespace void ProfileIOData::InitializeOnUIThread(Profile* profile) { @@ -278,6 +390,25 @@ void ProfileIOData::InitializeOnUIThread(Profile* profile) { params->managed_mode_url_filter = managed_user_service->GetURLFilterForIOThread(); #endif +#if defined(OS_CHROMEOS) + chromeos::UserManager* user_manager = chromeos::UserManager::Get(); + if (user_manager) { + chromeos::User* user = user_manager->GetUserByProfile(profile); + if (user) { + params->username_hash = user->username_hash(); + bool is_primary_user = (user_manager->GetPrimaryUser() == user); + BrowserThread::PostTask(BrowserThread::IO, + FROM_HERE, + base::Bind(&StartNSSInitOnIOThread, + user->email(), + user->username_hash(), + profile->GetPath(), + is_primary_user)); + } + } + if (params->username_hash.empty()) + LOG(WARNING) << "no username_hash"; +#endif params->profile = profile; profile_params_.reset(params.release()); @@ -833,6 +964,7 @@ void ProfileIOData::Init(content::ProtocolHandlerMap* protocol_handlers) const { main_request_context_->set_cert_verifier( io_thread_globals->cert_verifier.get()); } + username_hash_ = profile_params_->username_hash; #else main_request_context_->set_cert_verifier( io_thread_globals->cert_verifier.get()); diff --git a/chrome/browser/profiles/profile_io_data.h b/chrome/browser/profiles/profile_io_data.h index 136235d..d68adde 100644 --- a/chrome/browser/profiles/profile_io_data.h +++ b/chrome/browser/profiles/profile_io_data.h @@ -177,6 +177,12 @@ class ProfileIOData { return transport_security_state_.get(); } +#if defined(OS_CHROMEOS) + std::string username_hash() const { + return username_hash_; + } +#endif + bool is_incognito() const { return is_incognito_; } @@ -270,6 +276,10 @@ class ProfileIOData { scoped_refptr<const ManagedModeURLFilter> managed_mode_url_filter; #endif +#if defined(OS_CHROMEOS) + std::string username_hash; +#endif + // The profile this struct was populated from. It's passed as a void* to // ensure it's not accidently used on the IO thread. Before using it on the // UI thread, call ProfileManager::IsValidProfile to ensure it's alive. @@ -494,6 +504,7 @@ class ProfileIOData { http_server_properties_; #if defined(OS_CHROMEOS) mutable scoped_ptr<policy::PolicyCertVerifier> cert_verifier_; + mutable std::string username_hash_; #endif mutable scoped_ptr<net::TransportSecurityPersister> diff --git a/chrome/browser/ui/webui/chromeos/cryptohome_web_ui_handler.cc b/chrome/browser/ui/webui/chromeos/cryptohome_web_ui_handler.cc index b38e73e..9587dbf 100644 --- a/chrome/browser/ui/webui/chromeos/cryptohome_web_ui_handler.cc +++ b/chrome/browser/ui/webui/chromeos/cryptohome_web_ui_handler.cc @@ -43,7 +43,7 @@ void CryptohomeWebUIHandler::OnPageLoaded(const base::ListValue* args) { BrowserThread::PostTaskAndReplyWithResult( BrowserThread::IO, FROM_HERE, - base::Bind(&crypto::IsTPMTokenReady), + base::Bind(&crypto::IsTPMTokenReady, base::Closure()), base::Bind(&CryptohomeWebUIHandler::DidGetNSSUtilInfoOnUIThread, weak_ptr_factory_.GetWeakPtr())); } diff --git a/chrome/browser/ui/webui/options/certificate_manager_browsertest.cc b/chrome/browser/ui/webui/options/certificate_manager_browsertest.cc index 5faef24..d0fab46 100644 --- a/chrome/browser/ui/webui/options/certificate_manager_browsertest.cc +++ b/chrome/browser/ui/webui/options/certificate_manager_browsertest.cc @@ -46,10 +46,36 @@ class CertificateManagerBrowserTest : public options::OptionsUIBrowserTest { policy::BrowserPolicyConnector::SetPolicyProviderForTesting(&provider_); } + void SetUpOnIOThread() { +#if defined(OS_CHROMEOS) + test_nssdb_.reset(new crypto::ScopedTestNSSDB()); +#endif + } + + void TearDownOnIOThread() { +#if defined(OS_CHROMEOS) + test_nssdb_.reset(); +#endif + } + virtual void SetUpOnMainThread() OVERRIDE { + content::BrowserThread::PostTask( + content::BrowserThread::IO, + FROM_HERE, + base::Bind(&CertificateManagerBrowserTest::SetUpOnIOThread, this)); + content::RunAllPendingInMessageLoop(content::BrowserThread::IO); + content::RunAllPendingInMessageLoop(); } + virtual void CleanUpOnMainThread() OVERRIDE { + content::BrowserThread::PostTask( + content::BrowserThread::IO, + FROM_HERE, + base::Bind(&CertificateManagerBrowserTest::TearDownOnIOThread, this)); + content::RunAllPendingInMessageLoop(content::BrowserThread::IO); + } + #if defined(OS_CHROMEOS) void LoadONCPolicy(const std::string& filename) { const std::string& user_policy_blob = @@ -86,7 +112,7 @@ class CertificateManagerBrowserTest : public options::OptionsUIBrowserTest { policy::MockConfigurationPolicyProvider provider_; #if defined(OS_CHROMEOS) policy::DevicePolicyCrosTestHelper device_policy_test_helper_; - crypto::ScopedTestNSSDB test_nssdb_; + scoped_ptr<crypto::ScopedTestNSSDB> test_nssdb_; #endif }; diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 23ae3e4..efaa554 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -1225,6 +1225,9 @@ 'browser/net/net_pref_observer.h', 'browser/net/network_stats.cc', 'browser/net/network_stats.h', + 'browser/net/nss_context_chromeos.cc', + 'browser/net/nss_context_linux.cc', + 'browser/net/nss_context.h', 'browser/net/preconnect.cc', 'browser/net/preconnect.h', 'browser/net/predictor.cc', @@ -3115,6 +3118,9 @@ 'sources!': [ 'browser/certificate_manager_model.cc', 'browser/certificate_manager_model.h', + 'browser/net/nss_context_chromeos.cc', + 'browser/net/nss_context_linux.cc', + 'browser/net/nss_context.h', ], }], ['toolkit_uses_gtk == 1', { diff --git a/crypto/nss_util.cc b/crypto/nss_util.cc index 82556b4..5a33b6c 100644 --- a/crypto/nss_util.cc +++ b/crypto/nss_util.cc @@ -21,8 +21,10 @@ #include <sys/param.h> #endif +#include <map> #include <vector> +#include "base/callback.h" #include "base/cpu.h" #include "base/debug/alias.h" #include "base/debug/stack_trace.h" @@ -35,6 +37,7 @@ #include "base/memory/scoped_ptr.h" #include "base/metrics/histogram.h" #include "base/native_library.h" +#include "base/stl_util.h" #include "base/strings/stringprintf.h" #include "base/threading/thread_checker.h" #include "base/threading/thread_restrictions.h" @@ -89,6 +92,7 @@ base::FilePath GetDefaultConfigDirectory() { LOG(ERROR) << "Failed to create " << dir.value() << " directory."; dir.clear(); } + DVLOG(2) << "DefaultConfigDirectory: " << dir.value(); return dir; } @@ -204,6 +208,58 @@ void CrashOnNSSInitFailure() { LOG(FATAL) << "nss_error=" << nss_error << ", os_error=" << os_error; } +#if defined(OS_CHROMEOS) +class ChromeOSUserData { + public: + ChromeOSUserData(ScopedPK11Slot public_slot, bool is_primary_user) + : public_slot_(public_slot.Pass()), + is_primary_user_(is_primary_user) {} + ~ChromeOSUserData() { + if (public_slot_ && !is_primary_user_) { + SECStatus status = SECMOD_CloseUserDB(public_slot_.get()); + if (status != SECSuccess) + PLOG(ERROR) << "SECMOD_CloseUserDB failed: " << PORT_GetError(); + } + } + + ScopedPK11Slot GetPublicSlot() { + return ScopedPK11Slot( + public_slot_ ? PK11_ReferenceSlot(public_slot_.get()) : NULL); + } + + ScopedPK11Slot GetPrivateSlot( + const base::Callback<void(ScopedPK11Slot)>& callback) { + if (private_slot_) + return ScopedPK11Slot(PK11_ReferenceSlot(private_slot_.get())); + if (!callback.is_null()) + tpm_ready_callback_list_.push_back(callback); + return ScopedPK11Slot(); + } + + void SetPrivateSlot(ScopedPK11Slot private_slot) { + DCHECK(!private_slot_); + private_slot_ = private_slot.Pass(); + + SlotReadyCallbackList callback_list; + callback_list.swap(tpm_ready_callback_list_); + for (SlotReadyCallbackList::iterator i = callback_list.begin(); + i != callback_list.end(); + ++i) { + (*i).Run(ScopedPK11Slot(PK11_ReferenceSlot(private_slot_.get()))); + } + } + + private: + ScopedPK11Slot public_slot_; + ScopedPK11Slot private_slot_; + bool is_primary_user_; + + typedef std::vector<base::Callback<void(ScopedPK11Slot)> > + SlotReadyCallbackList; + SlotReadyCallbackList tpm_ready_callback_list_; +}; +#endif // defined(OS_CHROMEOS) + class NSSInitSingleton { public: #if defined(OS_CHROMEOS) @@ -225,6 +281,21 @@ class NSSInitSingleton { } } + PK11SlotInfo* OpenPersistentNSSDBForPath(const base::FilePath& path) { + DCHECK(thread_checker_.CalledOnValidThread()); + // NSS is allowed to do IO on the current thread since dispatching + // to a dedicated thread would still have the affect of blocking + // the current thread, due to NSS's internal locking requirements + base::ThreadRestrictions::ScopedAllowIO allow_io; + + base::FilePath nssdb_path = path.AppendASCII(".pki").AppendASCII("nssdb"); + if (!base::CreateDirectory(nssdb_path)) { + LOG(ERROR) << "Failed to create " << nssdb_path.value() << " directory."; + return NULL; + } + return OpenUserDB(nssdb_path, kNSSDatabaseName); + } + void EnableTPMTokenForNSS() { DCHECK(thread_checker_.CalledOnValidThread()); @@ -234,6 +305,11 @@ class NSSInitSingleton { tpm_token_enabled_for_nss_ = true; } + bool IsTPMTokenEnabledForNSS() { + DCHECK(thread_checker_.CalledOnValidThread()); + return tpm_token_enabled_for_nss_; + } + bool InitializeTPMToken(int token_slot_id) { DCHECK(thread_checker_.CalledOnValidThread()); @@ -267,19 +343,42 @@ class NSSInitSingleton { if (chaps_module_){ tpm_slot_ = GetTPMSlotForId(token_slot_id); - return tpm_slot_ != NULL; + if (!tpm_slot_) + return false; + + TPMReadyCallbackList callback_list; + callback_list.swap(tpm_ready_callback_list_); + for (TPMReadyCallbackList::iterator i = + callback_list.begin(); + i != callback_list.end(); + ++i) { + (*i).Run(); + } + + return true; } return false; } - bool IsTPMTokenReady() { - // TODO(mattm): Change to DCHECK when callers have been fixed. - if (!thread_checker_.CalledOnValidThread()) { + bool IsTPMTokenReady(const base::Closure& callback) { + if (!callback.is_null()) { + // Cannot DCHECK in the general case yet, but since the callback is + // a new addition to the API, DCHECK to make sure at least the new uses + // don't regress. + DCHECK(thread_checker_.CalledOnValidThread()); + } else if (!thread_checker_.CalledOnValidThread()) { + // TODO(mattm): Change to DCHECK when callers have been fixed. DVLOG(1) << "Called on wrong thread.\n" << base::debug::StackTrace().ToString(); } - return tpm_slot_ != NULL; + if (tpm_slot_ != NULL) + return true; + + if (!callback.is_null()) + tpm_ready_callback_list_.push_back(callback); + + return false; } // Note that CK_SLOT_ID is an unsigned long, but cryptohome gives us the slot @@ -291,7 +390,7 @@ class NSSInitSingleton { if (!chaps_module_) return NULL; - VLOG(1) << "Poking chaps module."; + DVLOG(3) << "Poking chaps module."; SECStatus rv = SECMOD_UpdateSlotList(chaps_module_); if (rv != SECSuccess) PLOG(ERROR) << "SECMOD_UpdateSlotList failed: " << PORT_GetError(); @@ -301,11 +400,85 @@ class NSSInitSingleton { LOG(ERROR) << "TPM slot " << slot_id << " not found."; return slot; } + + bool InitializeNSSForChromeOSUser( + const std::string& email, + const std::string& username_hash, + bool is_primary_user, + const base::FilePath& path) { + DCHECK(thread_checker_.CalledOnValidThread()); + if (chromeos_user_map_.find(username_hash) != chromeos_user_map_.end()) { + // This user already exists in our mapping. + DVLOG(2) << username_hash << " already initialized."; + return false; + } + ScopedPK11Slot public_slot; + if (is_primary_user) { + DVLOG(2) << "Primary user, using GetPublicNSSKeySlot()"; + public_slot.reset(GetPublicNSSKeySlot()); + } else { + DVLOG(2) << "Opening NSS DB " << path.value(); + public_slot.reset(OpenPersistentNSSDBForPath(path)); + } + chromeos_user_map_[username_hash] = + new ChromeOSUserData(public_slot.Pass(), is_primary_user); + return true; + } + + void InitializeTPMForChromeOSUser(const std::string& username_hash, + CK_SLOT_ID slot_id) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(chromeos_user_map_.find(username_hash) != chromeos_user_map_.end()); + chromeos_user_map_[username_hash] + ->SetPrivateSlot(ScopedPK11Slot(GetTPMSlotForId(slot_id))); + } + + void InitializePrivateSoftwareSlotForChromeOSUser( + const std::string& username_hash) { + DCHECK(thread_checker_.CalledOnValidThread()); + LOG(WARNING) << "using software private slot for " << username_hash; + DCHECK(chromeos_user_map_.find(username_hash) != chromeos_user_map_.end()); + chromeos_user_map_[username_hash]->SetPrivateSlot( + chromeos_user_map_[username_hash]->GetPublicSlot()); + } + + ScopedPK11Slot GetPublicSlotForChromeOSUser( + const std::string& username_hash) { + DCHECK(thread_checker_.CalledOnValidThread()); + if (test_slot_) { + DVLOG(2) << "returning test_slot_ for " << username_hash; + return ScopedPK11Slot(PK11_ReferenceSlot(test_slot_)); + } + + if (chromeos_user_map_.find(username_hash) == chromeos_user_map_.end()) { + LOG(ERROR) << username_hash << " not initialized."; + return ScopedPK11Slot(); + } + return chromeos_user_map_[username_hash]->GetPublicSlot(); + } + + ScopedPK11Slot GetPrivateSlotForChromeOSUser( + const std::string& username_hash, + const base::Callback<void(ScopedPK11Slot)>& callback) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(chromeos_user_map_.find(username_hash) != chromeos_user_map_.end()); + + if (test_slot_) { + DVLOG(2) << "returning test_slot_ for " << username_hash; + return ScopedPK11Slot(PK11_ReferenceSlot(test_slot_)); + } + + return chromeos_user_map_[username_hash]->GetPrivateSlot(callback); + } #endif // defined(OS_CHROMEOS) bool OpenTestNSSDB() { DCHECK(thread_checker_.CalledOnValidThread()); + // NSS is allowed to do IO on the current thread since dispatching + // to a dedicated thread would still have the affect of blocking + // the current thread, due to NSS's internal locking requirements + base::ThreadRestrictions::ScopedAllowIO allow_io; if (test_slot_) return true; @@ -317,6 +490,10 @@ class NSSInitSingleton { void CloseTestNSSDB() { DCHECK(thread_checker_.CalledOnValidThread()); + // NSS is allowed to do IO on the current thread since dispatching + // to a dedicated thread would still have the affect of blocking + // the current thread, due to NSS's internal locking requirements + base::ThreadRestrictions::ScopedAllowIO allow_io; if (!test_slot_) return; @@ -354,7 +531,7 @@ class NSSInitSingleton { #if defined(OS_CHROMEOS) if (tpm_token_enabled_for_nss_) { - if (IsTPMTokenReady()) { + if (IsTPMTokenReady(base::Closure())) { return PK11_ReferenceSlot(tpm_slot_); } else { // If we were supposed to get the hardware token, but were @@ -505,6 +682,9 @@ class NSSInitSingleton { // prevent non-joinable threads from using NSS after it's already been shut // down. ~NSSInitSingleton() { +#if defined(OS_CHROMEOS) + STLDeleteValues(&chromeos_user_map_); +#endif if (tpm_slot_) { PK11_FreeSlot(tpm_slot_); tpm_slot_ = NULL; @@ -612,12 +792,18 @@ class NSSInitSingleton { static bool force_nodb_init_; bool tpm_token_enabled_for_nss_; + typedef std::vector<base::Closure> TPMReadyCallbackList; + TPMReadyCallbackList tpm_ready_callback_list_; SECMODModule* chaps_module_; PK11SlotInfo* software_slot_; PK11SlotInfo* test_slot_; PK11SlotInfo* tpm_slot_; SECMODModule* root_; bool chromeos_user_logged_in_; +#if defined(OS_CHROMEOS) + typedef std::map<std::string, ChromeOSUserData*> ChromeOSUserMap; + ChromeOSUserMap chromeos_user_map_; +#endif #if defined(USE_NSS) // TODO(davidben): When https://bugzilla.mozilla.org/show_bug.cgi?id=564011 // is fixed, we will no longer need the lock. @@ -781,13 +967,45 @@ void EnableTPMTokenForNSS() { g_nss_singleton.Get().EnableTPMTokenForNSS(); } -bool IsTPMTokenReady() { - return g_nss_singleton.Get().IsTPMTokenReady(); +bool IsTPMTokenEnabledForNSS() { + return g_nss_singleton.Get().IsTPMTokenEnabledForNSS(); +} + +bool IsTPMTokenReady(const base::Closure& callback) { + return g_nss_singleton.Get().IsTPMTokenReady(callback); } bool InitializeTPMToken(int token_slot_id) { return g_nss_singleton.Get().InitializeTPMToken(token_slot_id); } + +bool InitializeNSSForChromeOSUser( + const std::string& email, + const std::string& username_hash, + bool is_primary_user, + const base::FilePath& path) { + return g_nss_singleton.Get().InitializeNSSForChromeOSUser( + email, username_hash, is_primary_user, path); +} +void InitializeTPMForChromeOSUser( + const std::string& username_hash, + CK_SLOT_ID slot_id) { + g_nss_singleton.Get().InitializeTPMForChromeOSUser(username_hash, slot_id); +} +void InitializePrivateSoftwareSlotForChromeOSUser( + const std::string& username_hash) { + g_nss_singleton.Get().InitializePrivateSoftwareSlotForChromeOSUser( + username_hash); +} +ScopedPK11Slot GetPublicSlotForChromeOSUser(const std::string& username_hash) { + return g_nss_singleton.Get().GetPublicSlotForChromeOSUser(username_hash); +} +ScopedPK11Slot GetPrivateSlotForChromeOSUser( + const std::string& username_hash, + const base::Callback<void(ScopedPK11Slot)>& callback) { + return g_nss_singleton.Get().GetPrivateSlotForChromeOSUser(username_hash, + callback); +} #endif // defined(OS_CHROMEOS) base::Time PRTimeToBaseTime(PRTime prtime) { diff --git a/crypto/nss_util.h b/crypto/nss_util.h index efc8140..4d3d3e2 100644 --- a/crypto/nss_util.h +++ b/crypto/nss_util.h @@ -7,6 +7,8 @@ #include <string> #include "base/basictypes.h" +#include "base/callback_forward.h" +#include "base/compiler_specific.h" #include "crypto/crypto_export.h" namespace base { @@ -102,11 +104,18 @@ CRYPTO_EXPORT void OpenPersistentNSSDB(); // GetPrivateNSSKeySlot() will return the TPM slot if one was found. CRYPTO_EXPORT void EnableTPMTokenForNSS(); +// Returns true if EnableTPMTokenForNSS has been called. +CRYPTO_EXPORT bool IsTPMTokenEnabledForNSS(); + // Returns true if the TPM is owned and PKCS#11 initialized with the // user and security officer PINs, and has been enabled in NSS by // calling EnableTPMForNSS, and Chaps has been successfully // loaded into NSS. -CRYPTO_EXPORT bool IsTPMTokenReady(); +// If |callback| is non-null and the function returns false, the |callback| will +// be run once the TPM is ready. |callback| will never be run if the function +// returns true. +CRYPTO_EXPORT bool IsTPMTokenReady(const base::Closure& callback) + WARN_UNUSED_RESULT; // Initialize the TPM token. Does nothing if it is already initialized. CRYPTO_EXPORT bool InitializeTPMToken(int token_slot_id); diff --git a/crypto/nss_util_internal.h b/crypto/nss_util_internal.h index 056ec28..fb76bd8 100644 --- a/crypto/nss_util_internal.h +++ b/crypto/nss_util_internal.h @@ -7,7 +7,14 @@ #include <secmodt.h> +#include "base/callback.h" +#include "base/compiler_specific.h" #include "crypto/crypto_export.h" +#include "crypto/scoped_nss_types.h" + +namespace base { +class FilePath; +} // These functions return a type defined in an NSS header, and so cannot be // declared in nss_util.h. Hence, they are declared here. @@ -17,14 +24,14 @@ namespace crypto { // Returns a reference to the default NSS key slot for storing // public-key data only (e.g. server certs). Caller must release // returned reference with PK11_FreeSlot. -CRYPTO_EXPORT PK11SlotInfo* GetPublicNSSKeySlot(); +CRYPTO_EXPORT PK11SlotInfo* GetPublicNSSKeySlot() WARN_UNUSED_RESULT; // Returns a reference to the default slot for storing private-key and // mixed private-key/public-key data. Returns a hardware (TPM) NSS // key slot if on ChromeOS and EnableTPMForNSS() has been called // successfully. Caller must release returned reference with // PK11_FreeSlot. -CRYPTO_EXPORT PK11SlotInfo* GetPrivateNSSKeySlot(); +CRYPTO_EXPORT PK11SlotInfo* GetPrivateNSSKeySlot() WARN_UNUSED_RESULT; // A helper class that acquires the SECMOD list read lock while the // AutoSECMODListReadLock is in scope. @@ -38,6 +45,38 @@ class AutoSECMODListReadLock { DISALLOW_COPY_AND_ASSIGN(AutoSECMODListReadLock); }; +#if defined(OS_CHROMEOS) +// Prepare per-user NSS slot mapping. It is safe to call this function multiple +// times. Returns true if the user was added, or false if it already existed. +CRYPTO_EXPORT bool InitializeNSSForChromeOSUser( + const std::string& email, + const std::string& username_hash, + bool is_primary_user, + const base::FilePath& path) WARN_UNUSED_RESULT; + +// Use TPM slot |slot_id| for user. InitializeNSSForChromeOSUser must have been +// called first. +CRYPTO_EXPORT void InitializeTPMForChromeOSUser( + const std::string& username_hash, + CK_SLOT_ID slot_id); + +// Use the software slot as the private slot for user. +// InitializeNSSForChromeOSUser must have been called first. +CRYPTO_EXPORT void InitializePrivateSoftwareSlotForChromeOSUser( + const std::string& username_hash); + +// Returns a reference to the public slot for user. +CRYPTO_EXPORT ScopedPK11Slot GetPublicSlotForChromeOSUser( + const std::string& username_hash) WARN_UNUSED_RESULT; + +// Returns the private slot for |username_hash| if it is loaded. If it is not +// loaded and |callback| is non-null, the |callback| will be run once the slot +// is loaded. +CRYPTO_EXPORT ScopedPK11Slot GetPrivateSlotForChromeOSUser( + const std::string& username_hash, + const base::Callback<void(ScopedPK11Slot)>& callback) WARN_UNUSED_RESULT; +#endif // defined(OS_CHROMEOS) + } // namespace crypto #endif // CRYPTO_NSS_UTIL_INTERNAL_H_ |