| 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(
|
|
|