//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1995 - 1998. // // File: seccache.cxx // // Contents: Security descriptor cache that maps SDIDs to granted/denied // // Class: CSecurityCache // // History: 25-Sep-95 dlee Created // 22 Jan 96 Alanw Modified for use in user mode // //---------------------------------------------------------------------------- #include #pragma hdrstop // Local includes: #include #include //+--------------------------------------------------------------------------- // // Method: CSecurityCache::CSecurityCache, public // // Synopsis: Creates a CSecurityCache. In user mode, an // impersonation token to use with the AccessCheck call is // obtained. // // History: 22 Jan 96 Alanw Created // //---------------------------------------------------------------------------- CSecurityCache::CSecurityCache( PCatalog & rCat ) : _aEntries( cDefaultSecurityDescriptorEntries ), _hToken( INVALID_HANDLE_VALUE ), _Cat( rCat ) { InitToken(); } //+--------------------------------------------------------------------------- // // Method: CSecurityCache::InitToken, public // // Synopsis: Captures an impersonation token to use with the AccessCheck // call. // // History: 15 Feb 96 Alanw Created // //---------------------------------------------------------------------------- void CSecurityCache::InitToken( ) { DWORD ReturnLength; NTSTATUS status; TOKEN_STATISTICS TokenInformation; status = NtOpenThreadToken( GetCurrentThread(), TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE, // Desired Access TRUE, // OpenAsSelf &_hToken); if (!NT_SUCCESS(status)) { if (status == STATUS_NO_TOKEN) { status = NtOpenProcessToken( GetCurrentProcess(), TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE, // Desired Access &_hToken); } if (!NT_SUCCESS(status)) { vqDebugOut(( DEB_ERROR, "CSecurityCache: failed to get token handle, %x\n", status )); THROW(CException( status )); } } status = NtQueryInformationToken ( _hToken, TokenStatistics, (LPVOID)&TokenInformation, sizeof TokenInformation, &ReturnLength); if (!NT_SUCCESS(status)) { vqDebugOut(( DEB_ERROR, "CSecurityCache: failed to get token info, %x\n", status )); THROW(CException( status )); } if ( TokenInformation.TokenType != TokenImpersonation ) { HANDLE hNewToken = INVALID_HANDLE_VALUE; OBJECT_ATTRIBUTES ObjA; SECURITY_QUALITY_OF_SERVICE SecurityQOS; SecurityQOS.Length = sizeof( SECURITY_QUALITY_OF_SERVICE ); SecurityQOS.ImpersonationLevel = SecurityIdentification; SecurityQOS.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING; SecurityQOS.EffectiveOnly = FALSE; InitializeObjectAttributes( &ObjA, NULL, 0, NULL, NULL ); ObjA.SecurityQualityOfService = &SecurityQOS; status = NtDuplicateToken( _hToken, TOKEN_IMPERSONATE | TOKEN_QUERY, &ObjA, FALSE, TokenImpersonation, &hNewToken ); if (! NT_SUCCESS( status ) ) { vqDebugOut(( DEB_ERROR, "CSecurityCache: failed to duplicate token, %x\n", status )); THROW(CException( status )); } NtClose( _hToken ); _hToken = hNewToken; } } CSecurityCache::~CSecurityCache() { if ( INVALID_HANDLE_VALUE != _hToken ) NtClose( _hToken ); } //+--------------------------------------------------------------------------- // // Method: CSecurityCache::_IsCached, private // // Synopsis: Determines whether a sdid is granted access given the // cache's security context. // // Arguments: [sdidOrd] -- security descriptor ordinal to test // [am] -- access mask of the request // [fGranted] -- if return value is TRUE, this is either // TRUE (if access is granted) or FALSE. // // Returns: TRUE if sdid was in the cache and fGranted is set // FALSE if sdid is not cached and fGranted should be ignored // // History: 25-Sep-95 dlee Created // //---------------------------------------------------------------------------- inline BOOL CSecurityCache::_IsCached( ULONG sdidOrd, ACCESS_MASK am, BOOL & fGranted ) const { // Look for the sdid in the cache for ( unsigned i = 0; i < _aEntries.Count(); i++ ) { if ( ( _aEntries[i].sdidOrd == sdidOrd ) && ( _aEntries[i].am == am ) ) { fGranted = _aEntries[i].fGranted; return TRUE; } } return FALSE; } //_IsCached //+--------------------------------------------------------------------------- // // Method: CSecurityCache::IsGranted, public // // Synopsis: Determines whether a security ordinal is granted access given // the cache's security context, and caches the result. // // Arguments: [sdidOrdinal] -- security descriptor ordinal to test // [am] -- access mask of the request, one or more of // FILE_READ_ATTRIBUTES // FILE_READ_DATA / FILE_LIST_DIRECTORY // FILE_TRAVERSE // // Returns: TRUE if sdid was granted access, FALSE otherwise // // History: 25-Sep-95 dlee Created // 22 Jan 96 Alanw Modified for use in user mode // //---------------------------------------------------------------------------- BOOL CSecurityCache::IsGranted( ULONG sdidOrdinal, ACCESS_MASK am ) { // if nothing asked for, grant if ( 0 == am ) return TRUE; if ( sdidNull == sdidOrdinal ) return TRUE; if ( sdidInvalid == sdidOrdinal ) return FALSE; BOOL fGranted; { CLock lock( _mutex ); if ( _IsCached( sdidOrdinal, am, fGranted ) ) return fGranted; } // do the security check fGranted = FALSE; BOOL fResult = _Cat.AccessCheck( sdidOrdinal, GetToken(), am, fGranted); if (! fResult) { DWORD dwError = GetLastError(); Win4Assert( fResult && dwError == NO_ERROR ); } // Not cached yet -- do so vqDebugOut(( DEB_ITRACE, "cacheing sdid %x, granted: %x\n", sdidOrdinal, fGranted )); { CLock lock( _mutex ); // check the cache again -- it may have slipped in via a different // thread while we weren't holding the lock. if ( !_IsCached( sdidOrdinal, am, fGranted ) ) { unsigned i = _aEntries.Count(); _aEntries[i].sdidOrd = sdidOrdinal; _aEntries[i].am = am; _aEntries[i].fGranted = fGranted; } } return fGranted; } //IsGranted