• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <map>
25 #include <vector>
26 
27 #include "base/callback.h"
28 #include "base/cpu.h"
29 #include "base/debug/alias.h"
30 #include "base/debug/stack_trace.h"
31 #include "base/environment.h"
32 #include "base/file_util.h"
33 #include "base/files/file_path.h"
34 #include "base/files/scoped_temp_dir.h"
35 #include "base/lazy_instance.h"
36 #include "base/logging.h"
37 #include "base/memory/scoped_ptr.h"
38 #include "base/metrics/histogram.h"
39 #include "base/native_library.h"
40 #include "base/stl_util.h"
41 #include "base/strings/stringprintf.h"
42 #include "base/threading/thread_checker.h"
43 #include "base/threading/thread_restrictions.h"
44 #include "build/build_config.h"
45 
46 // USE_NSS means we use NSS for everything crypto-related.  If USE_NSS is not
47 // defined, such as on Mac and Windows, we use NSS for SSL only -- we don't
48 // use NSS for crypto or certificate verification, and we don't use the NSS
49 // certificate and key databases.
50 #if defined(USE_NSS)
51 #include "base/synchronization/lock.h"
52 #include "crypto/nss_crypto_module_delegate.h"
53 #endif  // defined(USE_NSS)
54 
55 namespace crypto {
56 
57 namespace {
58 
59 #if defined(OS_CHROMEOS)
60 const char kNSSDatabaseName[] = "Real NSS database";
61 
62 // Constants for loading the Chrome OS TPM-backed PKCS #11 library.
63 const char kChapsModuleName[] = "Chaps";
64 const char kChapsPath[] = "libchaps.so";
65 
66 // Fake certificate authority database used for testing.
67 static const base::FilePath::CharType kReadOnlyCertDB[] =
68     FILE_PATH_LITERAL("/etc/fake_root_ca/nssdb");
69 #endif  // defined(OS_CHROMEOS)
70 
GetNSSErrorMessage()71 std::string GetNSSErrorMessage() {
72   std::string result;
73   if (PR_GetErrorTextLength()) {
74     scoped_ptr<char[]> error_text(new char[PR_GetErrorTextLength() + 1]);
75     PRInt32 copied = PR_GetErrorText(error_text.get());
76     result = std::string(error_text.get(), copied);
77   } else {
78     result = base::StringPrintf("NSS error code: %d", PR_GetError());
79   }
80   return result;
81 }
82 
83 #if defined(USE_NSS)
GetDefaultConfigDirectory()84 base::FilePath GetDefaultConfigDirectory() {
85   base::FilePath dir = base::GetHomeDir();
86   if (dir.empty()) {
87     LOG(ERROR) << "Failed to get home directory.";
88     return dir;
89   }
90   dir = dir.AppendASCII(".pki").AppendASCII("nssdb");
91   if (!base::CreateDirectory(dir)) {
92     LOG(ERROR) << "Failed to create " << dir.value() << " directory.";
93     dir.clear();
94   }
95   DVLOG(2) << "DefaultConfigDirectory: " << dir.value();
96   return dir;
97 }
98 
99 // On non-Chrome OS platforms, return the default config directory. On Chrome OS
100 // test images, return a read-only directory with fake root CA certs (which are
101 // used by the local Google Accounts server mock we use when testing our login
102 // code). On Chrome OS non-test images (where the read-only directory doesn't
103 // exist), return an empty path.
GetInitialConfigDirectory()104 base::FilePath GetInitialConfigDirectory() {
105 #if defined(OS_CHROMEOS)
106   base::FilePath database_dir = base::FilePath(kReadOnlyCertDB);
107   if (!base::PathExists(database_dir))
108     database_dir.clear();
109   return database_dir;
110 #else
111   return GetDefaultConfigDirectory();
112 #endif  // defined(OS_CHROMEOS)
113 }
114 
115 // This callback for NSS forwards all requests to a caller-specified
116 // CryptoModuleBlockingPasswordDelegate object.
PKCS11PasswordFunc(PK11SlotInfo * slot,PRBool retry,void * arg)117 char* PKCS11PasswordFunc(PK11SlotInfo* slot, PRBool retry, void* arg) {
118   crypto::CryptoModuleBlockingPasswordDelegate* delegate =
119       reinterpret_cast<crypto::CryptoModuleBlockingPasswordDelegate*>(arg);
120   if (delegate) {
121     bool cancelled = false;
122     std::string password = delegate->RequestPassword(PK11_GetTokenName(slot),
123                                                      retry != PR_FALSE,
124                                                      &cancelled);
125     if (cancelled)
126       return NULL;
127     char* result = PORT_Strdup(password.c_str());
128     password.replace(0, password.size(), password.size(), 0);
129     return result;
130   }
131   DLOG(ERROR) << "PK11 password requested with NULL arg";
132   return NULL;
133 }
134 
135 // NSS creates a local cache of the sqlite database if it detects that the
136 // filesystem the database is on is much slower than the local disk.  The
137 // detection doesn't work with the latest versions of sqlite, such as 3.6.22
138 // (NSS bug https://bugzilla.mozilla.org/show_bug.cgi?id=578561).  So we set
139 // the NSS environment variable NSS_SDB_USE_CACHE to "yes" to override NSS's
140 // detection when database_dir is on NFS.  See http://crbug.com/48585.
141 //
142 // TODO(wtc): port this function to other USE_NSS platforms.  It is defined
143 // only for OS_LINUX and OS_OPENBSD simply because the statfs structure
144 // is OS-specific.
145 //
146 // Because this function sets an environment variable it must be run before we
147 // go multi-threaded.
UseLocalCacheOfNSSDatabaseIfNFS(const base::FilePath & database_dir)148 void UseLocalCacheOfNSSDatabaseIfNFS(const base::FilePath& database_dir) {
149 #if defined(OS_LINUX) || defined(OS_OPENBSD)
150   struct statfs buf;
151   if (statfs(database_dir.value().c_str(), &buf) == 0) {
152 #if defined(OS_LINUX)
153     if (buf.f_type == NFS_SUPER_MAGIC) {
154 #elif defined(OS_OPENBSD)
155     if (strcmp(buf.f_fstypename, MOUNT_NFS) == 0) {
156 #endif
157       scoped_ptr<base::Environment> env(base::Environment::Create());
158       const char* use_cache_env_var = "NSS_SDB_USE_CACHE";
159       if (!env->HasVar(use_cache_env_var))
160         env->SetVar(use_cache_env_var, "yes");
161     }
162   }
163 #endif  // defined(OS_LINUX) || defined(OS_OPENBSD)
164 }
165 
166 #endif  // defined(USE_NSS)
167 
168 // A singleton to initialize/deinitialize NSPR.
169 // Separate from the NSS singleton because we initialize NSPR on the UI thread.
170 // Now that we're leaking the singleton, we could merge back with the NSS
171 // singleton.
172 class NSPRInitSingleton {
173  private:
174   friend struct base::DefaultLazyInstanceTraits<NSPRInitSingleton>;
175 
176   NSPRInitSingleton() {
177     PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
178   }
179 
180   // NOTE(willchan): We don't actually execute this code since we leak NSS to
181   // prevent non-joinable threads from using NSS after it's already been shut
182   // down.
183   ~NSPRInitSingleton() {
184     PL_ArenaFinish();
185     PRStatus prstatus = PR_Cleanup();
186     if (prstatus != PR_SUCCESS)
187       LOG(ERROR) << "PR_Cleanup failed; was NSPR initialized on wrong thread?";
188   }
189 };
190 
191 base::LazyInstance<NSPRInitSingleton>::Leaky
192     g_nspr_singleton = LAZY_INSTANCE_INITIALIZER;
193 
194 // This is a LazyInstance so that it will be deleted automatically when the
195 // unittest exits.  NSSInitSingleton is a LeakySingleton, so it would not be
196 // deleted if it were a regular member.
197 base::LazyInstance<base::ScopedTempDir> g_test_nss_db_dir =
198     LAZY_INSTANCE_INITIALIZER;
199 
200 // Force a crash with error info on NSS_NoDB_Init failure.
201 void CrashOnNSSInitFailure() {
202   int nss_error = PR_GetError();
203   int os_error = PR_GetOSError();
204   base::debug::Alias(&nss_error);
205   base::debug::Alias(&os_error);
206   LOG(ERROR) << "Error initializing NSS without a persistent database: "
207              << GetNSSErrorMessage();
208   LOG(FATAL) << "nss_error=" << nss_error << ", os_error=" << os_error;
209 }
210 
211 #if defined(OS_CHROMEOS)
212 class ChromeOSUserData {
213  public:
214   ChromeOSUserData(ScopedPK11Slot public_slot, bool is_primary_user)
215       : public_slot_(public_slot.Pass()),
216         is_primary_user_(is_primary_user) {}
217   ~ChromeOSUserData() {
218     if (public_slot_ && !is_primary_user_) {
219       SECStatus status = SECMOD_CloseUserDB(public_slot_.get());
220       if (status != SECSuccess)
221         PLOG(ERROR) << "SECMOD_CloseUserDB failed: " << PORT_GetError();
222     }
223   }
224 
225   ScopedPK11Slot GetPublicSlot() {
226     return ScopedPK11Slot(
227         public_slot_ ? PK11_ReferenceSlot(public_slot_.get()) : NULL);
228   }
229 
230   ScopedPK11Slot GetPrivateSlot(
231       const base::Callback<void(ScopedPK11Slot)>& callback) {
232     if (private_slot_)
233       return ScopedPK11Slot(PK11_ReferenceSlot(private_slot_.get()));
234     if (!callback.is_null())
235       tpm_ready_callback_list_.push_back(callback);
236     return ScopedPK11Slot();
237   }
238 
239   void SetPrivateSlot(ScopedPK11Slot private_slot) {
240     DCHECK(!private_slot_);
241     private_slot_ = private_slot.Pass();
242 
243     SlotReadyCallbackList callback_list;
244     callback_list.swap(tpm_ready_callback_list_);
245     for (SlotReadyCallbackList::iterator i = callback_list.begin();
246          i != callback_list.end();
247          ++i) {
248       (*i).Run(ScopedPK11Slot(PK11_ReferenceSlot(private_slot_.get())));
249     }
250   }
251 
252  private:
253   ScopedPK11Slot public_slot_;
254   ScopedPK11Slot private_slot_;
255   bool is_primary_user_;
256 
257   typedef std::vector<base::Callback<void(ScopedPK11Slot)> >
258       SlotReadyCallbackList;
259   SlotReadyCallbackList tpm_ready_callback_list_;
260 };
261 #endif  // defined(OS_CHROMEOS)
262 
263 class NSSInitSingleton {
264  public:
265 #if defined(OS_CHROMEOS)
266   void OpenPersistentNSSDB() {
267     DCHECK(thread_checker_.CalledOnValidThread());
268 
269     if (!chromeos_user_logged_in_) {
270       // GetDefaultConfigDirectory causes us to do blocking IO on UI thread.
271       // Temporarily allow it until we fix http://crbug.com/70119
272       base::ThreadRestrictions::ScopedAllowIO allow_io;
273       chromeos_user_logged_in_ = true;
274 
275       // This creates another DB slot in NSS that is read/write, unlike
276       // the fake root CA cert DB and the "default" crypto key
277       // provider, which are still read-only (because we initialized
278       // NSS before we had a cryptohome mounted).
279       software_slot_ = OpenUserDB(GetDefaultConfigDirectory(),
280                                   kNSSDatabaseName);
281     }
282   }
283 
284   PK11SlotInfo* OpenPersistentNSSDBForPath(const base::FilePath& path) {
285     DCHECK(thread_checker_.CalledOnValidThread());
286     // NSS is allowed to do IO on the current thread since dispatching
287     // to a dedicated thread would still have the affect of blocking
288     // the current thread, due to NSS's internal locking requirements
289     base::ThreadRestrictions::ScopedAllowIO allow_io;
290 
291     base::FilePath nssdb_path = path.AppendASCII(".pki").AppendASCII("nssdb");
292     if (!base::CreateDirectory(nssdb_path)) {
293       LOG(ERROR) << "Failed to create " << nssdb_path.value() << " directory.";
294       return NULL;
295     }
296     return OpenUserDB(nssdb_path, kNSSDatabaseName);
297   }
298 
299   void EnableTPMTokenForNSS() {
300     DCHECK(thread_checker_.CalledOnValidThread());
301 
302     // If this gets set, then we'll use the TPM for certs with
303     // private keys, otherwise we'll fall back to the software
304     // implementation.
305     tpm_token_enabled_for_nss_ = true;
306   }
307 
308   bool IsTPMTokenEnabledForNSS() {
309     DCHECK(thread_checker_.CalledOnValidThread());
310     return tpm_token_enabled_for_nss_;
311   }
312 
313   bool InitializeTPMToken(int token_slot_id) {
314     DCHECK(thread_checker_.CalledOnValidThread());
315 
316     // If EnableTPMTokenForNSS hasn't been called, return false.
317     if (!tpm_token_enabled_for_nss_)
318       return false;
319 
320     // If everything is already initialized, then return true.
321     if (chaps_module_ && tpm_slot_)
322       return true;
323 
324     // This tries to load the Chaps module so NSS can talk to the hardware
325     // TPM.
326     if (!chaps_module_) {
327       chaps_module_ = LoadModule(
328           kChapsModuleName,
329           kChapsPath,
330           // For more details on these parameters, see:
331           // https://developer.mozilla.org/en/PKCS11_Module_Specs
332           // slotFlags=[PublicCerts] -- Certificates and public keys can be
333           //   read from this slot without requiring a call to C_Login.
334           // askpw=only -- Only authenticate to the token when necessary.
335           "NSS=\"slotParams=(0={slotFlags=[PublicCerts] askpw=only})\"");
336       if (!chaps_module_ && test_slot_) {
337         // chromeos_unittests try to test the TPM initialization process. If we
338         // have a test DB open, pretend that it is the TPM slot.
339         tpm_slot_ = PK11_ReferenceSlot(test_slot_);
340         return true;
341       }
342     }
343     if (chaps_module_){
344       tpm_slot_ = GetTPMSlotForId(token_slot_id);
345 
346       if (!tpm_slot_)
347         return false;
348 
349       TPMReadyCallbackList callback_list;
350       callback_list.swap(tpm_ready_callback_list_);
351       for (TPMReadyCallbackList::iterator i =
352                callback_list.begin();
353            i != callback_list.end();
354            ++i) {
355         (*i).Run();
356       }
357 
358       return true;
359     }
360     return false;
361   }
362 
363   bool IsTPMTokenReady(const base::Closure& callback) {
364     if (!callback.is_null()) {
365       // Cannot DCHECK in the general case yet, but since the callback is
366       // a new addition to the API, DCHECK to make sure at least the new uses
367       // don't regress.
368       DCHECK(thread_checker_.CalledOnValidThread());
369     } else if (!thread_checker_.CalledOnValidThread()) {
370       // TODO(mattm): Change to DCHECK when callers have been fixed.
371       DVLOG(1) << "Called on wrong thread.\n"
372                << base::debug::StackTrace().ToString();
373     }
374 
375     if (tpm_slot_ != NULL)
376       return true;
377 
378     if (!callback.is_null())
379       tpm_ready_callback_list_.push_back(callback);
380 
381     return false;
382   }
383 
384   // Note that CK_SLOT_ID is an unsigned long, but cryptohome gives us the slot
385   // id as an int. This should be safe since this is only used with chaps, which
386   // we also control.
387   PK11SlotInfo* GetTPMSlotForId(CK_SLOT_ID slot_id) {
388     DCHECK(thread_checker_.CalledOnValidThread());
389 
390     if (!chaps_module_)
391       return NULL;
392 
393     DVLOG(3) << "Poking chaps module.";
394     SECStatus rv = SECMOD_UpdateSlotList(chaps_module_);
395     if (rv != SECSuccess)
396       PLOG(ERROR) << "SECMOD_UpdateSlotList failed: " << PORT_GetError();
397 
398     PK11SlotInfo* slot = SECMOD_LookupSlot(chaps_module_->moduleID, slot_id);
399     if (!slot)
400       LOG(ERROR) << "TPM slot " << slot_id << " not found.";
401     return slot;
402   }
403 
404   bool InitializeNSSForChromeOSUser(
405       const std::string& email,
406       const std::string& username_hash,
407       bool is_primary_user,
408       const base::FilePath& path) {
409     DCHECK(thread_checker_.CalledOnValidThread());
410     if (chromeos_user_map_.find(username_hash) != chromeos_user_map_.end()) {
411       // This user already exists in our mapping.
412       DVLOG(2) << username_hash << " already initialized.";
413       return false;
414     }
415     ScopedPK11Slot public_slot;
416     if (is_primary_user) {
417       DVLOG(2) << "Primary user, using GetPublicNSSKeySlot()";
418       public_slot.reset(GetPublicNSSKeySlot());
419     } else {
420       DVLOG(2) << "Opening NSS DB " << path.value();
421       public_slot.reset(OpenPersistentNSSDBForPath(path));
422     }
423     chromeos_user_map_[username_hash] =
424         new ChromeOSUserData(public_slot.Pass(), is_primary_user);
425     return true;
426   }
427 
428   void InitializeTPMForChromeOSUser(const std::string& username_hash,
429                                     CK_SLOT_ID slot_id) {
430     DCHECK(thread_checker_.CalledOnValidThread());
431     DCHECK(chromeos_user_map_.find(username_hash) != chromeos_user_map_.end());
432     chromeos_user_map_[username_hash]
433         ->SetPrivateSlot(ScopedPK11Slot(GetTPMSlotForId(slot_id)));
434   }
435 
436   void InitializePrivateSoftwareSlotForChromeOSUser(
437       const std::string& username_hash) {
438     DCHECK(thread_checker_.CalledOnValidThread());
439     LOG(WARNING) << "using software private slot for " << username_hash;
440     DCHECK(chromeos_user_map_.find(username_hash) != chromeos_user_map_.end());
441     chromeos_user_map_[username_hash]->SetPrivateSlot(
442         chromeos_user_map_[username_hash]->GetPublicSlot());
443   }
444 
445   ScopedPK11Slot GetPublicSlotForChromeOSUser(
446       const std::string& username_hash) {
447     DCHECK(thread_checker_.CalledOnValidThread());
448     if (test_slot_) {
449       DVLOG(2) << "returning test_slot_ for " << username_hash;
450       return ScopedPK11Slot(PK11_ReferenceSlot(test_slot_));
451     }
452 
453     if (chromeos_user_map_.find(username_hash) == chromeos_user_map_.end()) {
454       LOG(ERROR) << username_hash << " not initialized.";
455       return ScopedPK11Slot();
456     }
457     return chromeos_user_map_[username_hash]->GetPublicSlot();
458   }
459 
460   ScopedPK11Slot GetPrivateSlotForChromeOSUser(
461       const std::string& username_hash,
462       const base::Callback<void(ScopedPK11Slot)>& callback) {
463     DCHECK(thread_checker_.CalledOnValidThread());
464     DCHECK(chromeos_user_map_.find(username_hash) != chromeos_user_map_.end());
465 
466     if (test_slot_) {
467       DVLOG(2) << "returning test_slot_ for " << username_hash;
468       return ScopedPK11Slot(PK11_ReferenceSlot(test_slot_));
469     }
470 
471     return chromeos_user_map_[username_hash]->GetPrivateSlot(callback);
472   }
473 #endif  // defined(OS_CHROMEOS)
474 
475 
476   bool OpenTestNSSDB() {
477     DCHECK(thread_checker_.CalledOnValidThread());
478     // NSS is allowed to do IO on the current thread since dispatching
479     // to a dedicated thread would still have the affect of blocking
480     // the current thread, due to NSS's internal locking requirements
481     base::ThreadRestrictions::ScopedAllowIO allow_io;
482 
483     if (test_slot_)
484       return true;
485     if (!g_test_nss_db_dir.Get().CreateUniqueTempDir())
486       return false;
487     test_slot_ = OpenUserDB(g_test_nss_db_dir.Get().path(), kTestTPMTokenName);
488     return !!test_slot_;
489   }
490 
491   void CloseTestNSSDB() {
492     DCHECK(thread_checker_.CalledOnValidThread());
493     // NSS is allowed to do IO on the current thread since dispatching
494     // to a dedicated thread would still have the affect of blocking
495     // the current thread, due to NSS's internal locking requirements
496     base::ThreadRestrictions::ScopedAllowIO allow_io;
497 
498     if (!test_slot_)
499       return;
500     SECStatus status = SECMOD_CloseUserDB(test_slot_);
501     if (status != SECSuccess)
502       PLOG(ERROR) << "SECMOD_CloseUserDB failed: " << PORT_GetError();
503     PK11_FreeSlot(test_slot_);
504     test_slot_ = NULL;
505     ignore_result(g_test_nss_db_dir.Get().Delete());
506   }
507 
508   PK11SlotInfo* GetPublicNSSKeySlot() {
509     // TODO(mattm): Change to DCHECK when callers have been fixed.
510     if (!thread_checker_.CalledOnValidThread()) {
511       DVLOG(1) << "Called on wrong thread.\n"
512                << base::debug::StackTrace().ToString();
513     }
514 
515     if (test_slot_)
516       return PK11_ReferenceSlot(test_slot_);
517     if (software_slot_)
518       return PK11_ReferenceSlot(software_slot_);
519     return PK11_GetInternalKeySlot();
520   }
521 
522   PK11SlotInfo* GetPrivateNSSKeySlot() {
523     // TODO(mattm): Change to DCHECK when callers have been fixed.
524     if (!thread_checker_.CalledOnValidThread()) {
525       DVLOG(1) << "Called on wrong thread.\n"
526                << base::debug::StackTrace().ToString();
527     }
528 
529     if (test_slot_)
530       return PK11_ReferenceSlot(test_slot_);
531 
532 #if defined(OS_CHROMEOS)
533     if (tpm_token_enabled_for_nss_) {
534       if (IsTPMTokenReady(base::Closure())) {
535         return PK11_ReferenceSlot(tpm_slot_);
536       } else {
537         // If we were supposed to get the hardware token, but were
538         // unable to, return NULL rather than fall back to sofware.
539         return NULL;
540       }
541     }
542 #endif
543     // If we weren't supposed to enable the TPM for NSS, then return
544     // the software slot.
545     if (software_slot_)
546       return PK11_ReferenceSlot(software_slot_);
547     return PK11_GetInternalKeySlot();
548   }
549 
550 #if defined(USE_NSS)
551   base::Lock* write_lock() {
552     return &write_lock_;
553   }
554 #endif  // defined(USE_NSS)
555 
556   // This method is used to force NSS to be initialized without a DB.
557   // Call this method before NSSInitSingleton() is constructed.
558   static void ForceNoDBInit() {
559     force_nodb_init_ = true;
560   }
561 
562  private:
563   friend struct base::DefaultLazyInstanceTraits<NSSInitSingleton>;
564 
565   NSSInitSingleton()
566       : tpm_token_enabled_for_nss_(false),
567         chaps_module_(NULL),
568         software_slot_(NULL),
569         test_slot_(NULL),
570         tpm_slot_(NULL),
571         root_(NULL),
572         chromeos_user_logged_in_(false) {
573     base::TimeTicks start_time = base::TimeTicks::Now();
574 
575     // It's safe to construct on any thread, since LazyInstance will prevent any
576     // other threads from accessing until the constructor is done.
577     thread_checker_.DetachFromThread();
578 
579     DisableAESNIIfNeeded();
580 
581     EnsureNSPRInit();
582 
583     // We *must* have NSS >= 3.14.3.
584     COMPILE_ASSERT(
585         (NSS_VMAJOR == 3 && NSS_VMINOR == 14 && NSS_VPATCH >= 3) ||
586         (NSS_VMAJOR == 3 && NSS_VMINOR > 14) ||
587         (NSS_VMAJOR > 3),
588         nss_version_check_failed);
589     // Also check the run-time NSS version.
590     // NSS_VersionCheck is a >= check, not strict equality.
591     if (!NSS_VersionCheck("3.14.3")) {
592       LOG(FATAL) << "NSS_VersionCheck(\"3.14.3\") failed. NSS >= 3.14.3 is "
593                     "required. Please upgrade to the latest NSS, and if you "
594                     "still get this error, contact your distribution "
595                     "maintainer.";
596     }
597 
598     SECStatus status = SECFailure;
599     bool nodb_init = force_nodb_init_;
600 
601 #if !defined(USE_NSS)
602     // Use the system certificate store, so initialize NSS without database.
603     nodb_init = true;
604 #endif
605 
606     if (nodb_init) {
607       status = NSS_NoDB_Init(NULL);
608       if (status != SECSuccess) {
609         CrashOnNSSInitFailure();
610         return;
611       }
612 #if defined(OS_IOS)
613       root_ = InitDefaultRootCerts();
614 #endif  // defined(OS_IOS)
615     } else {
616 #if defined(USE_NSS)
617       base::FilePath database_dir = GetInitialConfigDirectory();
618       if (!database_dir.empty()) {
619         // This duplicates the work which should have been done in
620         // EarlySetupForNSSInit. However, this function is idempotent so
621         // there's no harm done.
622         UseLocalCacheOfNSSDatabaseIfNFS(database_dir);
623 
624         // Initialize with a persistent database (likely, ~/.pki/nssdb).
625         // Use "sql:" which can be shared by multiple processes safely.
626         std::string nss_config_dir =
627             base::StringPrintf("sql:%s", database_dir.value().c_str());
628 #if defined(OS_CHROMEOS)
629         status = NSS_Init(nss_config_dir.c_str());
630 #else
631         status = NSS_InitReadWrite(nss_config_dir.c_str());
632 #endif
633         if (status != SECSuccess) {
634           LOG(ERROR) << "Error initializing NSS with a persistent "
635                         "database (" << nss_config_dir
636                      << "): " << GetNSSErrorMessage();
637         }
638       }
639       if (status != SECSuccess) {
640         VLOG(1) << "Initializing NSS without a persistent database.";
641         status = NSS_NoDB_Init(NULL);
642         if (status != SECSuccess) {
643           CrashOnNSSInitFailure();
644           return;
645         }
646       }
647 
648       PK11_SetPasswordFunc(PKCS11PasswordFunc);
649 
650       // If we haven't initialized the password for the NSS databases,
651       // initialize an empty-string password so that we don't need to
652       // log in.
653       PK11SlotInfo* slot = PK11_GetInternalKeySlot();
654       if (slot) {
655         // PK11_InitPin may write to the keyDB, but no other thread can use NSS
656         // yet, so we don't need to lock.
657         if (PK11_NeedUserInit(slot))
658           PK11_InitPin(slot, NULL, NULL);
659         PK11_FreeSlot(slot);
660       }
661 
662       root_ = InitDefaultRootCerts();
663 #endif  // defined(USE_NSS)
664     }
665 
666     // Disable MD5 certificate signatures. (They are disabled by default in
667     // NSS 3.14.)
668     NSS_SetAlgorithmPolicy(SEC_OID_MD5, 0, NSS_USE_ALG_IN_CERT_SIGNATURE);
669     NSS_SetAlgorithmPolicy(SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION,
670                            0, NSS_USE_ALG_IN_CERT_SIGNATURE);
671 
672     // The UMA bit is conditionally set for this histogram in
673     // chrome/common/startup_metric_utils.cc .
674     HISTOGRAM_CUSTOM_TIMES("Startup.SlowStartupNSSInit",
675                            base::TimeTicks::Now() - start_time,
676                            base::TimeDelta::FromMilliseconds(10),
677                            base::TimeDelta::FromHours(1),
678                            50);
679   }
680 
681   // NOTE(willchan): We don't actually execute this code since we leak NSS to
682   // prevent non-joinable threads from using NSS after it's already been shut
683   // down.
684   ~NSSInitSingleton() {
685 #if defined(OS_CHROMEOS)
686     STLDeleteValues(&chromeos_user_map_);
687 #endif
688     if (tpm_slot_) {
689       PK11_FreeSlot(tpm_slot_);
690       tpm_slot_ = NULL;
691     }
692     if (software_slot_) {
693       SECMOD_CloseUserDB(software_slot_);
694       PK11_FreeSlot(software_slot_);
695       software_slot_ = NULL;
696     }
697     CloseTestNSSDB();
698     if (root_) {
699       SECMOD_UnloadUserModule(root_);
700       SECMOD_DestroyModule(root_);
701       root_ = NULL;
702     }
703     if (chaps_module_) {
704       SECMOD_UnloadUserModule(chaps_module_);
705       SECMOD_DestroyModule(chaps_module_);
706       chaps_module_ = NULL;
707     }
708 
709     SECStatus status = NSS_Shutdown();
710     if (status != SECSuccess) {
711       // We VLOG(1) because this failure is relatively harmless (leaking, but
712       // we're shutting down anyway).
713       VLOG(1) << "NSS_Shutdown failed; see http://crbug.com/4609";
714     }
715   }
716 
717 #if defined(USE_NSS) || defined(OS_IOS)
718   // Load nss's built-in root certs.
719   SECMODModule* InitDefaultRootCerts() {
720     SECMODModule* root = LoadModule("Root Certs", "libnssckbi.so", NULL);
721     if (root)
722       return root;
723 
724     // Aw, snap.  Can't find/load root cert shared library.
725     // This will make it hard to talk to anybody via https.
726     // TODO(mattm): Re-add the NOTREACHED here when crbug.com/310972 is fixed.
727     return NULL;
728   }
729 
730   // Load the given module for this NSS session.
731   SECMODModule* LoadModule(const char* name,
732                            const char* library_path,
733                            const char* params) {
734     std::string modparams = base::StringPrintf(
735         "name=\"%s\" library=\"%s\" %s",
736         name, library_path, params ? params : "");
737 
738     // Shouldn't need to const_cast here, but SECMOD doesn't properly
739     // declare input string arguments as const.  Bug
740     // https://bugzilla.mozilla.org/show_bug.cgi?id=642546 was filed
741     // on NSS codebase to address this.
742     SECMODModule* module = SECMOD_LoadUserModule(
743         const_cast<char*>(modparams.c_str()), NULL, PR_FALSE);
744     if (!module) {
745       LOG(ERROR) << "Error loading " << name << " module into NSS: "
746                  << GetNSSErrorMessage();
747       return NULL;
748     }
749     if (!module->loaded) {
750       LOG(ERROR) << "After loading " << name << ", loaded==false: "
751                  << GetNSSErrorMessage();
752       SECMOD_DestroyModule(module);
753       return NULL;
754     }
755     return module;
756   }
757 #endif
758 
759   static PK11SlotInfo* OpenUserDB(const base::FilePath& path,
760                                   const char* description) {
761     const std::string modspec =
762         base::StringPrintf("configDir='sql:%s' tokenDescription='%s'",
763                            path.value().c_str(), description);
764     PK11SlotInfo* db_slot = SECMOD_OpenUserDB(modspec.c_str());
765     if (db_slot) {
766       if (PK11_NeedUserInit(db_slot))
767         PK11_InitPin(db_slot, NULL, NULL);
768     }
769     else {
770       LOG(ERROR) << "Error opening persistent database (" << modspec
771                  << "): " << GetNSSErrorMessage();
772     }
773     return db_slot;
774   }
775 
776   static void DisableAESNIIfNeeded() {
777     if (NSS_VersionCheck("3.15") && !NSS_VersionCheck("3.15.4")) {
778       // Some versions of NSS have a bug that causes AVX instructions to be
779       // used without testing whether XSAVE is enabled by the operating system.
780       // In order to work around this, we disable AES-NI in NSS when we find
781       // that |has_avx()| is false (which includes the XSAVE test). See
782       // https://bugzilla.mozilla.org/show_bug.cgi?id=940794
783       base::CPU cpu;
784 
785       if (cpu.has_avx_hardware() && !cpu.has_avx()) {
786         base::Environment::Create()->SetVar("NSS_DISABLE_HW_AES", "1");
787       }
788     }
789   }
790 
791   // If this is set to true NSS is forced to be initialized without a DB.
792   static bool force_nodb_init_;
793 
794   bool tpm_token_enabled_for_nss_;
795   typedef std::vector<base::Closure> TPMReadyCallbackList;
796   TPMReadyCallbackList tpm_ready_callback_list_;
797   SECMODModule* chaps_module_;
798   PK11SlotInfo* software_slot_;
799   PK11SlotInfo* test_slot_;
800   PK11SlotInfo* tpm_slot_;
801   SECMODModule* root_;
802   bool chromeos_user_logged_in_;
803 #if defined(OS_CHROMEOS)
804   typedef std::map<std::string, ChromeOSUserData*> ChromeOSUserMap;
805   ChromeOSUserMap chromeos_user_map_;
806 #endif
807 #if defined(USE_NSS)
808   // TODO(davidben): When https://bugzilla.mozilla.org/show_bug.cgi?id=564011
809   // is fixed, we will no longer need the lock.
810   base::Lock write_lock_;
811 #endif  // defined(USE_NSS)
812 
813   base::ThreadChecker thread_checker_;
814 };
815 
816 // static
817 bool NSSInitSingleton::force_nodb_init_ = false;
818 
819 base::LazyInstance<NSSInitSingleton>::Leaky
820     g_nss_singleton = LAZY_INSTANCE_INITIALIZER;
821 }  // namespace
822 
823 const char kTestTPMTokenName[] = "Test DB";
824 
825 #if defined(USE_NSS)
EarlySetupForNSSInit()826 void EarlySetupForNSSInit() {
827   base::FilePath database_dir = GetInitialConfigDirectory();
828   if (!database_dir.empty())
829     UseLocalCacheOfNSSDatabaseIfNFS(database_dir);
830 }
831 #endif
832 
EnsureNSPRInit()833 void EnsureNSPRInit() {
834   g_nspr_singleton.Get();
835 }
836 
InitNSSSafely()837 void InitNSSSafely() {
838   // We might fork, but we haven't loaded any security modules.
839   DisableNSSForkCheck();
840   // If we're sandboxed, we shouldn't be able to open user security modules,
841   // but it's more correct to tell NSS to not even try.
842   // Loading user security modules would have security implications.
843   ForceNSSNoDBInit();
844   // Initialize NSS.
845   EnsureNSSInit();
846 }
847 
EnsureNSSInit()848 void EnsureNSSInit() {
849   // Initializing SSL causes us to do blocking IO.
850   // Temporarily allow it until we fix
851   //   http://code.google.com/p/chromium/issues/detail?id=59847
852   base::ThreadRestrictions::ScopedAllowIO allow_io;
853   g_nss_singleton.Get();
854 }
855 
ForceNSSNoDBInit()856 void ForceNSSNoDBInit() {
857   NSSInitSingleton::ForceNoDBInit();
858 }
859 
DisableNSSForkCheck()860 void DisableNSSForkCheck() {
861   scoped_ptr<base::Environment> env(base::Environment::Create());
862   env->SetVar("NSS_STRICT_NOFORK", "DISABLED");
863 }
864 
LoadNSSLibraries()865 void LoadNSSLibraries() {
866   // Some NSS libraries are linked dynamically so load them here.
867 #if defined(USE_NSS)
868   // Try to search for multiple directories to load the libraries.
869   std::vector<base::FilePath> paths;
870 
871   // Use relative path to Search PATH for the library files.
872   paths.push_back(base::FilePath());
873 
874   // For Debian derivatives NSS libraries are located here.
875   paths.push_back(base::FilePath("/usr/lib/nss"));
876 
877   // Ubuntu 11.10 (Oneiric) and Debian Wheezy place the libraries here.
878 #if defined(ARCH_CPU_X86_64)
879   paths.push_back(base::FilePath("/usr/lib/x86_64-linux-gnu/nss"));
880 #elif defined(ARCH_CPU_X86)
881   paths.push_back(base::FilePath("/usr/lib/i386-linux-gnu/nss"));
882 #elif defined(ARCH_CPU_ARMEL)
883 #if defined(__ARM_PCS_VFP)
884   paths.push_back(base::FilePath("/usr/lib/arm-linux-gnueabihf/nss"));
885 #else
886   paths.push_back(base::FilePath("/usr/lib/arm-linux-gnueabi/nss"));
887 #endif
888 #elif defined(ARCH_CPU_MIPSEL)
889   paths.push_back(base::FilePath("/usr/lib/mipsel-linux-gnu/nss"));
890 #endif
891 
892   // A list of library files to load.
893   std::vector<std::string> libs;
894   libs.push_back("libsoftokn3.so");
895   libs.push_back("libfreebl3.so");
896 
897   // For each combination of library file and path, check for existence and
898   // then load.
899   size_t loaded = 0;
900   for (size_t i = 0; i < libs.size(); ++i) {
901     for (size_t j = 0; j < paths.size(); ++j) {
902       base::FilePath path = paths[j].Append(libs[i]);
903       base::NativeLibrary lib = base::LoadNativeLibrary(path, NULL);
904       if (lib) {
905         ++loaded;
906         break;
907       }
908     }
909   }
910 
911   if (loaded == libs.size()) {
912     VLOG(3) << "NSS libraries loaded.";
913   } else {
914     LOG(ERROR) << "Failed to load NSS libraries.";
915   }
916 #endif
917 }
918 
CheckNSSVersion(const char * version)919 bool CheckNSSVersion(const char* version) {
920   return !!NSS_VersionCheck(version);
921 }
922 
923 #if defined(USE_NSS)
ScopedTestNSSDB()924 ScopedTestNSSDB::ScopedTestNSSDB()
925   : is_open_(g_nss_singleton.Get().OpenTestNSSDB()) {
926 }
927 
~ScopedTestNSSDB()928 ScopedTestNSSDB::~ScopedTestNSSDB() {
929   // Don't close when NSS is < 3.15.1, because it would require an additional
930   // sleep for 1 second after closing the database, due to
931   // http://bugzil.la/875601.
932   if (NSS_VersionCheck("3.15.1")) {
933     g_nss_singleton.Get().CloseTestNSSDB();
934   }
935 }
936 
GetNSSWriteLock()937 base::Lock* GetNSSWriteLock() {
938   return g_nss_singleton.Get().write_lock();
939 }
940 
AutoNSSWriteLock()941 AutoNSSWriteLock::AutoNSSWriteLock() : lock_(GetNSSWriteLock()) {
942   // May be NULL if the lock is not needed in our version of NSS.
943   if (lock_)
944     lock_->Acquire();
945 }
946 
~AutoNSSWriteLock()947 AutoNSSWriteLock::~AutoNSSWriteLock() {
948   if (lock_) {
949     lock_->AssertAcquired();
950     lock_->Release();
951   }
952 }
953 
AutoSECMODListReadLock()954 AutoSECMODListReadLock::AutoSECMODListReadLock()
955       : lock_(SECMOD_GetDefaultModuleListLock()) {
956     SECMOD_GetReadLock(lock_);
957   }
958 
~AutoSECMODListReadLock()959 AutoSECMODListReadLock::~AutoSECMODListReadLock() {
960   SECMOD_ReleaseReadLock(lock_);
961 }
962 
963 #endif  // defined(USE_NSS)
964 
965 #if defined(OS_CHROMEOS)
OpenPersistentNSSDB()966 void OpenPersistentNSSDB() {
967   g_nss_singleton.Get().OpenPersistentNSSDB();
968 }
969 
EnableTPMTokenForNSS()970 void EnableTPMTokenForNSS() {
971   g_nss_singleton.Get().EnableTPMTokenForNSS();
972 }
973 
IsTPMTokenEnabledForNSS()974 bool IsTPMTokenEnabledForNSS() {
975   return g_nss_singleton.Get().IsTPMTokenEnabledForNSS();
976 }
977 
IsTPMTokenReady(const base::Closure & callback)978 bool IsTPMTokenReady(const base::Closure& callback) {
979   return g_nss_singleton.Get().IsTPMTokenReady(callback);
980 }
981 
InitializeTPMToken(int token_slot_id)982 bool InitializeTPMToken(int token_slot_id) {
983   return g_nss_singleton.Get().InitializeTPMToken(token_slot_id);
984 }
985 
InitializeNSSForChromeOSUser(const std::string & email,const std::string & username_hash,bool is_primary_user,const base::FilePath & path)986 bool InitializeNSSForChromeOSUser(
987     const std::string& email,
988     const std::string& username_hash,
989     bool is_primary_user,
990     const base::FilePath& path) {
991   return g_nss_singleton.Get().InitializeNSSForChromeOSUser(
992       email, username_hash, is_primary_user, path);
993 }
InitializeTPMForChromeOSUser(const std::string & username_hash,CK_SLOT_ID slot_id)994 void InitializeTPMForChromeOSUser(
995     const std::string& username_hash,
996     CK_SLOT_ID slot_id) {
997   g_nss_singleton.Get().InitializeTPMForChromeOSUser(username_hash, slot_id);
998 }
InitializePrivateSoftwareSlotForChromeOSUser(const std::string & username_hash)999 void InitializePrivateSoftwareSlotForChromeOSUser(
1000     const std::string& username_hash) {
1001   g_nss_singleton.Get().InitializePrivateSoftwareSlotForChromeOSUser(
1002       username_hash);
1003 }
GetPublicSlotForChromeOSUser(const std::string & username_hash)1004 ScopedPK11Slot GetPublicSlotForChromeOSUser(const std::string& username_hash) {
1005   return g_nss_singleton.Get().GetPublicSlotForChromeOSUser(username_hash);
1006 }
GetPrivateSlotForChromeOSUser(const std::string & username_hash,const base::Callback<void (ScopedPK11Slot)> & callback)1007 ScopedPK11Slot GetPrivateSlotForChromeOSUser(
1008     const std::string& username_hash,
1009     const base::Callback<void(ScopedPK11Slot)>& callback) {
1010   return g_nss_singleton.Get().GetPrivateSlotForChromeOSUser(username_hash,
1011                                                              callback);
1012 }
1013 #endif  // defined(OS_CHROMEOS)
1014 
PRTimeToBaseTime(PRTime prtime)1015 base::Time PRTimeToBaseTime(PRTime prtime) {
1016   return base::Time::FromInternalValue(
1017       prtime + base::Time::UnixEpoch().ToInternalValue());
1018 }
1019 
BaseTimeToPRTime(base::Time time)1020 PRTime BaseTimeToPRTime(base::Time time) {
1021   return time.ToInternalValue() - base::Time::UnixEpoch().ToInternalValue();
1022 }
1023 
GetPublicNSSKeySlot()1024 PK11SlotInfo* GetPublicNSSKeySlot() {
1025   return g_nss_singleton.Get().GetPublicNSSKeySlot();
1026 }
1027 
GetPrivateNSSKeySlot()1028 PK11SlotInfo* GetPrivateNSSKeySlot() {
1029   return g_nss_singleton.Get().GetPrivateNSSKeySlot();
1030 }
1031 
1032 }  // namespace crypto
1033