• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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