Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

866 lines
25 KiB

//+---------------------------------------------------------------------------
//
// 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 = _pwszPassword = 0;
_ccUser = _ccDomain = _ccPassword = FALSE;
_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; }
WCHAR * GetPassword() { return _pwszPassword; }
private:
WCHAR * _pwszUser; // User Name
ULONG _ccUser; // Length of user name
WCHAR * _pwszDomain; // Domain Name
ULONG _ccDomain;
WCHAR * _pwszPassword; // Password to logon domain\user
ULONG _ccPassword;
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:
static ULONG IpAddressFromWStr( WCHAR const * ipAddress );
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 );