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