1 // Copyright 2014 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.
5 #include "chrome/browser/component_updater/supervised_user_whitelist_installer.h"
8 #include "base/callback.h"
9 #include "base/files/file_enumerator.h"
10 #include "base/files/file_path.h"
11 #include "base/files/file_util.h"
12 #include "base/files/important_file_writer.h"
13 #include "base/location.h"
14 #include "base/memory/weak_ptr.h"
15 #include "base/metrics/user_metrics.h"
16 #include "base/metrics/user_metrics_action.h"
17 #include "base/path_service.h"
18 #include "base/prefs/pref_registry_simple.h"
19 #include "base/prefs/pref_service.h"
20 #include "base/prefs/scoped_user_pref_update.h"
21 #include "base/scoped_observer.h"
22 #include "base/sequenced_task_runner.h"
23 #include "base/strings/string_util.h"
24 #include "base/thread_task_runner_handle.h"
25 #include "chrome/browser/profiles/profile_info_cache.h"
26 #include "chrome/browser/profiles/profile_info_cache_observer.h"
27 #include "chrome/common/chrome_paths.h"
28 #include "chrome/common/pref_names.h"
29 #include "components/component_updater/component_updater_paths.h"
30 #include "components/component_updater/component_updater_service.h"
31 #include "components/component_updater/default_component_installer.h"
32 #include "components/crx_file/id_util.h"
33 #include "components/safe_json/json_sanitizer.h"
35 namespace component_updater
{
39 const char kSanitizedWhitelistExtension
[] = ".json";
41 const char kWhitelist
[] = "whitelist";
42 const char kFile
[] = "file";
44 const char kClients
[] = "clients";
45 const char kName
[] = "name";
47 base::FilePath
GetRawWhitelistPath(const base::DictionaryValue
& manifest
,
48 const base::FilePath
& install_dir
) {
49 const base::DictionaryValue
* whitelist_dict
= nullptr;
50 if (!manifest
.GetDictionary(kWhitelist
, &whitelist_dict
))
51 return base::FilePath();
53 base::FilePath::StringType whitelist_file
;
54 if (!whitelist_dict
->GetString(kFile
, &whitelist_file
))
55 return base::FilePath();
57 return install_dir
.Append(whitelist_file
);
60 base::FilePath
GetSanitizedWhitelistPath(const std::string
& crx_id
) {
61 base::FilePath base_dir
;
62 PathService::Get(chrome::DIR_SUPERVISED_USER_INSTALLED_WHITELISTS
, &base_dir
);
63 return base_dir
.empty()
65 : base_dir
.AppendASCII(crx_id
+ kSanitizedWhitelistExtension
);
68 void OnWhitelistSanitizationError(const base::FilePath
& whitelist
,
69 const std::string
& error
) {
70 LOG(WARNING
) << "Invalid whitelist " << whitelist
.value() << ": " << error
;
73 void OnWhitelistSanitizationResult(
74 const std::string
& crx_id
,
75 const scoped_refptr
<base::SequencedTaskRunner
>& task_runner
,
76 const base::Closure
& callback
,
77 const std::string
& result
) {
78 const base::FilePath sanitized_whitelist_path
=
79 GetSanitizedWhitelistPath(crx_id
);
80 const base::FilePath install_directory
= sanitized_whitelist_path
.DirName();
81 if (!base::DirectoryExists(install_directory
)) {
82 if (!base::CreateDirectory(install_directory
)) {
83 PLOG(ERROR
) << "Could't create directory " << install_directory
.value();
88 const int size
= result
.size();
89 if (base::WriteFile(sanitized_whitelist_path
, result
.data(), size
) != size
) {
90 PLOG(ERROR
) << "Couldn't write file " << sanitized_whitelist_path
.value();
93 task_runner
->PostTask(FROM_HERE
, callback
);
96 void CheckForSanitizedWhitelistOnTaskRunner(
97 const std::string
& crx_id
,
98 const base::FilePath
& whitelist_path
,
99 const scoped_refptr
<base::SequencedTaskRunner
>& task_runner
,
100 const base::Closure
& callback
) {
101 if (base::PathExists(GetSanitizedWhitelistPath(crx_id
))) {
102 task_runner
->PostTask(FROM_HERE
, callback
);
106 std::string unsafe_json
;
107 if (!base::ReadFileToString(whitelist_path
, &unsafe_json
)) {
108 PLOG(ERROR
) << "Couldn't read file " << whitelist_path
.value();
112 safe_json::JsonSanitizer::Sanitize(
114 base::Bind(&OnWhitelistSanitizationResult
, crx_id
, task_runner
, callback
),
115 base::Bind(&OnWhitelistSanitizationError
, whitelist_path
));
118 void RemoveUnregisteredWhitelistsOnTaskRunner(
119 const std::set
<std::string
>& registered_whitelists
) {
120 base::FilePath base_dir
;
121 PathService::Get(DIR_SUPERVISED_USER_WHITELISTS
, &base_dir
);
122 if (!base_dir
.empty()) {
123 base::FileEnumerator
file_enumerator(base_dir
, false,
124 base::FileEnumerator::DIRECTORIES
);
125 for (base::FilePath path
= file_enumerator
.Next(); !path
.value().empty();
126 path
= file_enumerator
.Next()) {
127 const std::string crx_id
= path
.BaseName().MaybeAsASCII();
129 // Ignore folders that don't have valid CRX ID names. These folders are
130 // not managed by the component installer, so do not try to remove them.
131 if (!crx_file::id_util::IdIsValid(crx_id
))
134 // Ignore folders that correspond to registered whitelists.
135 if (registered_whitelists
.count(crx_id
) > 0)
139 base::UserMetricsAction("ManagedUsers_Whitelist_UncleanUninstall"));
141 if (!base::DeleteFile(path
, true))
142 DPLOG(ERROR
) << "Couldn't delete " << path
.value();
146 PathService::Get(chrome::DIR_SUPERVISED_USER_INSTALLED_WHITELISTS
, &base_dir
);
147 if (!base_dir
.empty()) {
148 base::FilePath
pattern(FILE_PATH_LITERAL("*"));
149 pattern
= pattern
.AppendASCII(kSanitizedWhitelistExtension
);
150 base::FileEnumerator
file_enumerator(
151 base_dir
, false, base::FileEnumerator::FILES
, pattern
.value());
152 for (base::FilePath path
= file_enumerator
.Next(); !path
.value().empty();
153 path
= file_enumerator
.Next()) {
154 // Ignore files that don't have valid CRX ID names. These files are not
155 // managed by the component installer, so do not try to remove them.
156 const std::string filename
= path
.BaseName().MaybeAsASCII();
157 DCHECK(base::EndsWith(filename
, kSanitizedWhitelistExtension
,
158 base::CompareCase::SENSITIVE
));
160 const std::string crx_id
= filename
.substr(
161 filename
.size() - strlen(kSanitizedWhitelistExtension
));
163 if (!crx_file::id_util::IdIsValid(crx_id
))
166 // Ignore files that correspond to registered whitelists.
167 if (registered_whitelists
.count(crx_id
) > 0)
171 base::UserMetricsAction("ManagedUsers_Whitelist_UncleanUninstall"));
173 if (!base::DeleteFile(path
, true))
174 DPLOG(ERROR
) << "Couldn't delete " << path
.value();
179 class SupervisedUserWhitelistComponentInstallerTraits
180 : public ComponentInstallerTraits
{
182 SupervisedUserWhitelistComponentInstallerTraits(
183 const std::string
& crx_id
,
184 const std::string
& name
,
185 const base::Callback
<void(const base::FilePath
&)>& callback
)
186 : crx_id_(crx_id
), name_(name
), callback_(callback
) {}
187 ~SupervisedUserWhitelistComponentInstallerTraits() override
{}
190 // ComponentInstallerTraits overrides:
191 bool VerifyInstallation(const base::DictionaryValue
& manifest
,
192 const base::FilePath
& install_dir
) const override
;
193 bool CanAutoUpdate() const override
;
194 bool OnCustomInstall(const base::DictionaryValue
& manifest
,
195 const base::FilePath
& install_dir
) override
;
196 void ComponentReady(const base::Version
& version
,
197 const base::FilePath
& install_dir
,
198 scoped_ptr
<base::DictionaryValue
> manifest
) override
;
199 base::FilePath
GetBaseDirectory() const override
;
200 void GetHash(std::vector
<uint8_t>* hash
) const override
;
201 std::string
GetName() const override
;
205 base::Callback
<void(const base::FilePath
&)> callback_
;
207 DISALLOW_COPY_AND_ASSIGN(SupervisedUserWhitelistComponentInstallerTraits
);
210 bool SupervisedUserWhitelistComponentInstallerTraits::VerifyInstallation(
211 const base::DictionaryValue
& manifest
,
212 const base::FilePath
& install_dir
) const {
213 // Check whether the whitelist exists at the path specified by the manifest.
214 // This does not check whether the whitelist is wellformed.
215 return base::PathExists(GetRawWhitelistPath(manifest
, install_dir
));
218 bool SupervisedUserWhitelistComponentInstallerTraits::CanAutoUpdate() const {
222 bool SupervisedUserWhitelistComponentInstallerTraits::OnCustomInstall(
223 const base::DictionaryValue
& manifest
,
224 const base::FilePath
& install_dir
) {
225 // Delete the existing sanitized whitelist.
226 return base::DeleteFile(GetSanitizedWhitelistPath(crx_id_
), false);
229 void SupervisedUserWhitelistComponentInstallerTraits::ComponentReady(
230 const base::Version
& version
,
231 const base::FilePath
& install_dir
,
232 scoped_ptr
<base::DictionaryValue
> manifest
) {
233 callback_
.Run(GetRawWhitelistPath(*manifest
, install_dir
));
237 SupervisedUserWhitelistComponentInstallerTraits::GetBaseDirectory() const {
238 base::FilePath whitelist_directory
;
239 PathService::Get(DIR_SUPERVISED_USER_WHITELISTS
, &whitelist_directory
);
240 return whitelist_directory
.AppendASCII(crx_id_
);
243 void SupervisedUserWhitelistComponentInstallerTraits::GetHash(
244 std::vector
<uint8_t>* hash
) const {
245 *hash
= SupervisedUserWhitelistInstaller::GetHashFromCrxId(crx_id_
);
248 std::string
SupervisedUserWhitelistComponentInstallerTraits::GetName() const {
252 class SupervisedUserWhitelistInstallerImpl
253 : public SupervisedUserWhitelistInstaller
,
254 public ProfileInfoCacheObserver
{
256 SupervisedUserWhitelistInstallerImpl(ComponentUpdateService
* cus
,
257 ProfileInfoCache
* profile_info_cache
,
258 PrefService
* local_state
);
259 ~SupervisedUserWhitelistInstallerImpl() override
{}
262 void RegisterComponent(const std::string
& crx_id
,
263 const std::string
& name
,
264 const base::Closure
& callback
);
265 void RegisterNewComponent(const std::string
& crx_id
, const std::string
& name
);
266 bool UnregisterWhitelistInternal(base::DictionaryValue
* pref_dict
,
267 const std::string
& client_id
,
268 const std::string
& crx_id
);
270 void OnRawWhitelistReady(const std::string
& crx_id
,
271 const base::FilePath
& whitelist_path
);
272 void OnSanitizedWhitelistReady(const std::string
& crx_id
);
274 // SupervisedUserWhitelistInstaller overrides:
275 void RegisterComponents() override
;
276 void Subscribe(const WhitelistReadyCallback
& callback
) override
;
277 void RegisterWhitelist(const std::string
& client_id
,
278 const std::string
& crx_id
,
279 const std::string
& name
) override
;
280 void UnregisterWhitelist(const std::string
& client_id
,
281 const std::string
& crx_id
) override
;
283 // ProfileInfoCacheObserver overrides:
284 void OnProfileWillBeRemoved(const base::FilePath
& profile_path
) override
;
286 ComponentUpdateService
* cus_
;
287 PrefService
* local_state_
;
289 std::vector
<WhitelistReadyCallback
> callbacks_
;
291 ScopedObserver
<ProfileInfoCache
, ProfileInfoCacheObserver
> observer_
;
293 base::WeakPtrFactory
<SupervisedUserWhitelistInstallerImpl
> weak_ptr_factory_
;
295 DISALLOW_COPY_AND_ASSIGN(SupervisedUserWhitelistInstallerImpl
);
298 SupervisedUserWhitelistInstallerImpl::SupervisedUserWhitelistInstallerImpl(
299 ComponentUpdateService
* cus
,
300 ProfileInfoCache
* profile_info_cache
,
301 PrefService
* local_state
)
303 local_state_(local_state
),
305 weak_ptr_factory_(this) {
308 // In unit tests, the profile info cache can be null.
309 if (profile_info_cache
)
310 observer_
.Add(profile_info_cache
);
313 void SupervisedUserWhitelistInstallerImpl::RegisterComponent(
314 const std::string
& crx_id
,
315 const std::string
& name
,
316 const base::Closure
& callback
) {
317 scoped_ptr
<ComponentInstallerTraits
> traits(
318 new SupervisedUserWhitelistComponentInstallerTraits(
320 base::Bind(&SupervisedUserWhitelistInstallerImpl::OnRawWhitelistReady
,
321 weak_ptr_factory_
.GetWeakPtr(), crx_id
)));
322 scoped_refptr
<DefaultComponentInstaller
> installer(
323 new DefaultComponentInstaller(traits
.Pass()));
324 installer
->Register(cus_
, callback
);
327 void SupervisedUserWhitelistInstallerImpl::RegisterNewComponent(
328 const std::string
& crx_id
,
329 const std::string
& name
) {
332 base::Bind(&SupervisedUserWhitelistInstaller::TriggerComponentUpdate
,
333 &cus_
->GetOnDemandUpdater(), crx_id
));
336 bool SupervisedUserWhitelistInstallerImpl::UnregisterWhitelistInternal(
337 base::DictionaryValue
* pref_dict
,
338 const std::string
& client_id
,
339 const std::string
& crx_id
) {
340 base::DictionaryValue
* whitelist_dict
= nullptr;
342 pref_dict
->GetDictionaryWithoutPathExpansion(crx_id
, &whitelist_dict
);
344 base::ListValue
* clients
= nullptr;
345 success
= whitelist_dict
->GetList(kClients
, &clients
);
347 const bool removed
= clients
->Remove(base::StringValue(client_id
), nullptr);
349 if (!clients
->empty())
352 pref_dict
->RemoveWithoutPathExpansion(crx_id
, nullptr);
353 bool result
= cus_
->UnregisterComponent(crx_id
);
356 result
= base::DeleteFile(GetSanitizedWhitelistPath(crx_id
), false);
362 void SupervisedUserWhitelistInstallerImpl::OnRawWhitelistReady(
363 const std::string
& crx_id
,
364 const base::FilePath
& whitelist_path
) {
365 cus_
->GetSequencedTaskRunner()->PostTask(
368 &CheckForSanitizedWhitelistOnTaskRunner
, crx_id
, whitelist_path
,
369 base::ThreadTaskRunnerHandle::Get(),
371 &SupervisedUserWhitelistInstallerImpl::OnSanitizedWhitelistReady
,
372 weak_ptr_factory_
.GetWeakPtr(), crx_id
)));
375 void SupervisedUserWhitelistInstallerImpl::OnSanitizedWhitelistReady(
376 const std::string
& crx_id
) {
377 for (const WhitelistReadyCallback
& callback
: callbacks_
)
378 callback
.Run(crx_id
, GetSanitizedWhitelistPath(crx_id
));
381 void SupervisedUserWhitelistInstallerImpl::RegisterComponents() {
382 std::set
<std::string
> registered_whitelists
;
383 const base::DictionaryValue
* whitelists
=
384 local_state_
->GetDictionary(prefs::kRegisteredSupervisedUserWhitelists
);
385 for (base::DictionaryValue::Iterator
it(*whitelists
); !it
.IsAtEnd();
387 const base::DictionaryValue
* dict
= nullptr;
388 it
.value().GetAsDictionary(&dict
);
390 bool result
= dict
->GetString(kName
, &name
);
392 const std::string
& id
= it
.key();
393 RegisterComponent(id
, name
, base::Closure());
395 registered_whitelists
.insert(id
);
398 cus_
->GetSequencedTaskRunner()->PostTask(
399 FROM_HERE
, base::Bind(&RemoveUnregisteredWhitelistsOnTaskRunner
,
400 registered_whitelists
));
403 void SupervisedUserWhitelistInstallerImpl::Subscribe(
404 const WhitelistReadyCallback
& callback
) {
405 return callbacks_
.push_back(callback
);
408 void SupervisedUserWhitelistInstallerImpl::RegisterWhitelist(
409 const std::string
& client_id
,
410 const std::string
& crx_id
,
411 const std::string
& name
) {
412 DictionaryPrefUpdate
update(local_state_
,
413 prefs::kRegisteredSupervisedUserWhitelists
);
414 base::DictionaryValue
* pref_dict
= update
.Get();
415 base::DictionaryValue
* whitelist_dict
= nullptr;
416 bool newly_added
= false;
417 if (!pref_dict
->GetDictionaryWithoutPathExpansion(crx_id
, &whitelist_dict
)) {
418 whitelist_dict
= new base::DictionaryValue
;
419 whitelist_dict
->SetString(kName
, name
);
420 pref_dict
->SetWithoutPathExpansion(crx_id
, whitelist_dict
);
424 base::ListValue
* clients
= nullptr;
425 if (!whitelist_dict
->GetList(kClients
, &clients
)) {
427 clients
= new base::ListValue
;
428 whitelist_dict
->Set(kClients
, clients
);
430 bool success
= clients
->AppendIfNotPresent(new base::StringValue(client_id
));
434 // Sanity-check that the stored name is equal to the name passed in.
435 // In release builds this is a no-op.
436 std::string stored_name
;
437 DCHECK(whitelist_dict
->GetString(kName
, &stored_name
));
438 DCHECK_EQ(stored_name
, name
);
442 RegisterNewComponent(crx_id
, name
);
445 void SupervisedUserWhitelistInstallerImpl::UnregisterWhitelist(
446 const std::string
& client_id
,
447 const std::string
& crx_id
) {
448 DictionaryPrefUpdate
update(local_state_
,
449 prefs::kRegisteredSupervisedUserWhitelists
);
450 bool removed
= UnregisterWhitelistInternal(update
.Get(), client_id
, crx_id
);
454 void SupervisedUserWhitelistInstallerImpl::OnProfileWillBeRemoved(
455 const base::FilePath
& profile_path
) {
456 std::string client_id
= ClientIdForProfilePath(profile_path
);
458 // Go through all registered whitelists and possibly unregister them for this
460 DictionaryPrefUpdate
update(local_state_
,
461 prefs::kRegisteredSupervisedUserWhitelists
);
462 base::DictionaryValue
* pref_dict
= update
.Get();
463 for (base::DictionaryValue::Iterator
it(*pref_dict
); !it
.IsAtEnd();
465 UnregisterWhitelistInternal(pref_dict
, client_id
, it
.key());
472 scoped_ptr
<SupervisedUserWhitelistInstaller
>
473 SupervisedUserWhitelistInstaller::Create(ComponentUpdateService
* cus
,
474 ProfileInfoCache
* profile_info_cache
,
475 PrefService
* local_state
) {
476 return make_scoped_ptr(new SupervisedUserWhitelistInstallerImpl(
477 cus
, profile_info_cache
, local_state
));
481 void SupervisedUserWhitelistInstaller::RegisterPrefs(
482 PrefRegistrySimple
* registry
) {
483 registry
->RegisterDictionaryPref(prefs::kRegisteredSupervisedUserWhitelists
);
487 std::string
SupervisedUserWhitelistInstaller::ClientIdForProfilePath(
488 const base::FilePath
& profile_path
) {
489 // See ProfileInfoCache::CacheKeyFromProfilePath().
490 return profile_path
.BaseName().MaybeAsASCII();
494 std::vector
<uint8_t> SupervisedUserWhitelistInstaller::GetHashFromCrxId(
495 const std::string
& crx_id
) {
496 DCHECK(crx_file::id_util::IdIsValid(crx_id
));
498 std::vector
<uint8_t> hash
;
500 for (size_t i
= 0; i
< crx_id
.size(); ++i
) {
501 // Uppercase characters in IDs are technically legal.
502 int val
= base::ToLowerASCII(crx_id
[i
]) - 'a';
508 hash
.push_back(16 * byte
+ val
);
516 void SupervisedUserWhitelistInstaller::TriggerComponentUpdate(
517 OnDemandUpdater
* updater
,
518 const std::string
& crx_id
) {
519 const bool result
= updater
->OnDemandUpdate(crx_id
);
523 } // namespace component_updater