nss_util.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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/callback.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/file_util.h" 31#include "base/files/file_path.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/stl_util.h" 39#include "base/strings/stringprintf.h" 40#include "base/threading/thread_checker.h" 41#include "base/threading/thread_restrictions.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 file_util::FileSystemType fs_type = file_util::FILE_SYSTEM_UNKNOWN; 150 if (file_util::GetFileSystemType(database_dir, &fs_type)) 151 db_on_nfs = (fs_type == file_util::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 void OpenPersistentNSSDB() { 269 DCHECK(thread_checker_.CalledOnValidThread()); 270 271 if (!chromeos_user_logged_in_) { 272 // GetDefaultConfigDirectory causes us to do blocking IO on UI thread. 273 // Temporarily allow it until we fix http://crbug.com/70119 274 base::ThreadRestrictions::ScopedAllowIO allow_io; 275 chromeos_user_logged_in_ = true; 276 277 // This creates another DB slot in NSS that is read/write, unlike 278 // the fake root CA cert DB and the "default" crypto key 279 // provider, which are still read-only (because we initialized 280 // NSS before we had a cryptohome mounted). 281 software_slot_ = OpenUserDB(GetDefaultConfigDirectory(), 282 kNSSDatabaseName); 283 } 284 } 285 286 PK11SlotInfo* OpenPersistentNSSDBForPath(const base::FilePath& path) { 287 DCHECK(thread_checker_.CalledOnValidThread()); 288 // NSS is allowed to do IO on the current thread since dispatching 289 // to a dedicated thread would still have the affect of blocking 290 // the current thread, due to NSS's internal locking requirements 291 base::ThreadRestrictions::ScopedAllowIO allow_io; 292 293 base::FilePath nssdb_path = path.AppendASCII(".pki").AppendASCII("nssdb"); 294 if (!base::CreateDirectory(nssdb_path)) { 295 LOG(ERROR) << "Failed to create " << nssdb_path.value() << " directory."; 296 return NULL; 297 } 298 return OpenUserDB(nssdb_path, kNSSDatabaseName); 299 } 300 301 void EnableTPMTokenForNSS() { 302 DCHECK(thread_checker_.CalledOnValidThread()); 303 304 // If this gets set, then we'll use the TPM for certs with 305 // private keys, otherwise we'll fall back to the software 306 // implementation. 307 tpm_token_enabled_for_nss_ = true; 308 } 309 310 bool IsTPMTokenEnabledForNSS() { 311 DCHECK(thread_checker_.CalledOnValidThread()); 312 return tpm_token_enabled_for_nss_; 313 } 314 315 bool InitializeTPMToken(int token_slot_id) { 316 DCHECK(thread_checker_.CalledOnValidThread()); 317 318 // If EnableTPMTokenForNSS hasn't been called, return false. 319 if (!tpm_token_enabled_for_nss_) 320 return false; 321 322 // If everything is already initialized, then return true. 323 if (chaps_module_ && tpm_slot_) 324 return true; 325 326 // This tries to load the Chaps module so NSS can talk to the hardware 327 // TPM. 328 if (!chaps_module_) { 329 chaps_module_ = LoadModule( 330 kChapsModuleName, 331 kChapsPath, 332 // For more details on these parameters, see: 333 // https://developer.mozilla.org/en/PKCS11_Module_Specs 334 // slotFlags=[PublicCerts] -- Certificates and public keys can be 335 // read from this slot without requiring a call to C_Login. 336 // askpw=only -- Only authenticate to the token when necessary. 337 "NSS=\"slotParams=(0={slotFlags=[PublicCerts] askpw=only})\""); 338 if (!chaps_module_ && test_slot_) { 339 // chromeos_unittests try to test the TPM initialization process. If we 340 // have a test DB open, pretend that it is the TPM slot. 341 tpm_slot_ = PK11_ReferenceSlot(test_slot_); 342 return true; 343 } 344 } 345 if (chaps_module_){ 346 tpm_slot_ = GetTPMSlotForId(token_slot_id); 347 348 if (!tpm_slot_) 349 return false; 350 351 TPMReadyCallbackList callback_list; 352 callback_list.swap(tpm_ready_callback_list_); 353 for (TPMReadyCallbackList::iterator i = 354 callback_list.begin(); 355 i != callback_list.end(); 356 ++i) { 357 (*i).Run(); 358 } 359 360 return true; 361 } 362 return false; 363 } 364 365 bool IsTPMTokenReady(const base::Closure& callback) { 366 if (!callback.is_null()) { 367 // Cannot DCHECK in the general case yet, but since the callback is 368 // a new addition to the API, DCHECK to make sure at least the new uses 369 // don't regress. 370 DCHECK(thread_checker_.CalledOnValidThread()); 371 } else if (!thread_checker_.CalledOnValidThread()) { 372 // TODO(mattm): Change to DCHECK when callers have been fixed. 373 DVLOG(1) << "Called on wrong thread.\n" 374 << base::debug::StackTrace().ToString(); 375 } 376 377 if (tpm_slot_ != NULL) 378 return true; 379 380 if (!callback.is_null()) 381 tpm_ready_callback_list_.push_back(callback); 382 383 return false; 384 } 385 386 // Note that CK_SLOT_ID is an unsigned long, but cryptohome gives us the slot 387 // id as an int. This should be safe since this is only used with chaps, which 388 // we also control. 389 PK11SlotInfo* GetTPMSlotForId(CK_SLOT_ID slot_id) { 390 DCHECK(thread_checker_.CalledOnValidThread()); 391 392 if (!chaps_module_) 393 return NULL; 394 395 DVLOG(3) << "Poking chaps module."; 396 SECStatus rv = SECMOD_UpdateSlotList(chaps_module_); 397 if (rv != SECSuccess) 398 PLOG(ERROR) << "SECMOD_UpdateSlotList failed: " << PORT_GetError(); 399 400 PK11SlotInfo* slot = SECMOD_LookupSlot(chaps_module_->moduleID, slot_id); 401 if (!slot) 402 LOG(ERROR) << "TPM slot " << slot_id << " not found."; 403 return slot; 404 } 405 406 bool InitializeNSSForChromeOSUser( 407 const std::string& email, 408 const std::string& username_hash, 409 bool is_primary_user, 410 const base::FilePath& path) { 411 DCHECK(thread_checker_.CalledOnValidThread()); 412 if (chromeos_user_map_.find(username_hash) != chromeos_user_map_.end()) { 413 // This user already exists in our mapping. 414 DVLOG(2) << username_hash << " already initialized."; 415 return false; 416 } 417 ScopedPK11Slot public_slot; 418 if (is_primary_user) { 419 DVLOG(2) << "Primary user, using GetPublicNSSKeySlot()"; 420 public_slot.reset(GetPublicNSSKeySlot()); 421 } else { 422 DVLOG(2) << "Opening NSS DB " << path.value(); 423 public_slot.reset(OpenPersistentNSSDBForPath(path)); 424 } 425 chromeos_user_map_[username_hash] = 426 new ChromeOSUserData(public_slot.Pass(), is_primary_user); 427 return true; 428 } 429 430 void InitializeTPMForChromeOSUser(const std::string& username_hash, 431 CK_SLOT_ID slot_id) { 432 DCHECK(thread_checker_.CalledOnValidThread()); 433 DCHECK(chromeos_user_map_.find(username_hash) != chromeos_user_map_.end()); 434 chromeos_user_map_[username_hash] 435 ->SetPrivateSlot(ScopedPK11Slot(GetTPMSlotForId(slot_id))); 436 } 437 438 void InitializePrivateSoftwareSlotForChromeOSUser( 439 const std::string& username_hash) { 440 DCHECK(thread_checker_.CalledOnValidThread()); 441 VLOG(1) << "using software private slot for " << username_hash; 442 DCHECK(chromeos_user_map_.find(username_hash) != chromeos_user_map_.end()); 443 chromeos_user_map_[username_hash]->SetPrivateSlot( 444 chromeos_user_map_[username_hash]->GetPublicSlot()); 445 } 446 447 ScopedPK11Slot GetPublicSlotForChromeOSUser( 448 const std::string& username_hash) { 449 DCHECK(thread_checker_.CalledOnValidThread()); 450 451 if (username_hash.empty()) { 452 DVLOG(2) << "empty username_hash"; 453 return ScopedPK11Slot(); 454 } 455 456 if (test_slot_) { 457 DVLOG(2) << "returning test_slot_ for " << username_hash; 458 return ScopedPK11Slot(PK11_ReferenceSlot(test_slot_)); 459 } 460 461 if (chromeos_user_map_.find(username_hash) == chromeos_user_map_.end()) { 462 LOG(ERROR) << username_hash << " not initialized."; 463 return ScopedPK11Slot(); 464 } 465 return chromeos_user_map_[username_hash]->GetPublicSlot(); 466 } 467 468 ScopedPK11Slot GetPrivateSlotForChromeOSUser( 469 const std::string& username_hash, 470 const base::Callback<void(ScopedPK11Slot)>& callback) { 471 DCHECK(thread_checker_.CalledOnValidThread()); 472 473 if (username_hash.empty()) { 474 DVLOG(2) << "empty username_hash"; 475 if (!callback.is_null()) { 476 base::MessageLoop::current()->PostTask( 477 FROM_HERE, base::Bind(callback, base::Passed(ScopedPK11Slot()))); 478 } 479 return ScopedPK11Slot(); 480 } 481 482 DCHECK(chromeos_user_map_.find(username_hash) != chromeos_user_map_.end()); 483 484 if (test_slot_) { 485 DVLOG(2) << "returning test_slot_ for " << username_hash; 486 return ScopedPK11Slot(PK11_ReferenceSlot(test_slot_)); 487 } 488 489 return chromeos_user_map_[username_hash]->GetPrivateSlot(callback); 490 } 491 492 void CloseTestChromeOSUser(const std::string& username_hash) { 493 DCHECK(thread_checker_.CalledOnValidThread()); 494 ChromeOSUserMap::iterator i = chromeos_user_map_.find(username_hash); 495 DCHECK(i != chromeos_user_map_.end()); 496 delete i->second; 497 chromeos_user_map_.erase(i); 498 } 499#endif // defined(OS_CHROMEOS) 500 501 502 bool OpenTestNSSDB() { 503 DCHECK(thread_checker_.CalledOnValidThread()); 504 // NSS is allowed to do IO on the current thread since dispatching 505 // to a dedicated thread would still have the affect of blocking 506 // the current thread, due to NSS's internal locking requirements 507 base::ThreadRestrictions::ScopedAllowIO allow_io; 508 509 if (test_slot_) 510 return true; 511 if (!g_test_nss_db_dir.Get().CreateUniqueTempDir()) 512 return false; 513 test_slot_ = OpenUserDB(g_test_nss_db_dir.Get().path(), kTestTPMTokenName); 514 return !!test_slot_; 515 } 516 517 void CloseTestNSSDB() { 518 DCHECK(thread_checker_.CalledOnValidThread()); 519 // NSS is allowed to do IO on the current thread since dispatching 520 // to a dedicated thread would still have the affect of blocking 521 // the current thread, due to NSS's internal locking requirements 522 base::ThreadRestrictions::ScopedAllowIO allow_io; 523 524 if (!test_slot_) 525 return; 526 SECStatus status = SECMOD_CloseUserDB(test_slot_); 527 if (status != SECSuccess) 528 PLOG(ERROR) << "SECMOD_CloseUserDB failed: " << PORT_GetError(); 529 PK11_FreeSlot(test_slot_); 530 test_slot_ = NULL; 531 ignore_result(g_test_nss_db_dir.Get().Delete()); 532 } 533 534 PK11SlotInfo* GetPublicNSSKeySlot() { 535 // TODO(mattm): Change to DCHECK when callers have been fixed. 536 if (!thread_checker_.CalledOnValidThread()) { 537 DVLOG(1) << "Called on wrong thread.\n" 538 << base::debug::StackTrace().ToString(); 539 } 540 541 if (test_slot_) 542 return PK11_ReferenceSlot(test_slot_); 543 if (software_slot_) 544 return PK11_ReferenceSlot(software_slot_); 545 return PK11_GetInternalKeySlot(); 546 } 547 548 PK11SlotInfo* GetPrivateNSSKeySlot() { 549 // TODO(mattm): Change to DCHECK when callers have been fixed. 550 if (!thread_checker_.CalledOnValidThread()) { 551 DVLOG(1) << "Called on wrong thread.\n" 552 << base::debug::StackTrace().ToString(); 553 } 554 555 if (test_slot_) 556 return PK11_ReferenceSlot(test_slot_); 557 558#if defined(OS_CHROMEOS) 559 if (tpm_token_enabled_for_nss_) { 560 if (IsTPMTokenReady(base::Closure())) { 561 return PK11_ReferenceSlot(tpm_slot_); 562 } else { 563 // If we were supposed to get the hardware token, but were 564 // unable to, return NULL rather than fall back to sofware. 565 return NULL; 566 } 567 } 568#endif 569 // If we weren't supposed to enable the TPM for NSS, then return 570 // the software slot. 571 if (software_slot_) 572 return PK11_ReferenceSlot(software_slot_); 573 return PK11_GetInternalKeySlot(); 574 } 575 576#if defined(USE_NSS) 577 base::Lock* write_lock() { 578 return &write_lock_; 579 } 580#endif // defined(USE_NSS) 581 582 // This method is used to force NSS to be initialized without a DB. 583 // Call this method before NSSInitSingleton() is constructed. 584 static void ForceNoDBInit() { 585 force_nodb_init_ = true; 586 } 587 588 private: 589 friend struct base::DefaultLazyInstanceTraits<NSSInitSingleton>; 590 591 NSSInitSingleton() 592 : tpm_token_enabled_for_nss_(false), 593 chaps_module_(NULL), 594 software_slot_(NULL), 595 test_slot_(NULL), 596 tpm_slot_(NULL), 597 root_(NULL), 598 chromeos_user_logged_in_(false) { 599 base::TimeTicks start_time = base::TimeTicks::Now(); 600 601 // It's safe to construct on any thread, since LazyInstance will prevent any 602 // other threads from accessing until the constructor is done. 603 thread_checker_.DetachFromThread(); 604 605 DisableAESNIIfNeeded(); 606 607 EnsureNSPRInit(); 608 609 // We *must* have NSS >= 3.14.3. 610 COMPILE_ASSERT( 611 (NSS_VMAJOR == 3 && NSS_VMINOR == 14 && NSS_VPATCH >= 3) || 612 (NSS_VMAJOR == 3 && NSS_VMINOR > 14) || 613 (NSS_VMAJOR > 3), 614 nss_version_check_failed); 615 // Also check the run-time NSS version. 616 // NSS_VersionCheck is a >= check, not strict equality. 617 if (!NSS_VersionCheck("3.14.3")) { 618 LOG(FATAL) << "NSS_VersionCheck(\"3.14.3\") failed. NSS >= 3.14.3 is " 619 "required. Please upgrade to the latest NSS, and if you " 620 "still get this error, contact your distribution " 621 "maintainer."; 622 } 623 624 SECStatus status = SECFailure; 625 bool nodb_init = force_nodb_init_; 626 627#if !defined(USE_NSS) 628 // Use the system certificate store, so initialize NSS without database. 629 nodb_init = true; 630#endif 631 632 if (nodb_init) { 633 status = NSS_NoDB_Init(NULL); 634 if (status != SECSuccess) { 635 CrashOnNSSInitFailure(); 636 return; 637 } 638#if defined(OS_IOS) 639 root_ = InitDefaultRootCerts(); 640#endif // defined(OS_IOS) 641 } else { 642#if defined(USE_NSS) 643 base::FilePath database_dir = GetInitialConfigDirectory(); 644 if (!database_dir.empty()) { 645 // This duplicates the work which should have been done in 646 // EarlySetupForNSSInit. However, this function is idempotent so 647 // there's no harm done. 648 UseLocalCacheOfNSSDatabaseIfNFS(database_dir); 649 650 // Initialize with a persistent database (likely, ~/.pki/nssdb). 651 // Use "sql:" which can be shared by multiple processes safely. 652 std::string nss_config_dir = 653 base::StringPrintf("sql:%s", database_dir.value().c_str()); 654#if defined(OS_CHROMEOS) 655 status = NSS_Init(nss_config_dir.c_str()); 656#else 657 status = NSS_InitReadWrite(nss_config_dir.c_str()); 658#endif 659 if (status != SECSuccess) { 660 LOG(ERROR) << "Error initializing NSS with a persistent " 661 "database (" << nss_config_dir 662 << "): " << GetNSSErrorMessage(); 663 } 664 } 665 if (status != SECSuccess) { 666 VLOG(1) << "Initializing NSS without a persistent database."; 667 status = NSS_NoDB_Init(NULL); 668 if (status != SECSuccess) { 669 CrashOnNSSInitFailure(); 670 return; 671 } 672 } 673 674 PK11_SetPasswordFunc(PKCS11PasswordFunc); 675 676 // If we haven't initialized the password for the NSS databases, 677 // initialize an empty-string password so that we don't need to 678 // log in. 679 PK11SlotInfo* slot = PK11_GetInternalKeySlot(); 680 if (slot) { 681 // PK11_InitPin may write to the keyDB, but no other thread can use NSS 682 // yet, so we don't need to lock. 683 if (PK11_NeedUserInit(slot)) 684 PK11_InitPin(slot, NULL, NULL); 685 PK11_FreeSlot(slot); 686 } 687 688 root_ = InitDefaultRootCerts(); 689#endif // defined(USE_NSS) 690 } 691 692 // Disable MD5 certificate signatures. (They are disabled by default in 693 // NSS 3.14.) 694 NSS_SetAlgorithmPolicy(SEC_OID_MD5, 0, NSS_USE_ALG_IN_CERT_SIGNATURE); 695 NSS_SetAlgorithmPolicy(SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION, 696 0, NSS_USE_ALG_IN_CERT_SIGNATURE); 697 698 // The UMA bit is conditionally set for this histogram in 699 // chrome/common/startup_metric_utils.cc . 700 HISTOGRAM_CUSTOM_TIMES("Startup.SlowStartupNSSInit", 701 base::TimeTicks::Now() - start_time, 702 base::TimeDelta::FromMilliseconds(10), 703 base::TimeDelta::FromHours(1), 704 50); 705 } 706 707 // NOTE(willchan): We don't actually execute this code since we leak NSS to 708 // prevent non-joinable threads from using NSS after it's already been shut 709 // down. 710 ~NSSInitSingleton() { 711#if defined(OS_CHROMEOS) 712 STLDeleteValues(&chromeos_user_map_); 713#endif 714 if (tpm_slot_) { 715 PK11_FreeSlot(tpm_slot_); 716 tpm_slot_ = NULL; 717 } 718 if (software_slot_) { 719 SECMOD_CloseUserDB(software_slot_); 720 PK11_FreeSlot(software_slot_); 721 software_slot_ = NULL; 722 } 723 CloseTestNSSDB(); 724 if (root_) { 725 SECMOD_UnloadUserModule(root_); 726 SECMOD_DestroyModule(root_); 727 root_ = NULL; 728 } 729 if (chaps_module_) { 730 SECMOD_UnloadUserModule(chaps_module_); 731 SECMOD_DestroyModule(chaps_module_); 732 chaps_module_ = NULL; 733 } 734 735 SECStatus status = NSS_Shutdown(); 736 if (status != SECSuccess) { 737 // We VLOG(1) because this failure is relatively harmless (leaking, but 738 // we're shutting down anyway). 739 VLOG(1) << "NSS_Shutdown failed; see http://crbug.com/4609"; 740 } 741 } 742 743#if defined(USE_NSS) || defined(OS_IOS) 744 // Load nss's built-in root certs. 745 SECMODModule* InitDefaultRootCerts() { 746 SECMODModule* root = LoadModule("Root Certs", "libnssckbi.so", NULL); 747 if (root) 748 return root; 749 750 // Aw, snap. Can't find/load root cert shared library. 751 // This will make it hard to talk to anybody via https. 752 // TODO(mattm): Re-add the NOTREACHED here when crbug.com/310972 is fixed. 753 return NULL; 754 } 755 756 // Load the given module for this NSS session. 757 SECMODModule* LoadModule(const char* name, 758 const char* library_path, 759 const char* params) { 760 std::string modparams = base::StringPrintf( 761 "name=\"%s\" library=\"%s\" %s", 762 name, library_path, params ? params : ""); 763 764 // Shouldn't need to const_cast here, but SECMOD doesn't properly 765 // declare input string arguments as const. Bug 766 // https://bugzilla.mozilla.org/show_bug.cgi?id=642546 was filed 767 // on NSS codebase to address this. 768 SECMODModule* module = SECMOD_LoadUserModule( 769 const_cast<char*>(modparams.c_str()), NULL, PR_FALSE); 770 if (!module) { 771 LOG(ERROR) << "Error loading " << name << " module into NSS: " 772 << GetNSSErrorMessage(); 773 return NULL; 774 } 775 if (!module->loaded) { 776 LOG(ERROR) << "After loading " << name << ", loaded==false: " 777 << GetNSSErrorMessage(); 778 SECMOD_DestroyModule(module); 779 return NULL; 780 } 781 return module; 782 } 783#endif 784 785 static PK11SlotInfo* OpenUserDB(const base::FilePath& path, 786 const char* description) { 787 const std::string modspec = 788 base::StringPrintf("configDir='sql:%s' tokenDescription='%s'", 789 path.value().c_str(), description); 790 PK11SlotInfo* db_slot = SECMOD_OpenUserDB(modspec.c_str()); 791 if (db_slot) { 792 if (PK11_NeedUserInit(db_slot)) 793 PK11_InitPin(db_slot, NULL, NULL); 794 } 795 else { 796 LOG(ERROR) << "Error opening persistent database (" << modspec 797 << "): " << GetNSSErrorMessage(); 798 } 799 return db_slot; 800 } 801 802 static void DisableAESNIIfNeeded() { 803 if (NSS_VersionCheck("3.15") && !NSS_VersionCheck("3.15.4")) { 804 // Some versions of NSS have a bug that causes AVX instructions to be 805 // used without testing whether XSAVE is enabled by the operating system. 806 // In order to work around this, we disable AES-NI in NSS when we find 807 // that |has_avx()| is false (which includes the XSAVE test). See 808 // https://bugzilla.mozilla.org/show_bug.cgi?id=940794 809 base::CPU cpu; 810 811 if (cpu.has_avx_hardware() && !cpu.has_avx()) { 812 base::Environment::Create()->SetVar("NSS_DISABLE_HW_AES", "1"); 813 } 814 } 815 } 816 817 // If this is set to true NSS is forced to be initialized without a DB. 818 static bool force_nodb_init_; 819 820 bool tpm_token_enabled_for_nss_; 821 typedef std::vector<base::Closure> TPMReadyCallbackList; 822 TPMReadyCallbackList tpm_ready_callback_list_; 823 SECMODModule* chaps_module_; 824 PK11SlotInfo* software_slot_; 825 PK11SlotInfo* test_slot_; 826 PK11SlotInfo* tpm_slot_; 827 SECMODModule* root_; 828 bool chromeos_user_logged_in_; 829#if defined(OS_CHROMEOS) 830 typedef std::map<std::string, ChromeOSUserData*> ChromeOSUserMap; 831 ChromeOSUserMap chromeos_user_map_; 832#endif 833#if defined(USE_NSS) 834 // TODO(davidben): When https://bugzilla.mozilla.org/show_bug.cgi?id=564011 835 // is fixed, we will no longer need the lock. 836 base::Lock write_lock_; 837#endif // defined(USE_NSS) 838 839 base::ThreadChecker thread_checker_; 840}; 841 842// static 843bool NSSInitSingleton::force_nodb_init_ = false; 844 845base::LazyInstance<NSSInitSingleton>::Leaky 846 g_nss_singleton = LAZY_INSTANCE_INITIALIZER; 847} // namespace 848 849const char kTestTPMTokenName[] = "Test DB"; 850 851#if defined(USE_NSS) 852void EarlySetupForNSSInit() { 853 base::FilePath database_dir = GetInitialConfigDirectory(); 854 if (!database_dir.empty()) 855 UseLocalCacheOfNSSDatabaseIfNFS(database_dir); 856} 857#endif 858 859void EnsureNSPRInit() { 860 g_nspr_singleton.Get(); 861} 862 863void InitNSSSafely() { 864 // We might fork, but we haven't loaded any security modules. 865 DisableNSSForkCheck(); 866 // If we're sandboxed, we shouldn't be able to open user security modules, 867 // but it's more correct to tell NSS to not even try. 868 // Loading user security modules would have security implications. 869 ForceNSSNoDBInit(); 870 // Initialize NSS. 871 EnsureNSSInit(); 872} 873 874void EnsureNSSInit() { 875 // Initializing SSL causes us to do blocking IO. 876 // Temporarily allow it until we fix 877 // http://code.google.com/p/chromium/issues/detail?id=59847 878 base::ThreadRestrictions::ScopedAllowIO allow_io; 879 g_nss_singleton.Get(); 880} 881 882void ForceNSSNoDBInit() { 883 NSSInitSingleton::ForceNoDBInit(); 884} 885 886void DisableNSSForkCheck() { 887 scoped_ptr<base::Environment> env(base::Environment::Create()); 888 env->SetVar("NSS_STRICT_NOFORK", "DISABLED"); 889} 890 891void LoadNSSLibraries() { 892 // Some NSS libraries are linked dynamically so load them here. 893#if defined(USE_NSS) 894 // Try to search for multiple directories to load the libraries. 895 std::vector<base::FilePath> paths; 896 897 // Use relative path to Search PATH for the library files. 898 paths.push_back(base::FilePath()); 899 900 // For Debian derivatives NSS libraries are located here. 901 paths.push_back(base::FilePath("/usr/lib/nss")); 902 903 // Ubuntu 11.10 (Oneiric) and Debian Wheezy place the libraries here. 904#if defined(ARCH_CPU_X86_64) 905 paths.push_back(base::FilePath("/usr/lib/x86_64-linux-gnu/nss")); 906#elif defined(ARCH_CPU_X86) 907 paths.push_back(base::FilePath("/usr/lib/i386-linux-gnu/nss")); 908#elif defined(ARCH_CPU_ARMEL) 909#if defined(__ARM_PCS_VFP) 910 paths.push_back(base::FilePath("/usr/lib/arm-linux-gnueabihf/nss")); 911#else 912 paths.push_back(base::FilePath("/usr/lib/arm-linux-gnueabi/nss")); 913#endif // defined(__ARM_PCS_VFP) 914#elif defined(ARCH_CPU_MIPSEL) 915 paths.push_back(base::FilePath("/usr/lib/mipsel-linux-gnu/nss")); 916#endif // defined(ARCH_CPU_X86_64) 917 918 // A list of library files to load. 919 std::vector<std::string> libs; 920 libs.push_back("libsoftokn3.so"); 921 libs.push_back("libfreebl3.so"); 922 923 // For each combination of library file and path, check for existence and 924 // then load. 925 size_t loaded = 0; 926 for (size_t i = 0; i < libs.size(); ++i) { 927 for (size_t j = 0; j < paths.size(); ++j) { 928 base::FilePath path = paths[j].Append(libs[i]); 929 base::NativeLibrary lib = base::LoadNativeLibrary(path, NULL); 930 if (lib) { 931 ++loaded; 932 break; 933 } 934 } 935 } 936 937 if (loaded == libs.size()) { 938 VLOG(3) << "NSS libraries loaded."; 939 } else { 940 LOG(ERROR) << "Failed to load NSS libraries."; 941 } 942#endif // defined(USE_NSS) 943} 944 945bool CheckNSSVersion(const char* version) { 946 return !!NSS_VersionCheck(version); 947} 948 949#if defined(USE_NSS) 950ScopedTestNSSDB::ScopedTestNSSDB() 951 : is_open_(g_nss_singleton.Get().OpenTestNSSDB()) { 952} 953 954ScopedTestNSSDB::~ScopedTestNSSDB() { 955 // Don't close when NSS is < 3.15.1, because it would require an additional 956 // sleep for 1 second after closing the database, due to 957 // http://bugzil.la/875601. 958 if (NSS_VersionCheck("3.15.1")) { 959 g_nss_singleton.Get().CloseTestNSSDB(); 960 } 961} 962 963base::Lock* GetNSSWriteLock() { 964 return g_nss_singleton.Get().write_lock(); 965} 966 967AutoNSSWriteLock::AutoNSSWriteLock() : lock_(GetNSSWriteLock()) { 968 // May be NULL if the lock is not needed in our version of NSS. 969 if (lock_) 970 lock_->Acquire(); 971} 972 973AutoNSSWriteLock::~AutoNSSWriteLock() { 974 if (lock_) { 975 lock_->AssertAcquired(); 976 lock_->Release(); 977 } 978} 979 980AutoSECMODListReadLock::AutoSECMODListReadLock() 981 : lock_(SECMOD_GetDefaultModuleListLock()) { 982 SECMOD_GetReadLock(lock_); 983 } 984 985AutoSECMODListReadLock::~AutoSECMODListReadLock() { 986 SECMOD_ReleaseReadLock(lock_); 987} 988 989#endif // defined(USE_NSS) 990 991#if defined(OS_CHROMEOS) 992void OpenPersistentNSSDB() { 993 g_nss_singleton.Get().OpenPersistentNSSDB(); 994} 995 996void EnableTPMTokenForNSS() { 997 g_nss_singleton.Get().EnableTPMTokenForNSS(); 998} 999 1000bool IsTPMTokenEnabledForNSS() { 1001 return g_nss_singleton.Get().IsTPMTokenEnabledForNSS(); 1002} 1003 1004bool IsTPMTokenReady(const base::Closure& callback) { 1005 return g_nss_singleton.Get().IsTPMTokenReady(callback); 1006} 1007 1008bool InitializeTPMToken(int token_slot_id) { 1009 return g_nss_singleton.Get().InitializeTPMToken(token_slot_id); 1010} 1011 1012ScopedTestNSSChromeOSUser::ScopedTestNSSChromeOSUser( 1013 const std::string& username_hash) 1014 : username_hash_(username_hash), constructed_successfully_(false) { 1015 if (!temp_dir_.CreateUniqueTempDir()) 1016 return; 1017 constructed_successfully_ = 1018 InitializeNSSForChromeOSUser(username_hash, 1019 username_hash, 1020 false /* is_primary_user */, 1021 temp_dir_.path()); 1022} 1023 1024ScopedTestNSSChromeOSUser::~ScopedTestNSSChromeOSUser() { 1025 if (constructed_successfully_) 1026 g_nss_singleton.Get().CloseTestChromeOSUser(username_hash_); 1027} 1028 1029void ScopedTestNSSChromeOSUser::FinishInit() { 1030 InitializePrivateSoftwareSlotForChromeOSUser(username_hash_); 1031} 1032 1033bool InitializeNSSForChromeOSUser( 1034 const std::string& email, 1035 const std::string& username_hash, 1036 bool is_primary_user, 1037 const base::FilePath& path) { 1038 return g_nss_singleton.Get().InitializeNSSForChromeOSUser( 1039 email, username_hash, is_primary_user, path); 1040} 1041void InitializeTPMForChromeOSUser( 1042 const std::string& username_hash, 1043 CK_SLOT_ID slot_id) { 1044 g_nss_singleton.Get().InitializeTPMForChromeOSUser(username_hash, slot_id); 1045} 1046void InitializePrivateSoftwareSlotForChromeOSUser( 1047 const std::string& username_hash) { 1048 g_nss_singleton.Get().InitializePrivateSoftwareSlotForChromeOSUser( 1049 username_hash); 1050} 1051ScopedPK11Slot GetPublicSlotForChromeOSUser(const std::string& username_hash) { 1052 return g_nss_singleton.Get().GetPublicSlotForChromeOSUser(username_hash); 1053} 1054ScopedPK11Slot GetPrivateSlotForChromeOSUser( 1055 const std::string& username_hash, 1056 const base::Callback<void(ScopedPK11Slot)>& callback) { 1057 return g_nss_singleton.Get().GetPrivateSlotForChromeOSUser(username_hash, 1058 callback); 1059} 1060#endif // defined(OS_CHROMEOS) 1061 1062base::Time PRTimeToBaseTime(PRTime prtime) { 1063 return base::Time::FromInternalValue( 1064 prtime + base::Time::UnixEpoch().ToInternalValue()); 1065} 1066 1067PRTime BaseTimeToPRTime(base::Time time) { 1068 return time.ToInternalValue() - base::Time::UnixEpoch().ToInternalValue(); 1069} 1070 1071PK11SlotInfo* GetPublicNSSKeySlot() { 1072 return g_nss_singleton.Get().GetPublicNSSKeySlot(); 1073} 1074 1075PK11SlotInfo* GetPrivateNSSKeySlot() { 1076 return g_nss_singleton.Get().GetPrivateNSSKeySlot(); 1077} 1078 1079} // namespace crypto 1080