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::__anonb0fc516a0111::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