Home | History | Annotate | Download | only in lock
      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