Home | History | Annotate | Download | only in login
      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/login_utils.h"
      6 
      7 #include <algorithm>
      8 #include <set>
      9 #include <vector>
     10 
     11 #include "base/base_paths.h"
     12 #include "base/bind.h"
     13 #include "base/bind_helpers.h"
     14 #include "base/callback.h"
     15 #include "base/command_line.h"
     16 #include "base/compiler_specific.h"
     17 #include "base/files/file_path.h"
     18 #include "base/files/file_util.h"
     19 #include "base/location.h"
     20 #include "base/memory/ref_counted.h"
     21 #include "base/memory/scoped_ptr.h"
     22 #include "base/memory/singleton.h"
     23 #include "base/memory/weak_ptr.h"
     24 #include "base/prefs/pref_member.h"
     25 #include "base/prefs/pref_service.h"
     26 #include "base/strings/string_util.h"
     27 #include "base/strings/utf_string_conversions.h"
     28 #include "base/synchronization/lock.h"
     29 #include "base/sys_info.h"
     30 #include "base/task_runner_util.h"
     31 #include "base/threading/worker_pool.h"
     32 #include "base/time/time.h"
     33 #include "chrome/browser/about_flags.h"
     34 #include "chrome/browser/app_mode/app_mode_utils.h"
     35 #include "chrome/browser/browser_process.h"
     36 #include "chrome/browser/browser_shutdown.h"
     37 #include "chrome/browser/chrome_notification_types.h"
     38 #include "chrome/browser/chromeos/boot_times_loader.h"
     39 #include "chrome/browser/chromeos/login/auth/chrome_cryptohome_authenticator.h"
     40 #include "chrome/browser/chromeos/login/chrome_restart_request.h"
     41 #include "chrome/browser/chromeos/login/demo_mode/demo_app_launcher.h"
     42 #include "chrome/browser/chromeos/login/existing_user_controller.h"
     43 #include "chrome/browser/chromeos/login/lock/screen_locker.h"
     44 #include "chrome/browser/chromeos/login/profile_auth_data.h"
     45 #include "chrome/browser/chromeos/login/saml/saml_offline_signin_limiter.h"
     46 #include "chrome/browser/chromeos/login/saml/saml_offline_signin_limiter_factory.h"
     47 #include "chrome/browser/chromeos/login/session/user_session_manager.h"
     48 #include "chrome/browser/chromeos/login/signin/oauth2_login_manager.h"
     49 #include "chrome/browser/chromeos/login/signin/oauth2_login_manager_factory.h"
     50 #include "chrome/browser/chromeos/login/startup_utils.h"
     51 #include "chrome/browser/chromeos/login/ui/input_events_blocker.h"
     52 #include "chrome/browser/chromeos/login/ui/login_display_host.h"
     53 #include "chrome/browser/chromeos/login/user_flow.h"
     54 #include "chrome/browser/chromeos/login/users/chrome_user_manager.h"
     55 #include "chrome/browser/chromeos/login/users/supervised_user_manager.h"
     56 #include "chrome/browser/chromeos/profiles/profile_helper.h"
     57 #include "chrome/browser/chromeos/settings/cros_settings.h"
     58 #include "chrome/browser/extensions/extension_service.h"
     59 #include "chrome/browser/first_run/first_run.h"
     60 #include "chrome/browser/google/google_brand_chromeos.h"
     61 #include "chrome/browser/lifetime/application_lifetime.h"
     62 #include "chrome/browser/pref_service_flags_storage.h"
     63 #include "chrome/browser/profiles/profile.h"
     64 #include "chrome/browser/profiles/profile_manager.h"
     65 #include "chrome/browser/rlz/rlz.h"
     66 #include "chrome/browser/signin/signin_manager_factory.h"
     67 #include "chrome/browser/sync/profile_sync_service.h"
     68 #include "chrome/browser/sync/profile_sync_service_factory.h"
     69 #include "chrome/browser/ui/app_list/start_page_service.h"
     70 #include "chrome/browser/ui/startup/startup_browser_creator.h"
     71 #include "chrome/common/chrome_switches.h"
     72 #include "chrome/common/logging_chrome.h"
     73 #include "chrome/common/pref_names.h"
     74 #include "chromeos/chromeos_switches.h"
     75 #include "chromeos/cryptohome/cryptohome_util.h"
     76 #include "chromeos/dbus/cryptohome_client.h"
     77 #include "chromeos/dbus/dbus_method_call_status.h"
     78 #include "chromeos/dbus/dbus_thread_manager.h"
     79 #include "chromeos/dbus/session_manager_client.h"
     80 #include "chromeos/login/auth/user_context.h"
     81 #include "chromeos/login/user_names.h"
     82 #include "chromeos/settings/cros_settings_names.h"
     83 #include "components/signin/core/browser/signin_manager.h"
     84 #include "components/user_manager/user.h"
     85 #include "components/user_manager/user_manager.h"
     86 #include "content/public/browser/browser_thread.h"
     87 #include "content/public/browser/notification_service.h"
     88 #include "google_apis/gaia/gaia_auth_consumer.h"
     89 #include "net/base/network_change_notifier.h"
     90 #include "net/url_request/url_request_context.h"
     91 #include "net/url_request/url_request_context_getter.h"
     92 #include "url/gurl.h"
     93 
     94 #if defined(USE_ATHENA)
     95 #include "athena/extensions/public/extensions_delegate.h"
     96 #include "athena/main/public/athena_launcher.h"
     97 #endif
     98 
     99 using content::BrowserThread;
    100 
    101 namespace {
    102 
    103 void LogCustomSwitches(const std::set<std::string>& switches) {
    104   if (!VLOG_IS_ON(1))
    105     return;
    106   for (std::set<std::string>::const_iterator it = switches.begin();
    107        it != switches.end();
    108        ++it) {
    109     VLOG(1) << "Switch leading to restart: '" << *it << "'";
    110   }
    111 }
    112 
    113 }  // anonymous namespace
    114 
    115 namespace chromeos {
    116 
    117 namespace {
    118 
    119 // Returns new CommandLine with per-user flags.
    120 CommandLine CreatePerSessionCommandLine(Profile* profile) {
    121   CommandLine user_flags(CommandLine::NO_PROGRAM);
    122   about_flags::PrefServiceFlagsStorage flags_storage_(profile->GetPrefs());
    123   about_flags::ConvertFlagsToSwitches(
    124       &flags_storage_, &user_flags, about_flags::kAddSentinels);
    125   return user_flags;
    126 }
    127 
    128 // Returns true if restart is needed to apply per-session flags.
    129 bool NeedRestartToApplyPerSessionFlags(
    130     const CommandLine& user_flags,
    131     std::set<CommandLine::StringType>* out_command_line_difference) {
    132   // Don't restart browser if it is not first profile in session.
    133   if (user_manager::UserManager::Get()->GetLoggedInUsers().size() != 1)
    134     return false;
    135 
    136   // Only restart if needed and if not going into managed mode.
    137   if (user_manager::UserManager::Get()->IsLoggedInAsSupervisedUser())
    138     return false;
    139 
    140   if (about_flags::AreSwitchesIdenticalToCurrentCommandLine(
    141           user_flags,
    142           *CommandLine::ForCurrentProcess(),
    143           out_command_line_difference)) {
    144     return false;
    145   }
    146 
    147   return true;
    148 }
    149 
    150 bool CanPerformEarlyRestart() {
    151   // Desktop build is used for development only. Early restart is not supported.
    152   if (!base::SysInfo::IsRunningOnChromeOS())
    153     return false;
    154 
    155   if (!ChromeUserManager::Get()->GetCurrentUserFlow()->
    156           SupportsEarlyRestartToApplyFlags()) {
    157     return false;
    158   }
    159 
    160   const ExistingUserController* controller =
    161       ExistingUserController::current_controller();
    162   if (!controller)
    163     return true;
    164 
    165   // Early restart is possible only if OAuth token is up to date.
    166 
    167   if (controller->password_changed())
    168     return false;
    169 
    170   if (controller->auth_mode() != LoginPerformer::AUTH_MODE_INTERNAL)
    171     return false;
    172 
    173   // No early restart if Easy unlock key needs to be updated.
    174   if (UserSessionManager::GetInstance()->NeedsToUpdateEasyUnlockKeys())
    175     return false;
    176 
    177   return true;
    178 }
    179 
    180 }  // namespace
    181 
    182 class LoginUtilsImpl : public LoginUtils,
    183                        public base::SupportsWeakPtr<LoginUtilsImpl>,
    184                        public UserSessionManagerDelegate {
    185  public:
    186   LoginUtilsImpl()
    187       : delegate_(NULL) {
    188   }
    189 
    190   virtual ~LoginUtilsImpl() {
    191   }
    192 
    193   // LoginUtils implementation:
    194   virtual void RespectLocalePreference(Profile* profile,
    195                                        const base::Closure& callback) OVERRIDE;
    196   virtual void DoBrowserLaunch(Profile* profile,
    197                                LoginDisplayHost* login_host) OVERRIDE;
    198   virtual void PrepareProfile(
    199       const UserContext& user_context,
    200       bool has_auth_cookies,
    201       bool has_active_session,
    202       LoginUtils::Delegate* delegate) OVERRIDE;
    203   virtual void DelegateDeleted(LoginUtils::Delegate* delegate) OVERRIDE;
    204   virtual void CompleteOffTheRecordLogin(const GURL& start_url) OVERRIDE;
    205   virtual scoped_refptr<Authenticator> CreateAuthenticator(
    206       AuthStatusConsumer* consumer) OVERRIDE;
    207   virtual bool RestartToApplyPerSessionFlagsIfNeed(Profile* profile,
    208                                                    bool early_restart) OVERRIDE;
    209 
    210   // UserSessionManager::Delegate implementation:
    211    virtual void OnProfilePrepared(Profile* profile) OVERRIDE;
    212  #if defined(ENABLE_RLZ)
    213    virtual void OnRlzInitialized() OVERRIDE;
    214  #endif
    215 
    216  private:
    217   void DoBrowserLaunchInternal(Profile* profile,
    218                                LoginDisplayHost* login_host,
    219                                bool locale_pref_checked);
    220 
    221   static void RunCallbackOnLocaleLoaded(
    222       const base::Closure& callback,
    223       InputEventsBlocker* input_events_blocker,
    224       const std::string& locale,
    225       const std::string& loaded_locale,
    226       const bool success);
    227 
    228   // Attempts restarting the browser process and esures that this does
    229   // not happen while we are still fetching new OAuth refresh tokens.
    230   void AttemptRestart(Profile* profile);
    231 
    232   // Has to be scoped_refptr, see comment for CreateAuthenticator(...).
    233   scoped_refptr<Authenticator> authenticator_;
    234 
    235   // Delegate to be fired when the profile will be prepared.
    236   LoginUtils::Delegate* delegate_;
    237 
    238   DISALLOW_COPY_AND_ASSIGN(LoginUtilsImpl);
    239 };
    240 
    241 class LoginUtilsWrapper {
    242  public:
    243   static LoginUtilsWrapper* GetInstance() {
    244     return Singleton<LoginUtilsWrapper>::get();
    245   }
    246 
    247   LoginUtils* get() {
    248     base::AutoLock create(create_lock_);
    249     if (!ptr_.get())
    250       reset(new LoginUtilsImpl);
    251     return ptr_.get();
    252   }
    253 
    254   void reset(LoginUtils* ptr) {
    255     ptr_.reset(ptr);
    256   }
    257 
    258  private:
    259   friend struct DefaultSingletonTraits<LoginUtilsWrapper>;
    260 
    261   LoginUtilsWrapper() {}
    262 
    263   base::Lock create_lock_;
    264   scoped_ptr<LoginUtils> ptr_;
    265 
    266   DISALLOW_COPY_AND_ASSIGN(LoginUtilsWrapper);
    267 };
    268 
    269 void LoginUtilsImpl::DoBrowserLaunchInternal(Profile* profile,
    270                                              LoginDisplayHost* login_host,
    271                                              bool locale_pref_checked) {
    272   if (browser_shutdown::IsTryingToQuit())
    273     return;
    274 
    275   if (!locale_pref_checked) {
    276     RespectLocalePreference(profile,
    277                             base::Bind(&LoginUtilsImpl::DoBrowserLaunchInternal,
    278                                        base::Unretained(this),
    279                                        profile,
    280                                        login_host,
    281                                        true /* locale_pref_checked */));
    282     return;
    283   }
    284 
    285   if (!ChromeUserManager::Get()->GetCurrentUserFlow()->ShouldLaunchBrowser()) {
    286     ChromeUserManager::Get()->GetCurrentUserFlow()->LaunchExtraSteps(profile);
    287     return;
    288   }
    289 
    290   if (RestartToApplyPerSessionFlagsIfNeed(profile, false))
    291     return;
    292 
    293   if (login_host) {
    294     login_host->SetStatusAreaVisible(true);
    295     login_host->BeforeSessionStart();
    296   }
    297 
    298   BootTimesLoader::Get()->AddLoginTimeMarker("BrowserLaunched", false);
    299 
    300   VLOG(1) << "Launching browser...";
    301   TRACE_EVENT0("login", "LaunchBrowser");
    302 
    303 #if defined(USE_ATHENA)
    304   athena::ExtensionsDelegate::CreateExtensionsDelegateForChrome(profile);
    305   athena::StartAthenaSessionWithContext(profile);
    306 #else
    307   StartupBrowserCreator browser_creator;
    308   int return_code;
    309   chrome::startup::IsFirstRun first_run = first_run::IsChromeFirstRun() ?
    310       chrome::startup::IS_FIRST_RUN : chrome::startup::IS_NOT_FIRST_RUN;
    311 
    312   browser_creator.LaunchBrowser(*CommandLine::ForCurrentProcess(),
    313                                 profile,
    314                                 base::FilePath(),
    315                                 chrome::startup::IS_PROCESS_STARTUP,
    316                                 first_run,
    317                                 &return_code);
    318 
    319   // Triggers app launcher start page service to load start page web contents.
    320   app_list::StartPageService::Get(profile);
    321 #endif
    322 
    323   // Mark login host for deletion after browser starts.  This
    324   // guarantees that the message loop will be referenced by the
    325   // browser before it is dereferenced by the login host.
    326   if (login_host)
    327     login_host->Finalize();
    328   user_manager::UserManager::Get()->SessionStarted();
    329   chromeos::BootTimesLoader::Get()->LoginDone(
    330       user_manager::UserManager::Get()->IsCurrentUserNew());
    331 }
    332 
    333 // static
    334 void LoginUtilsImpl::RunCallbackOnLocaleLoaded(
    335     const base::Closure& callback,
    336     InputEventsBlocker* /* input_events_blocker */,
    337     const std::string& /* locale */,
    338     const std::string& /* loaded_locale */,
    339     const bool /* success */) {
    340   callback.Run();
    341 }
    342 
    343 void LoginUtilsImpl::RespectLocalePreference(Profile* profile,
    344                                              const base::Closure& callback) {
    345   if (browser_shutdown::IsTryingToQuit())
    346     return;
    347 
    348   user_manager::User* const user =
    349       ProfileHelper::Get()->GetUserByProfile(profile);
    350   scoped_ptr<locale_util::SwitchLanguageCallback> locale_switched_callback(
    351       new locale_util::SwitchLanguageCallback(base::Bind(
    352               &LoginUtilsImpl::RunCallbackOnLocaleLoaded,
    353               callback,
    354               base::Owned(new InputEventsBlocker))));  // Block UI events until
    355                                                        // the ResourceBundle is
    356                                                        // reloaded.
    357   if (!UserSessionManager::GetInstance()->RespectLocalePreference(
    358           profile,
    359           user,
    360           locale_switched_callback.Pass())) {
    361     callback.Run();
    362   }
    363 }
    364 
    365 void LoginUtilsImpl::DoBrowserLaunch(Profile* profile,
    366                                      LoginDisplayHost* login_host) {
    367   DoBrowserLaunchInternal(profile, login_host, false /* locale_pref_checked */);
    368 }
    369 
    370 void LoginUtilsImpl::PrepareProfile(
    371     const UserContext& user_context,
    372     bool has_auth_cookies,
    373     bool has_active_session,
    374     LoginUtils::Delegate* delegate) {
    375   // TODO(nkostylev): We have to initialize LoginUtils delegate as long
    376   // as it coexist with SessionManager.
    377   delegate_ = delegate;
    378 
    379   // For the transition part LoginUtils will just delegate profile
    380   // creation and initialization to SessionManager. Later LoginUtils will be
    381   // removed and all LoginUtils clients will just work with SessionManager
    382   // directly.
    383   UserSessionManager::GetInstance()->StartSession(
    384       user_context, authenticator_, has_auth_cookies, has_active_session, this);
    385 }
    386 
    387 void LoginUtilsImpl::DelegateDeleted(LoginUtils::Delegate* delegate) {
    388   if (delegate_ == delegate)
    389     delegate_ = NULL;
    390 }
    391 
    392 bool LoginUtilsImpl::RestartToApplyPerSessionFlagsIfNeed(Profile* profile,
    393                                                          bool early_restart) {
    394   if (ProfileHelper::IsSigninProfile(profile))
    395     return false;
    396 
    397   if (early_restart && !CanPerformEarlyRestart())
    398     return false;
    399 
    400   const CommandLine user_flags(CreatePerSessionCommandLine(profile));
    401   std::set<CommandLine::StringType> command_line_difference;
    402   if (!NeedRestartToApplyPerSessionFlags(user_flags, &command_line_difference))
    403     return false;
    404 
    405   LogCustomSwitches(command_line_difference);
    406 
    407   about_flags::ReportCustomFlags("Login.CustomFlags", command_line_difference);
    408 
    409   CommandLine::StringVector flags;
    410   // argv[0] is the program name |CommandLine::NO_PROGRAM|.
    411   flags.assign(user_flags.argv().begin() + 1, user_flags.argv().end());
    412   LOG(WARNING) << "Restarting to apply per-session flags...";
    413   DBusThreadManager::Get()->GetSessionManagerClient()->SetFlagsForUser(
    414       user_manager::UserManager::Get()->GetActiveUser()->email(), flags);
    415   AttemptRestart(profile);
    416   return true;
    417 }
    418 
    419 void LoginUtilsImpl::CompleteOffTheRecordLogin(const GURL& start_url) {
    420   VLOG(1) << "Completing incognito login";
    421 
    422   // For guest session we ask session manager to restart Chrome with --bwsi
    423   // flag. We keep only some of the arguments of this process.
    424   const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess();
    425   CommandLine command_line(browser_command_line.GetProgram());
    426   std::string cmd_line_str =
    427       GetOffTheRecordCommandLine(start_url,
    428                                  StartupUtils::IsOobeCompleted(),
    429                                  browser_command_line,
    430                                  &command_line);
    431 
    432   // This makes sure that Chrome restarts with no per-session flags. The guest
    433   // profile will always have empty set of per-session flags. If this is not
    434   // done and device owner has some per-session flags, when Chrome is relaunched
    435   // the guest profile session flags will not match the current command line and
    436   // another restart will be attempted in order to reset the user flags for the
    437   // guest user.
    438   const CommandLine user_flags(CommandLine::NO_PROGRAM);
    439   if (!about_flags::AreSwitchesIdenticalToCurrentCommandLine(
    440            user_flags,
    441            *CommandLine::ForCurrentProcess(),
    442            NULL)) {
    443     DBusThreadManager::Get()->GetSessionManagerClient()->SetFlagsForUser(
    444         chromeos::login::kGuestUserName,
    445         CommandLine::StringVector());
    446   }
    447 
    448   RestartChrome(cmd_line_str);
    449 }
    450 
    451 scoped_refptr<Authenticator> LoginUtilsImpl::CreateAuthenticator(
    452     AuthStatusConsumer* consumer) {
    453   // Screen locker needs new Authenticator instance each time.
    454   if (ScreenLocker::default_screen_locker()) {
    455     if (authenticator_.get())
    456       authenticator_->SetConsumer(NULL);
    457     authenticator_ = NULL;
    458   }
    459 
    460   if (authenticator_.get() == NULL) {
    461     authenticator_ = new ChromeCryptohomeAuthenticator(consumer);
    462   } else {
    463     // TODO(nkostylev): Fix this hack by improving Authenticator dependencies.
    464     authenticator_->SetConsumer(consumer);
    465   }
    466   return authenticator_;
    467 }
    468 
    469 void LoginUtilsImpl::OnProfilePrepared(Profile* profile) {
    470   if (delegate_)
    471     delegate_->OnProfilePrepared(profile);
    472 }
    473 
    474 #if defined(ENABLE_RLZ)
    475 void LoginUtilsImpl::OnRlzInitialized() {
    476   if (delegate_)
    477     delegate_->OnRlzInitialized();
    478 }
    479 #endif
    480 
    481 void LoginUtilsImpl::AttemptRestart(Profile* profile) {
    482   if (UserSessionManager::GetInstance()
    483           ->CheckEasyUnlockKeyOps(
    484               base::Bind(&LoginUtilsImpl::AttemptRestart,
    485                          base::Unretained(this),
    486                          profile))) {
    487     return;
    488   }
    489 
    490   if (UserSessionManager::GetInstance()->GetSigninSessionRestoreStrategy() !=
    491       OAuth2LoginManager::RESTORE_FROM_COOKIE_JAR) {
    492     chrome::AttemptRestart();
    493     return;
    494   }
    495 
    496   // We can't really quit if the session restore process that mints new
    497   // refresh token is still in progress.
    498   OAuth2LoginManager* login_manager =
    499       OAuth2LoginManagerFactory::GetInstance()->GetForProfile(profile);
    500   if (login_manager->state() !=
    501           OAuth2LoginManager::SESSION_RESTORE_PREPARING &&
    502       login_manager->state() !=
    503           OAuth2LoginManager::SESSION_RESTORE_IN_PROGRESS) {
    504     chrome::AttemptRestart();
    505     return;
    506   }
    507 
    508   LOG(WARNING) << "Attempting browser restart during session restore.";
    509   UserSessionManager::GetInstance()->set_exit_after_session_restore(true);
    510 }
    511 
    512 // static
    513 LoginUtils* LoginUtils::Get() {
    514   return LoginUtilsWrapper::GetInstance()->get();
    515 }
    516 
    517 // static
    518 void LoginUtils::Set(LoginUtils* mock) {
    519   LoginUtilsWrapper::GetInstance()->reset(mock);
    520 }
    521 
    522 // static
    523 bool LoginUtils::IsWhitelisted(const std::string& username,
    524                                bool* wildcard_match) {
    525   // Skip whitelist check for tests.
    526   if (CommandLine::ForCurrentProcess()->HasSwitch(
    527       chromeos::switches::kOobeSkipPostLogin)) {
    528     return true;
    529   }
    530 
    531   CrosSettings* cros_settings = CrosSettings::Get();
    532   bool allow_new_user = false;
    533   cros_settings->GetBoolean(kAccountsPrefAllowNewUser, &allow_new_user);
    534   if (allow_new_user)
    535     return true;
    536   return cros_settings->FindEmailInList(
    537       kAccountsPrefUsers, username, wildcard_match);
    538 }
    539 
    540 }  // namespace chromeos
    541