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