chromium/chrome/credential_provider/gaiacp/gaia_credential_base.h

// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_CREDENTIAL_PROVIDER_GAIACP_GAIA_CREDENTIAL_BASE_H_
#define CHROME_CREDENTIAL_PROVIDER_GAIACP_GAIA_CREDENTIAL_BASE_H_

#include "chrome/credential_provider/gaiacp/stdafx.h"

#include <wrl/client.h>

#include <memory>
#include <string>

#include "base/values.h"
#include "base/win/scoped_handle.h"
#include "base/win/scoped_process_information.h"
#include "chrome/credential_provider/gaiacp/associated_user_validator.h"
#include "chrome/credential_provider/gaiacp/gaia_credential_provider_i.h"
#include "chrome/credential_provider/gaiacp/gcp_utils.h"
#include "chrome/credential_provider/gaiacp/scoped_handle.h"

namespace base {
class CommandLine;
class FilePath;
}  // namespace base

namespace credential_provider {

class OSProcessManager;

enum FIELDID {
  FID_DESCRIPTION,
  FID_CURRENT_PASSWORD_FIELD,
  FID_SUBMIT,
  FID_FORGOT_PASSWORD_LINK,
  FID_PROVIDER_LOGO,
  FID_PROVIDER_LABEL,
  FIELD_COUNT  // Must be last.
};

// Implementation of an ICredentialProviderCredential backed by a Gaia account.
// This is used as a base class for the COM objects that implement first time
// sign in and password update.
class ATL_NO_VTABLE CGaiaCredentialBase
    : public IGaiaCredential,
      public ICredentialProviderCredential2 {
 public:
  // Called when the DLL is registered or unregistered.
  static HRESULT OnDllRegisterServer();
  static HRESULT OnDllUnregisterServer();

  // Perform non-critical post-sign operations after everything is setup here.
  static HRESULT PerformPostSigninActions(const base::Value::Dict& properties,
                                          bool com_initialized);

  // Allocates a BSTR from a DLL string resource given by |id|.
  static BSTR AllocErrorString(UINT id);

  // Allocates a BSTR from a DLL string resource given by |id| replacing the
  // placeholders in the string by the provided replacements.
  static BSTR AllocErrorString(UINT id,
                               const std::vector<std::wstring>& replacements);

  // Gets the directory where the credential provider is installed.
  static HRESULT GetInstallDirectory(base::FilePath* path);

  // Passed to WaitForLoginUI().
  struct UIProcessInfo {
    UIProcessInfo();
    ~UIProcessInfo();

    Microsoft::WRL::ComPtr<IGaiaCredential> credential;
    base::win::ScopedHandle logon_token;
    base::win::ScopedProcessInformation procinfo;
    StdParentHandles parent_handles;
  };

  // Returns true if "enable_cloud_association" registry key is set to 1.
  static bool IsCloudAssociationEnabled();

 protected:
  CGaiaCredentialBase();
  ~CGaiaCredentialBase();

  // Members to access user credentials.
  const Microsoft::WRL::ComPtr<IGaiaCredentialProvider> provider() const {
    return provider_;
  }
  const CComBSTR& get_username() const { return username_; }
  const CComBSTR& get_password() const { return password_; }
  const CComBSTR& get_sid() const { return user_sid_; }
  const CComBSTR& get_current_windows_password() const {
    return current_windows_password_;
  }
  const std::optional<base::Value::Dict>& get_authentication_results() const {
    return authentication_results_;
  }

  // Saves account association and user profile information. Makes various HTTP
  // calls regarding device provisioning and password management.
  static HRESULT PerformActions(const base::Value::Dict& properties);

  // Returns true if the current credentials stored in |username_| and
  // |password_| are valid and should succeed a local Windows logon. This
  // function is successful if we are able to create a logon token with the
  // credentials.
  bool AreCredentialsValid() const;

  // Returns true if some kind of credential information is available so that a
  // sign in can be attempled. Does not guarantee that the sign in will succeed
  // with the current credentials.
  bool CanAttemptWindowsLogon() const;

  // Returns S_OK if the specific password credential is valid for |username_|,
  // S_FALSE if not, and a FAILED hr otherwise.
  HRESULT IsWindowsPasswordValidForStoredUser(BSTR password) const;

  // Updates the UI so that the password field is displayed and also sets the
  // state of the credential to wait for a password.
  void DisplayPasswordField(int password_message);

  // Returns true if GLS is running.
  bool IsGaiaLogonStubRunning() {
    return logon_ui_process_ != INVALID_HANDLE_VALUE;
  }

  // IGaiaCredential
  IFACEMETHODIMP Initialize(IGaiaCredentialProvider* provider) override;
  IFACEMETHODIMP Terminate() override;
  IFACEMETHODIMP OnUserAuthenticated(BSTR authentication_info,
                                     BSTR* status_text) override;
  IFACEMETHODIMP ReportError(LONG status,
                             LONG substatus,
                             BSTR status_text) override;

  // Gets the string value for the given credential UI field.
  virtual HRESULT GetStringValueImpl(DWORD field_id, wchar_t** value);

  // Can be overridden to change the icon used for anonymous tiles when they are
  // shown in the selection list on the side and when they are selected.
  virtual HRESULT GetBitmapValueImpl(DWORD field_id, HBITMAP* phbmp);

  // Resets the state of the credential, forgetting any username or password
  // that may have been set previously.  Derived classes may override to
  // perform more state resetting if needed, but should always call the base
  // class method.
  virtual void ResetInternalState();

  // Gets the base portion of the command line to run the Gaia Logon stub.
  // This portion of the command line would only include the executable and
  // any executable specific arguments needed to correctly start the GLS.
  virtual HRESULT GetBaseGlsCommandline(base::CommandLine* command_line);

  // Gets the user specific portion of the command line to run the Gaia Logon
  // stub. This portion of the command line could include additional information
  // such as the user's email and their gaia id.
  virtual HRESULT GetUserGlsCommandline(base::CommandLine* command_line);

  // Display error message to the user.  Virtual so that tests can override.
  virtual void DisplayErrorInUI(LONG status, LONG substatus, BSTR status_text);

  // Forks a stub process to perform all post sign-in actions for a user.
  virtual HRESULT ForkPerformPostSigninActionsStub(
      const base::Value::Dict& dict,
      BSTR* status_text);

  // Forks the logon stub process and waits for it to start.
  virtual HRESULT ForkGaiaLogonStub(OSProcessManager* process_manager,
                                    const base::CommandLine& command_line,
                                    UIProcessInfo* uiprocinfo);

  // Gets the full command line to run the Gaia Logon stub (GLS). This
  // function calls GetBaseGlsCommandline.
  HRESULT GetGlsCommandline(base::CommandLine* command_line);

 private:
  // Called from GetSerialization() to handle auto-logon.  If the credential
  // has enough information in internal state to auto-logon, the two arguments
  // are filled in as needed and S_OK is returned.  S_FALSE is returned to
  // indicate that the UI should be shown to the user.
  HRESULT HandleAutologon(
      CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE* pcpgsr,
      CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION* pcpcs);

  // Instantiates |token_update_locker_| so that user access cannot be denied
  // during this time. This function is called when we are about to really sign
  // in the user to Windows.
  void PreventDenyAccessUpdate();

  // Writes value to omaha registry to record that GCP has been used.
  static void TellOmahaDidRun();

  // Sets up the envriroment for the Gaia logon stub, runs it, and waits for
  // it to finish in a background thread.
  HRESULT CreateAndRunLogonStub();

  // Creates a restricted token for the Gaia account that can be used to run
  // the logon stub.  The returned SID is a logon SID and not the SID of the
  // Gaia account.
  static HRESULT CreateGaiaLogonToken(base::win::ScopedHandle* token,
                                      PSID* sid);

  // The param is a pointer to a UIProcessInfo struct.  This function must
  // release the memory for this structure using delete operator.
  static unsigned __stdcall WaitForLoginUI(void* param);

  // ICredentialProviderCredential2
  IFACEMETHODIMP Advise(ICredentialProviderCredentialEvents* cpce) override;
  IFACEMETHODIMP UnAdvise(void) override;
  IFACEMETHODIMP SetSelected(BOOL* auto_login) override;
  IFACEMETHODIMP SetDeselected(void) override;
  IFACEMETHODIMP GetFieldState(
      DWORD dwFieldID,
      CREDENTIAL_PROVIDER_FIELD_STATE* pcpfs,
      CREDENTIAL_PROVIDER_FIELD_INTERACTIVE_STATE* pcpfis) override;
  IFACEMETHODIMP GetStringValue(DWORD dwFieldID, wchar_t** ppsz) override;
  IFACEMETHODIMP GetBitmapValue(DWORD dwFieldID, HBITMAP* phbmp) override;
  IFACEMETHODIMP GetCheckboxValue(DWORD field_id,
                                  BOOL* pbChecked,
                                  wchar_t** ppszLabel) override;
  IFACEMETHODIMP GetSubmitButtonValue(DWORD field_id,
                                      DWORD* pdwAdjacentTo) override;
  IFACEMETHODIMP GetComboBoxValueCount(DWORD field_id,
                                       DWORD* pcItems,
                                       DWORD* pdwSelectedItem) override;
  IFACEMETHODIMP GetComboBoxValueAt(DWORD field_id,
                                    DWORD dwItem,
                                    wchar_t** ppszItem) override;
  IFACEMETHODIMP SetStringValue(DWORD field_id, const wchar_t* psz) override;
  IFACEMETHODIMP SetCheckboxValue(DWORD field_id, BOOL bChecked) override;
  IFACEMETHODIMP SetComboBoxSelectedValue(DWORD field_id,
                                          DWORD dwSelectedItem) override;
  IFACEMETHODIMP CommandLinkClicked(DWORD field_id) override;
  IFACEMETHODIMP GetSerialization(
      CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE* cpgsr,
      CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION* cpcs,
      wchar_t** status_text,
      CREDENTIAL_PROVIDER_STATUS_ICON* status_icon) override;
  IFACEMETHODIMP ReportResult(
      NTSTATUS ntsStatus,
      NTSTATUS ntsSubstatus,
      wchar_t** ppszOptionalStatusText,
      CREDENTIAL_PROVIDER_STATUS_ICON* pcpsiOptionalStatusIcon) override;
  IFACEMETHODIMP GetUserSid(wchar_t** sid) override;

  // Checks whether the submit button for the credential should be enabled
  // (depending on the state of the credential) and enables / disables it
  // accordingly. This generally occurs when processing is being done that does
  // not require direct user input to the credential (user is entering
  // credentials in  GLS) or a submit of the credential is not valid (user needs
  // to enter the old Windows password but currently nothing has been entered in
  // the password field). Returns true if the submit button is enabled.
  bool UpdateSubmitButtonInteractiveState();

  // Stops the GLS process in case it is still executing. Often called when user
  // switches credentials in the middle of a sign in through the GLS.
  void TerminateLogonProcess();

  // Checks whether this credential can sign in the user specified by
  // |domain|, |username|, |sid|. For the default anonymous gaia credential this
  // function will return S_OK. For implementers that are associated to a
  // specific user, this function should verify if the |domain|\|username| and
  // |sid| matches the domain\username and sid stored in the credential. If
  // these verifications fail then the function should return an error code
  // which will cause sign in to fail.
  virtual HRESULT ValidateExistingUser(const std::wstring& username,
                                       const std::wstring& domain,
                                       const std::wstring& sid,
                                       BSTR* error_text);

  // Checks the information given in |result| to determine if a user can be
  // created or re-used.
  // Returns S_OK if a valid was found or a new user was created. Also allocates
  // and fills |domain|, |username|, |sid| with the information for the user.
  // The caller must take ownership of this memory.
  // On failure |error_text| will be allocated and filled with an error message.
  // The caller must take ownership of this memory.
  HRESULT ValidateOrCreateUser(const base::Value::Dict& result,
                               BSTR* domain,
                               BSTR* username,
                               BSTR* sid,
                               BSTR* error_text);

  HRESULT RecoverWindowsPasswordIfPossible(std::wstring* recovered_password);

  // Sets the error message in the password field based on the HRESULT returned
  // by NetUserChangePassword win32 function.
  void SetErrorMessageInPasswordField(HRESULT hr);

  // Determines whether given message id corresponds to a password change error
  // which can't be worked out with manual user input in the forgot password
  // flow.
  bool BlockingPasswordError(UINT message_id);

  // Determines whether the logon stub can be launched by checking internet
  // connection and registry keys that should be present. |status_text| is
  // populated with a message to show in the login UI.
  bool CanProceedToLogonStub(wchar_t** status_text);

  Microsoft::WRL::ComPtr<ICredentialProviderCredentialEvents> events_;
  Microsoft::WRL::ComPtr<IGaiaCredentialProvider> provider_;

  // Handle to the logon UI process.
  HANDLE logon_ui_process_ = INVALID_HANDLE_VALUE;

  // Information about the just created or re-auth-ed user.
  CComBSTR username_;
  CComBSTR domain_;
  CComBSTR password_;
  CComBSTR user_sid_;

  // Indicates that the Windows password does not match the Gaia password and
  // the user must be enter the former.  For example this is used to properly
  // handle the password change case.
  bool needs_windows_password_ = false;
  bool request_force_password_change_ = false;

  // Boolean to indicate if we should wait for ReportResult() prior to clearing
  // internal state.
  bool wait_for_report_result_ = false;

  // The password entered into the FID_CURRENT_PASSWORD_FIELD to update the
  // Windows password with the gaia password.
  CComBSTR current_windows_password_;

  // Contains the information about the Gaia account that signed in.  See the
  // kKeyXXX constants for the data that is stored here.
  std::optional<base::Value::Dict> authentication_results_;

  // Holds information about the success or failure of the sign in.
  NTSTATUS result_status_ = STATUS_SUCCESS;

  // When we finally want to allow user sign in. This object is instantiated
  // to prevent updates of token handle validity until after sign in has
  // completed so the the user cannot be locked out while they are trying to
  // sign in.
  std::unique_ptr<AssociatedUserValidator::ScopedBlockDenyAccessUpdate>
      token_update_locker_;
};

}  // namespace credential_provider

#endif  // CHROME_CREDENTIAL_PROVIDER_GAIACP_GAIA_CREDENTIAL_BASE_H_