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