login_utils.cc revision 513209b27ff55e2841eac0e4120199c23acce758
1// Copyright (c) 2010 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/lock.h"
13#include "base/path_service.h"
14#include "base/scoped_ptr.h"
15#include "base/singleton.h"
16#include "base/string_util.h"
17#include "base/time.h"
18#include "base/utf_string_conversions.h"
19#include "chrome/browser/browser_init.h"
20#include "chrome/browser/browser_process.h"
21#include "chrome/browser/browser_thread.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/external_cookie_handler.h"
26#include "chrome/browser/chromeos/input_method/input_method_util.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/ownership_service.h"
30#include "chrome/browser/chromeos/login/parallel_authenticator.h"
31#include "chrome/browser/chromeos/login/user_image_downloader.h"
32#include "chrome/browser/chromeos/login/user_manager.h"
33#include "chrome/browser/extensions/extensions_service.h"
34#include "chrome/browser/net/gaia/token_service.h"
35#include "chrome/browser/net/preconnect.h"
36#include "chrome/browser/prefs/pref_member.h"
37#include "chrome/browser/profile.h"
38#include "chrome/browser/profile_manager.h"
39#include "chrome/browser/sync/profile_sync_service.h"
40#include "chrome/common/chrome_paths.h"
41#include "chrome/common/chrome_switches.h"
42#include "chrome/common/logging_chrome.h"
43#include "chrome/common/net/gaia/gaia_authenticator2.h"
44#include "chrome/common/net/gaia/gaia_constants.h"
45#include "chrome/common/net/url_request_context_getter.h"
46#include "chrome/common/pref_names.h"
47#include "googleurl/src/gurl.h"
48#include "net/base/cookie_store.h"
49#include "net/url_request/url_request_context.h"
50#include "views/widget/widget_gtk.h"
51
52namespace chromeos {
53
54namespace {
55
56// Prefix for Auth token received from ClientLogin request.
57const char kAuthPrefix[] = "Auth=";
58// Suffix for Auth token received from ClientLogin request.
59const char kAuthSuffix[] = "\n";
60
61}  // namespace
62
63class LoginUtilsImpl : public LoginUtils {
64 public:
65  LoginUtilsImpl()
66      : browser_launch_enabled_(true) {
67  }
68
69  // Invoked after the user has successfully logged in. This launches a browser
70  // and does other bookkeeping after logging in.
71  virtual void CompleteLogin(
72      const std::string& username,
73      const std::string& password,
74      const GaiaAuthConsumer::ClientLoginResult& credentials);
75
76  // Invoked after the tmpfs is successfully mounted.
77  // Launches a browser in the off the record (incognito) mode.
78  virtual void CompleteOffTheRecordLogin(const GURL& start_url);
79
80  // Creates and returns the authenticator to use. The caller owns the returned
81  // Authenticator and must delete it when done.
82  virtual Authenticator* CreateAuthenticator(LoginStatusConsumer* consumer);
83
84  // Used to postpone browser launch via DoBrowserLaunch() if some post
85  // login screen is to be shown.
86  virtual void EnableBrowserLaunch(bool enable);
87
88  // Returns if browser launch enabled now or not.
89  virtual bool IsBrowserLaunchEnabled() const;
90
91  // Warms the url used by authentication.
92  virtual void PrewarmAuthentication();
93
94 private:
95  // Indicates if DoBrowserLaunch will actually launch the browser or not.
96  bool browser_launch_enabled_;
97
98  DISALLOW_COPY_AND_ASSIGN(LoginUtilsImpl);
99};
100
101class LoginUtilsWrapper {
102 public:
103  LoginUtilsWrapper() {}
104
105  LoginUtils* get() {
106    AutoLock create(create_lock_);
107    if (!ptr_.get())
108      reset(new LoginUtilsImpl);
109    return ptr_.get();
110  }
111
112  void reset(LoginUtils* ptr) {
113    ptr_.reset(ptr);
114  }
115
116 private:
117  Lock create_lock_;
118  scoped_ptr<LoginUtils> ptr_;
119
120  DISALLOW_COPY_AND_ASSIGN(LoginUtilsWrapper);
121};
122
123void LoginUtilsImpl::CompleteLogin(
124    const std::string& username,
125    const std::string& password,
126    const GaiaAuthConsumer::ClientLoginResult& credentials) {
127  BootTimesLoader* btl = BootTimesLoader::Get();
128
129  VLOG(1) << "Completing login for " << username;
130  btl->AddLoginTimeMarker("CompletingLogin", false);
131
132  if (CrosLibrary::Get()->EnsureLoaded()) {
133    CrosLibrary::Get()->GetLoginLibrary()->StartSession(username, "");
134    btl->AddLoginTimeMarker("StartedSession", false);
135  }
136
137  bool first_login = !UserManager::Get()->IsKnownUser(username);
138  UserManager::Get()->UserLoggedIn(username);
139  btl->AddLoginTimeMarker("UserLoggedIn", false);
140
141  // Now get the new profile.
142  FilePath user_data_dir;
143  PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
144  ProfileManager* profile_manager = g_browser_process->profile_manager();
145
146  // Switch log file as soon as possible.
147  logging::RedirectChromeLogging(*(CommandLine::ForCurrentProcess()));
148  btl->AddLoginTimeMarker("LoggingRedirected", false);
149
150  // The default profile will have been changed because the ProfileManager
151  // will process the notification that the UserManager sends out.
152  Profile* profile = profile_manager->GetDefaultProfile(user_data_dir);
153  btl->AddLoginTimeMarker("UserProfileGotten", false);
154
155  // Take the credentials passed in and try to exchange them for
156  // full-fledged Google authentication cookies.  This is
157  // best-effort; it's possible that we'll fail due to network
158  // troubles or some such.  Either way, |cf| will call
159  // DoBrowserLaunch on the UI thread when it's done, and then
160  // delete itself.
161  CookieFetcher* cf = new CookieFetcher(profile);
162  cf->AttemptFetch(credentials.data);
163  btl->AddLoginTimeMarker("CookieFetchStarted", false);
164
165  // Init extension event routers; this normally happens in browser_main
166  // but on Chrome OS it has to be deferred until the user finishes
167  // logging in and the profile is not OTR.
168  if (profile->GetExtensionsService() &&
169      profile->GetExtensionsService()->extensions_enabled()) {
170    profile->GetExtensionsService()->InitEventRouters();
171  }
172  btl->AddLoginTimeMarker("ExtensionsServiceStarted", false);
173
174  // Supply credentials for sync and others to use. Load tokens from disk.
175  TokenService* token_service = profile->GetTokenService();
176  token_service->Initialize(GaiaConstants::kChromeOSSource,
177                            profile);
178  token_service->LoadTokensFromDB();
179  token_service->UpdateCredentials(credentials);
180  if (token_service->AreCredentialsValid()) {
181    token_service->StartFetchingTokens();
182  }
183  btl->AddLoginTimeMarker("TokensGotten", false);
184
185  // Set the CrOS user by getting this constructor run with the
186  // user's email on first retrieval.
187  profile->GetProfileSyncService(username)->SetPassphrase(password);
188  btl->AddLoginTimeMarker("SyncStarted", false);
189
190  // Attempt to take ownership; this will fail if device is already owned.
191  OwnershipService::GetSharedInstance()->StartTakeOwnershipAttempt(
192      UserManager::Get()->logged_in_user().email());
193  // Own TPM device if, for any reason, it has not been done in EULA
194  // wizard screen.
195  if (CrosLibrary::Get()->EnsureLoaded()) {
196    CryptohomeLibrary* cryptohome = CrosLibrary::Get()->GetCryptohomeLibrary();
197    if (cryptohome->TpmIsEnabled() && !cryptohome->TpmIsBeingOwned()) {
198      if (cryptohome->TpmIsOwned()) {
199        cryptohome->TpmClearStoredPassword();
200      } else {
201        cryptohome->TpmCanAttemptOwnership();
202      }
203    }
204  }
205  btl->AddLoginTimeMarker("TPMOwned", false);
206
207  static const char kFallbackInputMethodLocale[] = "en-US";
208  if (first_login) {
209    std::string locale(g_browser_process->GetApplicationLocale());
210    // Add input methods based on the application locale when the user first
211    // logs in. For instance, if the user chooses Japanese as the UI
212    // language at the first login, we'll add input methods associated with
213    // Japanese, such as mozc.
214    if (locale != kFallbackInputMethodLocale) {
215      StringPrefMember language_preload_engines;
216      language_preload_engines.Init(prefs::kLanguagePreloadEngines,
217                                    profile->GetPrefs(), NULL);
218      StringPrefMember language_preferred_languages;
219      language_preferred_languages.Init(prefs::kLanguagePreferredLanguages,
220                                        profile->GetPrefs(), NULL);
221
222      std::string preload_engines(language_preload_engines.GetValue());
223      std::vector<std::string> input_method_ids;
224      input_method::GetInputMethodIdsFromLanguageCode(
225          locale, input_method::kAllInputMethods, &input_method_ids);
226      if (!input_method_ids.empty()) {
227        if (!preload_engines.empty())
228          preload_engines += ',';
229        preload_engines += input_method_ids[0];
230      }
231      language_preload_engines.SetValue(preload_engines);
232
233      // Add the UI language to the preferred languages the user first logs in.
234      std::string preferred_languages(locale);
235      preferred_languages += ",";
236      preferred_languages += kFallbackInputMethodLocale;
237      language_preferred_languages.SetValue(preferred_languages);
238      btl->AddLoginTimeMarker("IMESTarted", false);
239    }
240  }
241
242  // We suck. This is a hack since we do not have the enterprise feature
243  // done yet to pull down policies from the domain admin. We'll take this
244  // out when we get that done properly.
245  // TODO(xiyuan): Remove this once enterprise feature is ready.
246  if (EndsWith(username, "@google.com", true)) {
247    PrefService* pref_service = profile->GetPrefs();
248    pref_service->SetBoolean(prefs::kEnableScreenLock, true);
249  }
250
251  DoBrowserLaunch(profile);
252}
253
254void LoginUtilsImpl::CompleteOffTheRecordLogin(const GURL& start_url) {
255  VLOG(1) << "Completing off the record login";
256
257  UserManager::Get()->OffTheRecordUserLoggedIn();
258
259  if (CrosLibrary::Get()->EnsureLoaded()) {
260    // For guest session we ask session manager to restart Chrome with --bwsi
261    // flag. We keep only some of the arguments of this process.
262    static const char* kForwardSwitches[] = {
263        switches::kLoggingLevel,
264        switches::kEnableLogging,
265        switches::kUserDataDir,
266        switches::kScrollPixels,
267        switches::kEnableGView,
268        switches::kNoFirstRun,
269        switches::kLoginProfile
270    };
271    const CommandLine& browser_command_line =
272        *CommandLine::ForCurrentProcess();
273    CommandLine command_line(browser_command_line.GetProgram());
274    command_line.CopySwitchesFrom(browser_command_line,
275                                  kForwardSwitches,
276                                  arraysize(kForwardSwitches));
277    command_line.AppendSwitch(switches::kGuestSession);
278    command_line.AppendSwitch(switches::kIncognito);
279    command_line.AppendSwitch(switches::kEnableTabbedOptions);
280    command_line.AppendSwitchASCII(
281        switches::kLoginUser,
282        UserManager::Get()->logged_in_user().email());
283    if (start_url.is_valid())
284      command_line.AppendArg(start_url.spec());
285    CrosLibrary::Get()->GetLoginLibrary()->RestartJob(
286        getpid(),
287        command_line.command_line_string());
288  }
289}
290
291Authenticator* LoginUtilsImpl::CreateAuthenticator(
292    LoginStatusConsumer* consumer) {
293  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kParallelAuth))
294    return new ParallelAuthenticator(consumer);
295  else
296    return new GoogleAuthenticator(consumer);
297}
298
299void LoginUtilsImpl::EnableBrowserLaunch(bool enable) {
300  browser_launch_enabled_ = enable;
301}
302
303bool LoginUtilsImpl::IsBrowserLaunchEnabled() const {
304  return browser_launch_enabled_;
305}
306
307// We use a special class for this so that it can be safely leaked if we
308// never connect. At shutdown the order is not well defined, and it's possible
309// for the infrastructure needed to unregister might be unstable and crash.
310class WarmingObserver : public NetworkLibrary::NetworkManagerObserver {
311 public:
312  WarmingObserver() {
313    NetworkLibrary *netlib = CrosLibrary::Get()->GetNetworkLibrary();
314    netlib->AddNetworkManagerObserver(this);
315  }
316
317  // If we're now connected, prewarm the auth url.
318  void OnNetworkManagerChanged(NetworkLibrary* netlib) {
319    if (netlib->Connected()) {
320      chrome_browser_net::Preconnect::PreconnectOnUIThread(
321          GURL(GaiaAuthenticator2::kClientLoginUrl),
322          chrome_browser_net::UrlInfo::EARLY_LOAD_MOTIVATED);
323      netlib->RemoveNetworkManagerObserver(this);
324      delete this;
325    }
326  }
327};
328
329void LoginUtilsImpl::PrewarmAuthentication() {
330  if (CrosLibrary::Get()->EnsureLoaded()) {
331    NetworkLibrary *network = CrosLibrary::Get()->GetNetworkLibrary();
332    if (network->Connected()) {
333      chrome_browser_net::Preconnect::PreconnectOnUIThread(
334          GURL(GaiaAuthenticator2::kClientLoginUrl),
335          chrome_browser_net::UrlInfo::EARLY_LOAD_MOTIVATED);
336    } else {
337      new WarmingObserver();
338    }
339  }
340}
341
342LoginUtils* LoginUtils::Get() {
343  return Singleton<LoginUtilsWrapper>::get()->get();
344}
345
346void LoginUtils::Set(LoginUtils* mock) {
347  Singleton<LoginUtilsWrapper>::get()->reset(mock);
348}
349
350void LoginUtils::DoBrowserLaunch(Profile* profile) {
351  BootTimesLoader::Get()->AddLoginTimeMarker("BrowserLaunched", false);
352  // Browser launch was disabled due to some post login screen.
353  if (!LoginUtils::Get()->IsBrowserLaunchEnabled())
354    return;
355
356  // Update command line in case loose values were added.
357  CommandLine::ForCurrentProcess()->InitFromArgv(
358      CommandLine::ForCurrentProcess()->argv());
359
360  VLOG(1) << "Launching browser...";
361  BrowserInit browser_init;
362  int return_code;
363  browser_init.LaunchBrowser(*CommandLine::ForCurrentProcess(),
364                             profile,
365                             FilePath(),
366                             true,
367                             &return_code);
368}
369
370}  // namespace chromeos
371