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 <vector>
     25 
     26 #include "base/debug/alias.h"
     27 #include "base/environment.h"
     28 #include "base/file_util.h"
     29 #include "base/files/file_path.h"
     30 #include "base/files/scoped_temp_dir.h"
     31 #include "base/lazy_instance.h"
     32 #include "base/logging.h"
     33 #include "base/memory/scoped_ptr.h"
     34 #include "base/metrics/histogram.h"
     35 #include "base/native_library.h"
     36 #include "base/strings/stringprintf.h"
     37 #include "base/threading/thread_restrictions.h"
     38 #include "build/build_config.h"
     39 
     40 // USE_NSS means we use NSS for everything crypto-related.  If USE_NSS is not
     41 // defined, such as on Mac and Windows, we use NSS for SSL only -- we don't
     42 // use NSS for crypto or certificate verification, and we don't use the NSS
     43 // certificate and key databases.
     44 #if defined(USE_NSS)
     45 #include "base/synchronization/lock.h"
     46 #include "crypto/crypto_module_blocking_password_delegate.h"
     47 #endif  // defined(USE_NSS)
     48 
     49 namespace crypto {
     50 
     51 namespace {
     52 
     53 #if defined(OS_CHROMEOS)
     54 const char kNSSDatabaseName[] = "Real NSS database";
     55 
     56 // Constants for loading the Chrome OS TPM-backed PKCS #11 library.
     57 const char kChapsModuleName[] = "Chaps";
     58 const char kChapsPath[] = "libchaps.so";
     59 
     60 // Fake certificate authority database used for testing.
     61 static const base::FilePath::CharType kReadOnlyCertDB[] =
     62     FILE_PATH_LITERAL("/etc/fake_root_ca/nssdb");
     63 #endif  // defined(OS_CHROMEOS)
     64 
     65 std::string GetNSSErrorMessage() {
     66   std::string result;
     67   if (PR_GetErrorTextLength()) {
     68     scoped_ptr<char[]> error_text(new char[PR_GetErrorTextLength() + 1]);
     69     PRInt32 copied = PR_GetErrorText(error_text.get());
     70     result = std::string(error_text.get(), copied);
     71   } else {
     72     result = base::StringPrintf("NSS error code: %d", PR_GetError());
     73   }
     74   return result;
     75 }
     76 
     77 #if defined(USE_NSS)
     78 base::FilePath GetDefaultConfigDirectory() {
     79   base::FilePath dir = file_util::GetHomeDir();
     80   if (dir.empty()) {
     81     LOG(ERROR) << "Failed to get home directory.";
     82     return dir;
     83   }
     84   dir = dir.AppendASCII(".pki").AppendASCII("nssdb");
     85   if (!file_util::CreateDirectory(dir)) {
     86     LOG(ERROR) << "Failed to create " << dir.value() << " directory.";
     87     dir.clear();
     88   }
     89   return dir;
     90 }
     91 
     92 // On non-chromeos platforms, return the default config directory.
     93 // On chromeos, return a read-only directory with fake root CA certs for testing
     94 // (which will not exist on non-testing images).  These root CA certs are used
     95 // by the local Google Accounts server mock we use when testing our login code.
     96 // If this directory is not present, NSS_Init() will fail.  It is up to the
     97 // caller to failover to NSS_NoDB_Init() at that point.
     98 base::FilePath GetInitialConfigDirectory() {
     99 #if defined(OS_CHROMEOS)
    100   return base::FilePath(kReadOnlyCertDB);
    101 #else
    102   return GetDefaultConfigDirectory();
    103 #endif  // defined(OS_CHROMEOS)
    104 }
    105 
    106 // This callback for NSS forwards all requests to a caller-specified
    107 // CryptoModuleBlockingPasswordDelegate object.
    108 char* PKCS11PasswordFunc(PK11SlotInfo* slot, PRBool retry, void* arg) {
    109 #if defined(OS_CHROMEOS)
    110   // If we get asked for a password for the TPM, then return the
    111   // well known password we use, as long as the TPM slot has been
    112   // initialized.
    113   if (crypto::IsTPMTokenReady()) {
    114     std::string token_name;
    115     std::string user_pin;
    116     crypto::GetTPMTokenInfo(&token_name, &user_pin);
    117     if (PK11_GetTokenName(slot) == token_name)
    118       return PORT_Strdup(user_pin.c_str());
    119   }
    120 #endif
    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 #if defined(OS_LINUX) || defined(OS_OPENBSD)
    153   struct statfs buf;
    154   if (statfs(database_dir.value().c_str(), &buf) == 0) {
    155 #if defined(OS_LINUX)
    156     if (buf.f_type == NFS_SUPER_MAGIC) {
    157 #elif defined(OS_OPENBSD)
    158     if (strcmp(buf.f_fstypename, MOUNT_NFS) == 0) {
    159 #endif
    160       scoped_ptr<base::Environment> env(base::Environment::Create());
    161       const char* use_cache_env_var = "NSS_SDB_USE_CACHE";
    162       if (!env->HasVar(use_cache_env_var))
    163         env->SetVar(use_cache_env_var, "yes");
    164     }
    165   }
    166 #endif  // defined(OS_LINUX) || defined(OS_OPENBSD)
    167 }
    168 
    169 PK11SlotInfo* FindSlotWithTokenName(const std::string& token_name) {
    170   AutoSECMODListReadLock auto_lock;
    171   SECMODModuleList* head = SECMOD_GetDefaultModuleList();
    172   for (SECMODModuleList* item = head; item != NULL; item = item->next) {
    173     int slot_count = item->module->loaded ? item->module->slotCount : 0;
    174     for (int i = 0; i < slot_count; i++) {
    175       PK11SlotInfo* slot = item->module->slots[i];
    176       if (PK11_GetTokenName(slot) == token_name)
    177         return PK11_ReferenceSlot(slot);
    178     }
    179   }
    180   return NULL;
    181 }
    182 
    183 #endif  // defined(USE_NSS)
    184 
    185 // A singleton to initialize/deinitialize NSPR.
    186 // Separate from the NSS singleton because we initialize NSPR on the UI thread.
    187 // Now that we're leaking the singleton, we could merge back with the NSS
    188 // singleton.
    189 class NSPRInitSingleton {
    190  private:
    191   friend struct base::DefaultLazyInstanceTraits<NSPRInitSingleton>;
    192 
    193   NSPRInitSingleton() {
    194     PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
    195   }
    196 
    197   // NOTE(willchan): We don't actually execute this code since we leak NSS to
    198   // prevent non-joinable threads from using NSS after it's already been shut
    199   // down.
    200   ~NSPRInitSingleton() {
    201     PL_ArenaFinish();
    202     PRStatus prstatus = PR_Cleanup();
    203     if (prstatus != PR_SUCCESS)
    204       LOG(ERROR) << "PR_Cleanup failed; was NSPR initialized on wrong thread?";
    205   }
    206 };
    207 
    208 base::LazyInstance<NSPRInitSingleton>::Leaky
    209     g_nspr_singleton = LAZY_INSTANCE_INITIALIZER;
    210 
    211 // This is a LazyInstance so that it will be deleted automatically when the
    212 // unittest exits.  NSSInitSingleton is a LeakySingleton, so it would not be
    213 // deleted if it were a regular member.
    214 base::LazyInstance<base::ScopedTempDir> g_test_nss_db_dir =
    215     LAZY_INSTANCE_INITIALIZER;
    216 
    217 // Force a crash with error info on NSS_NoDB_Init failure.
    218 void CrashOnNSSInitFailure() {
    219   int nss_error = PR_GetError();
    220   int os_error = PR_GetOSError();
    221   base::debug::Alias(&nss_error);
    222   base::debug::Alias(&os_error);
    223   LOG(ERROR) << "Error initializing NSS without a persistent database: "
    224              << GetNSSErrorMessage();
    225   LOG(FATAL) << "nss_error=" << nss_error << ", os_error=" << os_error;
    226 }
    227 
    228 class NSSInitSingleton {
    229  public:
    230 #if defined(OS_CHROMEOS)
    231   void OpenPersistentNSSDB() {
    232     if (!chromeos_user_logged_in_) {
    233       // GetDefaultConfigDirectory causes us to do blocking IO on UI thread.
    234       // Temporarily allow it until we fix http://crbug.com/70119
    235       base::ThreadRestrictions::ScopedAllowIO allow_io;
    236       chromeos_user_logged_in_ = true;
    237 
    238       // This creates another DB slot in NSS that is read/write, unlike
    239       // the fake root CA cert DB and the "default" crypto key
    240       // provider, which are still read-only (because we initialized
    241       // NSS before we had a cryptohome mounted).
    242       software_slot_ = OpenUserDB(GetDefaultConfigDirectory(),
    243                                   kNSSDatabaseName);
    244     }
    245   }
    246 
    247   void EnableTPMTokenForNSS() {
    248     tpm_token_enabled_for_nss_ = true;
    249   }
    250 
    251   bool InitializeTPMToken(const std::string& token_name,
    252                           const std::string& user_pin) {
    253     // If EnableTPMTokenForNSS hasn't been called, return false.
    254     if (!tpm_token_enabled_for_nss_)
    255       return false;
    256 
    257     // If everything is already initialized, then return true.
    258     if (chaps_module_ && tpm_slot_)
    259       return true;
    260 
    261     tpm_token_name_ = token_name;
    262     tpm_user_pin_ = user_pin;
    263 
    264     // This tries to load the Chaps module so NSS can talk to the hardware
    265     // TPM.
    266     if (!chaps_module_) {
    267       chaps_module_ = LoadModule(
    268           kChapsModuleName,
    269           kChapsPath,
    270           // For more details on these parameters, see:
    271           // https://developer.mozilla.org/en/PKCS11_Module_Specs
    272           // slotFlags=[PublicCerts] -- Certificates and public keys can be
    273           //   read from this slot without requiring a call to C_Login.
    274           // askpw=only -- Only authenticate to the token when necessary.
    275           "NSS=\"slotParams=(0={slotFlags=[PublicCerts] askpw=only})\"");
    276     }
    277     if (chaps_module_){
    278       // If this gets set, then we'll use the TPM for certs with
    279       // private keys, otherwise we'll fall back to the software
    280       // implementation.
    281       tpm_slot_ = GetTPMSlot();
    282 
    283       return tpm_slot_ != NULL;
    284     }
    285     return false;
    286   }
    287 
    288   void GetTPMTokenInfo(std::string* token_name, std::string* user_pin) {
    289     if (!tpm_token_enabled_for_nss_) {
    290       LOG(ERROR) << "GetTPMTokenInfo called before TPM Token is ready.";
    291       return;
    292     }
    293     if (token_name)
    294       *token_name = tpm_token_name_;
    295     if (user_pin)
    296       *user_pin = tpm_user_pin_;
    297   }
    298 
    299   bool IsTPMTokenReady() {
    300     return tpm_slot_ != NULL;
    301   }
    302 
    303   PK11SlotInfo* GetTPMSlot() {
    304     std::string token_name;
    305     GetTPMTokenInfo(&token_name, NULL);
    306     return FindSlotWithTokenName(token_name);
    307   }
    308 #endif  // defined(OS_CHROMEOS)
    309 
    310 
    311   bool OpenTestNSSDB() {
    312     if (test_slot_)
    313       return true;
    314     if (!g_test_nss_db_dir.Get().CreateUniqueTempDir())
    315       return false;
    316     test_slot_ = OpenUserDB(g_test_nss_db_dir.Get().path(), kTestTPMTokenName);
    317     return !!test_slot_;
    318   }
    319 
    320   void CloseTestNSSDB() {
    321     if (!test_slot_)
    322       return;
    323     SECStatus status = SECMOD_CloseUserDB(test_slot_);
    324     if (status != SECSuccess)
    325       PLOG(ERROR) << "SECMOD_CloseUserDB failed: " << PORT_GetError();
    326     PK11_FreeSlot(test_slot_);
    327     test_slot_ = NULL;
    328     ignore_result(g_test_nss_db_dir.Get().Delete());
    329   }
    330 
    331   PK11SlotInfo* GetPublicNSSKeySlot() {
    332     if (test_slot_)
    333       return PK11_ReferenceSlot(test_slot_);
    334     if (software_slot_)
    335       return PK11_ReferenceSlot(software_slot_);
    336     return PK11_GetInternalKeySlot();
    337   }
    338 
    339   PK11SlotInfo* GetPrivateNSSKeySlot() {
    340     if (test_slot_)
    341       return PK11_ReferenceSlot(test_slot_);
    342 
    343 #if defined(OS_CHROMEOS)
    344     if (tpm_token_enabled_for_nss_) {
    345       if (IsTPMTokenReady()) {
    346         return PK11_ReferenceSlot(tpm_slot_);
    347       } else {
    348         // If we were supposed to get the hardware token, but were
    349         // unable to, return NULL rather than fall back to sofware.
    350         return NULL;
    351       }
    352     }
    353 #endif
    354     // If we weren't supposed to enable the TPM for NSS, then return
    355     // the software slot.
    356     if (software_slot_)
    357       return PK11_ReferenceSlot(software_slot_);
    358     return PK11_GetInternalKeySlot();
    359   }
    360 
    361 #if defined(USE_NSS)
    362   base::Lock* write_lock() {
    363     return &write_lock_;
    364   }
    365 #endif  // defined(USE_NSS)
    366 
    367   // This method is used to force NSS to be initialized without a DB.
    368   // Call this method before NSSInitSingleton() is constructed.
    369   static void ForceNoDBInit() {
    370     force_nodb_init_ = true;
    371   }
    372 
    373  private:
    374   friend struct base::DefaultLazyInstanceTraits<NSSInitSingleton>;
    375 
    376   NSSInitSingleton()
    377       : tpm_token_enabled_for_nss_(false),
    378         chaps_module_(NULL),
    379         software_slot_(NULL),
    380         test_slot_(NULL),
    381         tpm_slot_(NULL),
    382         root_(NULL),
    383         chromeos_user_logged_in_(false) {
    384     base::TimeTicks start_time = base::TimeTicks::Now();
    385     EnsureNSPRInit();
    386 
    387     // We *must* have NSS >= 3.14.3.
    388     COMPILE_ASSERT(
    389         (NSS_VMAJOR == 3 && NSS_VMINOR == 14 && NSS_VPATCH >= 3) ||
    390         (NSS_VMAJOR == 3 && NSS_VMINOR > 14) ||
    391         (NSS_VMAJOR > 3),
    392         nss_version_check_failed);
    393     // Also check the run-time NSS version.
    394     // NSS_VersionCheck is a >= check, not strict equality.
    395     if (!NSS_VersionCheck("3.14.3")) {
    396       LOG(FATAL) << "NSS_VersionCheck(\"3.14.3\") failed. NSS >= 3.14.3 is "
    397                     "required. Please upgrade to the latest NSS, and if you "
    398                     "still get this error, contact your distribution "
    399                     "maintainer.";
    400     }
    401 
    402     SECStatus status = SECFailure;
    403     bool nodb_init = force_nodb_init_;
    404 
    405 #if !defined(USE_NSS)
    406     // Use the system certificate store, so initialize NSS without database.
    407     nodb_init = true;
    408 #endif
    409 
    410     if (nodb_init) {
    411       status = NSS_NoDB_Init(NULL);
    412       if (status != SECSuccess) {
    413         CrashOnNSSInitFailure();
    414         return;
    415       }
    416 #if defined(OS_IOS)
    417       root_ = InitDefaultRootCerts();
    418 #endif  // defined(OS_IOS)
    419     } else {
    420 #if defined(USE_NSS)
    421       base::FilePath database_dir = GetInitialConfigDirectory();
    422       if (!database_dir.empty()) {
    423         // This duplicates the work which should have been done in
    424         // EarlySetupForNSSInit. However, this function is idempotent so
    425         // there's no harm done.
    426         UseLocalCacheOfNSSDatabaseIfNFS(database_dir);
    427 
    428         // Initialize with a persistent database (likely, ~/.pki/nssdb).
    429         // Use "sql:" which can be shared by multiple processes safely.
    430         std::string nss_config_dir =
    431             base::StringPrintf("sql:%s", database_dir.value().c_str());
    432 #if defined(OS_CHROMEOS)
    433         status = NSS_Init(nss_config_dir.c_str());
    434 #else
    435         status = NSS_InitReadWrite(nss_config_dir.c_str());
    436 #endif
    437         if (status != SECSuccess) {
    438           LOG(ERROR) << "Error initializing NSS with a persistent "
    439                         "database (" << nss_config_dir
    440                      << "): " << GetNSSErrorMessage();
    441         }
    442       }
    443       if (status != SECSuccess) {
    444         VLOG(1) << "Initializing NSS without a persistent database.";
    445         status = NSS_NoDB_Init(NULL);
    446         if (status != SECSuccess) {
    447           CrashOnNSSInitFailure();
    448           return;
    449         }
    450       }
    451 
    452       PK11_SetPasswordFunc(PKCS11PasswordFunc);
    453 
    454       // If we haven't initialized the password for the NSS databases,
    455       // initialize an empty-string password so that we don't need to
    456       // log in.
    457       PK11SlotInfo* slot = PK11_GetInternalKeySlot();
    458       if (slot) {
    459         // PK11_InitPin may write to the keyDB, but no other thread can use NSS
    460         // yet, so we don't need to lock.
    461         if (PK11_NeedUserInit(slot))
    462           PK11_InitPin(slot, NULL, NULL);
    463         PK11_FreeSlot(slot);
    464       }
    465 
    466       root_ = InitDefaultRootCerts();
    467 #endif  // defined(USE_NSS)
    468     }
    469 
    470     // Disable MD5 certificate signatures. (They are disabled by default in
    471     // NSS 3.14.)
    472     NSS_SetAlgorithmPolicy(SEC_OID_MD5, 0, NSS_USE_ALG_IN_CERT_SIGNATURE);
    473     NSS_SetAlgorithmPolicy(SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION,
    474                            0, NSS_USE_ALG_IN_CERT_SIGNATURE);
    475 
    476     // The UMA bit is conditionally set for this histogram in
    477     // chrome/common/startup_metric_utils.cc .
    478     HISTOGRAM_CUSTOM_TIMES("Startup.SlowStartupNSSInit",
    479                            base::TimeTicks::Now() - start_time,
    480                            base::TimeDelta::FromMilliseconds(10),
    481                            base::TimeDelta::FromHours(1),
    482                            50);
    483   }
    484 
    485   // NOTE(willchan): We don't actually execute this code since we leak NSS to
    486   // prevent non-joinable threads from using NSS after it's already been shut
    487   // down.
    488   ~NSSInitSingleton() {
    489     if (tpm_slot_) {
    490       PK11_FreeSlot(tpm_slot_);
    491       tpm_slot_ = NULL;
    492     }
    493     if (software_slot_) {
    494       SECMOD_CloseUserDB(software_slot_);
    495       PK11_FreeSlot(software_slot_);
    496       software_slot_ = NULL;
    497     }
    498     CloseTestNSSDB();
    499     if (root_) {
    500       SECMOD_UnloadUserModule(root_);
    501       SECMOD_DestroyModule(root_);
    502       root_ = NULL;
    503     }
    504     if (chaps_module_) {
    505       SECMOD_UnloadUserModule(chaps_module_);
    506       SECMOD_DestroyModule(chaps_module_);
    507       chaps_module_ = NULL;
    508     }
    509 
    510     SECStatus status = NSS_Shutdown();
    511     if (status != SECSuccess) {
    512       // We VLOG(1) because this failure is relatively harmless (leaking, but
    513       // we're shutting down anyway).
    514       VLOG(1) << "NSS_Shutdown failed; see http://crbug.com/4609";
    515     }
    516   }
    517 
    518 #if defined(USE_NSS) || defined(OS_IOS)
    519   // Load nss's built-in root certs.
    520   SECMODModule* InitDefaultRootCerts() {
    521     SECMODModule* root = LoadModule("Root Certs", "libnssckbi.so", NULL);
    522     if (root)
    523       return root;
    524 
    525     // Aw, snap.  Can't find/load root cert shared library.
    526     // This will make it hard to talk to anybody via https.
    527     NOTREACHED();
    528     return NULL;
    529   }
    530 
    531   // Load the given module for this NSS session.
    532   SECMODModule* LoadModule(const char* name,
    533                            const char* library_path,
    534                            const char* params) {
    535     std::string modparams = base::StringPrintf(
    536         "name=\"%s\" library=\"%s\" %s",
    537         name, library_path, params ? params : "");
    538 
    539     // Shouldn't need to const_cast here, but SECMOD doesn't properly
    540     // declare input string arguments as const.  Bug
    541     // https://bugzilla.mozilla.org/show_bug.cgi?id=642546 was filed
    542     // on NSS codebase to address this.
    543     SECMODModule* module = SECMOD_LoadUserModule(
    544         const_cast<char*>(modparams.c_str()), NULL, PR_FALSE);
    545     if (!module) {
    546       LOG(ERROR) << "Error loading " << name << " module into NSS: "
    547                  << GetNSSErrorMessage();
    548       return NULL;
    549     }
    550     return module;
    551   }
    552 #endif
    553 
    554   static PK11SlotInfo* OpenUserDB(const base::FilePath& path,
    555                                   const char* description) {
    556     const std::string modspec =
    557         base::StringPrintf("configDir='sql:%s' tokenDescription='%s'",
    558                            path.value().c_str(), description);
    559     PK11SlotInfo* db_slot = SECMOD_OpenUserDB(modspec.c_str());
    560     if (db_slot) {
    561       if (PK11_NeedUserInit(db_slot))
    562         PK11_InitPin(db_slot, NULL, NULL);
    563     }
    564     else {
    565       LOG(ERROR) << "Error opening persistent database (" << modspec
    566                  << "): " << GetNSSErrorMessage();
    567     }
    568     return db_slot;
    569   }
    570 
    571   // If this is set to true NSS is forced to be initialized without a DB.
    572   static bool force_nodb_init_;
    573 
    574   bool tpm_token_enabled_for_nss_;
    575   std::string tpm_token_name_;
    576   std::string tpm_user_pin_;
    577   SECMODModule* chaps_module_;
    578   PK11SlotInfo* software_slot_;
    579   PK11SlotInfo* test_slot_;
    580   PK11SlotInfo* tpm_slot_;
    581   SECMODModule* root_;
    582   bool chromeos_user_logged_in_;
    583 #if defined(USE_NSS)
    584   // TODO(davidben): When https://bugzilla.mozilla.org/show_bug.cgi?id=564011
    585   // is fixed, we will no longer need the lock.
    586   base::Lock write_lock_;
    587 #endif  // defined(USE_NSS)
    588 };
    589 
    590 // static
    591 bool NSSInitSingleton::force_nodb_init_ = false;
    592 
    593 base::LazyInstance<NSSInitSingleton>::Leaky
    594     g_nss_singleton = LAZY_INSTANCE_INITIALIZER;
    595 }  // namespace
    596 
    597 const char kTestTPMTokenName[] = "Test DB";
    598 
    599 #if defined(USE_NSS)
    600 void EarlySetupForNSSInit() {
    601   base::FilePath database_dir = GetInitialConfigDirectory();
    602   if (!database_dir.empty())
    603     UseLocalCacheOfNSSDatabaseIfNFS(database_dir);
    604 }
    605 #endif
    606 
    607 void EnsureNSPRInit() {
    608   g_nspr_singleton.Get();
    609 }
    610 
    611 void InitNSSSafely() {
    612   // We might fork, but we haven't loaded any security modules.
    613   DisableNSSForkCheck();
    614   // If we're sandboxed, we shouldn't be able to open user security modules,
    615   // but it's more correct to tell NSS to not even try.
    616   // Loading user security modules would have security implications.
    617   ForceNSSNoDBInit();
    618   // Initialize NSS.
    619   EnsureNSSInit();
    620 }
    621 
    622 void EnsureNSSInit() {
    623   // Initializing SSL causes us to do blocking IO.
    624   // Temporarily allow it until we fix
    625   //   http://code.google.com/p/chromium/issues/detail?id=59847
    626   base::ThreadRestrictions::ScopedAllowIO allow_io;
    627   g_nss_singleton.Get();
    628 }
    629 
    630 void ForceNSSNoDBInit() {
    631   NSSInitSingleton::ForceNoDBInit();
    632 }
    633 
    634 void DisableNSSForkCheck() {
    635   scoped_ptr<base::Environment> env(base::Environment::Create());
    636   env->SetVar("NSS_STRICT_NOFORK", "DISABLED");
    637 }
    638 
    639 void LoadNSSLibraries() {
    640   // Some NSS libraries are linked dynamically so load them here.
    641 #if defined(USE_NSS)
    642   // Try to search for multiple directories to load the libraries.
    643   std::vector<base::FilePath> paths;
    644 
    645   // Use relative path to Search PATH for the library files.
    646   paths.push_back(base::FilePath());
    647 
    648   // For Debian derivatives NSS libraries are located here.
    649   paths.push_back(base::FilePath("/usr/lib/nss"));
    650 
    651   // Ubuntu 11.10 (Oneiric) and Debian Wheezy place the libraries here.
    652 #if defined(ARCH_CPU_X86_64)
    653   paths.push_back(base::FilePath("/usr/lib/x86_64-linux-gnu/nss"));
    654 #elif defined(ARCH_CPU_X86)
    655   paths.push_back(base::FilePath("/usr/lib/i386-linux-gnu/nss"));
    656 #elif defined(ARCH_CPU_ARMEL)
    657   paths.push_back(base::FilePath("/usr/lib/arm-linux-gnueabi/nss"));
    658 #elif defined(ARCH_CPU_MIPSEL)
    659   paths.push_back(base::FilePath("/usr/lib/mipsel-linux-gnu/nss"));
    660 #endif
    661 
    662   // A list of library files to load.
    663   std::vector<std::string> libs;
    664   libs.push_back("libsoftokn3.so");
    665   libs.push_back("libfreebl3.so");
    666 
    667   // For each combination of library file and path, check for existence and
    668   // then load.
    669   size_t loaded = 0;
    670   for (size_t i = 0; i < libs.size(); ++i) {
    671     for (size_t j = 0; j < paths.size(); ++j) {
    672       base::FilePath path = paths[j].Append(libs[i]);
    673       base::NativeLibrary lib = base::LoadNativeLibrary(path, NULL);
    674       if (lib) {
    675         ++loaded;
    676         break;
    677       }
    678     }
    679   }
    680 
    681   if (loaded == libs.size()) {
    682     VLOG(3) << "NSS libraries loaded.";
    683   } else {
    684     LOG(ERROR) << "Failed to load NSS libraries.";
    685   }
    686 #endif
    687 }
    688 
    689 bool CheckNSSVersion(const char* version) {
    690   return !!NSS_VersionCheck(version);
    691 }
    692 
    693 #if defined(USE_NSS)
    694 ScopedTestNSSDB::ScopedTestNSSDB()
    695   : is_open_(g_nss_singleton.Get().OpenTestNSSDB()) {
    696 }
    697 
    698 ScopedTestNSSDB::~ScopedTestNSSDB() {
    699   // Don't close when NSS is < 3.15.1, because it would require an additional
    700   // sleep for 1 second after closing the database, due to
    701   // http://bugzil.la/875601.
    702   if (NSS_VersionCheck("3.15.1")) {
    703     g_nss_singleton.Get().CloseTestNSSDB();
    704   }
    705 }
    706 
    707 base::Lock* GetNSSWriteLock() {
    708   return g_nss_singleton.Get().write_lock();
    709 }
    710 
    711 AutoNSSWriteLock::AutoNSSWriteLock() : lock_(GetNSSWriteLock()) {
    712   // May be NULL if the lock is not needed in our version of NSS.
    713   if (lock_)
    714     lock_->Acquire();
    715 }
    716 
    717 AutoNSSWriteLock::~AutoNSSWriteLock() {
    718   if (lock_) {
    719     lock_->AssertAcquired();
    720     lock_->Release();
    721   }
    722 }
    723 
    724 AutoSECMODListReadLock::AutoSECMODListReadLock()
    725       : lock_(SECMOD_GetDefaultModuleListLock()) {
    726     SECMOD_GetReadLock(lock_);
    727   }
    728 
    729 AutoSECMODListReadLock::~AutoSECMODListReadLock() {
    730   SECMOD_ReleaseReadLock(lock_);
    731 }
    732 
    733 #endif  // defined(USE_NSS)
    734 
    735 #if defined(OS_CHROMEOS)
    736 void OpenPersistentNSSDB() {
    737   g_nss_singleton.Get().OpenPersistentNSSDB();
    738 }
    739 
    740 void EnableTPMTokenForNSS() {
    741   g_nss_singleton.Get().EnableTPMTokenForNSS();
    742 }
    743 
    744 void GetTPMTokenInfo(std::string* token_name, std::string* user_pin) {
    745   g_nss_singleton.Get().GetTPMTokenInfo(token_name, user_pin);
    746 }
    747 
    748 bool IsTPMTokenReady() {
    749   return g_nss_singleton.Get().IsTPMTokenReady();
    750 }
    751 
    752 bool InitializeTPMToken(const std::string& token_name,
    753                         const std::string& user_pin) {
    754   return g_nss_singleton.Get().InitializeTPMToken(token_name, user_pin);
    755 }
    756 #endif  // defined(OS_CHROMEOS)
    757 
    758 base::Time PRTimeToBaseTime(PRTime prtime) {
    759   return base::Time::FromInternalValue(
    760       prtime + base::Time::UnixEpoch().ToInternalValue());
    761 }
    762 
    763 PRTime BaseTimeToPRTime(base::Time time) {
    764   return time.ToInternalValue() - base::Time::UnixEpoch().ToInternalValue();
    765 }
    766 
    767 PK11SlotInfo* GetPublicNSSKeySlot() {
    768   return g_nss_singleton.Get().GetPublicNSSKeySlot();
    769 }
    770 
    771 PK11SlotInfo* GetPrivateNSSKeySlot() {
    772   return g_nss_singleton.Get().GetPrivateNSSKeySlot();
    773 }
    774 
    775 }  // namespace crypto
    776