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