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