diff options
author | mattm@google.com <mattm@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-12-06 22:24:07 +0000 |
---|---|---|
committer | mattm@google.com <mattm@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-12-06 22:24:07 +0000 |
commit | 557737f70d0c097b2f1a78b4acb552dad725ab61 (patch) | |
tree | 03a8a5c14f6503eda33e4d28269e3857281ecf59 /crypto | |
parent | 637fc4cd2748b8679a9b4a4a4c518c02b1afa699 (diff) | |
download | chromium_src-557737f70d0c097b2f1a78b4acb552dad725ab61.zip chromium_src-557737f70d0c097b2f1a78b4acb552dad725ab61.tar.gz chromium_src-557737f70d0c097b2f1a78b4acb552dad725ab61.tar.bz2 |
Initialize per-ChromeOS-user NSS slots and provide the functions to access them.
BUG=302124
R=mmenke@chromium.org, rsleevi@chromium.org, xiyuan@chromium.org
Review URL: https://codereview.chromium.org/53763003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@239266 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'crypto')
-rw-r--r-- | crypto/nss_util.cc | 236 | ||||
-rw-r--r-- | crypto/nss_util.h | 11 | ||||
-rw-r--r-- | crypto/nss_util_internal.h | 43 |
3 files changed, 278 insertions, 12 deletions
diff --git a/crypto/nss_util.cc b/crypto/nss_util.cc index 82556b4..5a33b6c 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) { diff --git a/crypto/nss_util.h b/crypto/nss_util.h index efc8140..4d3d3e2 100644 --- a/crypto/nss_util.h +++ b/crypto/nss_util.h @@ -7,6 +7,8 @@ #include <string> #include "base/basictypes.h" +#include "base/callback_forward.h" +#include "base/compiler_specific.h" #include "crypto/crypto_export.h" namespace base { @@ -102,11 +104,18 @@ CRYPTO_EXPORT void OpenPersistentNSSDB(); // GetPrivateNSSKeySlot() will return the TPM slot if one was found. CRYPTO_EXPORT void EnableTPMTokenForNSS(); +// Returns true if EnableTPMTokenForNSS has been called. +CRYPTO_EXPORT bool IsTPMTokenEnabledForNSS(); + // Returns true if the TPM is owned and PKCS#11 initialized with the // user and security officer PINs, and has been enabled in NSS by // calling EnableTPMForNSS, and Chaps has been successfully // loaded into NSS. -CRYPTO_EXPORT bool IsTPMTokenReady(); +// If |callback| is non-null and the function returns false, the |callback| will +// be run once the TPM is ready. |callback| will never be run if the function +// returns true. +CRYPTO_EXPORT bool IsTPMTokenReady(const base::Closure& callback) + WARN_UNUSED_RESULT; // Initialize the TPM token. Does nothing if it is already initialized. CRYPTO_EXPORT bool InitializeTPMToken(int token_slot_id); diff --git a/crypto/nss_util_internal.h b/crypto/nss_util_internal.h index 056ec28..fb76bd8 100644 --- a/crypto/nss_util_internal.h +++ b/crypto/nss_util_internal.h @@ -7,7 +7,14 @@ #include <secmodt.h> +#include "base/callback.h" +#include "base/compiler_specific.h" #include "crypto/crypto_export.h" +#include "crypto/scoped_nss_types.h" + +namespace base { +class FilePath; +} // These functions return a type defined in an NSS header, and so cannot be // declared in nss_util.h. Hence, they are declared here. @@ -17,14 +24,14 @@ namespace crypto { // Returns a reference to the default NSS key slot for storing // public-key data only (e.g. server certs). Caller must release // returned reference with PK11_FreeSlot. -CRYPTO_EXPORT PK11SlotInfo* GetPublicNSSKeySlot(); +CRYPTO_EXPORT PK11SlotInfo* GetPublicNSSKeySlot() WARN_UNUSED_RESULT; // Returns a reference to the default slot for storing private-key and // mixed private-key/public-key data. Returns a hardware (TPM) NSS // key slot if on ChromeOS and EnableTPMForNSS() has been called // successfully. Caller must release returned reference with // PK11_FreeSlot. -CRYPTO_EXPORT PK11SlotInfo* GetPrivateNSSKeySlot(); +CRYPTO_EXPORT PK11SlotInfo* GetPrivateNSSKeySlot() WARN_UNUSED_RESULT; // A helper class that acquires the SECMOD list read lock while the // AutoSECMODListReadLock is in scope. @@ -38,6 +45,38 @@ class AutoSECMODListReadLock { DISALLOW_COPY_AND_ASSIGN(AutoSECMODListReadLock); }; +#if defined(OS_CHROMEOS) +// Prepare per-user NSS slot mapping. It is safe to call this function multiple +// times. Returns true if the user was added, or false if it already existed. +CRYPTO_EXPORT bool InitializeNSSForChromeOSUser( + const std::string& email, + const std::string& username_hash, + bool is_primary_user, + const base::FilePath& path) WARN_UNUSED_RESULT; + +// Use TPM slot |slot_id| for user. InitializeNSSForChromeOSUser must have been +// called first. +CRYPTO_EXPORT void InitializeTPMForChromeOSUser( + const std::string& username_hash, + CK_SLOT_ID slot_id); + +// Use the software slot as the private slot for user. +// InitializeNSSForChromeOSUser must have been called first. +CRYPTO_EXPORT void InitializePrivateSoftwareSlotForChromeOSUser( + const std::string& username_hash); + +// Returns a reference to the public slot for user. +CRYPTO_EXPORT ScopedPK11Slot GetPublicSlotForChromeOSUser( + const std::string& username_hash) WARN_UNUSED_RESULT; + +// Returns the private slot for |username_hash| if it is loaded. If it is not +// loaded and |callback| is non-null, the |callback| will be run once the slot +// is loaded. +CRYPTO_EXPORT ScopedPK11Slot GetPrivateSlotForChromeOSUser( + const std::string& username_hash, + const base::Callback<void(ScopedPK11Slot)>& callback) WARN_UNUSED_RESULT; +#endif // defined(OS_CHROMEOS) + } // namespace crypto #endif // CRYPTO_NSS_UTIL_INTERNAL_H_ |