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