1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "chrome/browser/profiles/profile_io_data.h"
6 
7 #include <stddef.h>
8 
9 #include <utility>
10 
11 #include "base/bind.h"
12 #include "base/callback.h"
13 #include "base/callback_helpers.h"
14 #include "base/compiler_specific.h"
15 #include "base/feature_list.h"
16 #include "base/files/file_util.h"
17 #include "base/logging.h"
18 #include "base/single_thread_task_runner.h"
19 #include "base/strings/string_util.h"
20 #include "base/task/task_traits.h"
21 #include "base/threading/thread_task_runner_handle.h"
22 #include "build/build_config.h"
23 #include "chrome/browser/profiles/profile.h"
24 #include "chrome/common/buildflags.h"
25 #include "chrome/common/chrome_constants.h"
26 #include "chrome/common/chrome_features.h"
27 #include "chrome/common/url_constants.h"
28 #include "components/dom_distiller/core/url_constants.h"
29 #include "content/public/browser/browser_task_traits.h"
30 #include "content/public/browser/browser_thread.h"
31 #include "third_party/blink/public/common/features.h"
32 
33 #if BUILDFLAG(ENABLE_EXTENSIONS)
34 #include "extensions/common/constants.h"
35 #endif  // BUILDFLAG(ENABLE_EXTENSIONS)
36 
37 #if defined(OS_CHROMEOS)
38 #include "chrome/browser/chromeos/login/startup_utils.h"
39 #include "chrome/browser/chromeos/profiles/profile_helper.h"
40 #include "chrome/browser/chromeos/settings/cros_settings.h"
41 #include "chrome/browser/net/nss_context.h"
42 #include "chromeos/dbus/cryptohome/cryptohome_client.h"
43 #include "chromeos/dbus/dbus_thread_manager.h"
44 #include "chromeos/settings/cros_settings_names.h"
45 #include "chromeos/tpm/tpm_token_info_getter.h"
46 #include "components/user_manager/user.h"
47 #include "components/user_manager/user_manager.h"
48 #include "crypto/nss_util.h"
49 #include "crypto/nss_util_internal.h"
50 #endif  // defined(OS_CHROMEOS)
51 
52 using content::BrowserContext;
53 using content::BrowserThread;
54 using content::ResourceContext;
55 
56 namespace {
57 
58 #if defined(OS_CHROMEOS)
59 // The following four functions are responsible for initializing NSS for each
60 // profile on ChromeOS, which has a separate NSS database and TPM slot
61 // per-profile.
62 //
63 // Initialization basically follows these steps:
64 // 1) Get some info from user_manager::UserManager about the User for this
65 // profile.
66 // 2) Tell nss_util to initialize the software slot for this profile.
67 // 3) Wait for the TPM module to be loaded by nss_util if it isn't already.
68 // 4) Ask CryptohomeClient which TPM slot id corresponds to this profile.
69 // 5) Tell nss_util to use that slot id on the TPM module.
70 //
71 // Some of these steps must happen on the UI thread, others must happen on the
72 // IO thread:
73 //               UI thread                              IO Thread
74 //
75 //  ProfileIOData::InitializeOnUIThread
76 //                   |
77 //  ProfileHelper::Get()->GetUserByProfile()
78 //                   \---------------------------------------v
79 //                                                 StartNSSInitOnIOThread
80 //                                                           |
81 //                                          crypto::InitializeNSSForChromeOSUser
82 //                                                           |
83 //                                                crypto::IsTPMTokenReady
84 //                                                           |
85 //                                          StartTPMSlotInitializationOnIOThread
86 //                   v---------------------------------------/
87 //     GetTPMInfoForUserOnUIThread
88 //                   |
89 // chromeos::TPMTokenInfoGetter::Start
90 //                   |
91 //     DidGetTPMInfoForUserOnUIThread
92 //                   \---------------------------------------v
93 //                                          crypto::InitializeTPMForChromeOSUser
94 
DidGetTPMInfoForUserOnUIThread(std::unique_ptr<chromeos::TPMTokenInfoGetter> getter,const std::string & username_hash,base::Optional<chromeos::CryptohomeClient::TpmTokenInfo> token_info)95 void DidGetTPMInfoForUserOnUIThread(
96     std::unique_ptr<chromeos::TPMTokenInfoGetter> getter,
97     const std::string& username_hash,
98     base::Optional<chromeos::CryptohomeClient::TpmTokenInfo> token_info) {
99   DCHECK_CURRENTLY_ON(BrowserThread::UI);
100   if (token_info.has_value() && token_info->slot != -1) {
101     DVLOG(1) << "Got TPM slot for " << username_hash << ": "
102              << token_info->slot;
103     content::GetIOThreadTaskRunner({})->PostTask(
104         FROM_HERE, base::BindOnce(&crypto::InitializeTPMForChromeOSUser,
105                                   username_hash, token_info->slot));
106   } else {
107     NOTREACHED() << "TPMTokenInfoGetter reported invalid token.";
108   }
109 }
110 
GetTPMInfoForUserOnUIThread(const AccountId & account_id,const std::string & username_hash)111 void GetTPMInfoForUserOnUIThread(const AccountId& account_id,
112                                  const std::string& username_hash) {
113   DCHECK_CURRENTLY_ON(BrowserThread::UI);
114   DVLOG(1) << "Getting TPM info from cryptohome for "
115            << " " << account_id.Serialize() << " " << username_hash;
116   std::unique_ptr<chromeos::TPMTokenInfoGetter> scoped_token_info_getter =
117       chromeos::TPMTokenInfoGetter::CreateForUserToken(
118           account_id, chromeos::CryptohomeClient::Get(),
119           base::ThreadTaskRunnerHandle::Get());
120   chromeos::TPMTokenInfoGetter* token_info_getter =
121       scoped_token_info_getter.get();
122 
123   // Bind |token_info_getter| to the callback to ensure it does not go away
124   // before TPM token info is fetched.
125   // TODO(tbarzic, pneubeck): Handle this in a nicer way when this logic is
126   //     moved to a separate profile service.
127   token_info_getter->Start(base::BindOnce(&DidGetTPMInfoForUserOnUIThread,
128                                           std::move(scoped_token_info_getter),
129                                           username_hash));
130 }
131 
StartTPMSlotInitializationOnIOThread(const AccountId & account_id,const std::string & username_hash)132 void StartTPMSlotInitializationOnIOThread(const AccountId& account_id,
133                                           const std::string& username_hash) {
134   DCHECK_CURRENTLY_ON(BrowserThread::IO);
135 
136   content::GetUIThreadTaskRunner({})->PostTask(
137       FROM_HERE,
138       base::BindOnce(&GetTPMInfoForUserOnUIThread, account_id, username_hash));
139 }
140 
IsTPMTokenEnabledForNSS()141 bool IsTPMTokenEnabledForNSS() {
142 #if !defined(TPM_FALLBACK)
143   return crypto::IsTPMTokenEnabledForNSS();
144 #else
145   return false;
146 #endif
147 }
148 
StartNSSInitOnIOThread(const AccountId & account_id,const std::string & username_hash,const base::FilePath & path)149 void StartNSSInitOnIOThread(const AccountId& account_id,
150                             const std::string& username_hash,
151                             const base::FilePath& path) {
152   DCHECK_CURRENTLY_ON(BrowserThread::IO);
153   DVLOG(1) << "Starting NSS init for " << account_id.Serialize()
154            << "  hash:" << username_hash;
155 
156   // Make sure NSS is initialized for the user.
157   crypto::InitializeNSSForChromeOSUser(username_hash, path);
158 
159   // Check if it's OK to initialize TPM for the user before continuing. This
160   // may not be the case if the TPM slot initialization was previously
161   // requested for the same user.
162   if (!crypto::ShouldInitializeTPMForChromeOSUser(username_hash))
163     return;
164 
165   crypto::WillInitializeTPMForChromeOSUser(username_hash);
166 
167   if (IsTPMTokenEnabledForNSS()) {
168     if (crypto::IsTPMTokenReady(
169             base::BindOnce(&StartTPMSlotInitializationOnIOThread, account_id,
170                            username_hash))) {
171       StartTPMSlotInitializationOnIOThread(account_id, username_hash);
172     } else {
173       DVLOG(1) << "Waiting for tpm ready ...";
174     }
175   } else {
176     crypto::InitializePrivateSoftwareSlotForChromeOSUser(username_hash);
177   }
178 }
179 #endif  // defined(OS_CHROMEOS)
180 
181 }  // namespace
182 
InitializeOnUIThread(Profile * profile)183 void ProfileIOData::InitializeOnUIThread(Profile* profile) {
184   DCHECK_CURRENTLY_ON(BrowserThread::UI);
185 
186   auto params = std::make_unique<ProfileParams>();
187 
188 #if defined(OS_CHROMEOS)
189   const user_manager::User* user =
190       chromeos::ProfileHelper::Get()->GetUserByProfile(profile);
191   // No need to initialize NSS for users with empty username hash:
192   // Getters for a user's NSS slots always return a null slot if the user's
193   // username hash is empty, even when the NSS is not initialized for the
194   // user.
195   if (user && !user->username_hash().empty()) {
196     params->username_hash = user->username_hash();
197     DCHECK(!params->username_hash.empty());
198     content::GetIOThreadTaskRunner({})->PostTask(
199         FROM_HERE, base::BindOnce(&StartNSSInitOnIOThread, user->GetAccountId(),
200                                   user->username_hash(), profile->GetPath()));
201 
202     if (user->IsAffiliated()) {
203       params->user_is_affiliated = true;
204     }
205   }
206 #endif
207 
208   profile_params_ = std::move(params);
209 
210   // We need to make sure that content initializes its own data structures that
211   // are associated with each ResourceContext because we might post this
212   // object to the IO thread after this function.
213   BrowserContext::EnsureResourceContextInitialized(profile);
214 
215   content::GetIOThreadTaskRunner({})->PostTask(
216       FROM_HERE, base::BindOnce(&ProfileIOData::Init, base::Unretained(this)));
217 }
218 
219 ProfileIOData::ProfileParams::ProfileParams() = default;
220 
221 ProfileIOData::ProfileParams::~ProfileParams() = default;
222 
ProfileIOData()223 ProfileIOData::ProfileIOData()
224     : initialized_(false),
225       resource_context_(new ResourceContext(this)) {
226   DCHECK_CURRENTLY_ON(BrowserThread::UI);
227 }
228 
~ProfileIOData()229 ProfileIOData::~ProfileIOData() {
230   if (BrowserThread::IsThreadInitialized(BrowserThread::IO))
231     DCHECK_CURRENTLY_ON(BrowserThread::IO);
232 }
233 
234 // static
FromResourceContext(content::ResourceContext * rc)235 ProfileIOData* ProfileIOData::FromResourceContext(
236     content::ResourceContext* rc) {
237   return (static_cast<ResourceContext*>(rc))->io_data_;
238 }
239 
240 // static
IsHandledProtocol(const std::string & scheme)241 bool ProfileIOData::IsHandledProtocol(const std::string& scheme) {
242   DCHECK_EQ(scheme, base::ToLowerASCII(scheme));
243   static const char* const kProtocolList[] = {
244     url::kHttpScheme,
245     url::kHttpsScheme,
246 #if BUILDFLAG(ENABLE_WEBSOCKETS)
247     url::kWsScheme,
248     url::kWssScheme,
249 #endif  // BUILDFLAG(ENABLE_WEBSOCKETS)
250     url::kFileScheme,
251     content::kChromeDevToolsScheme,
252     dom_distiller::kDomDistillerScheme,
253 #if BUILDFLAG(ENABLE_EXTENSIONS)
254     extensions::kExtensionScheme,
255 #endif
256     content::kChromeUIScheme,
257     content::kChromeUIUntrustedScheme,
258     url::kDataScheme,
259 #if defined(OS_CHROMEOS)
260     content::kExternalFileScheme,
261 #endif  // defined(OS_CHROMEOS)
262 #if defined(OS_ANDROID)
263     url::kContentScheme,
264 #endif  // defined(OS_ANDROID)
265     url::kAboutScheme,
266     url::kBlobScheme,
267     url::kFileSystemScheme,
268     chrome::kChromeSearchScheme,
269   };
270   for (const char* supported_protocol : kProtocolList) {
271     if (scheme == supported_protocol)
272       return true;
273   }
274 #if !BUILDFLAG(DISABLE_FTP_SUPPORT)
275   if (scheme == url::kFtpScheme &&
276       base::FeatureList::IsEnabled(blink::features::kFtpProtocol)) {
277     return true;
278   }
279 #endif  // !BUILDFLAG(DISABLE_FTP_SUPPORT)
280   return false;
281 }
282 
283 // static
IsHandledURL(const GURL & url)284 bool ProfileIOData::IsHandledURL(const GURL& url) {
285   if (!url.is_valid()) {
286     // We handle error cases.
287     return true;
288   }
289 
290   return IsHandledProtocol(url.scheme());
291 }
292 
GetResourceContext() const293 content::ResourceContext* ProfileIOData::GetResourceContext() const {
294   return resource_context_.get();
295 }
296 
ResourceContext(ProfileIOData * io_data)297 ProfileIOData::ResourceContext::ResourceContext(ProfileIOData* io_data)
298     : io_data_(io_data) {
299   DCHECK(io_data);
300 }
301 
~ResourceContext()302 ProfileIOData::ResourceContext::~ResourceContext() {}
303 
Init() const304 void ProfileIOData::Init() const {
305   DCHECK_CURRENTLY_ON(BrowserThread::IO);
306   DCHECK(!initialized_);
307   DCHECK(profile_params_.get());
308 
309 #if defined(OS_CHROMEOS)
310   username_hash_ = profile_params_->username_hash;
311   // If we're using the system slot for certificate management, we also must
312   // have access to the user's slots.
313   DCHECK(!(username_hash_.empty() && profile_params_->user_is_affiliated));
314   // Use the device-wide system key slot only if the user is affiliated on
315   // the device.
316   if (profile_params_->user_is_affiliated) {
317     EnableNSSSystemKeySlotForResourceContext(resource_context_.get());
318   }
319 #endif
320 
321   profile_params_.reset();
322   initialized_ = true;
323 }
324 
ShutdownOnUIThread()325 void ProfileIOData::ShutdownOnUIThread() {
326   DCHECK_CURRENTLY_ON(BrowserThread::UI);
327 
328   bool posted = content::GetIOThreadTaskRunner({})->DeleteSoon(FROM_HERE, this);
329   if (!posted)
330     delete this;
331 }
332