1 // Copyright 2014 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/lock/screen_locker.h" 6 7 #include <string> 8 #include <vector> 9 10 #include "ash/ash_switches.h" 11 #include "ash/audio/sounds.h" 12 #include "ash/desktop_background/desktop_background_controller.h" 13 #include "ash/shell.h" 14 #include "ash/wm/lock_state_controller.h" 15 #include "ash/wm/window_state.h" 16 #include "ash/wm/window_util.h" 17 #include "ash/wm/wm_event.h" 18 #include "base/bind.h" 19 #include "base/command_line.h" 20 #include "base/lazy_instance.h" 21 #include "base/memory/weak_ptr.h" 22 #include "base/message_loop/message_loop.h" 23 #include "base/metrics/histogram.h" 24 #include "base/strings/string_number_conversions.h" 25 #include "base/strings/string_util.h" 26 #include "base/timer/timer.h" 27 #include "chrome/browser/chrome_notification_types.h" 28 #include "chrome/browser/chromeos/login/auth/login_performer.h" 29 #include "chrome/browser/chromeos/login/lock/webui_screen_locker.h" 30 #include "chrome/browser/chromeos/login/login_utils.h" 31 #include "chrome/browser/chromeos/login/session/user_session_manager.h" 32 #include "chrome/browser/chromeos/login/supervised/supervised_user_authentication.h" 33 #include "chrome/browser/chromeos/login/ui/user_adding_screen.h" 34 #include "chrome/browser/chromeos/login/users/chrome_user_manager.h" 35 #include "chrome/browser/chromeos/login/users/supervised_user_manager.h" 36 #include "chrome/browser/lifetime/application_lifetime.h" 37 #include "chrome/browser/signin/easy_unlock_service.h" 38 #include "chrome/browser/signin/signin_manager_factory.h" 39 #include "chrome/browser/ui/webui/chromeos/login/screenlock_icon_provider.h" 40 #include "chrome/browser/ui/webui/chromeos/login/screenlock_icon_source.h" 41 #include "chrome/common/chrome_switches.h" 42 #include "chrome/grit/browser_resources.h" 43 #include "chrome/grit/generated_resources.h" 44 #include "chromeos/audio/chromeos_sounds.h" 45 #include "chromeos/dbus/dbus_thread_manager.h" 46 #include "chromeos/dbus/session_manager_client.h" 47 #include "chromeos/login/auth/authenticator.h" 48 #include "chromeos/login/auth/extended_authenticator.h" 49 #include "components/signin/core/browser/signin_manager.h" 50 #include "components/user_manager/user_manager.h" 51 #include "components/user_manager/user_type.h" 52 #include "content/public/browser/browser_thread.h" 53 #include "content/public/browser/notification_service.h" 54 #include "content/public/browser/url_data_source.h" 55 #include "content/public/browser/user_metrics.h" 56 #include "content/public/browser/web_contents.h" 57 #include "content/public/browser/web_ui.h" 58 #include "media/audio/sounds/sounds_manager.h" 59 #include "ui/base/resource/resource_bundle.h" 60 #include "ui/gfx/image/image.h" 61 #include "url/gurl.h" 62 63 using base::UserMetricsAction; 64 using content::BrowserThread; 65 66 namespace chromeos { 67 68 namespace { 69 70 // Timeout for unlock animation guard - some animations may be required to run 71 // on successful authentication before unlocking, but we want to be sure that 72 // unlock happens even if animations are broken. 73 const int kUnlockGuardTimeoutMs = 400; 74 75 // Observer to start ScreenLocker when locking the screen is requested. 76 class ScreenLockObserver : public SessionManagerClient::StubDelegate, 77 public content::NotificationObserver, 78 public UserAddingScreen::Observer { 79 public: 80 ScreenLockObserver() : session_started_(false) { 81 registrar_.Add(this, 82 chrome::NOTIFICATION_SESSION_STARTED, 83 content::NotificationService::AllSources()); 84 DBusThreadManager::Get()->GetSessionManagerClient()->SetStubDelegate(this); 85 } 86 87 virtual ~ScreenLockObserver() { 88 if (DBusThreadManager::IsInitialized()) { 89 DBusThreadManager::Get()->GetSessionManagerClient()->SetStubDelegate( 90 NULL); 91 } 92 } 93 94 bool session_started() const { return session_started_; } 95 96 // SessionManagerClient::StubDelegate overrides: 97 virtual void LockScreenForStub() OVERRIDE { 98 ScreenLocker::HandleLockScreenRequest(); 99 } 100 101 // NotificationObserver overrides: 102 virtual void Observe(int type, 103 const content::NotificationSource& source, 104 const content::NotificationDetails& details) OVERRIDE { 105 if (type == chrome::NOTIFICATION_SESSION_STARTED) 106 session_started_ = true; 107 else 108 NOTREACHED() << "Unexpected notification " << type; 109 } 110 111 // UserAddingScreen::Observer overrides: 112 virtual void OnUserAddingFinished() OVERRIDE { 113 UserAddingScreen::Get()->RemoveObserver(this); 114 ScreenLocker::HandleLockScreenRequest(); 115 } 116 117 private: 118 bool session_started_; 119 content::NotificationRegistrar registrar_; 120 121 DISALLOW_COPY_AND_ASSIGN(ScreenLockObserver); 122 }; 123 124 ScreenLockObserver* g_screen_lock_observer = NULL; 125 126 } // namespace 127 128 // static 129 ScreenLocker* ScreenLocker::screen_locker_ = NULL; 130 131 ////////////////////////////////////////////////////////////////////////////// 132 // ScreenLocker, public: 133 134 ScreenLocker::ScreenLocker(const user_manager::UserList& users) 135 : users_(users), 136 locked_(false), 137 start_time_(base::Time::Now()), 138 auth_status_consumer_(NULL), 139 incorrect_passwords_count_(0), 140 weak_factory_(this) { 141 DCHECK(!screen_locker_); 142 screen_locker_ = this; 143 144 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); 145 media::SoundsManager* manager = media::SoundsManager::Get(); 146 manager->Initialize(SOUND_LOCK, 147 bundle.GetRawDataResource(IDR_SOUND_LOCK_WAV)); 148 manager->Initialize(SOUND_UNLOCK, 149 bundle.GetRawDataResource(IDR_SOUND_UNLOCK_WAV)); 150 151 #if !defined(USE_ATHENA) 152 // crbug.com/408733 153 ash::Shell::GetInstance()-> 154 lock_state_controller()->SetLockScreenDisplayedCallback( 155 base::Bind(base::IgnoreResult(&ash::PlaySystemSoundIfSpokenFeedback), 156 static_cast<media::SoundsManager::SoundKey>( 157 chromeos::SOUND_LOCK))); 158 #endif 159 } 160 161 void ScreenLocker::Init() { 162 input_method::InputMethodManager* imm = 163 input_method::InputMethodManager::Get(); 164 saved_ime_state_ = imm->GetActiveIMEState(); 165 imm->SetState(saved_ime_state_->Clone()); 166 167 authenticator_ = LoginUtils::Get()->CreateAuthenticator(this); 168 extended_authenticator_ = ExtendedAuthenticator::Create(this); 169 delegate_.reset(new WebUIScreenLocker(this)); 170 delegate_->LockScreen(); 171 172 // Ownership of |icon_image_source| is passed. 173 screenlock_icon_provider_.reset(new ScreenlockIconProvider); 174 ScreenlockIconSource* screenlock_icon_source = 175 new ScreenlockIconSource(screenlock_icon_provider_->AsWeakPtr()); 176 content::URLDataSource::Add( 177 GetAssociatedWebUI()->GetWebContents()->GetBrowserContext(), 178 screenlock_icon_source); 179 } 180 181 void ScreenLocker::OnAuthFailure(const AuthFailure& error) { 182 content::RecordAction(UserMetricsAction("ScreenLocker_OnLoginFailure")); 183 if (authentication_start_time_.is_null()) { 184 LOG(ERROR) << "Start time is not set at authentication failure"; 185 } else { 186 base::TimeDelta delta = base::Time::Now() - authentication_start_time_; 187 VLOG(1) << "Authentication failure: " << delta.InSecondsF() << " second(s)"; 188 UMA_HISTOGRAM_TIMES("ScreenLocker.AuthenticationFailureTime", delta); 189 } 190 191 EnableInput(); 192 // Don't enable signout button here as we're showing 193 // MessageBubble. 194 195 delegate_->ShowErrorMessage(incorrect_passwords_count_++ ? 196 IDS_LOGIN_ERROR_AUTHENTICATING_2ND_TIME : 197 IDS_LOGIN_ERROR_AUTHENTICATING, 198 HelpAppLauncher::HELP_CANT_ACCESS_ACCOUNT); 199 200 if (auth_status_consumer_) 201 auth_status_consumer_->OnAuthFailure(error); 202 } 203 204 void ScreenLocker::OnAuthSuccess(const UserContext& user_context) { 205 incorrect_passwords_count_ = 0; 206 if (authentication_start_time_.is_null()) { 207 if (!user_context.GetUserID().empty()) 208 LOG(ERROR) << "Start time is not set at authentication success"; 209 } else { 210 base::TimeDelta delta = base::Time::Now() - authentication_start_time_; 211 VLOG(1) << "Authentication success: " << delta.InSecondsF() << " second(s)"; 212 UMA_HISTOGRAM_TIMES("ScreenLocker.AuthenticationSuccessTime", delta); 213 } 214 215 const user_manager::User* user = 216 user_manager::UserManager::Get()->FindUser(user_context.GetUserID()); 217 if (user) { 218 if (!user->is_active()) { 219 saved_ime_state_ = NULL; 220 user_manager::UserManager::Get()->SwitchActiveUser( 221 user_context.GetUserID()); 222 } 223 UserSessionManager::GetInstance()->UpdateEasyUnlockKeys(user_context); 224 } else { 225 NOTREACHED() << "Logged in user not found."; 226 } 227 228 authentication_capture_.reset(new AuthenticationParametersCapture()); 229 authentication_capture_->user_context = user_context; 230 231 // Add guard for case when something get broken in call chain to unlock 232 // for sure. 233 base::MessageLoop::current()->PostDelayedTask( 234 FROM_HERE, 235 base::Bind(&ScreenLocker::UnlockOnLoginSuccess, 236 weak_factory_.GetWeakPtr()), 237 base::TimeDelta::FromMilliseconds(kUnlockGuardTimeoutMs)); 238 delegate_->AnimateAuthenticationSuccess(); 239 } 240 241 void ScreenLocker::UnlockOnLoginSuccess() { 242 DCHECK(base::MessageLoopForUI::IsCurrent()); 243 if (!authentication_capture_.get()) { 244 LOG(WARNING) << "Call to UnlockOnLoginSuccess without previous " << 245 "authentication success."; 246 return; 247 } 248 249 if (auth_status_consumer_) { 250 auth_status_consumer_->OnAuthSuccess(authentication_capture_->user_context); 251 } 252 authentication_capture_.reset(); 253 weak_factory_.InvalidateWeakPtrs(); 254 255 VLOG(1) << "Hiding the lock screen."; 256 chromeos::ScreenLocker::Hide(); 257 } 258 259 void ScreenLocker::Authenticate(const UserContext& user_context) { 260 LOG_ASSERT(IsUserLoggedIn(user_context.GetUserID())) 261 << "Invalid user trying to unlock."; 262 263 authentication_start_time_ = base::Time::Now(); 264 delegate_->SetInputEnabled(false); 265 delegate_->OnAuthenticate(); 266 267 // Special case: supervised users. Use special authenticator. 268 if (const user_manager::User* user = 269 FindUnlockUser(user_context.GetUserID())) { 270 if (user->GetType() == user_manager::USER_TYPE_SUPERVISED) { 271 UserContext updated_context = ChromeUserManager::Get() 272 ->GetSupervisedUserManager() 273 ->GetAuthentication() 274 ->TransformKey(user_context); 275 // TODO(antrim) : replace empty closure with explicit method. 276 // http://crbug.com/351268 277 BrowserThread::PostTask( 278 BrowserThread::UI, 279 FROM_HERE, 280 base::Bind(&ExtendedAuthenticator::AuthenticateToCheck, 281 extended_authenticator_.get(), 282 updated_context, 283 base::Closure())); 284 return; 285 } 286 } 287 288 BrowserThread::PostTask( 289 BrowserThread::UI, FROM_HERE, 290 base::Bind(&ExtendedAuthenticator::AuthenticateToCheck, 291 extended_authenticator_.get(), 292 user_context, 293 base::Closure())); 294 } 295 296 const user_manager::User* ScreenLocker::FindUnlockUser( 297 const std::string& user_id) { 298 const user_manager::User* unlock_user = NULL; 299 for (user_manager::UserList::const_iterator it = users_.begin(); 300 it != users_.end(); 301 ++it) { 302 if ((*it)->email() == user_id) { 303 unlock_user = *it; 304 break; 305 } 306 } 307 return unlock_user; 308 } 309 310 void ScreenLocker::ClearErrors() { 311 delegate_->ClearErrors(); 312 } 313 314 void ScreenLocker::Signout() { 315 delegate_->ClearErrors(); 316 content::RecordAction(UserMetricsAction("ScreenLocker_Signout")); 317 // We expect that this call will not wait for any user input. 318 // If it changes at some point, we will need to force exit. 319 chrome::AttemptUserExit(); 320 321 // Don't hide yet the locker because the chrome screen may become visible 322 // briefly. 323 } 324 325 void ScreenLocker::EnableInput() { 326 delegate_->SetInputEnabled(true); 327 } 328 329 void ScreenLocker::ShowErrorMessage(int error_msg_id, 330 HelpAppLauncher::HelpTopic help_topic_id, 331 bool sign_out_only) { 332 delegate_->SetInputEnabled(!sign_out_only); 333 delegate_->ShowErrorMessage(error_msg_id, help_topic_id); 334 } 335 336 void ScreenLocker::SetLoginStatusConsumer( 337 chromeos::AuthStatusConsumer* consumer) { 338 auth_status_consumer_ = consumer; 339 } 340 341 // static 342 void ScreenLocker::InitClass() { 343 DCHECK(!g_screen_lock_observer); 344 g_screen_lock_observer = new ScreenLockObserver; 345 } 346 347 // static 348 void ScreenLocker::ShutDownClass() { 349 DCHECK(g_screen_lock_observer); 350 delete g_screen_lock_observer; 351 g_screen_lock_observer = NULL; 352 } 353 354 // static 355 void ScreenLocker::HandleLockScreenRequest() { 356 VLOG(1) << "Received LockScreen request from session manager"; 357 DCHECK(g_screen_lock_observer); 358 if (UserAddingScreen::Get()->IsRunning()) { 359 VLOG(1) << "Waiting for user adding screen to stop"; 360 UserAddingScreen::Get()->AddObserver(g_screen_lock_observer); 361 UserAddingScreen::Get()->Cancel(); 362 return; 363 } 364 if (g_screen_lock_observer->session_started() && 365 user_manager::UserManager::Get()->CanCurrentUserLock()) { 366 ScreenLocker::Show(); 367 ash::Shell::GetInstance()->lock_state_controller()->OnStartingLock(); 368 } else { 369 // If the current user's session cannot be locked or the user has not 370 // completed all sign-in steps yet, log out instead. The latter is done to 371 // avoid complications with displaying the lock screen over the login 372 // screen while remaining secure in the case the user walks away during 373 // the sign-in steps. See crbug.com/112225 and crbug.com/110933. 374 VLOG(1) << "Calling session manager's StopSession D-Bus method"; 375 DBusThreadManager::Get()->GetSessionManagerClient()->StopSession(); 376 } 377 } 378 379 // static 380 void ScreenLocker::Show() { 381 #if defined(USE_ATHENA) 382 // crbug.com/413926 383 return; 384 #endif 385 386 content::RecordAction(UserMetricsAction("ScreenLocker_Show")); 387 DCHECK(base::MessageLoopForUI::IsCurrent()); 388 389 // Check whether the currently logged in user is a guest account and if so, 390 // refuse to lock the screen (crosbug.com/23764). 391 // For a demo user, we should never show the lock screen (crosbug.com/27647). 392 if (user_manager::UserManager::Get()->IsLoggedInAsGuest() || 393 user_manager::UserManager::Get()->IsLoggedInAsDemoUser()) { 394 VLOG(1) << "Refusing to lock screen for guest/demo account"; 395 return; 396 } 397 398 // If the active window is fullscreen, exit fullscreen to avoid the web page 399 // or app mimicking the lock screen. Do not exit fullscreen if the shelf is 400 // visible while in fullscreen because the shelf makes it harder for a web 401 // page or app to mimick the lock screen. 402 ash::wm::WindowState* active_window_state = ash::wm::GetActiveWindowState(); 403 if (active_window_state && 404 active_window_state->IsFullscreen() && 405 active_window_state->hide_shelf_when_fullscreen()) { 406 const ash::wm::WMEvent event(ash::wm::WM_EVENT_TOGGLE_FULLSCREEN); 407 active_window_state->OnWMEvent(&event); 408 } 409 410 if (!screen_locker_) { 411 ScreenLocker* locker = 412 new ScreenLocker(user_manager::UserManager::Get()->GetUnlockUsers()); 413 VLOG(1) << "Created ScreenLocker " << locker; 414 locker->Init(); 415 } else { 416 VLOG(1) << "ScreenLocker " << screen_locker_ << " already exists; " 417 << " calling session manager's HandleLockScreenShown D-Bus method"; 418 DBusThreadManager::Get()->GetSessionManagerClient()-> 419 NotifyLockScreenShown(); 420 } 421 } 422 423 // static 424 void ScreenLocker::Hide() { 425 #if defined(USE_ATHENA) 426 // crbug.com/413926 427 return; 428 #endif 429 430 DCHECK(base::MessageLoopForUI::IsCurrent()); 431 // For a guest/demo user, screen_locker_ would have never been initialized. 432 if (user_manager::UserManager::Get()->IsLoggedInAsGuest() || 433 user_manager::UserManager::Get()->IsLoggedInAsDemoUser()) { 434 VLOG(1) << "Refusing to hide lock screen for guest/demo account"; 435 return; 436 } 437 438 DCHECK(screen_locker_); 439 base::Callback<void(void)> callback = 440 base::Bind(&ScreenLocker::ScheduleDeletion); 441 ash::Shell::GetInstance()->lock_state_controller()-> 442 OnLockScreenHide(callback); 443 } 444 445 void ScreenLocker::ScheduleDeletion() { 446 // Avoid possible multiple calls. 447 if (screen_locker_ == NULL) 448 return; 449 VLOG(1) << "Deleting ScreenLocker " << screen_locker_; 450 451 ash::PlaySystemSoundIfSpokenFeedback(SOUND_UNLOCK); 452 453 delete screen_locker_; 454 screen_locker_ = NULL; 455 } 456 457 //////////////////////////////////////////////////////////////////////////////// 458 // ScreenLocker, private: 459 460 ScreenLocker::~ScreenLocker() { 461 VLOG(1) << "Destroying ScreenLocker " << this; 462 DCHECK(base::MessageLoopForUI::IsCurrent()); 463 464 if (authenticator_.get()) 465 authenticator_->SetConsumer(NULL); 466 ClearErrors(); 467 468 VLOG(1) << "Moving desktop background to unlocked container"; 469 ash::Shell::GetInstance()-> 470 desktop_background_controller()->MoveDesktopToUnlockedContainer(); 471 472 screen_locker_ = NULL; 473 bool state = false; 474 VLOG(1) << "Emitting SCREEN_LOCK_STATE_CHANGED with state=" << state; 475 content::NotificationService::current()->Notify( 476 chrome::NOTIFICATION_SCREEN_LOCK_STATE_CHANGED, 477 content::Source<ScreenLocker>(this), 478 content::Details<bool>(&state)); 479 480 VLOG(1) << "Calling session manager's HandleLockScreenDismissed D-Bus method"; 481 DBusThreadManager::Get()->GetSessionManagerClient()-> 482 NotifyLockScreenDismissed(); 483 484 if (saved_ime_state_.get()) { 485 input_method::InputMethodManager::Get()->SetState(saved_ime_state_); 486 } 487 } 488 489 void ScreenLocker::SetAuthenticator(Authenticator* authenticator) { 490 authenticator_ = authenticator; 491 } 492 493 void ScreenLocker::ScreenLockReady() { 494 locked_ = true; 495 base::TimeDelta delta = base::Time::Now() - start_time_; 496 VLOG(1) << "ScreenLocker " << this << " is ready after " 497 << delta.InSecondsF() << " second(s)"; 498 UMA_HISTOGRAM_TIMES("ScreenLocker.ScreenLockTime", delta); 499 500 VLOG(1) << "Moving desktop background to locked container"; 501 ash::Shell::GetInstance()-> 502 desktop_background_controller()->MoveDesktopToLockedContainer(); 503 504 input_method::InputMethodManager::Get() 505 ->GetActiveIMEState() 506 ->EnableLockScreenLayouts(); 507 508 bool state = true; 509 VLOG(1) << "Emitting SCREEN_LOCK_STATE_CHANGED with state=" << state; 510 content::NotificationService::current()->Notify( 511 chrome::NOTIFICATION_SCREEN_LOCK_STATE_CHANGED, 512 content::Source<ScreenLocker>(this), 513 content::Details<bool>(&state)); 514 VLOG(1) << "Calling session manager's HandleLockScreenShown D-Bus method"; 515 DBusThreadManager::Get()->GetSessionManagerClient()->NotifyLockScreenShown(); 516 } 517 518 content::WebUI* ScreenLocker::GetAssociatedWebUI() { 519 return delegate_->GetAssociatedWebUI(); 520 } 521 522 bool ScreenLocker::IsUserLoggedIn(const std::string& username) { 523 for (user_manager::UserList::const_iterator it = users_.begin(); 524 it != users_.end(); 525 ++it) { 526 if ((*it)->email() == username) 527 return true; 528 } 529 return false; 530 } 531 532 } // namespace chromeos 533