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
99using content::BrowserThread;
100
101namespace {
102
103void 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
115namespace chromeos {
116
117namespace {
118
119// Returns new CommandLine with per-user flags.
120CommandLine 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.
129bool 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
150bool 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
182class 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
241class 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
269void 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
334void 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
343void 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
365void LoginUtilsImpl::DoBrowserLaunch(Profile* profile,
366                                     LoginDisplayHost* login_host) {
367  DoBrowserLaunchInternal(profile, login_host, false /* locale_pref_checked */);
368}
369
370void 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
387void LoginUtilsImpl::DelegateDeleted(LoginUtils::Delegate* delegate) {
388  if (delegate_ == delegate)
389    delegate_ = NULL;
390}
391
392bool 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
419void 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
451scoped_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
469void LoginUtilsImpl::OnProfilePrepared(Profile* profile) {
470  if (delegate_)
471    delegate_->OnProfilePrepared(profile);
472}
473
474#if defined(ENABLE_RLZ)
475void LoginUtilsImpl::OnRlzInitialized() {
476  if (delegate_)
477    delegate_->OnRlzInitialized();
478}
479#endif
480
481void 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
513LoginUtils* LoginUtils::Get() {
514  return LoginUtilsWrapper::GetInstance()->get();
515}
516
517// static
518void LoginUtils::Set(LoginUtils* mock) {
519  LoginUtilsWrapper::GetInstance()->reset(mock);
520}
521
522// static
523bool 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