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
43namespace crypto {
44
45namespace {
46
47#if defined(OS_CHROMEOS)
48const char kNSSDatabaseName[] = "Real NSS database";
49
50// Constants for loading opencryptoki.
51const char kOpencryptokiModuleName[] = "opencryptoki";
52const char kOpencryptokiPath[] = "/usr/lib/opencryptoki/libopencryptoki.so";
53
54// Fake certificate authority database used for testing.
55static const FilePath::CharType kReadOnlyCertDB[] =
56    FILE_PATH_LITERAL("/etc/fake_root_ca/nssdb");
57#endif  // defined(OS_CHROMEOS)
58
59std::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)
72FilePath 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.
92FilePath 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.
102char* 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.
144void 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.
160class 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
176PK11SlotInfo* 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.
196class 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
216base::LazyInstance<NSPRInitSingleton,
217                   base::LeakyLazyInstanceTraits<NSPRInitSingleton> >
218    g_nspr_singleton(base::LINKER_INITIALIZED);
219
220class 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
568bool NSSInitSingleton::force_nodb_init_ = false;
569
570base::LazyInstance<NSSInitSingleton,
571                   base::LeakyLazyInstanceTraits<NSSInitSingleton> >
572    g_nss_singleton(base::LINKER_INITIALIZED);
573
574}  // namespace
575
576#if defined(USE_NSS)
577void EarlySetupForNSSInit() {
578  FilePath database_dir = GetInitialConfigDirectory();
579  if (!database_dir.empty())
580    UseLocalCacheOfNSSDatabaseIfNFS(database_dir);
581}
582#endif
583
584void EnsureNSPRInit() {
585  g_nspr_singleton.Get();
586}
587
588void 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
596void ForceNSSNoDBInit() {
597  NSSInitSingleton::ForceNoDBInit();
598}
599
600void DisableNSSForkCheck() {
601  scoped_ptr<base::Environment> env(base::Environment::Create());
602  env->SetVar("NSS_STRICT_NOFORK", "DISABLED");
603}
604
605void 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
644bool CheckNSSVersion(const char* version) {
645  return !!NSS_VersionCheck(version);
646}
647
648#if defined(USE_NSS)
649bool OpenTestNSSDB(const FilePath& path, const char* description) {
650  return g_nss_singleton.Get().OpenTestNSSDB(path, description);
651}
652
653void CloseTestNSSDB() {
654  g_nss_singleton.Get().CloseTestNSSDB();
655}
656
657base::Lock* GetNSSWriteLock() {
658  return g_nss_singleton.Get().write_lock();
659}
660
661AutoNSSWriteLock::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
667AutoNSSWriteLock::~AutoNSSWriteLock() {
668  if (lock_) {
669    lock_->AssertAcquired();
670    lock_->Release();
671  }
672}
673#endif  // defined(USE_NSS)
674
675#if defined(OS_CHROMEOS)
676void OpenPersistentNSSDB() {
677  g_nss_singleton.Get().OpenPersistentNSSDB();
678}
679
680TPMTokenInfoDelegate::TPMTokenInfoDelegate() {}
681TPMTokenInfoDelegate::~TPMTokenInfoDelegate() {}
682
683void EnableTPMTokenForNSS(TPMTokenInfoDelegate* info_delegate) {
684  g_nss_singleton.Get().EnableTPMTokenForNSS(info_delegate);
685}
686
687void GetTPMTokenInfo(std::string* token_name, std::string* user_pin) {
688  g_nss_singleton.Get().GetTPMTokenInfo(token_name, user_pin);
689}
690
691bool 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).
699base::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
716PK11SlotInfo* GetPublicNSSKeySlot() {
717  return g_nss_singleton.Get().GetPublicNSSKeySlot();
718}
719
720PK11SlotInfo* GetPrivateNSSKeySlot() {
721  return g_nss_singleton.Get().GetPrivateNSSKeySlot();
722}
723
724}  // namespace crypto
725