1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "crypto/nss_util.h"
20 #include "base/base_paths.h"
21 #include "base/bind.h"
22 #include "base/debug/alias.h"
23 #include "base/debug/stack_trace.h"
24 #include "base/files/file_path.h"
25 #include "base/files/file_util.h"
26 #include "base/lazy_instance.h"
27 #include "base/location.h"
28 #include "base/logging.h"
29 #include "base/memory/ptr_util.h"
30 #include "base/path_service.h"
31 #include "base/strings/stringprintf.h"
32 #include "base/task/post_task.h"
33 #include "base/threading/scoped_blocking_call.h"
34 #include "base/threading/thread_checker.h"
35 #include "base/threading/thread_restrictions.h"
36 #include "base/threading/thread_task_runner_handle.h"
37 #include "build/build_config.h"
38 #include "crypto/nss_crypto_module_delegate.h"
39 #include "crypto/nss_util_internal.h"
41 #if defined(OS_CHROMEOS)
49 #if defined(OS_CHROMEOS)
50 const char kUserNSSDatabaseName[] = "UserNSSDB";
52 // Constants for loading the Chrome OS TPM-backed PKCS #11 library.
53 const char kChapsModuleName[] = "Chaps";
54 const char kChapsPath[] = "libchaps.so";
56 // Fake certificate authority database used for testing.
57 static const base::FilePath::CharType kReadOnlyCertDB[] =
58 FILE_PATH_LITERAL("/etc/fake_root_ca/nssdb");
59 #endif // defined(OS_CHROMEOS)
61 std::string GetNSSErrorMessage() {
63 if (PR_GetErrorTextLength()) {
64 std::unique_ptr<char[]> error_text(new char[PR_GetErrorTextLength() + 1]);
65 PRInt32 copied = PR_GetErrorText(error_text.get());
66 result = std::string(error_text.get(), copied);
68 result = base::StringPrintf("NSS error code: %d", PR_GetError());
73 #if !defined(OS_CHROMEOS)
74 base::FilePath GetDefaultConfigDirectory() {
76 base::PathService::Get(base::DIR_HOME, &dir);
78 LOG(ERROR) << "Failed to get home directory.";
81 dir = dir.AppendASCII(".pki").AppendASCII("nssdb");
82 if (!base::CreateDirectory(dir)) {
83 LOG(ERROR) << "Failed to create " << dir.value() << " directory.";
86 DVLOG(2) << "DefaultConfigDirectory: " << dir.value();
89 #endif // !defined(OS_CHROMEOS)
91 // On non-Chrome OS platforms, return the default config directory. On Chrome OS
92 // test images, return a read-only directory with fake root CA certs (which are
93 // used by the local Google Accounts server mock we use when testing our login
94 // code). On Chrome OS non-test images (where the read-only directory doesn't
95 // exist), return an empty path.
96 base::FilePath GetInitialConfigDirectory() {
97 #if defined(OS_CHROMEOS)
98 base::FilePath database_dir = base::FilePath(kReadOnlyCertDB);
99 if (!base::PathExists(database_dir))
100 database_dir.clear();
103 return GetDefaultConfigDirectory();
104 #endif // defined(OS_CHROMEOS)
107 // This callback for NSS forwards all requests to a caller-specified
108 // CryptoModuleBlockingPasswordDelegate object.
109 char* PKCS11PasswordFunc(PK11SlotInfo* slot, PRBool retry, void* arg) {
110 crypto::CryptoModuleBlockingPasswordDelegate* delegate =
111 reinterpret_cast<crypto::CryptoModuleBlockingPasswordDelegate*>(arg);
113 bool cancelled = false;
114 std::string password = delegate->RequestPassword(PK11_GetTokenName(slot),
119 char* result = PORT_Strdup(password.c_str());
120 password.replace(0, password.size(), password.size(), 0);
123 DLOG(ERROR) << "PK11 password requested with nullptr arg";
127 // A singleton to initialize/deinitialize NSPR.
128 // Separate from the NSS singleton because we initialize NSPR on the UI thread.
129 // Now that we're leaking the singleton, we could merge back with the NSS
131 class NSPRInitSingleton {
133 friend struct base::LazyInstanceTraitsBase<NSPRInitSingleton>;
135 NSPRInitSingleton() {
136 PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
139 // NOTE(willchan): We don't actually cleanup on destruction since we leak NSS
140 // to prevent non-joinable threads from using NSS after it's already been
142 ~NSPRInitSingleton() = delete;
145 base::LazyInstance<NSPRInitSingleton>::Leaky
146 g_nspr_singleton = LAZY_INSTANCE_INITIALIZER;
148 // Force a crash with error info on NSS_NoDB_Init failure.
149 void CrashOnNSSInitFailure() {
150 int nss_error = PR_GetError();
151 int os_error = PR_GetOSError();
152 base::debug::Alias(&nss_error);
153 base::debug::Alias(&os_error);
154 LOG(ERROR) << "Error initializing NSS without a persistent database: "
155 << GetNSSErrorMessage();
156 LOG(FATAL) << "nss_error=" << nss_error << ", os_error=" << os_error;
159 #if defined(OS_CHROMEOS)
160 class ChromeOSUserData {
162 explicit ChromeOSUserData(ScopedPK11Slot public_slot)
163 : public_slot_(std::move(public_slot)),
164 private_slot_initialization_started_(false) {}
165 ~ChromeOSUserData() {
167 SECStatus status = SECMOD_CloseUserDB(public_slot_.get());
168 if (status != SECSuccess)
169 PLOG(ERROR) << "SECMOD_CloseUserDB failed: " << PORT_GetError();
173 ScopedPK11Slot GetPublicSlot() {
174 return ScopedPK11Slot(public_slot_ ? PK11_ReferenceSlot(public_slot_.get())
178 ScopedPK11Slot GetPrivateSlot(
179 base::OnceCallback<void(ScopedPK11Slot)> callback) {
181 return ScopedPK11Slot(PK11_ReferenceSlot(private_slot_.get()));
182 if (!callback.is_null())
183 tpm_ready_callback_list_.push_back(std::move(callback));
184 return ScopedPK11Slot();
187 void SetPrivateSlot(ScopedPK11Slot private_slot) {
188 DCHECK(!private_slot_);
189 private_slot_ = std::move(private_slot);
191 SlotReadyCallbackList callback_list;
192 callback_list.swap(tpm_ready_callback_list_);
193 for (SlotReadyCallbackList::iterator i = callback_list.begin();
194 i != callback_list.end();
197 ScopedPK11Slot(PK11_ReferenceSlot(private_slot_.get())));
201 bool private_slot_initialization_started() const {
202 return private_slot_initialization_started_;
205 void set_private_slot_initialization_started() {
206 private_slot_initialization_started_ = true;
210 ScopedPK11Slot public_slot_;
211 ScopedPK11Slot private_slot_;
213 bool private_slot_initialization_started_;
215 typedef std::vector<base::OnceCallback<void(ScopedPK11Slot)>>
216 SlotReadyCallbackList;
217 SlotReadyCallbackList tpm_ready_callback_list_;
220 class ScopedChapsLoadFixup {
222 ScopedChapsLoadFixup();
223 ~ScopedChapsLoadFixup();
226 #if defined(COMPONENT_BUILD)
231 #if defined(COMPONENT_BUILD)
233 ScopedChapsLoadFixup::ScopedChapsLoadFixup() {
234 // HACK: libchaps links the system protobuf and there are symbol conflicts
235 // with the bundled copy. Load chaps with RTLD_DEEPBIND to workaround.
236 chaps_handle_ = dlopen(kChapsPath, RTLD_LOCAL | RTLD_NOW | RTLD_DEEPBIND);
239 ScopedChapsLoadFixup::~ScopedChapsLoadFixup() {
240 // LoadModule() will have taken a 2nd reference.
242 dlclose(chaps_handle_);
247 ScopedChapsLoadFixup::ScopedChapsLoadFixup() {}
248 ScopedChapsLoadFixup::~ScopedChapsLoadFixup() {}
250 #endif // defined(COMPONENT_BUILD)
251 #endif // defined(OS_CHROMEOS)
253 class NSSInitSingleton {
255 #if defined(OS_CHROMEOS)
256 // Used with PostTaskAndReply to pass handles to worker thread and back.
257 struct TPMModuleAndSlot {
258 explicit TPMModuleAndSlot(SECMODModule* init_chaps_module)
259 : chaps_module(init_chaps_module) {}
260 SECMODModule* chaps_module;
261 crypto::ScopedPK11Slot tpm_slot;
264 ScopedPK11Slot OpenPersistentNSSDBForPath(const std::string& db_name,
265 const base::FilePath& path) {
266 DCHECK(thread_checker_.CalledOnValidThread());
267 // NSS is allowed to do IO on the current thread since dispatching
268 // to a dedicated thread would still have the affect of blocking
269 // the current thread, due to NSS's internal locking requirements
270 base::ThreadRestrictions::ScopedAllowIO allow_io;
272 base::FilePath nssdb_path = path.AppendASCII(".pki").AppendASCII("nssdb");
273 if (!base::CreateDirectory(nssdb_path)) {
274 LOG(ERROR) << "Failed to create " << nssdb_path.value() << " directory.";
275 return ScopedPK11Slot();
277 return OpenSoftwareNSSDB(nssdb_path, db_name);
280 void EnableTPMTokenForNSS() {
281 DCHECK(thread_checker_.CalledOnValidThread());
283 // If this gets set, then we'll use the TPM for certs with
284 // private keys, otherwise we'll fall back to the software
286 tpm_token_enabled_for_nss_ = true;
289 bool IsTPMTokenEnabledForNSS() {
290 DCHECK(thread_checker_.CalledOnValidThread());
291 return tpm_token_enabled_for_nss_;
294 void InitializeTPMTokenAndSystemSlot(
296 base::OnceCallback<void(bool)> callback) {
297 DCHECK(thread_checker_.CalledOnValidThread());
298 // Should not be called while there is already an initialization in
300 DCHECK(!initializing_tpm_token_);
301 // If EnableTPMTokenForNSS hasn't been called, return false.
302 if (!tpm_token_enabled_for_nss_) {
303 base::ThreadTaskRunnerHandle::Get()->PostTask(
304 FROM_HERE, base::BindOnce(std::move(callback), false));
308 // If everything is already initialized, then return true.
309 // Note that only |tpm_slot_| is checked, since |chaps_module_| could be
310 // nullptr in tests while |tpm_slot_| has been set to the test DB.
312 base::ThreadTaskRunnerHandle::Get()->PostTask(
313 FROM_HERE, base::BindOnce(std::move(callback), true));
317 // Note that a reference is not taken to chaps_module_. This is safe since
318 // NSSInitSingleton is Leaky, so the reference it holds is never released.
319 std::unique_ptr<TPMModuleAndSlot> tpm_args(
320 new TPMModuleAndSlot(chaps_module_));
321 TPMModuleAndSlot* tpm_args_ptr = tpm_args.get();
322 base::PostTaskWithTraitsAndReply(
324 {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
325 base::BindOnce(&NSSInitSingleton::InitializeTPMTokenInThreadPool,
326 system_slot_id, tpm_args_ptr),
327 base::BindOnce(&NSSInitSingleton::OnInitializedTPMTokenAndSystemSlot,
328 base::Unretained(this), // NSSInitSingleton is leaky
329 std::move(callback), std::move(tpm_args)));
330 initializing_tpm_token_ = true;
333 static void InitializeTPMTokenInThreadPool(CK_SLOT_ID token_slot_id,
334 TPMModuleAndSlot* tpm_args) {
335 // NSS functions may reenter //net via extension hooks. If the reentered
336 // code needs to synchronously wait for a task to run but the thread pool in
337 // which that task must run doesn't have enough threads to schedule it, a
338 // deadlock occurs. To prevent that, the base::ScopedBlockingCall below
339 // increments the thread pool capacity for the duration of the TPM
341 base::ScopedBlockingCall scoped_blocking_call(
342 FROM_HERE, base::BlockingType::WILL_BLOCK);
344 if (!tpm_args->chaps_module) {
345 ScopedChapsLoadFixup chaps_loader;
347 DVLOG(3) << "Loading chaps...";
348 tpm_args->chaps_module = LoadModule(
351 // For more details on these parameters, see:
352 // https://developer.mozilla.org/en/PKCS11_Module_Specs
353 // slotFlags=[PublicCerts] -- Certificates and public keys can be
354 // read from this slot without requiring a call to C_Login.
355 // askpw=only -- Only authenticate to the token when necessary.
356 "NSS=\"slotParams=(0={slotFlags=[PublicCerts] askpw=only})\"");
358 if (tpm_args->chaps_module) {
360 GetTPMSlotForIdInThreadPool(tpm_args->chaps_module, token_slot_id);
364 void OnInitializedTPMTokenAndSystemSlot(
365 base::OnceCallback<void(bool)> callback,
366 std::unique_ptr<TPMModuleAndSlot> tpm_args) {
367 DCHECK(thread_checker_.CalledOnValidThread());
368 DVLOG(2) << "Loaded chaps: " << !!tpm_args->chaps_module
369 << ", got tpm slot: " << !!tpm_args->tpm_slot;
371 chaps_module_ = tpm_args->chaps_module;
372 tpm_slot_ = std::move(tpm_args->tpm_slot);
373 if (!chaps_module_ && test_system_slot_) {
374 // chromeos_unittests try to test the TPM initialization process. If we
375 // have a test DB open, pretend that it is the TPM slot.
376 tpm_slot_.reset(PK11_ReferenceSlot(test_system_slot_.get()));
378 initializing_tpm_token_ = false;
381 RunAndClearTPMReadyCallbackList();
383 std::move(callback).Run(!!tpm_slot_);
386 void RunAndClearTPMReadyCallbackList() {
387 TPMReadyCallbackList callback_list;
388 callback_list.swap(tpm_ready_callback_list_);
389 for (TPMReadyCallbackList::iterator i = callback_list.begin();
390 i != callback_list.end();
396 bool IsTPMTokenReady(base::OnceClosure callback) {
397 if (!callback.is_null()) {
398 // Cannot DCHECK in the general case yet, but since the callback is
399 // a new addition to the API, DCHECK to make sure at least the new uses
401 DCHECK(thread_checker_.CalledOnValidThread());
402 } else if (!thread_checker_.CalledOnValidThread()) {
403 // TODO(mattm): Change to DCHECK when callers have been fixed.
404 DVLOG(1) << "Called on wrong thread.\n"
405 << base::debug::StackTrace().ToString();
411 if (!callback.is_null())
412 tpm_ready_callback_list_.push_back(std::move(callback));
417 // Note that CK_SLOT_ID is an unsigned long, but cryptohome gives us the slot
418 // id as an int. This should be safe since this is only used with chaps, which
420 static crypto::ScopedPK11Slot GetTPMSlotForIdInThreadPool(
421 SECMODModule* chaps_module,
422 CK_SLOT_ID slot_id) {
423 DCHECK(chaps_module);
425 DVLOG(3) << "Poking chaps module.";
426 SECStatus rv = SECMOD_UpdateSlotList(chaps_module);
427 if (rv != SECSuccess)
428 PLOG(ERROR) << "SECMOD_UpdateSlotList failed: " << PORT_GetError();
430 PK11SlotInfo* slot = SECMOD_LookupSlot(chaps_module->moduleID, slot_id);
432 LOG(ERROR) << "TPM slot " << slot_id << " not found.";
433 return crypto::ScopedPK11Slot(slot);
436 bool InitializeNSSForChromeOSUser(const std::string& username_hash,
437 const base::FilePath& path) {
438 DCHECK(thread_checker_.CalledOnValidThread());
439 if (chromeos_user_map_.find(username_hash) != chromeos_user_map_.end()) {
440 // This user already exists in our mapping.
441 DVLOG(2) << username_hash << " already initialized.";
445 DVLOG(2) << "Opening NSS DB " << path.value();
446 std::string db_name = base::StringPrintf(
447 "%s %s", kUserNSSDatabaseName, username_hash.c_str());
448 ScopedPK11Slot public_slot(OpenPersistentNSSDBForPath(db_name, path));
449 chromeos_user_map_[username_hash] =
450 std::make_unique<ChromeOSUserData>(std::move(public_slot));
454 bool ShouldInitializeTPMForChromeOSUser(const std::string& username_hash) {
455 DCHECK(thread_checker_.CalledOnValidThread());
456 DCHECK(chromeos_user_map_.find(username_hash) != chromeos_user_map_.end());
458 return !chromeos_user_map_[username_hash]
459 ->private_slot_initialization_started();
462 void WillInitializeTPMForChromeOSUser(const std::string& username_hash) {
463 DCHECK(thread_checker_.CalledOnValidThread());
464 DCHECK(chromeos_user_map_.find(username_hash) != chromeos_user_map_.end());
466 chromeos_user_map_[username_hash]
467 ->set_private_slot_initialization_started();
470 void InitializeTPMForChromeOSUser(const std::string& username_hash,
471 CK_SLOT_ID slot_id) {
472 DCHECK(thread_checker_.CalledOnValidThread());
473 DCHECK(chromeos_user_map_.find(username_hash) != chromeos_user_map_.end());
474 DCHECK(chromeos_user_map_[username_hash]->
475 private_slot_initialization_started());
480 // Note that a reference is not taken to chaps_module_. This is safe since
481 // NSSInitSingleton is Leaky, so the reference it holds is never released.
482 std::unique_ptr<TPMModuleAndSlot> tpm_args(
483 new TPMModuleAndSlot(chaps_module_));
484 TPMModuleAndSlot* tpm_args_ptr = tpm_args.get();
485 base::PostTaskWithTraitsAndReply(
487 {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
488 base::BindOnce(&NSSInitSingleton::InitializeTPMTokenInThreadPool,
489 slot_id, tpm_args_ptr),
490 base::BindOnce(&NSSInitSingleton::OnInitializedTPMForChromeOSUser,
491 base::Unretained(this), // NSSInitSingleton is leaky
492 username_hash, std::move(tpm_args)));
495 void OnInitializedTPMForChromeOSUser(
496 const std::string& username_hash,
497 std::unique_ptr<TPMModuleAndSlot> tpm_args) {
498 DCHECK(thread_checker_.CalledOnValidThread());
499 DVLOG(2) << "Got tpm slot for " << username_hash << " "
500 << !!tpm_args->tpm_slot;
501 chromeos_user_map_[username_hash]->SetPrivateSlot(
502 std::move(tpm_args->tpm_slot));
505 void InitializePrivateSoftwareSlotForChromeOSUser(
506 const std::string& username_hash) {
507 DCHECK(thread_checker_.CalledOnValidThread());
508 VLOG(1) << "using software private slot for " << username_hash;
509 DCHECK(chromeos_user_map_.find(username_hash) != chromeos_user_map_.end());
510 DCHECK(chromeos_user_map_[username_hash]->
511 private_slot_initialization_started());
513 if (prepared_test_private_slot_) {
514 chromeos_user_map_[username_hash]->SetPrivateSlot(
515 std::move(prepared_test_private_slot_));
519 chromeos_user_map_[username_hash]->SetPrivateSlot(
520 chromeos_user_map_[username_hash]->GetPublicSlot());
523 ScopedPK11Slot GetPublicSlotForChromeOSUser(
524 const std::string& username_hash) {
525 DCHECK(thread_checker_.CalledOnValidThread());
527 if (username_hash.empty()) {
528 DVLOG(2) << "empty username_hash";
529 return ScopedPK11Slot();
532 if (chromeos_user_map_.find(username_hash) == chromeos_user_map_.end()) {
533 LOG(ERROR) << username_hash << " not initialized.";
534 return ScopedPK11Slot();
536 return chromeos_user_map_[username_hash]->GetPublicSlot();
539 ScopedPK11Slot GetPrivateSlotForChromeOSUser(
540 const std::string& username_hash,
541 base::OnceCallback<void(ScopedPK11Slot)> callback) {
542 DCHECK(thread_checker_.CalledOnValidThread());
544 if (username_hash.empty()) {
545 DVLOG(2) << "empty username_hash";
546 if (!callback.is_null()) {
547 base::ThreadTaskRunnerHandle::Get()->PostTask(
548 FROM_HERE, base::BindOnce(std::move(callback), ScopedPK11Slot()));
550 return ScopedPK11Slot();
553 DCHECK(chromeos_user_map_.find(username_hash) != chromeos_user_map_.end());
555 return chromeos_user_map_[username_hash]->GetPrivateSlot(
556 std::move(callback));
559 void CloseChromeOSUserForTesting(const std::string& username_hash) {
560 DCHECK(thread_checker_.CalledOnValidThread());
561 auto i = chromeos_user_map_.find(username_hash);
562 DCHECK(i != chromeos_user_map_.end());
563 chromeos_user_map_.erase(i);
566 void SetSystemKeySlotForTesting(ScopedPK11Slot slot) {
567 DCHECK(thread_checker_.CalledOnValidThread());
569 // Ensure that a previous value of test_system_slot_ is not overwritten.
570 // Unsetting, i.e. setting a nullptr, however is allowed.
571 DCHECK(!slot || !test_system_slot_);
572 test_system_slot_ = std::move(slot);
573 if (test_system_slot_) {
574 tpm_slot_.reset(PK11_ReferenceSlot(test_system_slot_.get()));
575 RunAndClearTPMReadyCallbackList();
581 void SetPrivateSoftwareSlotForChromeOSUserForTesting(ScopedPK11Slot slot) {
582 DCHECK(thread_checker_.CalledOnValidThread());
584 // Ensure that a previous value of prepared_test_private_slot_ is not
585 // overwritten. Unsetting, i.e. setting a nullptr, however is allowed.
586 DCHECK(!slot || !prepared_test_private_slot_);
587 prepared_test_private_slot_ = std::move(slot);
589 #endif // defined(OS_CHROMEOS)
591 #if !defined(OS_CHROMEOS)
592 PK11SlotInfo* GetPersistentNSSKeySlot() {
593 // TODO(mattm): Change to DCHECK when callers have been fixed.
594 if (!thread_checker_.CalledOnValidThread()) {
595 DVLOG(1) << "Called on wrong thread.\n"
596 << base::debug::StackTrace().ToString();
599 return PK11_GetInternalKeySlot();
603 #if defined(OS_CHROMEOS)
604 void GetSystemNSSKeySlotCallback(
605 base::OnceCallback<void(ScopedPK11Slot)> callback) {
606 std::move(callback).Run(
607 ScopedPK11Slot(PK11_ReferenceSlot(tpm_slot_.get())));
610 ScopedPK11Slot GetSystemNSSKeySlot(
611 base::OnceCallback<void(ScopedPK11Slot)> callback) {
612 DCHECK(thread_checker_.CalledOnValidThread());
613 // TODO(mattm): chromeos::TPMTokenloader always calls
614 // InitializeTPMTokenAndSystemSlot with slot 0. If the system slot is
615 // disabled, tpm_slot_ will be the first user's slot instead. Can that be
616 // detected and return nullptr instead?
618 base::OnceClosure wrapped_callback;
619 if (!callback.is_null()) {
620 wrapped_callback = base::BindOnce(
621 &NSSInitSingleton::GetSystemNSSKeySlotCallback,
622 base::Unretained(this) /* singleton is leaky */, std::move(callback));
624 if (IsTPMTokenReady(std::move(wrapped_callback)))
625 return ScopedPK11Slot(PK11_ReferenceSlot(tpm_slot_.get()));
626 return ScopedPK11Slot();
631 friend struct base::LazyInstanceTraitsBase<NSSInitSingleton>;
634 : tpm_token_enabled_for_nss_(false),
635 initializing_tpm_token_(false),
636 chaps_module_(nullptr),
638 // Initializing NSS causes us to do blocking IO.
639 // Temporarily allow it until we fix
640 // http://code.google.com/p/chromium/issues/detail?id=59847
641 base::ThreadRestrictions::ScopedAllowIO allow_io;
643 // It's safe to construct on any thread, since LazyInstance will prevent any
644 // other threads from accessing until the constructor is done.
645 thread_checker_.DetachFromThread();
649 // We *must* have NSS >= 3.26 at compile time.
650 static_assert((NSS_VMAJOR == 3 && NSS_VMINOR >= 26) || (NSS_VMAJOR > 3),
651 "nss version check failed");
652 // Also check the run-time NSS version.
653 // NSS_VersionCheck is a >= check, not strict equality.
654 if (!NSS_VersionCheck("3.26")) {
655 LOG(FATAL) << "NSS_VersionCheck(\"3.26\") failed. NSS >= 3.26 is "
656 "required. Please upgrade to the latest NSS, and if you "
657 "still get this error, contact your distribution "
661 SECStatus status = SECFailure;
662 base::FilePath database_dir = GetInitialConfigDirectory();
663 if (!database_dir.empty()) {
664 // Initialize with a persistent database (likely, ~/.pki/nssdb).
665 // Use "sql:" which can be shared by multiple processes safely.
666 std::string nss_config_dir =
667 base::StringPrintf("sql:%s", database_dir.value().c_str());
668 #if defined(OS_CHROMEOS)
669 status = NSS_Init(nss_config_dir.c_str());
671 status = NSS_InitReadWrite(nss_config_dir.c_str());
673 if (status != SECSuccess) {
674 LOG(ERROR) << "Error initializing NSS with a persistent "
675 "database (" << nss_config_dir
676 << "): " << GetNSSErrorMessage();
679 if (status != SECSuccess) {
680 VLOG(1) << "Initializing NSS without a persistent database.";
681 status = NSS_NoDB_Init(nullptr);
682 if (status != SECSuccess) {
683 CrashOnNSSInitFailure();
688 PK11_SetPasswordFunc(PKCS11PasswordFunc);
690 // If we haven't initialized the password for the NSS databases,
691 // initialize an empty-string password so that we don't need to
693 PK11SlotInfo* slot = PK11_GetInternalKeySlot();
695 // PK11_InitPin may write to the keyDB, but no other thread can use NSS
696 // yet, so we don't need to lock.
697 if (PK11_NeedUserInit(slot))
698 PK11_InitPin(slot, nullptr, nullptr);
702 root_ = InitDefaultRootCerts();
704 // Disable MD5 certificate signatures. (They are disabled by default in
706 NSS_SetAlgorithmPolicy(SEC_OID_MD5, 0, NSS_USE_ALG_IN_CERT_SIGNATURE);
707 NSS_SetAlgorithmPolicy(SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION,
708 0, NSS_USE_ALG_IN_CERT_SIGNATURE);
711 // NOTE(willchan): We don't actually cleanup on destruction since we leak NSS
712 // to prevent non-joinable threads from using NSS after it's already been
714 ~NSSInitSingleton() = delete;
716 // Load nss's built-in root certs.
717 SECMODModule* InitDefaultRootCerts() {
718 SECMODModule* root = LoadModule("Root Certs", "libnssckbi.so", nullptr);
722 // Aw, snap. Can't find/load root cert shared library.
723 // This will make it hard to talk to anybody via https.
724 // TODO(mattm): Re-add the NOTREACHED here when crbug.com/310972 is fixed.
728 // Load the given module for this NSS session.
729 static SECMODModule* LoadModule(const char* name,
730 const char* library_path,
731 const char* params) {
732 std::string modparams = base::StringPrintf(
733 "name=\"%s\" library=\"%s\" %s",
734 name, library_path, params ? params : "");
736 // Shouldn't need to const_cast here, but SECMOD doesn't properly
737 // declare input string arguments as const. Bug
738 // https://bugzilla.mozilla.org/show_bug.cgi?id=642546 was filed
739 // on NSS codebase to address this.
740 SECMODModule* module = SECMOD_LoadUserModule(
741 const_cast<char*>(modparams.c_str()), nullptr, PR_FALSE);
743 LOG(ERROR) << "Error loading " << name << " module into NSS: "
744 << GetNSSErrorMessage();
747 if (!module->loaded) {
748 LOG(ERROR) << "After loading " << name << ", loaded==false: "
749 << GetNSSErrorMessage();
750 SECMOD_DestroyModule(module);
756 bool tpm_token_enabled_for_nss_;
757 bool initializing_tpm_token_;
758 typedef std::vector<base::OnceClosure> TPMReadyCallbackList;
759 TPMReadyCallbackList tpm_ready_callback_list_;
760 SECMODModule* chaps_module_;
761 crypto::ScopedPK11Slot tpm_slot_;
763 #if defined(OS_CHROMEOS)
764 std::map<std::string, std::unique_ptr<ChromeOSUserData>> chromeos_user_map_;
765 ScopedPK11Slot test_system_slot_;
766 ScopedPK11Slot prepared_test_private_slot_;
769 base::ThreadChecker thread_checker_;
772 base::LazyInstance<NSSInitSingleton>::Leaky
773 g_nss_singleton = LAZY_INSTANCE_INITIALIZER;
776 ScopedPK11Slot OpenSoftwareNSSDB(const base::FilePath& path,
777 const std::string& description) {
778 const std::string modspec =
779 base::StringPrintf("configDir='sql:%s' tokenDescription='%s'",
780 path.value().c_str(),
781 description.c_str());
782 PK11SlotInfo* db_slot = SECMOD_OpenUserDB(modspec.c_str());
784 if (PK11_NeedUserInit(db_slot))
785 PK11_InitPin(db_slot, nullptr, nullptr);
787 LOG(ERROR) << "Error opening persistent database (" << modspec
788 << "): " << GetNSSErrorMessage();
790 return ScopedPK11Slot(db_slot);
793 void EnsureNSPRInit() {
794 g_nspr_singleton.Get();
797 void EnsureNSSInit() {
798 g_nss_singleton.Get();
801 bool CheckNSSVersion(const char* version) {
802 return !!NSS_VersionCheck(version);
805 AutoSECMODListReadLock::AutoSECMODListReadLock()
806 : lock_(SECMOD_GetDefaultModuleListLock()) {
807 SECMOD_GetReadLock(lock_);
810 AutoSECMODListReadLock::~AutoSECMODListReadLock() {
811 SECMOD_ReleaseReadLock(lock_);
814 #if defined(OS_CHROMEOS)
815 ScopedPK11Slot GetSystemNSSKeySlot(
816 base::OnceCallback<void(ScopedPK11Slot)> callback) {
817 return g_nss_singleton.Get().GetSystemNSSKeySlot(std::move(callback));
820 void SetSystemKeySlotForTesting(ScopedPK11Slot slot) {
821 g_nss_singleton.Get().SetSystemKeySlotForTesting(std::move(slot));
824 void EnableTPMTokenForNSS() {
825 g_nss_singleton.Get().EnableTPMTokenForNSS();
828 bool IsTPMTokenEnabledForNSS() {
829 return g_nss_singleton.Get().IsTPMTokenEnabledForNSS();
832 bool IsTPMTokenReady(base::OnceClosure callback) {
833 return g_nss_singleton.Get().IsTPMTokenReady(std::move(callback));
836 void InitializeTPMTokenAndSystemSlot(int token_slot_id,
837 base::OnceCallback<void(bool)> callback) {
838 g_nss_singleton.Get().InitializeTPMTokenAndSystemSlot(token_slot_id,
839 std::move(callback));
842 bool InitializeNSSForChromeOSUser(const std::string& username_hash,
843 const base::FilePath& path) {
844 return g_nss_singleton.Get().InitializeNSSForChromeOSUser(username_hash,
848 bool ShouldInitializeTPMForChromeOSUser(const std::string& username_hash) {
849 return g_nss_singleton.Get().ShouldInitializeTPMForChromeOSUser(
853 void WillInitializeTPMForChromeOSUser(const std::string& username_hash) {
854 g_nss_singleton.Get().WillInitializeTPMForChromeOSUser(username_hash);
857 void InitializeTPMForChromeOSUser(
858 const std::string& username_hash,
859 CK_SLOT_ID slot_id) {
860 g_nss_singleton.Get().InitializeTPMForChromeOSUser(username_hash, slot_id);
863 void InitializePrivateSoftwareSlotForChromeOSUser(
864 const std::string& username_hash) {
865 g_nss_singleton.Get().InitializePrivateSoftwareSlotForChromeOSUser(
869 ScopedPK11Slot GetPublicSlotForChromeOSUser(const std::string& username_hash) {
870 return g_nss_singleton.Get().GetPublicSlotForChromeOSUser(username_hash);
873 ScopedPK11Slot GetPrivateSlotForChromeOSUser(
874 const std::string& username_hash,
875 base::OnceCallback<void(ScopedPK11Slot)> callback) {
876 return g_nss_singleton.Get().GetPrivateSlotForChromeOSUser(
877 username_hash, std::move(callback));
880 void CloseChromeOSUserForTesting(const std::string& username_hash) {
881 g_nss_singleton.Get().CloseChromeOSUserForTesting(username_hash);
884 void SetPrivateSoftwareSlotForChromeOSUserForTesting(ScopedPK11Slot slot) {
885 g_nss_singleton.Get().SetPrivateSoftwareSlotForChromeOSUserForTesting(
888 #endif // defined(OS_CHROMEOS)
890 base::Time PRTimeToBaseTime(PRTime prtime) {
891 return base::Time::FromInternalValue(
892 prtime + base::Time::UnixEpoch().ToInternalValue());
895 PRTime BaseTimeToPRTime(base::Time time) {
896 return time.ToInternalValue() - base::Time::UnixEpoch().ToInternalValue();
899 #if !defined(OS_CHROMEOS)
900 PK11SlotInfo* GetPersistentNSSKeySlot() {
901 return g_nss_singleton.Get().GetPersistentNSSKeySlot();
905 } // namespace crypto