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. 4 5 #include "crypto/nss_util.h" 6 7 #include <nss.h> 8 #include <pk11pub.h> 9 #include <plarena.h> 10 #include <prerror.h> 11 #include <prinit.h> 12 #include <prtime.h> 13 #include <secmod.h> 14 15 #include <map> 16 #include <memory> 17 #include <utility> 18 #include <vector> 19 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_scheduler/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" 40 41 #if defined(OS_CHROMEOS) 42 #include <dlfcn.h> 43 #endif 44 45 namespace crypto { 46 47 namespace { 48 49 #if defined(OS_CHROMEOS) 50 const char kUserNSSDatabaseName[] = "UserNSSDB"; 51 52 // Constants for loading the Chrome OS TPM-backed PKCS #11 library. 53 const char kChapsModuleName[] = "Chaps"; 54 const char kChapsPath[] = "libchaps.so"; 55 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) 60 61 std::string GetNSSErrorMessage() { 62 std::string result; 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); 67 } else { 68 result = base::StringPrintf("NSS error code: %d", PR_GetError()); 69 } 70 return result; 71 } 72 73 #if !defined(OS_CHROMEOS) 74 base::FilePath GetDefaultConfigDirectory() { 75 base::FilePath dir; 76 base::PathService::Get(base::DIR_HOME, &dir); 77 if (dir.empty()) { 78 LOG(ERROR) << "Failed to get home directory."; 79 return dir; 80 } 81 dir = dir.AppendASCII(".pki").AppendASCII("nssdb"); 82 if (!base::CreateDirectory(dir)) { 83 LOG(ERROR) << "Failed to create " << dir.value() << " directory."; 84 dir.clear(); 85 } 86 DVLOG(2) << "DefaultConfigDirectory: " << dir.value(); 87 return dir; 88 } 89 #endif // !defined(OS_CHROMEOS) 90 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(); 101 return database_dir; 102 #else 103 return GetDefaultConfigDirectory(); 104 #endif // defined(OS_CHROMEOS) 105 } 106 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); 112 if (delegate) { 113 bool cancelled = false; 114 std::string password = delegate->RequestPassword(PK11_GetTokenName(slot), 115 retry != PR_FALSE, 116 &cancelled); 117 if (cancelled) 118 return nullptr; 119 char* result = PORT_Strdup(password.c_str()); 120 password.replace(0, password.size(), password.size(), 0); 121 return result; 122 } 123 DLOG(ERROR) << "PK11 password requested with nullptr arg"; 124 return nullptr; 125 } 126 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 130 // singleton. 131 class NSPRInitSingleton { 132 private: 133 friend struct base::LazyInstanceTraitsBase<NSPRInitSingleton>; 134 135 NSPRInitSingleton() { 136 PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); 137 } 138 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 141 // shut down. 142 ~NSPRInitSingleton() = delete; 143 }; 144 145 base::LazyInstance<NSPRInitSingleton>::Leaky 146 g_nspr_singleton = LAZY_INSTANCE_INITIALIZER; 147 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; 157 } 158 159 #if defined(OS_CHROMEOS) 160 class ChromeOSUserData { 161 public: 162 explicit ChromeOSUserData(ScopedPK11Slot public_slot) 163 : public_slot_(std::move(public_slot)), 164 private_slot_initialization_started_(false) {} 165 ~ChromeOSUserData() { 166 if (public_slot_) { 167 SECStatus status = SECMOD_CloseUserDB(public_slot_.get()); 168 if (status != SECSuccess) 169 PLOG(ERROR) << "SECMOD_CloseUserDB failed: " << PORT_GetError(); 170 } 171 } 172 173 ScopedPK11Slot GetPublicSlot() { 174 return ScopedPK11Slot(public_slot_ ? PK11_ReferenceSlot(public_slot_.get()) 175 : nullptr); 176 } 177 178 ScopedPK11Slot GetPrivateSlot( 179 base::OnceCallback<void(ScopedPK11Slot)> callback) { 180 if (private_slot_) 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(); 185 } 186 187 void SetPrivateSlot(ScopedPK11Slot private_slot) { 188 DCHECK(!private_slot_); 189 private_slot_ = std::move(private_slot); 190 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(); 195 ++i) { 196 std::move(*i).Run( 197 ScopedPK11Slot(PK11_ReferenceSlot(private_slot_.get()))); 198 } 199 } 200 201 bool private_slot_initialization_started() const { 202 return private_slot_initialization_started_; 203 } 204 205 void set_private_slot_initialization_started() { 206 private_slot_initialization_started_ = true; 207 } 208 209 private: 210 ScopedPK11Slot public_slot_; 211 ScopedPK11Slot private_slot_; 212 213 bool private_slot_initialization_started_; 214 215 typedef std::vector<base::OnceCallback<void(ScopedPK11Slot)>> 216 SlotReadyCallbackList; 217 SlotReadyCallbackList tpm_ready_callback_list_; 218 }; 219 220 class ScopedChapsLoadFixup { 221 public: 222 ScopedChapsLoadFixup(); 223 ~ScopedChapsLoadFixup(); 224 225 private: 226 #if defined(COMPONENT_BUILD) 227 void* chaps_handle_; 228 #endif 229 }; 230 231 #if defined(COMPONENT_BUILD) 232 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); 237 } 238 239 ScopedChapsLoadFixup::~ScopedChapsLoadFixup() { 240 // LoadModule() will have taken a 2nd reference. 241 if (chaps_handle_) 242 dlclose(chaps_handle_); 243 } 244 245 #else 246 247 ScopedChapsLoadFixup::ScopedChapsLoadFixup() {} 248 ScopedChapsLoadFixup::~ScopedChapsLoadFixup() {} 249 250 #endif // defined(COMPONENT_BUILD) 251 #endif // defined(OS_CHROMEOS) 252 253 class NSSInitSingleton { 254 public: 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; 262 }; 263 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; 271 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(); 276 } 277 return OpenSoftwareNSSDB(nssdb_path, db_name); 278 } 279 280 void EnableTPMTokenForNSS() { 281 DCHECK(thread_checker_.CalledOnValidThread()); 282 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 285 // implementation. 286 tpm_token_enabled_for_nss_ = true; 287 } 288 289 bool IsTPMTokenEnabledForNSS() { 290 DCHECK(thread_checker_.CalledOnValidThread()); 291 return tpm_token_enabled_for_nss_; 292 } 293 294 void InitializeTPMTokenAndSystemSlot( 295 int system_slot_id, 296 base::OnceCallback<void(bool)> callback) { 297 DCHECK(thread_checker_.CalledOnValidThread()); 298 // Should not be called while there is already an initialization in 299 // progress. 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)); 305 return; 306 } 307 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. 311 if (tpm_slot_) { 312 base::ThreadTaskRunnerHandle::Get()->PostTask( 313 FROM_HERE, base::BindOnce(std::move(callback), true)); 314 return; 315 } 316 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( 323 FROM_HERE, 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; 331 } 332 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 340 // initialization. 341 base::ScopedBlockingCall scoped_blocking_call( 342 base::BlockingType::WILL_BLOCK); 343 344 if (!tpm_args->chaps_module) { 345 ScopedChapsLoadFixup chaps_loader; 346 347 DVLOG(3) << "Loading chaps..."; 348 tpm_args->chaps_module = LoadModule( 349 kChapsModuleName, 350 kChapsPath, 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})\""); 357 } 358 if (tpm_args->chaps_module) { 359 tpm_args->tpm_slot = 360 GetTPMSlotForIdInThreadPool(tpm_args->chaps_module, token_slot_id); 361 } 362 } 363 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; 370 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())); 377 } 378 initializing_tpm_token_ = false; 379 380 if (tpm_slot_) 381 RunAndClearTPMReadyCallbackList(); 382 383 std::move(callback).Run(!!tpm_slot_); 384 } 385 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(); 391 ++i) { 392 std::move(*i).Run(); 393 } 394 } 395 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 400 // don't regress. 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(); 406 } 407 408 if (tpm_slot_) 409 return true; 410 411 if (!callback.is_null()) 412 tpm_ready_callback_list_.push_back(std::move(callback)); 413 414 return false; 415 } 416 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 419 // we also control. 420 static crypto::ScopedPK11Slot GetTPMSlotForIdInThreadPool( 421 SECMODModule* chaps_module, 422 CK_SLOT_ID slot_id) { 423 DCHECK(chaps_module); 424 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(); 429 430 PK11SlotInfo* slot = SECMOD_LookupSlot(chaps_module->moduleID, slot_id); 431 if (!slot) 432 LOG(ERROR) << "TPM slot " << slot_id << " not found."; 433 return crypto::ScopedPK11Slot(slot); 434 } 435 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."; 442 return false; 443 } 444 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)); 451 return true; 452 } 453 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()); 457 458 return !chromeos_user_map_[username_hash] 459 ->private_slot_initialization_started(); 460 } 461 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()); 465 466 chromeos_user_map_[username_hash] 467 ->set_private_slot_initialization_started(); 468 } 469 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()); 476 477 if (!chaps_module_) 478 return; 479 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( 486 FROM_HERE, 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))); 493 } 494 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)); 503 } 504 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()); 512 513 if (prepared_test_private_slot_) { 514 chromeos_user_map_[username_hash]->SetPrivateSlot( 515 std::move(prepared_test_private_slot_)); 516 return; 517 } 518 519 chromeos_user_map_[username_hash]->SetPrivateSlot( 520 chromeos_user_map_[username_hash]->GetPublicSlot()); 521 } 522 523 ScopedPK11Slot GetPublicSlotForChromeOSUser( 524 const std::string& username_hash) { 525 DCHECK(thread_checker_.CalledOnValidThread()); 526 527 if (username_hash.empty()) { 528 DVLOG(2) << "empty username_hash"; 529 return ScopedPK11Slot(); 530 } 531 532 if (chromeos_user_map_.find(username_hash) == chromeos_user_map_.end()) { 533 LOG(ERROR) << username_hash << " not initialized."; 534 return ScopedPK11Slot(); 535 } 536 return chromeos_user_map_[username_hash]->GetPublicSlot(); 537 } 538 539 ScopedPK11Slot GetPrivateSlotForChromeOSUser( 540 const std::string& username_hash, 541 base::OnceCallback<void(ScopedPK11Slot)> callback) { 542 DCHECK(thread_checker_.CalledOnValidThread()); 543 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())); 549 } 550 return ScopedPK11Slot(); 551 } 552 553 DCHECK(chromeos_user_map_.find(username_hash) != chromeos_user_map_.end()); 554 555 return chromeos_user_map_[username_hash]->GetPrivateSlot( 556 std::move(callback)); 557 } 558 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); 564 } 565 566 void SetSystemKeySlotForTesting(ScopedPK11Slot slot) { 567 DCHECK(thread_checker_.CalledOnValidThread()); 568 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(); 576 } else { 577 tpm_slot_.reset(); 578 } 579 } 580 581 void SetPrivateSoftwareSlotForChromeOSUserForTesting(ScopedPK11Slot slot) { 582 DCHECK(thread_checker_.CalledOnValidThread()); 583 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); 588 } 589 #endif // defined(OS_CHROMEOS) 590 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(); 597 } 598 599 return PK11_GetInternalKeySlot(); 600 } 601 #endif 602 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()))); 608 } 609 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? 617 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)); 623 } 624 if (IsTPMTokenReady(std::move(wrapped_callback))) 625 return ScopedPK11Slot(PK11_ReferenceSlot(tpm_slot_.get())); 626 return ScopedPK11Slot(); 627 } 628 #endif 629 630 private: 631 friend struct base::LazyInstanceTraitsBase<NSSInitSingleton>; 632 633 NSSInitSingleton() 634 : tpm_token_enabled_for_nss_(false), 635 initializing_tpm_token_(false), 636 chaps_module_(nullptr), 637 root_(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; 642 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(); 646 647 EnsureNSPRInit(); 648 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 " 658 "maintainer."; 659 } 660 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()); 670 #else 671 status = NSS_InitReadWrite(nss_config_dir.c_str()); 672 #endif 673 if (status != SECSuccess) { 674 LOG(ERROR) << "Error initializing NSS with a persistent " 675 "database (" << nss_config_dir 676 << "): " << GetNSSErrorMessage(); 677 } 678 } 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(); 684 return; 685 } 686 } 687 688 PK11_SetPasswordFunc(PKCS11PasswordFunc); 689 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 692 // log in. 693 PK11SlotInfo* slot = PK11_GetInternalKeySlot(); 694 if (slot) { 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); 699 PK11_FreeSlot(slot); 700 } 701 702 root_ = InitDefaultRootCerts(); 703 704 // Disable MD5 certificate signatures. (They are disabled by default in 705 // NSS 3.14.) 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); 709 } 710 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 713 // shut down. 714 ~NSSInitSingleton() = delete; 715 716 // Load nss's built-in root certs. 717 SECMODModule* InitDefaultRootCerts() { 718 SECMODModule* root = LoadModule("Root Certs", "libnssckbi.so", nullptr); 719 if (root) 720 return root; 721 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. 725 return nullptr; 726 } 727 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 : ""); 735 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); 742 if (!module) { 743 LOG(ERROR) << "Error loading " << name << " module into NSS: " 744 << GetNSSErrorMessage(); 745 return nullptr; 746 } 747 if (!module->loaded) { 748 LOG(ERROR) << "After loading " << name << ", loaded==false: " 749 << GetNSSErrorMessage(); 750 SECMOD_DestroyModule(module); 751 return nullptr; 752 } 753 return module; 754 } 755 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_; 762 SECMODModule* root_; 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_; 767 #endif 768 769 base::ThreadChecker thread_checker_; 770 }; 771 772 base::LazyInstance<NSSInitSingleton>::Leaky 773 g_nss_singleton = LAZY_INSTANCE_INITIALIZER; 774 } // namespace 775 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()); 783 if (db_slot) { 784 if (PK11_NeedUserInit(db_slot)) 785 PK11_InitPin(db_slot, nullptr, nullptr); 786 } else { 787 LOG(ERROR) << "Error opening persistent database (" << modspec 788 << "): " << GetNSSErrorMessage(); 789 } 790 return ScopedPK11Slot(db_slot); 791 } 792 793 void EnsureNSPRInit() { 794 g_nspr_singleton.Get(); 795 } 796 797 void EnsureNSSInit() { 798 g_nss_singleton.Get(); 799 } 800 801 bool CheckNSSVersion(const char* version) { 802 return !!NSS_VersionCheck(version); 803 } 804 805 AutoSECMODListReadLock::AutoSECMODListReadLock() 806 : lock_(SECMOD_GetDefaultModuleListLock()) { 807 SECMOD_GetReadLock(lock_); 808 } 809 810 AutoSECMODListReadLock::~AutoSECMODListReadLock() { 811 SECMOD_ReleaseReadLock(lock_); 812 } 813 814 #if defined(OS_CHROMEOS) 815 ScopedPK11Slot GetSystemNSSKeySlot( 816 base::OnceCallback<void(ScopedPK11Slot)> callback) { 817 return g_nss_singleton.Get().GetSystemNSSKeySlot(std::move(callback)); 818 } 819 820 void SetSystemKeySlotForTesting(ScopedPK11Slot slot) { 821 g_nss_singleton.Get().SetSystemKeySlotForTesting(std::move(slot)); 822 } 823 824 void EnableTPMTokenForNSS() { 825 g_nss_singleton.Get().EnableTPMTokenForNSS(); 826 } 827 828 bool IsTPMTokenEnabledForNSS() { 829 return g_nss_singleton.Get().IsTPMTokenEnabledForNSS(); 830 } 831 832 bool IsTPMTokenReady(base::OnceClosure callback) { 833 return g_nss_singleton.Get().IsTPMTokenReady(std::move(callback)); 834 } 835 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)); 840 } 841 842 bool InitializeNSSForChromeOSUser(const std::string& username_hash, 843 const base::FilePath& path) { 844 return g_nss_singleton.Get().InitializeNSSForChromeOSUser(username_hash, 845 path); 846 } 847 848 bool ShouldInitializeTPMForChromeOSUser(const std::string& username_hash) { 849 return g_nss_singleton.Get().ShouldInitializeTPMForChromeOSUser( 850 username_hash); 851 } 852 853 void WillInitializeTPMForChromeOSUser(const std::string& username_hash) { 854 g_nss_singleton.Get().WillInitializeTPMForChromeOSUser(username_hash); 855 } 856 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); 861 } 862 863 void InitializePrivateSoftwareSlotForChromeOSUser( 864 const std::string& username_hash) { 865 g_nss_singleton.Get().InitializePrivateSoftwareSlotForChromeOSUser( 866 username_hash); 867 } 868 869 ScopedPK11Slot GetPublicSlotForChromeOSUser(const std::string& username_hash) { 870 return g_nss_singleton.Get().GetPublicSlotForChromeOSUser(username_hash); 871 } 872 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)); 878 } 879 880 void CloseChromeOSUserForTesting(const std::string& username_hash) { 881 g_nss_singleton.Get().CloseChromeOSUserForTesting(username_hash); 882 } 883 884 void SetPrivateSoftwareSlotForChromeOSUserForTesting(ScopedPK11Slot slot) { 885 g_nss_singleton.Get().SetPrivateSoftwareSlotForChromeOSUserForTesting( 886 std::move(slot)); 887 } 888 #endif // defined(OS_CHROMEOS) 889 890 base::Time PRTimeToBaseTime(PRTime prtime) { 891 return base::Time::FromInternalValue( 892 prtime + base::Time::UnixEpoch().ToInternalValue()); 893 } 894 895 PRTime BaseTimeToPRTime(base::Time time) { 896 return time.ToInternalValue() - base::Time::UnixEpoch().ToInternalValue(); 897 } 898 899 #if !defined(OS_CHROMEOS) 900 PK11SlotInfo* GetPersistentNSSKeySlot() { 901 return g_nss_singleton.Get().GetPersistentNSSKeySlot(); 902 } 903 #endif 904 905 } // namespace crypto 906