//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1996 - 2000. // // File: secstore.cxx // // Contents: SDID to security descriptor mapping table // // History: 29 Jan 1996 AlanW Created // //---------------------------------------------------------------------------- #include #pragma hdrstop #include #include #include #include #include //+------------------------------------------------------------------------- // // Method: CSdidLookupTable::CSdidLookupTable, public // // Synopsis: Constructor of a CSdidLookupTable // // Arguments: -NONE- // // Returns: Nothing // //-------------------------------------------------------------------------- CSdidLookupTable::CSdidLookupTable( ) : _pTable( 0 ), _xrsoSdidTable( 0 ), _mutex(), _cache() { } void CSdidLookupTable::Empty() { CLock lock ( _mutex ); delete [] _pTable; _pTable = 0; _xrsoSdidTable.Free(); _cache.Empty(); } CSdidLookupTable::~CSdidLookupTable( ) { Empty(); } //+--------------------------------------------------------------------------- // // Member: CSdidLookupTable::Init, public // // Synopsis: Loads metadata from persistent location into memory. // // Arguments: [pobj] -- Stream(s) in which metadata is stored. // // History: 27-Dec-95 KyleP Created. // //---------------------------------------------------------------------------- BOOL CSdidLookupTable::Init( CiStorage * pobj ) { CLock lock ( _mutex ); _xrsoSdidTable.Set( pobj->QuerySdidLookupTable( eSecStoreWid ) ); // // Load header // CRcovStorageHdr & hdr = _xrsoSdidTable->GetHeader(); struct CRcovUserHdr data; hdr.GetUserHdr( hdr.GetPrimary(), data ); RtlCopyMemory( &_Header, &data._abHdr, sizeof(_Header) ); ciDebugOut(( DEB_SECSTORE, "SECSTORE: Record size = %d bytes\n", _Header.cbRecord )); ciDebugOut(( DEB_SECSTORE, "SECSTORE: %d file records\n", _Header.cRecords )); ciDebugOut(( DEB_SECSTORE, "SECSTORE: Hash size = %u\n", _Header.cHash )); if ( _Header.cHash == 0 ) { Win4Assert( 0 == Records() && _Header.cbRecord == 0 ); RtlCopyMemory( _Header.Signature, "SECSTORE", sizeof _Header.Signature ); Win4Assert( (sizeof (SSdHeaderRecord) + SECURITY_DESCRIPTOR_MIN_LENGTH) < SECSTORE_REC_SIZE ); _Header.cbRecord = SECSTORE_REC_SIZE; _Header.cHash = SECSTORE_HASH_SIZE; _Header.cRecords = 0; } else { Win4Assert( RtlEqualMemory( _Header.Signature, "SECSTORE", sizeof _Header.Signature) && _Header.cbRecord == SECSTORE_REC_SIZE && _Header.cHash == SECSTORE_HASH_SIZE ); if ( ! RtlEqualMemory( _Header.Signature, "SECSTORE", sizeof _Header.Signature) || _Header.cbRecord != SECSTORE_REC_SIZE || _Header.cHash != SECSTORE_HASH_SIZE ) return FALSE; } // // Load hash table // ULONG cRecordsFromFile = Records(); ULONG iRecord = 1; _pTable = new SDID [ _Header.cHash ]; RtlZeroMemory( _pTable, _Header.cHash * sizeof (SDID) ); _Header.cRecords = 0; #if (DBG == 1) _cMaxChainLen = 0; _cTotalSearches = 0; _cTotalLength = 0; #endif // (DBG == 1) CRcovStrmReadTrans xact( _xrsoSdidTable.GetReference() ); CRcovStrmReadIter iter( xact, SECSTORE_REC_SIZE ); BYTE temp[ SECSTORE_REC_SIZE ]; while ( iter.GetRec( &temp, iRecord-1 ) ) { SSdHeaderRecord SdHdr = *(SSdHeaderRecord *)temp; Win4Assert( SdHdr.cbSD >= SECURITY_DESCRIPTOR_MIN_LENGTH && SdHdr.cbSD < 256 * 1024 && _pTable[ SdHdr.ulHash % SECSTORE_HASH_SIZE ] == SdHdr.iHashChain ); if ( SdHdr.cbSD < SECURITY_DESCRIPTOR_MIN_LENGTH || SdHdr.cbSD >= 256 * 1024 || _pTable[ SdHdr.ulHash % SECSTORE_HASH_SIZE ] != SdHdr.iHashChain ) return FALSE; _pTable[ SdHdr.ulHash % SECSTORE_HASH_SIZE ] = iRecord; ciDebugOut(( DEB_SECSTORE, "SECSTORE: SD record\tSDID = %d, cb = %d, hash = %08x, chain = %d\n", iRecord, SdHdr.cbSD, SdHdr.ulHash, SdHdr.iHashChain )); #ifdef UNIT_TEST // much below is debug code; don't need to allocate // the SD here; just seek to the start of each record // and read the record header. XArray pbSD ( SdHdr.cbSD ); BYTE * pbDst = pbSD.GetPointer(); BYTE * pbSrc = &temp[0] + sizeof (SSdHeaderRecord); ULONG cb = SdHdr.cbSD; ULONG cbPart = SECSTORE_REC_SIZE - sizeof (SSdHeaderRecord); if (cb < cbPart) cbPart = cb; RtlCopyMemory( pbDst, pbSrc, cbPart ); pbDst += cbPart; cb -= cbPart; pbSrc = &temp[0]; while( 0 != cb ) { iter.GetRec( temp ); cbPart = (cb > SECSTORE_REC_SIZE) ? SECSTORE_REC_SIZE : cb; RtlCopyMemory( pbDst, pbSrc, cbPart ); pbDst += cbPart; cb -= cbPart; pbSrc = &temp[0]; iRecord++; } PSECURITY_DESCRIPTOR pSD = pbSD.GetPointer(); Win4Assert( SdHdr.cbSD == GetSecurityDescriptorLength( pSD ) && SdHdr.ulHash == Hash( pSD, SdHdr.cbSD ) ); iRecord++; #else iRecord += (SdHdr.cbSD + (sizeof SdHdr) + SECSTORE_REC_SIZE - 1) / SECSTORE_REC_SIZE; #endif _Header.cRecords = iRecord - 1; } Win4Assert( Records() == cRecordsFromFile ); return TRUE; } //+------------------------------------------------------------------------- // // Method: CSdidLookupTable::Hash, public // // Synopsis: Generate a hash value for the passed SECURITY_DESCRIPTOR // // Arguments: [pSD] -- pointer to SECURITY_DESCRIPTOR // [cb] -- length of SECURITY_DESCRIPTOR in bytes // // Returns: ULONG - Hash value for the input SECURITY_DESCRIPTOR // //-------------------------------------------------------------------------- ULONG CSdidLookupTable::Hash(const PSECURITY_DESCRIPTOR pSD, unsigned cb) { ULONG ulHash = 0; BYTE * pb = (BYTE *) pSD; while (cb-- != 0) { if (ulHash & 0x80000000) { ulHash = (ulHash << 1) | 1; } else { ulHash <<= 1; } ulHash ^= *pb++; } return(ulHash); } //+--------------------------------------------------------------------------- // // Method: CSdidLookupTable::Lookup, private // // Synopsis: Looks up a security descriptor in the table. // // Arguments: [sdid] - SDID to look up. // // Returns: CSdidLookupEntry* - pointer to entry for SDID // // History: 29 Jan 1996 Alanw Created // // Notes: The security descriptor entry will be owned by // the caller after the call. // //---------------------------------------------------------------------------- CSdidLookupEntry * CSdidLookupTable::Lookup( SDID sdid ) { Win4Assert( sdid <= Records() ); Win4Assert( !_xrsoSdidTable.IsNull() ); if ( sdid > Records() ) { return 0; } CSdidLookupEntry * pEntry = 0; CLock lock ( _mutex ); // // First see if the desired item is in the cache // for ( CSdidCacheIter listiter( _cache ); !_cache.AtEnd( listiter ); _cache.Advance( listiter ) ) { if ( listiter.GetEntry()->Sdid() == sdid ) { pEntry = listiter.GetEntry(); _cache.RemoveFromList( pEntry ); return pEntry; } } XPtr xEntry; // // The entry was not in the cache. Read it from storage. // TRY { // Corrupt? if (_xrsoSdidTable.IsNull()) THROW(CException(CI_CORRUPT_DATABASE)); xEntry.Set( new CSdidLookupEntry(sdid) ); CRcovStrmReadTrans xact( _xrsoSdidTable.GetReference() ); CRcovStrmReadIter iter( xact, SECSTORE_REC_SIZE ); LoadTableEntry( iter, xEntry.GetReference(), sdid ); } CATCH(CException, e) { ciDebugOut(( DEB_WARN, "CSdidLookupTable::Lookup - exception %x\n", e.GetErrorCode() )); if (e.GetErrorCode() != STATUS_ACCESS_VIOLATION) RETHROW(); } END_CATCH return xEntry.Acquire(); } //+--------------------------------------------------------------------------- // // Method: CSdidLookupTable::LookUpSDID, public // // Synopsis: Looks up a security descriptor's ID in the table. // Add the SD to the table if not found. // // Arguments: [pSD] - SD to look up. // [cbSD] - size of security descriptor // // Returns: SDID - ID of security descriptor input // // History: 29 Jan 1996 Alanw Created // // Notes: // //---------------------------------------------------------------------------- SDID CSdidLookupTable::LookupSDID( PSECURITY_DESCRIPTOR pSD, ULONG cbSD ) { Win4Assert( (((SECURITY_DESCRIPTOR *)pSD)->Control & SE_SELF_RELATIVE) && GetSecurityDescriptorLength( pSD ) == cbSD ); Win4Assert( !_xrsoSdidTable.IsNull() ); SDID iSdid = 0; BOOL fFound = FALSE; #if (DBG == 1) ULONG cSearchLen = 0; #endif // (DBG == 1) ULONG ulHash = Hash( pSD, cbSD ); CLock lock ( _mutex ); // // First see if a matching item is in the cache // for ( CSdidCacheIter listiter( _cache ); !_cache.AtEnd( listiter ); _cache.Advance( listiter ) ) { if ( listiter.GetEntry()->IsEqual( pSD, cbSD, ulHash ) ) { Win4Assert( listiter.GetEntry()->Sdid() > 0 ); return listiter.GetEntry()->Sdid(); } } // // The SD was not found in the cache. Try looking in storage. // TRY { SDID iNext = _pTable[ ulHash % HashSize() ]; if (iNext != 0) { CRcovStrmReadTrans xact( _xrsoSdidTable.GetReference() ); CRcovStrmReadIter iter( xact, SECSTORE_REC_SIZE ); BYTE temp[ SECSTORE_REC_SIZE ]; while (iNext != 0) { #if (DBG == 1) cSearchLen++; #endif // (DBG == 1) iter.GetRec( &temp, iNext-1 ); SSdHeaderRecord * pSdHdr = (SSdHeaderRecord *)temp; Win4Assert( pSdHdr->cbSD >= SECURITY_DESCRIPTOR_MIN_LENGTH && pSdHdr->iHashChain < iNext ); if (pSdHdr->cbSD == cbSD && pSdHdr->ulHash == ulHash) { // The byte count and hash value match. Fetch the rest of // the SD to compare it byte-for-byte. XPtr xEntry( new CSdidLookupEntry( iNext ) ); LoadTableEntry( iter, xEntry.GetReference(), iNext ); if (RtlEqualMemory( pSD, xEntry->GetSD(), cbSD)) { fFound = TRUE; iSdid = iNext; _cache.Add( xEntry.Acquire() ); } } iNext = pSdHdr->iHashChain; } } if (! fFound) { // // The SD was not found. // Write new mapping to the recoverable storage. // iSdid = Records() + 1; CRcovStorageHdr & hdr = _xrsoSdidTable->GetHeader(); CRcovStrmAppendTrans xact( _xrsoSdidTable.GetReference() ); CRcovStrmAppendIter iter( xact, SECSTORE_REC_SIZE ); BYTE temp[ SECSTORE_REC_SIZE ]; SSdHeaderRecord * pSdHdr = (SSdHeaderRecord *)temp; pSdHdr->cbSD = cbSD; pSdHdr->ulHash = ulHash; pSdHdr->iHashChain = _pTable[ ulHash % HashSize() ]; BYTE * pbDst = &temp[0] + sizeof (SSdHeaderRecord); BYTE * pbSrc = (BYTE *)pSD; ULONG cb = cbSD; ULONG cbPart = SECSTORE_REC_SIZE - sizeof (SSdHeaderRecord); if (cb < cbPart) cbPart = cb; RtlCopyMemory( pbDst, pbSrc, cbPart ); pbSrc += cbPart; cb -= cbPart; iter.AppendRec( temp ); ULONG cRecordsWritten = 1; while( 0 != cb ) { if (cb >= SECSTORE_REC_SIZE) { iter.AppendRec( pbSrc ); cbPart = SECSTORE_REC_SIZE; } else { cbPart = (cb > SECSTORE_REC_SIZE) ? SECSTORE_REC_SIZE : cb; RtlCopyMemory( temp, pbSrc, cbPart ); RtlZeroMemory( &temp[cbPart], SECSTORE_REC_SIZE - cbPart ); iter.AppendRec( temp ); } pbSrc += cbPart; cb -= cbPart; cRecordsWritten++; } ciDebugOut(( DEB_SECSTORE, "SECSTORE: new SD record\tSDID = %d, cb = %d, hash = %08x, chain = %d\n", iSdid, cbSD, ulHash, _pTable[ ulHash % SECSTORE_HASH_SIZE ] )); _pTable[ ulHash % SECSTORE_HASH_SIZE ] = iSdid; _Header.cRecords += cRecordsWritten; struct CRcovUserHdr data; RtlCopyMemory( &data._abHdr, &_Header, sizeof(_Header) ); Win4Assert( hdr.GetCount(hdr.GetBackup()) == hdr.GetCount(hdr.GetPrimary()) + cRecordsWritten); hdr.SetUserHdr( hdr.GetBackup(), data ); xact.Commit(); } } CATCH(CException, e) { ciDebugOut(( DEB_WARN, "CSdidLookupTable::LookupSDID - exception %x\n", e.GetErrorCode() )); if (e.GetErrorCode() == STATUS_ACCESS_VIOLATION) { Win4Assert( !"Access violation in CSdidLookupTable::LookupSDID - " "Are you running two queries on the same downlevel catalog?" ); } RETHROW(); } END_CATCH #if (DBG == 1) // Update search statistics _cTotalSearches++; if (fFound) { _cTotalLength += cSearchLen; } else { if (cSearchLen >= _cMaxChainLen) _cMaxChainLen = cSearchLen + 1; } #endif // (DBG == 1) return iSdid; } //+--------------------------------------------------------------------------- // // Method: CSdidLookupTable::LoadTableEntry, private // // Synopsis: Loads a table entry for some SDID from the table. // // Arguments: [Iter] - CRcovStrmReadIter for access to the stream // [Entry] - CSdidTableEntry to be filled in // [iSdid] - SDID to be looked up. // // Returns: Nothing // // History: 29 Jan 1996 Alanw Created // // Notes: // //---------------------------------------------------------------------------- void CSdidLookupTable::LoadTableEntry( CRcovStrmReadIter & iter, CSdidLookupEntry & Entry, SDID iSdid ) { Win4Assert( iSdid <= Records() ); BYTE temp[ SECSTORE_REC_SIZE ]; iter.GetRec( &temp, iSdid-1 ); Entry._hdr = *(SSdHeaderRecord *)temp; Win4Assert( Entry._hdr.cbSD >= SECURITY_DESCRIPTOR_MIN_LENGTH && Entry._hdr.cbSD < 256 * 1024 ); ciDebugOut(( DEB_SECSTORE, "SECSTORE: SD record\tSDID = %d, cb = %d, hash = %08x, chain = %d\n", iSdid, Entry._hdr.cbSD, Entry._hdr.ulHash, Entry._hdr.iHashChain )); XArray pbSD ( Entry._hdr.cbSD ); BYTE * pbDst = pbSD.GetPointer(); BYTE * pbSrc = &temp[0] + sizeof (SSdHeaderRecord); ULONG cb = Entry._hdr.cbSD; ULONG cbPart = SECSTORE_REC_SIZE - sizeof (SSdHeaderRecord); if (cb < cbPart) cbPart = cb; RtlCopyMemory( pbDst, pbSrc, cbPart ); pbDst += cbPart; cb -= cbPart; pbSrc = &temp[0]; while( 0 != cb ) { if (cb >= SECSTORE_REC_SIZE) { iter.GetRec( pbDst ); cbPart = SECSTORE_REC_SIZE; } else { iter.GetRec( temp ); cbPart = (cb > SECSTORE_REC_SIZE) ? SECSTORE_REC_SIZE : cb; RtlCopyMemory( pbDst, pbSrc, cbPart ); } pbDst += cbPart; cb -= cbPart; pbSrc = &temp[0]; } Win4Assert( Entry._hdr.cbSD == GetSecurityDescriptorLength( pbSD.GetPointer() ) && Entry._hdr.ulHash == Hash( pbSD.GetPointer(), Entry._hdr.cbSD ) ); Entry._pSD = pbSD.Acquire(); } //+--------------------------------------------------------------------------- // // Method: CSdidLookupTable::AccessCheck, public // // Synopsis: Performs an access check for some SDID, access mask combination // // Arguments: [sdid] - SDID of file to be checked // [hToken] - security token to be checked against // [am] - access mode to be checked against // [fGranted] - TRUE is access is granted, FALSE otherwise // // Returns: BOOL - TRUE if access check was successful // // History: 05 Feb 1996 Alanw Created // // Notes: // //---------------------------------------------------------------------------- GENERIC_MAPPING gmFile = { FILE_GENERIC_READ, FILE_GENERIC_WRITE, FILE_GENERIC_EXECUTE, FILE_ALL_ACCESS }; BOOL CSdidLookupTable::AccessCheck( SDID sdid, HANDLE hToken, ACCESS_MASK am, BOOL & fGranted ) { Win4Assert( sdidInvalid != sdid && sdidNull != sdid ); CSdidLookupEntry * pSD = Lookup( sdid ); fGranted = FALSE; if ( 0 == pSD ) return FALSE; PRIVILEGE_SET ps; ULONG ulPrivSize = sizeof ps; ACCESS_MASK GrantedAccess; BOOL fResult = ::AccessCheck( pSD->GetSD(), hToken, am, &gmFile, &ps, &ulPrivSize, &GrantedAccess, &fGranted); { CLock lock ( _mutex ); _cache.Add( pSD ); } return fResult; } //+--------------------------------------------------------------------------- // // Member: CSdidLookupTable::GetSecurityDescriptor // // Synopsis: Retrieves the security descriptor for the given SDID. // // Arguments: [sdid] - SDID to lookup // [pbData] - Pointer to the buffer to write the desc. // [cbIn] - Size of the pSD buffer // [cbOut] - Size of the security descriptor; if cbIn < cbOut, // then the buffer is not big enough to copy the data. // // Returns: S_OK if successfully returned. // S_FALSE if the buffer is not big enough to hold // the data. In this case cbOut will have the actual buffer // needed. // CI_E_NOT_FOUND the sdid is not valid. // // History: 7-18-97 srikants Created // //---------------------------------------------------------------------------- HRESULT CSdidLookupTable::GetSecurityDescriptor( SDID sdid, PSECURITY_DESCRIPTOR pbData, ULONG cbIn, ULONG & cbOut ) { Win4Assert( sdidInvalid != sdid && sdidNull != sdid ); CSdidLookupEntry * pSD = Lookup( sdid ); if ( 0 == pSD ) return CI_E_NOT_FOUND; cbOut = pSD->Size(); if ( cbOut > cbIn ) return S_FALSE; RtlCopyMemory( pbData, pSD->GetSD(), cbOut ); return S_OK; } //+--------------------------------------------------------------------------- // // Member: CSdidLookupTable::Save // // Synopsis: Makes a copy of the current security table using the // destination storage object. // // Arguments: [pIProgressNotify] - Progress notification. // [fAbort] - Flag set to TRUE if the copy must // be aborted in the middle. // [dstStorage] - Destination storage object to use // for creating the bakcup. // [ppFileList] - List of files that constitute the // the security store. // // History: 7-14-97 srikants Created // //---------------------------------------------------------------------------- void CSdidLookupTable::Save( IProgressNotify * pIProgressNotify, BOOL & fAbort, CiStorage & dstStorage, IEnumString **ppFileList ) { dstStorage.RemoveSecStore( eSecStoreWid ); XPtr xObj( dstStorage.QuerySdidLookupTable( eSecStoreWid ) ); // =============================================================== CLock lock ( _mutex ); // // Make a copy of the security table. // Win4Assert( !_xrsoSdidTable.IsNull() ); CCopyRcovObject copyRcov( xObj.GetReference(), _xrsoSdidTable.GetReference() ); copyRcov.DoIt(); // // Retrive the names of the files that constitute the security store. // CEnumString * pEnumString = new CEnumString(); XInterface xEnumStr(pEnumString); dstStorage.ListSecStoreFileNames( *pEnumString, 0 ); *ppFileList = xEnumStr.Acquire(); // =============================================================== } //+--------------------------------------------------------------------------- // // Member: CSdidLookupTable::Load // // Synopsis: Loads the security store from a saved location into the // target directory. // // Arguments: [pwszDestDir] - Destination directory to load to // [pFileList] - List of the files. // [pProgressNotify] - Progress notification. // [fCallerOwnsFiles] - If the caller owns files. // [pfAbort] - Set to TRUE if must be aborted. // // History: 7-18-97 srikants Created // //---------------------------------------------------------------------------- void CSdidLookupTable::Load( CiStorage * pobj, IEnumString * pFileList, IProgressNotify * pProgressNotify, BOOL fCallerOwnsFiles, BOOL * pfAbort ) { // =============================================================== CLock lock ( _mutex ); Win4Assert(pobj); Win4Assert(pFileList); Win4Assert(pfAbort); ULONG ulFetched; WCHAR * pwszFilePath; while ( !(*pfAbort) && (S_OK == pFileList->Next(1, &pwszFilePath, &ulFetched)) ) { pobj->CopyGivenFile( pwszFilePath, !fCallerOwnsFiles ); } } //+--------------------------------------------------------------------------- // // Method: CSdidCache::Add, public // // Synopsis: Add an SDID record to the lookaside cache // // Arguments: [pEntry] - the item to be added to the cache // // Returns: Nothing // // History: 18 Apr 1996 Alanw Created // // Notes: The cache must be locked when this method is called. // //---------------------------------------------------------------------------- void CSdidCache::Add( CSdidLookupEntry * pEntry ) { // // If the cache is full, check to see if the item is in the cache. Another // copy may have been added while we were using this one. // If there is space in the cache, allow multiple copies of the same SDID. // if ( Count() >= _maxEntries ) { for ( CSdidCacheIter iter( *this ); !AtEnd( iter ); Advance( iter ) ) { if ( iter.GetEntry()->Sdid() == pEntry->Sdid() ) { MoveToFront( iter.GetEntry() ); delete pEntry; return; } } } // // Add the entry to the front of the list. If there are too many // items in the cache, delete the last entry. // Push( pEntry ); if ( Count() > _maxEntries ) { delete RemoveLast(); } } //+--------------------------------------------------------------------------- // // Method: CSdidCache::Empty, public // // Synopsis: Clean out the lookaside cache // // Arguments: NONE // // Returns: Nothing // // History: 18 Apr 1996 Alanw Created // // Notes: // //---------------------------------------------------------------------------- void CSdidCache::Empty( ) { CSdidLookupEntry * pEntry = 0; while ( pEntry = Pop() ) delete pEntry; }