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 50 using content::BrowserThread; 51 using content::UserMetricsAction; 52 53 namespace { 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. 58 const int kUnlockGuardTimeoutMs = 400; 59 60 // Observer to start ScreenLocker when the screen lock 61 class 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 141 static base::LazyInstance<ScreenLockObserver> g_screen_lock_observer = 142 LAZY_INSTANCE_INITIALIZER; 143 144 } // namespace 145 146 namespace chromeos { 147 148 // static 149 ScreenLocker* ScreenLocker::screen_locker_ = NULL; 150 151 ////////////////////////////////////////////////////////////////////////////// 152 // ScreenLocker, public: 153 154 ScreenLocker::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 165 void ScreenLocker::Init() { 166 authenticator_ = LoginUtils::Get()->CreateAuthenticator(this); 167 delegate_.reset(new WebUIScreenLocker(this)); 168 delegate_->LockScreen(); 169 } 170 171 void 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 194 void 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 254 void 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 277 void 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 291 void ScreenLocker::AuthenticateByPassword(const std::string& password) { 292 LOG_ASSERT(users_.size() == 1); 293 Authenticate(UserContext(users_[0]->email(), password, "")); 294 } 295 296 void ScreenLocker::ClearErrors() { 297 delegate_->ClearErrors(); 298 } 299 300 void ScreenLocker::EnableInput() { 301 delegate_->SetInputEnabled(true); 302 } 303 304 void 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 315 void 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 322 void ScreenLocker::SetLoginStatusConsumer( 323 chromeos::LoginStatusConsumer* consumer) { 324 login_status_consumer_ = consumer; 325 } 326 327 // static 328 void 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 364 void 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 380 void 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 391 void ScreenLocker::InitClass() { 392 g_screen_lock_observer.Get(); 393 } 394 395 //////////////////////////////////////////////////////////////////////////////// 396 // ScreenLocker, private: 397 398 ScreenLocker::~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 422 void ScreenLocker::SetAuthenticator(Authenticator* authenticator) { 423 authenticator_ = authenticator; 424 } 425 426 void 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 447 content::WebUI* ScreenLocker::GetAssociatedWebUI() { 448 return delegate_->GetAssociatedWebUI(); 449 } 450 451 bool 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