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