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