Home | History | Annotate | Download | only in crypto
      1 // Copyright (c) 2011 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 <plarena.h>
     10 #include <prerror.h>
     11 #include <prinit.h>
     12 #include <prtime.h>
     13 #include <pk11pub.h>
     14 #include <secmod.h>
     15 
     16 #if defined(OS_LINUX)
     17 #include <linux/nfs_fs.h>
     18 #include <sys/vfs.h>
     19 #endif
     20 
     21 #include <vector>
     22 
     23 #include "base/environment.h"
     24 #include "base/file_path.h"
     25 #include "base/file_util.h"
     26 #include "base/lazy_instance.h"
     27 #include "base/logging.h"
     28 #include "base/memory/scoped_ptr.h"
     29 #include "base/native_library.h"
     30 #include "base/stringprintf.h"
     31 #include "base/threading/thread_restrictions.h"
     32 #include "crypto/scoped_nss_types.h"
     33 
     34 // USE_NSS means we use NSS for everything crypto-related.  If USE_NSS is not
     35 // defined, such as on Mac and Windows, we use NSS for SSL only -- we don't
     36 // use NSS for crypto or certificate verification, and we don't use the NSS
     37 // certificate and key databases.
     38 #if defined(USE_NSS)
     39 #include "base/synchronization/lock.h"
     40 #include "crypto/crypto_module_blocking_password_delegate.h"
     41 #endif  // defined(USE_NSS)
     42 
     43 namespace crypto {
     44 
     45 namespace {
     46 
     47 #if defined(OS_CHROMEOS)
     48 const char kNSSDatabaseName[] = "Real NSS database";
     49 
     50 // Constants for loading opencryptoki.
     51 const char kOpencryptokiModuleName[] = "opencryptoki";
     52 const char kOpencryptokiPath[] = "/usr/lib/opencryptoki/libopencryptoki.so";
     53 
     54 // Fake certificate authority database used for testing.
     55 static const FilePath::CharType kReadOnlyCertDB[] =
     56     FILE_PATH_LITERAL("/etc/fake_root_ca/nssdb");
     57 #endif  // defined(OS_CHROMEOS)
     58 
     59 std::string GetNSSErrorMessage() {
     60   std::string result;
     61   if (PR_GetErrorTextLength()) {
     62     scoped_array<char> error_text(new char[PR_GetErrorTextLength() + 1]);
     63     PRInt32 copied = PR_GetErrorText(error_text.get());
     64     result = std::string(error_text.get(), copied);
     65   } else {
     66     result = StringPrintf("NSS error code: %d", PR_GetError());
     67   }
     68   return result;
     69 }
     70 
     71 #if defined(USE_NSS)
     72 FilePath GetDefaultConfigDirectory() {
     73   FilePath dir = file_util::GetHomeDir();
     74   if (dir.empty()) {
     75     LOG(ERROR) << "Failed to get home directory.";
     76     return dir;
     77   }
     78   dir = dir.AppendASCII(".pki").AppendASCII("nssdb");
     79   if (!file_util::CreateDirectory(dir)) {
     80     LOG(ERROR) << "Failed to create ~/.pki/nssdb directory.";
     81     dir.clear();
     82   }
     83   return dir;
     84 }
     85 
     86 // On non-chromeos platforms, return the default config directory.
     87 // On chromeos, return a read-only directory with fake root CA certs for testing
     88 // (which will not exist on non-testing images).  These root CA certs are used
     89 // by the local Google Accounts server mock we use when testing our login code.
     90 // If this directory is not present, NSS_Init() will fail.  It is up to the
     91 // caller to failover to NSS_NoDB_Init() at that point.
     92 FilePath GetInitialConfigDirectory() {
     93 #if defined(OS_CHROMEOS)
     94   return FilePath(kReadOnlyCertDB);
     95 #else
     96   return GetDefaultConfigDirectory();
     97 #endif  // defined(OS_CHROMEOS)
     98 }
     99 
    100 // This callback for NSS forwards all requests to a caller-specified
    101 // CryptoModuleBlockingPasswordDelegate object.
    102 char* PKCS11PasswordFunc(PK11SlotInfo* slot, PRBool retry, void* arg) {
    103 #if defined(OS_CHROMEOS)
    104   // If we get asked for a password for the TPM, then return the
    105   // well known password we use, as long as the TPM slot has been
    106   // initialized.
    107   if (crypto::IsTPMTokenReady()) {
    108     std::string token_name;
    109     std::string user_pin;
    110     crypto::GetTPMTokenInfo(&token_name, &user_pin);
    111     if (PK11_GetTokenName(slot) == token_name)
    112       return PORT_Strdup(user_pin.c_str());
    113   }
    114 #endif
    115   crypto::CryptoModuleBlockingPasswordDelegate* delegate =
    116       reinterpret_cast<crypto::CryptoModuleBlockingPasswordDelegate*>(arg);
    117   if (delegate) {
    118     bool cancelled = false;
    119     std::string password = delegate->RequestPassword(PK11_GetTokenName(slot),
    120                                                      retry != PR_FALSE,
    121                                                      &cancelled);
    122     if (cancelled)
    123       return NULL;
    124     char* result = PORT_Strdup(password.c_str());
    125     password.replace(0, password.size(), password.size(), 0);
    126     return result;
    127   }
    128   DLOG(ERROR) << "PK11 password requested with NULL arg";
    129   return NULL;
    130 }
    131 
    132 // NSS creates a local cache of the sqlite database if it detects that the
    133 // filesystem the database is on is much slower than the local disk.  The
    134 // detection doesn't work with the latest versions of sqlite, such as 3.6.22
    135 // (NSS bug https://bugzilla.mozilla.org/show_bug.cgi?id=578561).  So we set
    136 // the NSS environment variable NSS_SDB_USE_CACHE to "yes" to override NSS's
    137 // detection when database_dir is on NFS.  See http://crbug.com/48585.
    138 //
    139 // TODO(wtc): port this function to other USE_NSS platforms.  It is defined
    140 // only for OS_LINUX simply because the statfs structure is OS-specific.
    141 //
    142 // Because this function sets an environment variable it must be run before we
    143 // go multi-threaded.
    144 void UseLocalCacheOfNSSDatabaseIfNFS(const FilePath& database_dir) {
    145 #if defined(OS_LINUX)
    146   struct statfs buf;
    147   if (statfs(database_dir.value().c_str(), &buf) == 0) {
    148     if (buf.f_type == NFS_SUPER_MAGIC) {
    149       scoped_ptr<base::Environment> env(base::Environment::Create());
    150       const char* use_cache_env_var = "NSS_SDB_USE_CACHE";
    151       if (!env->HasVar(use_cache_env_var))
    152         env->SetVar(use_cache_env_var, "yes");
    153     }
    154   }
    155 #endif  // defined(OS_LINUX)
    156 }
    157 
    158 // A helper class that acquires the SECMOD list read lock while the
    159 // AutoSECMODListReadLock is in scope.
    160 class AutoSECMODListReadLock {
    161  public:
    162   AutoSECMODListReadLock()
    163       : lock_(SECMOD_GetDefaultModuleListLock()) {
    164     SECMOD_GetReadLock(lock_);
    165   }
    166 
    167   ~AutoSECMODListReadLock() {
    168     SECMOD_ReleaseReadLock(lock_);
    169   }
    170 
    171  private:
    172   SECMODListLock* lock_;
    173   DISALLOW_COPY_AND_ASSIGN(AutoSECMODListReadLock);
    174 };
    175 
    176 PK11SlotInfo* FindSlotWithTokenName(const std::string& token_name) {
    177   AutoSECMODListReadLock auto_lock;
    178   SECMODModuleList* head = SECMOD_GetDefaultModuleList();
    179   for (SECMODModuleList* item = head; item != NULL; item = item->next) {
    180     int slot_count = item->module->loaded ? item->module->slotCount : 0;
    181     for (int i = 0; i < slot_count; i++) {
    182       PK11SlotInfo* slot = item->module->slots[i];
    183       if (PK11_GetTokenName(slot) == token_name)
    184         return PK11_ReferenceSlot(slot);
    185     }
    186   }
    187   return NULL;
    188 }
    189 
    190 #endif  // defined(USE_NSS)
    191 
    192 // A singleton to initialize/deinitialize NSPR.
    193 // Separate from the NSS singleton because we initialize NSPR on the UI thread.
    194 // Now that we're leaking the singleton, we could merge back with the NSS
    195 // singleton.
    196 class NSPRInitSingleton {
    197  private:
    198   friend struct base::DefaultLazyInstanceTraits<NSPRInitSingleton>;
    199 
    200   NSPRInitSingleton() {
    201     PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
    202   }
    203 
    204   // NOTE(willchan): We don't actually execute this code since we leak NSS to
    205   // prevent non-joinable threads from using NSS after it's already been shut
    206   // down.
    207   ~NSPRInitSingleton() {
    208     PL_ArenaFinish();
    209     PRStatus prstatus = PR_Cleanup();
    210     if (prstatus != PR_SUCCESS) {
    211       LOG(ERROR) << "PR_Cleanup failed; was NSPR initialized on wrong thread?";
    212     }
    213   }
    214 };
    215 
    216 base::LazyInstance<NSPRInitSingleton,
    217                    base::LeakyLazyInstanceTraits<NSPRInitSingleton> >
    218     g_nspr_singleton(base::LINKER_INITIALIZED);
    219 
    220 class NSSInitSingleton {
    221  public:
    222 #if defined(OS_CHROMEOS)
    223   void OpenPersistentNSSDB() {
    224     if (!chromeos_user_logged_in_) {
    225       // GetDefaultConfigDirectory causes us to do blocking IO on UI thread.
    226       // Temporarily allow it until we fix http://crbug.com/70119
    227       base::ThreadRestrictions::ScopedAllowIO allow_io;
    228       chromeos_user_logged_in_ = true;
    229 
    230       // This creates another DB slot in NSS that is read/write, unlike
    231       // the fake root CA cert DB and the "default" crypto key
    232       // provider, which are still read-only (because we initialized
    233       // NSS before we had a cryptohome mounted).
    234       software_slot_ = OpenUserDB(GetDefaultConfigDirectory(),
    235                                   kNSSDatabaseName);
    236     }
    237   }
    238 
    239   void EnableTPMTokenForNSS(TPMTokenInfoDelegate* info_delegate) {
    240     CHECK(info_delegate);
    241     tpm_token_info_delegate_.reset(info_delegate);
    242     // Try to load once to avoid jank later.  Ignore the return value,
    243     // because if it fails we will try again later.
    244     EnsureTPMTokenReady();
    245   }
    246 
    247   void GetTPMTokenInfo(std::string* token_name, std::string* user_pin) {
    248     tpm_token_info_delegate_->GetTokenInfo(token_name, user_pin);
    249   }
    250 
    251   bool IsTPMTokenReady() {
    252     return tpm_slot_ != NULL;
    253   }
    254 
    255   PK11SlotInfo* GetTPMSlot() {
    256     std::string token_name;
    257     GetTPMTokenInfo(&token_name, NULL);
    258     return FindSlotWithTokenName(token_name);
    259   }
    260 #endif  // defined(OS_CHROMEOS)
    261 
    262 
    263   bool OpenTestNSSDB(const FilePath& path, const char* description) {
    264     test_slot_ = OpenUserDB(path, description);
    265     return !!test_slot_;
    266   }
    267 
    268   void CloseTestNSSDB() {
    269     if (test_slot_) {
    270       SECStatus status = SECMOD_CloseUserDB(test_slot_);
    271       if (status != SECSuccess)
    272         LOG(ERROR) << "SECMOD_CloseUserDB failed: " << PORT_GetError();
    273       PK11_FreeSlot(test_slot_);
    274       test_slot_ = NULL;
    275     }
    276   }
    277 
    278   PK11SlotInfo* GetPublicNSSKeySlot() {
    279     if (test_slot_)
    280       return PK11_ReferenceSlot(test_slot_);
    281     if (software_slot_)
    282       return PK11_ReferenceSlot(software_slot_);
    283     return PK11_GetInternalKeySlot();
    284   }
    285 
    286   PK11SlotInfo* GetPrivateNSSKeySlot() {
    287     if (test_slot_)
    288       return PK11_ReferenceSlot(test_slot_);
    289 
    290 #if defined(OS_CHROMEOS)
    291     // Make sure that if EnableTPMTokenForNSS has been called that we
    292     // have successfully loaded opencryptoki.
    293     if (tpm_token_info_delegate_.get() != NULL) {
    294       if (EnsureTPMTokenReady()) {
    295         return PK11_ReferenceSlot(tpm_slot_);
    296       } else {
    297         // If we were supposed to get the hardware token, but were
    298         // unable to, return NULL rather than fall back to sofware.
    299         return NULL;
    300       }
    301     }
    302 #endif
    303     // If we weren't supposed to enable the TPM for NSS, then return
    304     // the software slot.
    305     if (software_slot_)
    306       return PK11_ReferenceSlot(software_slot_);
    307     return PK11_GetInternalKeySlot();
    308   }
    309 
    310 #if defined(USE_NSS)
    311   base::Lock* write_lock() {
    312     return &write_lock_;
    313   }
    314 #endif  // defined(USE_NSS)
    315 
    316   // This method is used to force NSS to be initialized without a DB.
    317   // Call this method before NSSInitSingleton() is constructed.
    318   static void ForceNoDBInit() {
    319     force_nodb_init_ = true;
    320   }
    321 
    322  private:
    323   friend struct base::DefaultLazyInstanceTraits<NSSInitSingleton>;
    324 
    325   NSSInitSingleton()
    326       : opencryptoki_module_(NULL),
    327         software_slot_(NULL),
    328         test_slot_(NULL),
    329         tpm_slot_(NULL),
    330         root_(NULL),
    331         chromeos_user_logged_in_(false) {
    332     EnsureNSPRInit();
    333 
    334     // We *must* have NSS >= 3.12.3.  See bug 26448.
    335     COMPILE_ASSERT(
    336         (NSS_VMAJOR == 3 && NSS_VMINOR == 12 && NSS_VPATCH >= 3) ||
    337         (NSS_VMAJOR == 3 && NSS_VMINOR > 12) ||
    338         (NSS_VMAJOR > 3),
    339         nss_version_check_failed);
    340     // Also check the run-time NSS version.
    341     // NSS_VersionCheck is a >= check, not strict equality.
    342     if (!NSS_VersionCheck("3.12.3")) {
    343       // It turns out many people have misconfigured NSS setups, where
    344       // their run-time NSPR doesn't match the one their NSS was compiled
    345       // against.  So rather than aborting, complain loudly.
    346       LOG(ERROR) << "NSS_VersionCheck(\"3.12.3\") failed.  "
    347                     "We depend on NSS >= 3.12.3, and this error is not fatal "
    348                     "only because many people have busted NSS setups (for "
    349                     "example, using the wrong version of NSPR). "
    350                     "Please upgrade to the latest NSS and NSPR, and if you "
    351                     "still get this error, contact your distribution "
    352                     "maintainer.";
    353     }
    354 
    355     SECStatus status = SECFailure;
    356     bool nodb_init = force_nodb_init_;
    357 
    358 #if !defined(USE_NSS)
    359     // Use the system certificate store, so initialize NSS without database.
    360     nodb_init = true;
    361 #endif
    362 
    363     if (nodb_init) {
    364       status = NSS_NoDB_Init(NULL);
    365       if (status != SECSuccess) {
    366         LOG(ERROR) << "Error initializing NSS without a persistent "
    367                       "database: " << GetNSSErrorMessage();
    368       }
    369     } else {
    370 #if defined(USE_NSS)
    371       FilePath database_dir = GetInitialConfigDirectory();
    372       if (!database_dir.empty()) {
    373         // This duplicates the work which should have been done in
    374         // EarlySetupForNSSInit. However, this function is idempotent so
    375         // there's no harm done.
    376         UseLocalCacheOfNSSDatabaseIfNFS(database_dir);
    377 
    378         // Initialize with a persistent database (likely, ~/.pki/nssdb).
    379         // Use "sql:" which can be shared by multiple processes safely.
    380         std::string nss_config_dir =
    381             StringPrintf("sql:%s", database_dir.value().c_str());
    382 #if defined(OS_CHROMEOS)
    383         status = NSS_Init(nss_config_dir.c_str());
    384 #else
    385         status = NSS_InitReadWrite(nss_config_dir.c_str());
    386 #endif
    387         if (status != SECSuccess) {
    388           LOG(ERROR) << "Error initializing NSS with a persistent "
    389                         "database (" << nss_config_dir
    390                      << "): " << GetNSSErrorMessage();
    391         }
    392       }
    393       if (status != SECSuccess) {
    394         VLOG(1) << "Initializing NSS without a persistent database.";
    395         status = NSS_NoDB_Init(NULL);
    396         if (status != SECSuccess) {
    397           LOG(ERROR) << "Error initializing NSS without a persistent "
    398                         "database: " << GetNSSErrorMessage();
    399           return;
    400         }
    401       }
    402 
    403       PK11_SetPasswordFunc(PKCS11PasswordFunc);
    404 
    405       // If we haven't initialized the password for the NSS databases,
    406       // initialize an empty-string password so that we don't need to
    407       // log in.
    408       PK11SlotInfo* slot = PK11_GetInternalKeySlot();
    409       if (slot) {
    410         // PK11_InitPin may write to the keyDB, but no other thread can use NSS
    411         // yet, so we don't need to lock.
    412         if (PK11_NeedUserInit(slot))
    413           PK11_InitPin(slot, NULL, NULL);
    414         PK11_FreeSlot(slot);
    415       }
    416 
    417       root_ = InitDefaultRootCerts();
    418 #endif  // defined(USE_NSS)
    419     }
    420   }
    421 
    422   // NOTE(willchan): We don't actually execute this code since we leak NSS to
    423   // prevent non-joinable threads from using NSS after it's already been shut
    424   // down.
    425   ~NSSInitSingleton() {
    426     if (tpm_slot_) {
    427       PK11_FreeSlot(tpm_slot_);
    428       tpm_slot_ = NULL;
    429     }
    430     if (software_slot_) {
    431       SECMOD_CloseUserDB(software_slot_);
    432       PK11_FreeSlot(software_slot_);
    433       software_slot_ = NULL;
    434     }
    435     CloseTestNSSDB();
    436     if (root_) {
    437       SECMOD_UnloadUserModule(root_);
    438       SECMOD_DestroyModule(root_);
    439       root_ = NULL;
    440     }
    441     if (opencryptoki_module_) {
    442       SECMOD_UnloadUserModule(opencryptoki_module_);
    443       SECMOD_DestroyModule(opencryptoki_module_);
    444       opencryptoki_module_ = NULL;
    445     }
    446 
    447     SECStatus status = NSS_Shutdown();
    448     if (status != SECSuccess) {
    449       // We VLOG(1) because this failure is relatively harmless (leaking, but
    450       // we're shutting down anyway).
    451       VLOG(1) << "NSS_Shutdown failed; see http://crbug.com/4609";
    452     }
    453   }
    454 
    455 #if defined(USE_NSS)
    456   // Load nss's built-in root certs.
    457   SECMODModule* InitDefaultRootCerts() {
    458     SECMODModule* root = LoadModule("Root Certs", "libnssckbi.so", NULL);
    459     if (root)
    460       return root;
    461 
    462     // Aw, snap.  Can't find/load root cert shared library.
    463     // This will make it hard to talk to anybody via https.
    464     NOTREACHED();
    465     return NULL;
    466   }
    467 
    468   // Load the given module for this NSS session.
    469   SECMODModule* LoadModule(const char* name,
    470                            const char* library_path,
    471                            const char* params) {
    472     std::string modparams = StringPrintf(
    473         "name=\"%s\" library=\"%s\" %s",
    474         name, library_path, params ? params : "");
    475 
    476     // Shouldn't need to const_cast here, but SECMOD doesn't properly
    477     // declare input string arguments as const.  Bug
    478     // https://bugzilla.mozilla.org/show_bug.cgi?id=642546 was filed
    479     // on NSS codebase to address this.
    480     SECMODModule* module = SECMOD_LoadUserModule(
    481         const_cast<char*>(modparams.c_str()), NULL, PR_FALSE);
    482     if (!module) {
    483       LOG(ERROR) << "Error loading " << name << " module into NSS: "
    484                  << GetNSSErrorMessage();
    485       return NULL;
    486     }
    487     return module;
    488   }
    489 #endif
    490 
    491   static PK11SlotInfo* OpenUserDB(const FilePath& path,
    492                                   const char* description) {
    493     const std::string modspec =
    494         StringPrintf("configDir='sql:%s' tokenDescription='%s'",
    495                      path.value().c_str(), description);
    496     PK11SlotInfo* db_slot = SECMOD_OpenUserDB(modspec.c_str());
    497     if (db_slot) {
    498       if (PK11_NeedUserInit(db_slot))
    499         PK11_InitPin(db_slot, NULL, NULL);
    500     }
    501     else {
    502       LOG(ERROR) << "Error opening persistent database (" << modspec
    503                  << "): " << GetNSSErrorMessage();
    504     }
    505     return db_slot;
    506   }
    507 
    508 #if defined(OS_CHROMEOS)
    509   // This is called whenever we want to make sure opencryptoki is
    510   // properly loaded, because it can fail shortly after the initial
    511   // login while the PINs are being initialized, and we want to retry
    512   // if this happens.
    513   bool EnsureTPMTokenReady() {
    514     // If EnableTPMTokenForNSS hasn't been called, or if everything is
    515     // already initialized, then this call succeeds.
    516     if (tpm_token_info_delegate_.get() == NULL ||
    517         (opencryptoki_module_ && tpm_slot_)) {
    518       return true;
    519     }
    520 
    521     if (tpm_token_info_delegate_->IsTokenReady()) {
    522       // This tries to load the opencryptoki module so NSS can talk to
    523       // the hardware TPM.
    524       if (!opencryptoki_module_) {
    525         opencryptoki_module_ = LoadModule(
    526             kOpencryptokiModuleName,
    527             kOpencryptokiPath,
    528             // trustOrder=100 -- means it'll select this as the most
    529             //   trusted slot for the mechanisms it provides.
    530             // slotParams=... -- selects RSA as the only mechanism, and only
    531             //   asks for the password when necessary (instead of every
    532             //   time, or after a timeout).
    533             "trustOrder=100 slotParams=(1={slotFlags=[RSA] askpw=only})");
    534       }
    535       if (opencryptoki_module_) {
    536         // If this gets set, then we'll use the TPM for certs with
    537         // private keys, otherwise we'll fall back to the software
    538         // implementation.
    539         tpm_slot_ = GetTPMSlot();
    540         return tpm_slot_ != NULL;
    541       }
    542     }
    543     return false;
    544   }
    545 #endif
    546 
    547   // If this is set to true NSS is forced to be initialized without a DB.
    548   static bool force_nodb_init_;
    549 
    550 #if defined(OS_CHROMEOS)
    551   scoped_ptr<TPMTokenInfoDelegate> tpm_token_info_delegate_;
    552 #endif
    553 
    554   SECMODModule* opencryptoki_module_;
    555   PK11SlotInfo* software_slot_;
    556   PK11SlotInfo* test_slot_;
    557   PK11SlotInfo* tpm_slot_;
    558   SECMODModule* root_;
    559   bool chromeos_user_logged_in_;
    560 #if defined(USE_NSS)
    561   // TODO(davidben): When https://bugzilla.mozilla.org/show_bug.cgi?id=564011
    562   // is fixed, we will no longer need the lock.
    563   base::Lock write_lock_;
    564 #endif  // defined(USE_NSS)
    565 };
    566 
    567 // static
    568 bool NSSInitSingleton::force_nodb_init_ = false;
    569 
    570 base::LazyInstance<NSSInitSingleton,
    571                    base::LeakyLazyInstanceTraits<NSSInitSingleton> >
    572     g_nss_singleton(base::LINKER_INITIALIZED);
    573 
    574 }  // namespace
    575 
    576 #if defined(USE_NSS)
    577 void EarlySetupForNSSInit() {
    578   FilePath database_dir = GetInitialConfigDirectory();
    579   if (!database_dir.empty())
    580     UseLocalCacheOfNSSDatabaseIfNFS(database_dir);
    581 }
    582 #endif
    583 
    584 void EnsureNSPRInit() {
    585   g_nspr_singleton.Get();
    586 }
    587 
    588 void EnsureNSSInit() {
    589   // Initializing SSL causes us to do blocking IO.
    590   // Temporarily allow it until we fix
    591   //   http://code.google.com/p/chromium/issues/detail?id=59847
    592   base::ThreadRestrictions::ScopedAllowIO allow_io;
    593   g_nss_singleton.Get();
    594 }
    595 
    596 void ForceNSSNoDBInit() {
    597   NSSInitSingleton::ForceNoDBInit();
    598 }
    599 
    600 void DisableNSSForkCheck() {
    601   scoped_ptr<base::Environment> env(base::Environment::Create());
    602   env->SetVar("NSS_STRICT_NOFORK", "DISABLED");
    603 }
    604 
    605 void LoadNSSLibraries() {
    606   // Some NSS libraries are linked dynamically so load them here.
    607 #if defined(USE_NSS)
    608   // Try to search for multiple directories to load the libraries.
    609   std::vector<FilePath> paths;
    610 
    611   // Use relative path to Search PATH for the library files.
    612   paths.push_back(FilePath());
    613 
    614   // For Debian derivaties NSS libraries are located here.
    615   paths.push_back(FilePath("/usr/lib/nss"));
    616 
    617   // A list of library files to load.
    618   std::vector<std::string> libs;
    619   libs.push_back("libsoftokn3.so");
    620   libs.push_back("libfreebl3.so");
    621 
    622   // For each combination of library file and path, check for existence and
    623   // then load.
    624   size_t loaded = 0;
    625   for (size_t i = 0; i < libs.size(); ++i) {
    626     for (size_t j = 0; j < paths.size(); ++j) {
    627       FilePath path = paths[j].Append(libs[i]);
    628       base::NativeLibrary lib = base::LoadNativeLibrary(path, NULL);
    629       if (lib) {
    630         ++loaded;
    631         break;
    632       }
    633     }
    634   }
    635 
    636   if (loaded == libs.size()) {
    637     VLOG(3) << "NSS libraries loaded.";
    638   } else {
    639     LOG(WARNING) << "Failed to load NSS libraries.";
    640   }
    641 #endif
    642 }
    643 
    644 bool CheckNSSVersion(const char* version) {
    645   return !!NSS_VersionCheck(version);
    646 }
    647 
    648 #if defined(USE_NSS)
    649 bool OpenTestNSSDB(const FilePath& path, const char* description) {
    650   return g_nss_singleton.Get().OpenTestNSSDB(path, description);
    651 }
    652 
    653 void CloseTestNSSDB() {
    654   g_nss_singleton.Get().CloseTestNSSDB();
    655 }
    656 
    657 base::Lock* GetNSSWriteLock() {
    658   return g_nss_singleton.Get().write_lock();
    659 }
    660 
    661 AutoNSSWriteLock::AutoNSSWriteLock() : lock_(GetNSSWriteLock()) {
    662   // May be NULL if the lock is not needed in our version of NSS.
    663   if (lock_)
    664     lock_->Acquire();
    665 }
    666 
    667 AutoNSSWriteLock::~AutoNSSWriteLock() {
    668   if (lock_) {
    669     lock_->AssertAcquired();
    670     lock_->Release();
    671   }
    672 }
    673 #endif  // defined(USE_NSS)
    674 
    675 #if defined(OS_CHROMEOS)
    676 void OpenPersistentNSSDB() {
    677   g_nss_singleton.Get().OpenPersistentNSSDB();
    678 }
    679 
    680 TPMTokenInfoDelegate::TPMTokenInfoDelegate() {}
    681 TPMTokenInfoDelegate::~TPMTokenInfoDelegate() {}
    682 
    683 void EnableTPMTokenForNSS(TPMTokenInfoDelegate* info_delegate) {
    684   g_nss_singleton.Get().EnableTPMTokenForNSS(info_delegate);
    685 }
    686 
    687 void GetTPMTokenInfo(std::string* token_name, std::string* user_pin) {
    688   g_nss_singleton.Get().GetTPMTokenInfo(token_name, user_pin);
    689 }
    690 
    691 bool IsTPMTokenReady() {
    692   return g_nss_singleton.Get().IsTPMTokenReady();
    693 }
    694 
    695 #endif  // defined(OS_CHROMEOS)
    696 
    697 // TODO(port): Implement this more simply.  We can convert by subtracting an
    698 // offset (the difference between NSPR's and base::Time's epochs).
    699 base::Time PRTimeToBaseTime(PRTime prtime) {
    700   PRExplodedTime prxtime;
    701   PR_ExplodeTime(prtime, PR_GMTParameters, &prxtime);
    702 
    703   base::Time::Exploded exploded;
    704   exploded.year         = prxtime.tm_year;
    705   exploded.month        = prxtime.tm_month + 1;
    706   exploded.day_of_week  = prxtime.tm_wday;
    707   exploded.day_of_month = prxtime.tm_mday;
    708   exploded.hour         = prxtime.tm_hour;
    709   exploded.minute       = prxtime.tm_min;
    710   exploded.second       = prxtime.tm_sec;
    711   exploded.millisecond  = prxtime.tm_usec / 1000;
    712 
    713   return base::Time::FromUTCExploded(exploded);
    714 }
    715 
    716 PK11SlotInfo* GetPublicNSSKeySlot() {
    717   return g_nss_singleton.Get().GetPublicNSSKeySlot();
    718 }
    719 
    720 PK11SlotInfo* GetPrivateNSSKeySlot() {
    721   return g_nss_singleton.Get().GetPrivateNSSKeySlot();
    722 }
    723 
    724 }  // namespace crypto
    725