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