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 <memory> 16 #include <utility> 17 18 #include "base/location.h" 19 #include "base/single_thread_task_runner.h" 20 #include "base/threading/thread_task_runner_handle.h" 21 #include "crypto/nss_util_internal.h" 22 23 #if defined(OS_OPENBSD) 24 #include <sys/mount.h> 25 #include <sys/param.h> 26 #endif 27 28 #if defined(OS_CHROMEOS) 29 #include <dlfcn.h> 30 #endif 31 32 #include <map> 33 #include <vector> 34 35 #include "base/base_paths.h" 36 #include "base/bind.h" 37 #include "base/cpu.h" 38 #include "base/debug/alias.h" 39 #include "base/debug/stack_trace.h" 40 #include "base/environment.h" 41 #include "base/files/file_path.h" 42 #include "base/files/file_util.h" 43 #include "base/lazy_instance.h" 44 #include "base/logging.h" 45 #include "base/memory/ptr_util.h" 46 #include "base/native_library.h" 47 #include "base/path_service.h" 48 #include "base/strings/stringprintf.h" 49 #include "base/synchronization/lock.h" 50 #include "base/threading/thread_checker.h" 51 #include "base/threading/thread_restrictions.h" 52 #include "base/threading/worker_pool.h" 53 #include "build/build_config.h" 54 #include "crypto/nss_crypto_module_delegate.h" 55 56 namespace crypto { 57 58 namespace { 59 60 #if defined(OS_CHROMEOS) 61 const char kUserNSSDatabaseName[] = "UserNSSDB"; 62 63 // Constants for loading the Chrome OS TPM-backed PKCS #11 library. 64 const char kChapsModuleName[] = "Chaps"; 65 const char kChapsPath[] = "libchaps.so"; 66 67 // Fake certificate authority database used for testing. 68 static const base::FilePath::CharType kReadOnlyCertDB[] = 69 FILE_PATH_LITERAL("/etc/fake_root_ca/nssdb"); 70 #endif // defined(OS_CHROMEOS) 71 72 std::string GetNSSErrorMessage() { 73 std::string result; 74 if (PR_GetErrorTextLength()) { 75 std::unique_ptr<char[]> error_text(new char[PR_GetErrorTextLength() + 1]); 76 PRInt32 copied = PR_GetErrorText(error_text.get()); 77 result = std::string(error_text.get(), copied); 78 } else { 79 result = base::StringPrintf("NSS error code: %d", PR_GetError()); 80 } 81 return result; 82 } 83 84 #if !defined(OS_CHROMEOS) 85 base::FilePath GetDefaultConfigDirectory() { 86 base::FilePath dir; 87 PathService::Get(base::DIR_HOME, &dir); 88 if (dir.empty()) { 89 LOG(ERROR) << "Failed to get home directory."; 90 return dir; 91 } 92 dir = dir.AppendASCII(".pki").AppendASCII("nssdb"); 93 if (!base::CreateDirectory(dir)) { 94 LOG(ERROR) << "Failed to create " << dir.value() << " directory."; 95 dir.clear(); 96 } 97 DVLOG(2) << "DefaultConfigDirectory: " << dir.value(); 98 return dir; 99 } 100 #endif // !defined(IS_CHROMEOS) 101 102 // On non-Chrome OS platforms, return the default config directory. On Chrome OS 103 // test images, return a read-only directory with fake root CA certs (which are 104 // used by the local Google Accounts server mock we use when testing our login 105 // code). On Chrome OS non-test images (where the read-only directory doesn't 106 // exist), return an empty path. 107 base::FilePath GetInitialConfigDirectory() { 108 #if defined(OS_CHROMEOS) 109 base::FilePath database_dir = base::FilePath(kReadOnlyCertDB); 110 if (!base::PathExists(database_dir)) 111 database_dir.clear(); 112 return database_dir; 113 #else 114 return GetDefaultConfigDirectory(); 115 #endif // defined(OS_CHROMEOS) 116 } 117 118 // This callback for NSS forwards all requests to a caller-specified 119 // CryptoModuleBlockingPasswordDelegate object. 120 char* PKCS11PasswordFunc(PK11SlotInfo* slot, PRBool retry, void* arg) { 121 crypto::CryptoModuleBlockingPasswordDelegate* delegate = 122 reinterpret_cast<crypto::CryptoModuleBlockingPasswordDelegate*>(arg); 123 if (delegate) { 124 bool cancelled = false; 125 std::string password = delegate->RequestPassword(PK11_GetTokenName(slot), 126 retry != PR_FALSE, 127 &cancelled); 128 if (cancelled) 129 return nullptr; 130 char* result = PORT_Strdup(password.c_str()); 131 password.replace(0, password.size(), password.size(), 0); 132 return result; 133 } 134 DLOG(ERROR) << "PK11 password requested with nullptr arg"; 135 return nullptr; 136 } 137 138 // NSS creates a local cache of the sqlite database if it detects that the 139 // filesystem the database is on is much slower than the local disk. The 140 // detection doesn't work with the latest versions of sqlite, such as 3.6.22 141 // (NSS bug https://bugzilla.mozilla.org/show_bug.cgi?id=578561). So we set 142 // the NSS environment variable NSS_SDB_USE_CACHE to "yes" to override NSS's 143 // detection when database_dir is on NFS. See http://crbug.com/48585. 144 // 145 // Because this function sets an environment variable it must be run before we 146 // go multi-threaded. 147 void UseLocalCacheOfNSSDatabaseIfNFS(const base::FilePath& database_dir) { 148 bool db_on_nfs = false; 149 #if defined(OS_LINUX) 150 base::FileSystemType fs_type = base::FILE_SYSTEM_UNKNOWN; 151 if (base::GetFileSystemType(database_dir, &fs_type)) 152 db_on_nfs = (fs_type == base::FILE_SYSTEM_NFS); 153 #elif defined(OS_OPENBSD) 154 struct statfs buf; 155 if (statfs(database_dir.value().c_str(), &buf) == 0) 156 db_on_nfs = (strcmp(buf.f_fstypename, MOUNT_NFS) == 0); 157 #else 158 NOTIMPLEMENTED(); 159 #endif 160 161 if (db_on_nfs) { 162 std::unique_ptr<base::Environment> env(base::Environment::Create()); 163 static const char kUseCacheEnvVar[] = "NSS_SDB_USE_CACHE"; 164 if (!env->HasVar(kUseCacheEnvVar)) 165 env->SetVar(kUseCacheEnvVar, "yes"); 166 } 167 } 168 169 // A singleton to initialize/deinitialize NSPR. 170 // Separate from the NSS singleton because we initialize NSPR on the UI thread. 171 // Now that we're leaking the singleton, we could merge back with the NSS 172 // singleton. 173 class NSPRInitSingleton { 174 private: 175 friend struct base::LazyInstanceTraitsBase<NSPRInitSingleton>; 176 177 NSPRInitSingleton() { 178 PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); 179 } 180 181 // NOTE(willchan): We don't actually execute this code since we leak NSS to 182 // prevent non-joinable threads from using NSS after it's already been shut 183 // down. 184 ~NSPRInitSingleton() { 185 PL_ArenaFinish(); 186 PRStatus prstatus = PR_Cleanup(); 187 if (prstatus != PR_SUCCESS) 188 LOG(ERROR) << "PR_Cleanup failed; was NSPR initialized on wrong thread?"; 189 } 190 }; 191 192 base::LazyInstance<NSPRInitSingleton>::Leaky 193 g_nspr_singleton = LAZY_INSTANCE_INITIALIZER; 194 195 // Force a crash with error info on NSS_NoDB_Init failure. 196 void CrashOnNSSInitFailure() { 197 int nss_error = PR_GetError(); 198 int os_error = PR_GetOSError(); 199 base::debug::Alias(&nss_error); 200 base::debug::Alias(&os_error); 201 LOG(ERROR) << "Error initializing NSS without a persistent database: " 202 << GetNSSErrorMessage(); 203 LOG(FATAL) << "nss_error=" << nss_error << ", os_error=" << os_error; 204 } 205 206 #if defined(OS_CHROMEOS) 207 class ChromeOSUserData { 208 public: 209 explicit ChromeOSUserData(ScopedPK11Slot public_slot) 210 : public_slot_(std::move(public_slot)), 211 private_slot_initialization_started_(false) {} 212 ~ChromeOSUserData() { 213 if (public_slot_) { 214 SECStatus status = SECMOD_CloseUserDB(public_slot_.get()); 215 if (status != SECSuccess) 216 PLOG(ERROR) << "SECMOD_CloseUserDB failed: " << PORT_GetError(); 217 } 218 } 219 220 ScopedPK11Slot GetPublicSlot() { 221 return ScopedPK11Slot(public_slot_ ? PK11_ReferenceSlot(public_slot_.get()) 222 : nullptr); 223 } 224 225 ScopedPK11Slot GetPrivateSlot( 226 const base::Callback<void(ScopedPK11Slot)>& callback) { 227 if (private_slot_) 228 return ScopedPK11Slot(PK11_ReferenceSlot(private_slot_.get())); 229 if (!callback.is_null()) 230 tpm_ready_callback_list_.push_back(callback); 231 return ScopedPK11Slot(); 232 } 233 234 void SetPrivateSlot(ScopedPK11Slot private_slot) { 235 DCHECK(!private_slot_); 236 private_slot_ = std::move(private_slot); 237 238 SlotReadyCallbackList callback_list; 239 callback_list.swap(tpm_ready_callback_list_); 240 for (SlotReadyCallbackList::iterator i = callback_list.begin(); 241 i != callback_list.end(); 242 ++i) { 243 (*i).Run(ScopedPK11Slot(PK11_ReferenceSlot(private_slot_.get()))); 244 } 245 } 246 247 bool private_slot_initialization_started() const { 248 return private_slot_initialization_started_; 249 } 250 251 void set_private_slot_initialization_started() { 252 private_slot_initialization_started_ = true; 253 } 254 255 private: 256 ScopedPK11Slot public_slot_; 257 ScopedPK11Slot private_slot_; 258 259 bool private_slot_initialization_started_; 260 261 typedef std::vector<base::Callback<void(ScopedPK11Slot)> > 262 SlotReadyCallbackList; 263 SlotReadyCallbackList tpm_ready_callback_list_; 264 }; 265 266 class ScopedChapsLoadFixup { 267 public: 268 ScopedChapsLoadFixup(); 269 ~ScopedChapsLoadFixup(); 270 271 private: 272 #if defined(COMPONENT_BUILD) 273 void* chaps_handle_; 274 #endif 275 }; 276 277 #if defined(COMPONENT_BUILD) 278 279 ScopedChapsLoadFixup::ScopedChapsLoadFixup() { 280 // HACK: libchaps links the system protobuf and there are symbol conflicts 281 // with the bundled copy. Load chaps with RTLD_DEEPBIND to workaround. 282 chaps_handle_ = dlopen(kChapsPath, RTLD_LOCAL | RTLD_NOW | RTLD_DEEPBIND); 283 } 284 285 ScopedChapsLoadFixup::~ScopedChapsLoadFixup() { 286 // LoadModule() will have taken a 2nd reference. 287 if (chaps_handle_) 288 dlclose(chaps_handle_); 289 } 290 291 #else 292 293 ScopedChapsLoadFixup::ScopedChapsLoadFixup() {} 294 ScopedChapsLoadFixup::~ScopedChapsLoadFixup() {} 295 296 #endif // defined(COMPONENT_BUILD) 297 #endif // defined(OS_CHROMEOS) 298 299 class NSSInitSingleton { 300 public: 301 #if defined(OS_CHROMEOS) 302 // Used with PostTaskAndReply to pass handles to worker thread and back. 303 struct TPMModuleAndSlot { 304 explicit TPMModuleAndSlot(SECMODModule* init_chaps_module) 305 : chaps_module(init_chaps_module) {} 306 SECMODModule* chaps_module; 307 crypto::ScopedPK11Slot tpm_slot; 308 }; 309 310 ScopedPK11Slot OpenPersistentNSSDBForPath(const std::string& db_name, 311 const base::FilePath& path) { 312 DCHECK(thread_checker_.CalledOnValidThread()); 313 // NSS is allowed to do IO on the current thread since dispatching 314 // to a dedicated thread would still have the affect of blocking 315 // the current thread, due to NSS's internal locking requirements 316 base::ThreadRestrictions::ScopedAllowIO allow_io; 317 318 base::FilePath nssdb_path = path.AppendASCII(".pki").AppendASCII("nssdb"); 319 if (!base::CreateDirectory(nssdb_path)) { 320 LOG(ERROR) << "Failed to create " << nssdb_path.value() << " directory."; 321 return ScopedPK11Slot(); 322 } 323 return OpenSoftwareNSSDB(nssdb_path, db_name); 324 } 325 326 void EnableTPMTokenForNSS() { 327 DCHECK(thread_checker_.CalledOnValidThread()); 328 329 // If this gets set, then we'll use the TPM for certs with 330 // private keys, otherwise we'll fall back to the software 331 // implementation. 332 tpm_token_enabled_for_nss_ = true; 333 } 334 335 bool IsTPMTokenEnabledForNSS() { 336 DCHECK(thread_checker_.CalledOnValidThread()); 337 return tpm_token_enabled_for_nss_; 338 } 339 340 void InitializeTPMTokenAndSystemSlot( 341 int system_slot_id, 342 const base::Callback<void(bool)>& callback) { 343 DCHECK(thread_checker_.CalledOnValidThread()); 344 // Should not be called while there is already an initialization in 345 // progress. 346 DCHECK(!initializing_tpm_token_); 347 // If EnableTPMTokenForNSS hasn't been called, return false. 348 if (!tpm_token_enabled_for_nss_) { 349 base::ThreadTaskRunnerHandle::Get()->PostTask( 350 FROM_HERE, base::Bind(callback, false)); 351 return; 352 } 353 354 // If everything is already initialized, then return true. 355 // Note that only |tpm_slot_| is checked, since |chaps_module_| could be 356 // nullptr in tests while |tpm_slot_| has been set to the test DB. 357 if (tpm_slot_) { 358 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, 359 base::Bind(callback, true)); 360 return; 361 } 362 363 // Note that a reference is not taken to chaps_module_. This is safe since 364 // NSSInitSingleton is Leaky, so the reference it holds is never released. 365 std::unique_ptr<TPMModuleAndSlot> tpm_args( 366 new TPMModuleAndSlot(chaps_module_)); 367 TPMModuleAndSlot* tpm_args_ptr = tpm_args.get(); 368 if (base::WorkerPool::PostTaskAndReply( 369 FROM_HERE, 370 base::Bind(&NSSInitSingleton::InitializeTPMTokenOnWorkerThread, 371 system_slot_id, tpm_args_ptr), 372 base::Bind(&NSSInitSingleton::OnInitializedTPMTokenAndSystemSlot, 373 base::Unretained(this), // NSSInitSingleton is leaky 374 callback, base::Passed(&tpm_args)), 375 true /* task_is_slow */)) { 376 initializing_tpm_token_ = true; 377 } else { 378 base::ThreadTaskRunnerHandle::Get()->PostTask( 379 FROM_HERE, base::Bind(callback, false)); 380 } 381 } 382 383 static void InitializeTPMTokenOnWorkerThread(CK_SLOT_ID token_slot_id, 384 TPMModuleAndSlot* tpm_args) { 385 // This tries to load the Chaps module so NSS can talk to the hardware 386 // TPM. 387 if (!tpm_args->chaps_module) { 388 ScopedChapsLoadFixup chaps_loader; 389 390 DVLOG(3) << "Loading chaps..."; 391 tpm_args->chaps_module = LoadModule( 392 kChapsModuleName, 393 kChapsPath, 394 // For more details on these parameters, see: 395 // https://developer.mozilla.org/en/PKCS11_Module_Specs 396 // slotFlags=[PublicCerts] -- Certificates and public keys can be 397 // read from this slot without requiring a call to C_Login. 398 // askpw=only -- Only authenticate to the token when necessary. 399 "NSS=\"slotParams=(0={slotFlags=[PublicCerts] askpw=only})\""); 400 } 401 if (tpm_args->chaps_module) { 402 tpm_args->tpm_slot = 403 GetTPMSlotForIdOnWorkerThread(tpm_args->chaps_module, token_slot_id); 404 } 405 } 406 407 void OnInitializedTPMTokenAndSystemSlot( 408 const base::Callback<void(bool)>& callback, 409 std::unique_ptr<TPMModuleAndSlot> tpm_args) { 410 DCHECK(thread_checker_.CalledOnValidThread()); 411 DVLOG(2) << "Loaded chaps: " << !!tpm_args->chaps_module 412 << ", got tpm slot: " << !!tpm_args->tpm_slot; 413 414 chaps_module_ = tpm_args->chaps_module; 415 tpm_slot_ = std::move(tpm_args->tpm_slot); 416 if (!chaps_module_ && test_system_slot_) { 417 // chromeos_unittests try to test the TPM initialization process. If we 418 // have a test DB open, pretend that it is the TPM slot. 419 tpm_slot_.reset(PK11_ReferenceSlot(test_system_slot_.get())); 420 } 421 initializing_tpm_token_ = false; 422 423 if (tpm_slot_) 424 RunAndClearTPMReadyCallbackList(); 425 426 callback.Run(!!tpm_slot_); 427 } 428 429 void RunAndClearTPMReadyCallbackList() { 430 TPMReadyCallbackList callback_list; 431 callback_list.swap(tpm_ready_callback_list_); 432 for (TPMReadyCallbackList::iterator i = callback_list.begin(); 433 i != callback_list.end(); 434 ++i) { 435 i->Run(); 436 } 437 } 438 439 bool IsTPMTokenReady(const base::Closure& callback) { 440 if (!callback.is_null()) { 441 // Cannot DCHECK in the general case yet, but since the callback is 442 // a new addition to the API, DCHECK to make sure at least the new uses 443 // don't regress. 444 DCHECK(thread_checker_.CalledOnValidThread()); 445 } else if (!thread_checker_.CalledOnValidThread()) { 446 // TODO(mattm): Change to DCHECK when callers have been fixed. 447 DVLOG(1) << "Called on wrong thread.\n" 448 << base::debug::StackTrace().ToString(); 449 } 450 451 if (tpm_slot_) 452 return true; 453 454 if (!callback.is_null()) 455 tpm_ready_callback_list_.push_back(callback); 456 457 return false; 458 } 459 460 // Note that CK_SLOT_ID is an unsigned long, but cryptohome gives us the slot 461 // id as an int. This should be safe since this is only used with chaps, which 462 // we also control. 463 static crypto::ScopedPK11Slot GetTPMSlotForIdOnWorkerThread( 464 SECMODModule* chaps_module, 465 CK_SLOT_ID slot_id) { 466 DCHECK(chaps_module); 467 468 DVLOG(3) << "Poking chaps module."; 469 SECStatus rv = SECMOD_UpdateSlotList(chaps_module); 470 if (rv != SECSuccess) 471 PLOG(ERROR) << "SECMOD_UpdateSlotList failed: " << PORT_GetError(); 472 473 PK11SlotInfo* slot = SECMOD_LookupSlot(chaps_module->moduleID, slot_id); 474 if (!slot) 475 LOG(ERROR) << "TPM slot " << slot_id << " not found."; 476 return crypto::ScopedPK11Slot(slot); 477 } 478 479 bool InitializeNSSForChromeOSUser(const std::string& username_hash, 480 const base::FilePath& path) { 481 DCHECK(thread_checker_.CalledOnValidThread()); 482 if (chromeos_user_map_.find(username_hash) != chromeos_user_map_.end()) { 483 // This user already exists in our mapping. 484 DVLOG(2) << username_hash << " already initialized."; 485 return false; 486 } 487 488 DVLOG(2) << "Opening NSS DB " << path.value(); 489 std::string db_name = base::StringPrintf( 490 "%s %s", kUserNSSDatabaseName, username_hash.c_str()); 491 ScopedPK11Slot public_slot(OpenPersistentNSSDBForPath(db_name, path)); 492 chromeos_user_map_[username_hash] = 493 base::MakeUnique<ChromeOSUserData>(std::move(public_slot)); 494 return true; 495 } 496 497 bool ShouldInitializeTPMForChromeOSUser(const std::string& username_hash) { 498 DCHECK(thread_checker_.CalledOnValidThread()); 499 DCHECK(chromeos_user_map_.find(username_hash) != chromeos_user_map_.end()); 500 501 return !chromeos_user_map_[username_hash] 502 ->private_slot_initialization_started(); 503 } 504 505 void WillInitializeTPMForChromeOSUser(const std::string& username_hash) { 506 DCHECK(thread_checker_.CalledOnValidThread()); 507 DCHECK(chromeos_user_map_.find(username_hash) != chromeos_user_map_.end()); 508 509 chromeos_user_map_[username_hash] 510 ->set_private_slot_initialization_started(); 511 } 512 513 void InitializeTPMForChromeOSUser(const std::string& username_hash, 514 CK_SLOT_ID slot_id) { 515 DCHECK(thread_checker_.CalledOnValidThread()); 516 DCHECK(chromeos_user_map_.find(username_hash) != chromeos_user_map_.end()); 517 DCHECK(chromeos_user_map_[username_hash]-> 518 private_slot_initialization_started()); 519 520 if (!chaps_module_) 521 return; 522 523 // Note that a reference is not taken to chaps_module_. This is safe since 524 // NSSInitSingleton is Leaky, so the reference it holds is never released. 525 std::unique_ptr<TPMModuleAndSlot> tpm_args( 526 new TPMModuleAndSlot(chaps_module_)); 527 TPMModuleAndSlot* tpm_args_ptr = tpm_args.get(); 528 base::WorkerPool::PostTaskAndReply( 529 FROM_HERE, 530 base::Bind(&NSSInitSingleton::InitializeTPMTokenOnWorkerThread, slot_id, 531 tpm_args_ptr), 532 base::Bind(&NSSInitSingleton::OnInitializedTPMForChromeOSUser, 533 base::Unretained(this), // NSSInitSingleton is leaky 534 username_hash, base::Passed(&tpm_args)), 535 true /* task_is_slow */); 536 } 537 538 void OnInitializedTPMForChromeOSUser( 539 const std::string& username_hash, 540 std::unique_ptr<TPMModuleAndSlot> tpm_args) { 541 DCHECK(thread_checker_.CalledOnValidThread()); 542 DVLOG(2) << "Got tpm slot for " << username_hash << " " 543 << !!tpm_args->tpm_slot; 544 chromeos_user_map_[username_hash]->SetPrivateSlot( 545 std::move(tpm_args->tpm_slot)); 546 } 547 548 void InitializePrivateSoftwareSlotForChromeOSUser( 549 const std::string& username_hash) { 550 DCHECK(thread_checker_.CalledOnValidThread()); 551 VLOG(1) << "using software private slot for " << username_hash; 552 DCHECK(chromeos_user_map_.find(username_hash) != chromeos_user_map_.end()); 553 DCHECK(chromeos_user_map_[username_hash]-> 554 private_slot_initialization_started()); 555 556 chromeos_user_map_[username_hash]->SetPrivateSlot( 557 chromeos_user_map_[username_hash]->GetPublicSlot()); 558 } 559 560 ScopedPK11Slot GetPublicSlotForChromeOSUser( 561 const std::string& username_hash) { 562 DCHECK(thread_checker_.CalledOnValidThread()); 563 564 if (username_hash.empty()) { 565 DVLOG(2) << "empty username_hash"; 566 return ScopedPK11Slot(); 567 } 568 569 if (chromeos_user_map_.find(username_hash) == chromeos_user_map_.end()) { 570 LOG(ERROR) << username_hash << " not initialized."; 571 return ScopedPK11Slot(); 572 } 573 return chromeos_user_map_[username_hash]->GetPublicSlot(); 574 } 575 576 ScopedPK11Slot GetPrivateSlotForChromeOSUser( 577 const std::string& username_hash, 578 const base::Callback<void(ScopedPK11Slot)>& callback) { 579 DCHECK(thread_checker_.CalledOnValidThread()); 580 581 if (username_hash.empty()) { 582 DVLOG(2) << "empty username_hash"; 583 if (!callback.is_null()) { 584 base::ThreadTaskRunnerHandle::Get()->PostTask( 585 FROM_HERE, base::Bind(callback, base::Passed(ScopedPK11Slot()))); 586 } 587 return ScopedPK11Slot(); 588 } 589 590 DCHECK(chromeos_user_map_.find(username_hash) != chromeos_user_map_.end()); 591 592 return chromeos_user_map_[username_hash]->GetPrivateSlot(callback); 593 } 594 595 void CloseChromeOSUserForTesting(const std::string& username_hash) { 596 DCHECK(thread_checker_.CalledOnValidThread()); 597 auto i = chromeos_user_map_.find(username_hash); 598 DCHECK(i != chromeos_user_map_.end()); 599 chromeos_user_map_.erase(i); 600 } 601 602 void SetSystemKeySlotForTesting(ScopedPK11Slot slot) { 603 // Ensure that a previous value of test_system_slot_ is not overwritten. 604 // Unsetting, i.e. setting a nullptr, however is allowed. 605 DCHECK(!slot || !test_system_slot_); 606 test_system_slot_ = std::move(slot); 607 if (test_system_slot_) { 608 tpm_slot_.reset(PK11_ReferenceSlot(test_system_slot_.get())); 609 RunAndClearTPMReadyCallbackList(); 610 } else { 611 tpm_slot_.reset(); 612 } 613 } 614 #endif // defined(OS_CHROMEOS) 615 616 #if !defined(OS_CHROMEOS) 617 PK11SlotInfo* GetPersistentNSSKeySlot() { 618 // TODO(mattm): Change to DCHECK when callers have been fixed. 619 if (!thread_checker_.CalledOnValidThread()) { 620 DVLOG(1) << "Called on wrong thread.\n" 621 << base::debug::StackTrace().ToString(); 622 } 623 624 return PK11_GetInternalKeySlot(); 625 } 626 #endif 627 628 #if defined(OS_CHROMEOS) 629 void GetSystemNSSKeySlotCallback( 630 const base::Callback<void(ScopedPK11Slot)>& callback) { 631 callback.Run(ScopedPK11Slot(PK11_ReferenceSlot(tpm_slot_.get()))); 632 } 633 634 ScopedPK11Slot GetSystemNSSKeySlot( 635 const base::Callback<void(ScopedPK11Slot)>& callback) { 636 DCHECK(thread_checker_.CalledOnValidThread()); 637 // TODO(mattm): chromeos::TPMTokenloader always calls 638 // InitializeTPMTokenAndSystemSlot with slot 0. If the system slot is 639 // disabled, tpm_slot_ will be the first user's slot instead. Can that be 640 // detected and return nullptr instead? 641 642 base::Closure wrapped_callback; 643 if (!callback.is_null()) { 644 wrapped_callback = 645 base::Bind(&NSSInitSingleton::GetSystemNSSKeySlotCallback, 646 base::Unretained(this) /* singleton is leaky */, 647 callback); 648 } 649 if (IsTPMTokenReady(wrapped_callback)) 650 return ScopedPK11Slot(PK11_ReferenceSlot(tpm_slot_.get())); 651 return ScopedPK11Slot(); 652 } 653 #endif 654 655 base::Lock* write_lock() { 656 return &write_lock_; 657 } 658 659 private: 660 friend struct base::LazyInstanceTraitsBase<NSSInitSingleton>; 661 662 NSSInitSingleton() 663 : tpm_token_enabled_for_nss_(false), 664 initializing_tpm_token_(false), 665 chaps_module_(nullptr), 666 root_(nullptr) { 667 // It's safe to construct on any thread, since LazyInstance will prevent any 668 // other threads from accessing until the constructor is done. 669 thread_checker_.DetachFromThread(); 670 671 EnsureNSPRInit(); 672 673 // We *must* have NSS >= 3.14.3. 674 static_assert( 675 (NSS_VMAJOR == 3 && NSS_VMINOR == 14 && NSS_VPATCH >= 3) || 676 (NSS_VMAJOR == 3 && NSS_VMINOR > 14) || 677 (NSS_VMAJOR > 3), 678 "nss version check failed"); 679 // Also check the run-time NSS version. 680 // NSS_VersionCheck is a >= check, not strict equality. 681 if (!NSS_VersionCheck("3.14.3")) { 682 LOG(FATAL) << "NSS_VersionCheck(\"3.14.3\") failed. NSS >= 3.14.3 is " 683 "required. Please upgrade to the latest NSS, and if you " 684 "still get this error, contact your distribution " 685 "maintainer."; 686 } 687 688 SECStatus status = SECFailure; 689 base::FilePath database_dir = GetInitialConfigDirectory(); 690 if (!database_dir.empty()) { 691 // This duplicates the work which should have been done in 692 // EarlySetupForNSSInit. However, this function is idempotent so 693 // there's no harm done. 694 UseLocalCacheOfNSSDatabaseIfNFS(database_dir); 695 696 // Initialize with a persistent database (likely, ~/.pki/nssdb). 697 // Use "sql:" which can be shared by multiple processes safely. 698 std::string nss_config_dir = 699 base::StringPrintf("sql:%s", database_dir.value().c_str()); 700 #if defined(OS_CHROMEOS) 701 status = NSS_Init(nss_config_dir.c_str()); 702 #else 703 status = NSS_InitReadWrite(nss_config_dir.c_str()); 704 #endif 705 if (status != SECSuccess) { 706 LOG(ERROR) << "Error initializing NSS with a persistent " 707 "database (" << nss_config_dir 708 << "): " << GetNSSErrorMessage(); 709 } 710 } 711 if (status != SECSuccess) { 712 VLOG(1) << "Initializing NSS without a persistent database."; 713 status = NSS_NoDB_Init(nullptr); 714 if (status != SECSuccess) { 715 CrashOnNSSInitFailure(); 716 return; 717 } 718 } 719 720 PK11_SetPasswordFunc(PKCS11PasswordFunc); 721 722 // If we haven't initialized the password for the NSS databases, 723 // initialize an empty-string password so that we don't need to 724 // log in. 725 PK11SlotInfo* slot = PK11_GetInternalKeySlot(); 726 if (slot) { 727 // PK11_InitPin may write to the keyDB, but no other thread can use NSS 728 // yet, so we don't need to lock. 729 if (PK11_NeedUserInit(slot)) 730 PK11_InitPin(slot, nullptr, nullptr); 731 PK11_FreeSlot(slot); 732 } 733 734 root_ = InitDefaultRootCerts(); 735 736 // Disable MD5 certificate signatures. (They are disabled by default in 737 // NSS 3.14.) 738 NSS_SetAlgorithmPolicy(SEC_OID_MD5, 0, NSS_USE_ALG_IN_CERT_SIGNATURE); 739 NSS_SetAlgorithmPolicy(SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION, 740 0, NSS_USE_ALG_IN_CERT_SIGNATURE); 741 } 742 743 // NOTE(willchan): We don't actually execute this code since we leak NSS to 744 // prevent non-joinable threads from using NSS after it's already been shut 745 // down. 746 ~NSSInitSingleton() { 747 #if defined(OS_CHROMEOS) 748 chromeos_user_map_.clear(); 749 #endif 750 tpm_slot_.reset(); 751 if (root_) { 752 SECMOD_UnloadUserModule(root_); 753 SECMOD_DestroyModule(root_); 754 root_ = nullptr; 755 } 756 if (chaps_module_) { 757 SECMOD_UnloadUserModule(chaps_module_); 758 SECMOD_DestroyModule(chaps_module_); 759 chaps_module_ = nullptr; 760 } 761 762 SECStatus status = NSS_Shutdown(); 763 if (status != SECSuccess) { 764 // We VLOG(1) because this failure is relatively harmless (leaking, but 765 // we're shutting down anyway). 766 VLOG(1) << "NSS_Shutdown failed; see http://crbug.com/4609"; 767 } 768 } 769 770 // Load nss's built-in root certs. 771 SECMODModule* InitDefaultRootCerts() { 772 SECMODModule* root = LoadModule("Root Certs", "libnssckbi.so", nullptr); 773 if (root) 774 return root; 775 776 // Aw, snap. Can't find/load root cert shared library. 777 // This will make it hard to talk to anybody via https. 778 // TODO(mattm): Re-add the NOTREACHED here when crbug.com/310972 is fixed. 779 return nullptr; 780 } 781 782 // Load the given module for this NSS session. 783 static SECMODModule* LoadModule(const char* name, 784 const char* library_path, 785 const char* params) { 786 std::string modparams = base::StringPrintf( 787 "name=\"%s\" library=\"%s\" %s", 788 name, library_path, params ? params : ""); 789 790 // Shouldn't need to const_cast here, but SECMOD doesn't properly 791 // declare input string arguments as const. Bug 792 // https://bugzilla.mozilla.org/show_bug.cgi?id=642546 was filed 793 // on NSS codebase to address this. 794 SECMODModule* module = SECMOD_LoadUserModule( 795 const_cast<char*>(modparams.c_str()), nullptr, PR_FALSE); 796 if (!module) { 797 LOG(ERROR) << "Error loading " << name << " module into NSS: " 798 << GetNSSErrorMessage(); 799 return nullptr; 800 } 801 if (!module->loaded) { 802 LOG(ERROR) << "After loading " << name << ", loaded==false: " 803 << GetNSSErrorMessage(); 804 SECMOD_DestroyModule(module); 805 return nullptr; 806 } 807 return module; 808 } 809 810 bool tpm_token_enabled_for_nss_; 811 bool initializing_tpm_token_; 812 typedef std::vector<base::Closure> TPMReadyCallbackList; 813 TPMReadyCallbackList tpm_ready_callback_list_; 814 SECMODModule* chaps_module_; 815 crypto::ScopedPK11Slot tpm_slot_; 816 SECMODModule* root_; 817 #if defined(OS_CHROMEOS) 818 std::map<std::string, std::unique_ptr<ChromeOSUserData>> chromeos_user_map_; 819 ScopedPK11Slot test_system_slot_; 820 #endif 821 // TODO(davidben): When https://bugzilla.mozilla.org/show_bug.cgi?id=564011 822 // is fixed, we will no longer need the lock. 823 base::Lock write_lock_; 824 825 base::ThreadChecker thread_checker_; 826 }; 827 828 base::LazyInstance<NSSInitSingleton>::Leaky 829 g_nss_singleton = LAZY_INSTANCE_INITIALIZER; 830 } // namespace 831 832 ScopedPK11Slot OpenSoftwareNSSDB(const base::FilePath& path, 833 const std::string& description) { 834 const std::string modspec = 835 base::StringPrintf("configDir='sql:%s' tokenDescription='%s'", 836 path.value().c_str(), 837 description.c_str()); 838 PK11SlotInfo* db_slot = SECMOD_OpenUserDB(modspec.c_str()); 839 if (db_slot) { 840 if (PK11_NeedUserInit(db_slot)) 841 PK11_InitPin(db_slot, nullptr, nullptr); 842 } else { 843 LOG(ERROR) << "Error opening persistent database (" << modspec 844 << "): " << GetNSSErrorMessage(); 845 } 846 return ScopedPK11Slot(db_slot); 847 } 848 849 void EarlySetupForNSSInit() { 850 base::FilePath database_dir = GetInitialConfigDirectory(); 851 if (!database_dir.empty()) 852 UseLocalCacheOfNSSDatabaseIfNFS(database_dir); 853 } 854 855 void EnsureNSPRInit() { 856 g_nspr_singleton.Get(); 857 } 858 859 void EnsureNSSInit() { 860 // Initializing SSL causes us to do blocking IO. 861 // Temporarily allow it until we fix 862 // http://code.google.com/p/chromium/issues/detail?id=59847 863 base::ThreadRestrictions::ScopedAllowIO allow_io; 864 g_nss_singleton.Get(); 865 } 866 867 bool CheckNSSVersion(const char* version) { 868 return !!NSS_VersionCheck(version); 869 } 870 871 base::Lock* GetNSSWriteLock() { 872 return g_nss_singleton.Get().write_lock(); 873 } 874 875 AutoNSSWriteLock::AutoNSSWriteLock() : lock_(GetNSSWriteLock()) { 876 // May be nullptr if the lock is not needed in our version of NSS. 877 if (lock_) 878 lock_->Acquire(); 879 } 880 881 AutoNSSWriteLock::~AutoNSSWriteLock() { 882 if (lock_) { 883 lock_->AssertAcquired(); 884 lock_->Release(); 885 } 886 } 887 888 AutoSECMODListReadLock::AutoSECMODListReadLock() 889 : lock_(SECMOD_GetDefaultModuleListLock()) { 890 SECMOD_GetReadLock(lock_); 891 } 892 893 AutoSECMODListReadLock::~AutoSECMODListReadLock() { 894 SECMOD_ReleaseReadLock(lock_); 895 } 896 897 #if defined(OS_CHROMEOS) 898 ScopedPK11Slot GetSystemNSSKeySlot( 899 const base::Callback<void(ScopedPK11Slot)>& callback) { 900 return g_nss_singleton.Get().GetSystemNSSKeySlot(callback); 901 } 902 903 void SetSystemKeySlotForTesting(ScopedPK11Slot slot) { 904 g_nss_singleton.Get().SetSystemKeySlotForTesting(std::move(slot)); 905 } 906 907 void EnableTPMTokenForNSS() { 908 g_nss_singleton.Get().EnableTPMTokenForNSS(); 909 } 910 911 bool IsTPMTokenEnabledForNSS() { 912 return g_nss_singleton.Get().IsTPMTokenEnabledForNSS(); 913 } 914 915 bool IsTPMTokenReady(const base::Closure& callback) { 916 return g_nss_singleton.Get().IsTPMTokenReady(callback); 917 } 918 919 void InitializeTPMTokenAndSystemSlot( 920 int token_slot_id, 921 const base::Callback<void(bool)>& callback) { 922 g_nss_singleton.Get().InitializeTPMTokenAndSystemSlot(token_slot_id, 923 callback); 924 } 925 926 bool InitializeNSSForChromeOSUser(const std::string& username_hash, 927 const base::FilePath& path) { 928 return g_nss_singleton.Get().InitializeNSSForChromeOSUser(username_hash, 929 path); 930 } 931 932 bool ShouldInitializeTPMForChromeOSUser(const std::string& username_hash) { 933 return g_nss_singleton.Get().ShouldInitializeTPMForChromeOSUser( 934 username_hash); 935 } 936 937 void WillInitializeTPMForChromeOSUser(const std::string& username_hash) { 938 g_nss_singleton.Get().WillInitializeTPMForChromeOSUser(username_hash); 939 } 940 941 void InitializeTPMForChromeOSUser( 942 const std::string& username_hash, 943 CK_SLOT_ID slot_id) { 944 g_nss_singleton.Get().InitializeTPMForChromeOSUser(username_hash, slot_id); 945 } 946 947 void InitializePrivateSoftwareSlotForChromeOSUser( 948 const std::string& username_hash) { 949 g_nss_singleton.Get().InitializePrivateSoftwareSlotForChromeOSUser( 950 username_hash); 951 } 952 953 ScopedPK11Slot GetPublicSlotForChromeOSUser(const std::string& username_hash) { 954 return g_nss_singleton.Get().GetPublicSlotForChromeOSUser(username_hash); 955 } 956 957 ScopedPK11Slot GetPrivateSlotForChromeOSUser( 958 const std::string& username_hash, 959 const base::Callback<void(ScopedPK11Slot)>& callback) { 960 return g_nss_singleton.Get().GetPrivateSlotForChromeOSUser(username_hash, 961 callback); 962 } 963 964 void CloseChromeOSUserForTesting(const std::string& username_hash) { 965 g_nss_singleton.Get().CloseChromeOSUserForTesting(username_hash); 966 } 967 #endif // defined(OS_CHROMEOS) 968 969 base::Time PRTimeToBaseTime(PRTime prtime) { 970 return base::Time::FromInternalValue( 971 prtime + base::Time::UnixEpoch().ToInternalValue()); 972 } 973 974 PRTime BaseTimeToPRTime(base::Time time) { 975 return time.ToInternalValue() - base::Time::UnixEpoch().ToInternalValue(); 976 } 977 978 #if !defined(OS_CHROMEOS) 979 PK11SlotInfo* GetPersistentNSSKeySlot() { 980 return g_nss_singleton.Get().GetPersistentNSSKeySlot(); 981 } 982 #endif 983 984 } // namespace crypto 985