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