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