OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 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/chromeos/user_cros_settings_provider.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/bind_helpers.h" | |
9 #include "base/callback.h" | |
10 #include "base/hash_tables.h" | |
11 #include "base/logging.h" | |
12 #include "base/memory/singleton.h" | |
13 #include "base/string_util.h" | |
14 #include "base/values.h" | |
15 #include "chrome/browser/browser_process.h" | |
16 #include "chrome/browser/chromeos/cros/cros_library.h" | |
17 #include "chrome/browser/chromeos/cros/network_library.h" | |
18 #include "chrome/browser/chromeos/cros_settings.h" | |
19 #include "chrome/browser/chromeos/cros_settings_names.h" | |
20 #include "chrome/browser/chromeos/login/ownership_service.h" | |
21 #include "chrome/browser/chromeos/login/ownership_status_checker.h" | |
22 #include "chrome/browser/chromeos/login/user_manager.h" | |
23 #include "chrome/browser/prefs/pref_service.h" | |
24 #include "chrome/browser/prefs/pref_value_map.h" | |
25 #include "chrome/browser/prefs/scoped_user_pref_update.h" | |
26 #include "chrome/browser/ui/options/options_util.h" | |
27 #include "chrome/common/chrome_notification_types.h" | |
28 #include "chrome/installer/util/google_update_settings.h" | |
29 #include "content/public/browser/browser_thread.h" | |
30 #include "content/public/browser/notification_service.h" | |
31 | |
32 using content::BrowserThread; | |
33 | |
34 namespace chromeos { | |
35 | |
36 namespace { | |
37 | |
38 const char kTrueIncantation[] = "true"; | |
39 const char kFalseIncantation[] = "false"; | |
40 const char kTrustedSuffix[] = "/trusted"; | |
41 | |
42 // For all our boolean settings following is applicable: | |
43 // true is default permissive value and false is safe prohibitic value. | |
44 // Exception: kSignedDataRoamingEnabled which has default value of false. | |
45 const char* kBooleanSettings[] = { | |
46 kAccountsPrefAllowNewUser, | |
47 kAccountsPrefAllowGuest, | |
48 kAccountsPrefShowUserNamesOnSignIn, | |
49 kSignedDataRoamingEnabled, | |
50 kStatsReportingPref | |
51 }; | |
52 | |
53 const char* kStringSettings[] = { | |
54 kDeviceOwner, | |
55 kReleaseChannel | |
56 }; | |
57 | |
58 const char* kListSettings[] = { | |
59 kAccountsPrefUsers | |
60 }; | |
61 | |
62 // This class provides the means to migrate settings to the signed settings | |
63 // store. It does one of three things - store the settings in the policy blob | |
64 // immediately if the current user is the owner. Uses the | |
65 // SignedSettingsTempStorage if there is no owner yet, or waits for an | |
66 // OWNERSHIP_CHECKED notification to delay the storing until the owner has | |
67 // logged in. | |
68 class MigrationHelper : public content::NotificationObserver { | |
69 public: | |
70 explicit MigrationHelper() : callback_(NULL) { | |
71 registrar_.Add(this, chrome::NOTIFICATION_OWNERSHIP_CHECKED, | |
72 content::NotificationService::AllSources()); | |
73 } | |
74 | |
75 void set_callback(SignedSettingsHelper::Callback* callback) { | |
76 callback_ = callback; | |
77 } | |
78 | |
79 void AddMigrationValue(const std::string& path, base::Value* value) { | |
80 migration_values_.SetValue(path, value); | |
81 } | |
82 | |
83 void MigrateValues(void) { | |
84 ownership_checker_.reset(new OwnershipStatusChecker( | |
85 base::Bind(&MigrationHelper::DoMigrateValues, base::Unretained(this)))); | |
86 } | |
87 | |
88 // NotificationObserver overrides: | |
89 virtual void Observe(int type, | |
90 const content::NotificationSource& source, | |
91 const content::NotificationDetails& details) OVERRIDE { | |
92 if (type == chrome::NOTIFICATION_OWNERSHIP_CHECKED) | |
93 MigrateValues(); | |
94 } | |
95 | |
96 private: | |
97 void DoMigrateValues(OwnershipService::Status status, | |
98 bool current_user_is_owner) { | |
99 ownership_checker_.reset(NULL); | |
100 | |
101 // We can call StartStorePropertyOp in two cases - either if the owner is | |
102 // currently logged in and the policy can be updated immediately or if there | |
103 // is no owner yet in which case the value will be temporarily stored in the | |
104 // SignedSettingsTempStorage until the device is owned. If none of these | |
105 // cases is met then we will wait for user change notification and retry. | |
106 if (current_user_is_owner || status != OwnershipService::OWNERSHIP_TAKEN) { | |
107 PrefValueMap::const_iterator i; | |
108 for (i = migration_values_.begin(); i != migration_values_.end(); ++i) { | |
109 // Queue all values for storing. | |
110 SignedSettingsHelper::Get()->StartStorePropertyOp(i->first, *i->second, | |
111 callback_); | |
112 } | |
113 migration_values_.Clear(); | |
114 } | |
115 } | |
116 | |
117 content::NotificationRegistrar registrar_; | |
118 scoped_ptr<OwnershipStatusChecker> ownership_checker_; | |
119 SignedSettingsHelper::Callback* callback_; | |
120 PrefValueMap migration_values_; | |
121 | |
122 DISALLOW_COPY_AND_ASSIGN(MigrationHelper); | |
123 }; | |
124 | |
125 bool IsControlledBooleanSetting(const std::string& pref_path) { | |
126 // TODO(nkostylev): Using std::find for 4 value array generates this warning | |
127 // in chroot stl_algo.h:231: error: array subscript is above array bounds. | |
128 // GCC 4.4.3 | |
129 return (pref_path == kAccountsPrefAllowNewUser) || | |
130 (pref_path == kAccountsPrefAllowGuest) || | |
131 (pref_path == kAccountsPrefShowUserNamesOnSignIn) || | |
132 (pref_path == kSignedDataRoamingEnabled) || | |
133 (pref_path == kStatsReportingPref); | |
134 } | |
135 | |
136 bool IsControlledStringSetting(const std::string& pref_path) { | |
137 return std::find(kStringSettings, | |
138 kStringSettings + arraysize(kStringSettings), | |
139 pref_path) != | |
140 kStringSettings + arraysize(kStringSettings); | |
141 } | |
142 | |
143 bool IsControlledListSetting(const std::string& pref_path) { | |
144 return std::find(kListSettings, | |
145 kListSettings + arraysize(kListSettings), | |
146 pref_path) != | |
147 kListSettings + arraysize(kListSettings); | |
148 } | |
149 | |
150 bool IsControlledSetting(const std::string& pref_path) { | |
151 return (IsControlledBooleanSetting(pref_path) || | |
152 IsControlledStringSetting(pref_path) || | |
153 IsControlledListSetting(pref_path)); | |
154 } | |
155 | |
156 void RegisterSetting(PrefService* local_state, const std::string& pref_path) { | |
157 local_state->RegisterBooleanPref((pref_path + kTrustedSuffix).c_str(), | |
158 false, | |
159 PrefService::UNSYNCABLE_PREF); | |
160 if (IsControlledBooleanSetting(pref_path)) { | |
161 if (pref_path == kSignedDataRoamingEnabled || | |
162 pref_path == kStatsReportingPref) | |
163 local_state->RegisterBooleanPref(pref_path.c_str(), | |
164 false, | |
165 PrefService::UNSYNCABLE_PREF); | |
166 else | |
167 local_state->RegisterBooleanPref(pref_path.c_str(), | |
168 true, | |
169 PrefService::UNSYNCABLE_PREF); | |
170 } else if (IsControlledStringSetting(pref_path)) { | |
171 local_state->RegisterStringPref(pref_path.c_str(), | |
172 "", | |
173 PrefService::UNSYNCABLE_PREF); | |
174 } else { | |
175 DCHECK(IsControlledListSetting(pref_path)); | |
176 local_state->RegisterListPref(pref_path.c_str(), | |
177 PrefService::UNSYNCABLE_PREF); | |
178 } | |
179 } | |
180 | |
181 enum UseValue { | |
182 USE_VALUE_SUPPLIED, | |
183 USE_VALUE_DEFAULT | |
184 }; | |
185 | |
186 void UpdateCache(const std::string& name, | |
187 const base::Value* value, | |
188 UseValue use_value) { | |
189 PrefService* prefs = g_browser_process->local_state(); | |
190 if (use_value == USE_VALUE_DEFAULT) | |
191 prefs->ClearPref(name.c_str()); | |
192 else | |
193 prefs->Set(name.c_str(), *value); | |
194 prefs->ScheduleSavePersistentPrefs(); | |
195 } | |
196 | |
197 class UserCrosSettingsTrust : public SignedSettingsHelper::Callback { | |
198 public: | |
199 static UserCrosSettingsTrust* GetInstance() { | |
200 return Singleton<UserCrosSettingsTrust>::get(); | |
201 } | |
202 | |
203 // Working horse for UserCrosSettingsProvider::RequestTrusted* family. | |
204 bool RequestTrustedEntity(const std::string& name) { | |
205 OwnershipService::Status ownership_status = | |
206 ownership_service_->GetStatus(false); | |
207 if (ownership_status == OwnershipService::OWNERSHIP_NONE) | |
208 return true; | |
209 PrefService* prefs = g_browser_process->local_state(); | |
210 if (prefs->IsManagedPreference(name.c_str())) | |
211 return true; | |
212 if (ownership_status == OwnershipService::OWNERSHIP_TAKEN) { | |
213 DCHECK(g_browser_process); | |
214 PrefService* prefs = g_browser_process->local_state(); | |
215 DCHECK(prefs); | |
216 if (prefs->GetBoolean((name + kTrustedSuffix).c_str())) | |
217 return true; | |
218 } | |
219 return false; | |
220 } | |
221 | |
222 bool RequestTrustedEntity(const std::string& name, | |
223 const base::Closure& callback) { | |
224 if (RequestTrustedEntity(name)) { | |
225 return true; | |
226 } else { | |
227 if (!callback.is_null()) | |
228 callbacks_[name].push_back(callback); | |
229 return false; | |
230 } | |
231 } | |
232 | |
233 void Reload() { | |
234 for (size_t i = 0; i < arraysize(kBooleanSettings); ++i) | |
235 StartFetchingSetting(kBooleanSettings[i]); | |
236 for (size_t i = 0; i < arraysize(kStringSettings); ++i) | |
237 StartFetchingSetting(kStringSettings[i]); | |
238 for (size_t i = 0; i < arraysize(kListSettings); ++i) | |
239 StartFetchingSetting(kListSettings[i]); | |
240 } | |
241 | |
242 void Set(const std::string& path, const base::Value& in_value) { | |
243 PrefService* prefs = g_browser_process->local_state(); | |
244 DCHECK(!prefs->IsManagedPreference(path.c_str())); | |
245 | |
246 if (!UserManager::Get()->current_user_is_owner()) { | |
247 LOG(WARNING) << "Changing settings from non-owner, setting=" << path; | |
248 | |
249 // Revert UI change. | |
250 CrosSettings::Get()->FireObservers(path.c_str()); | |
251 return; | |
252 } | |
253 if (IsControlledSetting(path)) { | |
254 if (IsControlledBooleanSetting(path)) { | |
255 bool bool_value = false; | |
256 if (in_value.GetAsBoolean(&bool_value)) { | |
257 OnBooleanPropertyChange(path, bool_value); | |
258 } | |
259 } | |
260 SignedSettingsHelper::Get()->StartStorePropertyOp( | |
261 path, in_value, this); | |
262 UpdateCache(path, &in_value, USE_VALUE_SUPPLIED); | |
263 | |
264 LOG(ERROR) << "Set cros setting " << path; | |
265 } else { | |
266 LOG(WARNING) << "Try to set unhandled cros setting " << path; | |
267 } | |
268 } | |
269 | |
270 private: | |
271 // upper bound for number of retries to fetch a signed setting. | |
272 static const int kNumRetriesLimit = 9; | |
273 | |
274 UserCrosSettingsTrust() | |
275 : ownership_service_(OwnershipService::GetSharedInstance()), | |
276 retries_left_(kNumRetriesLimit) { | |
277 migration_helper_.set_callback(this); | |
278 // Start prefetching Boolean and String preferences. | |
279 Reload(); | |
280 } | |
281 | |
282 virtual ~UserCrosSettingsTrust() { | |
283 if (BrowserThread::CurrentlyOn(BrowserThread::UI)) { | |
284 // Cancels all pending callbacks from us. | |
285 SignedSettingsHelper::Get()->CancelCallback(this); | |
286 } | |
287 } | |
288 | |
289 // Called right before boolean property is changed. | |
290 void OnBooleanPropertyChange(const std::string& path, bool new_value) { | |
291 if (path == kSignedDataRoamingEnabled) { | |
292 NetworkLibrary* cros = CrosLibrary::Get()->GetNetworkLibrary(); | |
293 if (cros->IsCellularAlwaysInRoaming()) { | |
294 // If operator requires roaming always enabled, ignore supplied value | |
295 // and set data roaming allowed in true always. | |
296 new_value = true; | |
297 } | |
298 cros->SetCellularDataRoamingAllowed(new_value); | |
299 } else if (path == kStatsReportingPref) { | |
300 // TODO(pastarmovj): Remove this once we don't need to regenerate the | |
301 // consent file for the GUID anymore. | |
302 OptionsUtil::ResolveMetricsReportingEnabled(new_value); | |
303 } | |
304 } | |
305 | |
306 // Called right after signed value was checked. | |
307 void OnPropertyRetrieve(const std::string& path, | |
308 const base::Value* value, | |
309 UseValue use_value) { | |
310 if (path == kSignedDataRoamingEnabled) { | |
311 NetworkLibrary* cros = CrosLibrary::Get()->GetNetworkLibrary(); | |
312 const NetworkDevice* cellular = cros->FindCellularDevice(); | |
313 if (cellular) { | |
314 bool device_value = cellular->data_roaming_allowed(); | |
315 if (!device_value && cros->IsCellularAlwaysInRoaming()) { | |
316 // If operator requires roaming always enabled, ignore supplied value | |
317 // and set data roaming allowed in true always. | |
318 cros->SetCellularDataRoamingAllowed(true); | |
319 } else { | |
320 bool new_value = false; | |
321 if (use_value == USE_VALUE_SUPPLIED) | |
322 value->GetAsBoolean(&new_value); | |
323 if (device_value != new_value) | |
324 cros->SetCellularDataRoamingAllowed(new_value); | |
325 } | |
326 } | |
327 } else if (path == kStatsReportingPref) { | |
328 bool stats_consent = false; | |
329 if (use_value == USE_VALUE_SUPPLIED) | |
330 value->GetAsBoolean(&stats_consent); | |
331 // TODO(pastarmovj): Remove this once migration is not needed anymore. | |
332 // If the value is not set we should try to migrate legacy consent file. | |
333 if (use_value == USE_VALUE_DEFAULT) { | |
334 // Loading consent file state causes us to do blocking IO on UI thread. | |
335 // Temporarily allow it until we fix http://crbug.com/62626 | |
336 base::ThreadRestrictions::ScopedAllowIO allow_io; | |
337 stats_consent = GoogleUpdateSettings::GetCollectStatsConsent(); | |
338 // Make sure the values will get eventually written to the policy file. | |
339 migration_helper_.AddMigrationValue( | |
340 path, base::Value::CreateBooleanValue(stats_consent)); | |
341 migration_helper_.MigrateValues(); | |
342 base::FundamentalValue base_value(stats_consent); | |
343 UpdateCache(path, &base_value, USE_VALUE_SUPPLIED); | |
344 LOG(WARNING) << "No metrics policy set will revert to checking " | |
345 << "consent file which is " | |
346 << (stats_consent ? "on." : "off."); | |
347 } | |
348 // TODO(pastarmovj): Remove this once we don't need to regenerate the | |
349 // consent file for the GUID anymore. | |
350 VLOG(1) << "Metrics policy is being set to : " << stats_consent | |
351 << "(reason : " << use_value << ")"; | |
352 OptionsUtil::ResolveMetricsReportingEnabled(stats_consent); | |
353 } | |
354 } | |
355 | |
356 void StartFetchingSetting(const std::string& name) { | |
357 DCHECK(g_browser_process); | |
358 PrefService* prefs = g_browser_process->local_state(); | |
359 if (!prefs) | |
360 return; | |
361 // Do not trust before fetching complete. | |
362 prefs->ClearPref((name + kTrustedSuffix).c_str()); | |
363 prefs->ScheduleSavePersistentPrefs(); | |
364 SignedSettingsHelper::Get()->StartRetrieveProperty(name, this); | |
365 } | |
366 | |
367 // Implementation of SignedSettingsHelper::Callback. | |
368 virtual void OnRetrievePropertyCompleted(SignedSettings::ReturnCode code, | |
369 const std::string& name, | |
370 const base::Value* value) { | |
371 if (!IsControlledSetting(name)) { | |
372 NOTREACHED(); | |
373 return; | |
374 } | |
375 | |
376 bool is_owned = ownership_service_->GetStatus(true) == | |
377 OwnershipService::OWNERSHIP_TAKEN; | |
378 PrefService* prefs = g_browser_process->local_state(); | |
379 switch (code) { | |
380 case SignedSettings::SUCCESS: | |
381 case SignedSettings::NOT_FOUND: | |
382 case SignedSettings::KEY_UNAVAILABLE: { | |
383 bool fallback_to_default = !is_owned | |
384 || (code == SignedSettings::NOT_FOUND); | |
385 DCHECK(fallback_to_default || code == SignedSettings::SUCCESS); | |
386 if (fallback_to_default) | |
387 VLOG(1) << "Going default for cros setting " << name; | |
388 else | |
389 VLOG(1) << "Retrieved cros setting " << name; | |
390 UpdateCache( | |
391 name, value, | |
392 fallback_to_default ? USE_VALUE_DEFAULT : USE_VALUE_SUPPLIED); | |
393 OnPropertyRetrieve( | |
394 name, value, | |
395 fallback_to_default ? USE_VALUE_DEFAULT : USE_VALUE_SUPPLIED); | |
396 break; | |
397 } | |
398 case SignedSettings::OPERATION_FAILED: | |
399 default: { | |
400 DCHECK(code == SignedSettings::OPERATION_FAILED); | |
401 DCHECK(is_owned); | |
402 LOG(ERROR) << "On owned device: failed to retrieve cros " | |
403 "setting, name=" << name; | |
404 if (retries_left_ > 0) { | |
405 retries_left_ -= 1; | |
406 StartFetchingSetting(name); | |
407 return; | |
408 } | |
409 LOG(ERROR) << "No retries left"; | |
410 if (IsControlledBooleanSetting(name)) { | |
411 // For boolean settings we can just set safe (false) values | |
412 // and continue as trusted. | |
413 scoped_ptr<base::Value> false_value( | |
414 base::Value::CreateBooleanValue(false)); | |
415 OnPropertyRetrieve(name, false_value.get(), USE_VALUE_SUPPLIED); | |
416 UpdateCache(name, false_value.get(), USE_VALUE_SUPPLIED); | |
417 } else { | |
418 prefs->ClearPref((name + kTrustedSuffix).c_str()); | |
419 return; | |
420 } | |
421 break; | |
422 } | |
423 } | |
424 prefs->SetBoolean((name + kTrustedSuffix).c_str(), true); | |
425 { | |
426 std::vector<base::Closure>& callbacks_vector = callbacks_[name]; | |
427 for (size_t i = 0; i < callbacks_vector.size(); ++i) | |
428 MessageLoop::current()->PostTask(FROM_HERE, callbacks_vector[i]); | |
429 callbacks_vector.clear(); | |
430 } | |
431 if (code == SignedSettings::SUCCESS) | |
432 CrosSettings::Get()->FireObservers(name.c_str()); | |
433 } | |
434 | |
435 // Implementation of SignedSettingsHelper::Callback. | |
436 virtual void OnStorePropertyCompleted(SignedSettings::ReturnCode code, | |
437 const std::string& name, | |
438 const base::Value& value) { | |
439 VLOG(1) << "Store cros setting " << name << ", code=" << code; | |
440 | |
441 // Reload the setting if store op fails. | |
442 if (code != SignedSettings::SUCCESS) | |
443 SignedSettingsHelper::Get()->StartRetrieveProperty(name, this); | |
444 } | |
445 | |
446 // Implementation of SignedSettingsHelper::Callback. | |
447 virtual void OnWhitelistCompleted(SignedSettings::ReturnCode code, | |
448 const std::string& email) { | |
449 VLOG(1) << "Add " << email << " to whitelist, code=" << code; | |
450 | |
451 // Reload the whitelist on settings op failure. | |
452 if (code != SignedSettings::SUCCESS) | |
453 CrosSettings::Get()->FireObservers(kAccountsPrefUsers); | |
454 } | |
455 | |
456 // Implementation of SignedSettingsHelper::Callback. | |
457 virtual void OnUnwhitelistCompleted(SignedSettings::ReturnCode code, | |
458 const std::string& email) { | |
459 VLOG(1) << "Remove " << email << " from whitelist, code=" << code; | |
460 | |
461 // Reload the whitelist on settings op failure. | |
462 if (code != SignedSettings::SUCCESS) | |
463 CrosSettings::Get()->FireObservers(kAccountsPrefUsers); | |
464 } | |
465 | |
466 // Pending callbacks that need to be invoked after settings verification. | |
467 base::hash_map<std::string, std::vector<base::Closure> > callbacks_; | |
468 | |
469 OwnershipService* ownership_service_; | |
470 MigrationHelper migration_helper_; | |
471 | |
472 // In order to guard against occasional failure to fetch a property | |
473 // we allow for some number of retries. | |
474 int retries_left_; | |
475 | |
476 friend class SignedSettingsHelper; | |
477 friend struct DefaultSingletonTraits<UserCrosSettingsTrust>; | |
478 | |
479 DISALLOW_COPY_AND_ASSIGN(UserCrosSettingsTrust); | |
480 }; | |
481 | |
482 } // namespace | |
483 | |
484 } // namespace chromeos | |
485 | |
486 namespace chromeos { | |
487 | |
488 UserCrosSettingsProvider::UserCrosSettingsProvider() { | |
489 // Trigger prefetching of settings. | |
490 UserCrosSettingsTrust::GetInstance(); | |
491 } | |
492 | |
493 // static | |
494 void UserCrosSettingsProvider::RegisterPrefs(PrefService* local_state) { | |
495 for (size_t i = 0; i < arraysize(kBooleanSettings); ++i) | |
496 RegisterSetting(local_state, kBooleanSettings[i]); | |
497 for (size_t i = 0; i < arraysize(kStringSettings); ++i) | |
498 RegisterSetting(local_state, kStringSettings[i]); | |
499 for (size_t i = 0; i < arraysize(kListSettings); ++i) | |
500 RegisterSetting(local_state, kListSettings[i]); | |
501 } | |
502 | |
503 void UserCrosSettingsProvider::Reload() { | |
504 UserCrosSettingsTrust::GetInstance()->Reload(); | |
505 } | |
506 | |
507 void UserCrosSettingsProvider::DoSet(const std::string& path, | |
508 const base::Value& in_value) { | |
509 UserCrosSettingsTrust::GetInstance()->Set(path, in_value); | |
510 } | |
511 | |
512 const base::Value* UserCrosSettingsProvider::Get( | |
513 const std::string& path) const { | |
514 if (HandlesSetting(path)) { | |
515 const PrefService* prefs = g_browser_process->local_state(); | |
516 const PrefService::Preference* pref = prefs->FindPreference(path.c_str()); | |
517 return pref->GetValue(); | |
518 } | |
519 return NULL; | |
520 } | |
521 | |
522 bool UserCrosSettingsProvider::GetTrusted(const std::string& path, | |
523 const base::Closure& callback) const { | |
524 return UserCrosSettingsTrust::GetInstance()->RequestTrustedEntity( | |
525 path, callback); | |
526 } | |
527 | |
528 bool UserCrosSettingsProvider::HandlesSetting(const std::string& path) const { | |
529 return ::StartsWithASCII(path, "cros.accounts.", true) || | |
530 ::StartsWithASCII(path, "cros.signed.", true) || | |
531 ::StartsWithASCII(path, "cros.metrics.", true) || | |
532 path == kDeviceOwner || | |
533 path == kReleaseChannel; | |
534 } | |
535 | |
536 } // namespace chromeos | |
OLD | NEW |