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