|
|
//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1996 - 1998.
//
// File: imprsnat.hxx
//
// Contents: Classes used to control the security context.
// When called from the "WWW service", the thread is
// impersonated as the authenticated client, often the
// "Internet Anonymous User". This user has few privileges
// and cannot access the content index data needed to
// resolve the queries. However, for enumerated queries, we
// need to stay in the context of the client so that normal
// access controls will work correctly.
//
// To assume the system context, we use the fact that before
// impersonating as the client, the thread was in fact had
// "local system" privileges.
// We save the TOKEN of the current thread, revert to the system
// privileges and on the way out, we restore the TOKEN of the
// anonymous user.
//
// Impersonating a client involves just calling
// ImpersonateLoggedOnUser with the supplied client token,
// then calling RevertToSelf when done.
//
// Classes: CImpersonateSystem - Become the "system" context
// CImpersonateClient - Impersonate to a client
//
// History: 2-16-96 srikants Created
//
//----------------------------------------------------------------------------
#pragma once
#include <smatch.hxx>
#include <filterr.h>
//+---------------------------------------------------------------------------
//
// Class: CImpersonateSystem
//
// Purpose: An unwindable class to impersonate system and revert back
// to what it was before.
//
// History: 4-01-96 srikants Added header
//
//----------------------------------------------------------------------------
class CImpersonateSystem { public:
CImpersonateSystem( BOOL fShouldImpersonate = IsRunningAsSystem() ) : _fRevertedToSystem( FALSE ), _hClientToken( INVALID_HANDLE_VALUE ) { if ( fShouldImpersonate ) MakePrivileged(); }
~CImpersonateSystem();
// in SYSTEM context
static BOOL IsImpersonated();
static BOOL IsRunningAsSystem();
static void SetRunningAsSystem();
private:
void MakePrivileged();
static BOOL _fIsRunningAsSystem; // TRUE if query.dll came up initially
BOOL _fRevertedToSystem; // Set to TRUE if we are running in
// "SYSTEM" context.
HANDLE _hClientToken; // Handle of the client token
};
//+---------------------------------------------------------------------------
//
// Class: CImpersonateClient
//
// Purpose: An unwindable class to impersonate a user whose token is given
// and to revert back to system later.
//
// History: 4-01-96 srikants Added Header
//
//----------------------------------------------------------------------------
class CImpersonateClient { public:
CImpersonateClient( HANDLE hToken ) : _hClientToken( hToken ) { if ( INVALID_HANDLE_VALUE != hToken ) Impersonate(); }
~CImpersonateClient();
private: void Impersonate();
HANDLE _hClientToken; // Handle of the client token
};
//+---------------------------------------------------------------------------
//
// Class: CLogonInfo
//
// Purpose: A linked list element that keeps logon information for a
// user.
//
// History: 4-05-96 srikants Created
//
// Notes:
//
//----------------------------------------------------------------------------
class CLogonInfo : public CDoubleLink {
public:
CLogonInfo() { CDoubleLink::Close(); _pwszUser = _pwszDomain = 0; _ccUser = _ccDomain = 0;
_cRef = 0; _fZombie = FALSE; _hToken = INVALID_HANDLE_VALUE; }
~CLogonInfo();
DWORD Logon( WCHAR const * pwszUser, WCHAR const * pwszDomain, WCHAR const * pwszPassword );
BOOL IsSameUser( WCHAR const * pwszUser, WCHAR const * pwszDomain ) const; BOOL IsSamePassword( WCHAR const * pwszPassword ) const;
void Addref() { _cRef++; } void Release() { Win4Assert( _cRef > 0 ); _cRef--; }
BOOL IsInUse() const { return _cRef > 0; }
void Zombify() { _fZombie = TRUE; } BOOL IsZombie() const { return _fZombie; }
HANDLE GetHandle() const { return _hToken; }
static WCHAR * AllocAndCopy( WCHAR const * pwszSrc, ULONG & cc );
WCHAR * GetUser() { return _pwszUser; } WCHAR * GetDomain() { return _pwszDomain; }
private:
WCHAR * _pwszUser; // User Name
ULONG _ccUser; // Length of user name
WCHAR * _pwszDomain; // Domain Name
ULONG _ccDomain;
XArray<BYTE> _xEncryptedPassword;
LONG _cRef; // Refcount
BOOL _fZombie; // Set to TRUE if this has been
// zombified
HANDLE _hToken; // "Logged on" token for the account
};
class CLogonInfoList : public TDoubleList<CLogonInfo> { public: CLogonInfoList() {} void Empty(); ~CLogonInfoList() { Empty(); } };
typedef class TFwdListIter<CLogonInfo, CLogonInfoList> CFwdLogonInfoIter;
//+---------------------------------------------------------------------------
//
// Class: CImprsObjInfo
//
// Purpose: A class that encapsulates the object that a user is trying
// to access.
//
// History: 7-12-96 srikants Created
//
// Notes:
//
//----------------------------------------------------------------------------
class CImprsObjInfo {
public:
CImprsObjInfo( WCHAR const * const pwszPath, WCHAR const * const pwszVPath ) : _pwszPath( pwszPath ), _pwszVPath( pwszVPath ) {
}
WCHAR const * GetPhysicalPath() const { return _pwszPath; } WCHAR const * GetVPath() const { return _pwszVPath; }
private:
WCHAR const * const _pwszPath; // physical path
WCHAR const * const _pwszVPath; // Virtual path, can be 0
};
//+---------------------------------------------------------------------------
//
// Class: CPhyDirLogonInfo
//
// Purpose: Logon information for a physical directory
//
// History: 4-01-96 srikants Created
//
// Notes:
//
//----------------------------------------------------------------------------
class CPhyDirLogonInfo : public CDoubleLink { friend class CImpersonationTokenCache;
public:
CPhyDirLogonInfo( CImpersonationTokenCache & cache, CImprsObjInfo const & obj, CLogonInfo * pLogonInfo );
BOOL IsInScope( WCHAR const * pwszPath, ULONG len ) const { return _phyScope.IsInScope( pwszPath, len ); }
BOOL IsInScope( WCHAR const * pwszPath ) const { return _phyScope.IsInScope( pwszPath, wcslen(pwszPath) ); }
BOOL IsInVirtualScope( WCHAR const * pwszVPath ) const { return pwszVPath ? _virtualScope.IsInScope( pwszVPath, wcslen(pwszVPath) ) : TRUE; }
BOOL IsMatch( CImprsObjInfo const & info ) const;
int Compare( CPhyDirLogonInfo const & rhs ) const;
HANDLE GetHandle() const { return _pLogonInfo ? _pLogonInfo->GetHandle() : INVALID_HANDLE_VALUE; }
BOOL IsZombie() const { return _fIsZombie; }
BOOL IsInUse() const { return _cRef > 0; }
ULONG GetVirtualRootLen() const { return _ccVirtual; }
BOOL IsSame( CImprsObjInfo const & info ) const;
BOOL IsSameVPath( WCHAR const * pwszVPath ) const { Win4Assert( (0 == _pwszVirtualRoot && 0 == _ccVirtual) || (0 != _pwszVirtualRoot && 0 != _ccVirtual) );
ULONG ccVirtual = pwszVPath ? wcslen( pwszVPath ) : 0;
if ( 0 == _ccVirtual && 0 == ccVirtual ) return TRUE; else if ( ccVirtual != _ccVirtual ) return FALSE;
return RtlEqualMemory( pwszVPath, _pwszVirtualRoot, ccVirtual * sizeof(WCHAR) ); }
BOOL IsSamePhysicalPath( WCHAR const * pwszPath ) const { ULONG cc = pwszPath ? wcslen( pwszPath ) : 0;
if ( cc != _cc ) return FALSE;
Win4Assert( 0 != _cc ); return RtlEqualMemory( pwszPath, _pwszDirName, cc * sizeof(WCHAR) ); }
~CPhyDirLogonInfo();
CLogonInfo * GetLogonInfo() { return _pLogonInfo; }
private:
WCHAR const * GetDirName() const { return _pwszDirName; } ULONG GetNameLen() const { return _cc; }
void AddRef() { _cRef++; } void Release() { Win4Assert( _cRef > 0 ); _cRef--; }
void Zombify() { _fIsZombie = TRUE; }
CImpersonationTokenCache & _cache; // token cache
WCHAR * _pwszDirName; // Physical directory
ULONG _cc; // Length of the physical dir
WCHAR * _pwszVirtualRoot; // Virtual root.
ULONG _ccVirtual; // Length of the virtual root
CLogonInfo * _pLogonInfo; // LogonInformation for this share
// The impersonation token to use
BOOL _fIsZombie; // Set to TRUE if this is a zombie
long _cRef; // Refcount
CScopeMatch _phyScope; // For physical scope testing
CScopeMatch _virtualScope; // For virtula scope testing
};
//+---------------------------------------------------------------------------
//
// Member: CPhyDirLogonInfo::IsMatch
//
// Synopsis: Tests if the given path and the vroot are valid
// for access using this logon info.
//
// Arguments: [info] -- object to be compared
//
// Returns: TRUE if the logon info is valid for this vroot.
//
// History: 7-12-96 srikants Created
//
// Notes: It is assumed that the default server entries are at the end
// in the list. For the default server, there will be no check
// done on the ip-address for match.
//
//----------------------------------------------------------------------------
inline BOOL CPhyDirLogonInfo::IsMatch( CImprsObjInfo const & info ) const
{ return IsInVirtualScope(info.GetVPath() ) && IsInScope( info.GetPhysicalPath() ); }
//+---------------------------------------------------------------------------
//
// Member: CPhyDirLogonInfo::IsSame
//
// Synopsis: Tests if the information contained for a path here is exactly
// the same as the given info.
//
// Arguments: [info] -
//
// History: 7-12-96 srikants Created
//
//----------------------------------------------------------------------------
inline BOOL CPhyDirLogonInfo::IsSame( CImprsObjInfo const & info ) const
{ return IsSameVPath( info.GetVPath() ) && IsSamePhysicalPath( info.GetPhysicalPath() ); }
//+---------------------------------------------------------------------------
//
// Member: CPhyDirLogonInfo::Compare
//
// Synopsis: Compares this logon information for precedence calculation with
// the given rhs.
//
// Arguments: [rhs] -
//
// Returns: 0 if they are equal
// -1 if this is < than rhs
// +1 if this is > rhs
//
// History: 7-12-96 srikants Created
//
// Notes: The nodes in the list are ordered by .
//
// Increasing order of ip addresses,
// Decreasing order of vpath lengths,
// Decreasing order of phy.path lengths
//
// This causes the default ip address nodes to be at the end
// of the list.
//
//----------------------------------------------------------------------------
inline int CPhyDirLogonInfo::Compare( CPhyDirLogonInfo const & rhs ) const { if ( _ccVirtual == rhs._ccVirtual ) { if ( _cc == rhs._cc ) { return 0; } else { return _cc < rhs._cc ? -1 : 1; } } else { return _ccVirtual < rhs._ccVirtual ? -1 : 1; } }
typedef class TDoubleList<CPhyDirLogonInfo> CPhyDirLogonList; typedef class TFwdListIter<CPhyDirLogonInfo, CPhyDirLogonList> CFwdPhyDirLogonIter;
//+---------------------------------------------------------------------------
//
// Class: CImpersonationTokenCache
//
// Purpose: A cache of impersonation tokens for use in accessing remote
// shares.
//
// History: 4-01-96 srikants Created
//
// Notes:
//
//----------------------------------------------------------------------------
typedef struct _INET_INFO_VIRTUAL_ROOT_LIST INET_INFO_VIRTUAL_ROOT_LIST; typedef struct _INET_INFO_VIRTUAL_ROOT_ENTRY INET_INFO_VIRTUAL_ROOT_ENTRY;
class CIISVirtualDirectories;
class CImpersonationTokenCache { friend class CRegistryScopesCallBackImp; friend class CIISCallBackImp;
enum { CI_MAX_DRIVES = 26 }; enum EDriveType { eUnknown, eLocal, eRemote };
public:
CImpersonationTokenCache( WCHAR const * pwcCatName ); ~CImpersonationTokenCache();
CPhyDirLogonInfo * Find( CImprsObjInfo & info, ULONG cSkip );
CPhyDirLogonInfo * Find( WCHAR const * pwszPath, ULONG cc );
void Release( CPhyDirLogonInfo * pInfo ) { CLock lock(_mutex);
pInfo->Release(); if ( pInfo->IsZombie() && !pInfo->IsInUse() ) { Win4Assert( pInfo->IsSingle() ); delete pInfo; } }
void Release( CLogonInfo * pInfo ) { CLock lock(_mutex);
pInfo->Release(); if ( pInfo->IsZombie() && !pInfo->IsInUse() ) { Win4Assert( pInfo->IsSingle() ); delete pInfo; } }
BOOL IsNetworkDrive( WCHAR const * pwszPath );
BOOL IsIndexingW3Roots() const { return _fIndexW3Roots; } BOOL IsIndexingNNTPRoots() const { return _fIndexNNTPRoots; } BOOL IsIndexingIMAPRoots() const { return _fIndexIMAPRoots; }
ULONG GetW3Instance() const { return _W3SvcInstance; } ULONG GetNNTPInstance() const { return _NNTPSvcInstance; } ULONG GetIMAPInstance() const { return _IMAPSvcInstance; }
BOOL AreRemoteSharesPresent() const { return _fRemoteSharesPresent; }
void Initialize( WCHAR const * pwszComponent, BOOL fIndexW3Roots, BOOL fIndexNNTPRoots, BOOL fIndexIMAPRoots, ULONG W3SvcInstance, ULONG NNTPSvcInstance, ULONG IMAPSvcInstance );
void ReInitializeIISScopes(); void ReInitializeIISScopes( CIISVirtualDirectories * pW3Dirs, CIISVirtualDirectories * pNNTPDirs, CIISVirtualDirectories * pIMAPDirs ); void ReInitializeScopes();
WCHAR const * GetCatalog() { return _awcCatalogName; }
private:
CPhyDirLogonInfo * _FindExact( CImprsObjInfo const & info );
CLogonInfo * _LokFindLogonEntry( WCHAR const * pwszUser, WCHAR const * pwszDomain );
DWORD _LokValidateOrAddLogonEntry( WCHAR const * pwszUser, WCHAR const * pwszDomain, WCHAR const * pwszPassword );
void _LokAddLogonEntry( WCHAR const * pwszUser, WCHAR const * pwszDomain, WCHAR const * pwszPassword );
void _LokValidateOrAddDirEntry( CImprsObjInfo const & info, WCHAR const * pwszAccount );
void _LokAddDirEntry( CImprsObjInfo const & info, WCHAR const * pwszAccountName );
void _LokSyncUp( INET_INFO_VIRTUAL_ROOT_ENTRY * aEntries, ULONG cEntries );
unsigned _GetDriveIndex( WCHAR wcDriveLetter ) { return towlower(wcDriveLetter) - L'a'; }
void _WriteLogonFailure( WCHAR const * pwszUser, DWORD dwError );
BOOL _fIndexW3Roots; // Set to TRUE if indexing w3svc roots
BOOL _fIndexNNTPRoots; // Set to TRUE if NNTP is enabled
BOOL _fIndexIMAPRoots; // Set to TRUE if IMAP is enabled
ULONG _W3SvcInstance; // server instance #
ULONG _NNTPSvcInstance; // server instance #
ULONG _IMAPSvcInstance; // server instance #
BOOL _fRemoteSharesPresent; // Optimization - don't do any
// checks if there are no remote
// shares.
WCHAR * _pwszComponentName; // Name of the component for event log
// messages
CMutexSem _mutex; // Serialization Mutex
EDriveType _aDriveInfo[CI_MAX_DRIVES]; // Array of information on drive
// letters.
CPhyDirLogonList _phyDirList; // list of remote directories
WCHAR _awcCatalogName[MAX_PATH]; // name of the catalog
};
extern CLogonInfoList g_LogonList; // List of logon information
#define CI_DAEMON_NAME L"CiDaemon"
#define CI_ACTIVEX_NAME L"Indexing Service"
//+---------------------------------------------------------------------------
//
// Class: CImpersonateRemoteAccess
//
// Purpose: An unwindable object to impersonate for access to remote
// shares.
//
// History: 4-01-96 srikants Created
//
// Notes:
//
//----------------------------------------------------------------------------
class CImpersonateRemoteAccess { public:
CImpersonateRemoteAccess( CImpersonationTokenCache * pCache );
~CImpersonateRemoteAccess() { Release(); }
void SetTokenCache( CImpersonationTokenCache * pCache ) { _pCache = pCache; }
static BOOL IsNetPath( WCHAR const * pwszPath ) { //
// WWW server and CI don't allow remote drive letters.
// This allows us to do the check significantly cheaper.
//
return L'\\' == pwszPath[0] && L'\\' == pwszPath[1] && L'.' != pwszPath[2]; }
BOOL ImpersonateIfNoThrow( WCHAR const * pwszPath, WCHAR const * pwszVirtualPath ) { Win4Assert( 0 != pwszPath );
if ( IsNetPath( pwszPath ) ) { if ( !_ImpersonateIf( pwszPath, pwszVirtualPath ) ) return FALSE; } else if ( IsImpersonated() ) { //
// This thread was impersonated for some network access. Revert back to
// what it was before impersonation.
//
Release(); }
return TRUE; }
void ImpersonateIf( WCHAR const * pwszPath, WCHAR const * pwszVirtualPath ) { if ( !ImpersonateIfNoThrow( pwszPath, pwszVirtualPath ) ) { THROW( CException(STATUS_LOGON_FAILURE) ); } }
BOOL ImpersonateIf( WCHAR const * pwszPath, ULONG cSkip, BOOL fDummy ) { Win4Assert( 0 != pwszPath );
if ( IsNetPath( pwszPath ) ) { return _ImpersonateIf( pwszPath, 0, cSkip ); } else if ( IsImpersonated() ) { //
// This thread was impersonated for some network access. Revert back to
// what it was before impersonation.
//
Release(); }
return TRUE; }
void Release();
BOOL IsImpersonated() const { return _fMustRevert; }
BOOL IsTokenFound() const { return 0 != _pTokenInfo; }
private:
BOOL _ImpersonateIf( WCHAR const * pwszPath, WCHAR const * pwszVirtualPath, ULONG cSkip = 0 );
CImpersonationTokenCache * _pCache; CPhyDirLogonInfo * _pTokenInfo; // Impersonation information
HANDLE _hTokenPrev; // If valid, the token of the
// thread before impersonation
BOOL _fMustRevert; // Set to TRUE if we have to
// revert to previous state
};
//+---------------------------------------------------------------------------
//
// Class: PImpersonatedWorkItem
//
// Purpose: Abstract base class that allows the caller to impersonate
// until success or no more options.
//
// History: 7-15-96 srikants Created
//
// Notes: This class defines the framework by which threads that need
// maximum impersonation level allowed by the user to do a
// particular work. For example, if /vroot1 and /vroot2 point to
// the same physical remote root, but have different user-id and
// levels of permission, we have to use whatever id allows the
// required level of access.
//
//----------------------------------------------------------------------------
class PImpersonatedWorkItem {
public:
PImpersonatedWorkItem( WCHAR const * pwszPath ) : _pwszPath(pwszPath), _fDontUseDefault(FALSE) { _fNetPath = CImpersonateRemoteAccess::IsNetPath( _pwszPath ); }
virtual BOOL DoIt() = 0;
static BOOL IsRetryableError( NTSTATUS status ) { return STATUS_ACCESS_DENIED == status || ERROR_ACCESS_DENIED == status || FILTER_E_ACCESS == status || HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) == status; }
protected:
void ImpersonateAndDoWork( CImpersonateRemoteAccess & remoteAccess );
WCHAR const * const _pwszPath; // Phy. Path to impersonate for
BOOL _fNetPath; // Set to TRUE if this is a network path
private:
BOOL _fDontUseDefault; // Set to TRUE if default vroot must not
// be used for impersonation
};
//+---------------------------------------------------------------------------
//
// Class: CImpersonatedGetAttr
//
// Purpose: A class that is capable of repeatedly trying to do
// GetAttributesEx() until there is a success or there are no
// more impersonation contexts to try.
//
// History: 7-18-96 srikants Created
//
//----------------------------------------------------------------------------
class CImpersonatedGetAttr: public PImpersonatedWorkItem {
public:
CImpersonatedGetAttr( const CFunnyPath & funnyPath ) : PImpersonatedWorkItem( funnyPath.GetActualPath() ), _funnyPath( funnyPath ) { }
// THROWS if it cannot get attributes in any context
void DoWork( CImpersonateRemoteAccess & remoteAccess ) { ImpersonateAndDoWork( remoteAccess ); }
BOOL DoIt(); // virtual
WIN32_FIND_DATA const & GetAttrEx() const { return _ffData; }
private:
WIN32_FIND_DATA _ffData;
const CFunnyPath & _funnyPath; };
//+---------------------------------------------------------------------------
//
// Class: CImpersonatedGetFileAttr
//
// Purpose: A class that is capable of repeatedly trying to do
// GetAttributesEx() until there is a success or there are no
// more impersonation contexts to try.
//
// History: 7-18-96 srikants Created
//
//----------------------------------------------------------------------------
class CImpersonatedGetFileAttr: public PImpersonatedWorkItem {
public:
CImpersonatedGetFileAttr( const CFunnyPath & funnyPath ) : PImpersonatedWorkItem( funnyPath.GetActualPath() ), _ulAttr( INVALID_FILE_ATTRIBUTES ), _funnyPath( funnyPath ) { }
// THROWS if it cannot get attributes in any context
void DoWork( CImpersonateRemoteAccess & remoteAccess ) { ImpersonateAndDoWork( remoteAccess ); }
BOOL DoIt(); // virtual
ULONG GetAttr() const { return _ulAttr; }
private:
ULONG _ulAttr;
const CFunnyPath & _funnyPath; };
//
// This function throws if the caller doesn't have admin priv
//
void VerifyThreadHasAdminPrivilege( void );
|