Index: crypto/nss_util.cc |
diff --git a/crypto/nss_util.cc b/crypto/nss_util.cc |
index 82556b44128093b5eeebb68d498f94a6de6309db..5a33b6c3fb9f6bca7fd690561518b01f91303083 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) { |