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_profile_loader.h" 6 7 #include "base/logging.h" 8 #include "base/memory/weak_ptr.h" 9 #include "base/message_loop/message_loop.h" 10 #include "base/strings/string_util.h" 11 #include "base/sys_info.h" 12 #include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h" 13 #include "chrome/browser/chromeos/login/demo_mode/demo_app_launcher.h" 14 #include "chrome/browser/chromeos/login/login_utils.h" 15 #include "chrome/browser/chromeos/login/ui/login_display_host_impl.h" 16 #include "chrome/browser/chromeos/settings/cros_settings.h" 17 #include "chrome/browser/lifetime/application_lifetime.h" 18 #include "chromeos/cryptohome/async_method_caller.h" 19 #include "chromeos/dbus/cryptohome_client.h" 20 #include "chromeos/dbus/dbus_thread_manager.h" 21 #include "chromeos/login/auth/auth_status_consumer.h" 22 #include "chromeos/login/auth/user_context.h" 23 #include "chromeos/login/user_names.h" 24 #include "content/public/browser/browser_thread.h" 25 #include "google_apis/gaia/gaia_auth_util.h" 26 27 using content::BrowserThread; 28 29 namespace chromeos { 30 31 namespace { 32 33 KioskAppLaunchError::Error LoginFailureToKioskAppLaunchError( 34 const AuthFailure& error) { 35 switch (error.reason()) { 36 case AuthFailure::COULD_NOT_MOUNT_TMPFS: 37 case AuthFailure::COULD_NOT_MOUNT_CRYPTOHOME: 38 return KioskAppLaunchError::UNABLE_TO_MOUNT; 39 case AuthFailure::DATA_REMOVAL_FAILED: 40 return KioskAppLaunchError::UNABLE_TO_REMOVE; 41 case AuthFailure::USERNAME_HASH_FAILED: 42 return KioskAppLaunchError::UNABLE_TO_RETRIEVE_HASH; 43 default: 44 NOTREACHED(); 45 return KioskAppLaunchError::UNABLE_TO_MOUNT; 46 } 47 } 48 49 } // namespace 50 51 //////////////////////////////////////////////////////////////////////////////// 52 // KioskProfileLoader::CryptohomedChecker ensures cryptohome daemon is up 53 // and running by issuing an IsMounted call. If the call does not go through 54 // and chromeos::DBUS_METHOD_CALL_SUCCESS is not returned, it will retry after 55 // some time out and at the maximum five times before it gives up. Upon 56 // success, it resumes the launch by logging in as a kiosk mode account. 57 58 class KioskProfileLoader::CryptohomedChecker 59 : public base::SupportsWeakPtr<CryptohomedChecker> { 60 public: 61 explicit CryptohomedChecker(KioskProfileLoader* loader) 62 : loader_(loader), 63 retry_count_(0) { 64 } 65 ~CryptohomedChecker() {} 66 67 void StartCheck() { 68 chromeos::DBusThreadManager::Get()->GetCryptohomeClient()->IsMounted( 69 base::Bind(&CryptohomedChecker::OnCryptohomeIsMounted, 70 AsWeakPtr())); 71 } 72 73 private: 74 void OnCryptohomeIsMounted(chromeos::DBusMethodCallStatus call_status, 75 bool is_mounted) { 76 if (call_status != chromeos::DBUS_METHOD_CALL_SUCCESS) { 77 const int kMaxRetryTimes = 5; 78 ++retry_count_; 79 if (retry_count_ > kMaxRetryTimes) { 80 LOG(ERROR) << "Could not talk to cryptohomed for launching kiosk app."; 81 ReportCheckResult(KioskAppLaunchError::CRYPTOHOMED_NOT_RUNNING); 82 return; 83 } 84 85 const int retry_delay_in_milliseconds = 500 * (1 << retry_count_); 86 base::MessageLoop::current()->PostDelayedTask( 87 FROM_HERE, 88 base::Bind(&CryptohomedChecker::StartCheck, AsWeakPtr()), 89 base::TimeDelta::FromMilliseconds(retry_delay_in_milliseconds)); 90 return; 91 } 92 93 if (is_mounted) 94 LOG(ERROR) << "Cryptohome is mounted before launching kiosk app."; 95 96 // Proceed only when cryptohome is not mounded or running on dev box. 97 if (!is_mounted || !base::SysInfo::IsRunningOnChromeOS()) 98 ReportCheckResult(KioskAppLaunchError::NONE); 99 else 100 ReportCheckResult(KioskAppLaunchError::ALREADY_MOUNTED); 101 } 102 103 void ReportCheckResult(KioskAppLaunchError::Error error) { 104 if (error == KioskAppLaunchError::NONE) 105 loader_->LoginAsKioskAccount(); 106 else 107 loader_->ReportLaunchResult(error); 108 } 109 110 KioskProfileLoader* loader_; 111 int retry_count_; 112 113 DISALLOW_COPY_AND_ASSIGN(CryptohomedChecker); 114 }; 115 116 117 //////////////////////////////////////////////////////////////////////////////// 118 // KioskProfileLoader 119 120 KioskProfileLoader::KioskProfileLoader(const std::string& app_user_id, 121 bool use_guest_mount, 122 Delegate* delegate) 123 : user_id_(app_user_id), 124 use_guest_mount_(use_guest_mount), 125 delegate_(delegate) {} 126 127 KioskProfileLoader::~KioskProfileLoader() {} 128 129 void KioskProfileLoader::Start() { 130 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 131 login_performer_.reset(); 132 cryptohomed_checker_.reset(new CryptohomedChecker(this)); 133 cryptohomed_checker_->StartCheck(); 134 } 135 136 void KioskProfileLoader::LoginAsKioskAccount() { 137 login_performer_.reset(new LoginPerformer(this)); 138 login_performer_->LoginAsKioskAccount(user_id_, use_guest_mount_); 139 } 140 141 void KioskProfileLoader::ReportLaunchResult(KioskAppLaunchError::Error error) { 142 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 143 144 if (error != KioskAppLaunchError::NONE) { 145 delegate_->OnProfileLoadFailed(error); 146 } 147 } 148 149 void KioskProfileLoader::OnAuthSuccess(const UserContext& user_context) { 150 // LoginPerformer will delete itself. 151 login_performer_->set_delegate(NULL); 152 ignore_result(login_performer_.release()); 153 154 // If we are launching a demo session, we need to start MountGuest with the 155 // guest username; this is because there are several places in the cros code 156 // which rely on the username sent to cryptohome to be $guest. Back in Chrome 157 // we switch this back to the demo user name to correctly identify this 158 // user as a demo user. 159 UserContext context = user_context; 160 if (context.GetUserID() == chromeos::login::kGuestUserName) 161 context.SetUserID(DemoAppLauncher::kDemoUserName); 162 LoginUtils::Get()->PrepareProfile(context, 163 false, // has_auth_cookies 164 false, // has_active_session 165 this); 166 } 167 168 void KioskProfileLoader::OnAuthFailure(const AuthFailure& error) { 169 ReportLaunchResult(LoginFailureToKioskAppLaunchError(error)); 170 } 171 172 void KioskProfileLoader::WhiteListCheckFailed(const std::string& email) { 173 NOTREACHED(); 174 } 175 176 void KioskProfileLoader::PolicyLoadFailed() { 177 ReportLaunchResult(KioskAppLaunchError::POLICY_LOAD_FAILED); 178 } 179 180 void KioskProfileLoader::OnOnlineChecked( 181 const std::string& email, bool success) { 182 NOTREACHED(); 183 } 184 185 void KioskProfileLoader::OnProfilePrepared(Profile* profile) { 186 // This object could be deleted any time after successfully reporting 187 // a profile load, so invalidate the LoginUtils delegate now. 188 LoginUtils::Get()->DelegateDeleted(this); 189 190 delegate_->OnProfileLoaded(profile); 191 ReportLaunchResult(KioskAppLaunchError::NONE); 192 } 193 194 } // namespace chromeos 195