C++程序  |  282行  |  11.98 KB

// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_CHROMEOS_LOGIN_PARALLEL_AUTHENTICATOR_H_
#define CHROME_BROWSER_CHROMEOS_LOGIN_PARALLEL_AUTHENTICATOR_H_
#pragma once

#include <string>
#include <vector>

#include "base/basictypes.h"
#include "base/gtest_prod_util.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "chrome/browser/chromeos/cros/cros_library.h"
#include "chrome/browser/chromeos/cros/cryptohome_library.h"
#include "chrome/browser/chromeos/login/authenticator.h"
#include "chrome/browser/chromeos/login/auth_attempt_state.h"
#include "chrome/browser/chromeos/login/auth_attempt_state_resolver.h"
#include "chrome/browser/chromeos/login/test_attempt_state.h"
#include "chrome/browser/chromeos/login/cryptohome_op.h"
#include "chrome/browser/chromeos/login/online_attempt.h"
#include "chrome/common/net/gaia/gaia_auth_consumer.h"

class GaiaAuthFetcher;
class LoginFailure;
class Profile;

namespace base {
class Lock;
}

namespace chromeos {

class LoginStatusConsumer;
class ParallelAuthenticator;
class ResolveChecker;

// Authenticates a Chromium OS user against the Google Accounts ClientLogin API.
//
// Simultaneously attempts authentication both offline and online, failing over
// to the "localaccount" in the event that authentication fails.
//
// At a high, level, here's what happens:
// AuthenticateToLogin() creates an OnlineAttempt and a CryptohomeOp that
// attempt to perform online and offline login simultaneously.  When one of
// these completes, it will store results in a AuthAttemptState owned by
// ParallelAuthenticator and then call Resolve().  Resolve() will attempt to
// determine which AuthState we're in, based on the info at hand.
// It then triggers further action based on the calculated AuthState; this
// further action might include calling back the passed-in LoginStatusConsumer
// to signal that login succeeded or failed, waiting for more outstanding
// operations to complete, or triggering some more CryptohomeOps.
class ParallelAuthenticator : public Authenticator,
                              public AuthAttemptStateResolver {
 public:
  enum AuthState {
    CONTINUE,        // State indeterminate; try again when more info available.
    NO_MOUNT,        // Cryptohome doesn't exist yet.
    FAILED_MOUNT,    // Failed to mount existing cryptohome.
    FAILED_REMOVE,   // Failed to remove existing cryptohome.
    FAILED_TMPFS,    // Failed to mount tmpfs for guest user
    CREATE_NEW,      // Need to create cryptohome for a new user.
    RECOVER_MOUNT,   // After RecoverEncryptedData, mount cryptohome.
    POSSIBLE_PW_CHANGE,  // Offline login failed, user may have changed pw.
    NEED_NEW_PW,     // User changed pw, and we have the old one.
    NEED_OLD_PW,     // User changed pw, and we have the new one.
    HAVE_NEW_PW,     // We have verified new pw, time to migrate key.
    OFFLINE_LOGIN,   // Login succeeded offline.
    ONLINE_LOGIN,    // Offline and online login succeeded.
    UNLOCK,          // Screen unlock succeeded.
    LOCAL_LOGIN,     // Login with localaccount succeded.
    ONLINE_FAILED,   // Online login disallowed, but offline succeeded.
    LOGIN_FAILED     // Login denied.
  };

  explicit ParallelAuthenticator(LoginStatusConsumer* consumer);
  virtual ~ParallelAuthenticator();

  // Given a |username| and |password|, this method attempts to authenticate to
  // the Google accounts servers and your Chrome OS device simultaneously.
  // As soon as we have successfully mounted the encrypted home directory for
  // |username|, we will call consumer_->OnLoginSuccess() with |username| and a
  // vector of authentication cookies.  If we're still waiting for an online
  // result at that time, we'll also pass back a flag indicating that more
  // callbacks are on the way; if not, we pass back false.  When the pending
  // request completes, either consumer_->OnLoginSuccess() with an indication
  // that no more requests are outstanding will be called, or
  // consumer_->OnLoginFailure() if appropriate.
  //
  // Upon failure to login (online fails, then offline fails;
  // offline fails, then online fails) consumer_->OnLoginFailure() is called
  // with an error message.
  //
  // In the event that we see an online success and then an offline failure,
  // consumer_->OnPasswordChangeDetected() is called.
  //
  // Uses |profile| when doing URL fetches.
  // Optionally could pass CAPTCHA challenge token - |login_token| and
  // |login_captcha| string that user has entered.
  //
  // NOTE: We do not allow HOSTED accounts to log in.  In the event that
  // we are asked to authenticate valid HOSTED account creds, we will
  // call OnLoginFailure() with HOSTED_NOT_ALLOWED.
  //
  // Returns true if the attempt gets sent successfully and false if not.
  bool AuthenticateToLogin(Profile* profile,
                           const std::string& username,
                           const std::string& password,
                           const std::string& login_token,
                           const std::string& login_captcha);

  // Given a |username| and |password|, this method attempts to
  // authenticate to the cached credentials. This will never contact
  // the server even if it's online. The auth result is sent to
  // LoginStatusConsumer in a same way as AuthenticateToLogin does.
  bool AuthenticateToUnlock(const std::string& username,
                            const std::string& password);

  // Initiates incognito ("browse without signing in") login.
  // Mounts tmpfs and notifies consumer on the success/failure.
  void LoginOffTheRecord();

  // These methods must be called on the UI thread, as they make DBus calls
  // and also call back to the login UI.
  void OnLoginSuccess(const GaiaAuthConsumer::ClientLoginResult& credentials,
                      bool request_pending);
  void OnOffTheRecordLoginSuccess();
  void OnPasswordChangeDetected(
      const GaiaAuthConsumer::ClientLoginResult& credentials);
  void OnLoginFailure(const LoginFailure& error);

  void RecoverEncryptedData(
      const std::string& old_password,
      const GaiaAuthConsumer::ClientLoginResult& credentials);
  void ResyncEncryptedData(
      const GaiaAuthConsumer::ClientLoginResult& credentials);
  void RetryAuth(Profile* profile,
                 const std::string& username,
                 const std::string& password,
                 const std::string& login_token,
                 const std::string& login_captcha);

  // Call this on the FILE thread.
  void CheckLocalaccount(const LoginFailure& error);

  // Attempts to make a decision and call back |consumer_| based on
  // the state we have gathered at the time of call.  If a decision
  // can't be made, defers until the next time this is called.
  // When a decision is made, will call back to |consumer_| on the UI thread.
  //
  // Must be called on the IO thread.
  void Resolve();

 private:
  // Returns the AuthState we're in, given the status info we have at
  // the time of call.
  // Must be called on the IO thread.
  AuthState ResolveState();

  // Helper for ResolveState().
  // Given that we're attempting to auth the user again, with a new password,
  // determine which state we're in.  Returns CONTINUE if no resolution.
  // Must be called on the IO thread.
  AuthState ResolveReauthState();

  // Helper for ResolveState().
  // Given that some cryptohome operation has failed, determine which of the
  // possible failure states we're in.
  // Must be called on the IO thread.
  AuthState ResolveCryptohomeFailureState();

  // Helper for ResolveState().
  // Given that some cryptohome operation has succeeded, determine which of
  // the possible states we're in.
  // Must be called on the IO thread.
  AuthState ResolveCryptohomeSuccessState();

  // Helper for ResolveState().
  // Given that some online auth operation has failed, determine which of the
  // possible failure states we're in.  Handles both failure to complete and
  // actual failure responses from the server.
  // Must be called on the IO thread.
  AuthState ResolveOnlineFailureState(AuthState offline_state);

  // Helper for ResolveState().
  // Given that some online auth operation has succeeded, determine which of
  // the possible success states we're in.
  // Must be called on the IO thread.
  AuthState ResolveOnlineSuccessState(AuthState offline_state);

  // Used for testing.
  void set_attempt_state(TestAttemptState* new_state) {  // takes ownership.
    current_state_.reset(new_state);
  }

  // Resets |current_state_| and then posts a task to the UI thread to
  // Initiate() |to_initiate|.
  // Call this method on the IO thread.
  void ResyncRecoverHelper(CryptohomeOp* to_initiate);

  // If we don't have the system salt yet, loads it from the CryptohomeLibrary.
  void LoadSystemSalt();

  // If we haven't already, looks in a file called |filename| next to
  // the browser executable for a "localaccount" name, and retrieves it
  // if one is present.  If someone attempts to authenticate with this
  // username, we will mount a tmpfs for them and let them use the
  // browser.
  // Should only be called on the FILE thread.
  void LoadLocalaccount(const std::string& filename);

  void SetLocalaccount(const std::string& new_name);

  // Stores a hash of |password|, salted with the ascii of |system_salt_|.
  std::string HashPassword(const std::string& password);

  // Returns the ascii encoding of the system salt.
  std::string SaltAsAscii();

  // Converts the binary data |binary| into an ascii hex string and stores
  // it in |hex_string|.  Not guaranteed to be NULL-terminated.
  // Returns false if |hex_string| is too small, true otherwise.
  static bool BinaryToHex(const std::vector<unsigned char>& binary,
                          const unsigned int binary_len,
                          char* hex_string,
                          const unsigned int len);

  // Name of a file, next to chrome, that contains a local account username.
  static const char kLocalaccountFile[];

  // Milliseconds until we timeout our attempt to hit ClientLogin.
  static const int kClientLoginTimeoutMs;

  // Milliseconds until we re-check whether we've gotten the localaccount name.
  static const int kLocalaccountRetryIntervalMs;

  // Handles all net communications with Gaia.
  scoped_ptr<GaiaAuthFetcher> gaia_authenticator_;

  // Used when we need to try online authentication again, after successful
  // mount, but failed online login.
  scoped_ptr<AuthAttemptState> reauth_state_;

  scoped_ptr<AuthAttemptState> current_state_;
  scoped_refptr<OnlineAttempt> current_online_;
  scoped_refptr<CryptohomeOp> mounter_;
  scoped_refptr<CryptohomeOp> key_migrator_;
  scoped_refptr<CryptohomeOp> data_remover_;
  scoped_refptr<CryptohomeOp> guest_mounter_;
  scoped_refptr<CryptohomeOp> key_checker_;

  std::string ascii_hash_;
  chromeos::CryptohomeBlob system_salt_;

  // When the user has changed her password, but gives us the old one, we will
  // be able to mount her cryptohome, but online authentication will fail.
  // This allows us to present the same behavior to the caller, regardless
  // of the order in which we receive these results.
  bool already_reported_success_;
  base::Lock success_lock_;  // A lock around already_reported_success_.

  // Status relating to the local "backdoor" account.
  std::string localaccount_;
  bool checked_for_localaccount_;  // Needed because empty localaccount_ is ok.
  base::Lock localaccount_lock_;  // A lock around checked_for_localaccount_.

  friend class ResolveChecker;
  friend class ParallelAuthenticatorTest;
  FRIEND_TEST_ALL_PREFIXES(ParallelAuthenticatorTest, SaltToAscii);
  FRIEND_TEST_ALL_PREFIXES(ParallelAuthenticatorTest, ReadLocalaccount);
  FRIEND_TEST_ALL_PREFIXES(ParallelAuthenticatorTest,
                           ReadLocalaccountTrailingWS);
  FRIEND_TEST_ALL_PREFIXES(ParallelAuthenticatorTest, ReadNoLocalaccount);
  DISALLOW_COPY_AND_ASSIGN(ParallelAuthenticator);
};

}  // namespace chromeos

#endif  // CHROME_BROWSER_CHROMEOS_LOGIN_PARALLEL_AUTHENTICATOR_H_