blob: c9a0f0058ed3e2b58e252e042aa2414d750fa463 [file] [log] [blame]
// Copyright 2018 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.
#include <credentialprovider.h>
#include <map>
#include <set>
#include "base/strings/string16.h"
#include "base/synchronization/lock.h"
#include "base/time/time.h"
#include "base/win/scoped_handle.h"
#include "chrome/credential_provider/gaiacp/gaia_credential_provider_i.h"
namespace credential_provider {
// Caches the current validity of token handles and updates the validity if
// it is older than a specified validity lifetime.
// NOTE: This class is thread safe.
// The following functions are called at a time when it is impossible for
// the validator to be accessed by multiple threads. The validator will only
// be accessed from another thread through the BackgroundTokenHandleUpdater
// that is created in CGaiaCredentialProvider::Advise and destroyed in
// CGaiaCredentialProvider::Unadvise:
// StartRefreshingTokenHandleValidity: Only called on the main thread during
// a call to DllGetClassObject.
// IsUserAccessBlockingEnforced: Only called on the main thread in
// CGaiaCredentialProvider::Advise and in
// CGaiaCredentialProviderFilter::UpdateRemoteCredential.
// AllowSigninForUsersWithInvalidTokenHandles: Only called on the main thread
// in CGaiaCredentialProvider::FinalRelease.
// AllowSigninForAllAssociatedUsers: Only called on the main thread in
// CGaiaCredentialProviderFilter::Filter.
// The following functions can be called while the validator can be accessed
// from another thread:
// IsTokenHandleValidForUser: Called on the main thread indirectly in
// CGaiaCredentialProvider::GetCredentialCount. Also called on the update
// thread while checking DenySigninForUsersWithInvalidTokenHandles.
// GetAssociatedUsersCount: Only called on the main thread indirectly in
// CGaiaCredentialProvider::GetCredentialCount.
// RestoreUserAccess: Only called on the main thread in
// CGaiaCredentialBase::HandleAutologon.
// Finally the one function that can be called on the update thread is
// DenySigninForUsersWithInvalidTokenHandles. If this function returns
// true, it will queue a credential update which will only be executed
// on the main thread. The update thread will then be dormant for
// |kTokenHandleValidityLifetime| seconds and in this time the expected
// update of the credentials on the main thread via a call to
// CGaiaCredentialProvider::GetCredentialCount should be able to complete
// before a new update is requested on the update thread. This timing will
// protect the two functions IsTokenHandleValidForUser and
// GetAssociatedUsersCount from being called by multiple threads at the same
// time.
class AssociatedUserValidator {
// Prevent update of user access through the call to
// DenySigninForUsersWithInvalidTokenHandles. This will be used to prevent
// locking out users that are in the process of signing in.
class ScopedBlockDenyAccessUpdate {
explicit ScopedBlockDenyAccessUpdate(AssociatedUserValidator* validator);
AssociatedUserValidator* validator_;
// Default timeout when querying token info for token handles. If a timeout
// occurs the token handle is assumed to be valid.
static const base::TimeDelta kDefaultTokenHandleValidationTimeout;
// Minimum time between token handle info refreshes. When trying to get token
// info, if the info is older than this value, a new token info query will
// be made.
static const base::TimeDelta kTokenHandleValidityLifetime;
// Default URL used to fetch token info for token handles.
static const char kTokenInfoUrl[];
static AssociatedUserValidator* Get();
// Get all the token handles for all associated users and start queries
// for their validity. The queries are fired in separate threads but
// no wait is done for the result. This allows background processing of
// the queries until they are actually needed. An eventual call to
// IsTokenHandleValidForUser will cause the wait for the result as needed.
void StartRefreshingTokenHandleValidity();
// Checks whether the token handle for the given user is valid or not.
// This function is blocking and may fire off a query for a token handle that
// needs to complete before the function returns.
bool IsTokenHandleValidForUser(const base::string16& sid);
enum EnforceAuthReason {
// Returns the reason for enforcing authentication for the provided |sid|.
// This function is blocking and may fire off a query for a token handle that
// needs to complete before the function returns.
EnforceAuthReason GetAuthEnforceReason(const base::string16& sid);
// Checks if user access blocking is enforced given the usage scenario (and
// other registry based checks).
bool IsUserAccessBlockingEnforced(
// Goes through all associated users found and denies their access to sign
// in to the system based on the validity of their token handle. Returns true
// if a user has just been denied signin access.
bool DenySigninForUsersWithInvalidTokenHandles(
// Restores the access for a user that was denied access (if applicable).
// Returns S_OK on success, failure otherwise.
HRESULT RestoreUserAccess(const base::string16& sid);
// Allows access for all users that have had their access denied by this
// token validator.
void AllowSigninForUsersWithInvalidTokenHandles();
// Restores access to all associated users. Regardless of their access
// state. This ensures that no user can be completely locked out due
// a bad computer state or crash.
void AllowSigninForAllAssociatedUsers(
// Gets the updated count of valid associated users that exist on this system.
size_t GetAssociatedUsersCount();
// Returns whether the user should be locked out of sign in (only used in
// tests).
bool IsDenyAccessUpdateBlocked() const;
bool HasInternetConnection() const;
// Returns the storage used for the instance pointer.
static AssociatedUserValidator** GetInstanceStorage();
explicit AssociatedUserValidator(base::TimeDelta validation_timeout);
virtual ~AssociatedUserValidator();
// Returns whether the user should be locked out of sign in (only used in
// tests).
bool IsUserAccessBlockedForTesting(const base::string16& sid) const;
// Forces a refresh of all token handles the next time they are queried.
// This function should only be called in tests.
void ForceRefreshTokenHandlesForTesting();
void CheckTokenHandleValidity(
const std::map<base::string16, base::string16>& handles_to_verify);
void StartTokenValidityQuery(const base::string16& sid,
const base::string16& token_handle,
base::TimeDelta timeout);
HRESULT UpdateAssociatedSids(
std::map<base::string16, base::string16>* sid_to_handle);
// Stores information about the current state of a user's token handle.
// This information includes:
// * The last token handle found for the user.
// * The validity of this last token handle (if checked).
// * The time of the last update of the validity of this token handle.
// This will often be the max end time of the last query that was made on
// the token handle.
// * The handle to the current thread being executed to verify the
// validity of the last token handle.
struct TokenHandleInfo {
// Used when the handle is empty or invalid.
explicit TokenHandleInfo(const base::string16& token_handle);
// Used to create a new token handle info that needs to query validity.
// The validity is assumed to be invalid at the time of construction.
TokenHandleInfo(const base::string16& token_handle,
base::Time update_time,
base::win::ScopedHandle::Handle thread_handle);
base::string16 queried_token_handle;
bool is_valid = false;
base::Time last_update;
base::win::ScopedHandle pending_query_thread;
// Increments / decrements |block_deny_access_update_| to prevent denying
// user access when a token handle becomes invalid. Only called via a
// ScopedBlockDenyAccessUpdate object.
void BlockDenyAccessUpdate();
void UnblockDenyAccessUpdate();
// Maps a user's sid to the token handle info associated with this user (if
// any).
std::map<base::string16, std::unique_ptr<TokenHandleInfo>>
base::TimeDelta validation_timeout_;
std::set<base::string16> locked_user_sids_;
mutable base::Lock validator_lock_;
// When |block_deny_access_update_| != 0, prevent users from being denied
// access when DenySigninForUsersWithInvalidTokenHandles is called. This
// prevents users from being locked out while signing is occurring but a token
// handle update is also being requested at the same time. The functions
// LockDenyAccessUpdate / UnlockDenyAccessUpdate are called in the lifetime of
// a ScopedBlockDenyAccessUpdate to update this member.
size_t block_deny_access_update_ = 0;
} // namespace credential_provider