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