summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/net/nss_context.h32
-rw-r--r--chrome/browser/net/nss_context_chromeos.cc29
-rw-r--r--chrome/browser/net/nss_context_linux.cc21
-rw-r--r--chrome/browser/profiles/profile_io_data.cc132
-rw-r--r--chrome/browser/profiles/profile_io_data.h11
-rw-r--r--chrome/browser/ui/webui/chromeos/cryptohome_web_ui_handler.cc2
-rw-r--r--chrome/browser/ui/webui/options/certificate_manager_browsertest.cc28
-rw-r--r--chrome/chrome_browser.gypi6
-rw-r--r--crypto/nss_util.cc236
-rw-r--r--crypto/nss_util.h11
-rw-r--r--crypto/nss_util_internal.h43
11 files changed, 537 insertions, 14 deletions
diff --git a/chrome/browser/net/nss_context.h b/chrome/browser/net/nss_context.h
new file mode 100644
index 0000000..0735df2
--- /dev/null
+++ b/chrome/browser/net/nss_context.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_NET_NSS_CONTEXT_H_
+#define CHROME_BROWSER_NET_NSS_CONTEXT_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "crypto/scoped_nss_types.h"
+
+namespace content {
+class ResourceContext;
+} // namespace content
+
+// Returns a reference to the public slot for the user associated with
+// |context|. Should be called only on the IO thread.
+crypto::ScopedPK11Slot GetPublicNSSKeySlotForResourceContext(
+ content::ResourceContext* context);
+
+// Returns a reference to the private slot for the user associated with
+// |context|, if it is loaded. If it is not loaded and |callback| is non-null,
+// the |callback| will be run once the slot is loaded.
+// Should be called only on the IO thread.
+crypto::ScopedPK11Slot GetPrivateNSSKeySlotForResourceContext(
+ content::ResourceContext* context,
+ const base::Callback<void(crypto::ScopedPK11Slot)>& callback)
+ WARN_UNUSED_RESULT;
+
+#endif // CHROME_BROWSER_NET_NSS_CONTEXT_H_
diff --git a/chrome/browser/net/nss_context_chromeos.cc b/chrome/browser/net/nss_context_chromeos.cc
new file mode 100644
index 0000000..b789f4e
--- /dev/null
+++ b/chrome/browser/net/nss_context_chromeos.cc
@@ -0,0 +1,29 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/net/nss_context.h"
+
+#include "chrome/browser/profiles/profile_io_data.h"
+#include "content/public/browser/browser_thread.h"
+#include "crypto/nss_util_internal.h"
+
+namespace {
+std::string GetUsername(content::ResourceContext* context) {
+ return ProfileIOData::FromResourceContext(context)->username_hash();
+}
+} // namespace
+
+crypto::ScopedPK11Slot GetPublicNSSKeySlotForResourceContext(
+ content::ResourceContext* context) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+ return crypto::GetPublicSlotForChromeOSUser(GetUsername(context));
+}
+
+crypto::ScopedPK11Slot GetPrivateNSSKeySlotForResourceContext(
+ content::ResourceContext* context,
+ const base::Callback<void(crypto::ScopedPK11Slot)>& callback) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+ return crypto::GetPrivateSlotForChromeOSUser(GetUsername(context), callback);
+}
+
diff --git a/chrome/browser/net/nss_context_linux.cc b/chrome/browser/net/nss_context_linux.cc
new file mode 100644
index 0000000..d737387
--- /dev/null
+++ b/chrome/browser/net/nss_context_linux.cc
@@ -0,0 +1,21 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/net/nss_context.h"
+
+#include "content/public/browser/browser_thread.h"
+#include "crypto/nss_util_internal.h"
+
+crypto::ScopedPK11Slot GetPublicNSSKeySlotForResourceContext(
+ content::ResourceContext* context) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+ return crypto::ScopedPK11Slot(crypto::GetPublicNSSKeySlot());
+}
+
+crypto::ScopedPK11Slot GetPrivateNSSKeySlotForResourceContext(
+ content::ResourceContext* context,
+ const base::Callback<void(crypto::ScopedPK11Slot)>& callback) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+ return crypto::ScopedPK11Slot(crypto::GetPrivateNSSKeySlot());
+}
diff --git a/chrome/browser/profiles/profile_io_data.cc b/chrome/browser/profiles/profile_io_data.cc
index 46129e6..e50779c 100644
--- a/chrome/browser/profiles/profile_io_data.cc
+++ b/chrome/browser/profiles/profile_io_data.cc
@@ -89,11 +89,17 @@
#if defined(OS_CHROMEOS)
#include "chrome/browser/chromeos/drive/drive_protocol_handler.h"
+#include "chrome/browser/chromeos/login/user.h"
+#include "chrome/browser/chromeos/login/user_manager.h"
#include "chrome/browser/chromeos/policy/policy_cert_service.h"
#include "chrome/browser/chromeos/policy/policy_cert_service_factory.h"
#include "chrome/browser/chromeos/policy/policy_cert_verifier.h"
#include "chrome/browser/chromeos/settings/cros_settings.h"
+#include "chromeos/dbus/cryptohome_client.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/settings/cros_settings_names.h"
+#include "crypto/nss_util.h"
+#include "crypto/nss_util_internal.h"
#endif // defined(OS_CHROMEOS)
#if defined(USE_NSS)
@@ -228,6 +234,112 @@ class DebugDevToolsInterceptor
};
#endif // defined(DEBUG_DEVTOOLS)
+#if defined(OS_CHROMEOS)
+// The following four functions are responsible for initializing NSS for each
+// profile on ChromeOS, which has a separate NSS database and TPM slot
+// per-profile.
+//
+// Initialization basically follows these steps:
+// 1) Get some info from chromeos::UserManager about the User for this profile.
+// 2) Tell nss_util to initialize the software slot for this profile.
+// 3) Wait for the TPM module to be loaded by nss_util if it isn't already.
+// 4) Ask CryptohomeClient which TPM slot id corresponds to this profile.
+// 5) Tell nss_util to use that slot id on the TPM module.
+//
+// Some of these steps must happen on the UI thread, others must happen on the
+// IO thread:
+// UI thread IO Thread
+//
+// ProfileIOData::InitializeOnUIThread
+// |
+// chromeos::UserManager::GetUserByProfile
+// \---------------------------------------v
+// StartNSSInitOnIOThread
+// |
+// crypto::InitializeNSSForChromeOSUser
+// |
+// crypto::IsTPMTokenReady
+// |
+// StartTPMSlotInitializationOnIOThread
+// v---------------------------------------/
+// GetTPMInfoForUserOnUIThread
+// |
+// CryptohomeClient::Pkcs11GetTpmTokenInfoForUser
+// |
+// DidGetTPMInfoForUserOnUIThread
+// \---------------------------------------v
+// crypto::InitializeTPMForChromeOSUser
+
+void DidGetTPMInfoForUserOnUIThread(const std::string& username_hash,
+ chromeos::DBusMethodCallStatus call_status,
+ const std::string& label,
+ const std::string& user_pin,
+ int slot_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ if (call_status == chromeos::DBUS_METHOD_CALL_FAILURE) {
+ NOTREACHED() << "dbus error getting TPM info for " << username_hash;
+ return;
+ }
+ DVLOG(1) << "Got TPM slot for " << username_hash << ": " << slot_id;
+ BrowserThread::PostTask(
+ BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(
+ &crypto::InitializeTPMForChromeOSUser, username_hash, slot_id));
+}
+
+void GetTPMInfoForUserOnUIThread(const std::string& username,
+ const std::string& username_hash) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DVLOG(1) << "Getting TPM info from cryptohome for "
+ << " " << username << " " << username_hash;
+ chromeos::DBusThreadManager::Get()
+ ->GetCryptohomeClient()
+ ->Pkcs11GetTpmTokenInfoForUser(
+ username,
+ base::Bind(&DidGetTPMInfoForUserOnUIThread, username_hash));
+}
+
+void StartTPMSlotInitializationOnIOThread(const std::string& username,
+ const std::string& username_hash) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&GetTPMInfoForUserOnUIThread, username, username_hash));
+}
+
+void StartNSSInitOnIOThread(const std::string& username,
+ const std::string& username_hash,
+ const base::FilePath& path,
+ bool is_primary_user) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DVLOG(1) << "Starting NSS init for " << username
+ << " hash:" << username_hash
+ << " is_primary_user:" << is_primary_user;
+
+ if (!crypto::InitializeNSSForChromeOSUser(
+ username, username_hash, is_primary_user, path)) {
+ // If the user already exists in nss_util's map, it is already initialized
+ // or in the process of being initialized. In either case, there's no need
+ // to do anything.
+ return;
+ }
+
+ if (crypto::IsTPMTokenEnabledForNSS()) {
+ if (crypto::IsTPMTokenReady(base::Bind(
+ &StartTPMSlotInitializationOnIOThread, username, username_hash))) {
+ StartTPMSlotInitializationOnIOThread(username, username_hash);
+ } else {
+ DVLOG(1) << "Waiting for tpm ready ...";
+ }
+ } else {
+ crypto::InitializePrivateSoftwareSlotForChromeOSUser(username_hash);
+ }
+}
+#endif // defined(OS_CHROMEOS)
+
} // namespace
void ProfileIOData::InitializeOnUIThread(Profile* profile) {
@@ -278,6 +390,25 @@ void ProfileIOData::InitializeOnUIThread(Profile* profile) {
params->managed_mode_url_filter =
managed_user_service->GetURLFilterForIOThread();
#endif
+#if defined(OS_CHROMEOS)
+ chromeos::UserManager* user_manager = chromeos::UserManager::Get();
+ if (user_manager) {
+ chromeos::User* user = user_manager->GetUserByProfile(profile);
+ if (user) {
+ params->username_hash = user->username_hash();
+ bool is_primary_user = (user_manager->GetPrimaryUser() == user);
+ BrowserThread::PostTask(BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&StartNSSInitOnIOThread,
+ user->email(),
+ user->username_hash(),
+ profile->GetPath(),
+ is_primary_user));
+ }
+ }
+ if (params->username_hash.empty())
+ LOG(WARNING) << "no username_hash";
+#endif
params->profile = profile;
profile_params_.reset(params.release());
@@ -833,6 +964,7 @@ void ProfileIOData::Init(content::ProtocolHandlerMap* protocol_handlers) const {
main_request_context_->set_cert_verifier(
io_thread_globals->cert_verifier.get());
}
+ username_hash_ = profile_params_->username_hash;
#else
main_request_context_->set_cert_verifier(
io_thread_globals->cert_verifier.get());
diff --git a/chrome/browser/profiles/profile_io_data.h b/chrome/browser/profiles/profile_io_data.h
index 136235d..d68adde 100644
--- a/chrome/browser/profiles/profile_io_data.h
+++ b/chrome/browser/profiles/profile_io_data.h
@@ -177,6 +177,12 @@ class ProfileIOData {
return transport_security_state_.get();
}
+#if defined(OS_CHROMEOS)
+ std::string username_hash() const {
+ return username_hash_;
+ }
+#endif
+
bool is_incognito() const {
return is_incognito_;
}
@@ -270,6 +276,10 @@ class ProfileIOData {
scoped_refptr<const ManagedModeURLFilter> managed_mode_url_filter;
#endif
+#if defined(OS_CHROMEOS)
+ std::string username_hash;
+#endif
+
// The profile this struct was populated from. It's passed as a void* to
// ensure it's not accidently used on the IO thread. Before using it on the
// UI thread, call ProfileManager::IsValidProfile to ensure it's alive.
@@ -494,6 +504,7 @@ class ProfileIOData {
http_server_properties_;
#if defined(OS_CHROMEOS)
mutable scoped_ptr<policy::PolicyCertVerifier> cert_verifier_;
+ mutable std::string username_hash_;
#endif
mutable scoped_ptr<net::TransportSecurityPersister>
diff --git a/chrome/browser/ui/webui/chromeos/cryptohome_web_ui_handler.cc b/chrome/browser/ui/webui/chromeos/cryptohome_web_ui_handler.cc
index b38e73e..9587dbf 100644
--- a/chrome/browser/ui/webui/chromeos/cryptohome_web_ui_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/cryptohome_web_ui_handler.cc
@@ -43,7 +43,7 @@ void CryptohomeWebUIHandler::OnPageLoaded(const base::ListValue* args) {
BrowserThread::PostTaskAndReplyWithResult(
BrowserThread::IO,
FROM_HERE,
- base::Bind(&crypto::IsTPMTokenReady),
+ base::Bind(&crypto::IsTPMTokenReady, base::Closure()),
base::Bind(&CryptohomeWebUIHandler::DidGetNSSUtilInfoOnUIThread,
weak_ptr_factory_.GetWeakPtr()));
}
diff --git a/chrome/browser/ui/webui/options/certificate_manager_browsertest.cc b/chrome/browser/ui/webui/options/certificate_manager_browsertest.cc
index 5faef24..d0fab46 100644
--- a/chrome/browser/ui/webui/options/certificate_manager_browsertest.cc
+++ b/chrome/browser/ui/webui/options/certificate_manager_browsertest.cc
@@ -46,10 +46,36 @@ class CertificateManagerBrowserTest : public options::OptionsUIBrowserTest {
policy::BrowserPolicyConnector::SetPolicyProviderForTesting(&provider_);
}
+ void SetUpOnIOThread() {
+#if defined(OS_CHROMEOS)
+ test_nssdb_.reset(new crypto::ScopedTestNSSDB());
+#endif
+ }
+
+ void TearDownOnIOThread() {
+#if defined(OS_CHROMEOS)
+ test_nssdb_.reset();
+#endif
+ }
+
virtual void SetUpOnMainThread() OVERRIDE {
+ content::BrowserThread::PostTask(
+ content::BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&CertificateManagerBrowserTest::SetUpOnIOThread, this));
+ content::RunAllPendingInMessageLoop(content::BrowserThread::IO);
+
content::RunAllPendingInMessageLoop();
}
+ virtual void CleanUpOnMainThread() OVERRIDE {
+ content::BrowserThread::PostTask(
+ content::BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&CertificateManagerBrowserTest::TearDownOnIOThread, this));
+ content::RunAllPendingInMessageLoop(content::BrowserThread::IO);
+ }
+
#if defined(OS_CHROMEOS)
void LoadONCPolicy(const std::string& filename) {
const std::string& user_policy_blob =
@@ -86,7 +112,7 @@ class CertificateManagerBrowserTest : public options::OptionsUIBrowserTest {
policy::MockConfigurationPolicyProvider provider_;
#if defined(OS_CHROMEOS)
policy::DevicePolicyCrosTestHelper device_policy_test_helper_;
- crypto::ScopedTestNSSDB test_nssdb_;
+ scoped_ptr<crypto::ScopedTestNSSDB> test_nssdb_;
#endif
};
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 23ae3e4..efaa554 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -1225,6 +1225,9 @@
'browser/net/net_pref_observer.h',
'browser/net/network_stats.cc',
'browser/net/network_stats.h',
+ 'browser/net/nss_context_chromeos.cc',
+ 'browser/net/nss_context_linux.cc',
+ 'browser/net/nss_context.h',
'browser/net/preconnect.cc',
'browser/net/preconnect.h',
'browser/net/predictor.cc',
@@ -3115,6 +3118,9 @@
'sources!': [
'browser/certificate_manager_model.cc',
'browser/certificate_manager_model.h',
+ 'browser/net/nss_context_chromeos.cc',
+ 'browser/net/nss_context_linux.cc',
+ 'browser/net/nss_context.h',
],
}],
['toolkit_uses_gtk == 1', {
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_