1// Copyright (c) 2011 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 <vector>
8
9#include "base/command_line.h"
10#include "base/file_path.h"
11#include "base/file_util.h"
12#include "base/memory/scoped_ptr.h"
13#include "base/memory/singleton.h"
14#include "base/path_service.h"
15#include "base/string_util.h"
16#include "base/stringprintf.h"
17#include "base/synchronization/lock.h"
18#include "base/threading/thread_restrictions.h"
19#include "base/time.h"
20#include "base/utf_string_conversions.h"
21#include "chrome/browser/browser_process.h"
22#include "chrome/browser/chromeos/boot_times_loader.h"
23#include "chrome/browser/chromeos/cros/login_library.h"
24#include "chrome/browser/chromeos/cros/network_library.h"
25#include "chrome/browser/chromeos/input_method/input_method_util.h"
26#include "chrome/browser/chromeos/login/background_view.h"
27#include "chrome/browser/chromeos/login/cookie_fetcher.h"
28#include "chrome/browser/chromeos/login/google_authenticator.h"
29#include "chrome/browser/chromeos/login/language_switch_menu.h"
30#include "chrome/browser/chromeos/login/ownership_service.h"
31#include "chrome/browser/chromeos/login/parallel_authenticator.h"
32#include "chrome/browser/chromeos/login/user_image_downloader.h"
33#include "chrome/browser/chromeos/login/user_manager.h"
34#include "chrome/browser/chromeos/proxy_config_service.h"
35#include "chrome/browser/extensions/extension_service.h"
36#include "chrome/browser/net/chrome_url_request_context.h"
37#include "chrome/browser/net/gaia/token_service.h"
38#include "chrome/browser/net/preconnect.h"
39#include "chrome/browser/net/pref_proxy_config_service.h"
40#include "chrome/browser/plugin_updater.h"
41#include "chrome/browser/prefs/pref_member.h"
42#include "chrome/browser/profiles/profile.h"
43#include "chrome/browser/profiles/profile_manager.h"
44#include "chrome/browser/sync/profile_sync_service.h"
45#include "chrome/browser/ui/browser_init.h"
46#include "chrome/common/chrome_paths.h"
47#include "chrome/common/chrome_switches.h"
48#include "chrome/common/logging_chrome.h"
49#include "chrome/common/net/gaia/gaia_auth_fetcher.h"
50#include "chrome/common/net/gaia/gaia_constants.h"
51#include "chrome/common/pref_names.h"
52#include "chrome/common/url_constants.h"
53#include "content/browser/browser_thread.h"
54#include "googleurl/src/gurl.h"
55#include "net/base/cookie_store.h"
56#include "net/proxy/proxy_config_service.h"
57#include "net/url_request/url_request_context.h"
58#include "net/url_request/url_request_context_getter.h"
59#include "views/widget/widget_gtk.h"
60#include "ui/gfx/gl/gl_switches.h"
61
62namespace chromeos {
63
64namespace {
65
66// Affixes for Auth token received from ClientLogin request.
67const char kAuthPrefix[] = "Auth=";
68const char kAuthSuffix[] = "\n";
69
70// Increase logging level for Guest mode to avoid LOG(INFO) messages in logs.
71const char kGuestModeLoggingLevel[] = "1";
72
73// Format of command line switch.
74const char kSwitchFormatString[] = " --%s=\"%s\"";
75
76// User name which is used in the Guest session.
77const char kGuestUserName[] = "";
78
79// Resets the proxy configuration service for the default request context.
80class ResetDefaultProxyConfigServiceTask : public Task {
81 public:
82  ResetDefaultProxyConfigServiceTask(
83      net::ProxyConfigService* proxy_config_service)
84      : proxy_config_service_(proxy_config_service) {}
85  virtual ~ResetDefaultProxyConfigServiceTask() {}
86
87  // Task override.
88  virtual void Run() {
89    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
90    net::URLRequestContextGetter* getter = Profile::GetDefaultRequestContext();
91    DCHECK(getter);
92    if (getter) {
93      getter->GetURLRequestContext()->proxy_service()->ResetConfigService(
94          proxy_config_service_.release());
95    }
96  }
97
98 private:
99  scoped_ptr<net::ProxyConfigService> proxy_config_service_;
100
101  DISALLOW_COPY_AND_ASSIGN(ResetDefaultProxyConfigServiceTask);
102};
103
104}  // namespace
105
106class LoginUtilsImpl : public LoginUtils,
107                       public ProfileManager::Observer {
108 public:
109  LoginUtilsImpl()
110      : background_view_(NULL) {
111  }
112
113  virtual void PrepareProfile(
114      const std::string& username,
115      const std::string& password,
116      const GaiaAuthConsumer::ClientLoginResult& credentials,
117      bool pending_requests,
118      LoginUtils::Delegate* delegate);
119
120  // Invoked after the tmpfs is successfully mounted.
121  // Launches a browser in the incognito mode.
122  virtual void CompleteOffTheRecordLogin(const GURL& start_url);
123
124  // Invoked when the user is logging in for the first time, or is logging in as
125  // a guest user.
126  virtual void SetFirstLoginPrefs(PrefService* prefs);
127
128  // Creates and returns the authenticator to use. The caller owns the returned
129  // Authenticator and must delete it when done.
130  virtual Authenticator* CreateAuthenticator(LoginStatusConsumer* consumer);
131
132  // Warms the url used by authentication.
133  virtual void PrewarmAuthentication();
134
135  // Given the credentials try to exchange them for
136  // full-fledged Google authentication cookies.
137  virtual void FetchCookies(
138      Profile* profile,
139      const GaiaAuthConsumer::ClientLoginResult& credentials);
140
141  // Supply credentials for sync and others to use.
142  virtual void FetchTokens(
143      Profile* profile,
144      const GaiaAuthConsumer::ClientLoginResult& credentials);
145
146  // Sets the current background view.
147  virtual void SetBackgroundView(chromeos::BackgroundView* background_view);
148
149  // Gets the current background view.
150  virtual chromeos::BackgroundView* GetBackgroundView();
151
152  // ProfileManager::Observer implementation:
153  virtual void OnProfileCreated(Profile* profile);
154
155 protected:
156  virtual std::string GetOffTheRecordCommandLine(
157      const GURL& start_url,
158      const CommandLine& base_command_line,
159      CommandLine *command_line);
160
161 private:
162  // Check user's profile for kApplicationLocale setting.
163  void RespectLocalePreference(Profile* pref);
164
165  // The current background view.
166  chromeos::BackgroundView* background_view_;
167
168  std::string username_;
169  std::string password_;
170  GaiaAuthConsumer::ClientLoginResult credentials_;
171  bool pending_requests_;
172
173  // Delegate to be fired when the profile will be prepared.
174  LoginUtils::Delegate* delegate_;
175
176  DISALLOW_COPY_AND_ASSIGN(LoginUtilsImpl);
177};
178
179class LoginUtilsWrapper {
180 public:
181  static LoginUtilsWrapper* GetInstance() {
182    return Singleton<LoginUtilsWrapper>::get();
183  }
184
185  LoginUtils* get() {
186    base::AutoLock create(create_lock_);
187    if (!ptr_.get())
188      reset(new LoginUtilsImpl);
189    return ptr_.get();
190  }
191
192  void reset(LoginUtils* ptr) {
193    ptr_.reset(ptr);
194  }
195
196 private:
197  friend struct DefaultSingletonTraits<LoginUtilsWrapper>;
198
199  LoginUtilsWrapper() {}
200
201  base::Lock create_lock_;
202  scoped_ptr<LoginUtils> ptr_;
203
204  DISALLOW_COPY_AND_ASSIGN(LoginUtilsWrapper);
205};
206
207void LoginUtilsImpl::PrepareProfile(
208    const std::string& username,
209    const std::string& password,
210    const GaiaAuthConsumer::ClientLoginResult& credentials,
211    bool pending_requests,
212    LoginUtils::Delegate* delegate) {
213  BootTimesLoader* btl = BootTimesLoader::Get();
214
215  VLOG(1) << "Completing login for " << username;
216  btl->AddLoginTimeMarker("CompletingLogin", false);
217
218  if (CrosLibrary::Get()->EnsureLoaded()) {
219    CrosLibrary::Get()->GetLoginLibrary()->StartSession(username, "");
220    btl->AddLoginTimeMarker("StartedSession", false);
221  }
222
223  UserManager::Get()->UserLoggedIn(username);
224  btl->AddLoginTimeMarker("UserLoggedIn", false);
225
226  // Switch log file as soon as possible.
227  logging::RedirectChromeLogging(*(CommandLine::ForCurrentProcess()));
228  btl->AddLoginTimeMarker("LoggingRedirected", false);
229
230  username_ = username;
231  password_ = password;
232  credentials_ = credentials;
233  pending_requests_ = pending_requests;
234  delegate_ = delegate;
235
236  // The default profile will have been changed because the ProfileManager
237  // will process the notification that the UserManager sends out.
238  ProfileManager::CreateDefaultProfileAsync(this);
239}
240
241void LoginUtilsImpl::OnProfileCreated(Profile* profile) {
242  CHECK(profile);
243
244  BootTimesLoader* btl = BootTimesLoader::Get();
245  btl->AddLoginTimeMarker("UserProfileGotten", false);
246
247  // Change the proxy configuration service of the default request context to
248  // use the preference configuration from the logged-in profile. This ensures
249  // that requests done through the default context use the proxy configuration
250  // provided by configuration policy.
251  //
252  // Note: Many of the clients of the default request context should probably be
253  // fixed to use the request context of the profile they are associated with.
254  // This includes preconnect, autofill, metrics service to only name a few;
255  // see http://code.google.com/p/chromium/issues/detail?id=64339 for details.
256  //
257  // TODO(mnissler) Revisit when support for device-specific policy arrives, at
258  // which point the default request context can directly be managed through
259  // device policy.
260  net::ProxyConfigService* proxy_config_service =
261      new PrefProxyConfigService(
262          profile->GetProxyConfigTracker(),
263          new chromeos::ProxyConfigService(
264              g_browser_process->chromeos_proxy_config_service_impl()));
265  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
266                          new ResetDefaultProxyConfigServiceTask(
267                              proxy_config_service));
268
269  // Since we're doing parallel authentication, only new user sign in
270  // would perform online auth before calling PrepareProfile.
271  // For existing users there's usually a pending online auth request.
272  // Cookies will be fetched after it's is succeeded.
273  if (!pending_requests_) {
274    FetchCookies(profile, credentials_);
275  }
276
277  // Init extension event routers; this normally happens in browser_main
278  // but on Chrome OS it has to be deferred until the user finishes
279  // logging in and the profile is not OTR.
280  if (profile->GetExtensionService() &&
281      profile->GetExtensionService()->extensions_enabled()) {
282    profile->GetExtensionService()->InitEventRouters();
283  }
284  btl->AddLoginTimeMarker("ExtensionsServiceStarted", false);
285
286  // Supply credentials for sync and others to use. Load tokens from disk.
287  TokenService* token_service = profile->GetTokenService();
288  token_service->Initialize(GaiaConstants::kChromeOSSource,
289                            profile);
290  token_service->LoadTokensFromDB();
291
292  // For existing users there's usually a pending online auth request.
293  // Tokens will be fetched after it's is succeeded.
294  if (!pending_requests_) {
295    FetchTokens(profile, credentials_);
296  }
297  btl->AddLoginTimeMarker("TokensGotten", false);
298
299  // Set the CrOS user by getting this constructor run with the
300  // user's email on first retrieval.
301  profile->GetProfileSyncService(username_)->SetPassphrase(password_,
302                                                           false,
303                                                           true);
304  btl->AddLoginTimeMarker("SyncStarted", false);
305
306  // Own TPM device if, for any reason, it has not been done in EULA
307  // wizard screen.
308  if (CrosLibrary::Get()->EnsureLoaded()) {
309    CryptohomeLibrary* cryptohome = CrosLibrary::Get()->GetCryptohomeLibrary();
310    if (cryptohome->TpmIsEnabled() && !cryptohome->TpmIsBeingOwned()) {
311      if (cryptohome->TpmIsOwned()) {
312        cryptohome->TpmClearStoredPassword();
313      } else {
314        cryptohome->TpmCanAttemptOwnership();
315      }
316    }
317  }
318  btl->AddLoginTimeMarker("TPMOwned", false);
319
320  RespectLocalePreference(profile);
321
322  if (UserManager::Get()->current_user_is_new()) {
323    SetFirstLoginPrefs(profile->GetPrefs());
324  }
325
326  // Enable/disable plugins based on user preferences.
327  PluginUpdater::GetInstance()->UpdatePluginGroupsStateFromPrefs(profile);
328  btl->AddLoginTimeMarker("PluginsStateUpdated", false);
329
330  // We suck. This is a hack since we do not have the enterprise feature
331  // done yet to pull down policies from the domain admin. We'll take this
332  // out when we get that done properly.
333  // TODO(xiyuan): Remove this once enterprise feature is ready.
334  if (EndsWith(username_, "@google.com", true)) {
335    PrefService* pref_service = profile->GetPrefs();
336    pref_service->SetBoolean(prefs::kEnableScreenLock, true);
337  }
338
339  profile->OnLogin();
340
341  delegate_->OnProfilePrepared(profile);
342
343  // TODO(altimofeev): Need to sanitize memory used to store password.
344  password_ = "";
345  username_ = "";
346  credentials_ = GaiaAuthConsumer::ClientLoginResult();
347}
348
349void LoginUtilsImpl::FetchCookies(
350    Profile* profile,
351    const GaiaAuthConsumer::ClientLoginResult& credentials) {
352  // Take the credentials passed in and try to exchange them for
353  // full-fledged Google authentication cookies.  This is
354  // best-effort; it's possible that we'll fail due to network
355  // troubles or some such.
356  // CookieFetcher will delete itself once done.
357  CookieFetcher* cf = new CookieFetcher(profile);
358  cf->AttemptFetch(credentials.data);
359  BootTimesLoader::Get()->AddLoginTimeMarker("CookieFetchStarted", false);
360}
361
362void LoginUtilsImpl::FetchTokens(
363    Profile* profile,
364    const GaiaAuthConsumer::ClientLoginResult& credentials) {
365  TokenService* token_service = profile->GetTokenService();
366  token_service->UpdateCredentials(credentials);
367  if (token_service->AreCredentialsValid()) {
368    token_service->StartFetchingTokens();
369  }
370}
371
372void LoginUtilsImpl::RespectLocalePreference(Profile* profile) {
373  DCHECK(profile != NULL);
374  PrefService* prefs = profile->GetPrefs();
375  DCHECK(prefs != NULL);
376  if (g_browser_process == NULL)
377    return;
378
379  std::string pref_locale = prefs->GetString(prefs::kApplicationLocale);
380  if (pref_locale.empty())
381    pref_locale = prefs->GetString(prefs::kApplicationLocaleBackup);
382  if (pref_locale.empty())
383    pref_locale = g_browser_process->GetApplicationLocale();
384  DCHECK(!pref_locale.empty());
385  profile->ChangeAppLocale(pref_locale, Profile::APP_LOCALE_CHANGED_VIA_LOGIN);
386  // Here we don't enable keyboard layouts. Input methods are set up when
387  // the user first logs in. Then the user may customize the input methods.
388  // Hence changing input methods here, just because the user's UI language
389  // is different from the login screen UI language, is not desirable. Note
390  // that input method preferences are synced, so users can use their
391  // farovite input methods as soon as the preferences are synced.
392  LanguageSwitchMenu::SwitchLanguage(pref_locale);
393}
394
395void LoginUtilsImpl::CompleteOffTheRecordLogin(const GURL& start_url) {
396  VLOG(1) << "Completing incognito login";
397
398  UserManager::Get()->OffTheRecordUserLoggedIn();
399
400  if (CrosLibrary::Get()->EnsureLoaded()) {
401    // For guest session we ask session manager to restart Chrome with --bwsi
402    // flag. We keep only some of the arguments of this process.
403    const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess();
404    CommandLine command_line(browser_command_line.GetProgram());
405    std::string cmd_line_str =
406        GetOffTheRecordCommandLine(start_url,
407                                   browser_command_line,
408                                   &command_line);
409
410    CrosLibrary::Get()->GetLoginLibrary()->RestartJob(getpid(), cmd_line_str);
411  }
412}
413
414std::string LoginUtilsImpl::GetOffTheRecordCommandLine(
415    const GURL& start_url,
416    const CommandLine& base_command_line,
417    CommandLine* command_line) {
418  static const char* kForwardSwitches[] = {
419      switches::kEnableLogging,
420      switches::kEnableAcceleratedPlugins,
421      switches::kUseGL,
422      switches::kUserDataDir,
423      switches::kScrollPixels,
424      switches::kEnableGView,
425      switches::kNoFirstRun,
426      switches::kLoginProfile,
427      switches::kCompressSystemFeedback,
428      switches::kDisableSeccompSandbox,
429      switches::kPpapiFlashInProcess,
430      switches::kPpapiFlashPath,
431      switches::kPpapiFlashVersion,
432#if defined(HAVE_XINPUT2)
433      switches::kTouchDevices,
434#endif
435  };
436  command_line->CopySwitchesFrom(base_command_line,
437                                 kForwardSwitches,
438                                 arraysize(kForwardSwitches));
439  command_line->AppendSwitch(switches::kGuestSession);
440  command_line->AppendSwitch(switches::kIncognito);
441  command_line->AppendSwitchASCII(switches::kLoggingLevel,
442                                 kGuestModeLoggingLevel);
443
444  command_line->AppendSwitchASCII(switches::kLoginUser, kGuestUserName);
445
446  if (start_url.is_valid())
447    command_line->AppendArg(start_url.spec());
448
449  // Override the value of the homepage that is set in first run mode.
450  // TODO(altimofeev): extend action of the |kNoFirstRun| to cover this case.
451  command_line->AppendSwitchASCII(
452      switches::kHomePage,
453      GURL(chrome::kChromeUINewTabURL).spec());
454
455  std::string cmd_line_str = command_line->command_line_string();
456  // Special workaround for the arguments that should be quoted.
457  // Copying switches won't be needed when Guest mode won't need restart
458  // http://crosbug.com/6924
459  if (base_command_line.HasSwitch(switches::kRegisterPepperPlugins)) {
460    cmd_line_str += base::StringPrintf(
461        kSwitchFormatString,
462        switches::kRegisterPepperPlugins,
463        base_command_line.GetSwitchValueNative(
464            switches::kRegisterPepperPlugins).c_str());
465  }
466
467  return cmd_line_str;
468}
469
470void LoginUtilsImpl::SetFirstLoginPrefs(PrefService* prefs) {
471  VLOG(1) << "Setting first login prefs";
472  BootTimesLoader* btl = BootTimesLoader::Get();
473  std::string locale = g_browser_process->GetApplicationLocale();
474
475  // First, we'll set kLanguagePreloadEngines.
476  InputMethodLibrary* library = CrosLibrary::Get()->GetInputMethodLibrary();
477  std::vector<std::string> input_method_ids;
478  input_method::GetFirstLoginInputMethodIds(locale,
479                                            library->current_input_method(),
480                                            &input_method_ids);
481  // Save the input methods in the user's preferences.
482  StringPrefMember language_preload_engines;
483  language_preload_engines.Init(prefs::kLanguagePreloadEngines,
484                                prefs, NULL);
485  language_preload_engines.SetValue(JoinString(input_method_ids, ','));
486  btl->AddLoginTimeMarker("IMEStarted", false);
487
488  // Second, we'll set kLanguagePreferredLanguages.
489  std::vector<std::string> language_codes;
490  // The current locale should be on the top.
491  language_codes.push_back(locale);
492
493  // Add input method IDs based on the input methods, as there may be
494  // input methods that are unrelated to the current locale. Example: the
495  // hardware keyboard layout xkb:us::eng is used for logging in, but the
496  // UI language is set to French. In this case, we should set "fr,en"
497  // to the preferred languages preference.
498  std::vector<std::string> candidates;
499  input_method::GetLanguageCodesFromInputMethodIds(
500      input_method_ids, &candidates);
501  for (size_t i = 0; i < candidates.size(); ++i) {
502    const std::string& candidate = candidates[i];
503    // Skip if it's already in language_codes.
504    if (std::count(language_codes.begin(), language_codes.end(),
505                   candidate) == 0) {
506      language_codes.push_back(candidate);
507    }
508  }
509  // Save the preferred languages in the user's preferences.
510  StringPrefMember language_preferred_languages;
511  language_preferred_languages.Init(prefs::kLanguagePreferredLanguages,
512                                    prefs, NULL);
513  language_preferred_languages.SetValue(JoinString(language_codes, ','));
514  prefs->ScheduleSavePersistentPrefs();
515}
516
517Authenticator* LoginUtilsImpl::CreateAuthenticator(
518    LoginStatusConsumer* consumer) {
519  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kParallelAuth))
520    return new ParallelAuthenticator(consumer);
521  else
522    return new GoogleAuthenticator(consumer);
523}
524
525// We use a special class for this so that it can be safely leaked if we
526// never connect. At shutdown the order is not well defined, and it's possible
527// for the infrastructure needed to unregister might be unstable and crash.
528class WarmingObserver : public NetworkLibrary::NetworkManagerObserver {
529 public:
530  WarmingObserver() {
531    NetworkLibrary *netlib = CrosLibrary::Get()->GetNetworkLibrary();
532    netlib->AddNetworkManagerObserver(this);
533  }
534
535  // If we're now connected, prewarm the auth url.
536  void OnNetworkManagerChanged(NetworkLibrary* netlib) {
537    if (netlib->Connected()) {
538      const int kConnectionsNeeded = 1;
539      chrome_browser_net::PreconnectOnUIThread(
540          GURL(GaiaAuthFetcher::kClientLoginUrl),
541          chrome_browser_net::UrlInfo::EARLY_LOAD_MOTIVATED,
542          kConnectionsNeeded);
543      netlib->RemoveNetworkManagerObserver(this);
544      delete this;
545    }
546  }
547};
548
549void LoginUtilsImpl::PrewarmAuthentication() {
550  if (CrosLibrary::Get()->EnsureLoaded()) {
551    NetworkLibrary *network = CrosLibrary::Get()->GetNetworkLibrary();
552    if (network->Connected()) {
553      const int kConnectionsNeeded = 1;
554      chrome_browser_net::PreconnectOnUIThread(
555          GURL(GaiaAuthFetcher::kClientLoginUrl),
556          chrome_browser_net::UrlInfo::EARLY_LOAD_MOTIVATED,
557          kConnectionsNeeded);
558    } else {
559      new WarmingObserver();
560    }
561  }
562}
563
564void LoginUtilsImpl::SetBackgroundView(BackgroundView* background_view) {
565  background_view_ = background_view;
566}
567
568BackgroundView* LoginUtilsImpl::GetBackgroundView() {
569  return background_view_;
570}
571
572LoginUtils* LoginUtils::Get() {
573  return LoginUtilsWrapper::GetInstance()->get();
574}
575
576void LoginUtils::Set(LoginUtils* mock) {
577  LoginUtilsWrapper::GetInstance()->reset(mock);
578}
579
580void LoginUtils::DoBrowserLaunch(Profile* profile) {
581  BootTimesLoader::Get()->AddLoginTimeMarker("BrowserLaunched", false);
582
583  // Update command line in case loose values were added.
584  CommandLine::ForCurrentProcess()->InitFromArgv(
585      CommandLine::ForCurrentProcess()->argv());
586
587  VLOG(1) << "Launching browser...";
588  BrowserInit browser_init;
589  int return_code;
590  browser_init.LaunchBrowser(*CommandLine::ForCurrentProcess(),
591                             profile,
592                             FilePath(),
593                             true,
594                             &return_code);
595}
596
597}  // namespace chromeos
598