1 //------------------------------------------------------------------------------
2 // <copyright file="InternalConfigHost.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 //------------------------------------------------------------------------------
7 namespace System
.Configuration
.Internal
{
9 using System
.Diagnostics
.CodeAnalysis
;
10 using System
.CodeDom
.Compiler
;
11 using System
.Configuration
;
13 using System
.Reflection
;
14 using System
.Security
;
16 using System
.Security
.AccessControl
;
18 using System
.Security
.Permissions
;
19 using System
.Security
.Policy
;
20 using System
.Threading
;
24 // An IInternalConfigHost with common implementations of some file functions.
26 internal sealed class InternalConfigHost
: IInternalConfigHost
, IInternalConfigurationBuilderHost
{
27 private IInternalConfigRoot _configRoot
;
29 internal InternalConfigHost() {
32 void IInternalConfigHost
.Init(IInternalConfigRoot configRoot
, params object[] hostInitParams
) {
33 _configRoot
= configRoot
;
36 void IInternalConfigHost
.InitForConfiguration(ref string locationSubPath
, out string configPath
, out string locationConfigPath
,
37 IInternalConfigRoot configRoot
, params object[] hostInitConfigurationParams
) {
39 _configRoot
= configRoot
;
41 locationConfigPath
= null;
44 // config path support
45 bool IInternalConfigHost
.IsConfigRecordRequired(string configPath
) {
49 bool IInternalConfigHost
.IsInitDelayed(IInternalConfigRecord configRecord
) {
53 void IInternalConfigHost
.RequireCompleteInit(IInternalConfigRecord configRecord
) {
58 // In the default there are no secondary root's
60 public bool IsSecondaryRoot(string configPath
) {
65 string IInternalConfigHost
.GetStreamName(string configPath
) {
66 throw ExceptionUtil
.UnexpectedError("IInternalConfigHost.GetStreamName");
69 [FileIOPermission(SecurityAction
.Assert
, AllFiles
= FileIOPermissionAccess
.PathDiscovery
)]
70 [SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Justification
= "The callers don't leak this information.")]
71 static internal string StaticGetStreamNameForConfigSource(string streamName
, string configSource
) {
73 // Note (Microsoft 7/08/05):
74 // RemoteWebConfigurationHost also redirects GetStreamNameForConfigSource to this
75 // method, and that means streamName is referring to a path that's on the remote
76 // machine. The problem is that Path.GetFullPath will demand FileIOPermission on
77 // that file and *assume* the file is referring to one on the local machine.
78 // This can be a potential problem for RemoteWebConfigurationHost. However, since
79 // we Assert the PathDiscovery permission at this method, so this problem is handled
80 // and we're okay. But in the future if we modify this method to handle anything
81 // that assumes streamName is a local path, then RemoteWebConfigurationHost has to
82 // override GetStreamNameForConfigSource.
85 // don't allow relative paths for stream name
86 if (!Path
.IsPathRooted(streamName
)) {
87 throw ExceptionUtil
.ParameterInvalid("streamName");
90 // get the path part of the original stream
91 streamName
= Path
.GetFullPath(streamName
);
92 string dirStream
= UrlPath
.GetDirectoryOrRootName(streamName
);
94 // combine with the new config source
95 string result
= Path
.Combine(dirStream
, configSource
);
96 result
= Path
.GetFullPath(result
);
98 // ensure the result is in or under the directory of the original source
99 string dirResult
= UrlPath
.GetDirectoryOrRootName(result
);
100 if (!UrlPath
.IsEqualOrSubdirectory(dirStream
, dirResult
)) {
101 throw new ArgumentException(SR
.GetString(SR
.Config_source_not_under_config_dir
, configSource
));
107 string IInternalConfigHost
.GetStreamNameForConfigSource(string streamName
, string configSource
) {
108 return StaticGetStreamNameForConfigSource(streamName
, configSource
);
111 static internal object StaticGetStreamVersion(string streamName
) {
114 DateTime utcCreationTime
= DateTime
.MinValue
;
115 DateTime utcLastWriteTime
= DateTime
.MinValue
;
117 UnsafeNativeMethods
.WIN32_FILE_ATTRIBUTE_DATA data
;
118 if ( UnsafeNativeMethods
.GetFileAttributesEx(streamName
, UnsafeNativeMethods
.GetFileExInfoStandard
, out data
) &&
119 (data
.fileAttributes
& (int) FileAttributes
.Directory
) == 0) {
121 fileSize
= (long)(uint)data
.fileSizeHigh
<< 32 | (long)(uint)data
.fileSizeLow
;
122 utcCreationTime
= DateTime
.FromFileTimeUtc(((long)data
.ftCreationTimeHigh
) << 32 | (long)data
.ftCreationTimeLow
);
123 utcLastWriteTime
= DateTime
.FromFileTimeUtc(((long)data
.ftLastWriteTimeHigh
) << 32 | (long)data
.ftLastWriteTimeLow
);
126 return new FileVersion(exists
, fileSize
, utcCreationTime
, utcLastWriteTime
);
129 object IInternalConfigHost
.GetStreamVersion(string streamName
) {
130 return StaticGetStreamVersion(streamName
);
133 // default impl treats name as a file name
134 // null means stream doesn't exist for this name
135 static internal Stream
StaticOpenStreamForRead(string streamName
) {
136 if (string.IsNullOrEmpty(streamName
)) {
137 throw ExceptionUtil
.UnexpectedError("InternalConfigHost::StaticOpenStreamForRead");
140 if (!FileUtil
.FileExists(streamName
, true))
143 // consider: FileShare.Delete is not exposed by FileShare enumeration
144 return new FileStream(streamName
, FileMode
.Open
, FileAccess
.Read
, FileShare
.Read
);
147 Stream IInternalConfigHost
.OpenStreamForRead(string streamName
) {
148 return ((IInternalConfigHost
)this).OpenStreamForRead(streamName
, false);
151 // Okay to suppress, since this is callable only through internal interfaces.
152 [SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity")]
153 Stream IInternalConfigHost
.OpenStreamForRead(string streamName
, bool assertPermissions
) {
154 Stream stream
= null;
155 bool revertAssert
= false;
158 // Runtime config: assert access to the file
159 // Designtime config: require caller to have all required permissions
161 // assertPermissions: if true, we'll assert permission. Used by ClientSettingsConfigurationHost.
163 if (assertPermissions
|| !_configRoot
.IsDesignTime
) {
164 new FileIOPermission(FileIOPermissionAccess
.Read
| FileIOPermissionAccess
.PathDiscovery
, streamName
).Assert();
169 stream
= StaticOpenStreamForRead(streamName
);
173 CodeAccessPermission
.RevertAssert();
180 const FileAttributes InvalidAttributesForWrite
= (FileAttributes
.ReadOnly
| FileAttributes
.Hidden
);
182 // This method doesn't really open the streamName for write. Instead, using WriteFileContext
183 // it opens a stream on a temporary file created in the same directory as streamName.
186 // assertPermissions - If true, then we'll assert all required permissions. Used by ClientSettingsConfigurationHost.
187 // to allow low-trust apps to use ClientSettingsStore.
188 static internal Stream
StaticOpenStreamForWrite(string streamName
, string templateStreamName
, ref object writeContext
, bool assertPermissions
) {
189 bool revertAssert
= false;
191 if (string.IsNullOrEmpty(streamName
)) {
192 throw new ConfigurationErrorsException(SR
.GetString(SR
.Config_no_stream_to_write
));
195 // Create directory if it does not exist.
196 // Ignore errors, allow any failure to come when trying to open the file.
197 string dir
= Path
.GetDirectoryName(streamName
);
199 if (!Directory
.Exists(dir
)) {
206 if (assertPermissions
) {
207 new FileIOPermission(PermissionState
.Unrestricted
).Assert();
211 Directory
.CreateDirectory(dir
);
218 CodeAccessPermission
.RevertAssert();
223 WriteFileContext writeFileContext
= null;
224 revertAssert
= false;
226 if (assertPermissions
) {
227 // If we're asked to assert permission, we will assert allAccess on the directory (instead of just the file).
228 // We need to assert for the whole directory because WriteFileContext will call TempFileCollection.AddExtension,
229 // which will generate a temporary file and make a AllAccess Demand on that file.
230 // Since we don't know the name of the temporary file right now, we need to assert for the whole dir.
231 new FileIOPermission(FileIOPermissionAccess
.AllAccess
, dir
).Assert();
236 writeFileContext
= new WriteFileContext(streamName
, templateStreamName
);
238 if (File
.Exists(streamName
)) {
239 FileInfo fi
= new FileInfo(streamName
);
240 FileAttributes attrs
= fi
.Attributes
;
241 if ((int)(attrs
& InvalidAttributesForWrite
) != 0) {
242 throw new IOException(SR
.GetString(SR
.Config_invalid_attributes_for_write
, streamName
));
247 stream
= new FileStream(writeFileContext
.TempNewFilename
, FileMode
.Create
, FileAccess
.Write
, FileShare
.Read
);
249 // Wrap all exceptions so that we provide a meaningful filename - otherwise the end user
250 // will just see the temporary file name, which is meaningless.
251 catch (Exception e
) {
252 throw new ConfigurationErrorsException(SR
.GetString(SR
.Config_write_failed
, streamName
), e
);
256 if (writeFileContext
!= null) {
257 writeFileContext
.Complete(streamName
, false);
263 CodeAccessPermission
.RevertAssert();
267 writeContext
= writeFileContext
;
272 Stream IInternalConfigHost
.OpenStreamForWrite(string streamName
, string templateStreamName
, ref object writeContext
) {
273 return ((IInternalConfigHost
)this).OpenStreamForWrite(streamName
, templateStreamName
, ref writeContext
, false);
277 Stream IInternalConfigHost
.OpenStreamForWrite(string streamName
, string templateStreamName
, ref object writeContext
, bool assertPermissions
) {
278 return StaticOpenStreamForWrite(streamName
, templateStreamName
, ref writeContext
, assertPermissions
);
282 // assertPermissions - If true, then we'll assert all required permissions. Used by ClientSettingsConfigurationHost.
283 // to allow low-trust apps to use ClientSettingsStore.
284 static internal void StaticWriteCompleted(string streamName
, bool success
, object writeContext
, bool assertPermissions
) {
285 WriteFileContext writeFileContext
= (WriteFileContext
) writeContext
;
286 bool revertAssert
= false;
288 if (assertPermissions
) {
289 // If asked to assert permissions, we will assert allAccess on the streamName, the temporary file
290 // created by WriteContext, and also the directory itself. The last one is needed because
291 // WriteFileContext will call TempFileCollection.Dispose, which will remove a .tmp file it created.
292 string dir
= Path
.GetDirectoryName(streamName
);
293 string[] filePaths
= new string[] {streamName, writeFileContext.TempNewFilename, dir}
;
294 FileIOPermission fileIOPerm
= new FileIOPermission(FileIOPermissionAccess
.AllAccess
, AccessControlActions
.View
| AccessControlActions
.Change
, filePaths
);
300 writeFileContext
.Complete(streamName
, success
);
304 CodeAccessPermission
.RevertAssert();
309 void IInternalConfigHost
.WriteCompleted(string streamName
, bool success
, object writeContext
) {
310 ((IInternalConfigHost
)this).WriteCompleted(streamName
, success
, writeContext
, false);
313 void IInternalConfigHost
.WriteCompleted(string streamName
, bool success
, object writeContext
, bool assertPermissions
) {
314 StaticWriteCompleted(streamName
, success
, writeContext
, assertPermissions
);
317 static internal void StaticDeleteStream(string streamName
) {
318 File
.Delete(streamName
);
321 void IInternalConfigHost
.DeleteStream(string streamName
) {
322 StaticDeleteStream(streamName
);
325 // ConfigurationErrorsException support
326 static internal bool StaticIsFile(string streamName
) {
327 // We want to avoid loading configuration before machine.config
328 // is instantiated. Referencing the Uri class will cause config
329 // to be loaded, so we use Path.IsPathRooted.
330 return Path
.IsPathRooted(streamName
);
333 bool IInternalConfigHost
.IsFile(string streamName
) {
334 return StaticIsFile(streamName
);
337 // change notification support - runtime only
338 bool IInternalConfigHost
.SupportsChangeNotifications
{
342 object IInternalConfigHost
.StartMonitoringStreamForChanges(string streamName
, StreamChangeCallback callback
) {
343 throw ExceptionUtil
.UnexpectedError("IInternalConfigHost.StartMonitoringStreamForChanges");
346 void IInternalConfigHost
.StopMonitoringStreamForChanges(string streamName
, StreamChangeCallback callback
) {
347 throw ExceptionUtil
.UnexpectedError("IInternalConfigHost.StopMonitoringStreamForChanges");
350 // RefreshConfig support - runtime only
351 bool IInternalConfigHost
.SupportsRefresh
{
356 bool IInternalConfigHost
.SupportsPath
{
360 bool IInternalConfigHost
.IsDefinitionAllowed(string configPath
, ConfigurationAllowDefinition allowDefinition
, ConfigurationAllowExeDefinition allowExeDefinition
) {
364 void IInternalConfigHost
.VerifyDefinitionAllowed(string configPath
, ConfigurationAllowDefinition allowDefinition
, ConfigurationAllowExeDefinition allowExeDefinition
, IConfigErrorInfo errorInfo
) {
367 // Do we support location tags?
368 bool IInternalConfigHost
.SupportsLocation
{
372 bool IInternalConfigHost
.IsAboveApplication(string configPath
) {
373 throw ExceptionUtil
.UnexpectedError("IInternalConfigHost.IsAboveApplication");
376 string IInternalConfigHost
.GetConfigPathFromLocationSubPath(string configPath
, string locationSubPath
) {
377 throw ExceptionUtil
.UnexpectedError("IInternalConfigHost.GetConfigPathFromLocationSubPath");
380 bool IInternalConfigHost
.IsLocationApplicable(string configPath
) {
381 throw ExceptionUtil
.UnexpectedError("IInternalConfigHost.IsLocationApplicable");
384 bool IInternalConfigHost
.IsTrustedConfigPath(string configPath
) {
385 throw ExceptionUtil
.UnexpectedError("IInternalConfigHost.IsTrustedConfigPath");
388 // Default implementation: ensure that the caller has full trust.
389 bool IInternalConfigHost
.IsFullTrustSectionWithoutAptcaAllowed(IInternalConfigRecord configRecord
) {
390 return TypeUtil
.IsCallerFullTrust
;
394 void IInternalConfigHost
.GetRestrictedPermissions(IInternalConfigRecord configRecord
, out PermissionSet permissionSet
, out bool isHostReady
) {
395 permissionSet
= null;
399 IDisposable IInternalConfigHost
.Impersonate() {
404 bool IInternalConfigHost
.PrefetchAll(string configPath
, string streamName
) {
408 bool IInternalConfigHost
.PrefetchSection(string sectionGroupName
, string sectionName
) {
413 object IInternalConfigHost
.CreateDeprecatedConfigContext(string configPath
) {
414 throw ExceptionUtil
.UnexpectedError("IInternalConfigHost.CreateDeprecatedConfigContext");
420 IInternalConfigHost
.CreateConfigurationContext( string configPath
,
421 string locationSubPath
)
423 throw ExceptionUtil
.UnexpectedError("IInternalConfigHost.CreateConfigurationContext");
426 // Encrypt/decrypt support
427 string IInternalConfigHost
.DecryptSection(string encryptedXml
, ProtectedConfigurationProvider protectionProvider
, ProtectedConfigurationSection protectedConfigSection
) {
428 return ProtectedConfigurationSection
.DecryptSection(encryptedXml
, protectionProvider
);
431 string IInternalConfigHost
.EncryptSection(string clearTextXml
, ProtectedConfigurationProvider protectionProvider
, ProtectedConfigurationSection protectedConfigSection
) {
432 return ProtectedConfigurationSection
.EncryptSection(clearTextXml
, protectionProvider
);
436 Type IInternalConfigHost
.GetConfigType(string typeName
, bool throwOnError
) {
437 return Type
.GetType(typeName
, throwOnError
);
440 string IInternalConfigHost
.GetConfigTypeName(Type t
) {
441 return t
.AssemblyQualifiedName
;
444 bool IInternalConfigHost
.IsRemote
{
450 XmlNode IInternalConfigurationBuilderHost
.ProcessRawXml(XmlNode rawXml
, ConfigurationBuilder builder
) {
451 if (builder
!= null) {
452 return builder
.ProcessRawXml(rawXml
);
458 ConfigurationSection IInternalConfigurationBuilderHost
.ProcessConfigurationSection(ConfigurationSection configSection
, ConfigurationBuilder builder
) {
459 if (builder
!= null) {
460 return builder
.ProcessConfigurationSection(configSection
);
463 return configSection
;