1 // Copyright 2013 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/app_mode/kiosk_app_manager.h"
6
7 #include <map>
8 #include <set>
9
10 #include "base/bind.h"
11 #include "base/logging.h"
12 #include "base/path_service.h"
13 #include "base/prefs/pref_registry_simple.h"
14 #include "base/prefs/pref_service.h"
15 #include "base/prefs/scoped_user_pref_update.h"
16 #include "base/stl_util.h"
17 #include "base/sys_info.h"
18 #include "chrome/browser/browser_process.h"
19 #include "chrome/browser/chromeos/app_mode/kiosk_app_data.h"
20 #include "chrome/browser/chromeos/app_mode/kiosk_app_manager_observer.h"
21 #include "chrome/browser/chromeos/login/user_manager.h"
22 #include "chrome/browser/chromeos/policy/device_local_account.h"
23 #include "chrome/browser/chromeos/settings/cros_settings.h"
24 #include "chrome/browser/chromeos/settings/owner_key_util.h"
25 #include "chrome/browser/policy/browser_policy_connector.h"
26 #include "chrome/common/chrome_paths.h"
27 #include "chromeos/cryptohome/async_method_caller.h"
28 #include "chromeos/settings/cros_settings_names.h"
29 #include "content/public/browser/browser_thread.h"
30
31 namespace chromeos {
32
33 namespace {
34
35 // Domain that is used for kiosk-app account IDs.
36 const char kKioskAppAccountDomain[] = "kiosk-apps";
37
GenerateKioskAppAccountId(const std::string & app_id)38 std::string GenerateKioskAppAccountId(const std::string& app_id) {
39 return app_id + '@' + kKioskAppAccountDomain;
40 }
41
OnRemoveAppCryptohomeComplete(const std::string & app,bool success,cryptohome::MountError return_code)42 void OnRemoveAppCryptohomeComplete(const std::string& app,
43 bool success,
44 cryptohome::MountError return_code) {
45 if (!success) {
46 LOG(ERROR) << "Remove cryptohome for " << app
47 << " failed, return code: " << return_code;
48 }
49 }
50
51 // Check for presence of machine owner public key file.
CheckOwnerFilePresence(bool * present)52 void CheckOwnerFilePresence(bool *present) {
53 scoped_refptr<OwnerKeyUtil> util = OwnerKeyUtil::Create();
54 *present = util->IsPublicKeyPresent();
55 }
56
57 } // namespace
58
59 // static
60 const char KioskAppManager::kKioskDictionaryName[] = "kiosk";
61 const char KioskAppManager::kKeyApps[] = "apps";
62 const char KioskAppManager::kKeyAutoLoginState[] = "auto_login_state";
63 const char KioskAppManager::kIconCacheDir[] = "kiosk";
64
65 // static
66 static base::LazyInstance<KioskAppManager> instance = LAZY_INSTANCE_INITIALIZER;
Get()67 KioskAppManager* KioskAppManager::Get() {
68 return instance.Pointer();
69 }
70
71 // static
Shutdown()72 void KioskAppManager::Shutdown() {
73 if (instance == NULL)
74 return;
75
76 instance.Pointer()->CleanUp();
77 }
78
79 // static
RegisterPrefs(PrefRegistrySimple * registry)80 void KioskAppManager::RegisterPrefs(PrefRegistrySimple* registry) {
81 registry->RegisterDictionaryPref(kKioskDictionaryName);
82 }
83
App(const KioskAppData & data)84 KioskAppManager::App::App(const KioskAppData& data)
85 : app_id(data.app_id()),
86 user_id(data.user_id()),
87 name(data.name()),
88 icon(data.icon()),
89 is_loading(data.IsLoading()) {
90 }
91
App()92 KioskAppManager::App::App() : is_loading(false) {}
~App()93 KioskAppManager::App::~App() {}
94
GetAutoLaunchApp() const95 std::string KioskAppManager::GetAutoLaunchApp() const {
96 return auto_launch_app_id_;
97 }
98
SetAutoLaunchApp(const std::string & app_id)99 void KioskAppManager::SetAutoLaunchApp(const std::string& app_id) {
100 SetAutoLoginState(AUTOLOGIN_REQUESTED);
101 // Clean first, so the proper change callbacks are triggered even
102 // if we are only changing AutoLoginState here.
103 if (!auto_launch_app_id_.empty()) {
104 CrosSettings::Get()->SetString(kAccountsPrefDeviceLocalAccountAutoLoginId,
105 std::string());
106 }
107
108 CrosSettings::Get()->SetString(
109 kAccountsPrefDeviceLocalAccountAutoLoginId,
110 app_id.empty() ? std::string() : GenerateKioskAppAccountId(app_id));
111 CrosSettings::Get()->SetInteger(
112 kAccountsPrefDeviceLocalAccountAutoLoginDelay, 0);
113 }
114
EnableConsumerModeKiosk(const KioskAppManager::EnableKioskModeCallback & callback)115 void KioskAppManager::EnableConsumerModeKiosk(
116 const KioskAppManager::EnableKioskModeCallback& callback) {
117 g_browser_process->browser_policy_connector()->GetInstallAttributes()->
118 LockDevice(std::string(), // user
119 policy::DEVICE_MODE_CONSUMER_KIOSK,
120 std::string(), // device_id
121 base::Bind(&KioskAppManager::OnLockDevice,
122 base::Unretained(this),
123 callback));
124 }
125
GetConsumerKioskModeStatus(const KioskAppManager::GetConsumerKioskModeStatusCallback & callback)126 void KioskAppManager::GetConsumerKioskModeStatus(
127 const KioskAppManager::GetConsumerKioskModeStatusCallback& callback) {
128 g_browser_process->browser_policy_connector()->GetInstallAttributes()->
129 ReadImmutableAttributes(
130 base::Bind(&KioskAppManager::OnReadImmutableAttributes,
131 base::Unretained(this),
132 callback));
133 }
134
OnLockDevice(const KioskAppManager::EnableKioskModeCallback & callback,policy::EnterpriseInstallAttributes::LockResult result)135 void KioskAppManager::OnLockDevice(
136 const KioskAppManager::EnableKioskModeCallback& callback,
137 policy::EnterpriseInstallAttributes::LockResult result) {
138 if (callback.is_null())
139 return;
140
141 callback.Run(result == policy::EnterpriseInstallAttributes::LOCK_SUCCESS);
142 }
143
OnOwnerFileChecked(const KioskAppManager::GetConsumerKioskModeStatusCallback & callback,bool * owner_present)144 void KioskAppManager::OnOwnerFileChecked(
145 const KioskAppManager::GetConsumerKioskModeStatusCallback& callback,
146 bool* owner_present) {
147 ownership_established_ = *owner_present;
148
149 if (callback.is_null())
150 return;
151
152 // If we have owner already established on the machine, don't let
153 // consumer kiosk to be enabled.
154 if (ownership_established_)
155 callback.Run(CONSUMER_KIOSK_MODE_DISABLED);
156 else
157 callback.Run(CONSUMER_KIOSK_MODE_CONFIGURABLE);
158 }
159
OnReadImmutableAttributes(const KioskAppManager::GetConsumerKioskModeStatusCallback & callback)160 void KioskAppManager::OnReadImmutableAttributes(
161 const KioskAppManager::GetConsumerKioskModeStatusCallback& callback) {
162 if (callback.is_null())
163 return;
164
165 ConsumerKioskModeStatus status = CONSUMER_KIOSK_MODE_DISABLED;
166 policy::EnterpriseInstallAttributes* attributes =
167 g_browser_process->browser_policy_connector()->GetInstallAttributes();
168 switch (attributes->GetMode()) {
169 case policy::DEVICE_MODE_NOT_SET: {
170 if (!base::SysInfo::IsRunningOnChromeOS()) {
171 status = CONSUMER_KIOSK_MODE_CONFIGURABLE;
172 } else if (!ownership_established_) {
173 bool* owner_present = new bool(false);
174 content::BrowserThread::PostBlockingPoolTaskAndReply(
175 FROM_HERE,
176 base::Bind(&CheckOwnerFilePresence,
177 owner_present),
178 base::Bind(&KioskAppManager::OnOwnerFileChecked,
179 base::Unretained(this),
180 callback,
181 base::Owned(owner_present)));
182 return;
183 }
184 break;
185 }
186 case policy::DEVICE_MODE_CONSUMER_KIOSK:
187 status = CONSUMER_KIOSK_MODE_ENABLED;
188 break;
189 default:
190 break;
191 }
192
193 callback.Run(status);
194 }
195
SetEnableAutoLaunch(bool value)196 void KioskAppManager::SetEnableAutoLaunch(bool value) {
197 SetAutoLoginState(value ? AUTOLOGIN_APPROVED : AUTOLOGIN_REJECTED);
198 }
199
IsAutoLaunchRequested() const200 bool KioskAppManager::IsAutoLaunchRequested() const {
201 if (GetAutoLaunchApp().empty())
202 return false;
203
204 // Apps that were installed by the policy don't require machine owner
205 // consent through UI.
206 if (g_browser_process->browser_policy_connector()->IsEnterpriseManaged())
207 return false;
208
209 return GetAutoLoginState() == AUTOLOGIN_REQUESTED;
210 }
211
IsAutoLaunchEnabled() const212 bool KioskAppManager::IsAutoLaunchEnabled() const {
213 if (GetAutoLaunchApp().empty())
214 return false;
215
216 // Apps that were installed by the policy don't require machine owner
217 // consent through UI.
218 if (g_browser_process->browser_policy_connector()->IsEnterpriseManaged())
219 return true;
220
221 return GetAutoLoginState() == AUTOLOGIN_APPROVED;
222 }
223
AddApp(const std::string & app_id)224 void KioskAppManager::AddApp(const std::string& app_id) {
225 std::vector<policy::DeviceLocalAccount> device_local_accounts =
226 policy::GetDeviceLocalAccounts(CrosSettings::Get());
227
228 // Don't insert the app if it's already in the list.
229 for (std::vector<policy::DeviceLocalAccount>::const_iterator
230 it = device_local_accounts.begin();
231 it != device_local_accounts.end(); ++it) {
232 if (it->type == policy::DeviceLocalAccount::TYPE_KIOSK_APP &&
233 it->kiosk_app_id == app_id) {
234 return;
235 }
236 }
237
238 // Add the new account.
239 device_local_accounts.push_back(policy::DeviceLocalAccount(
240 policy::DeviceLocalAccount::TYPE_KIOSK_APP,
241 GenerateKioskAppAccountId(app_id),
242 app_id));
243
244 policy::SetDeviceLocalAccounts(CrosSettings::Get(), device_local_accounts);
245 }
246
RemoveApp(const std::string & app_id)247 void KioskAppManager::RemoveApp(const std::string& app_id) {
248 // Resets auto launch app if it is the removed app.
249 if (auto_launch_app_id_ == app_id)
250 SetAutoLaunchApp(std::string());
251
252 std::vector<policy::DeviceLocalAccount> device_local_accounts =
253 policy::GetDeviceLocalAccounts(CrosSettings::Get());
254 if (device_local_accounts.empty())
255 return;
256
257 // Remove entries that match |app_id|.
258 for (std::vector<policy::DeviceLocalAccount>::iterator
259 it = device_local_accounts.begin();
260 it != device_local_accounts.end(); ++it) {
261 if (it->type == policy::DeviceLocalAccount::TYPE_KIOSK_APP &&
262 it->kiosk_app_id == app_id) {
263 device_local_accounts.erase(it);
264 break;
265 }
266 }
267
268 policy::SetDeviceLocalAccounts(CrosSettings::Get(), device_local_accounts);
269 }
270
GetApps(Apps * apps) const271 void KioskAppManager::GetApps(Apps* apps) const {
272 apps->clear();
273 apps->reserve(apps_.size());
274 for (size_t i = 0; i < apps_.size(); ++i)
275 apps->push_back(App(*apps_[i]));
276 }
277
GetApp(const std::string & app_id,App * app) const278 bool KioskAppManager::GetApp(const std::string& app_id, App* app) const {
279 const KioskAppData* data = GetAppData(app_id);
280 if (!data)
281 return false;
282
283 *app = App(*data);
284 return true;
285 }
286
GetAppRawIcon(const std::string & app_id) const287 const base::RefCountedString* KioskAppManager::GetAppRawIcon(
288 const std::string& app_id) const {
289 const KioskAppData* data = GetAppData(app_id);
290 if (!data)
291 return NULL;
292
293 return data->raw_icon();
294 }
295
GetDisableBailoutShortcut() const296 bool KioskAppManager::GetDisableBailoutShortcut() const {
297 bool enable;
298 if (CrosSettings::Get()->GetBoolean(
299 kAccountsPrefDeviceLocalAccountAutoLoginBailoutEnabled, &enable)) {
300 return !enable;
301 }
302
303 return false;
304 }
305
ClearAppData(const std::string & app_id)306 void KioskAppManager::ClearAppData(const std::string& app_id) {
307 KioskAppData* app_data = GetAppDataMutable(app_id);
308 if (!app_data)
309 return;
310
311 app_data->ClearCache();
312 }
313
UpdateAppDataFromProfile(const std::string & app_id,Profile * profile,const extensions::Extension * app)314 void KioskAppManager::UpdateAppDataFromProfile(
315 const std::string& app_id,
316 Profile* profile,
317 const extensions::Extension* app) {
318 KioskAppData* app_data = GetAppDataMutable(app_id);
319 if (!app_data)
320 return;
321
322 app_data->LoadFromInstalledApp(profile, app);
323 }
324
AddObserver(KioskAppManagerObserver * observer)325 void KioskAppManager::AddObserver(KioskAppManagerObserver* observer) {
326 observers_.AddObserver(observer);
327 }
328
RemoveObserver(KioskAppManagerObserver * observer)329 void KioskAppManager::RemoveObserver(KioskAppManagerObserver* observer) {
330 observers_.RemoveObserver(observer);
331 }
332
KioskAppManager()333 KioskAppManager::KioskAppManager() : ownership_established_(false) {
334 UpdateAppData();
335 local_accounts_subscription_ =
336 CrosSettings::Get()->AddSettingsObserver(
337 kAccountsPrefDeviceLocalAccounts,
338 base::Bind(&KioskAppManager::UpdateAppData, base::Unretained(this)));
339 local_account_auto_login_id_subscription_ =
340 CrosSettings::Get()->AddSettingsObserver(
341 kAccountsPrefDeviceLocalAccountAutoLoginId,
342 base::Bind(&KioskAppManager::UpdateAppData, base::Unretained(this)));
343 }
344
~KioskAppManager()345 KioskAppManager::~KioskAppManager() {}
346
CleanUp()347 void KioskAppManager::CleanUp() {
348 local_accounts_subscription_.reset();
349 local_account_auto_login_id_subscription_.reset();
350 apps_.clear();
351 }
352
GetAppData(const std::string & app_id) const353 const KioskAppData* KioskAppManager::GetAppData(
354 const std::string& app_id) const {
355 for (size_t i = 0; i < apps_.size(); ++i) {
356 const KioskAppData* data = apps_[i];
357 if (data->app_id() == app_id)
358 return data;
359 }
360
361 return NULL;
362 }
363
GetAppDataMutable(const std::string & app_id)364 KioskAppData* KioskAppManager::GetAppDataMutable(const std::string& app_id) {
365 return const_cast<KioskAppData*>(GetAppData(app_id));
366 }
367
UpdateAppData()368 void KioskAppManager::UpdateAppData() {
369 // Gets app id to data mapping for existing apps.
370 std::map<std::string, KioskAppData*> old_apps;
371 for (size_t i = 0; i < apps_.size(); ++i)
372 old_apps[apps_[i]->app_id()] = apps_[i];
373 apps_.weak_clear(); // |old_apps| takes ownership
374
375 auto_launch_app_id_.clear();
376 std::string auto_login_account_id;
377 CrosSettings::Get()->GetString(kAccountsPrefDeviceLocalAccountAutoLoginId,
378 &auto_login_account_id);
379
380 // Re-populates |apps_| and reuses existing KioskAppData when possible.
381 const std::vector<policy::DeviceLocalAccount> device_local_accounts =
382 policy::GetDeviceLocalAccounts(CrosSettings::Get());
383 for (std::vector<policy::DeviceLocalAccount>::const_iterator
384 it = device_local_accounts.begin();
385 it != device_local_accounts.end(); ++it) {
386 if (it->type != policy::DeviceLocalAccount::TYPE_KIOSK_APP)
387 continue;
388
389 if (it->account_id == auto_login_account_id)
390 auto_launch_app_id_ = it->kiosk_app_id;
391
392 // TODO(mnissler): Support non-CWS update URLs.
393
394 std::map<std::string, KioskAppData*>::iterator old_it =
395 old_apps.find(it->kiosk_app_id);
396 if (old_it != old_apps.end()) {
397 apps_.push_back(old_it->second);
398 old_apps.erase(old_it);
399 } else {
400 KioskAppData* new_app =
401 new KioskAppData(this, it->kiosk_app_id, it->user_id);
402 apps_.push_back(new_app); // Takes ownership of |new_app|.
403 new_app->Load();
404 }
405 }
406
407 // Clears cache and deletes the remaining old data.
408 for (std::map<std::string, KioskAppData*>::iterator it = old_apps.begin();
409 it != old_apps.end(); ++it) {
410 it->second->ClearCache();
411 cryptohome::AsyncMethodCaller::GetInstance()->AsyncRemove(
412 it->second->user_id(),
413 base::Bind(&OnRemoveAppCryptohomeComplete, it->first));
414 }
415 STLDeleteValues(&old_apps);
416
417 FOR_EACH_OBSERVER(KioskAppManagerObserver, observers_,
418 OnKioskAppsSettingsChanged());
419 }
420
GetKioskAppIconCacheDir(base::FilePath * cache_dir)421 void KioskAppManager::GetKioskAppIconCacheDir(base::FilePath* cache_dir) {
422 base::FilePath user_data_dir;
423 CHECK(PathService::Get(chrome::DIR_USER_DATA, &user_data_dir));
424 *cache_dir = user_data_dir.AppendASCII(kIconCacheDir);
425 }
426
OnKioskAppDataChanged(const std::string & app_id)427 void KioskAppManager::OnKioskAppDataChanged(const std::string& app_id) {
428 FOR_EACH_OBSERVER(KioskAppManagerObserver,
429 observers_,
430 OnKioskAppDataChanged(app_id));
431 }
432
OnKioskAppDataLoadFailure(const std::string & app_id)433 void KioskAppManager::OnKioskAppDataLoadFailure(const std::string& app_id) {
434 FOR_EACH_OBSERVER(KioskAppManagerObserver,
435 observers_,
436 OnKioskAppDataLoadFailure(app_id));
437 RemoveApp(app_id);
438 }
439
GetAutoLoginState() const440 KioskAppManager::AutoLoginState KioskAppManager::GetAutoLoginState() const {
441 PrefService* prefs = g_browser_process->local_state();
442 const base::DictionaryValue* dict =
443 prefs->GetDictionary(KioskAppManager::kKioskDictionaryName);
444 int value;
445 if (!dict->GetInteger(kKeyAutoLoginState, &value))
446 return AUTOLOGIN_NONE;
447
448 return static_cast<AutoLoginState>(value);
449 }
450
SetAutoLoginState(AutoLoginState state)451 void KioskAppManager::SetAutoLoginState(AutoLoginState state) {
452 PrefService* prefs = g_browser_process->local_state();
453 DictionaryPrefUpdate dict_update(prefs,
454 KioskAppManager::kKioskDictionaryName);
455 dict_update->SetInteger(kKeyAutoLoginState, state);
456 prefs->CommitPendingWrite();
457 }
458
459 } // namespace chromeos
460