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 38 std::string GenerateKioskAppAccountId(const std::string& app_id) { 39 return app_id + '@' + kKioskAppAccountDomain; 40 } 41 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. 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; 67 KioskAppManager* KioskAppManager::Get() { 68 return instance.Pointer(); 69 } 70 71 // static 72 void KioskAppManager::Shutdown() { 73 if (instance == NULL) 74 return; 75 76 instance.Pointer()->CleanUp(); 77 } 78 79 // static 80 void KioskAppManager::RegisterPrefs(PrefRegistrySimple* registry) { 81 registry->RegisterDictionaryPref(kKioskDictionaryName); 82 } 83 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 92 KioskAppManager::App::App() : is_loading(false) {} 93 KioskAppManager::App::~App() {} 94 95 std::string KioskAppManager::GetAutoLaunchApp() const { 96 return auto_launch_app_id_; 97 } 98 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 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 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 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 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 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 196 void KioskAppManager::SetEnableAutoLaunch(bool value) { 197 SetAutoLoginState(value ? AUTOLOGIN_APPROVED : AUTOLOGIN_REJECTED); 198 } 199 200 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 212 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 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 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 271 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 278 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 287 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 296 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 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 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 325 void KioskAppManager::AddObserver(KioskAppManagerObserver* observer) { 326 observers_.AddObserver(observer); 327 } 328 329 void KioskAppManager::RemoveObserver(KioskAppManagerObserver* observer) { 330 observers_.RemoveObserver(observer); 331 } 332 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 345 KioskAppManager::~KioskAppManager() {} 346 347 void KioskAppManager::CleanUp() { 348 local_accounts_subscription_.reset(); 349 local_account_auto_login_id_subscription_.reset(); 350 apps_.clear(); 351 } 352 353 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 364 KioskAppData* KioskAppManager::GetAppDataMutable(const std::string& app_id) { 365 return const_cast<KioskAppData*>(GetAppData(app_id)); 366 } 367 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 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 427 void KioskAppManager::OnKioskAppDataChanged(const std::string& app_id) { 428 FOR_EACH_OBSERVER(KioskAppManagerObserver, 429 observers_, 430 OnKioskAppDataChanged(app_id)); 431 } 432 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 440 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 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