// 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_PROVIDER_H_
#define CHROME_CREDENTIAL_PROVIDER_GAIACP_GAIA_CREDENTIAL_PROVIDER_H_
#include <wrl/client.h>
#include <limits>
#include <memory>
#include <set>
#include <string>
#include <vector>
#include "base/synchronization/lock.h"
#include "base/synchronization/waitable_event.h"
#include "base/win/scoped_handle.h"
#include "chrome/credential_provider/gaiacp/gaia_credential.h"
#include "chrome/credential_provider/gaiacp/gaia_credential_provider_i.h"
#include "chrome/credential_provider/gaiacp/gaia_resources.h"
namespace credential_provider {
class BackgroundTokenHandleUpdater;
// Event handler that can be notified when a user's access has been revoked,
// allowing the credential provider to update the list of available credentials.
class DECLSPEC_UUID("fc2c889b-b468-4eb9-a61c-c984be8cc496")
ICredentialUpdateEventsHandler : public IUnknown {
public:
virtual ~ICredentialUpdateEventsHandler() = default;
virtual void UpdateCredentialsIfNeeded(bool user_access_changed) = 0;
};
// Implementation of ICredentialProvider backed by Gaia.
class ATL_NO_VTABLE CGaiaCredentialProvider
: public CComObjectRootEx<CComMultiThreadModel>,
public CComCoClass<CGaiaCredentialProvider,
&CLSID_GaiaCredentialProvider>,
public IGaiaCredentialProvider,
public ICredentialProviderSetUserArray,
public ICredentialProvider,
public ICredentialUpdateEventsHandler {
public:
// This COM object is registered with the rgs file. The rgs file is used by
// CGaiaCredentialProviderModule class, see latter for details.
DECLARE_NO_REGISTRY()
CGaiaCredentialProvider();
~CGaiaCredentialProvider() override;
BEGIN_COM_MAP(CGaiaCredentialProvider)
COM_INTERFACE_ENTRY(IGaiaCredentialProvider)
COM_INTERFACE_ENTRY(ICredentialProviderSetUserArray)
COM_INTERFACE_ENTRY(ICredentialProvider)
COM_INTERFACE_ENTRY(ICredentialUpdateEventsHandler)
END_COM_MAP()
DECLARE_PROTECT_FINAL_CONSTRUCT()
HRESULT FinalConstruct();
void FinalRelease();
// ICredentialUpdateEventsHandler:
void UpdateCredentialsIfNeeded(bool user_access_changed) override;
// Returns true if the given usage scenario is supported by GCPW. Currently
// only CPUS_LOGON and CPUS_UNLOCK_WORKSTATION are supported.
static bool IsUsageScenarioSupported(CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus);
// Returns true if a new user can be added in the current usage scenario. This
// function also checks other settings controlled by registry settings to
// determine the result of this query.
static bool CanNewUsersBeCreated(CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus);
// Struct to allow passing ComPtr by pointer without the implicit conversion
// to ** version of the ComPtr
struct GaiaCredentialComPtrStorage {
GaiaCredentialComPtrStorage();
~GaiaCredentialComPtrStorage();
Microsoft::WRL::ComPtr<IGaiaCredential> gaia_cred;
};
typedef HRESULT (*CredentialCreatorFn)(GaiaCredentialComPtrStorage*);
protected:
void SetCredentialCreatorFunctionsForTesting(
CredentialCreatorFn anonymous_cred_creator,
CredentialCreatorFn other_user_cred_creator,
CredentialCreatorFn reauth_cred_creator);
virtual HRESULT OnUserAuthenticatedImpl(IUnknown* credential,
BSTR username,
BSTR password,
BSTR sid,
BOOL fire_credentials_changed);
private:
HRESULT DestroyCredentials();
// Class used to store state information for the provider that may be accessed
// concurrently. This class is thread safe and ensures that the correct state
// can be set / queried at any moment. When modifying the state, the modifying
// function will return true to state that the operation was completed
// successfully to allow the caller to determine if they need to raise a
// credential changed event.
class ProviderConcurrentState {
public:
ProviderConcurrentState();
~ProviderConcurrentState();
// Checks if a user refresh can be performed on the next call to
// |GetCredentialCount|. Normally a user refresh is only needed if
// |user_access_changed|, but if an auto logon is pending the refresh will
// need to be deferred. In this case a pending refresh is queued and
// requested when possible on the next call to RequestUserRefreshIfNeeded.
// Returns true if a new credential changed event needs to be fired.
bool RequestUserRefreshIfNeeded(bool user_access_changed);
// Sets the credential used to auto logon credential. This function will
// always set the credential as auto logon has precedence over a user
// refresh. If a user refresh was already requested then it will be changed
// to a pending refresh request. Returns true if a new credential changed
// event needs to be fired. A credential changed event is not always
// required if a previous call to RequestUserRefreshIfNeeded was made that
// requested a credential changed event.
bool SetAutoLogonCredential(
const Microsoft::WRL::ComPtr<IGaiaCredential>& auto_logon_credential);
// Gets the current valid update state of the provider to determnie whether
// an auto logon needs to be done or a refresh of the credentials. The two
// update states are mutually exclusive. Only one of
// |needs_to_refresh_users| and |auto_logon_credential| can be set to a non
// default value.
void GetUpdatedState(bool* needs_to_refresh_users,
GaiaCredentialComPtrStorage* auto_logon_credential);
// Resets the state of the provider to be default one. On the next call to
// GetCredentialCount no auto logon should be performed and no refresh of
// credentials should be done.
void Reset();
private:
void InternalReset();
// Reference to the credential that authenticated the user.
Microsoft::WRL::ComPtr<IGaiaCredential> auto_logon_credential_;
// Set in NotifyUserAccessDenied to notify the main thread that it will need
// to update credentials on the next call to GetCredentialCount. This
// ensures that |users_| is only accessed on the main thread. This member
// can only be accessed on multiple threads if |token_handle_updater_| is
// instantiated, which only occurs between a call to Advise and UnAdvise.
bool users_need_to_be_refreshed_ = false;
// Used to queue up user refresh requests that had to be deferred because
// |auto_logon_credential_| has precedence over
// |users_need_to_be_refreshed_|.
bool pending_users_refresh_needed_ = false;
// Locks access to |users_need_to_be_refreshed_| and
// |auto_logon_credential_| to prevent races between calls to
// OnUserAuthenticated / GetCredentialCount and calls to
// NotifyUserAccessDenied.
base::Lock state_update_lock_;
};
// Functions to create credentials during the processing of SetUserArray.
// Creates necessary anonymous credentials given the state of the sign in
// screen (currently only whether |showing_other_user| set influences this
// behavior.
HRESULT CreateAnonymousCredentialIfNeeded(bool showing_other_user);
// Creates all the reauth credentials from the users that is returned from
// |users|. Fills the |gaia_cred| in |auto_logon_credential| with a reference
// to the credential that needs to perform auto logon (if any).
HRESULT CreateReauthCredentials(
ICredentialProviderUserArray* users,
GaiaCredentialComPtrStorage* auto_logon_credential);
// This function will always add |cred| to |users_| and will also try to check
// if the |sid| matches the one set in |set_serialization_sid_| to allow auto
// logon of remote connections. Fills the |gaia_cred| in
// |auto_logon_credential| with a reference to the credential that needs to
// perform auto logon (if any).
void AddCredentialAndCheckAutoLogon(
const Microsoft::WRL::ComPtr<IGaiaCredential>& cred,
const std::wstring& sid,
GaiaCredentialComPtrStorage* auto_logon_credential);
// Destroys existing credentials and recreates them based on the contents of
// |user_array_|. This member must be set via a call to SetUserArray before
// RecreateCredentials is called. Fills the |gaia_cred| in
// |auto_logon_credential| with a reference to the credential that needs to
// perform auto logon (if any).
void RecreateCredentials(GaiaCredentialComPtrStorage* auto_logon_credential);
void ClearTransient();
void CleanupOlderVersions();
// IGaiaCredentialProvider
IFACEMETHODIMP GetUsageScenario(DWORD* cpus) override;
IFACEMETHODIMP OnUserAuthenticated(IUnknown* credential,
BSTR username,
BSTR password,
BSTR sid,
BOOL fire_credentials_changed) override;
// ICredentialProviderSetUserArray
IFACEMETHODIMP SetUserArray(ICredentialProviderUserArray* users) override;
// ICredentialProvider
IFACEMETHODIMP SetUsageScenario(CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus,
DWORD dwFlags) override;
IFACEMETHODIMP SetSerialization(
const CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION* pcpcs) override;
IFACEMETHODIMP Advise(ICredentialProviderEvents* pcpe,
UINT_PTR upAdviseContext) override;
IFACEMETHODIMP UnAdvise() override;
IFACEMETHODIMP GetFieldDescriptorCount(DWORD* pdwCount) override;
IFACEMETHODIMP GetFieldDescriptorAt(
DWORD dwIndex,
CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR** ppcpfd) override;
IFACEMETHODIMP GetCredentialCount(DWORD* pdwCount,
DWORD* pdwDefault,
BOOL* pbAutoLogonWithDefault) override;
IFACEMETHODIMP GetCredentialAt(
DWORD dwIndex,
ICredentialProviderCredential** ppcpc) override;
CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus_ = CPUS_INVALID;
DWORD cpus_flags_ = 0;
UINT_PTR advise_context_;
Microsoft::WRL::ComPtr<ICredentialProviderEvents> events_;
Microsoft::WRL::ComPtr<ICredentialProviderUserArray> user_array_;
// List of credentials exposed by this provider. The first is always the
// Gaia credential for creating new users. The rest are reauth credentials.
std::vector<Microsoft::WRL::ComPtr<IGaiaCredential>> users_;
// Reference to the credential that authenticated the user.
Microsoft::WRL::ComPtr<IGaiaCredential> auto_logon_credential_;
// Background thread updater of token handles that is created on startup to
// ensure that user must sign in through gaia if their token handle becomes
// invalid while idle on the sign in screen. The updater will also handle the
// situation where the machine becomes unenrolled from MDM during sign in.
std::unique_ptr<BackgroundTokenHandleUpdater> token_handle_updater_;
// The SID extracted from the serialization information passed into
// SetSerialization. This sid is used to determine which credential to try
// to auto logon when GetCredentialCount is called.
std::wstring set_serialization_sid_;
ProviderConcurrentState concurrent_state_;
CredentialCreatorFn anonymous_cred_creator_ = nullptr;
CredentialCreatorFn other_user_cred_creator_ = nullptr;
CredentialCreatorFn reauth_cred_creator_ = nullptr;
std::vector<std::wstring> reauth_cred_sids_;
};
// OBJECT_ENTRY_AUTO() contains an extra semicolon.
// TODO(thakis): Make -Wextra-semi not warn on semicolons that are from a
// macro in a system header, then remove the pragma, https://llvm.org/PR40874
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wextra-semi"
#endif
OBJECT_ENTRY_AUTO(__uuidof(GaiaCredentialProvider), CGaiaCredentialProvider)
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
} // namespace credential_provider
#endif // CHROME_CREDENTIAL_PROVIDER_GAIACP_GAIA_CREDENTIAL_PROVIDER_H_