2016-08-03 10:59:49 +00:00
//------------------------------------------------------------------------------
// <copyright file="RemoteWebConfigurationHostServer.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
namespace System.Web.Configuration {
using System.Collections ;
using System.Configuration ;
using System.Security ;
using System.IO ;
using System.Globalization ;
using System.Text ;
using System.Runtime.InteropServices ;
using System.Reflection ;
using System.Web.Util ;
using System.Collections.Specialized ;
using System.Xml ;
using System.Security.Cryptography ;
#if ! FEATURE_PAL // FEATURE_PAL does not enable access control
using System.Security.AccessControl ;
#endif // !FEATURE_PAL
using System.Security.Permissions ;
2016-11-10 13:04:39 +00:00
using System.Diagnostics.CodeAnalysis ;
2016-08-03 10:59:49 +00:00
#if ! FEATURE_PAL // FEATURE_PAL does not enable COM
[ ComVisible ( true ) , ClassInterface ( ClassInterfaceType . AutoDual ) ,
#if WIN64
Guid ( "DFD0D215-72C0-450d-92B5-10971FC24625" ) , ProgId ( "System.Web.Configuration.RemoteWebConfigurationHostServerV4_64" ) ]
#else
Guid ( "9FDB6D2C-90EA-4e42-99E6-38B96E28698E" ) , ProgId ( "System.Web.Configuration.RemoteWebConfigurationHostServerV4_32" ) ]
#endif
#endif // FEATURE_PAL does not enable COM
[SecurityPermission(SecurityAction.Demand, Unrestricted = true)]
public class RemoteWebConfigurationHostServer : IRemoteWebConfigurationHostServer
{
internal const char FilePathsSeparatorChar = '<' ;
static internal readonly char [ ] FilePathsSeparatorParams = new char [ ] { FilePathsSeparatorChar } ;
public byte [ ] GetData ( string fileName , bool getReadTimeOnly , out long readTime )
{
if ( ! fileName . ToLowerInvariant ( ) . EndsWith ( ".config" , StringComparison . Ordinal ) )
throw new Exception ( SR . GetString ( SR . Can_not_access_files_other_than_config ) ) ;
byte [ ] buf ;
if ( File . Exists ( fileName ) ) {
if ( getReadTimeOnly ) {
buf = new byte [ 0 ] ;
}
else {
buf = File . ReadAllBytes ( fileName ) ;
}
DateTime lastWrite = File . GetLastWriteTimeUtc ( fileName ) ;
readTime = ( DateTime . UtcNow > lastWrite ? DateTime . UtcNow . Ticks : lastWrite . Ticks ) ;
} else {
buf = new byte [ 0 ] ;
readTime = DateTime . UtcNow . Ticks ;
}
return buf ;
}
public void WriteData ( string fileName , string templateFileName , byte [ ] data , ref long readTime )
{
if ( ! fileName . ToLowerInvariant ( ) . EndsWith ( ".config" , StringComparison . Ordinal ) )
throw new Exception ( SR . GetString ( SR . Can_not_access_files_other_than_config ) ) ;
bool fileExists = File . Exists ( fileName ) ;
FileInfo fileInfo = null ;
FileAttributes fileAttributes = FileAttributes . Normal ;
string tempFile = null ;
Exception createStreamExcep = null ;
FileStream tempFileStream = null ;
long lastWriteTicks = 0 ;
long utcNowTicks = 0 ;
/////////////////////////////////////////////////////////////////////
// Step 1: If the file exists, then make sure it hasn't been written to since it was read
if ( fileExists & & File . GetLastWriteTimeUtc ( fileName ) . Ticks > readTime ) {
throw new Exception ( SR . GetString ( SR . File_changed_since_read , fileName ) ) ;
}
/////////////////////////////////////////////////////////////////////
// Step 2: Get the security-descriptor and attributes of the file
if ( fileExists ) {
try {
fileInfo = new FileInfo ( fileName ) ;
fileAttributes = fileInfo . Attributes ;
} catch { }
if ( ( ( int ) ( fileAttributes & ( FileAttributes . ReadOnly | FileAttributes . Hidden ) ) ) ! = 0 )
throw new Exception ( SR . GetString ( SR . File_is_read_only , fileName ) ) ;
}
/////////////////////////////////////////////////////////////////////
// Step 3: Generate a temp file name. Make sure that the temp file doesn't exist
tempFile = fileName + "." + GetRandomFileExt ( ) + ".tmp" ;
for ( int iter = 0 ; File . Exists ( tempFile ) ; iter + + ) { // if it exists, then use a different random name
if ( iter > 100 ) // don't try more than 100 times
throw new Exception ( SR . GetString ( SR . Unable_to_create_temp_file ) ) ;
else
tempFile = fileName + "." + GetRandomFileExt ( ) + ".tmp" ;
}
/////////////////////////////////////////////////////////////////////
// Step 4: Write the buffer to the temp file, and move it to the actual file
try {
tempFileStream = new FileStream ( tempFile , FileMode . CreateNew , FileAccess . Write , FileShare . ReadWrite , data . Length , FileOptions . WriteThrough ) ;
tempFileStream . Write ( data , 0 , data . Length ) ;
} catch ( Exception e ) {
createStreamExcep = e ;
} finally {
if ( tempFileStream ! = null )
tempFileStream . Close ( ) ;
}
if ( createStreamExcep ! = null ) {
try {
File . Delete ( tempFile ) ;
} catch { }
throw createStreamExcep ;
}
if ( fileExists ) {
try {
DuplicateFileAttributes ( fileName , tempFile ) ;
} catch { }
}
else if ( templateFileName ! = null ) {
try {
DuplicateTemplateAttributes ( fileName , templateFileName ) ;
} catch { }
}
/////////////////////////////////////////////////////////////////////
// Step 4: Move the temp filt to the actual file
if ( ! UnsafeNativeMethods . MoveFileEx ( tempFile , fileName , MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH ) ) {
try {
File . Delete ( tempFile ) ;
} catch { }
Marshal . ThrowExceptionForHR ( Marshal . GetHRForLastWin32Error ( ) ) ;
}
/////////////////////////////////////////////////////////////////////
// Step 5: Set the attributes of the file
if ( fileExists ) {
fileInfo = new FileInfo ( fileName ) ;
fileInfo . Attributes = fileAttributes ;
}
/////////////////////////////////////////////////////////////////////
// Step 6: Record the current time as the read-time
lastWriteTicks = File . GetLastWriteTimeUtc ( fileName ) . Ticks ;
utcNowTicks = DateTime . UtcNow . Ticks ;
readTime = ( utcNowTicks > lastWriteTicks ? utcNowTicks : lastWriteTicks ) ;
}
public string GetFilePaths ( int webLevelAsInt , string path , string site , string locationSubPath )
{
WebLevel webLevel = ( WebLevel ) webLevelAsInt ;
IConfigMapPath configMapPath = IISMapPath . GetInstance ( ) ;
// Get the configuration paths and application information
string appSiteName , appSiteID ;
VirtualPath appPath ;
string configPath , locationConfigPath ;
WebConfigurationHost . GetConfigPaths ( configMapPath , webLevel , VirtualPath . CreateNonRelativeAllowNull ( path ) , site , locationSubPath ,
out appPath , out appSiteName , out appSiteID , out configPath , out locationConfigPath ) ;
//
// Format of filePaths:
// appPath < appSiteName < appSiteID < configPath < locationConfigPath [< configPath < fileName]+
//
ArrayList filePaths = new ArrayList ( ) ;
filePaths . Add ( VirtualPath . GetVirtualPathString ( appPath ) ) ;
filePaths . Add ( appSiteName ) ;
filePaths . Add ( appSiteID ) ;
filePaths . Add ( configPath ) ;
filePaths . Add ( locationConfigPath ) ;
string dummySiteID ;
VirtualPath virtualPath ;
WebConfigurationHost . GetSiteIDAndVPathFromConfigPath ( configPath , out dummySiteID , out virtualPath ) ;
// pathmap for machine.config
filePaths . Add ( WebConfigurationHost . MachineConfigPath ) ;
filePaths . Add ( HttpConfigurationSystem . MachineConfigurationFilePath ) ;
// pathmap for root web.config
if ( webLevel ! = WebLevel . Machine ) {
filePaths . Add ( WebConfigurationHost . RootWebConfigPath ) ;
filePaths . Add ( HttpConfigurationSystem . RootWebConfigurationFilePath ) ;
// pathmap for other paths
for ( VirtualPath currentVirtualPath = virtualPath ; currentVirtualPath ! = null ; currentVirtualPath = currentVirtualPath . Parent )
{
string currentConfigPath = WebConfigurationHost . GetConfigPathFromSiteIDAndVPath ( appSiteID , currentVirtualPath ) ;
string currentFilePath = configMapPath . MapPath ( appSiteID , currentVirtualPath . VirtualPathString ) ;
currentFilePath = System . IO . Path . Combine ( currentFilePath , HttpConfigurationSystem . WebConfigFileName ) ;
filePaths . Add ( currentConfigPath ) ;
filePaths . Add ( currentFilePath ) ;
}
}
// join into a single string
StringBuilder sb = new StringBuilder ( ) ;
for ( int i = 0 ; i < filePaths . Count ; i + + ) {
if ( i > 0 ) {
sb . Append ( FilePathsSeparatorChar ) ;
}
string part = ( string ) filePaths [ i ] ;
sb . Append ( part ) ;
}
return sb . ToString ( ) ;
}
2016-11-10 13:04:39 +00:00
[SuppressMessage("Microsoft.Security.Xml", "CA3057:DoNotUseLoadXml", Justification = "Developer-controlled xml contents are implicitly trusted by ASP.Net.")]
2016-08-03 10:59:49 +00:00
public string DoEncryptOrDecrypt ( bool doEncrypt , string xmlString , string protectionProviderName , string protectionProviderType , string [ ] paramKeys , string [ ] paramValues )
{
Type t = Type . GetType ( protectionProviderType , true ) ;
if ( ! typeof ( ProtectedConfigurationProvider ) . IsAssignableFrom ( t ) ) {
throw new Exception ( SR . GetString ( SR . WrongType_of_Protected_provider ) ) ;
}
ProtectedConfigurationProvider provider = ( ProtectedConfigurationProvider ) Activator . CreateInstance ( t ) ;
NameValueCollection cloneParams = new NameValueCollection ( paramKeys . Length ) ;
XmlNode node ;
for ( int iter = 0 ; iter < paramKeys . Length ; iter + + )
cloneParams . Add ( paramKeys [ iter ] , paramValues [ iter ] ) ;
provider . Initialize ( protectionProviderName , cloneParams ) ;
XmlDocument xmlDocument = new XmlDocument ( ) ;
xmlDocument . PreserveWhitespace = true ;
xmlDocument . LoadXml ( xmlString ) ;
if ( doEncrypt ) {
node = provider . Encrypt ( xmlDocument . DocumentElement ) ;
} else {
node = provider . Decrypt ( xmlDocument . DocumentElement ) ;
}
return node . OuterXml ;
}
public void GetFileDetails ( string name , out bool exists , out long size , out long createDate , out long lastWriteDate ) {
if ( ! name . ToLowerInvariant ( ) . EndsWith ( ".config" , StringComparison . Ordinal ) )
throw new Exception ( SR . GetString ( SR . Can_not_access_files_other_than_config ) ) ;
UnsafeNativeMethods . WIN32_FILE_ATTRIBUTE_DATA data ;
if ( UnsafeNativeMethods . GetFileAttributesEx ( name , UnsafeNativeMethods . GetFileExInfoStandard , out data ) & & ( data . fileAttributes & ( int ) FileAttributes . Directory ) = = 0 ) {
exists = true ;
size = ( long ) ( uint ) data . fileSizeHigh < < 32 | ( long ) ( uint ) data . fileSizeLow ;
createDate = ( ( ( long ) data . ftCreationTimeHigh ) < < 32 ) | ( ( long ) data . ftCreationTimeLow ) ;
lastWriteDate = ( ( ( long ) data . ftLastWriteTimeHigh ) < < 32 ) | ( ( long ) data . ftLastWriteTimeLow ) ;
} else {
exists = false ;
size = 0 ;
createDate = 0 ;
lastWriteDate = 0 ;
}
}
private static string GetRandomFileExt ( ) {
byte [ ] buf = new byte [ 2 ] ;
( new RNGCryptoServiceProvider ( ) ) . GetBytes ( buf ) ;
return buf [ 1 ] . ToString ( "X" , CultureInfo . InvariantCulture ) + buf [ 0 ] . ToString ( "X" , CultureInfo . InvariantCulture ) ;
}
private void DuplicateFileAttributes ( string oldFileName , string newFileName ) {
#if ! FEATURE_PAL // FEATURE_PAL does not enable access control
FileAttributes attributes ;
DateTime creationTime ;
// Copy File Attributes, ie. Hidden, Readonly, etc.
attributes = File . GetAttributes ( oldFileName ) ;
File . SetAttributes ( newFileName , attributes ) ;
// Copy Creation Time
creationTime = File . GetCreationTimeUtc ( oldFileName ) ;
File . SetCreationTimeUtc ( newFileName , creationTime ) ;
DuplicateTemplateAttributes ( oldFileName , newFileName ) ;
}
private void DuplicateTemplateAttributes ( string oldFileName , string newFileName ) {
FileSecurity fileSecurity ;
// Copy Security information
// If we don't have the privelege to get the Audit information,
// then just persist the DACL
try {
fileSecurity = File . GetAccessControl ( oldFileName ,
AccessControlSections . Access |
AccessControlSections . Audit ) ;
// Mark dirty, so effective for write
fileSecurity . SetAuditRuleProtection ( fileSecurity . AreAuditRulesProtected , true ) ;
} catch ( UnauthorizedAccessException ) {
fileSecurity = File . GetAccessControl ( oldFileName ,
AccessControlSections . Access ) ;
}
// Mark dirty, so effective for write
fileSecurity . SetAccessRuleProtection ( fileSecurity . AreAccessRulesProtected , true ) ;
File . SetAccessControl ( newFileName , fileSecurity ) ;
#endif // !FEATURE_PAL
}
const int MOVEFILE_REPLACE_EXISTING = 0x00000001 ;
const int MOVEFILE_COPY_ALLOWED = 0x00000002 ;
const int MOVEFILE_DELAY_UNTIL_REBOOT = 0x00000004 ;
const int MOVEFILE_WRITE_THROUGH = 0x00000008 ;
}
}