1 // Copyright 2012 The Chromium Authors
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
19 #include "base/callback_list.h"
20 #include "base/containers/contains.h"
21 #include "base/debug/stack_trace.h"
22 #include "base/files/file_enumerator.h"
23 #include "base/files/file_path.h"
24 #include "base/files/file_util.h"
25 #include "base/functional/bind.h"
26 #include "base/lazy_instance.h"
27 #include "base/location.h"
28 #include "base/logging.h"
29 #include "base/memory/raw_ptr.h"
30 #include "base/no_destructor.h"
31 #include "base/not_fatal_until.h"
32 #include "base/notreached.h"
33 #include "base/path_service.h"
34 #include "base/strings/stringprintf.h"
35 #include "base/task/sequenced_task_runner.h"
36 #include "base/task/single_thread_task_runner.h"
37 #include "base/task/thread_pool.h"
38 #include "base/threading/scoped_blocking_call.h"
39 #include "base/threading/thread_checker.h"
40 #include "base/threading/thread_restrictions.h"
41 #include "build/build_config.h"
42 #include "crypto/chaps_support.h"
43 #include "crypto/nss_util_internal.h"
44
45 namespace crypto {
46
47 namespace {
48
49 const char kUserNSSDatabaseName[] = "UserNSSDB";
50
51 class ChromeOSUserData {
52 public:
53 using SlotReadyCallback = base::OnceCallback<void(ScopedPK11Slot)>;
54
ChromeOSUserData(ScopedPK11Slot public_slot)55 explicit ChromeOSUserData(ScopedPK11Slot public_slot)
56 : public_slot_(std::move(public_slot)) {}
57
~ChromeOSUserData()58 ~ChromeOSUserData() {
59 if (public_slot_) {
60 SECStatus status = CloseSoftwareNSSDB(public_slot_.get());
61 if (status != SECSuccess)
62 PLOG(ERROR) << "CloseSoftwareNSSDB failed: " << PORT_GetError();
63 }
64 }
65
GetPublicSlot()66 ScopedPK11Slot GetPublicSlot() {
67 return ScopedPK11Slot(public_slot_ ? PK11_ReferenceSlot(public_slot_.get())
68 : nullptr);
69 }
70
GetPrivateSlot(SlotReadyCallback callback)71 ScopedPK11Slot GetPrivateSlot(SlotReadyCallback callback) {
72 if (private_slot_)
73 return ScopedPK11Slot(PK11_ReferenceSlot(private_slot_.get()));
74 if (!callback.is_null()) {
75 // Callback lists cannot hold callbacks that take move-only args, since
76 // Notify()ing such a list would move the arg into the first callback,
77 // leaving it null or unspecified for remaining callbacks. Instead, adapt
78 // the provided callbacks to accept a raw pointer, which can be copied,
79 // and then wrap in a separate scoping object for each callback.
80 tpm_ready_callback_list_.AddUnsafe(base::BindOnce(
81 [](SlotReadyCallback callback, PK11SlotInfo* info) {
82 std::move(callback).Run(ScopedPK11Slot(PK11_ReferenceSlot(info)));
83 },
84 std::move(callback)));
85 }
86 return ScopedPK11Slot();
87 }
88
SetPrivateSlot(ScopedPK11Slot private_slot)89 void SetPrivateSlot(ScopedPK11Slot private_slot) {
90 DCHECK(!private_slot_);
91 private_slot_ = std::move(private_slot);
92 tpm_ready_callback_list_.Notify(private_slot_.get());
93 }
94
private_slot_initialization_started() const95 bool private_slot_initialization_started() const {
96 return private_slot_initialization_started_;
97 }
98
set_private_slot_initialization_started()99 void set_private_slot_initialization_started() {
100 private_slot_initialization_started_ = true;
101 }
102
103 private:
104 using SlotReadyCallbackList = base::OnceCallbackList<void(PK11SlotInfo*)>;
105
106 ScopedPK11Slot public_slot_;
107 ScopedPK11Slot private_slot_;
108
109 bool private_slot_initialization_started_ = false;
110
111 SlotReadyCallbackList tpm_ready_callback_list_;
112 };
113
114 // Contains state used for the ChromeOSTokenManager. Unlike the
115 // ChromeOSTokenManager, which is thread-checked, this object may live
116 // and be accessed on multiple threads. While this is normally dangerous,
117 // this is done to support callers initializing early in process startup,
118 // where the threads using the objects may not be created yet, and the
119 // thread startup may depend on these objects.
120 // Put differently: They may be written to from any thread, if, and only
121 // if, the thread they will be read from has not yet been created;
122 // otherwise, this should be treated as thread-affine/thread-hostile.
123 struct ChromeOSTokenManagerDataForTesting {
GetInstancecrypto::__anon3dd13a9b0111::ChromeOSTokenManagerDataForTesting124 static ChromeOSTokenManagerDataForTesting& GetInstance() {
125 static base::NoDestructor<ChromeOSTokenManagerDataForTesting> instance;
126 return *instance;
127 }
128
129 // System slot that will be used for the system slot initialization.
130 ScopedPK11Slot test_system_slot;
131 };
132
133 class ChromeOSTokenManager {
134 public:
135 enum class State {
136 // Initial state.
137 kInitializationNotStarted,
138 // Initialization of the TPM token was started.
139 kInitializationStarted,
140 // TPM token was successfully initialized, but not available to the class'
141 // users yet.
142 kTpmTokenInitialized,
143 // TPM token was successfully enabled. It is a final state.
144 kTpmTokenEnabled,
145 // TPM token will never be enabled. It is a final state.
146 kTpmTokenDisabled,
147 };
148
149 // Used with PostTaskAndReply to pass handles to worker thread and back.
150 struct TPMModuleAndSlot {
TPMModuleAndSlotcrypto::__anon3dd13a9b0111::ChromeOSTokenManager::TPMModuleAndSlot151 explicit TPMModuleAndSlot(SECMODModule* init_chaps_module)
152 : chaps_module(init_chaps_module) {}
153
154 raw_ptr<SECMODModule> chaps_module;
155 ScopedPK11Slot tpm_slot;
156 };
157
OpenPersistentNSSDBForPath(const std::string & db_name,const base::FilePath & path)158 ScopedPK11Slot OpenPersistentNSSDBForPath(const std::string& db_name,
159 const base::FilePath& path) {
160 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
161 // NSS is allowed to do IO on the current thread since dispatching
162 // to a dedicated thread would still have the affect of blocking
163 // the current thread, due to NSS's internal locking requirements
164 ScopedAllowBlockingForNSS allow_blocking;
165
166 base::FilePath nssdb_path = GetSoftwareNSSDBPath(path);
167 if (!base::CreateDirectory(nssdb_path)) {
168 LOG(ERROR) << "Failed to create " << nssdb_path.value() << " directory.";
169 return ScopedPK11Slot();
170 }
171 return OpenSoftwareNSSDB(nssdb_path, db_name);
172 }
173
InitializeTPMTokenAndSystemSlot(int system_slot_id,base::OnceCallback<void (bool)> callback)174 void InitializeTPMTokenAndSystemSlot(
175 int system_slot_id,
176 base::OnceCallback<void(bool)> callback) {
177 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
178 DCHECK_EQ(state_, State::kInitializationNotStarted);
179 state_ = State::kInitializationStarted;
180
181 // Note that a reference is not taken to chaps_module_. This is safe since
182 // ChromeOSTokenManager is Leaky, so the reference it holds is never
183 // released.
184 std::unique_ptr<TPMModuleAndSlot> tpm_args(
185 new TPMModuleAndSlot(chaps_module_));
186 TPMModuleAndSlot* tpm_args_ptr = tpm_args.get();
187 base::ThreadPool::PostTaskAndReply(
188 FROM_HERE,
189 {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
190 base::BindOnce(&ChromeOSTokenManager::InitializeTPMTokenInThreadPool,
191 system_slot_id, tpm_args_ptr),
192 base::BindOnce(
193 &ChromeOSTokenManager::OnInitializedTPMTokenAndSystemSlot,
194 base::Unretained(this), // ChromeOSTokenManager is leaky
195 std::move(callback), std::move(tpm_args)));
196 }
197
FinishInitializingTPMTokenAndSystemSlot()198 void FinishInitializingTPMTokenAndSystemSlot() {
199 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
200 DCHECK(!IsInitializationFinished());
201
202 // If `OnInitializedTPMTokenAndSystemSlot` was not called, but a test system
203 // slot is prepared, start using it now. Can happen in tests that don't fake
204 // enable TPM.
205 if (!system_slot_ &&
206 ChromeOSTokenManagerDataForTesting::GetInstance().test_system_slot) {
207 system_slot_ = ScopedPK11Slot(
208 PK11_ReferenceSlot(ChromeOSTokenManagerDataForTesting::GetInstance()
209 .test_system_slot.get()));
210 }
211
212 state_ = (state_ == State::kTpmTokenInitialized) ? State::kTpmTokenEnabled
213 : State::kTpmTokenDisabled;
214
215 tpm_ready_callback_list_->Notify();
216 }
217
InitializeTPMTokenInThreadPool(CK_SLOT_ID token_slot_id,TPMModuleAndSlot * tpm_args)218 static void InitializeTPMTokenInThreadPool(CK_SLOT_ID token_slot_id,
219 TPMModuleAndSlot* tpm_args) {
220 // NSS functions may reenter //net via extension hooks. If the reentered
221 // code needs to synchronously wait for a task to run but the thread pool in
222 // which that task must run doesn't have enough threads to schedule it, a
223 // deadlock occurs. To prevent that, the base::ScopedBlockingCall below
224 // increments the thread pool capacity for the duration of the TPM
225 // initialization.
226 base::ScopedBlockingCall scoped_blocking_call(
227 FROM_HERE, base::BlockingType::WILL_BLOCK);
228
229 if (!tpm_args->chaps_module) {
230 tpm_args->chaps_module = LoadChaps();
231 }
232 if (tpm_args->chaps_module) {
233 tpm_args->tpm_slot = GetChapsSlot(tpm_args->chaps_module, token_slot_id);
234 }
235 }
236
OnInitializedTPMTokenAndSystemSlot(base::OnceCallback<void (bool)> callback,std::unique_ptr<TPMModuleAndSlot> tpm_args)237 void OnInitializedTPMTokenAndSystemSlot(
238 base::OnceCallback<void(bool)> callback,
239 std::unique_ptr<TPMModuleAndSlot> tpm_args) {
240 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
241 DVLOG(2) << "Loaded chaps: " << !!tpm_args->chaps_module
242 << ", got tpm slot: " << !!tpm_args->tpm_slot;
243
244 chaps_module_ = tpm_args->chaps_module;
245
246 if (ChromeOSTokenManagerDataForTesting::GetInstance().test_system_slot) {
247 // chromeos_unittests try to test the TPM initialization process. If we
248 // have a test DB open, pretend that it is the system slot.
249 system_slot_ = ScopedPK11Slot(
250 PK11_ReferenceSlot(ChromeOSTokenManagerDataForTesting::GetInstance()
251 .test_system_slot.get()));
252 } else {
253 system_slot_ = std::move(tpm_args->tpm_slot);
254 }
255
256 if (system_slot_) {
257 state_ = State::kTpmTokenInitialized;
258 }
259
260 std::move(callback).Run(!!system_slot_);
261 }
262
IsTPMTokenEnabled(base::OnceCallback<void (bool)> callback)263 void IsTPMTokenEnabled(base::OnceCallback<void(bool)> callback) {
264 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
265 DCHECK(!callback.is_null());
266
267 if (!IsInitializationFinished()) {
268 // Call back to this method when initialization is finished.
269 tpm_ready_callback_list_->AddUnsafe(
270 base::BindOnce(&ChromeOSTokenManager::IsTPMTokenEnabled,
271 base::Unretained(this) /* singleton is leaky */,
272 std::move(callback)));
273 return;
274 }
275
276 DCHECK(base::SequencedTaskRunner::HasCurrentDefault());
277 base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
278 FROM_HERE,
279 base::BindOnce(std::move(callback),
280 /*is_tpm_enabled=*/(state_ == State::kTpmTokenEnabled)));
281 }
282
InitializeNSSForChromeOSUser(const std::string & username_hash,const base::FilePath & path)283 bool InitializeNSSForChromeOSUser(const std::string& username_hash,
284 const base::FilePath& path) {
285 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
286 if (base::Contains(chromeos_user_map_, username_hash)) {
287 // This user already exists in our mapping.
288 DVLOG(2) << username_hash << " already initialized.";
289 return false;
290 }
291
292 DVLOG(2) << "Opening NSS DB " << path.value();
293 std::string db_name = base::StringPrintf("%s %s", kUserNSSDatabaseName,
294 username_hash.c_str());
295 ScopedPK11Slot public_slot(OpenPersistentNSSDBForPath(db_name, path));
296
297 return InitializeNSSForChromeOSUserWithSlot(username_hash,
298 std::move(public_slot));
299 }
300
InitializeNSSForChromeOSUserWithSlot(const std::string & username_hash,ScopedPK11Slot public_slot)301 bool InitializeNSSForChromeOSUserWithSlot(const std::string& username_hash,
302 ScopedPK11Slot public_slot) {
303 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
304 if (base::Contains(chromeos_user_map_, username_hash)) {
305 // This user already exists in our mapping.
306 DVLOG(2) << username_hash << " already initialized.";
307 return false;
308 }
309
310 chromeos_user_map_[username_hash] =
311 std::make_unique<ChromeOSUserData>(std::move(public_slot));
312 return true;
313 }
314
ShouldInitializeTPMForChromeOSUser(const std::string & username_hash)315 bool ShouldInitializeTPMForChromeOSUser(const std::string& username_hash) {
316 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
317 DCHECK(base::Contains(chromeos_user_map_, username_hash));
318
319 return !chromeos_user_map_[username_hash]
320 ->private_slot_initialization_started();
321 }
322
WillInitializeTPMForChromeOSUser(const std::string & username_hash)323 void WillInitializeTPMForChromeOSUser(const std::string& username_hash) {
324 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
325 DCHECK(base::Contains(chromeos_user_map_, username_hash));
326
327 chromeos_user_map_[username_hash]
328 ->set_private_slot_initialization_started();
329 }
330
InitializeTPMForChromeOSUser(const std::string & username_hash,CK_SLOT_ID slot_id)331 void InitializeTPMForChromeOSUser(const std::string& username_hash,
332 CK_SLOT_ID slot_id) {
333 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
334 DCHECK(base::Contains(chromeos_user_map_, username_hash));
335 DCHECK(chromeos_user_map_[username_hash]
336 ->private_slot_initialization_started());
337
338 if (!chaps_module_)
339 return;
340
341 // Note that a reference is not taken to chaps_module_. This is safe since
342 // ChromeOSTokenManager is Leaky, so the reference it holds is never
343 // released.
344 std::unique_ptr<TPMModuleAndSlot> tpm_args(
345 new TPMModuleAndSlot(chaps_module_));
346 TPMModuleAndSlot* tpm_args_ptr = tpm_args.get();
347 base::ThreadPool::PostTaskAndReply(
348 FROM_HERE,
349 {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
350 base::BindOnce(&ChromeOSTokenManager::InitializeTPMTokenInThreadPool,
351 slot_id, tpm_args_ptr),
352 base::BindOnce(&ChromeOSTokenManager::OnInitializedTPMForChromeOSUser,
353 base::Unretained(this), // ChromeOSTokenManager is leaky
354 username_hash, std::move(tpm_args)));
355 }
356
OnInitializedTPMForChromeOSUser(const std::string & username_hash,std::unique_ptr<TPMModuleAndSlot> tpm_args)357 void OnInitializedTPMForChromeOSUser(
358 const std::string& username_hash,
359 std::unique_ptr<TPMModuleAndSlot> tpm_args) {
360 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
361 DVLOG(2) << "Got tpm slot for " << username_hash << " "
362 << !!tpm_args->tpm_slot;
363 chromeos_user_map_[username_hash]->SetPrivateSlot(
364 std::move(tpm_args->tpm_slot));
365 }
366
InitializePrivateSoftwareSlotForChromeOSUser(const std::string & username_hash)367 void InitializePrivateSoftwareSlotForChromeOSUser(
368 const std::string& username_hash) {
369 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
370 VLOG(1) << "using software private slot for " << username_hash;
371 DCHECK(base::Contains(chromeos_user_map_, username_hash));
372 DCHECK(chromeos_user_map_[username_hash]
373 ->private_slot_initialization_started());
374
375 if (prepared_test_private_slot_) {
376 chromeos_user_map_[username_hash]->SetPrivateSlot(
377 std::move(prepared_test_private_slot_));
378 return;
379 }
380
381 chromeos_user_map_[username_hash]->SetPrivateSlot(
382 chromeos_user_map_[username_hash]->GetPublicSlot());
383 }
384
GetPublicSlotForChromeOSUser(const std::string & username_hash)385 ScopedPK11Slot GetPublicSlotForChromeOSUser(
386 const std::string& username_hash) {
387 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
388
389 if (username_hash.empty()) {
390 DVLOG(2) << "empty username_hash";
391 return ScopedPK11Slot();
392 }
393
394 if (!base::Contains(chromeos_user_map_, username_hash)) {
395 LOG(ERROR) << username_hash << " not initialized.";
396 return ScopedPK11Slot();
397 }
398 return chromeos_user_map_[username_hash]->GetPublicSlot();
399 }
400
GetPrivateSlotForChromeOSUser(const std::string & username_hash,base::OnceCallback<void (ScopedPK11Slot)> callback)401 ScopedPK11Slot GetPrivateSlotForChromeOSUser(
402 const std::string& username_hash,
403 base::OnceCallback<void(ScopedPK11Slot)> callback) {
404 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
405
406 if (username_hash.empty()) {
407 DVLOG(2) << "empty username_hash";
408 if (!callback.is_null()) {
409 base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
410 FROM_HERE, base::BindOnce(std::move(callback), ScopedPK11Slot()));
411 }
412 return ScopedPK11Slot();
413 }
414
415 DCHECK(base::Contains(chromeos_user_map_, username_hash));
416
417 return chromeos_user_map_[username_hash]->GetPrivateSlot(
418 std::move(callback));
419 }
420
CloseChromeOSUserForTesting(const std::string & username_hash)421 void CloseChromeOSUserForTesting(const std::string& username_hash) {
422 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
423 auto i = chromeos_user_map_.find(username_hash);
424 CHECK(i != chromeos_user_map_.end(), base::NotFatalUntil::M130);
425 chromeos_user_map_.erase(i);
426 }
427
GetSystemNSSKeySlot(base::OnceCallback<void (ScopedPK11Slot)> callback)428 void GetSystemNSSKeySlot(base::OnceCallback<void(ScopedPK11Slot)> callback) {
429 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
430
431 if (!IsInitializationFinished()) {
432 // Call back to this method when initialization is finished.
433 tpm_ready_callback_list_->AddUnsafe(
434 base::BindOnce(&ChromeOSTokenManager::GetSystemNSSKeySlot,
435 base::Unretained(this) /* singleton is leaky */,
436 std::move(callback)));
437 return;
438 }
439
440 base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
441 FROM_HERE,
442 base::BindOnce(std::move(callback),
443 /*system_slot=*/ScopedPK11Slot(
444 system_slot_ ? PK11_ReferenceSlot(system_slot_.get())
445 : nullptr)));
446 }
447
ResetSystemSlotForTesting()448 void ResetSystemSlotForTesting() { system_slot_.reset(); }
449
ResetTokenManagerForTesting()450 void ResetTokenManagerForTesting() {
451 // Prevent test failures when two tests in the same process use the same
452 // ChromeOSTokenManager from different threads.
453 DETACH_FROM_THREAD(thread_checker_);
454 state_ = State::kInitializationNotStarted;
455
456 // Configuring chaps_module_ here is not supported yet.
457 CHECK(!chaps_module_);
458
459 // Make sure there are no outstanding callbacks between tests.
460 // OnceClosureList doesn't provide a way to clear the callback list.
461 tpm_ready_callback_list_ = std::make_unique<base::OnceClosureList>();
462
463 chromeos_user_map_.clear();
464 ResetSystemSlotForTesting(); // IN-TEST
465 prepared_test_private_slot_.reset();
466 }
467
SetPrivateSoftwareSlotForChromeOSUserForTesting(ScopedPK11Slot slot)468 void SetPrivateSoftwareSlotForChromeOSUserForTesting(ScopedPK11Slot slot) {
469 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
470
471 // Ensure that a previous value of prepared_test_private_slot_ is not
472 // overwritten. Unsetting, i.e. setting a nullptr, however is allowed.
473 DCHECK(!slot || !prepared_test_private_slot_);
474 prepared_test_private_slot_ = std::move(slot);
475 }
476
IsInitializationStarted()477 bool IsInitializationStarted() {
478 return (state_ != State::kInitializationNotStarted);
479 }
480
481 private:
482 friend struct base::LazyInstanceTraitsBase<ChromeOSTokenManager>;
483
ChromeOSTokenManager()484 ChromeOSTokenManager() { EnsureNSSInit(); }
485
486 // NOTE(willchan): We don't actually cleanup on destruction since we leak NSS
487 // to prevent non-joinable threads from using NSS after it's already been
488 // shut down.
489 ~ChromeOSTokenManager() = delete;
490
IsInitializationFinished()491 bool IsInitializationFinished() {
492 switch (state_) {
493 case State::kTpmTokenEnabled:
494 case State::kTpmTokenDisabled:
495 return true;
496 case State::kInitializationNotStarted:
497 case State::kInitializationStarted:
498 case State::kTpmTokenInitialized:
499 return false;
500 }
501 }
502
503 State state_ = State::kInitializationNotStarted;
504 std::unique_ptr<base::OnceClosureList> tpm_ready_callback_list_ =
505 std::make_unique<base::OnceClosureList>();
506
507 raw_ptr<SECMODModule> chaps_module_ = nullptr;
508 ScopedPK11Slot system_slot_;
509 std::map<std::string, std::unique_ptr<ChromeOSUserData>> chromeos_user_map_;
510 ScopedPK11Slot prepared_test_private_slot_;
511
512 THREAD_CHECKER(thread_checker_);
513 };
514
515 base::LazyInstance<ChromeOSTokenManager>::Leaky g_token_manager =
516 LAZY_INSTANCE_INITIALIZER;
517 } // namespace
518
GetSoftwareNSSDBPath(const base::FilePath & profile_directory_path)519 base::FilePath GetSoftwareNSSDBPath(
520 const base::FilePath& profile_directory_path) {
521 return profile_directory_path.AppendASCII(".pki").AppendASCII("nssdb");
522 }
523
GetSystemNSSKeySlot(base::OnceCallback<void (ScopedPK11Slot)> callback)524 void GetSystemNSSKeySlot(base::OnceCallback<void(ScopedPK11Slot)> callback) {
525 g_token_manager.Get().GetSystemNSSKeySlot(std::move(callback));
526 }
527
PrepareSystemSlotForTesting(ScopedPK11Slot slot)528 void PrepareSystemSlotForTesting(ScopedPK11Slot slot) {
529 DCHECK(!ChromeOSTokenManagerDataForTesting::GetInstance().test_system_slot);
530 DCHECK(!g_token_manager.IsCreated() ||
531 !g_token_manager.Get().IsInitializationStarted())
532 << "PrepareSystemSlotForTesting is called after initialization started";
533
534 ChromeOSTokenManagerDataForTesting::GetInstance().test_system_slot =
535 std::move(slot);
536 }
537
ResetSystemSlotForTesting()538 void ResetSystemSlotForTesting() {
539 if (g_token_manager.IsCreated()) {
540 g_token_manager.Get().ResetSystemSlotForTesting(); // IN-TEST
541 }
542 ChromeOSTokenManagerDataForTesting::GetInstance().test_system_slot.reset();
543 }
544
ResetTokenManagerForTesting()545 void ResetTokenManagerForTesting() {
546 if (g_token_manager.IsCreated()) {
547 g_token_manager.Get().ResetTokenManagerForTesting(); // IN-TEST
548 }
549 ResetSystemSlotForTesting(); // IN-TEST
550 }
551
IsTPMTokenEnabled(base::OnceCallback<void (bool)> callback)552 void IsTPMTokenEnabled(base::OnceCallback<void(bool)> callback) {
553 g_token_manager.Get().IsTPMTokenEnabled(std::move(callback));
554 }
555
InitializeTPMTokenAndSystemSlot(int token_slot_id,base::OnceCallback<void (bool)> callback)556 void InitializeTPMTokenAndSystemSlot(int token_slot_id,
557 base::OnceCallback<void(bool)> callback) {
558 g_token_manager.Get().InitializeTPMTokenAndSystemSlot(token_slot_id,
559 std::move(callback));
560 }
561
FinishInitializingTPMTokenAndSystemSlot()562 void FinishInitializingTPMTokenAndSystemSlot() {
563 g_token_manager.Get().FinishInitializingTPMTokenAndSystemSlot();
564 }
565
InitializeNSSForChromeOSUser(const std::string & username_hash,const base::FilePath & path)566 bool InitializeNSSForChromeOSUser(const std::string& username_hash,
567 const base::FilePath& path) {
568 return g_token_manager.Get().InitializeNSSForChromeOSUser(username_hash,
569 path);
570 }
571
InitializeNSSForChromeOSUserWithSlot(const std::string & username_hash,ScopedPK11Slot public_slot)572 bool InitializeNSSForChromeOSUserWithSlot(const std::string& username_hash,
573 ScopedPK11Slot public_slot) {
574 return g_token_manager.Get().InitializeNSSForChromeOSUserWithSlot(
575 username_hash, std::move(public_slot));
576 }
577
ShouldInitializeTPMForChromeOSUser(const std::string & username_hash)578 bool ShouldInitializeTPMForChromeOSUser(const std::string& username_hash) {
579 return g_token_manager.Get().ShouldInitializeTPMForChromeOSUser(
580 username_hash);
581 }
582
WillInitializeTPMForChromeOSUser(const std::string & username_hash)583 void WillInitializeTPMForChromeOSUser(const std::string& username_hash) {
584 g_token_manager.Get().WillInitializeTPMForChromeOSUser(username_hash);
585 }
586
InitializeTPMForChromeOSUser(const std::string & username_hash,CK_SLOT_ID slot_id)587 void InitializeTPMForChromeOSUser(const std::string& username_hash,
588 CK_SLOT_ID slot_id) {
589 g_token_manager.Get().InitializeTPMForChromeOSUser(username_hash, slot_id);
590 }
591
InitializePrivateSoftwareSlotForChromeOSUser(const std::string & username_hash)592 void InitializePrivateSoftwareSlotForChromeOSUser(
593 const std::string& username_hash) {
594 g_token_manager.Get().InitializePrivateSoftwareSlotForChromeOSUser(
595 username_hash);
596 }
597
GetPublicSlotForChromeOSUser(const std::string & username_hash)598 ScopedPK11Slot GetPublicSlotForChromeOSUser(const std::string& username_hash) {
599 return g_token_manager.Get().GetPublicSlotForChromeOSUser(username_hash);
600 }
601
GetPrivateSlotForChromeOSUser(const std::string & username_hash,base::OnceCallback<void (ScopedPK11Slot)> callback)602 ScopedPK11Slot GetPrivateSlotForChromeOSUser(
603 const std::string& username_hash,
604 base::OnceCallback<void(ScopedPK11Slot)> callback) {
605 return g_token_manager.Get().GetPrivateSlotForChromeOSUser(
606 username_hash, std::move(callback));
607 }
608
CloseChromeOSUserForTesting(const std::string & username_hash)609 void CloseChromeOSUserForTesting(const std::string& username_hash) {
610 g_token_manager.Get().CloseChromeOSUserForTesting(username_hash);
611 }
612
SetPrivateSoftwareSlotForChromeOSUserForTesting(ScopedPK11Slot slot)613 void SetPrivateSoftwareSlotForChromeOSUserForTesting(ScopedPK11Slot slot) {
614 g_token_manager.Get().SetPrivateSoftwareSlotForChromeOSUserForTesting(
615 std::move(slot));
616 }
617
618 namespace {
PrintDirectoryInfo(const base::FilePath & path)619 void PrintDirectoryInfo(const base::FilePath& path) {
620 base::stat_wrapper_t file_stat;
621
622 if (base::File::Stat(path, &file_stat) == -1) {
623 base::File::Error error_code = base::File::OSErrorToFileError(errno);
624 LOG(ERROR) << "Failed to collect directory info, error: " << error_code;
625 }
626
627 LOG(ERROR) << path << ", " << std::oct << file_stat.st_mode << std::dec
628 << ", "
629 << "uid " << file_stat.st_uid << ", "
630 << "gid " << file_stat.st_gid << std::endl;
631 }
632 } // namespace
633
634 // TODO(crbug.com/1163303): Remove when the bug is fixed.
DiagnosePublicSlotAndCrash(const base::FilePath & nss_path)635 void DiagnosePublicSlotAndCrash(const base::FilePath& nss_path) {
636 LOG(ERROR) << "Public slot is invalid. Start collecting stats.";
637 // Should be something like /home/chronos/u-<hash>/.pki/nssdb .
638 LOG(ERROR) << "NSS path: " << nss_path;
639
640 {
641 // NSS files like pkcs11.txt, cert9.db, key4.db .
642 base::FileEnumerator files(
643 nss_path, /*recursive=*/false,
644 /*file_type=*/base::FileEnumerator::FILES,
645 /*pattern=*/base::FilePath::StringType(),
646 base::FileEnumerator::FolderSearchPolicy::MATCH_ONLY,
647 base::FileEnumerator::ErrorPolicy::STOP_ENUMERATION);
648 LOG(ERROR) << "Public slot database files:";
649 for (base::FilePath path = files.Next(); !path.empty();
650 path = files.Next()) {
651 base::FileEnumerator::FileInfo file_info = files.GetInfo();
652
653 char buf[16];
654 int read_result = base::ReadFile(path, buf, sizeof(buf) - 1);
655
656 LOG(ERROR) << file_info.GetName() << ", " << std::oct
657 << file_info.stat().st_mode << std::dec << ", "
658 << "uid " << file_info.stat().st_uid << ", "
659 << "gid " << file_info.stat().st_gid << ", "
660 << file_info.stat().st_size << " bytes, "
661 << ((read_result > 0) ? "readable" : "not readable");
662 }
663 LOG(ERROR) << "Enumerate error code: " << files.GetError();
664 }
665
666 // NSS directory might not be created yet, also check parent directories.
667 // Use u-hash directory as a comparison point for user and group ids and
668 // access permissions.
669
670 base::FilePath nssdb_path = nss_path.Append(base::FilePath::kParentDirectory);
671 PrintDirectoryInfo(nssdb_path);
672
673 base::FilePath pki_path = nssdb_path.Append(base::FilePath::kParentDirectory);
674 PrintDirectoryInfo(pki_path);
675
676 base::FilePath u_hash_path =
677 pki_path.Append(base::FilePath::kParentDirectory);
678 PrintDirectoryInfo(u_hash_path);
679
680 {
681 // Check whether the NSS path exists, and if not, check whether it's
682 // possible to create it.
683 if (base::DirectoryExists(nss_path)) {
684 LOG(ERROR) << "NSS path exists (as expected).";
685 } else {
686 base::File::Error error = base::File::Error::FILE_OK;
687 if (base::CreateDirectoryAndGetError(nss_path, &error)) {
688 LOG(ERROR) << "NSS path didn't exist. Created successfully.";
689 } else {
690 LOG(ERROR) << "NSS path didn't exist. Failed to create, error: "
691 << error;
692 }
693 }
694 }
695
696 NOTREACHED() << "Public slot is invalid.";
697 }
698
699 } // namespace crypto
700