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