Chromium Code Reviews| Index: crypto/nss_util.cc |
| diff --git a/crypto/nss_util.cc b/crypto/nss_util.cc |
| index 4d4a8f7976f600122839bf02fa0248f65e5d52af..2adc21e7f90962f5f4caa784aaa3150efb0d9f78 100644 |
| --- a/crypto/nss_util.cc |
| +++ b/crypto/nss_util.cc |
| @@ -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 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<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 @@ 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 = |
| + GetTPMSlotForId(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 @@ 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* GetTPMSlotForId(SECMODModule* chaps_module, |
|
Ryan Sleevi
2014/02/27 04:22:32
Should you rename this method to clearly indicate
mattm
2014/02/27 05:32:37
Done.
|
| + 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,10 +481,40 @@ 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<TPMModuleAndSlot> tpm_args(new TPMModuleAndSlot(chaps_module_)); |
| + TPMModuleAndSlot* tpm_args_ptr = tpm_args.get(); |
| + base::WorkerPool::PostTaskAndReply( |
| + FROM_HERE, |
| + base::Bind(&NSSInitSingleton::GetTPMSlotForIdOnWorkerThread, |
| + 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 */ |
| + ); |
| } |
| + static void GetTPMSlotForIdOnWorkerThread(CK_SLOT_ID token_slot_id, |
| + TPMModuleAndSlot* tpm_args) { |
| + tpm_args->tpm_slot = GetTPMSlotForId(tpm_args->chaps_module, token_slot_id); |
| + } |
| + |
| + 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 +670,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 +835,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 +899,7 @@ class NSSInitSingleton { |
| 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 +1087,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<void(bool)>& callback) { |
| + g_nss_singleton.Get().InitializeTPMToken(token_slot_id, callback); |
| } |
| ScopedTestNSSChromeOSUser::ScopedTestNSSChromeOSUser( |