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