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