Code:
/ WCF / WCF / 3.5.30729.1 / untmp / Orcas / SP / ndp / cdf / src / WCF / infocard / Service / managed / Microsoft / InfoCards / FileDataSource.cs / 2 / FileDataSource.cs
//------------------------------------------------------------------------------ // Copyright (c) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- // // Presharp uses the c# pragma mechanism to supress its warnings. // These are not recognised by the base compiler so we need to explictly // disable the following warnings. See http://winweb/cse/Tools/PREsharp/userguide/default.asp // for details. // #pragma warning disable 1634, 1691 // unknown message, unknown pragma namespace Microsoft.InfoCards { using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.IO; using System.Globalization; using System.Runtime.InteropServices; using System.Security; using System.Security.AccessControl; using System.Security.Cryptography; using System.Security.Principal; using System.Text; using System.Threading; using IDT = Microsoft.InfoCards.Diagnostics.InfoCardTrace; // // Summary: // File base DataSource for storing infocard/ledger information // internal class FileDataSource :DataSource { public const Int32 ENCRYPTIONKEYBUFFERSIZE = 32; public const Int32 ENCRYPTIONKEYBITLENGTH = ENCRYPTIONKEYBUFFERSIZE * 8; public const Int32 ENCRYPTIONBLOCKBUFFERSIZE = 16; public const Int32 ENCRYPTIONBLOCKBITLENGTH = ENCRYPTIONBLOCKBUFFERSIZE * 8; public const Int32 ENCRYPTIONITERATIONS = 1000; public const Int32 INITIALINDEXITEMCOUNT = 20; public const Int32 INITIALINDEXAREASIZE = ( INITIALINDEXITEMCOUNT * 4 /*sizeof(Int32)*/ );//room for 25; public const Int32 INITIALBLOBAREASIZE = 2048;// public const Int32 FILEVERSION = 11; public const Int32 FILEVERSIONV2 = 12; private static readonly SecurityIdentifier AdministratorSid = new SecurityIdentifier( WellKnownSidType.BuiltinAdministratorsSid, null ); // // See SignatureHeader.IVSize OR m_encAlg.IV.Length for IV Size // // // This is the signature length for the current version of the store. // int m_variableSignatureLength = 64; FileInfo m_fileInfo; FileStream m_file; FileInfo m_shadowInfo; FileStream m_shadow; byte[ ] m_header; IndexedDataBuffer m_data; SecondaryIndexList m_indexes; ReaderWriterLock m_lock; SymmetricAlgorithm m_encAlg; byte[ ] m_key; bool m_keyProtected; WindowsIdentity m_identity; bool m_currentTransactionDirty; SafeRsaProviderHandle m_provider; byte[ ] m_encryptedKey; // // Summary: // Protected default ctor for tools to use. // protected FileDataSource() : base( null, null ) { } // // Summary: // Creates an instance of a FileDataSource to store and index data object // // Remarks: // if fileName does not exist, it will be created. // will be created using SecondaryIndexDefinition.MasterIndexes as the index mapping // // Parameters: // identity: The WindowsIdentity to use to when decrypting the keys use DPAPI // fileName: The full path to the file to open. // public FileDataSource( WindowsIdentity identity, string fileName ) : this( identity, fileName, Guid.NewGuid().ToString( "P" ), SecondaryIndexDefinition.MasterIndexes ) { } // // Summary: // Creates an instance of a FileDataSource to store and index data object // // Remarks: // if fileName does not exist, it will be created. // // Parameters: // identity: The WindowsIdentity to use to when decrypting the keys use DPAPI // fileName: The full path to the file to open. // instanceId: The id of the instance that owns this object. // indexDefinitions: The list of index definitions to map over this file. // public FileDataSource( WindowsIdentity identity, string fileName, string instanceId, SecondaryIndexDefinition[ ] indexDefinitions ) : base( instanceId, fileName ) { m_identity = identity; m_currentTransactionDirty = false; m_fileInfo = new FileInfo( fileName ); m_shadowInfo = new FileInfo( m_fileInfo.FullName + ".shadow" ); m_lock = new ReaderWriterLock(); m_indexes = new SecondaryIndexList( indexDefinitions ); m_encAlg = CreateEncryptionAlg(); // // Setup the native provider who will do the encryption and decryption for us. // SetupProvider(); } // // Summary: // Gets the internal buffer that contains all of the data blobs. // // Remarks: // This is only exposed to assist tools and debugging. // public IndexedDataBuffer Buffer { get { ThrowIfDisposed(); ThrowIfNotLoaded(); return m_data; } } // // Summary: // Gets the internal buffer that file header information. // // Remarks: // This is only exposed to assist tools and debugging. // public byte[ ] Header { get { ThrowIfDisposed(); ThrowIfNotLoaded(); return m_header; } } // // Summary: // Gets the Collection of SecondaryIndexes that can be used for searching. // // Remarks: // These index structures are specific to the file implementation, there for // they are not defined in the base class. // This is only exposed to assist tools and debugging. // public SecondaryIndexList Indexes { get { ThrowIfDisposed(); ThrowIfNotLoaded(); return m_indexes; } } // // Summary: // Identity of the owner of the data source. // protected WindowsIdentity Identity { get { return m_identity; } } // // Summary: // Override for handling the Delete events // protected internal override void OnClear() { using( SystemIdentity lsa = new SystemIdentity( true ) ) { ResilientDelete( m_fileInfo ); ResilientDelete( m_shadowInfo ); } } // // Summary: // Override for handling the Load events // protected internal override void OnLoad() { ThrowIfDisposed(); IDT.TraceDebug( "STORE: Opening file with READ/WRITE:{0} ", m_fileInfo.FullName ); IDT.TraceDebug( "STORE: Opening shadow file with READ/WRITE:{0} ", m_shadowInfo.FullName ); try { // // Create file required for the store // CreateDirAndFiles(); if( m_shadow.Length > 0 ) { // // We had a failed write, or the store didn't close properly // if( m_file.Length > 0 ) { // // RollBack: // Original file seem intact, we will load it. // m_shadow.SetLength( 0 ); LoadFrom( m_file ); } else { // // RollForward: // Original file seem corrupted, rewrite // LoadFrom( m_shadow ); FlushToShadow(); SwapFileWithShadow(); } } else { // // No shadow file. // if( m_file.Length > 0 ) { // // All OK: // Original file seems ok, // LoadFrom( m_file ); } else { // // New Store: // No storage files are present // CreateEmptyStore(); FlushToShadow(); SwapFileWithShadow(); } } m_file.Seek( 0, SeekOrigin.Begin ); } catch { CloseFiles(); throw; } } void CloseFiles() { using( SystemIdentity lsa = new SystemIdentity( false ) ) { if( null != m_file ) { m_file.Close(); m_file = null; } if( null != m_shadow ) { m_shadow.Close(); m_shadow = null; } } } // // Virutal functions that can be overriden // // // Summary: // Helper mehtod to create directory if it does not exist // and to open/create the file with read/write access // protected void CreateDirAndFiles() { // // Validate that the user has the appropriate // access to the infocard directory. This can throw DataAccessExceptions . // CheckReparsePoints(); if( NativeMcppMethods.PathSupportsPersistedSecurity( m_fileInfo.Directory.FullName ) ) { ValidateDirectoryAccess(); } using( SystemIdentity lsa = new SystemIdentity( true ) ) { try { // // Create directory if necessary // if( !m_fileInfo.Directory.Exists ) { // // Create directory: Scenario - user starting infocard for the first time // try { if( NativeMcppMethods.PathSupportsPersistedSecurity( m_fileInfo.Directory.FullName ) ) { m_fileInfo.Directory.Create( CreateSecurityDescriptor() ); } else { m_fileInfo.Directory.Create(); } m_fileInfo.Directory.Attributes |= FileAttributes.NotContentIndexed | FileAttributes.Hidden; } catch( UnauthorizedAccessException uae ) { // // We do not even have permissions to create the directory // throw IDT.ThrowHelperError( new DataAccessException( uae.Message ) ); } } else if( NativeMcppMethods.PathSupportsPersistedSecurity( m_fileInfo.Directory.FullName ) && LogIfAclsTampered( m_fileInfo.Directory ) ) { // // Directory exists but acls modified // m_fileInfo.Directory.SetAccessControl( CreateSecurityDescriptor () ); } IDT.Assert( m_shadowInfo.Directory.Exists, "m_shadowInfo directory should be same as main directory" ); // // Open or create the main file // OpenOrCreateHelper( m_fileInfo, out m_file ); // // Open or create the shadow file // OpenOrCreateHelper( m_shadowInfo, out m_shadow ); } catch( IOException ioe ) { // // Race conditions could lead to IOExceptions for example // where a file is created externally just after we checked it does not exist. // We should not crash then so translate to a non-fatal exception. // throw IDT.ThrowHelperError( new DataAccessException( ioe.Message ) ); } } } // // Summary: // Validates that no directory in infocard path contains reparse points. // private void CheckReparsePoints() { using( SystemIdentity lsa = new SystemIdentity( true ) ) { DirectoryInfo f = m_fileInfo.Directory; while( null != f ) { if( f.Exists ) { if( ( f.Attributes & FileAttributes.ReparsePoint ) != 0 ) { throw IDT.ThrowHelperError( new DataAccessException( SR.GetString( SR.StoreNoReparsePointAllowed ) ) ); } } f = f.Parent; } // // Check file for reparse points. use directory check as reparse points are created as directories // if( Directory.Exists( m_fileInfo.FullName ) ) { if( ( m_fileInfo.Attributes & FileAttributes.ReparsePoint ) != 0 ) { throw IDT.ThrowHelperError( new DataAccessException( SR.GetString( SR.StoreNoReparsePointAllowed ) ) ); } } } } // // Summary: // Validates that m_fileInfo has the appropriate access levels. // private void ValidateDirectoryAccess() { IDT.Assert( !WindowsIdentity.GetCurrent().IsSystem, "Should not be running as system" ); IdentityReference user = WindowsIdentity.GetCurrent().User.Translate( typeof( NTAccount ) ); bool allowed = false; // // The parent directory must exist. // DirectoryInfo parent = m_fileInfo.Directory.Parent; if( parent.Exists ) { // // The current user must have full control. Include both inherited and explicit rights. // AuthorizationRuleCollection rules = parent.GetAccessControl().GetAccessRules( true, true, typeof( NTAccount ) ); foreach( FileSystemAccessRule rule in rules ) { if( rule.IdentityReference == user && rule.FileSystemRights == FileSystemRights.FullControl && rule.AccessControlType == AccessControlType.Allow ) { allowed = true; break; } } } if( !allowed ) { throw IDT.ThrowHelperError( new DataAccessException( SR.GetString( SR.StoreInvalidDataFilePath ) ) ); } return; } // // Summary: // Open or create file pointed to by the fileInfo object and init // the fileStream // // Params: // theFile - the file we want to open or create // fileStream - the r/w stream that needs to be initialized // private void OpenOrCreateHelper( FileInfo theFile, out FileStream fileStream ) { IDT.Assert( WindowsIdentity.GetCurrent().IsSystem, "Should be running as system" ); // // File.Exists works more reliably than theFile.Exists // if( !File.Exists( theFile.FullName ) ) { // // We just ACLed the directory, so we should be able to create... // fileStream = ResilientOpen( theFile, FileMode.CreateNew, FileAccess.ReadWrite ); theFile.Attributes |= FileAttributes.NotContentIndexed | FileAttributes.Hidden; theFile.SetAccessControl( CreateSecurityDescriptor () ); } else { if( NativeMcppMethods.PathSupportsPersistedSecurity( m_fileInfo.Directory.FullName ) && LogIfAclsTampered( theFile ) ) { // // Reset the ACL since we may not have permission to open the file // theFile.SetAccessControl( CreateSecurityDescriptor () ); theFile.Attributes |= FileAttributes.NotContentIndexed | FileAttributes.Hidden; } // // Now open the file // fileStream = ResilientOpen( theFile, FileMode.Open, FileAccess.ReadWrite ); } } // // Summary: // Implements the logic to delete main file, // move shadow file to main file, and to create // an empty shadow file // protected virtual void OnSwapFileWithShadow() { using( SystemIdentity lsa = new SystemIdentity( true ) ) { string fileName = m_fileInfo.FullName; ResilientDelete( m_fileInfo ); ResilientMove( m_shadowInfo, m_fileInfo ); m_fileInfo = new FileInfo( fileName ); m_shadowInfo = new FileInfo( fileName + ".shadow" ); } } // // Summary: // Override for handling the Close events // protected internal override void OnClose() { ThrowIfDisposed(); // // m_data is null in event of 0 file size // if( null != m_data ) { m_data.Close(); m_data = null; } m_indexes.Close(); CloseFiles(); m_provider.Dispose(); } // // Summary: // Definition for Reading a Row from the data source, using a local identifier. // // Remarks: // if QueryDetals.Identifiers or QueryDetails.None is specified, NULL wil be returned. // // Parameters: // localId: The local identifier of the object to read. // details: The level of detail to return from the query. // // Returns: // The data row representing object. // protected internal override DataRow ReadRow( Int32 localId, QueryDetails details ) { DataRow row = null; if( QueryDetails.Header == ( details & QueryDetails.Header ) ) { // // Create a data row, including all header info // row = m_data.CreateDataRow( localId ); // // Read and decrypt the blob if required. // if( QueryDetails.DataBlob == ( details & QueryDetails.DataBlob ) ) { byte[ ] iv = AllocateIvBuffer(); m_data.CopyIVFromObject( localId, iv, 0 ); using( Stream encrypted = m_data.GetStreamForObjectData( localId ) ) { using( MemoryStream decryptedStream = new MemoryStream( (int)encrypted.Length ) ) { DecryptData( iv, encrypted, decryptedStream ); // // This will re-allocate a new array that is the correct size. // row.SetDataField( decryptedStream.ToArray() ); // // Becuase we allocated a new array above, we left the // sensitive data in the other array. We will will clear // the other array manually to ensure no data disclosure. // byte[ ] leftOver = decryptedStream.GetBuffer(); Array.Clear( leftOver, 0, leftOver.Length ); } } } // // If we are supposed to get all index data, scrape all secondary indexes // for any value that pertains to this object. // if( QueryDetails.IndexData == ( details & QueryDetails.IndexData ) ) { m_indexes.PopulateRowIndexBuffer( row ); } } return row; } // // Summary: // Performs a Single match using the specified query data. // // Remarks: // // Parameters: // match: The QueryParameter you want to match against. // localIds: The list of LocalId values that have matched // in previous queries, all results will will be // anded with this list. // Returns: // boolean indicating a successful query match // protected internal override bool SingleMatch( QueryParameter match, LocalIdCollection localIds ) { ThrowIfDisposed(); ThrowIfNotLoaded(); if( null == localIds ) { throw IDT.ThrowHelperArgumentNull( "localIds" ); } if( null == match ) { throw IDT.ThrowHelperArgumentNull( "match" ); } if( String.IsNullOrEmpty( match.IndexName ) ) { throw IDT.ThrowHelperError( new ArgumentException( SR.GetString( SR.StoreDataSourceInvalidIndexName, match.IndexName ), "match" ) ); } bool lockHeld = false; try { try { } finally { m_lock.AcquireReaderLock( 0 ); lockHeld = true; } return m_indexes.Match( match, localIds ); } finally { if( lockHeld ) { m_lock.ReleaseReaderLock(); } } } // // Summary: // Handle BeginTransaction Event. // protected internal override void OnBeginTransaction() { IDT.Assert( false == m_currentTransactionDirty, "m_currentTransactionDirty should have been set to false before beginning a transaction" ); base.OnBeginTransaction(); } // // Summary: // Handle Rollback Event // // Remarks: // On Rollbacks, we simply re-load the data buffers from // the original data file. // protected internal override void OnRollbackTransaction() { try { base.OnRollbackTransaction(); LoadFrom( m_file ); } finally { m_currentTransactionDirty = false; } } // // Summary: // Handle the Commit Event // // Remarks: // On Commit, we flush the data to the shadow file, // then swap this main file with the shadow file. // protected internal override void OnCommitTransaction() { try { base.OnCommitTransaction(); if( m_currentTransactionDirty ) { FlushToShadow(); SwapFileWithShadow(); } } finally { m_currentTransactionDirty = false; } } // // Summary: // Writes a DataRow into the data buffers, using the // index and identifier information with in the datarow. // // Remarks: // // // Parameters: // row: The row to write to the instance. // protected internal override void WriteRow( DataRow row ) { ThrowIfDisposed(); ThrowIfNotLoaded(); ThrowIfWriteLockNotHeld(); if( null == row ) { throw IDT.ThrowHelperArgumentNull( "row" ); } bool lockHeld = false; try { try { } finally { m_lock.AcquireWriterLock( 0 ); lockHeld = true; } byte[ ] iv; byte[ ] dataField; Int32 id; using( MemoryStream encrypted = new MemoryStream() ) { m_encAlg.GenerateIV(); iv = m_encAlg.IV; dataField = row.GetDataField(); using( MemoryStream dataFieldStream = new MemoryStream( dataField ) ) { EncryptData( iv, dataFieldStream, encrypted ); id = m_data.WriteObject( row.LocalId, iv, encrypted.ToArray(),//this re-allocs, maybe there is a better way? row.ObjectType, row.LastChange, row.GlobalId ); m_indexes.SetValuesForId( id, row.IndexBuffer, true ); row.SourceId = SourceId; row.InstanceId = InstanceId; row.LocalId = id; } } } finally { if( lockHeld ) { m_lock.ReleaseWriterLock(); } } // // We wrote to store in memory, so set the dirty flag // m_currentTransactionDirty = true; } // // Summary: // Removes a DataObject from this data source. // // Remarks: // // Parameters: // id: The LocalId of the data object to remove. // protected internal unsafe override void RemoveObject( Int32 id ) { if( (UInt32)id > (UInt32)( ( m_data.Index.Length / sizeof( Int32 ) ) - 1 ) ) { throw IDT.ThrowHelperError( new ArgumentOutOfRangeException( "id", id, SR.GetString( SR.StoreDataSourceIdOutOfRange ) ) ); } bool lockHeld = false; try { try { } finally { m_lock.AcquireWriterLock( 0 ); lockHeld = true; } m_data.RemoveObject( id ); m_indexes.RemoveAllValuesForId( id ); } finally { if( lockHeld ) { m_lock.ReleaseWriterLock(); } } // // We wrote to store in memory, so set the dirty flag // m_currentTransactionDirty = true; } // // Summary: // Encrypts the m_key member using DPAPI. // unsafe void ProtectKey() { if( m_keyProtected ) { throw IDT.ThrowHelperError( new InvalidOperationException( SR.GetString( SR.StoreKeyAlreadyProtected ) ) ); } ProtectedMemory.Protect( m_key, MemoryProtectionScope.SameProcess ); m_keyProtected = true; } // // Summary: // Decrypts the m_key member using DPAPI. // unsafe void UnprotectKey() { if( !m_keyProtected ) { throw IDT.ThrowHelperError( new InvalidOperationException( SR.GetString( SR.StoreKeyNotAlreadyProtected ) ) ); } ProtectedMemory.Unprotect( m_key, MemoryProtectionScope.SameProcess ); m_keyProtected = false; } // // Summary: // Decrypts data from one stream into another. // // Remarks: // Data in the input stream should be clear and disposed when finished. // // Parameters: // iv: The initialization vector to use for encryption // input: The input stream to encrypt // output: The stream to hold the encrypted data. // unsafe void DecryptData( byte[ ] iv, Stream input, Stream output ) { Exception e = null; IDT.Assert( input is MemoryStream, "Invalid stream type" ); if( null == iv || iv.Length == 0 ) { throw IDT.ThrowHelperArgumentNull( "iv" ); } if( null == input ) { throw IDT.ThrowHelperArgumentNull( "input" ); } if( null == output ) { throw IDT.ThrowHelperArgumentNull( "output" ); } using( SafeCryptoKey key = new SafeCryptoKey( m_provider, m_key, iv ) ) { byte[ ] buffer = new byte[ input.Length ]; input.Seek( 0, SeekOrigin.Begin ); input.Read( buffer, 0, (int)input.Length ); uint length = (uint)buffer.Length; // // Encrypt the data returning the length + padding. // fixed( byte* pB = &buffer[ 0 ] ) { if( !NativeMethods.CryptDecrypt( key.Handle, IntPtr.Zero, 1, 0, (IntPtr)pB, ref length ) ) { e = new Win32Exception( Marshal.GetLastWin32Error() ); Array.Clear( buffer, 0, buffer.Length ); IDT.TraceAndLogException( e ); throw IDT.ThrowHelperError( e ); } } output.Write( buffer, 0, (int)length ); } } // // Summary: // Encrypts data from one stream into another. // // Remarks: // Data in the input stream should be clear and disposed when finished. // // Parameters: // iv: The initialization vector to use for encryption // input: The input stream to encrypt // output: The stream to hold the encrypted data. // unsafe void EncryptData( byte[ ] iv, Stream input, Stream output ) { Exception e = null; if( null == iv || iv.Length == 0 ) { throw IDT.ThrowHelperArgumentNull( "iv" ); } if( null == input ) { throw IDT.ThrowHelperArgumentNull( "input" ); } if( null == output ) { throw IDT.ThrowHelperArgumentNull( "output" ); } using( SafeCryptoKey key = new SafeCryptoKey( m_provider, m_key, iv ) ) { // // Encrypting could require at least a block length's padding, so add it // as appropriate. // byte[ ] buffer = new byte[ input.Length + m_key.Length ]; input.Seek( 0, SeekOrigin.Begin ); input.Read( buffer, 0, (int)input.Length ); uint realLength = (uint)input.Length; // // Encrypt the data returning the length + padding. // fixed( byte* pB = &buffer[ 0 ] ) { if( !NativeMethods.CryptEncrypt( key.Handle, IntPtr.Zero, 1, 0, (IntPtr)pB, ref realLength, (uint)buffer.Length ) ) { e = new Win32Exception( Marshal.GetLastWin32Error() ); IDT.TraceAndLogException( e ); throw IDT.ThrowHelperError( e ); } } output.Write( buffer, 0, (int)realLength ); } } // // Summary: // Creates an default store buffers // unsafe void CreateEmptyStore() { // // Allocate a masterIndex and Data buffer. // byte[ ] mIndex = new byte[ INITIALINDEXAREASIZE ]; byte[ ] data = new byte[ INITIALBLOBAREASIZE ]; // // Create the IndexedDataBuffer from mIndex and data // m_data = new IndexedDataBuffer( mIndex, data, 0, this ); // // Create and protect the key // m_encAlg.GenerateKey(); m_key = new byte[ m_encAlg.Key.Length ]; Array.Copy( m_encAlg.Key, 0, m_key, 0, m_encAlg.Key.Length ); // // It is invalid to clear the key, // and the SymmetricAlgorithm.Clear method will dispose the object // which we don't want. So, we will generate a new key, // thus erasing the key that was there. // m_encAlg.GenerateKey(); // // Protect our copy of the key. // m_keyProtected = false; ProtectKey(); // // Allocate the File Header buffer. // m_header = new byte[ GetTotalHeaderSize() ]; // // Set the version in the header // fixed( byte* pHeaderBuffer = &m_header[ GetOffsetToEncryptedFileHeader() ] ) { EncryptedFileStoreHeader* pHeader = (EncryptedFileStoreHeader*)pHeaderBuffer; // // From now on we write the new version of the header // pHeader->Version = FILEVERSIONV2; } // // Encrypt and protect the store key // into the m_header buffer. // EncryptAndSaveDPAPIKeyToHeader(); } // // Summary: // Flushes all data buffers to the shadow file // void FlushToShadow() { FlushToStream( m_shadow ); } // // Summary: // Gets the size of header including the signature header // unsafe int GetTotalHeaderSize() { return sizeof( EncryptedFileStoreHeader ) + GetTotalSignatureSize(); } // // Summary: // Gets the offset to the EncryptedFileHeader which is located after the signature header // int GetOffsetToEncryptedFileHeader() { return m_variableSignatureLength + sizeof( Int32 ); } // // Summary: // Gets the size of the signature header // int GetTotalSignatureSize() { return GetOffsetToEncryptedFileHeader(); } // // Helper function used to calculate how much space is to be allocated for an output // file data source // unsafe Int64 GetRequiredFileSize() { //length of header Int64 size = Convert.ToInt64( GetTotalHeaderSize() ); // // Add the length of the encryptedKey // size += Convert.ToInt64( m_encryptedKey.Length ); // // Length of data // size += Convert.ToInt64( m_data.DataLength ); // // Length of master index. // size += Convert.ToInt64( m_data.Index.Length + sizeof( Int32 ) ); foreach( string key in m_indexes.InnerIndexes.Keys ) { byte[ ] indexBuffer = ( (SecondaryIndex)m_indexes.InnerIndexes[ key ] ).GetBuffer(); size += Convert.ToInt64( sizeof( Int32 ) + sizeof( Int32 ) + indexBuffer.Length ); } // // Give 5% buffer room. // size += size / 20; return size; } // // Summary: // Flushes all data buffer to the specified stream. // // Remarks: // // Parameters: // stream: the data stream to write to // void FlushToStream( Stream stream ) { if( null == stream ) { throw IDT.ThrowHelperArgumentNull( "stream" ); } stream.SetLength( GetRequiredFileSize() ); const UInt32 version = FILEVERSIONV2; Int32 dataSize = 0; Int32 fullSize = 0; Int32 indexSize = 0; byte[ ] iv = null; BinaryWriter writer = new BinaryWriter( stream ); Int64 indexStart = 0; // // Create a new IV for the indexes // m_encAlg.GenerateIV(); iv = m_encAlg.IV; // // Capture the data size. // dataSize = m_data.DataLength; unsafe { // // Skip past the header for now, we will come back to it // after we gather up all the require information. // the data we are working is can be large, so we don't // want to have to create temp working space to calculate // sizes. We will just do it as we require. // stream.Seek( GetTotalHeaderSize(), SeekOrigin.Begin ); // // Write the key and the stream automatically advances // writer.Write( m_encryptedKey ); // // Write the IV // writer.Write( iv ); // // Capture the location in the stream // where the index starts // indexStart = stream.Position; // // Create a temp MemoryStream to hold the decrypted index buffers // using( BinaryWriter indexStream = new BinaryWriter( new MemoryStream() ) ) { // // Write the count of MasterIndex items and then the buffer // indexStream.Write( m_data.Index.Length / sizeof( Int32 ) ); indexStream.Write( m_data.Index ); // // Write the count of secondary indexes. // indexStream.Write( m_indexes.Count ); foreach( string key in m_indexes.InnerIndexes.Keys ) { // // Write the count of SecondaryIndexItem entries in the buffer, // the lastIndex value, and the buffer for the index. // SecondaryIndex secIndex = (SecondaryIndex)m_indexes.InnerIndexes[ key ]; int lastIndex = secIndex.LastIndex; indexStream.Write( lastIndex ); int minCapacity = SecondaryIndexDefinition.GetByName( key ).InitialSize; if( lastIndex + 1 < minCapacity ) { // // Write the minimum capacity of elements // indexStream.Write( minCapacity ); IDT.Assert( ( minCapacity * sizeof( SecondaryIndexItem ) ) <= secIndex.GetBuffer().Length, "Buffer length is at least min capacity" ); indexStream.Write( secIndex.GetBuffer(), 0, minCapacity * sizeof( SecondaryIndexItem ) ); } else { // // Write only last index amount of entries, i.e. no unused SecondaryIndexItems are persisted // indexStream.Write( lastIndex + 1 ); indexStream.Write( secIndex.GetBuffer(), 0, ( lastIndex + 1 ) * sizeof( SecondaryIndexItem ) ); } } indexStream.Flush(); // // Seek the temp stream back to the beginning. // indexStream.Seek( 0, SeekOrigin.Begin ); // // Encrypt the temp memory stream directly to the // incoming stream param. // EncryptData( iv, indexStream.BaseStream, writer.BaseStream ); } // // Capture the size of the index based off the new position // of the stream vs the position we captured before we started. // indexSize = Convert.ToInt32( stream.Position - indexStart ); // // Capture the full size of the file. // fullSize = indexSize + m_data.DataLength + GetTotalHeaderSize() + iv.Length + m_encryptedKey.Length; // // Write out the data length. // writer.Write( m_data.Data, 0, m_data.DataLength ); // // Seek back to the start, and write the header. // stream.Seek( 0, SeekOrigin.Begin ); // // Flush the data to the steram, so we can write // the header information safely. // writer.Flush(); // // Syncronize the header buffer with the other objects. // fixed( Byte* pHeaderBuffer = &m_header[ GetOffsetToEncryptedFileHeader() ] ) { EncryptedFileStoreHeader* pHeader = (EncryptedFileStoreHeader*)pHeaderBuffer; pHeader->DataSize = dataSize; pHeader->Version = version; pHeader->Size = fullSize; pHeader->IndexSize = indexSize; } } // // Write the header buffer // writer.Write( m_header ); // // Set the length to the caluclated size. // stream.SetLength( Convert.ToInt64( fullSize ) ); // // Flush the data back to the disk. // writer.Flush(); // // Sign the stream using the key. // SignStream( stream ); } // // Summary: // Reads and signs a stream. // // Remarks: // Signature is written to first block of the file. // Only the non-signature data is signed. // // Parameters: // input: input stream to read and sign // void SignStream( Stream input ) { if( null == input ) { throw IDT.ThrowHelperArgumentNull( "input" ); } long pos = input.Position; m_encAlg.GenerateIV(); byte[ ] iv = m_encAlg.IV; byte[ ] signature = SignDigest( iv, CreateStreamDigest( input ) ); // // Allocate the Signature Header buffer. // byte[ ] m_signatureHeader = new byte[ GetTotalSignatureSize() ]; unsafe { fixed( byte* pSignatureHeaderBuffer = &m_signatureHeader[ 0 ] ) { SignatureHeader* pSignatureHeader = (SignatureHeader*)pSignatureHeaderBuffer; pSignatureHeader->SignatureSize = m_variableSignatureLength; } } Array.Copy( signature, 0, m_signatureHeader, sizeof( Int32 ), signature.Length ); // // Write in the signature header at the start of the stream // input.Seek( 0, SeekOrigin.Begin ); input.Write( m_signatureHeader, 0, m_signatureHeader.Length ); IDT.Assert( m_variableSignatureLength >= signature.Length, "Generated signature should be less than signature length" ); input.Seek( pos, SeekOrigin.Begin ); } byte[ ] CreateStreamDigest( Stream input ) { input.Seek( GetOffsetToEncryptedFileHeader(), SeekOrigin.Begin ); // // Enough space to hold all 4096 segment hashes plus any remaining. // Int32 totalSpace = Convert.ToInt32( ( ( input.Length / 4096 ) + 1 ) * HashUtility.HashBufferLength ); byte[ ] digest = new byte[ totalSpace ]; int offset = 0; byte[ ] tmp = new byte[ 4096 ]; int bytesRead = 0; do { bytesRead = input.Read( tmp, 0, tmp.Length ); if( bytesRead > 0 ) { HashUtility.SetHashValue( digest, offset, tmp, 0, bytesRead ); offset += HashUtility.HashBufferLength; } } while( bytesRead == tmp.Length ); return digest; } byte[ ] SignDigest( byte[ ] iv, byte[ ] digest ) { byte[ ] output = new byte[ m_variableSignatureLength ]; using( MemoryStream input = new MemoryStream( digest ) ) { using( MemoryStream encrypted = new MemoryStream( digest.Length ) ) { EncryptData( iv, input, encrypted ); byte[ ] fullData = encrypted.GetBuffer(); int start = Convert.ToInt32( encrypted.Position - 1 ) - iv.Length; Array.Copy( fullData, start, output, 0, iv.Length ); Array.Copy( iv, 0, output, iv.Length, iv.Length ); return output; } } } // // Summary: // Validates the signature of the provided stream. // // Remarks: // throws InvalidOperationException when the signature is invalid. // // Parameters: // input: The stream who signature needs validated. // void ValidateSignature( Stream input ) { if( null == input ) { throw IDT.ThrowHelperArgumentNull( "input" ); } long pos = input.Position; byte[ ] sigToMatch = new byte[ m_variableSignatureLength ]; byte[ ] iv = AllocateIvBuffer(); byte[ ] signature; // // Read the IV // input.Seek( SignatureHeader.IVOffset, SeekOrigin.Begin ); input.Read( iv, 0, iv.Length ); // // Generate the signature. // signature = SignDigest( iv, CreateStreamDigest( input ) ); // // Read the existing signature. // input.Seek( SignatureHeader.SignatureOffset, SeekOrigin.Begin ); input.Read( sigToMatch, 0, sigToMatch.Length ); for( int i = 0; i < sigToMatch.Length; i++ ) { if( sigToMatch[ i ] != signature[ i ] ) { throw IDT.ThrowHelperError( new CorruptStoreException( SR.GetString( SR.StoreSignatureNotValid ) ) ); } } input.Seek( pos, SeekOrigin.Begin ); } // // Summary: // Loads all data buffers from the specified stream. // // Remarks: // // Parameters: // stream: Data stream to use to initalize all buffers. // protected void LoadFrom( Stream stream ) { if( null == stream ) { throw IDT.ThrowHelperArgumentNull( "stream" ); } stream.Seek( 0, SeekOrigin.Begin ); unsafe { BinaryReader reader = new InfoCardBinaryReader( stream ); { m_header = new byte[ GetTotalHeaderSize() ]; // // Read the File Header. // reader.Read( m_header, 0, m_header.Length ); // // Read the version // fixed( Byte* pHeaderBuffer = &m_header[ GetOffsetToEncryptedFileHeader() ] ) { EncryptedFileStoreHeader* pHeader = (EncryptedFileStoreHeader*)pHeaderBuffer; // // First check the version info that it is supported. // if ( FILEVERSION != pHeader->Version && FILEVERSIONV2 != pHeader->Version ) { CorruptStoreException cse = new CorruptStoreException( SR.GetString( SR.StoreVersionNotSupported, pHeader->Version ) ); IDT.TraceAndLogException( cse ); throw IDT.ThrowHelperError( cse ); } if( FILEVERSIONV2 == pHeader->Version ) { // // Since the stream is at the end of the header, we know // that the next chunk of data is the encryptedKey m_encryptedKey = new byte[ pHeader->KeyBlockV2.KeyDataSize ]; reader.Read( m_encryptedKey, 0, pHeader->KeyBlockV2.KeyDataSize ); } } // // Populate the signature size // fixed( Byte* pSignatureHeaderBuffer = &m_header[ 0 ] ) { SignatureHeader* pSignatureHeader = (SignatureHeader*)pSignatureHeaderBuffer; int inputSignatureLength = pSignatureHeader->SignatureSize; // // This code will go away // if( m_variableSignatureLength != inputSignatureLength ) { throw IDT.ThrowHelperError( new CorruptStoreException( SR.GetString( SR.StoreSignatureNotValid ) ) ); } } // // Try to get the encryption key from the file header using // DPAPI or the passphrase. Will throw if invalid. // ObtainDataKeyFromHeader(); // // Once the key has been obtained, we must ensure that the signature // matches the data in the file. // ValidateSignature( stream ); fixed( Byte* pHeaderBuffer = &m_header[ GetOffsetToEncryptedFileHeader() ] ) { EncryptedFileStoreHeader* pHeader = (EncryptedFileStoreHeader*)pHeaderBuffer; byte[ ] iv = AllocateIvBuffer(); byte[ ] mIndex; byte[ ] sIndex; byte[ ] dBuffer; int secondaryIndexCount; int secondaryIndexLastIndex; int masterIndexCount; int secondaryIndexSize; // // Read all of the secondaryindex information. // using( MemoryStream tempDataStream = new MemoryStream() ) { // // read the IV used for encrypting the indexes. // reader.Read( iv, 0, iv.Length ); // // Decrypt the index information // byte[ ] encryptedIndexBuffer = reader.ReadBytes( pHeader->IndexSize ); using( MemoryStream tempEncryptedStream = new MemoryStream( encryptedIndexBuffer ) ) { DecryptData( iv, tempEncryptedStream, tempDataStream ); Array.Clear( encryptedIndexBuffer, 0, encryptedIndexBuffer.Length ); } tempDataStream.Seek( 0, SeekOrigin.Begin ); BinaryReader indexReader = new InfoCardBinaryReader( tempDataStream ); { masterIndexCount = indexReader.ReadInt32(); IDT.Assert( masterIndexCount > 0, "MasterIndexCount Invalid" ); mIndex = indexReader.ReadBytes( masterIndexCount * sizeof( Int32 ) ); secondaryIndexCount = indexReader.ReadInt32(); IDT.Assert( (UInt32)secondaryIndexCount <= (UInt32)m_indexes.Count, "SecondaryIndexCount Invalid" ); foreach( string key in m_indexes.InnerIndexes.Keys ) { secondaryIndexLastIndex = indexReader.ReadInt32(); secondaryIndexSize = indexReader.ReadInt32(); int newSize = Utility.CalculateIncreaseByPercent( secondaryIndexSize * sizeof( SecondaryIndexItem ), sizeof( SecondaryIndexItem ), SecondaryIndexDefinition.GetByName( key ).GrowthFactor ); sIndex = new byte[ newSize ]; indexReader.Read( sIndex, 0, secondaryIndexSize * sizeof( SecondaryIndexItem ) ); m_indexes.SetBuffer( key, sIndex, secondaryIndexLastIndex ); } } } // // Read the datablob section // int dataBlobLength = Utility.CalculateIncreaseByPercent( Convert.ToInt32( reader.BaseStream.Length - reader.BaseStream.Position ), 1, 5 ); using( MemoryStream tempDataStream = new MemoryStream( dataBlobLength ) ) { byte[ ] localBuffer = new byte[ 256 ]; int bytesRead = 0; int totalBytesRead = 0; do { bytesRead = reader.Read( localBuffer, 0, localBuffer.Length ); tempDataStream.Write( localBuffer, 0, bytesRead ); totalBytesRead += bytesRead; } while( bytesRead == localBuffer.Length ); dBuffer = tempDataStream.GetBuffer(); if( 0 == totalBytesRead ) { dBuffer = new byte[ INITIALBLOBAREASIZE ]; } m_data = new IndexedDataBuffer( mIndex, dBuffer, pHeader->DataSize, this ); } } } } } // // Summary: // Do "atomic" swap of shadow with real file. // protected virtual void SwapFileWithShadow() { CloseFiles(); OnSwapFileWithShadow(); try { CreateDirAndFiles(); } catch { CloseFiles(); throw; } } // // Summary: // Encrypts and saves KSTORE to the file header using DPAPI // unsafe void EncryptAndSaveDPAPIKeyToHeader() { // // NOTE: These are all STACK structures, // even though they have a new statement. // They must be assigned to something, or the compiler, // will nto let nem be used as a ref. // DataBlob raw; DataBlob userEncrypted = new DataBlob(); DataBlob doubleEncrypted = new DataBlob(); byte[ ] dataToEncrypt = new byte[ ENCRYPTIONKEYBUFFERSIZE + HashUtility.HashBufferLength ]; UnprotectKey(); try { // // Copy in the key // Array.Copy( m_key, 0, dataToEncrypt, 0, ENCRYPTIONKEYBUFFERSIZE ); // // Append the hash of the key // HashUtility.SetHashValue( dataToEncrypt, ENCRYPTIONKEYBUFFERSIZE, m_key, 0, ENCRYPTIONKEYBUFFERSIZE ); fixed( Byte* pKey = &dataToEncrypt[ 0 ] ) { // // Capture the raw key // raw.pbData = new IntPtr( pKey ); raw.cbData = dataToEncrypt.Length; try { // // We first protect the data using the users credentials. // if( !NativeMethods.CryptProtectData( new IntPtr( &raw ), null, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, NativeMethods.CRYPTPROTECT_UI_FORBIDDEN | NativeMethods.CRYPTPROTECT_VERIFY_PROTECTION, new IntPtr( &userEncrypted ) ) ) { throw IDT.ThrowHelperError( new Win32Exception( Marshal.GetLastWin32Error(), SR.GetString( SR.StoreCryptProtectDataFailed ) ) ); } using( SystemIdentity lsa = new SystemIdentity( true ) ) { if( !NativeMethods.CryptProtectData( new IntPtr( &userEncrypted ), null, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, NativeMethods.CRYPTPROTECT_UI_FORBIDDEN | NativeMethods.CRYPTPROTECT_VERIFY_PROTECTION, new IntPtr( &doubleEncrypted ) ) ) { throw IDT.ThrowHelperError( new Win32Exception( Marshal.GetLastWin32Error(), SR.GetString( SR.StoreCryptProtectDataAsSystemFailed ) ) ); } } fixed( Byte* pHeaderBuffer = &m_header[ GetOffsetToEncryptedFileHeader() ] ) { EncryptedFileStoreHeader* pHeader = (EncryptedFileStoreHeader*)pHeaderBuffer; // // Set the KeyBlockV2 fields // pHeader->KeyBlockV2.KeyDataOffset = 0; pHeader->KeyBlockV2.KeyDataSize = doubleEncrypted.cbData; m_encryptedKey = new byte[ doubleEncrypted.cbData ]; // // Copy the encrypted key to the member // for( int i = 0; i < doubleEncrypted.cbData; i++ ) { m_encryptedKey[ i ] = ( (Byte*)doubleEncrypted.pbData.ToPointer() )[ i ]; } } } finally { Exception e = null; if( IntPtr.Zero != userEncrypted.pbData ) { Utility.ClearUnsafeMemory( userEncrypted.pbData, userEncrypted.cbData ); if( IntPtr.Zero != NativeMethods.LocalFree( userEncrypted.pbData ) ) { e = new Win32Exception( Marshal.GetLastWin32Error() ); } } if( IntPtr.Zero != doubleEncrypted.pbData ) { Utility.ClearUnsafeMemory( doubleEncrypted.pbData, doubleEncrypted.cbData ); if( IntPtr.Zero != NativeMethods.LocalFree( doubleEncrypted.pbData ) ) { // // We potentially overwrite here, but will always report one of the // exceptions. // e = new Win32Exception( Marshal.GetLastWin32Error() ); } } if( null != e ) { throw IDT.ThrowHelperError( e ); } } } } finally { ProtectKey(); Array.Clear( dataToEncrypt, 0, dataToEncrypt.Length ); } } // // Summary: // Reads the key from the header file, base on the state // of the object. // void ObtainDataKeyFromHeader() { try { byte[ ] key = TryObtainDataKeyFromDPAPI(); if( null == key ) { throw IDT.ThrowHelperError( new InvalidOperationException( SR.GetString( SR.StoreUnableToGetStoreKeyFromDPAPI ) ) ); } m_key = key; m_keyProtected = false; ProtectKey(); } catch( Exception e ) { if( IDT.IsFatal( e ) ) { throw; } throw IDT.ThrowHelperError( new InvalidStoreProtectionKeyException() ); } } // // Summary: // Trys to obtain KSTORE using DPAPI // // Returns: // KSTORE on success, null on failure // unsafe byte[ ] TryObtainDataKeyFromDPAPI() { // // We do double encryption here. // First, we should be running as System, // so our initial call to DPAPI will run as System. // We then need to decrypt using the Users context, // this reduces the chance of a single key exposing all // user data. // DataBlob raw = new DataBlob(); DataBlob userEncrypted = new DataBlob(); // not an out param on a method, so we don't have to explicity ctor it. DataBlob doubleEncrypted; fixed( Byte* pHeaderBuffer = &m_header[ GetOffsetToEncryptedFileHeader() ] ) { EncryptedFileStoreHeader* pHeader = (EncryptedFileStoreHeader*)pHeaderBuffer; if( FILEVERSION == pHeader->Version && pHeader->KeyBlock.KeyType != 0 ) { throw IDT.ThrowHelperError( new InvalidOperationException( SR.GetString( SR.StoreFileNotProtectedWithDPAPI ) ) ); } // // Managed members for output and verificaiton // byte[ ] key = null; byte[ ] keyVerify = null; GCHandle gch = new GCHandle(); try { // // Switch on the Fileversion on the header type to read // the encrypted key // if( pHeader->Version == FILEVERSIONV2 ) { gch = GCHandle.Alloc( m_encryptedKey, GCHandleType.Pinned ); doubleEncrypted.pbData = gch.AddrOfPinnedObject(); doubleEncrypted.cbData = pHeader->KeyBlockV2.KeyDataSize; } else { doubleEncrypted.pbData = new IntPtr( &pHeader->KeyBlock.EncryptedKey ); doubleEncrypted.cbData = pHeader->KeyBlock.EncryptedKeySize; // // Upgrade Scenario: We have read the key from the v1 format, we need to // store the key in the v2 format from now on. // // // Set the KeyBlockV2 fields // pHeader->KeyBlockV2.KeyDataOffset = 0; pHeader->KeyBlockV2.KeyDataSize = doubleEncrypted.cbData; m_encryptedKey = new byte[ doubleEncrypted.cbData ]; // // Copy the encrypted key to the member // for( int i = 0; i < doubleEncrypted.cbData; i++ ) { m_encryptedKey[ i ] = ( (Byte*)doubleEncrypted.pbData.ToPointer() )[ i ]; } } using( SystemIdentity lsa = new SystemIdentity( true ) ) { // // Decrypt using System. // if( !NativeMethods.CryptUnprotectData( new IntPtr( &doubleEncrypted ), null, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, NativeMethods.CRYPTPROTECT_UI_FORBIDDEN, new IntPtr( &userEncrypted ) ) ) { throw IDT.ThrowHelperError( new Win32Exception( Marshal.GetLastWin32Error(), SR.GetString( SR.StoreCryptUnprotectDataAsSystemFailed ) ) ); } } // // Decrypt using the Store key as the user. // if( !NativeMethods.CryptUnprotectData( new IntPtr( &userEncrypted ), null, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, NativeMethods.CRYPTPROTECT_UI_FORBIDDEN, new IntPtr( &raw ) ) ) { throw IDT.ThrowHelperError( new Win32Exception( Marshal.GetLastWin32Error(), SR.GetString( SR.StoreCryptUnprotectDataFailed ) ) ); } // // Ensure that we have the correct size // IDT.Assert( raw.cbData == ( ENCRYPTIONKEYBUFFERSIZE + HashUtility.HashBufferLength ), "The data size returned by CryptUnprotectData is invalid or corrupt." ); key = new byte[ ENCRYPTIONKEYBUFFERSIZE ]; Marshal.Copy( raw.pbData, key, 0, ENCRYPTIONKEYBUFFERSIZE ); // // Calc a hash of the first part of the output. // keyVerify = new byte[ HashUtility.HashBufferLength ]; HashUtility.SetHashValue( keyVerify, 0, key, 0, ENCRYPTIONKEYBUFFERSIZE ); // // Verify the hash against the hash in the encrypted blob, // if they are different, then our files are invalid. // for( int i = 0; i < keyVerify.Length; i++ ) { if( keyVerify[ i ] != ( (Byte*)raw.pbData.ToPointer() )[ ENCRYPTIONKEYBUFFERSIZE + i ] ) { throw IDT.ThrowHelperError( new InvalidOperationException( SR.GetString( SR.StoreDecryptedKeyIsNotValid ) ) ); } } return key; } catch { if( null != key ) { Array.Clear( key, 0, key.Length ); } throw; } finally { // // Free the GCHandle // if( gch.IsAllocated ) { gch.Free(); } if( IntPtr.Zero != userEncrypted.pbData ) { Utility.ClearUnsafeMemory( userEncrypted.pbData, userEncrypted.cbData ); #pragma warning suppress 56523 // return value ignored NativeMethods.LocalFree( userEncrypted.pbData ); } if( IntPtr.Zero != raw.pbData ) { Utility.ClearUnsafeMemory( raw.pbData, raw.cbData ); #pragma warning suppress 56523 // return value ignored. NativeMethods.LocalFree( raw.pbData ); } } } } // // Summary: // Returns true & Log if acl associated with FILE/DIRECTORY has been modified/tampered with // // Arguments: // fileSysInfo - the file or dir we want to check // bool LogIfAclsTampered( FileSystemInfo fileSysInfo ) { bool tampered = true; try { FileSystemSecurity fs = null; // // Get the ACL based on whether we're dealing with dir or file. // if( fileSysInfo is DirectoryInfo ) { fs = ( (DirectoryInfo)fileSysInfo ).GetAccessControl( AccessControlSections.Access | AccessControlSections.Owner ); } else { IDT.Assert( fileSysInfo is FileInfo, "Only fileinfo possible" ); fs = ( (FileInfo)fileSysInfo ).GetAccessControl( AccessControlSections.Access | AccessControlSections.Owner ); } // // Lets do the check // tampered = LogIfAclsTamperedHelper( fs ); } catch( UnauthorizedAccessException ) { // tampered with for sure since we are not even able to access it IDT.Assert( tampered, "ACL must have been tampered with" ); } if( tampered ) { DataAccessException dae = new DataAccessException( SR.GetString( SR.StoreAclsTamperedWith ) ); IDT.TraceAndLogException( dae ); } return tampered; } // // Summary: // Returns true if acl modified/tampered with // // Arguments: // fs - the acl of the file or dir we want to check // bool LogIfAclsTamperedHelper( FileSystemSecurity fs ) { // // Check owner // if( SystemIdentity.LsaIdentityReference != fs.GetOwner( typeof( SecurityIdentifier ) ) ) { return true; } // // Check the rules // AuthorizationRuleCollection rules = fs.GetAccessRules( true, true, typeof( SecurityIdentifier ) ); // // For files only system is allowed ( and with full control ) // for the directory we grant admins the right to delete the directory and files within. // bool isDirectory = fs is DirectorySecurity; foreach( FileSystemAccessRule rule in rules ) { if( ( SystemIdentity.LsaIdentityReference == rule.IdentityReference && FileSystemRights.FullControl == rule.FileSystemRights && AccessControlType.Allow == rule.AccessControlType ) || ( isDirectory && AdministratorSid == rule.IdentityReference && AccessControlType.Allow == rule.AccessControlType && ( FileSystemRights.DeleteSubdirectoriesAndFiles | FileSystemRights.ReadData | FileSystemRights.Synchronize ) == rule.FileSystemRights ) ) { // allowed } else { return true; } } return false; } static T CreateSecurityDescriptor () where T :FileSystemSecurity, new() { T sec = new T(); sec.SetOwner( SystemIdentity.LsaIdentityReference ); AuthorizationRuleCollection rules = sec.GetAccessRules( true, true, typeof( SecurityIdentifier ) ); foreach( FileSystemAccessRule rule in rules ) { sec.RemoveAccessRuleAll( rule ); } sec.AddAccessRule( new FileSystemAccessRule( SystemIdentity.LsaIdentityReference, FileSystemRights.FullControl, InheritanceFlags.None, PropagationFlags.None, AccessControlType.Allow ) ); if( sec is DirectorySecurity ) { sec.AddAccessRule( new FileSystemAccessRule( AdministratorSid , FileSystemRights.DeleteSubdirectoriesAndFiles | FileSystemRights.ListDirectory, AccessControlType.Allow ) ); } sec.SetAccessRuleProtection( true, false );//protected and no inheritance. return sec; } private void SetupProvider() { m_provider = SafeRsaProviderHandle.Construct(); } private byte[ ] AllocateIvBuffer() { IDT.Assert( SignatureHeader.IVSize == m_encAlg.IV.Length, "Must be equal" ); IDT.Assert( ENCRYPTIONBLOCKBITLENGTH / 8 == m_encAlg.IV.Length, "Must be equal" ); return new byte[ m_encAlg.IV.Length ]; } // // Summary: // Attempts to delete a file using exponential backoff to cope // with other processes temporarily locking the data. // parameters: // toDelete - the file to delete // static private void ResilientDelete( FileInfo toDelete ) { const int MAX_TIME = 500; // milliseconds const int START_TIME = 10; // milliseconds int current = START_TIME; bool succeeded = true; Exception toReport = null; do { if( !succeeded ) { Thread.Sleep( current ); current *= 2; } try { toDelete.Delete(); succeeded = true; } catch( IOException ioe ) { IDT.TraceDebug( "Failed to delete {0} with error {1}\nDelay is now {2}\n", toDelete.FullName, ioe.Message, current ); toReport = ioe; succeeded = false; } } while( !succeeded && current <= MAX_TIME ); // // If we fail here the rollback logic should apply. // if( !succeeded ) { throw IDT.ThrowHelperError( new DataAccessException( SR.GetString( SR.StoreFileInUse ), toReport ) ); } } // // Summary: // Attempts to move a file using exponential backoff to cope // with other processes temporarily locking the data. // parameters: // fileFrom - the file we are moving from // fileTo - the file we are moving to. // static private void ResilientMove( FileInfo fileFrom, FileInfo fileTo ) { const int MAX_TIME = 500; // milliseconds const int START_TIME = 10; // milliseconds int current = START_TIME; bool succeeded = true; Exception toReport = null; do { if( !succeeded ) { Thread.Sleep( current ); current *= 2; } try { fileFrom.MoveTo( fileTo.FullName ); succeeded = true; } catch( IOException ioe ) { IDT.TraceDebug( "Failed to move {0} to {1} with error {2}\nDelay is now {3}\n", fileFrom.FullName, fileTo.FullName, ioe.Message, current ); toReport = ioe; succeeded = false; } } while( !succeeded && current <= MAX_TIME ); // // There is not much we can do here, as the commit operation is not currently atomic. We // will failfast the service and tell the user why we had to do so. // if( !succeeded ) { IDT.FailFast( SR.GetString( SR.StoreFileInUse ) ); } } // // Summary: // Attempts to open a file using exponential backoff to cope // with other processes temporarily locking the data. // parameters: // toOpen - the file we are looking to open // mode - the mode the file is to be openned in // access - the appropriate access to be granted. // static private FileStream ResilientOpen( FileInfo toOpen, FileMode mode, FileAccess access ) { const int MAX_TIME = 500; // milliseconds const int START_TIME = 10; // milliseconds int current = START_TIME; bool succeeded = true; Exception toReport = null; FileStream returnStream = null; do { if( !succeeded ) { Thread.Sleep( current ); current *= 2; } try { returnStream = toOpen.Open( mode, access, FileShare.None ); succeeded = true; } catch( IOException ioe ) { IDT.TraceDebug( "Failed to open {0} with error {1}\nDelay is now {2}\n", toOpen.FullName, ioe.Message, current ); toReport = ioe; succeeded = false; } } while( !succeeded && current <= MAX_TIME ); // // If we fail here then tell the user that the store files are in use. // if( !succeeded ) { throw IDT.ThrowHelperError( new DataAccessException( SR.GetString( SR.StoreFileInUse ), toReport ) ); } return returnStream; } // // Summary: // Creates the encryption alg we use. // internal static SymmetricAlgorithm CreateEncryptionAlg() { RijndaelManaged r = new RijndaelManaged(); r.Mode = CipherMode.CBC; r.KeySize = ENCRYPTIONKEYBITLENGTH; r.BlockSize = ENCRYPTIONBLOCKBITLENGTH; return r; } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- Pair.cs
- AssemblyResourceLoader.cs
- SrgsElementFactoryCompiler.cs
- CharUnicodeInfo.cs
- QilReplaceVisitor.cs
- MenuItemStyleCollection.cs
- OdbcParameterCollection.cs
- XmlUtil.cs
- FontSource.cs
- Encoder.cs
- GenericPrincipal.cs
- RegexCompilationInfo.cs
- PathSegmentCollection.cs
- CompilationRelaxations.cs
- ObjectDisposedException.cs
- PartDesigner.cs
- MulticastNotSupportedException.cs
- NamedObject.cs
- ElapsedEventArgs.cs
- ContractInferenceHelper.cs
- ProfileGroupSettingsCollection.cs
- StatusCommandUI.cs
- TypeProvider.cs
- ToolboxBitmapAttribute.cs
- StringBuilder.cs
- CFStream.cs
- Button.cs
- SqlNodeAnnotations.cs
- CmsInterop.cs
- DockPanel.cs
- EncoderParameter.cs
- KoreanLunisolarCalendar.cs
- ProcessInputEventArgs.cs
- GroupByQueryOperator.cs
- TextTreeNode.cs
- ConnectionStringSettings.cs
- VBCodeProvider.cs
- HtmlLiteralTextAdapter.cs
- securitymgrsite.cs
- SmtpFailedRecipientsException.cs
- PropertySegmentSerializer.cs
- DiagnosticTraceRecords.cs
- RedBlackList.cs
- UInt16Storage.cs
- QueryResult.cs
- Header.cs
- MasterPageParser.cs
- ControlEvent.cs
- DesignerHost.cs
- EntityDataSourceContextCreatedEventArgs.cs
- SBCSCodePageEncoding.cs
- MetadataElement.cs
- ConsumerConnectionPointCollection.cs
- SoapExtensionStream.cs
- InternalPermissions.cs
- ServiceHostingEnvironment.cs
- EdmToObjectNamespaceMap.cs
- SystemThemeKey.cs
- WebServiceErrorEvent.cs
- SessionState.cs
- DocumentEventArgs.cs
- Pens.cs
- PropertyChangedEventArgs.cs
- FormViewCommandEventArgs.cs
- PerformanceCounterPermission.cs
- WebPartDescription.cs
- TreeNodeMouseHoverEvent.cs
- GenericTransactionFlowAttribute.cs
- DefaultAutoFieldGenerator.cs
- ConnectionManagementSection.cs
- VectorCollection.cs
- CompilerParameters.cs
- ellipse.cs
- VirtualizedCellInfoCollection.cs
- XmlSchemaParticle.cs
- WebPermission.cs
- UndoEngine.cs
- ForeignConstraint.cs
- Point4DConverter.cs
- InfoCardRSAPKCS1SignatureDeformatter.cs
- OdbcDataReader.cs
- altserialization.cs
- DesignerObjectListAdapter.cs
- ListControl.cs
- DataGridViewRowPostPaintEventArgs.cs
- wgx_sdk_version.cs
- Version.cs
- XMLSchema.cs
- Config.cs
- RewritingPass.cs
- XmlComment.cs
- UnauthorizedAccessException.cs
- DecimalAnimationBase.cs
- WorkflowLayouts.cs
- Mouse.cs
- SizeValueSerializer.cs
- ColumnTypeConverter.cs
- InvalidateEvent.cs
- TimeManager.cs
- DataGridViewBindingCompleteEventArgs.cs