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