From e2ea5ca6a89c308af53c5ee6d5d1f61ccb11faf8 Mon Sep 17 00:00:00 2001 From: "mattm@chromium.org" Date: Thu, 27 Feb 2014 22:27:21 +0000 Subject: chromeos: Load chaps module and lookup TPM slots on the worker pool. BUG=345713 Review URL: https://codereview.chromium.org/181053002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@253942 0039d316-1c4b-4281-b951-d872f2087c98 --- crypto/nss_util.cc | 155 ++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 117 insertions(+), 38 deletions(-) (limited to 'crypto/nss_util.cc') diff --git a/crypto/nss_util.cc b/crypto/nss_util.cc index 4d4a8f7..8e7e9ac 100644 --- a/crypto/nss_util.cc +++ b/crypto/nss_util.cc @@ -22,7 +22,6 @@ #include #include "base/bind.h" -#include "base/callback.h" #include "base/cpu.h" #include "base/debug/alias.h" #include "base/debug/stack_trace.h" @@ -39,6 +38,7 @@ #include "base/strings/stringprintf.h" #include "base/threading/thread_checker.h" #include "base/threading/thread_restrictions.h" +#include "base/threading/worker_pool.h" #include "build/build_config.h" // USE_NSS means we use NSS for everything crypto-related. If USE_NSS is not @@ -265,6 +265,14 @@ class ChromeOSUserData { class NSSInitSingleton { public: #if defined(OS_CHROMEOS) + // Used with PostTaskAndReply to pass handles to worker thread and back. + struct TPMModuleAndSlot { + explicit TPMModuleAndSlot(SECMODModule* init_chaps_module) + : chaps_module(init_chaps_module), tpm_slot(NULL) {} + SECMODModule* chaps_module; + PK11SlotInfo* tpm_slot; + }; + void OpenPersistentNSSDB() { DCHECK(thread_checker_.CalledOnValidThread()); @@ -312,21 +320,57 @@ class NSSInitSingleton { return tpm_token_enabled_for_nss_; } - bool InitializeTPMToken(int token_slot_id) { + void InitializeTPMToken(int token_slot_id, + const base::Callback& callback) { DCHECK(thread_checker_.CalledOnValidThread()); - + // Should not be called while there is already an initialization in + // progress. + DCHECK(!initializing_tpm_token_); // If EnableTPMTokenForNSS hasn't been called, return false. - if (!tpm_token_enabled_for_nss_) - return false; + if (!tpm_token_enabled_for_nss_) { + base::MessageLoop::current()->PostTask(FROM_HERE, + base::Bind(callback, false)); + return; + } // If everything is already initialized, then return true. - if (chaps_module_ && tpm_slot_) - return true; + // Note that only |tpm_slot_| is checked, since |chaps_module_| could be + // NULL in tests while |tpm_slot_| has been set to the test DB. + if (tpm_slot_) { + base::MessageLoop::current()->PostTask(FROM_HERE, + base::Bind(callback, true)); + return; + } + // Note that a reference is not taken to chaps_module_. This is safe since + // NSSInitSingleton is Leaky, so the reference it holds is never released. + scoped_ptr tpm_args(new TPMModuleAndSlot(chaps_module_)); + TPMModuleAndSlot* tpm_args_ptr = tpm_args.get(); + if (base::WorkerPool::PostTaskAndReply( + FROM_HERE, + base::Bind(&NSSInitSingleton::InitializeTPMTokenOnWorkerThread, + token_slot_id, + tpm_args_ptr), + base::Bind(&NSSInitSingleton::OnInitializedTPMToken, + base::Unretained(this), // NSSInitSingleton is leaky + callback, + base::Passed(&tpm_args)), + true /* task_is_slow */ + )) { + initializing_tpm_token_ = true; + } else { + base::MessageLoop::current()->PostTask(FROM_HERE, + base::Bind(callback, false)); + } + } + + static void InitializeTPMTokenOnWorkerThread(CK_SLOT_ID token_slot_id, + TPMModuleAndSlot* tpm_args) { // This tries to load the Chaps module so NSS can talk to the hardware // TPM. - if (!chaps_module_) { - chaps_module_ = LoadModule( + if (!tpm_args->chaps_module) { + DVLOG(3) << "Loading chaps..."; + tpm_args->chaps_module = LoadModule( kChapsModuleName, kChapsPath, // For more details on these parameters, see: @@ -335,31 +379,39 @@ class NSSInitSingleton { // read from this slot without requiring a call to C_Login. // askpw=only -- Only authenticate to the token when necessary. "NSS=\"slotParams=(0={slotFlags=[PublicCerts] askpw=only})\""); - if (!chaps_module_ && test_slot_) { - // chromeos_unittests try to test the TPM initialization process. If we - // have a test DB open, pretend that it is the TPM slot. - tpm_slot_ = PK11_ReferenceSlot(test_slot_); - return true; - } } - if (chaps_module_){ - tpm_slot_ = GetTPMSlotForId(token_slot_id); + if (tpm_args->chaps_module) { + tpm_args->tpm_slot = + GetTPMSlotForIdOnWorkerThread(tpm_args->chaps_module, token_slot_id); + } + } - if (!tpm_slot_) - return false; + void OnInitializedTPMToken(const base::Callback& callback, + scoped_ptr tpm_args) { + DCHECK(thread_checker_.CalledOnValidThread()); + DVLOG(2) << "Loaded chaps: " << !!tpm_args->chaps_module + << ", got tpm slot: " << !!tpm_args->tpm_slot; + + chaps_module_ = tpm_args->chaps_module; + tpm_slot_ = tpm_args->tpm_slot; + if (!chaps_module_ && test_slot_) { + // chromeos_unittests try to test the TPM initialization process. If we + // have a test DB open, pretend that it is the TPM slot. + tpm_slot_ = PK11_ReferenceSlot(test_slot_); + } + initializing_tpm_token_ = false; + if (tpm_slot_) { TPMReadyCallbackList callback_list; callback_list.swap(tpm_ready_callback_list_); - for (TPMReadyCallbackList::iterator i = - callback_list.begin(); + for (TPMReadyCallbackList::iterator i = callback_list.begin(); i != callback_list.end(); ++i) { (*i).Run(); } - - return true; } - return false; + + callback.Run(!!tpm_slot_); } bool IsTPMTokenReady(const base::Closure& callback) { @@ -386,18 +438,16 @@ class NSSInitSingleton { // Note that CK_SLOT_ID is an unsigned long, but cryptohome gives us the slot // id as an int. This should be safe since this is only used with chaps, which // we also control. - PK11SlotInfo* GetTPMSlotForId(CK_SLOT_ID slot_id) { - DCHECK(thread_checker_.CalledOnValidThread()); - - if (!chaps_module_) - return NULL; + static PK11SlotInfo* GetTPMSlotForIdOnWorkerThread(SECMODModule* chaps_module, + CK_SLOT_ID slot_id) { + DCHECK(chaps_module); DVLOG(3) << "Poking chaps module."; - SECStatus rv = SECMOD_UpdateSlotList(chaps_module_); + SECStatus rv = SECMOD_UpdateSlotList(chaps_module); if (rv != SECSuccess) PLOG(ERROR) << "SECMOD_UpdateSlotList failed: " << PORT_GetError(); - PK11SlotInfo* slot = SECMOD_LookupSlot(chaps_module_->moduleID, slot_id); + PK11SlotInfo* slot = SECMOD_LookupSlot(chaps_module->moduleID, slot_id); if (!slot) LOG(ERROR) << "TPM slot " << slot_id << " not found."; return slot; @@ -431,8 +481,34 @@ class NSSInitSingleton { 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))); + + if (!chaps_module_) + return; + + // Note that a reference is not taken to chaps_module_. This is safe since + // NSSInitSingleton is Leaky, so the reference it holds is never released. + scoped_ptr tpm_args(new TPMModuleAndSlot(chaps_module_)); + TPMModuleAndSlot* tpm_args_ptr = tpm_args.get(); + base::WorkerPool::PostTaskAndReply( + FROM_HERE, + base::Bind(&NSSInitSingleton::InitializeTPMTokenOnWorkerThread, + slot_id, + tpm_args_ptr), + base::Bind(&NSSInitSingleton::OnInitializedTPMForChromeOSUser, + base::Unretained(this), // NSSInitSingleton is leaky + username_hash, + base::Passed(&tpm_args)), + true /* task_is_slow */ + ); + } + + void OnInitializedTPMForChromeOSUser(const std::string& username_hash, + scoped_ptr tpm_args) { + DCHECK(thread_checker_.CalledOnValidThread()); + DVLOG(2) << "Got tpm slot for " << username_hash << " " + << !!tpm_args->tpm_slot; + chromeos_user_map_[username_hash]->SetPrivateSlot( + ScopedPK11Slot(tpm_args->tpm_slot)); } void InitializePrivateSoftwareSlotForChromeOSUser( @@ -590,6 +666,7 @@ class NSSInitSingleton { NSSInitSingleton() : tpm_token_enabled_for_nss_(false), + initializing_tpm_token_(false), chaps_module_(NULL), software_slot_(NULL), test_slot_(NULL), @@ -754,9 +831,9 @@ class NSSInitSingleton { } // Load the given module for this NSS session. - SECMODModule* LoadModule(const char* name, - const char* library_path, - const char* params) { + static SECMODModule* LoadModule(const char* name, + const char* library_path, + const char* params) { std::string modparams = base::StringPrintf( "name=\"%s\" library=\"%s\" %s", name, library_path, params ? params : ""); @@ -818,6 +895,7 @@ class NSSInitSingleton { static bool force_nodb_init_; bool tpm_token_enabled_for_nss_; + bool initializing_tpm_token_; typedef std::vector TPMReadyCallbackList; TPMReadyCallbackList tpm_ready_callback_list_; SECMODModule* chaps_module_; @@ -1005,8 +1083,9 @@ 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); +void InitializeTPMToken(int token_slot_id, + const base::Callback& callback) { + g_nss_singleton.Get().InitializeTPMToken(token_slot_id, callback); } ScopedTestNSSChromeOSUser::ScopedTestNSSChromeOSUser( -- cgit v1.1