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