1// Copyright (c) 2012 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/login/screen_locker.h" 6 7#include <string> 8#include <vector> 9 10#include "ash/ash_switches.h" 11#include "ash/desktop_background/desktop_background_controller.h" 12#include "ash/shell.h" 13#include "ash/wm/lock_state_controller.h" 14#include "base/bind.h" 15#include "base/command_line.h" 16#include "base/lazy_instance.h" 17#include "base/memory/weak_ptr.h" 18#include "base/message_loop/message_loop.h" 19#include "base/metrics/histogram.h" 20#include "base/strings/string_util.h" 21#include "base/timer/timer.h" 22#include "chrome/browser/chrome_notification_types.h" 23#include "chrome/browser/chromeos/login/authenticator.h" 24#include "chrome/browser/chromeos/login/login_performer.h" 25#include "chrome/browser/chromeos/login/login_utils.h" 26#include "chrome/browser/chromeos/login/user_adding_screen.h" 27#include "chrome/browser/chromeos/login/user_manager.h" 28#include "chrome/browser/chromeos/login/webui_screen_locker.h" 29#include "chrome/browser/lifetime/application_lifetime.h" 30#include "chrome/browser/profiles/profile.h" 31#include "chrome/browser/profiles/profile_manager.h" 32#include "chrome/browser/signin/signin_manager.h" 33#include "chrome/browser/signin/signin_manager_factory.h" 34#include "chrome/browser/sync/profile_sync_service.h" 35#include "chrome/browser/sync/profile_sync_service_factory.h" 36#include "chrome/browser/ui/browser.h" 37#include "chrome/browser/ui/browser_commands.h" 38#include "chrome/browser/ui/browser_finder.h" 39#include "chrome/browser/ui/browser_window.h" 40#include "chrome/common/chrome_switches.h" 41#include "chromeos/dbus/dbus_thread_manager.h" 42#include "chromeos/dbus/session_manager_client.h" 43#include "content/public/browser/browser_thread.h" 44#include "content/public/browser/notification_service.h" 45#include "content/public/browser/user_metrics.h" 46#include "grit/generated_resources.h" 47#include "ui/base/l10n/l10n_util.h" 48#include "url/gurl.h" 49 50using content::BrowserThread; 51using content::UserMetricsAction; 52 53namespace { 54 55// Timeout for unlock animation guard - some animations may be required to run 56// on successful authentication before unlocking, but we want to be sure that 57// unlock happens even if animations are broken. 58const int kUnlockGuardTimeoutMs = 400; 59 60// Observer to start ScreenLocker when the screen lock 61class ScreenLockObserver : public chromeos::SessionManagerClient::Observer, 62 public content::NotificationObserver, 63 public chromeos::UserAddingScreen::Observer { 64 public: 65 ScreenLockObserver() : session_started_(false) { 66 registrar_.Add(this, 67 chrome::NOTIFICATION_LOGIN_USER_CHANGED, 68 content::NotificationService::AllSources()); 69 registrar_.Add(this, 70 chrome::NOTIFICATION_SESSION_STARTED, 71 content::NotificationService::AllSources()); 72 } 73 74 // NotificationObserver overrides: 75 virtual void Observe(int type, 76 const content::NotificationSource& source, 77 const content::NotificationDetails& details) OVERRIDE { 78 switch (type) { 79 case chrome::NOTIFICATION_LOGIN_USER_CHANGED: { 80 // Register Screen Lock only after a user has logged in. 81 chromeos::SessionManagerClient* session_manager = 82 chromeos::DBusThreadManager::Get()->GetSessionManagerClient(); 83 if (!session_manager->HasObserver(this)) 84 session_manager->AddObserver(this); 85 break; 86 } 87 88 case chrome::NOTIFICATION_SESSION_STARTED: { 89 session_started_ = true; 90 break; 91 } 92 93 default: 94 NOTREACHED(); 95 } 96 } 97 98 virtual void LockScreen() OVERRIDE { 99 VLOG(1) << "Received LockScreen D-Bus signal from session manager"; 100 if (chromeos::UserAddingScreen::Get()->IsRunning()) { 101 VLOG(1) << "Waiting for user adding screen to stop"; 102 chromeos::UserAddingScreen::Get()->AddObserver(this); 103 chromeos::UserAddingScreen::Get()->Cancel(); 104 return; 105 } 106 if (session_started_ && 107 chromeos::UserManager::Get()->CanCurrentUserLock()) { 108 chromeos::ScreenLocker::Show(); 109 } else { 110 // If the current user's session cannot be locked or the user has not 111 // completed all sign-in steps yet, log out instead. The latter is done to 112 // avoid complications with displaying the lock screen over the login 113 // screen while remaining secure in the case the user walks away during 114 // the sign-in steps. See crbug.com/112225 and crbug.com/110933. 115 VLOG(1) << "Calling session manager's StopSession D-Bus method"; 116 chromeos::DBusThreadManager::Get()-> 117 GetSessionManagerClient()->StopSession(); 118 } 119 } 120 121 virtual void UnlockScreen() OVERRIDE { 122 VLOG(1) << "Received UnlockScreen D-Bus signal from session manager"; 123 chromeos::ScreenLocker::Hide(); 124 } 125 126 virtual void OnUserAddingFinished() OVERRIDE { 127 chromeos::UserAddingScreen::Get()->RemoveObserver(this); 128 LockScreen(); 129 } 130 131 private: 132 bool session_started_; 133 content::NotificationRegistrar registrar_; 134 std::string saved_previous_input_method_id_; 135 std::string saved_current_input_method_id_; 136 std::vector<std::string> saved_active_input_method_list_; 137 138 DISALLOW_COPY_AND_ASSIGN(ScreenLockObserver); 139}; 140 141static base::LazyInstance<ScreenLockObserver> g_screen_lock_observer = 142 LAZY_INSTANCE_INITIALIZER; 143 144} // namespace 145 146namespace chromeos { 147 148// static 149ScreenLocker* ScreenLocker::screen_locker_ = NULL; 150 151////////////////////////////////////////////////////////////////////////////// 152// ScreenLocker, public: 153 154ScreenLocker::ScreenLocker(const UserList& users) 155 : users_(users), 156 locked_(false), 157 start_time_(base::Time::Now()), 158 login_status_consumer_(NULL), 159 incorrect_passwords_count_(0), 160 weak_factory_(this) { 161 DCHECK(!screen_locker_); 162 screen_locker_ = this; 163} 164 165void ScreenLocker::Init() { 166 authenticator_ = LoginUtils::Get()->CreateAuthenticator(this); 167 delegate_.reset(new WebUIScreenLocker(this)); 168 delegate_->LockScreen(); 169} 170 171void ScreenLocker::OnLoginFailure(const LoginFailure& error) { 172 content::RecordAction(UserMetricsAction("ScreenLocker_OnLoginFailure")); 173 if (authentication_start_time_.is_null()) { 174 LOG(ERROR) << "Start time is not set at authentication failure"; 175 } else { 176 base::TimeDelta delta = base::Time::Now() - authentication_start_time_; 177 VLOG(1) << "Authentication failure: " << delta.InSecondsF() << " second(s)"; 178 UMA_HISTOGRAM_TIMES("ScreenLocker.AuthenticationFailureTime", delta); 179 } 180 181 EnableInput(); 182 // Don't enable signout button here as we're showing 183 // MessageBubble. 184 185 delegate_->ShowErrorMessage(incorrect_passwords_count_++ ? 186 IDS_LOGIN_ERROR_AUTHENTICATING_2ND_TIME : 187 IDS_LOGIN_ERROR_AUTHENTICATING, 188 HelpAppLauncher::HELP_CANT_ACCESS_ACCOUNT); 189 190 if (login_status_consumer_) 191 login_status_consumer_->OnLoginFailure(error); 192} 193 194void ScreenLocker::OnLoginSuccess( 195 const UserContext& user_context, 196 bool pending_requests, 197 bool using_oauth) { 198 incorrect_passwords_count_ = 0; 199 if (authentication_start_time_.is_null()) { 200 if (!user_context.username.empty()) 201 LOG(ERROR) << "Start time is not set at authentication success"; 202 } else { 203 base::TimeDelta delta = base::Time::Now() - authentication_start_time_; 204 VLOG(1) << "Authentication success: " << delta.InSecondsF() << " second(s)"; 205 UMA_HISTOGRAM_TIMES("ScreenLocker.AuthenticationSuccessTime", delta); 206 } 207 208 if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kMultiProfiles)) { 209 // TODO(dzhioev): It seems like this branch never executed and should be 210 // removed before multi-profile enabling. 211 Profile* profile = ProfileManager::GetDefaultProfile(); 212 if (profile && !user_context.password.empty()) { 213 // We have a non-empty password, so notify listeners (such as the sync 214 // engine). 215 SigninManagerBase* signin = SigninManagerFactory::GetForProfile(profile); 216 DCHECK(signin); 217 GoogleServiceSigninSuccessDetails details( 218 signin->GetAuthenticatedUsername(), 219 user_context.password); 220 content::NotificationService::current()->Notify( 221 chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL, 222 content::Source<Profile>(profile), 223 content::Details<const GoogleServiceSigninSuccessDetails>(&details)); 224 } 225 } 226 227 if (const User* user = UserManager::Get()->FindUser(user_context.username)) { 228 if (!user->is_active()) 229 UserManager::Get()->SwitchActiveUser(user_context.username); 230 } else { 231 NOTREACHED() << "Logged in user not found."; 232 } 233 234 authentication_capture_.reset(new AuthenticationParametersCapture()); 235 authentication_capture_->username = user_context.username; 236 authentication_capture_->pending_requests = pending_requests; 237 authentication_capture_->using_oauth = using_oauth; 238 239 CommandLine* command_line = CommandLine::ForCurrentProcess(); 240 if (command_line->HasSwitch(ash::switches::kAshDisableNewLockAnimations)) { 241 UnlockOnLoginSuccess(); 242 } else { 243 // Add guard for case when something get broken in call chain to unlock 244 // for sure. 245 base::MessageLoop::current()->PostDelayedTask( 246 FROM_HERE, 247 base::Bind(&ScreenLocker::UnlockOnLoginSuccess, 248 weak_factory_.GetWeakPtr()), 249 base::TimeDelta::FromMilliseconds(kUnlockGuardTimeoutMs)); 250 delegate_->AnimateAuthenticationSuccess(); 251 } 252} 253 254void ScreenLocker::UnlockOnLoginSuccess() { 255 DCHECK(base::MessageLoop::current()->type() == base::MessageLoop::TYPE_UI); 256 if (!authentication_capture_.get()) { 257 LOG(WARNING) << "Call to UnlockOnLoginSuccess without previous " << 258 "authentication success."; 259 return; 260 } 261 262 VLOG(1) << "Calling session manager's UnlockScreen D-Bus method"; 263 DBusThreadManager::Get()->GetSessionManagerClient()->RequestUnlockScreen(); 264 265 if (login_status_consumer_) { 266 login_status_consumer_->OnLoginSuccess( 267 UserContext(authentication_capture_->username, 268 std::string(), // password 269 std::string()), // auth_code 270 authentication_capture_->pending_requests, 271 authentication_capture_->using_oauth); 272 } 273 authentication_capture_.reset(); 274 weak_factory_.InvalidateWeakPtrs(); 275} 276 277void ScreenLocker::Authenticate(const UserContext& user_context) { 278 LOG_ASSERT(IsUserLoggedIn(user_context.username)) 279 << "Invalid user trying to unlock."; 280 authentication_start_time_ = base::Time::Now(); 281 delegate_->SetInputEnabled(false); 282 delegate_->OnAuthenticate(); 283 284 BrowserThread::PostTask( 285 BrowserThread::UI, FROM_HERE, 286 base::Bind(&Authenticator::AuthenticateToUnlock, 287 authenticator_.get(), 288 user_context)); 289} 290 291void ScreenLocker::AuthenticateByPassword(const std::string& password) { 292 LOG_ASSERT(users_.size() == 1); 293 Authenticate(UserContext(users_[0]->email(), password, "")); 294} 295 296void ScreenLocker::ClearErrors() { 297 delegate_->ClearErrors(); 298} 299 300void ScreenLocker::EnableInput() { 301 delegate_->SetInputEnabled(true); 302} 303 304void ScreenLocker::Signout() { 305 delegate_->ClearErrors(); 306 content::RecordAction(UserMetricsAction("ScreenLocker_Signout")); 307 // We expect that this call will not wait for any user input. 308 // If it changes at some point, we will need to force exit. 309 chrome::AttemptUserExit(); 310 311 // Don't hide yet the locker because the chrome screen may become visible 312 // briefly. 313} 314 315void ScreenLocker::ShowErrorMessage(int error_msg_id, 316 HelpAppLauncher::HelpTopic help_topic_id, 317 bool sign_out_only) { 318 delegate_->SetInputEnabled(!sign_out_only); 319 delegate_->ShowErrorMessage(error_msg_id, help_topic_id); 320} 321 322void ScreenLocker::SetLoginStatusConsumer( 323 chromeos::LoginStatusConsumer* consumer) { 324 login_status_consumer_ = consumer; 325} 326 327// static 328void ScreenLocker::Show() { 329 content::RecordAction(UserMetricsAction("ScreenLocker_Show")); 330 DCHECK(base::MessageLoop::current()->type() == base::MessageLoop::TYPE_UI); 331 332 // Check whether the currently logged in user is a guest account and if so, 333 // refuse to lock the screen (crosbug.com/23764). 334 // For a demo user, we should never show the lock screen (crosbug.com/27647). 335 if (UserManager::Get()->IsLoggedInAsGuest() || 336 UserManager::Get()->IsLoggedInAsDemoUser()) { 337 VLOG(1) << "Refusing to lock screen for guest/demo account"; 338 return; 339 } 340 341 // Exit fullscreen. 342 Browser* browser = chrome::FindLastActiveWithHostDesktopType( 343 chrome::HOST_DESKTOP_TYPE_ASH); 344 // browser can be NULL if we receive a lock request before the first browser 345 // window is shown. 346 if (browser && browser->window()->IsFullscreen()) { 347 chrome::ToggleFullscreenMode(browser); 348 } 349 350 if (!screen_locker_) { 351 ScreenLocker* locker = 352 new ScreenLocker(UserManager::Get()->GetLRULoggedInUsers()); 353 VLOG(1) << "Created ScreenLocker " << locker; 354 locker->Init(); 355 } else { 356 VLOG(1) << "ScreenLocker " << screen_locker_ << " already exists; " 357 << " calling session manager's HandleLockScreenShown D-Bus method"; 358 DBusThreadManager::Get()->GetSessionManagerClient()-> 359 NotifyLockScreenShown(); 360 } 361} 362 363// static 364void ScreenLocker::Hide() { 365 DCHECK(base::MessageLoop::current()->type() == base::MessageLoop::TYPE_UI); 366 // For a guest/demo user, screen_locker_ would have never been initialized. 367 if (UserManager::Get()->IsLoggedInAsGuest() || 368 UserManager::Get()->IsLoggedInAsDemoUser()) { 369 VLOG(1) << "Refusing to hide lock screen for guest/demo account"; 370 return; 371 } 372 373 DCHECK(screen_locker_); 374 base::Callback<void(void)> callback = 375 base::Bind(&ScreenLocker::ScheduleDeletion); 376 ash::Shell::GetInstance()->lock_state_controller()-> 377 OnLockScreenHide(callback); 378} 379 380void ScreenLocker::ScheduleDeletion() { 381 // Avoid possible multiple calls. 382 if (screen_locker_ == NULL) 383 return; 384 VLOG(1) << "Posting task to delete ScreenLocker " << screen_locker_; 385 ScreenLocker* screen_locker = screen_locker_; 386 screen_locker_ = NULL; 387 base::MessageLoopForUI::current()->DeleteSoon(FROM_HERE, screen_locker); 388} 389 390// static 391void ScreenLocker::InitClass() { 392 g_screen_lock_observer.Get(); 393} 394 395//////////////////////////////////////////////////////////////////////////////// 396// ScreenLocker, private: 397 398ScreenLocker::~ScreenLocker() { 399 VLOG(1) << "Destroying ScreenLocker " << this; 400 DCHECK(base::MessageLoop::current()->type() == base::MessageLoop::TYPE_UI); 401 402 if (authenticator_.get()) 403 authenticator_->SetConsumer(NULL); 404 ClearErrors(); 405 406 VLOG(1) << "Moving desktop background to unlocked container"; 407 ash::Shell::GetInstance()-> 408 desktop_background_controller()->MoveDesktopToUnlockedContainer(); 409 410 screen_locker_ = NULL; 411 bool state = false; 412 VLOG(1) << "Emitting SCREEN_LOCK_STATE_CHANGED with state=" << state; 413 content::NotificationService::current()->Notify( 414 chrome::NOTIFICATION_SCREEN_LOCK_STATE_CHANGED, 415 content::Source<ScreenLocker>(this), 416 content::Details<bool>(&state)); 417 VLOG(1) << "Calling session manager's HandleLockScreenDismissed D-Bus method"; 418 DBusThreadManager::Get()->GetSessionManagerClient()-> 419 NotifyLockScreenDismissed(); 420} 421 422void ScreenLocker::SetAuthenticator(Authenticator* authenticator) { 423 authenticator_ = authenticator; 424} 425 426void ScreenLocker::ScreenLockReady() { 427 locked_ = true; 428 base::TimeDelta delta = base::Time::Now() - start_time_; 429 VLOG(1) << "ScreenLocker " << this << " is ready after " 430 << delta.InSecondsF() << " second(s)"; 431 UMA_HISTOGRAM_TIMES("ScreenLocker.ScreenLockTime", delta); 432 433 VLOG(1) << "Moving desktop background to locked container"; 434 ash::Shell::GetInstance()-> 435 desktop_background_controller()->MoveDesktopToLockedContainer(); 436 437 bool state = true; 438 VLOG(1) << "Emitting SCREEN_LOCK_STATE_CHANGED with state=" << state; 439 content::NotificationService::current()->Notify( 440 chrome::NOTIFICATION_SCREEN_LOCK_STATE_CHANGED, 441 content::Source<ScreenLocker>(this), 442 content::Details<bool>(&state)); 443 VLOG(1) << "Calling session manager's HandleLockScreenShown D-Bus method"; 444 DBusThreadManager::Get()->GetSessionManagerClient()->NotifyLockScreenShown(); 445} 446 447content::WebUI* ScreenLocker::GetAssociatedWebUI() { 448 return delegate_->GetAssociatedWebUI(); 449} 450 451bool ScreenLocker::IsUserLoggedIn(const std::string& username) { 452 for (UserList::const_iterator it = users_.begin(); it != users_.end(); ++it) { 453 if ((*it)->email() == username) 454 return true; 455 } 456 return false; 457} 458 459} // namespace chromeos 460 461