Index: crypto/nss_util.cc |
=================================================================== |
--- crypto/nss_util.cc (revision 257483) |
+++ crypto/nss_util.cc (working copy) |
@@ -22,7 +22,6 @@ |
#include <vector> |
#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 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 @@ |
return tpm_token_enabled_for_nss_; |
} |
- bool InitializeTPMToken(int token_slot_id) { |
+ void InitializeTPMToken(int token_slot_id, |
+ const base::Callback<void(bool)>& 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<TPMModuleAndSlot> 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 @@ |
// 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<void(bool)>& callback, |
+ scoped_ptr<TPMModuleAndSlot> 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 @@ |
// 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()); |
+ static PK11SlotInfo* GetTPMSlotForIdOnWorkerThread(SECMODModule* chaps_module, |
+ CK_SLOT_ID slot_id) { |
+ DCHECK(chaps_module); |
- if (!chaps_module_) |
- return NULL; |
- |
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,10 +481,36 @@ |
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<TPMModuleAndSlot> 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<TPMModuleAndSlot> 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( |
const std::string& username_hash) { |
DCHECK(thread_checker_.CalledOnValidThread()); |
@@ -590,6 +666,7 @@ |
NSSInitSingleton() |
: tpm_token_enabled_for_nss_(false), |
+ initializing_tpm_token_(false), |
chaps_module_(NULL), |
software_slot_(NULL), |
test_slot_(NULL), |
@@ -754,9 +831,9 @@ |
} |
// 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 @@ |
static bool force_nodb_init_; |
bool tpm_token_enabled_for_nss_; |
+ bool initializing_tpm_token_; |
typedef std::vector<base::Closure> TPMReadyCallbackList; |
TPMReadyCallbackList tpm_ready_callback_list_; |
SECMODModule* chaps_module_; |
@@ -1005,8 +1083,9 @@ |
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<void(bool)>& callback) { |
+ g_nss_singleton.Get().InitializeTPMToken(token_slot_id, callback); |
} |
ScopedTestNSSChromeOSUser::ScopedTestNSSChromeOSUser( |