summaryrefslogtreecommitdiff
path: root/src/vm/securityconfig.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/vm/securityconfig.cpp')
-rw-r--r--src/vm/securityconfig.cpp2181
1 files changed, 0 insertions, 2181 deletions
diff --git a/src/vm/securityconfig.cpp b/src/vm/securityconfig.cpp
deleted file mode 100644
index 9c7970db8c..0000000000
--- a/src/vm/securityconfig.cpp
+++ /dev/null
@@ -1,2181 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-//
-// File: SecurityConfig.cpp
-//
-
-//
-// Native implementation for security config access and manipulation
-//
-
-
-// #SecurityConfigFormat
-//
-// The security config system resides outside of the rest
-// of the config system since our needs are different. The
-// unmanaged portion of the security config system is only
-// concerned with data file/cache file pairs, not what they
-// are used for. It performs all the duties of reading data
-// from the disk, saving data back to the disk, and maintaining
-// the policy and quick cache data structures.
-//
-// FILE FORMAT
-//
-// The data file is a purely opaque blob for the unmanaged
-// code; however, the cache file is constructed and maintained
-// completely in the unmanaged code. It's format is as follows:
-//
-// CacheHeader
-// |
-// +-- dummyFileTime (FILETIME, 8 bytes) = this exists to make sure we don't read old format cache files. Must be set to {1, 0}.
-// |
-// +-- version (DWORD) = The version of this config file.
-// |
-// +-- configFileTime (FILETIME, 8 bytes) = The file time of the config file associated with this cache file.
-// |
-// +-- isSecurityOn (DWORD, 4 bytes) = This is currently not used.
-// |
-// +-- quickCache (DWORD, 4 bytes) = Used as a bitfield to maintain the information for the QuickCache. See the QuickCache section for more details.
-// |
-// +-- registryExtensionsInfo (struct RegistryExtensionsInfo) = Indicates whether this cache file was generated in the presence of registry extensions.
-// |
-// +-- numEntries (DWORD, 4 bytes) = The number of policy cache entries in the latter portion of this cache file.
-// |
-// +-- sizeConfig (DWORD, 4 bytes) = The size of the config information stored in the latter portion of this cache file.
-//
-// Config Data (if any)
-// The cache file can include an entire copy of this
-// information in the adjoining config file. This is
-// necessary since the cache often allows us to make
-// policy decisions without having parsed the data in
-// the config file. In order to guarantee that the config
-// data used by this process is not altered in the
-// meantime, we need to store the data in a readonly
-// location. Due to the design of the caching system
-// the cache file is locked when it is opened and therefore
-// is the perfect place to store this information. The
-// other alternative is to hold it in memory, but since
-// this can amount to many kilobytes of data we decided
-// on this design.
-//
-// List of CacheEntries
-// |
-// +-- CacheEntry
-// | |
-// | +-- numItemsInKey (DWORD, 4 bytes) = The number of evidence objects serialized in the key blob
-// | |
-// | +-- keySize (DWORD, 4 bytes) = The number of bytes in the key blob.
-// | |
-// | +-- dataSize (DWORD, 4 bytes) = The number of bytes in the data blob.
-// | |
-// | +-- keyBlob (raw) = A raw blob representing the serialized evidence.
-// | |
-// | +-- dataBlob (raw) = A raw blob representing an XML serialized PolicyStatement
-// |
-// +-- ...
-
-#include "common.h"
-
-#ifdef FEATURE_CAS_POLICY
-
-#include "securityconfig.h"
-
-// Header version of the cache file.
-#define CONFIG_VERSION 2
-// This controls the maximum size of the cache file.
-#define MAX_CACHEFILE_SIZE (1 << 20)
-
-#define SIZE_OF_ENTRY( X ) sizeof( CacheEntryHeader ) + X->header.keySize + X->header.dataSize
-#define MAX_NUM_LENGTH 16
-
-WCHAR* SecurityConfig::wcscatDWORD( __out_ecount(cchdst) __out_z WCHAR* dst, size_t cchdst, DWORD num )
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_ANY;
- }
- CONTRACTL_END
-
- _ASSERTE( SecurityConfig::dataLock_.OwnedByCurrentThread() );
-
- static WCHAR buffer[MAX_NUM_LENGTH];
-
- buffer[MAX_NUM_LENGTH-1] = W('\0');
-
- size_t index = MAX_NUM_LENGTH-2;
-
- if (num == 0)
- {
- buffer[index--] = W('0');
- }
- else
- {
- while (num != 0)
- {
- buffer[index--] = (WCHAR)(W('0') + (num % 10));
- num = num / 10;
- }
- }
-
- wcscat_s( dst, cchdst, buffer + index + 1 );
-
- return dst;
-}
-
-inline WCHAR * Wszdup(const WCHAR * str)
-{
- CONTRACTL
- {
- THROWS;
- GC_NOTRIGGER;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- size_t len = wcslen(str) + 1;
- WCHAR * ret = new WCHAR[len];
- wcscpy_s(ret, len, str);
- return ret;
-}
-
-struct CacheHeader
-{
- FILETIME dummyFileTime;
- DWORD version;
- FILETIME configFileTime;
- DWORD isSecurityOn, quickCache;
- SecurityConfig::RegistryExtensionsInfo registryExtensionsInfo;
- DWORD numEntries, sizeConfig;
-
- CacheHeader() : isSecurityOn( (DWORD) -1 ), quickCache( 0 ), numEntries( 0 ), sizeConfig( 0 )
- {
- WRAPPER_NO_CONTRACT;
- memset( &this->configFileTime, 0, sizeof( configFileTime ) );
- dummyFileTime.dwLowDateTime = 1;
- dummyFileTime.dwHighDateTime = 0;
- version = CONFIG_VERSION;
- memset(&registryExtensionsInfo, 0, sizeof(registryExtensionsInfo));
- _ASSERTE( IsValid() && "CacheHeader constructor should make it valid" );
- };
-
- bool IsValid()
- {
- LIMITED_METHOD_CONTRACT;
- return dummyFileTime.dwLowDateTime == 1 &&
- dummyFileTime.dwHighDateTime == 0 &&
- version == CONFIG_VERSION;
- }
-};
-
-struct CacheEntryHeader
-{
- DWORD numItemsInKey;
- DWORD keySize;
- DWORD dataSize;
-};
-
-struct CacheEntry
-{
- CacheEntryHeader header;
- BYTE* key;
- BYTE* data;
- DWORD cachePosition;
- BOOL used;
-
- CacheEntry() : key( NULL ), data( NULL ), used( FALSE )
- {
- LIMITED_METHOD_CONTRACT;
- };
-
- ~CacheEntry( void )
- {
- WRAPPER_NO_CONTRACT;
- delete [] key;
- delete [] data;
- }
-};
-
-struct Data
-{
- enum State
- {
- None = 0x0,
- UsingCacheFile = 0x1,
- CopyCacheFile = 0x2,
- CacheUpdated = 0x4,
- UsingConfigFile = 0x10,
- CacheExhausted = 0x20,
- NewConfigFile = 0x40
- };
-
- INT32 id;
- WCHAR* configFileName;
- WCHAR* cacheFileName;
- WCHAR* cacheFileNameTemp;
-
- LPBYTE configData;
- DWORD configDataSize;
- FILETIME configFileTime;
- FILETIME cacheFileTime;
- CacheHeader header;
- ArrayList* oldCacheEntries;
- ArrayList* newCacheEntries;
- State state;
- DWORD cacheCurrentPosition;
- HANDLE cache;
- PBYTE configBuffer;
- DWORD sizeConfig;
- SecurityConfig::ConfigRetval initRetval;
- DWORD newEntriesSize;
-
- Data( INT32 id )
- : id( id ),
- configFileName( NULL ),
- cacheFileName( NULL ),
- configData( NULL ),
- oldCacheEntries( new ArrayList ),
- newCacheEntries( new ArrayList ),
- state( Data::None ),
- cache( INVALID_HANDLE_VALUE ),
- configBuffer( NULL ),
- newEntriesSize( 0 )
- {
- LIMITED_METHOD_CONTRACT;
- }
-
- Data( INT32 id, STRINGREF* configFile )
- : id( id ),
- cacheFileName( NULL ),
- configData( NULL ),
- oldCacheEntries( new ArrayList ),
- newCacheEntries( new ArrayList ),
- state( Data::None ),
- cache( INVALID_HANDLE_VALUE ),
- configBuffer( NULL ),
- newEntriesSize( 0 )
- {
- CONTRACTL {
- THROWS;
- GC_NOTRIGGER;
- MODE_ANY;
- PRECONDITION(*configFile != NULL);
- } CONTRACTL_END;
-
- configFileName = Wszdup( (*configFile)->GetBuffer() );
- cacheFileName = NULL;
- cacheFileNameTemp = NULL;
- }
-
- Data( INT32 id, STRINGREF* configFile, STRINGREF* cacheFile )
- : id( id ),
- configData( NULL ),
- oldCacheEntries( new ArrayList ),
- newCacheEntries( new ArrayList ),
- state( Data::None ),
- cache( INVALID_HANDLE_VALUE ),
- configBuffer( NULL ),
- newEntriesSize( 0 )
- {
- CONTRACTL {
- THROWS;
- GC_NOTRIGGER;
- MODE_ANY;
- PRECONDITION(*configFile != NULL);
- } CONTRACTL_END;
-
- configFileName = Wszdup( (*configFile)->GetBuffer() );
-
- if (cacheFile != NULL)
- {
- // Since temp cache files can stick around even after the process that
- // created them, we want to make sure they are fairly unique (if they
- // aren't, we'll just fail to save cache information, which is not good
- // but it won't cause anyone to crash or anything). The unique name
- // algorithm used here is to append the process id and tick count to
- // the name of the cache file.
-
- cacheFileName = Wszdup( (*cacheFile)->GetBuffer() );
- size_t len = wcslen( cacheFileName ) + 1 + 2 * MAX_NUM_LENGTH;
- cacheFileNameTemp = new WCHAR[len];
- wcscpy_s( cacheFileNameTemp, len, cacheFileName );
- wcscat_s( cacheFileNameTemp, len, W(".") );
- SecurityConfig::wcscatDWORD( cacheFileNameTemp, len, GetCurrentProcessId() );
- wcscat_s( cacheFileNameTemp, len, W(".") );
- SecurityConfig::wcscatDWORD( cacheFileNameTemp, len, GetTickCount() );
- }
- else
- {
- cacheFileName = NULL;
- cacheFileNameTemp = NULL;
- }
- }
-
- Data( INT32 id, const WCHAR* configFile, const WCHAR* cacheFile )
- : id( id ),
- configData( NULL ),
- oldCacheEntries( new ArrayList ),
- newCacheEntries( new ArrayList ),
- state( Data::None ),
- cache( INVALID_HANDLE_VALUE ),
- configBuffer( NULL ),
- newEntriesSize( 0 )
-
- {
- CONTRACTL {
- THROWS;
- GC_NOTRIGGER;
- MODE_ANY;
- PRECONDITION(*configFile != NULL);
- } CONTRACTL_END;
-
- configFileName = Wszdup( configFile );
-
- if (cacheFile != NULL)
- {
- cacheFileName = Wszdup( cacheFile );
- size_t len = wcslen( cacheFileName ) + 1 + 2 * MAX_NUM_LENGTH;
- cacheFileNameTemp = new WCHAR[len];
- wcscpy_s( cacheFileNameTemp, len, cacheFileName );
- wcscat_s( cacheFileNameTemp, len, W(".") );
- SecurityConfig::wcscatDWORD( cacheFileNameTemp, len, GetCurrentProcessId() );
- wcscat_s( cacheFileNameTemp, len, W(".") );
- SecurityConfig::wcscatDWORD( cacheFileNameTemp, len, GetTickCount() );
- }
- else
- {
- cacheFileName = NULL;
- cacheFileNameTemp = NULL;
- }
- }
-
- void Reset( void )
- {
- CONTRACTL {
- THROWS;
- GC_NOTRIGGER;
- MODE_ANY;
- } CONTRACTL_END;
-
- delete [] configBuffer;
- configBuffer = NULL;
-
- if (cache != INVALID_HANDLE_VALUE)
- {
- CloseHandle( cache );
- cache = INVALID_HANDLE_VALUE;
- }
-
- if (cacheFileNameTemp != NULL)
- {
- // Note: we don't check a return value here as the worst thing that
- // happens is we leave a spurious cache file.
-
- WszDeleteFile( cacheFileNameTemp );
- }
-
- if (configData != NULL)
- delete [] configData;
- configData = NULL;
-
- DeleteAllEntries();
- header = CacheHeader();
-
- oldCacheEntries = new ArrayList();
- newCacheEntries = new ArrayList();
-
- }
-
- void Cleanup( void )
- {
- CONTRACTL {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_ANY;
- } CONTRACTL_END;
-
- if (cache != INVALID_HANDLE_VALUE)
- {
- CloseHandle( cache );
- cache = INVALID_HANDLE_VALUE;
- }
-
- if (cacheFileNameTemp != NULL)
- {
- // Note: we don't check a return value here as the worst thing that
- // happens is we leave a spurious cache file.
-
- WszDeleteFile( cacheFileNameTemp );
- }
- }
-
- ~Data( void )
- {
- CONTRACTL {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_ANY;
- } CONTRACTL_END;
-
- Cleanup();
- delete [] configBuffer;
-
- delete [] configFileName;
- delete [] cacheFileName;
- delete [] cacheFileNameTemp;
-
- if (configData != NULL)
- delete [] configData;
- DeleteAllEntries();
- }
-
- void DeleteAllEntries( void );
-};
-
-void Data::DeleteAllEntries( void )
-{
- CONTRACTL {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_ANY;
- } CONTRACTL_END;
-
- ArrayList::Iterator iter;
-
- if (oldCacheEntries != NULL)
- {
- iter = oldCacheEntries->Iterate();
-
- while (iter.Next())
- {
- delete (CacheEntry*) iter.GetElement();
- }
-
- delete oldCacheEntries;
- oldCacheEntries = NULL;
- }
-
- if (newCacheEntries != NULL)
- {
- iter = newCacheEntries->Iterate();
-
- while (iter.Next())
- {
- delete (CacheEntry*) iter.GetElement();
- }
-
- delete newCacheEntries;
- newCacheEntries = NULL;
- }
-}
-
-void* SecurityConfig::GetData( INT32 id )
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_ANY;
- }
- CONTRACTL_END
-
- ArrayList::Iterator iter = entries_.Iterate();
-
- while (iter.Next())
- {
- Data* data = (Data*)iter.GetElement();
-
- if (data->id == id)
- {
- return data;
- }
- }
-
- return NULL;
-}
-
-static BOOL CacheOutOfDate( FILETIME* configFileTime, __in_z WCHAR* configFileName, __in_z_opt WCHAR* cacheFileName )
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_PREEMPTIVE;
- }
- CONTRACTL_END
-
- BOOL retval = TRUE;
- BOOL deleteFile = FALSE;
-
- HandleHolder config(WszCreateFile( configFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ));
-
- if (config.GetValue() == INVALID_HANDLE_VALUE)
- {
- goto CLEANUP;
- }
-
- // Get the last write time for both files.
-
- FILETIME newConfigTime;
-
- if (!GetFileTime( config.GetValue(), NULL, NULL, &newConfigTime ))
- {
- goto CLEANUP;
- }
-
- if (CompareFileTime( configFileTime, &newConfigTime ) != 0)
- {
- // Cache is dated. Delete the cache.
- deleteFile = TRUE;
- goto CLEANUP;
- }
-
- retval = FALSE;
-
-CLEANUP:
- // Note: deleting this file is a perf optimization so that
- // we don't have to do this file time comparison next time.
- // Therefore, if it fails for some reason we just loss a
- // little perf.
-
- if (deleteFile && cacheFileName != NULL)
- WszDeleteFile( cacheFileName );
-
- return retval;
-}
-
-static BOOL CacheOutOfDate( FILETIME* cacheFileTime, HANDLE cache, __in_z_opt WCHAR* cacheFileName )
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_PREEMPTIVE;
- }
- CONTRACTL_END
-
- BOOL retval = TRUE;
-
- // Get the last write time for both files.
-
- FILETIME newCacheTime;
-
- if (!GetFileTime( cache, NULL, NULL, &newCacheTime ))
- {
- goto CLEANUP;
- }
-
- if (CompareFileTime( cacheFileTime, &newCacheTime ) != 0)
- {
- // Cache is dated. Delete the cache.
- // Note: deleting this file is a perf optimization so that
- // we don't have to do this file time comparison next time.
- // Therefore, if it fails for some reason we just loss a
- // little perf.
-
- if (cacheFileName != NULL)
- {
- CloseHandle( cache );
- WszDeleteFile( cacheFileName );
- }
- goto CLEANUP;
- }
-
- retval = FALSE;
-
-CLEANUP:
- return retval;
-}
-
-static BOOL CacheOutOfDate( FILETIME* configTime, FILETIME* cachedConfigTime )
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_PREEMPTIVE;
- }
- CONTRACTL_END
-
- DWORD result = CompareFileTime( configTime, cachedConfigTime );
-
- return result != 0;
-}
-
-static DWORD GetShareFlags()
-{
- LIMITED_METHOD_CONTRACT;
-
- return FILE_SHARE_READ | FILE_SHARE_DELETE;
-}
-
-static DWORD WriteFileData( HANDLE file, LPCBYTE data, DWORD size )
-{
- CONTRACTL
- {
- NOTHROW;
- GC_TRIGGERS;
- MODE_ANY;
- }
- CONTRACTL_END
-
- DWORD totalBytesWritten = 0;
- DWORD bytesWritten;
-
- do
- {
- if (WriteFile( file, data, size - totalBytesWritten, &bytesWritten, NULL ) == 0)
- {
- return E_FAIL;
- }
- if (bytesWritten == 0)
- {
- return E_FAIL;
- }
- totalBytesWritten += bytesWritten;
- } while (totalBytesWritten < size);
-
- return S_OK;
-}
-
-// the data argument to this function can be a pointer to GC heap.
-// We do ensure cooperative mode before we call this function using a pointer to GC heap,
-// so we can't change GC mode inside this function.
-// Greg will look into the ways to pin the object.
-
-static DWORD ReadFileData( HANDLE file, PBYTE data, DWORD size )
-{
- CONTRACTL
- {
- NOTHROW;
- GC_TRIGGERS;
- MODE_ANY;
- }
- CONTRACTL_END
-
- DWORD totalBytesRead = 0;
- DWORD bytesRead;
- do
- {
- if (ReadFile( file, data, size - totalBytesRead, &bytesRead, NULL ) == 0)
- {
- return E_FAIL;
- }
-
- if (bytesRead == 0)
- {
- return E_FAIL;
- }
-
- totalBytesRead += bytesRead;
-
- } while (totalBytesRead < size);
-
- return S_OK;
-}
-
-SecurityConfig::ConfigRetval SecurityConfig::InitData( INT32 id, const WCHAR* configFileName, const WCHAR* cacheFileName )
-{
- STANDARD_VM_CONTRACT;
-
- Data* data = (Data*)GetData( id );
- if (data != NULL)
- {
- return data->initRetval;
- }
-
- if (configFileName == NULL || wcslen( configFileName ) == 0)
- {
- return NoFile;
- }
-
- {
- CrstHolder ch( &dataLock_ );
- data = new (nothrow) Data( id, configFileName, cacheFileName );
- }
-
- if (data == NULL)
- {
- return NoFile;
- }
-
- return InitData( data, TRUE );
-}
-
-
-SecurityConfig::ConfigRetval SecurityConfig::InitData( void* configDataParam, BOOL addToList )
-{
- STANDARD_VM_CONTRACT;
-
- _ASSERTE( configDataParam != NULL );
-
- Data* data = (Data*) configDataParam;
- DWORD cacheSize;
- DWORD configSize;
- ConfigRetval retval = NoFile;
- DWORD shareFlags;
-
- shareFlags = GetShareFlags();
-
- // Crack open the config file.
-
- HandleHolder config(WszCreateFile( data->configFileName, GENERIC_READ, shareFlags, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ));
- if (config == INVALID_HANDLE_VALUE || !GetFileTime( config, NULL, NULL, &data->configFileTime ))
- {
- memset( &data->configFileTime, 0, sizeof( data->configFileTime ) );
- }
- else
- {
- data->state = (Data::State)(Data::UsingConfigFile | data->state);
- }
-
- // If we want a cache file, try to open that up.
- // Note: we do not use a holder for data->cache because the new holder for data will
- // delete the entire data structure which includes closing this handle as necessary.
-
- if (data->cacheFileName != NULL)
- data->cache = WszCreateFile( data->cacheFileName, GENERIC_READ, shareFlags, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
-
- if (data->cache == INVALID_HANDLE_VALUE)
- {
- goto READ_DATA;
- }
-
- // Validate that the cache file is in a good form by checking
- // that it is at least big enough to contain a header.
-
- cacheSize = SafeGetFileSize( data->cache, NULL );
-
- if (cacheSize == 0xFFFFFFFF)
- {
- goto READ_DATA;
- }
-
- if (cacheSize < sizeof( CacheHeader ))
- {
- goto READ_DATA;
- }
-
- // Finally read the data from the file into the buffer.
-
- if (ReadFileData( data->cache, (BYTE*)&data->header, sizeof( CacheHeader ) ) != S_OK)
- {
- goto READ_DATA;
- }
-
- if (!data->header.IsValid())
- {
- goto READ_DATA;
- }
-
- // Check to make sure the cache file and the config file
- // match up by comparing the actual file time of the config
- // file and the config file time stored in the cache file.
-
- if (CacheOutOfDate( &data->configFileTime, &data->header.configFileTime ))
- {
- goto READ_DATA;
- }
-
- if (!GetFileTime( data->cache, NULL, NULL, &data->cacheFileTime ))
- {
- goto READ_DATA;
- }
-
- // Set the file pointer to after both the header and config data (if any) so
- // that we are ready to read cache entries.
-
- if (SetFilePointer( data->cache, sizeof( CacheHeader ) + data->header.sizeConfig, NULL, FILE_BEGIN ) == INVALID_SET_FILE_POINTER)
- {
- goto READ_DATA;
- }
-
- data->cacheCurrentPosition = sizeof( CacheHeader ) + data->header.sizeConfig;
- data->state = (Data::State)(Data::UsingCacheFile | Data::CopyCacheFile | data->state);
-
- retval = (ConfigRetval)(retval | CacheFile);
-
-READ_DATA:
- // If we are not using the cache file but we successfully opened it, we need
- // to close it now. In addition, we need to reset the cache information
- // stored in the Data object to make sure there is no spill over.
-
- if (data->cache != INVALID_HANDLE_VALUE && (data->state & Data::UsingCacheFile) == 0)
- {
- CloseHandle( data->cache );
- data->header = CacheHeader();
- data->cache = INVALID_HANDLE_VALUE;
- }
-
- if (config != INVALID_HANDLE_VALUE)
- {
- configSize = SafeGetFileSize( config, NULL );
-
- if (configSize == 0xFFFFFFFF)
- {
- goto ADD_DATA;
- }
-
- // Be paranoid and only use the cache file version if we find that it has the correct sized
- // blob in it.
-
- if ((data->state & Data::UsingCacheFile) != 0 && configSize == data->header.sizeConfig)
- {
- goto ADD_DATA;
- }
- else
- {
- if (data->cache != INVALID_HANDLE_VALUE)
- {
- CloseHandle( data->cache );
- data->header = CacheHeader();
- data->cache = INVALID_HANDLE_VALUE;
- data->state = (Data::State)(data->state & ~(Data::UsingCacheFile));
- }
-
- data->configData = new BYTE[configSize];
- if (ReadFileData( config, data->configData, configSize ) != S_OK)
- {
- goto ADD_DATA;
- }
- data->configDataSize = configSize;
- }
- retval = (ConfigRetval)(retval | ConfigFile);
- }
-
-ADD_DATA:
- {
- CrstHolder ch(&dataLock_);
-
- if (addToList)
- {
- IfFailThrow(entries_.Append(data));
- }
- }
-
- _ASSERTE( data );
- data->initRetval = retval;
-
- return retval;
-
-};
-
-static CacheEntry* LoadNextEntry( HANDLE cache, Data* data )
-{
- STANDARD_VM_CONTRACT;
-
- if ((data->state & Data::CacheExhausted) != 0)
- return NULL;
-
- NewHolder<CacheEntry> entry(new CacheEntry());
-
- if (SetFilePointer( cache, data->cacheCurrentPosition, NULL, FILE_BEGIN ) == INVALID_SET_FILE_POINTER)
- {
- return NULL;
- }
-
- if (ReadFileData( cache, (BYTE*)&entry.GetValue()->header, sizeof( CacheEntryHeader ) ) != S_OK)
- {
- return NULL;
- }
-
- entry.GetValue()->cachePosition = data->cacheCurrentPosition + sizeof( entry.GetValue()->header );
-
- data->cacheCurrentPosition += sizeof( entry.GetValue()->header ) + entry.GetValue()->header.keySize + entry.GetValue()->header.dataSize;
-
- if (SetFilePointer( cache, entry.GetValue()->header.keySize + entry->header.dataSize, NULL, FILE_CURRENT ) == INVALID_SET_FILE_POINTER)
- {
- return NULL;
- }
-
- // We append a partially populated entry. CompareEntry is robust enough to handle this.
- IfFailThrow(data->oldCacheEntries->Append( entry ));
-
- return entry.Extract();
-}
-
-static BOOL WriteEntry( HANDLE cache, CacheEntry* entry, HANDLE oldCache = NULL )
-{
- STANDARD_VM_CONTRACT;
-
- if (WriteFileData( cache, (BYTE*)&entry->header, sizeof( CacheEntryHeader ) ) != S_OK)
- {
- return FALSE;
- }
-
- if (entry->key == NULL)
- {
- _ASSERTE (oldCache != NULL);
-
- // We were lazy in reading the entry. Read the key now.
- entry->key = new BYTE[entry->header.keySize];
-
- _ASSERTE (cache != INVALID_HANDLE_VALUE);
-
- if (SetFilePointer( oldCache, entry->cachePosition, NULL, FILE_BEGIN ) == INVALID_SET_FILE_POINTER)
- return NULL;
-
- if (ReadFileData( oldCache, entry->key, entry->header.keySize ) != S_OK)
- {
- return NULL;
- }
-
- entry->cachePosition += entry->header.keySize;
- }
-
- _ASSERTE( entry->key != NULL );
-
- if (entry->data == NULL)
- {
- _ASSERTE (oldCache != NULL);
-
- // We were lazy in reading the entry. Read the data also.
- entry->data = new BYTE[entry->header.dataSize];
-
- if (SetFilePointer( oldCache, entry->cachePosition, NULL, FILE_BEGIN ) == INVALID_SET_FILE_POINTER)
- return NULL;
-
- if (ReadFileData( oldCache, entry->data, entry->header.dataSize ) != S_OK)
- return NULL;
-
- entry->cachePosition += entry->header.dataSize;
- }
-
- _ASSERT( entry->data != NULL );
-
- if (WriteFileData( cache, entry->key, entry->header.keySize ) != S_OK)
- {
- return FALSE;
- }
-
- if (WriteFileData( cache, entry->data, entry->header.dataSize ) != S_OK)
- {
- return FALSE;
- }
-
- return TRUE;
-}
-
-BOOL SecurityConfig::SaveCacheData( INT32 id )
-{
- CONTRACTL
- {
- THROWS;
- GC_TRIGGERS;
- MODE_ANY;
- }
- CONTRACTL_END
-
- GCX_PREEMP();
-
- // Note: this function should only be called at EEShutdown time.
- // This is because we need to close the current cache file in
- // order to delete it. If it ever became necessary to do
- // cache saves while a process we still executing managed code
- // it should be possible to create a locking scheme for usage
- // of the cache handle with very little reordering of the below
- // (as it should always be possible for us to have a live copy of
- // the file and yet still be making the swap).
-
- HandleHolder cache;
- HandleHolder config;
- CacheHeader header;
- BOOL retval = FALSE;
- BOOL fWriteSucceeded = FALSE;
- DWORD numEntriesWritten = 0;
- DWORD amountWritten = 0;
- DWORD sizeConfig = 0;
- NewHolder<BYTE> configBuffer;
- BOOL useConfigData = FALSE;
-
- Data* data = (Data*)GetData( id );
-
- // If there is not data by the id or there is no
- // cache file name associated with the data, then fail.
-
- if (data == NULL || data->cacheFileName == NULL)
- return FALSE;
-
- // If we haven't added anything new to the cache
- // then just return success.
-
- if ((data->state & Data::CacheUpdated) == 0)
- return TRUE;
-
- // If the config file has changed since the process started
- // then our cache data is no longer valid. We'll just
- // return success in this case.
-
- if ((data->state & Data::UsingConfigFile) != 0 && CacheOutOfDate( &data->configFileTime, data->configFileName, NULL ))
- return TRUE;
-
- DWORD fileNameLength = (DWORD)wcslen( data->cacheFileName );
-
- NewArrayHolder<WCHAR> newFileName(new WCHAR[fileNameLength + 5]);
-
- swprintf_s( newFileName.GetValue(), fileNameLength + 5, W("%s%s"), data->cacheFileName, W(".new") );
-
- cache.Assign( WszCreateFile( newFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ) );
-
- for (DWORD RetryCount = 0; RetryCount < 5; RetryCount++)
- {
- if (cache != INVALID_HANDLE_VALUE)
- {
- break;
- }
- else
- {
- DWORD error = GetLastError();
-
- if (error == ERROR_PATH_NOT_FOUND)
- {
- // The directory does not exist, iterate through and try to create it.
-
- WCHAR* currentChar = newFileName;
-
- // Skip the first backslash
-
- while (*currentChar != W('\0'))
- {
- if (*currentChar == W('\\') || *currentChar == W('/'))
- {
- currentChar++;
- break;
- }
- currentChar++;
- }
-
- // Iterate through trying to create each subdirectory.
-
- while (*currentChar != W('\0'))
- {
- if (*currentChar == W('\\') || *currentChar == W('/'))
- {
- *currentChar = W('\0');
-
- if (!WszCreateDirectory( newFileName, NULL ))
- {
- error = GetLastError();
-
- if (error != ERROR_ACCESS_DENIED && error != ERROR_INVALID_NAME && error != ERROR_ALREADY_EXISTS)
- {
- goto CLEANUP;
- }
- }
-
- *currentChar = W('\\');
- }
- currentChar++;
- }
-
- // Try the file creation again
- continue;
- }
- }
-
- // CreateFile failed. Sleep a little and retry, in case a
- // virus scanner caused the creation to fail.
- ClrSleepEx(10, FALSE);
- }
-
- if (cache.GetValue() == INVALID_HANDLE_VALUE)
- goto CLEANUP;
-
- // This code seems complicated only because of the
- // number of cases that we are trying to handle. All we
- // are trying to do is determine the amount of space to
- // leave for the config information.
-
- // If we saved out a new config file during this run, use
- // the config size stored in the Data object itself.
-
- if (data->configData != NULL)
- {
- useConfigData = TRUE;
- }
-
- if ((data->state & Data::NewConfigFile) != 0)
- {
- sizeConfig = data->sizeConfig;
- }
-
- // If we have a cache file, then use the size stored in the
- // cache header.
-
- else if ((data->state & Data::UsingCacheFile) != 0)
- {
- sizeConfig = data->header.sizeConfig;
- }
-
- // If we read in the config data, use the size of the
- // managed byte array that it is stored in.
-
- else if (useConfigData)
- {
- sizeConfig = data->configDataSize;
- }
-
- // Otherwise, check the config file itself to get the size.
-
- else
- {
- DWORD shareFlags;
-
- shareFlags = GetShareFlags();
-
- config.Assign( WszCreateFile( data->configFileName, GENERIC_READ, shareFlags, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ) );
-
- if (config == INVALID_HANDLE_VALUE)
- {
- sizeConfig = 0;
- }
- else
- {
- sizeConfig = SafeGetFileSize( config, NULL );
-
- if (sizeConfig == 0xFFFFFFFF)
- {
- sizeConfig = 0;
- }
- }
- }
-
- // First write the entries.
-
- if (SetFilePointer( cache, sizeof( CacheHeader ) + sizeConfig, NULL, FILE_BEGIN ) == INVALID_SET_FILE_POINTER)
- {
- goto CLEANUP;
- }
-
- // We're going to write out the cache entries in a modified
- // least recently used order, throwing out any that end up
- // taking us past our hardcoded max file size.
-
- {
- // First, write the entries from the cache file that were used.
- // We do this because presumably these are system assemblies
- // and other assemblies used by a number of applications.
-
- ArrayList::Iterator iter;
-
- if ((data->state & Data::UsingCacheFile) != 0)
- {
- iter = data->oldCacheEntries->Iterate();
-
- while (iter.Next() && amountWritten < MAX_CACHEFILE_SIZE)
- {
- CacheEntry* currentEntry = (CacheEntry*)iter.GetElement();
-
- if (currentEntry->used)
- {
- if(!WriteEntry( cache, currentEntry, data->cache ))
- {
- goto CLEANUP;
- }
-
- amountWritten += SIZE_OF_ENTRY( currentEntry );
- numEntriesWritten++;
- }
- }
- }
-
- // Second, write any new cache entries to the file. These are
- // more likely to be assemblies specific to this app.
-
- iter = data->newCacheEntries->Iterate();
-
- while (iter.Next() && amountWritten < MAX_CACHEFILE_SIZE)
- {
- CacheEntry* currentEntry = (CacheEntry*)iter.GetElement();
-
- if (!WriteEntry( cache, currentEntry ))
- {
- goto CLEANUP;
- }
-
- amountWritten += SIZE_OF_ENTRY( currentEntry );
- numEntriesWritten++;
- }
-
- // Third, if we are using the cache file, write the old entries
- // that were not used this time around.
-
- if ((data->state & Data::UsingCacheFile) != 0)
- {
- // First, write the ones that we already have partially loaded
-
- iter = data->oldCacheEntries->Iterate();
-
- while (iter.Next() && amountWritten < MAX_CACHEFILE_SIZE)
- {
- CacheEntry* currentEntry = (CacheEntry*)iter.GetElement();
-
- if (!currentEntry->used)
- {
- if(!WriteEntry( cache, currentEntry, data->cache ))
- {
- goto CLEANUP;
- }
-
- amountWritten += SIZE_OF_ENTRY( currentEntry );
- numEntriesWritten++;
- }
- }
-
- while (amountWritten < MAX_CACHEFILE_SIZE)
- {
- CacheEntry* entry = LoadNextEntry( data->cache, data );
-
- if (entry == NULL)
- break;
-
- if (!WriteEntry( cache, entry, data->cache ))
- {
- goto CLEANUP;
- }
-
- amountWritten += SIZE_OF_ENTRY( entry );
- numEntriesWritten++;
- }
- }
-
- fWriteSucceeded = TRUE;
- }
-
-
- if (!fWriteSucceeded)
- {
- CloseHandle( cache.GetValue() );
- cache.SuppressRelease();
- WszDeleteFile( newFileName );
- goto CLEANUP;
- }
-
- // End with writing the header.
-
- header.configFileTime = data->configFileTime;
- header.isSecurityOn = 1;
- header.numEntries = numEntriesWritten;
- header.quickCache = data->header.quickCache;
- header.sizeConfig = sizeConfig;
-
- if (SetFilePointer( cache, 0, NULL, FILE_BEGIN ) == INVALID_SET_FILE_POINTER)
- {
- // Couldn't move to the beginning of the file
- goto CLEANUP;
- }
-
- if (WriteFileData( cache, (PBYTE)&header, sizeof( header ) ) != S_OK)
- {
- // Couldn't write header info.
- goto CLEANUP;
- }
-
- if (sizeConfig != 0)
- {
- if ((data->state & Data::NewConfigFile) != 0)
- {
- if (WriteFileData( cache, data->configBuffer, sizeConfig ) != S_OK)
- {
- goto CLEANUP;
- }
- }
- else
- {
- if (data->configData != NULL)
- {
- if (WriteFileData( cache, data->configData, sizeConfig ) != S_OK)
- {
- goto CLEANUP;
- }
- }
- else if ((data->state & Data::UsingCacheFile) != 0)
- {
- configBuffer.Assign( new BYTE[sizeConfig] );
-
- if (SetFilePointer( data->cache, sizeof( CacheHeader ), NULL, FILE_BEGIN ) == INVALID_SET_FILE_POINTER)
- {
- goto CLEANUP;
- }
-
- if (ReadFileData( data->cache, configBuffer.GetValue(), sizeConfig ) != S_OK)
- {
- goto CLEANUP;
- }
-
- if (WriteFileData( cache, configBuffer.GetValue(), sizeConfig ) != S_OK)
- {
- goto CLEANUP;
- }
- }
- else
- {
- configBuffer.Assign( new BYTE[sizeConfig] );
-
- if (SetFilePointer( config, 0, NULL, FILE_BEGIN ) == INVALID_SET_FILE_POINTER)
- {
- goto CLEANUP;
- }
-
- if (ReadFileData( config, configBuffer.GetValue(), sizeConfig ) != S_OK)
- {
- goto CLEANUP;
- }
-
- if (WriteFileData( cache, configBuffer.GetValue(), sizeConfig ) != S_OK)
- {
- goto CLEANUP;
- }
- }
- }
- }
-
- // Flush the file buffers to make sure
- // we get full write through.
-
- FlushFileBuffers( cache.GetValue() );
-
- CloseHandle( cache );
- cache.SuppressRelease();
- CloseHandle( data->cache );
- data->cache = INVALID_HANDLE_VALUE;
-
- // Move the existing file out of the way
- // Note: use MoveFile because we know it will never cross
- // device boundaries.
-
- // Note: the delete file can fail, but we can't really do anything
- // if it does so just ignore any failures.
- WszDeleteFile( data->cacheFileNameTemp );
-
- // Try to move the existing cache file out of the way. However, if we can't
- // then try to delete it. If it can't be deleted then just bail out.
- if (!WszMoveFile( data->cacheFileName, data->cacheFileNameTemp ) &&
- (!Assembly::FileNotFound(HRESULT_FROM_WIN32(GetLastError()))) &&
- !WszDeleteFile( data->cacheFileName ))
- {
- if (!Assembly::FileNotFound(HRESULT_FROM_WIN32(GetLastError())))
- goto CLEANUP;
- }
-
- // Move the new file into position
-
- if (!WszMoveFile( newFileName, data->cacheFileName ))
- {
- goto CLEANUP;
- }
-
- retval = TRUE;
-
-CLEANUP:
- if (retval)
- cache.SuppressRelease();
-
- return retval;
-
-}
-
-void QCALLTYPE SecurityConfig::ResetCacheData(INT32 id)
-{
- QCALL_CONTRACT;
-
- BEGIN_QCALL;
-
- Data* data = (Data*)GetData( id );
-
- if (data != NULL)
- {
- CrstHolder ch(&dataLock_);
-
- data->DeleteAllEntries();
-
- data->oldCacheEntries = new ArrayList;
- data->newCacheEntries = new ArrayList;
-
- data->header = CacheHeader();
- data->state = (Data::State)(~(Data::CopyCacheFile | Data::UsingCacheFile) & data->state);
-
- HandleHolder config(WszCreateFile( data->configFileName, GENERIC_READ, GetShareFlags(), NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ));
-
- if (config.GetValue() != INVALID_HANDLE_VALUE)
- {
- VERIFY(GetFileTime( config, NULL, NULL, &data->configFileTime ));
- VERIFY(GetFileTime( config, NULL, NULL, &data->header.configFileTime ));
- }
-}
-
- END_QCALL;
-}
-
-HRESULT QCALLTYPE SecurityConfig::SaveDataByte(LPCWSTR wszConfigPath, LPCBYTE pbData, DWORD cbData)
-{
- QCALL_CONTRACT;
-
- HRESULT retval = E_FAIL;
-
- BEGIN_QCALL;
-
- HandleHolder newFile(INVALID_HANDLE_VALUE);
-
- int RetryCount;
- DWORD error = 0;
- DWORD fileNameLength = (DWORD) wcslen(wszConfigPath);
-
- NewArrayHolder<WCHAR> newFileName(new WCHAR[fileNameLength + 5]);
- NewArrayHolder<WCHAR> oldFileName(new WCHAR[fileNameLength + 5]);
-
- swprintf_s( newFileName.GetValue(), fileNameLength + 5, W("%s%s"), wszConfigPath, W(".new") );
- swprintf_s( oldFileName.GetValue(), fileNameLength + 5, W("%s%s"), wszConfigPath, W(".old") );
-
- // Create the new file.
- for (RetryCount = 0; RetryCount < 5; RetryCount++) {
- newFile.Assign( WszCreateFile( newFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ) );
-
- if (newFile != INVALID_HANDLE_VALUE)
- break;
- else
- {
- error = GetLastError();
-
- if (error == ERROR_PATH_NOT_FOUND)
- {
- // The directory does not exist, iterate through and try to create it.
-
- WCHAR* currentChar = newFileName;
-
- // Skip the first backslash
-
- while (*currentChar != W('\0'))
- {
- if (*currentChar == W('\\') || *currentChar == W('/'))
- {
- currentChar++;
- break;
- }
- currentChar++;
- }
-
- // Iterate through trying to create each subdirectory.
-
- while (*currentChar != W('\0'))
- {
- if (*currentChar == W('\\') || *currentChar == W('/'))
- {
- *currentChar = W('\0');
-
- if (!WszCreateDirectory( newFileName, NULL ))
- {
- error = GetLastError();
-
- if (error != ERROR_ACCESS_DENIED && error != ERROR_ALREADY_EXISTS)
- {
- goto CLEANUP;
- }
- }
-
- *currentChar = W('\\');
- }
- currentChar++;
- }
-
- // Try the file creation again
- continue;
- }
- }
-
- // CreateFile failed. Sleep a little and retry, in case a
- // virus scanner caused the creation to fail.
- ClrSleepEx(10, FALSE);
- }
-
- if (newFile == INVALID_HANDLE_VALUE) {
- goto CLEANUP;
- }
-
- // Write the data into it.
- if ((retval = WriteFileData(newFile.GetValue(), pbData, cbData)) != S_OK)
- {
- // Write failed, destroy the file and bail.
- // Note: if the delete fails, we always do a CREATE_NEW
- // for this file so that should take care of it. If not
- // we'll fail to write out future cache files.
- CloseHandle( newFile.GetValue() );
- newFile.SuppressRelease();
- WszDeleteFile( newFileName );
- goto CLEANUP;
- }
-
- if (!FlushFileBuffers(newFile.GetValue()))
- {
- error = GetLastError();
- goto CLEANUP;
- }
-
- CloseHandle( newFile.GetValue() );
- newFile.SuppressRelease();
-
- // Move the existing file out of the way
- if (!WszMoveFileEx( wszConfigPath, oldFileName, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED ))
- {
- // If move fails for a reason other than not being able to find the file, bail out.
- // Also, if the old file didn't exist, we have no need to delete it.
- HRESULT hrMove = HRESULT_FROM_WIN32(GetLastError());
- if (!Assembly::FileNotFound(hrMove))
- {
- retval = hrMove;
- WszDeleteFile(wszConfigPath);
- goto CLEANUP;
- }
- }
-
- // Move the new file into position
-
- if (!WszMoveFileEx( newFileName, wszConfigPath, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED ))
- {
- error = GetLastError();
- goto CLEANUP;
- }
-
- retval = S_OK;
-
-CLEANUP:
- if (retval == E_FAIL && error != 0)
- retval = HRESULT_FROM_WIN32(error);
-
- END_QCALL;
-
- return retval;
-}
-
-BOOL QCALLTYPE SecurityConfig::RecoverData(INT32 id)
- {
- QCALL_CONTRACT;
-
- BOOL retval = FALSE;
-
- BEGIN_QCALL;
-
- Data* data = (Data*)GetData( id );
-
- if (data == NULL)
- goto CLEANUP;
-
- {
- DWORD fileNameLength = (DWORD)wcslen( data->configFileName );
-
- NewArrayHolder<WCHAR> tempFileName(new WCHAR[fileNameLength + 10]);
- NewArrayHolder<WCHAR> oldFileName(new WCHAR[fileNameLength + 5]);
-
- swprintf_s( tempFileName.GetValue(), fileNameLength + 10, W("%s%s"), data->configFileName, W(".old.temp") );
- swprintf_s( oldFileName.GetValue(), fileNameLength + 5, W("%s%s"), data->configFileName, W(".old") );
-
- HandleHolder oldFile(WszCreateFile( oldFileName, 0, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ));
-
- if (oldFile.GetValue() == INVALID_HANDLE_VALUE)
- {
- goto CLEANUP;
- }
-
- CloseHandle( oldFile );
- oldFile.SuppressRelease();
-
- if (!WszMoveFile( data->configFileName, tempFileName ))
- {
- goto CLEANUP;
- }
-
- if (!WszMoveFile( oldFileName, data->configFileName ))
- {
- goto CLEANUP;
- }
-
- if (!WszMoveFile( tempFileName, oldFileName ))
- {
- goto CLEANUP;
- }
- }
-
- // We need to do some work to reset the unmanaged data object
- // so that the managed side of things behaves like you'd expect.
- // This basically means cleaning up the open resources and
- // doing the work to init on a different set of files.
-
- data->Reset();
- InitData( data, FALSE );
-
- retval = TRUE;
-
-CLEANUP:
- END_QCALL;
-
- return retval;
-}
-
-BOOL SecurityConfig::GetQuickCacheEntry( INT32 id, QuickCacheEntryType type )
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_ANY;
- }
- CONTRACTL_END
-
- //
- // If there is no config file for this level, then we'll assume the default
- // security policy is in effect. This could happen for example if there is
- // user profile loaded or if the config file is not present.
- //
-
- Data* data = (Data*)GetData( id );
- if (data == NULL || ((data->state & Data::UsingConfigFile) == 0))
- return (type == FullTrustZoneMyComputer); // MyComputer gets FT by default.
-
- if ((data->state & Data::UsingCacheFile) == 0)
- return FALSE;
-
- return (data->header.quickCache & type);
-}
-
-void QCALLTYPE SecurityConfig::SetQuickCache(INT32 id, QuickCacheEntryType type)
-{
- QCALL_CONTRACT;
-
- BEGIN_QCALL;
-
- Data* data = (Data*)GetData( id );
-
- if (data != NULL && (DWORD) type != data->header.quickCache)
- {
- CrstHolder ch(&dataLock_);
-
- data->state = (Data::State)(Data::CacheUpdated | data->state);
- data->header.quickCache = type;
- }
-
- END_QCALL;
-}
-
-static HANDLE OpenCacheFile( Data* data )
-{
- STANDARD_VM_CONTRACT;
-
- CrstHolder ch(&SecurityConfig::dataLock_);
-
- if (data->cache != INVALID_HANDLE_VALUE)
- return data->cache;
-
- _ASSERTE( FALSE && "This case should never happen" );
-
- data->cache = WszCreateFile( data->cacheFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
- if (data->cache == INVALID_HANDLE_VALUE)
- return NULL;
-
- // Check whether the cache has changed since we first looked at it.
- // If it has but the config file hasn't, then we need to start fresh.
- // However, if the config file has changed then we have to ignore it.
-
- if (CacheOutOfDate( &data->cacheFileTime, data->cache, NULL ))
- {
- if (CacheOutOfDate( &data->configFileTime, data->configFileName, NULL ))
- return NULL;
-
- if (ReadFileData( data->cache, (BYTE*)&data->header, sizeof( CacheHeader ) ) != S_OK)
- return NULL;
-
- data->cacheCurrentPosition = sizeof( CacheHeader );
-
- if (data->oldCacheEntries != NULL)
- {
- ArrayList::Iterator iter = data->oldCacheEntries->Iterate();
- while (iter.Next())
- {
- delete (CacheEntry*)iter.GetElement();
- }
- delete data->oldCacheEntries;
- data->oldCacheEntries = new ArrayList();
- }
- }
-
- return data->cache;
-}
-
-static BYTE* CompareEntry( CacheEntry* entry, DWORD numEvidence, DWORD evidenceSize, LPCBYTE evidenceBlock, HANDLE cache, DWORD* size)
-{
- STANDARD_VM_CONTRACT;
-
- _ASSERTE (entry);
-
- if (entry->header.numItemsInKey == numEvidence &&
- entry->header.keySize == evidenceSize)
- {
- if (entry->key == NULL)
- {
- // We were lazy in reading the entry. Read the key now.
- entry->key = new BYTE[entry->header.keySize];
-
- _ASSERTE (cache != INVALID_HANDLE_VALUE);
-
- if (SetFilePointer( cache, entry->cachePosition, NULL, FILE_BEGIN ) == INVALID_SET_FILE_POINTER)
- return NULL;
-
- if (ReadFileData( cache, entry->key, entry->header.keySize ) != S_OK)
- return NULL;
-
- entry->cachePosition += entry->header.keySize;
- }
-
- _ASSERTE (entry->key);
-
- if (memcmp( entry->key, evidenceBlock, entry->header.keySize ) == 0)
- {
- if (entry->data == NULL)
- {
- // We were lazy in reading the entry. Read the data also.
- entry->data = new BYTE[entry->header.dataSize];
-
- if (SetFilePointer( cache, entry->cachePosition, NULL, FILE_BEGIN ) == INVALID_SET_FILE_POINTER)
- return NULL;
-
- if (ReadFileData( cache, entry->data, entry->header.dataSize ) != S_OK)
- return NULL;
-
- entry->cachePosition += entry->header.dataSize;
- }
-
- entry->used = TRUE;
- *size = entry->header.dataSize;
-
- return entry->data;
- }
- }
- return NULL;
-}
-
-BOOL QCALLTYPE SecurityConfig::GetCacheEntry(INT32 id, DWORD numEvidence, LPCBYTE pEvidence, DWORD cbEvidence, QCall::ObjectHandleOnStack retPolicy)
-{
- QCALL_CONTRACT;
-
- BOOL success = FALSE;
-
- BEGIN_QCALL;
-
- HANDLE cache = INVALID_HANDLE_VALUE;
-
- BYTE* retval = NULL;
- DWORD size = (DWORD) -1;
-
- Data* data = (Data*)GetData( id );
-
- if (data == NULL)
- {
- goto CLEANUP;
- }
-
- {
-
- ArrayList::Iterator iter;
-
- if ((data->state & Data::UsingCacheFile) == 0)
- {
- // We know we don't have anything in the config file, so
- // let's just look through the new entries to make sure we
- // aren't getting any repeats.
-
- // Then try the existing new entries
-
- iter = data->newCacheEntries->Iterate();
-
- while (iter.Next())
- {
- // newCacheEntries do not need the cache file so pass in NULL.
- retval = CompareEntry( (CacheEntry*)iter.GetElement(), numEvidence, cbEvidence, pEvidence, NULL, &size );
-
- if (retval != NULL)
- {
- success = TRUE;
- goto CLEANUP;
- }
- }
-
- goto CLEANUP;
- }
-
- // Its possible that the old entries were not read in completely
- // so we keep the cache file open before iterating through the
- // old entries.
-
- cache = OpenCacheFile( data );
-
- if ( cache == NULL )
- {
- goto CLEANUP;
- }
-
- // First, iterator over the old entries
-
- {
- CrstHolder ch(&dataLock_);
-
- iter = data->oldCacheEntries->Iterate();
- while (iter.Next())
- {
- retval = CompareEntry( (CacheEntry*)iter.GetElement(), numEvidence, cbEvidence, pEvidence, cache, &size );
- if (retval != NULL)
- {
- success = TRUE;
- goto CLEANUP;
- }
- }
-
- // LockHolder goes out of scope here
- }
-
- // Then try the existing new entries
- iter = data->newCacheEntries->Iterate();
- while (iter.Next())
- {
- // newCacheEntries do not need the cache file so pass in NULL.
- retval = CompareEntry( (CacheEntry*)iter.GetElement(), numEvidence, cbEvidence, pEvidence, NULL, &size );
- if (retval != NULL)
- {
- success = TRUE;
- goto CLEANUP;
- }
- }
-
- // Finally, try loading existing entries from the file
-
- {
- CrstHolder ch(&dataLock_);
-
- if (SetFilePointer( cache, data->cacheCurrentPosition, NULL, FILE_BEGIN ) == INVALID_SET_FILE_POINTER)
- {
- goto CLEANUP;
- }
-
- do
- {
- CacheEntry* entry = LoadNextEntry( cache, data );
- if (entry == NULL)
- {
- data->state = (Data::State)(Data::CacheExhausted | data->state);
- break;
- }
-
- retval = CompareEntry( entry, numEvidence, cbEvidence, pEvidence, cache, &size );
- if (retval != NULL)
- {
- success = TRUE;
- break;
- }
- } while (TRUE);
-
- // LockHolder goes out of scope here
- }
- }
-
-CLEANUP:
- if (success && retval != NULL)
- {
- _ASSERTE( size != (DWORD) -1 );
- retPolicy.SetByteArray(retval, size);
- }
-
- END_QCALL;
-
- return success;
-}
-
-void QCALLTYPE SecurityConfig::AddCacheEntry(INT32 id, DWORD numEvidence, LPCBYTE pEvidence, DWORD cbEvidence, LPCBYTE pPolicy, DWORD cbPolicy)
-{
- QCALL_CONTRACT;
-
- BEGIN_QCALL;
-
- Data* data = (Data*)GetData( id );
-
- DWORD sizeOfEntry = 0;
- NewHolder<CacheEntry> entry;
-
- if (data == NULL)
- {
- goto lExit;
- }
-
- // In order to limit how large a long running app can become,
- // we limit the total memory held by the new cache entries list.
- // For now this limit corresponds with how large the max cache file
- // can be.
-
- sizeOfEntry = cbEvidence + cbPolicy + sizeof( CacheEntryHeader );
-
- if (data->newEntriesSize + sizeOfEntry >= MAX_CACHEFILE_SIZE)
- {
- goto lExit;
- }
-
- entry = new CacheEntry();
-
- entry->header.numItemsInKey = numEvidence;
- entry->header.keySize = cbEvidence;
- entry->header.dataSize = cbPolicy;
-
- entry->key = new BYTE[entry->header.keySize];
- entry->data = new BYTE[entry->header.dataSize];
-
- memcpyNoGCRefs(entry->key, pEvidence, cbEvidence);
- memcpyNoGCRefs(entry->data, pPolicy, cbPolicy);
-
- {
- CrstHolder ch(&dataLock_);
-
- // Check the size again to handle the race.
- if (data->newEntriesSize + sizeOfEntry < MAX_CACHEFILE_SIZE)
- {
- data->state = (Data::State)(Data::CacheUpdated | data->state);
- IfFailThrow(data->newCacheEntries->Append( entry.GetValue() ));
- entry.SuppressRelease();
- data->newEntriesSize += sizeOfEntry;
- }
- }
-
-lExit: ;
-
- END_QCALL;
-}
-
-ArrayListStatic SecurityConfig::entries_;
-CrstStatic SecurityConfig::dataLock_;
-
-void SecurityConfig::Init( void )
-{
- CONTRACTL
- {
- THROWS;
- GC_NOTRIGGER;
- MODE_ANY;
- }
- CONTRACTL_END
-
- dataLock_.Init(CrstSecurityPolicyCache);
- entries_.Init();
-}
-
-void SecurityConfig::Cleanup( void )
-{
- CONTRACTL
- {
- NOTHROW;
- GC_TRIGGERS;
- MODE_ANY;
- }
- CONTRACTL_END
-
- ArrayList::Iterator iter = entries_.Iterate();
-
- GCX_PREEMP();
-
- CrstHolder ch(&dataLock_);
-
- while (iter.Next())
- {
- ((Data*) iter.GetElement())->Cleanup();
- }
-}
-
-void SecurityConfig::Delete( void )
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_ANY;
- }
- CONTRACTL_END
-
- ArrayList::Iterator iter = entries_.Iterate();
-
- while (iter.Next())
- {
- delete (Data*) iter.GetElement();
- }
-
- entries_.Destroy();
- dataLock_.Destroy();
-}
-
-void QCALLTYPE SecurityConfig::_GetMachineDirectory(QCall::StringHandleOnStack retDirectory)
-{
- QCALL_CONTRACT;
-
- BEGIN_QCALL;
-
- WCHAR machine[MAX_LONGPATH];
-
- HRESULT hr = GetMachineDirectory(machine, MAX_LONGPATH);
- if (FAILED(hr))
- ThrowHR(hr);
-
- retDirectory.Set(machine);
-
- END_QCALL;
-}
-
-void QCALLTYPE SecurityConfig::_GetUserDirectory(QCall::StringHandleOnStack retDirectory)
-{
- QCALL_CONTRACT;
-
- BEGIN_QCALL;
-
- WCHAR user[MAX_LONGPATH];
-
- BOOL result = GetUserDirectory(user, MAX_LONGPATH);
- if (result)
- retDirectory.Set(user);
-
- END_QCALL;
-}
-
-HRESULT SecurityConfig::GetMachineDirectory(__out_ecount(bufferCount) __out_z WCHAR* buffer, size_t bufferCount)
-{
- STANDARD_VM_CONTRACT;
-
- HRESULT hr;
-
- DWORD length = (DWORD)bufferCount;
- hr = GetInternalSystemDirectory(buffer, &length);
- if (FAILED(hr))
- return hr;
-
- // Make sure we have enough buffer to concat the string.
- // Note the length including the terminating zero.
- if((bufferCount - wcslen(buffer) - 1) < wcslen(W("config\\")))
- return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
-
- wcscat_s(buffer, bufferCount, W("config\\"));
-
- return S_OK;
-}
-
-BOOL SecurityConfig::GetVIUserDirectory(__out_ecount(bufferCount) __out_z WCHAR* buffer, size_t bufferCount)
-{
- STANDARD_VM_CONTRACT;
-
- WCHAR scratchBuffer[MAX_LONGPATH];
- BOOL retval = FALSE;
-
- DWORD size = MAX_LONGPATH;
-
- if (!GetUserDir(buffer, bufferCount, TRUE))
- goto CLEANUP;
-
- wcscpy_s( scratchBuffer, COUNTOF(scratchBuffer), W("\\Microsoft\\CLR Security Config\\") );
-
- if (bufferCount < wcslen( buffer ) + wcslen( scratchBuffer ) + 1)
- {
- goto CLEANUP;
- }
-
- wcscat_s( buffer, bufferCount, scratchBuffer );
-
- retval = TRUE;
-
-CLEANUP:
- return retval;
-}
-
-BOOL SecurityConfig::GetUserDirectory(__out_ecount(bufferCount) __out_z WCHAR* buffer, size_t bufferCount)
-{
- STANDARD_VM_CONTRACT;
-
- StackSString ssScratchBuffer;
- BOOL retval = FALSE;
-
- WCHAR* wszScratchBuffer = ssScratchBuffer.OpenUnicodeBuffer( (COUNT_T)bufferCount );
- retval = GetVIUserDirectory(wszScratchBuffer, bufferCount);
- ssScratchBuffer.CloseBuffer( (COUNT_T)wcslen( wszScratchBuffer ) );
-
- if (!retval)
- return retval;
-
- ssScratchBuffer.Append( W("v") );
- ssScratchBuffer.Append( VER_PRODUCTVERSION_NO_QFE_STR_L );
- ssScratchBuffer.Append( W("\\") );
-
-#ifdef _WIN64
- ssScratchBuffer.Append( W("64bit\\") );
-#endif // _WIN64
-
- if (ssScratchBuffer.GetCount() + 1 > bufferCount)
- return FALSE;
-
- wcscpy_s( buffer, bufferCount, ssScratchBuffer.GetUnicode() );
-
- return TRUE;
-}
-
-BOOL QCALLTYPE SecurityConfig::WriteToEventLog(LPCWSTR wszMessage)
-{
- QCALL_CONTRACT;
-
- BOOL retVal = FALSE;
-
- BEGIN_QCALL;
-
- retVal = ReportEventCLR(
- EVENTLOG_WARNING_TYPE, // event type
- 0, // category
- (DWORD)1000, // event identifier
- NULL, // no user security identifier
- &StackSString(wszMessage)); // message to log
-
- END_QCALL
-
- return retVal;
-}
-
-#ifdef _DEBUG
-HRESULT QCALLTYPE SecurityConfig::DebugOut(LPCWSTR wszFileName, LPCWSTR wszMessage)
-{
- HRESULT retVal = E_FAIL;
-
- QCALL_CONTRACT;
-
- BEGIN_QCALL;
-
- HandleHolder file(WszCreateFile( wszFileName, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ));
-
- if (file == INVALID_HANDLE_VALUE)
- {
- goto lExit;
- }
-
- SetFilePointer( file, 0, NULL, FILE_END );
-
- DWORD cbMessage;
- DWORD cbWritten;
-
- cbMessage = (DWORD)wcslen(wszMessage) * sizeof(WCHAR);
- if (!WriteFile( file, wszMessage, cbMessage, &cbWritten, NULL ))
- {
- goto lExit;
- }
-
- if (cbMessage != cbWritten)
- {
- goto lExit;
- }
-
- retVal = S_OK;
-
-lExit: ;
- END_QCALL;
-
- return retVal;
-}
-#endif
-
-#endif // FEATURE_CAS_POLICY