xref: /aosp_15_r20/external/cronet/crypto/nss_util_chromeos.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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 
ChromeOSUserData(ScopedPK11Slot public_slot)53   explicit ChromeOSUserData(ScopedPK11Slot public_slot)
54       : public_slot_(std::move(public_slot)) {}
55 
~ChromeOSUserData()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 
GetPublicSlot()64   ScopedPK11Slot GetPublicSlot() {
65     return ScopedPK11Slot(public_slot_ ? PK11_ReferenceSlot(public_slot_.get())
66                                        : nullptr);
67   }
68 
GetPrivateSlot(SlotReadyCallback callback)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 
SetPrivateSlot(ScopedPK11Slot private_slot)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 
private_slot_initialization_started() const93   bool private_slot_initialization_started() const {
94     return private_slot_initialization_started_;
95   }
96 
set_private_slot_initialization_started()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 {
GetInstancecrypto::__anon58d160150111::ChromeOSTokenManagerDataForTesting122   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 {
TPMModuleAndSlotcrypto::__anon58d160150111::ChromeOSTokenManager::TPMModuleAndSlot149     explicit TPMModuleAndSlot(SECMODModule* init_chaps_module)
150         : chaps_module(init_chaps_module) {}
151 
152     raw_ptr<SECMODModule> chaps_module;
153     ScopedPK11Slot tpm_slot;
154   };
155 
OpenPersistentNSSDBForPath(const std::string & db_name,const base::FilePath & path)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 
InitializeTPMTokenAndSystemSlot(int system_slot_id,base::OnceCallback<void (bool)> callback)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 
FinishInitializingTPMTokenAndSystemSlot()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 
InitializeTPMTokenInThreadPool(CK_SLOT_ID token_slot_id,TPMModuleAndSlot * tpm_args)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 
OnInitializedTPMTokenAndSystemSlot(base::OnceCallback<void (bool)> callback,std::unique_ptr<TPMModuleAndSlot> tpm_args)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 
IsTPMTokenEnabled(base::OnceCallback<void (bool)> callback)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 
InitializeNSSForChromeOSUser(const std::string & username_hash,const base::FilePath & path)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 
295     return InitializeNSSForChromeOSUserWithSlot(username_hash,
296                                                 std::move(public_slot));
297   }
298 
InitializeNSSForChromeOSUserWithSlot(const std::string & username_hash,ScopedPK11Slot public_slot)299   bool InitializeNSSForChromeOSUserWithSlot(const std::string& username_hash,
300                                             ScopedPK11Slot public_slot) {
301     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
302     if (base::Contains(chromeos_user_map_, username_hash)) {
303       // This user already exists in our mapping.
304       DVLOG(2) << username_hash << " already initialized.";
305       return false;
306     }
307 
308     chromeos_user_map_[username_hash] =
309         std::make_unique<ChromeOSUserData>(std::move(public_slot));
310     return true;
311   }
312 
ShouldInitializeTPMForChromeOSUser(const std::string & username_hash)313   bool ShouldInitializeTPMForChromeOSUser(const std::string& username_hash) {
314     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
315     DCHECK(base::Contains(chromeos_user_map_, username_hash));
316 
317     return !chromeos_user_map_[username_hash]
318                 ->private_slot_initialization_started();
319   }
320 
WillInitializeTPMForChromeOSUser(const std::string & username_hash)321   void WillInitializeTPMForChromeOSUser(const std::string& username_hash) {
322     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
323     DCHECK(base::Contains(chromeos_user_map_, username_hash));
324 
325     chromeos_user_map_[username_hash]
326         ->set_private_slot_initialization_started();
327   }
328 
InitializeTPMForChromeOSUser(const std::string & username_hash,CK_SLOT_ID slot_id)329   void InitializeTPMForChromeOSUser(const std::string& username_hash,
330                                     CK_SLOT_ID slot_id) {
331     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
332     DCHECK(base::Contains(chromeos_user_map_, username_hash));
333     DCHECK(chromeos_user_map_[username_hash]
334                ->private_slot_initialization_started());
335 
336     if (!chaps_module_)
337       return;
338 
339     // Note that a reference is not taken to chaps_module_. This is safe since
340     // ChromeOSTokenManager is Leaky, so the reference it holds is never
341     // released.
342     std::unique_ptr<TPMModuleAndSlot> tpm_args(
343         new TPMModuleAndSlot(chaps_module_));
344     TPMModuleAndSlot* tpm_args_ptr = tpm_args.get();
345     base::ThreadPool::PostTaskAndReply(
346         FROM_HERE,
347         {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
348         base::BindOnce(&ChromeOSTokenManager::InitializeTPMTokenInThreadPool,
349                        slot_id, tpm_args_ptr),
350         base::BindOnce(&ChromeOSTokenManager::OnInitializedTPMForChromeOSUser,
351                        base::Unretained(this),  // ChromeOSTokenManager is leaky
352                        username_hash, std::move(tpm_args)));
353   }
354 
OnInitializedTPMForChromeOSUser(const std::string & username_hash,std::unique_ptr<TPMModuleAndSlot> tpm_args)355   void OnInitializedTPMForChromeOSUser(
356       const std::string& username_hash,
357       std::unique_ptr<TPMModuleAndSlot> tpm_args) {
358     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
359     DVLOG(2) << "Got tpm slot for " << username_hash << " "
360              << !!tpm_args->tpm_slot;
361     chromeos_user_map_[username_hash]->SetPrivateSlot(
362         std::move(tpm_args->tpm_slot));
363   }
364 
InitializePrivateSoftwareSlotForChromeOSUser(const std::string & username_hash)365   void InitializePrivateSoftwareSlotForChromeOSUser(
366       const std::string& username_hash) {
367     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
368     VLOG(1) << "using software private slot for " << username_hash;
369     DCHECK(base::Contains(chromeos_user_map_, username_hash));
370     DCHECK(chromeos_user_map_[username_hash]
371                ->private_slot_initialization_started());
372 
373     if (prepared_test_private_slot_) {
374       chromeos_user_map_[username_hash]->SetPrivateSlot(
375           std::move(prepared_test_private_slot_));
376       return;
377     }
378 
379     chromeos_user_map_[username_hash]->SetPrivateSlot(
380         chromeos_user_map_[username_hash]->GetPublicSlot());
381   }
382 
GetPublicSlotForChromeOSUser(const std::string & username_hash)383   ScopedPK11Slot GetPublicSlotForChromeOSUser(
384       const std::string& username_hash) {
385     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
386 
387     if (username_hash.empty()) {
388       DVLOG(2) << "empty username_hash";
389       return ScopedPK11Slot();
390     }
391 
392     if (!base::Contains(chromeos_user_map_, username_hash)) {
393       LOG(ERROR) << username_hash << " not initialized.";
394       return ScopedPK11Slot();
395     }
396     return chromeos_user_map_[username_hash]->GetPublicSlot();
397   }
398 
GetPrivateSlotForChromeOSUser(const std::string & username_hash,base::OnceCallback<void (ScopedPK11Slot)> callback)399   ScopedPK11Slot GetPrivateSlotForChromeOSUser(
400       const std::string& username_hash,
401       base::OnceCallback<void(ScopedPK11Slot)> callback) {
402     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
403 
404     if (username_hash.empty()) {
405       DVLOG(2) << "empty username_hash";
406       if (!callback.is_null()) {
407         base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
408             FROM_HERE, base::BindOnce(std::move(callback), ScopedPK11Slot()));
409       }
410       return ScopedPK11Slot();
411     }
412 
413     DCHECK(base::Contains(chromeos_user_map_, username_hash));
414 
415     return chromeos_user_map_[username_hash]->GetPrivateSlot(
416         std::move(callback));
417   }
418 
CloseChromeOSUserForTesting(const std::string & username_hash)419   void CloseChromeOSUserForTesting(const std::string& username_hash) {
420     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
421     auto i = chromeos_user_map_.find(username_hash);
422     DCHECK(i != chromeos_user_map_.end());
423     chromeos_user_map_.erase(i);
424   }
425 
GetSystemNSSKeySlot(base::OnceCallback<void (ScopedPK11Slot)> callback)426   void GetSystemNSSKeySlot(base::OnceCallback<void(ScopedPK11Slot)> callback) {
427     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
428 
429     if (!IsInitializationFinished()) {
430       // Call back to this method when initialization is finished.
431       tpm_ready_callback_list_->AddUnsafe(
432           base::BindOnce(&ChromeOSTokenManager::GetSystemNSSKeySlot,
433                          base::Unretained(this) /* singleton is leaky */,
434                          std::move(callback)));
435       return;
436     }
437 
438     base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
439         FROM_HERE,
440         base::BindOnce(std::move(callback),
441                        /*system_slot=*/ScopedPK11Slot(
442                            system_slot_ ? PK11_ReferenceSlot(system_slot_.get())
443                                         : nullptr)));
444   }
445 
ResetSystemSlotForTesting()446   void ResetSystemSlotForTesting() { system_slot_.reset(); }
447 
ResetTokenManagerForTesting()448   void ResetTokenManagerForTesting() {
449     // Prevent test failures when two tests in the same process use the same
450     // ChromeOSTokenManager from different threads.
451     DETACH_FROM_THREAD(thread_checker_);
452     state_ = State::kInitializationNotStarted;
453 
454     // Configuring chaps_module_ here is not supported yet.
455     CHECK(!chaps_module_);
456 
457     // Make sure there are no outstanding callbacks between tests.
458     // OnceClosureList doesn't provide a way to clear the callback list.
459     tpm_ready_callback_list_ = std::make_unique<base::OnceClosureList>();
460 
461     chromeos_user_map_.clear();
462     ResetSystemSlotForTesting();  // IN-TEST
463     prepared_test_private_slot_.reset();
464   }
465 
SetPrivateSoftwareSlotForChromeOSUserForTesting(ScopedPK11Slot slot)466   void SetPrivateSoftwareSlotForChromeOSUserForTesting(ScopedPK11Slot slot) {
467     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
468 
469     // Ensure that a previous value of prepared_test_private_slot_ is not
470     // overwritten. Unsetting, i.e. setting a nullptr, however is allowed.
471     DCHECK(!slot || !prepared_test_private_slot_);
472     prepared_test_private_slot_ = std::move(slot);
473   }
474 
IsInitializationStarted()475   bool IsInitializationStarted() {
476     return (state_ != State::kInitializationNotStarted);
477   }
478 
479  private:
480   friend struct base::LazyInstanceTraitsBase<ChromeOSTokenManager>;
481 
ChromeOSTokenManager()482   ChromeOSTokenManager() { EnsureNSSInit(); }
483 
484   // NOTE(willchan): We don't actually cleanup on destruction since we leak NSS
485   // to prevent non-joinable threads from using NSS after it's already been
486   // shut down.
487   ~ChromeOSTokenManager() = delete;
488 
IsInitializationFinished()489   bool IsInitializationFinished() {
490     switch (state_) {
491       case State::kTpmTokenEnabled:
492       case State::kTpmTokenDisabled:
493         return true;
494       case State::kInitializationNotStarted:
495       case State::kInitializationStarted:
496       case State::kTpmTokenInitialized:
497         return false;
498     }
499   }
500 
501   State state_ = State::kInitializationNotStarted;
502   std::unique_ptr<base::OnceClosureList> tpm_ready_callback_list_ =
503       std::make_unique<base::OnceClosureList>();
504 
505   raw_ptr<SECMODModule> chaps_module_ = nullptr;
506   ScopedPK11Slot system_slot_;
507   std::map<std::string, std::unique_ptr<ChromeOSUserData>> chromeos_user_map_;
508   ScopedPK11Slot prepared_test_private_slot_;
509 
510   THREAD_CHECKER(thread_checker_);
511 };
512 
513 base::LazyInstance<ChromeOSTokenManager>::Leaky g_token_manager =
514     LAZY_INSTANCE_INITIALIZER;
515 }  // namespace
516 
GetSoftwareNSSDBPath(const base::FilePath & profile_directory_path)517 base::FilePath GetSoftwareNSSDBPath(
518     const base::FilePath& profile_directory_path) {
519   return profile_directory_path.AppendASCII(".pki").AppendASCII("nssdb");
520 }
521 
GetSystemNSSKeySlot(base::OnceCallback<void (ScopedPK11Slot)> callback)522 void GetSystemNSSKeySlot(base::OnceCallback<void(ScopedPK11Slot)> callback) {
523   g_token_manager.Get().GetSystemNSSKeySlot(std::move(callback));
524 }
525 
PrepareSystemSlotForTesting(ScopedPK11Slot slot)526 void PrepareSystemSlotForTesting(ScopedPK11Slot slot) {
527   DCHECK(!ChromeOSTokenManagerDataForTesting::GetInstance().test_system_slot);
528   DCHECK(!g_token_manager.IsCreated() ||
529          !g_token_manager.Get().IsInitializationStarted())
530       << "PrepareSystemSlotForTesting is called after initialization started";
531 
532   ChromeOSTokenManagerDataForTesting::GetInstance().test_system_slot =
533       std::move(slot);
534 }
535 
ResetSystemSlotForTesting()536 void ResetSystemSlotForTesting() {
537   if (g_token_manager.IsCreated()) {
538     g_token_manager.Get().ResetSystemSlotForTesting();  // IN-TEST
539   }
540   ChromeOSTokenManagerDataForTesting::GetInstance().test_system_slot.reset();
541 }
542 
ResetTokenManagerForTesting()543 void ResetTokenManagerForTesting() {
544   if (g_token_manager.IsCreated()) {
545     g_token_manager.Get().ResetTokenManagerForTesting();  // IN-TEST
546   }
547   ResetSystemSlotForTesting();  // IN-TEST
548 }
549 
IsTPMTokenEnabled(base::OnceCallback<void (bool)> callback)550 void IsTPMTokenEnabled(base::OnceCallback<void(bool)> callback) {
551   g_token_manager.Get().IsTPMTokenEnabled(std::move(callback));
552 }
553 
InitializeTPMTokenAndSystemSlot(int token_slot_id,base::OnceCallback<void (bool)> callback)554 void InitializeTPMTokenAndSystemSlot(int token_slot_id,
555                                      base::OnceCallback<void(bool)> callback) {
556   g_token_manager.Get().InitializeTPMTokenAndSystemSlot(token_slot_id,
557                                                         std::move(callback));
558 }
559 
FinishInitializingTPMTokenAndSystemSlot()560 void FinishInitializingTPMTokenAndSystemSlot() {
561   g_token_manager.Get().FinishInitializingTPMTokenAndSystemSlot();
562 }
563 
InitializeNSSForChromeOSUser(const std::string & username_hash,const base::FilePath & path)564 bool InitializeNSSForChromeOSUser(const std::string& username_hash,
565                                   const base::FilePath& path) {
566   return g_token_manager.Get().InitializeNSSForChromeOSUser(username_hash,
567                                                             path);
568 }
569 
InitializeNSSForChromeOSUserWithSlot(const std::string & username_hash,ScopedPK11Slot public_slot)570 bool InitializeNSSForChromeOSUserWithSlot(const std::string& username_hash,
571                                           ScopedPK11Slot public_slot) {
572   return g_token_manager.Get().InitializeNSSForChromeOSUserWithSlot(
573       username_hash, std::move(public_slot));
574 }
575 
ShouldInitializeTPMForChromeOSUser(const std::string & username_hash)576 bool ShouldInitializeTPMForChromeOSUser(const std::string& username_hash) {
577   return g_token_manager.Get().ShouldInitializeTPMForChromeOSUser(
578       username_hash);
579 }
580 
WillInitializeTPMForChromeOSUser(const std::string & username_hash)581 void WillInitializeTPMForChromeOSUser(const std::string& username_hash) {
582   g_token_manager.Get().WillInitializeTPMForChromeOSUser(username_hash);
583 }
584 
InitializeTPMForChromeOSUser(const std::string & username_hash,CK_SLOT_ID slot_id)585 void InitializeTPMForChromeOSUser(const std::string& username_hash,
586                                   CK_SLOT_ID slot_id) {
587   g_token_manager.Get().InitializeTPMForChromeOSUser(username_hash, slot_id);
588 }
589 
InitializePrivateSoftwareSlotForChromeOSUser(const std::string & username_hash)590 void InitializePrivateSoftwareSlotForChromeOSUser(
591     const std::string& username_hash) {
592   g_token_manager.Get().InitializePrivateSoftwareSlotForChromeOSUser(
593       username_hash);
594 }
595 
GetPublicSlotForChromeOSUser(const std::string & username_hash)596 ScopedPK11Slot GetPublicSlotForChromeOSUser(const std::string& username_hash) {
597   return g_token_manager.Get().GetPublicSlotForChromeOSUser(username_hash);
598 }
599 
GetPrivateSlotForChromeOSUser(const std::string & username_hash,base::OnceCallback<void (ScopedPK11Slot)> callback)600 ScopedPK11Slot GetPrivateSlotForChromeOSUser(
601     const std::string& username_hash,
602     base::OnceCallback<void(ScopedPK11Slot)> callback) {
603   return g_token_manager.Get().GetPrivateSlotForChromeOSUser(
604       username_hash, std::move(callback));
605 }
606 
CloseChromeOSUserForTesting(const std::string & username_hash)607 void CloseChromeOSUserForTesting(const std::string& username_hash) {
608   g_token_manager.Get().CloseChromeOSUserForTesting(username_hash);
609 }
610 
SetPrivateSoftwareSlotForChromeOSUserForTesting(ScopedPK11Slot slot)611 void SetPrivateSoftwareSlotForChromeOSUserForTesting(ScopedPK11Slot slot) {
612   g_token_manager.Get().SetPrivateSoftwareSlotForChromeOSUserForTesting(
613       std::move(slot));
614 }
615 
616 namespace {
PrintDirectoryInfo(const base::FilePath & path)617 void PrintDirectoryInfo(const base::FilePath& path) {
618   base::stat_wrapper_t file_stat;
619 
620   if (base::File::Stat(path.value().c_str(), &file_stat) == -1) {
621     base::File::Error error_code = base::File::OSErrorToFileError(errno);
622     LOG(ERROR) << "Failed to collect directory info, error: " << error_code;
623   }
624 
625   LOG(ERROR) << path << ", " << std::oct << file_stat.st_mode << std::dec
626              << ", "
627              << "uid " << file_stat.st_uid << ", "
628              << "gid " << file_stat.st_gid << std::endl;
629 }
630 }  // namespace
631 
632 // TODO(crbug.com/1163303): Remove when the bug is fixed.
DiagnosePublicSlotAndCrash(const base::FilePath & nss_path)633 void DiagnosePublicSlotAndCrash(const base::FilePath& nss_path) {
634   LOG(ERROR) << "Public slot is invalid. Start collecting stats.";
635   // Should be something like /home/chronos/u-<hash>/.pki/nssdb .
636   LOG(ERROR) << "NSS path: " << nss_path;
637 
638   {
639     // NSS files like pkcs11.txt, cert9.db, key4.db .
640     base::FileEnumerator files(
641         nss_path, /*recursive=*/false,
642         /*file_type=*/base::FileEnumerator::FILES,
643         /*pattern=*/base::FilePath::StringType(),
644         base::FileEnumerator::FolderSearchPolicy::MATCH_ONLY,
645         base::FileEnumerator::ErrorPolicy::STOP_ENUMERATION);
646     LOG(ERROR) << "Public slot database files:";
647     for (base::FilePath path = files.Next(); !path.empty();
648          path = files.Next()) {
649       base::FileEnumerator::FileInfo file_info = files.GetInfo();
650 
651       char buf[16];
652       int read_result = base::ReadFile(path, buf, sizeof(buf) - 1);
653 
654       LOG(ERROR) << file_info.GetName() << ", " << std::oct
655                  << file_info.stat().st_mode << std::dec << ", "
656                  << "uid " << file_info.stat().st_uid << ", "
657                  << "gid " << file_info.stat().st_gid << ", "
658                  << file_info.stat().st_size << " bytes, "
659                  << ((read_result > 0) ? "readable" : "not readable");
660     }
661     LOG(ERROR) << "Enumerate error code: " << files.GetError();
662   }
663 
664   // NSS directory might not be created yet, also check parent directories.
665   // Use u-hash directory as a comparison point for user and group ids and
666   // access permissions.
667 
668   base::FilePath nssdb_path = nss_path.Append(base::FilePath::kParentDirectory);
669   PrintDirectoryInfo(nssdb_path);
670 
671   base::FilePath pki_path = nssdb_path.Append(base::FilePath::kParentDirectory);
672   PrintDirectoryInfo(pki_path);
673 
674   base::FilePath u_hash_path =
675       pki_path.Append(base::FilePath::kParentDirectory);
676   PrintDirectoryInfo(u_hash_path);
677 
678   {
679     // Check whether the NSS path exists, and if not, check whether it's
680     // possible to create it.
681     if (base::DirectoryExists(nss_path)) {
682       LOG(ERROR) << "NSS path exists (as expected).";
683     } else {
684       base::File::Error error = base::File::Error::FILE_OK;
685       if (base::CreateDirectoryAndGetError(nss_path, &error)) {
686         LOG(ERROR) << "NSS path didn't exist. Created successfully.";
687       } else {
688         LOG(ERROR) << "NSS path didn't exist. Failed to create, error: "
689                    << error;
690       }
691     }
692   }
693 
694   CHECK(false) << "Public slot is invalid.";
695 }
696 
697 }  // namespace crypto
698