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;
23 // An IInternalConfigHost with common implementations of some file functions.
25 internal sealed class InternalConfigHost : IInternalConfigHost {
26 private IInternalConfigRoot _configRoot;
28 internal InternalConfigHost() {
31 void IInternalConfigHost.Init(IInternalConfigRoot configRoot, params object[] hostInitParams) {
32 _configRoot = configRoot;
35 void IInternalConfigHost.InitForConfiguration(ref string locationSubPath, out string configPath, out string locationConfigPath,
36 IInternalConfigRoot configRoot, params object[] hostInitConfigurationParams) {
38 _configRoot = configRoot;
40 locationConfigPath = null;
43 // config path support
44 bool IInternalConfigHost.IsConfigRecordRequired(string configPath) {
48 bool IInternalConfigHost.IsInitDelayed(IInternalConfigRecord configRecord) {
52 void IInternalConfigHost.RequireCompleteInit(IInternalConfigRecord configRecord) {
57 // In the default there are no secondary root's
59 public bool IsSecondaryRoot(string configPath) {
64 string IInternalConfigHost.GetStreamName(string configPath) {
65 throw ExceptionUtil.UnexpectedError("IInternalConfigHost.GetStreamName");
68 [FileIOPermission(SecurityAction.Assert, AllFiles = FileIOPermissionAccess.PathDiscovery)]
69 [SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Justification = "The callers don't leak this information.")]
70 static internal string StaticGetStreamNameForConfigSource(string streamName, string configSource) {
72 // Note (Microsoft 7/08/05):
73 // RemoteWebConfigurationHost also redirects GetStreamNameForConfigSource to this
74 // method, and that means streamName is referring to a path that's on the remote
75 // machine. The problem is that Path.GetFullPath will demand FileIOPermission on
76 // that file and *assume* the file is referring to one on the local machine.
77 // This can be a potential problem for RemoteWebConfigurationHost. However, since
78 // we Assert the PathDiscovery permission at this method, so this problem is handled
79 // and we're okay. But in the future if we modify this method to handle anything
80 // that assumes streamName is a local path, then RemoteWebConfigurationHost has to
81 // override GetStreamNameForConfigSource.
84 // don't allow relative paths for stream name
85 if (!Path.IsPathRooted(streamName)) {
86 throw ExceptionUtil.ParameterInvalid("streamName");
89 // get the path part of the original stream
90 streamName = Path.GetFullPath(streamName);
91 string dirStream = UrlPath.GetDirectoryOrRootName(streamName);
93 // combine with the new config source
94 string result = Path.Combine(dirStream, configSource);
95 result = Path.GetFullPath(result);
97 // ensure the result is in or under the directory of the original source
98 string dirResult = UrlPath.GetDirectoryOrRootName(result);
99 if (!UrlPath.IsEqualOrSubdirectory(dirStream, dirResult)) {
100 throw new ArgumentException(SR.GetString(SR.Config_source_not_under_config_dir, configSource));
106 string IInternalConfigHost.GetStreamNameForConfigSource(string streamName, string configSource) {
107 return StaticGetStreamNameForConfigSource(streamName, configSource);
110 static internal object StaticGetStreamVersion(string streamName) {
113 DateTime utcCreationTime = DateTime.MinValue;
114 DateTime utcLastWriteTime = DateTime.MinValue;
116 UnsafeNativeMethods.WIN32_FILE_ATTRIBUTE_DATA data;
117 if ( UnsafeNativeMethods.GetFileAttributesEx(streamName, UnsafeNativeMethods.GetFileExInfoStandard, out data) &&
118 (data.fileAttributes & (int) FileAttributes.Directory) == 0) {
120 fileSize = (long)(uint)data.fileSizeHigh << 32 | (long)(uint)data.fileSizeLow;
121 utcCreationTime = DateTime.FromFileTimeUtc(((long)data.ftCreationTimeHigh) << 32 | (long)data.ftCreationTimeLow);
122 utcLastWriteTime = DateTime.FromFileTimeUtc(((long)data.ftLastWriteTimeHigh) << 32 | (long)data.ftLastWriteTimeLow);
125 return new FileVersion(exists, fileSize, utcCreationTime, utcLastWriteTime);
128 object IInternalConfigHost.GetStreamVersion(string streamName) {
129 return StaticGetStreamVersion(streamName);
132 // default impl treats name as a file name
133 // null means stream doesn't exist for this name
134 static internal Stream StaticOpenStreamForRead(string streamName) {
135 if (string.IsNullOrEmpty(streamName)) {
136 throw ExceptionUtil.UnexpectedError("InternalConfigHost::StaticOpenStreamForRead");
139 if (!FileUtil.FileExists(streamName, true))
142 // consider: FileShare.Delete is not exposed by FileShare enumeration
143 return new FileStream(streamName, FileMode.Open, FileAccess.Read, FileShare.Read);
146 Stream IInternalConfigHost.OpenStreamForRead(string streamName) {
147 return ((IInternalConfigHost)this).OpenStreamForRead(streamName, false);
150 // Okay to suppress, since this is callable only through internal interfaces.
151 [SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity")]
152 Stream IInternalConfigHost.OpenStreamForRead(string streamName, bool assertPermissions) {
153 Stream stream = null;
154 bool revertAssert = false;
157 // Runtime config: assert access to the file
158 // Designtime config: require caller to have all required permissions
160 // assertPermissions: if true, we'll assert permission. Used by ClientSettingsConfigurationHost.
162 if (assertPermissions || !_configRoot.IsDesignTime) {
163 new FileIOPermission(FileIOPermissionAccess.Read | FileIOPermissionAccess.PathDiscovery, streamName).Assert();
168 stream = StaticOpenStreamForRead(streamName);
172 CodeAccessPermission.RevertAssert();
179 const FileAttributes InvalidAttributesForWrite = (FileAttributes.ReadOnly | FileAttributes.Hidden);
181 // This method doesn't really open the streamName for write. Instead, using WriteFileContext
182 // it opens a stream on a temporary file created in the same directory as streamName.
185 // assertPermissions - If true, then we'll assert all required permissions. Used by ClientSettingsConfigurationHost.
186 // to allow low-trust apps to use ClientSettingsStore.
187 static internal Stream StaticOpenStreamForWrite(string streamName, string templateStreamName, ref object writeContext, bool assertPermissions) {
188 bool revertAssert = false;
190 if (string.IsNullOrEmpty(streamName)) {
191 throw new ConfigurationErrorsException(SR.GetString(SR.Config_no_stream_to_write));
194 // Create directory if it does not exist.
195 // Ignore errors, allow any failure to come when trying to open the file.
196 string dir = Path.GetDirectoryName(streamName);
198 if (!Directory.Exists(dir)) {
205 if (assertPermissions) {
206 new FileIOPermission(PermissionState.Unrestricted).Assert();
210 Directory.CreateDirectory(dir);
217 CodeAccessPermission.RevertAssert();
222 WriteFileContext writeFileContext = null;
223 revertAssert = false;
225 if (assertPermissions) {
226 // If we're asked to assert permission, we will assert allAccess on the directory (instead of just the file).
227 // We need to assert for the whole directory because WriteFileContext will call TempFileCollection.AddExtension,
228 // which will generate a temporary file and make a AllAccess Demand on that file.
229 // Since we don't know the name of the temporary file right now, we need to assert for the whole dir.
230 new FileIOPermission(FileIOPermissionAccess.AllAccess, dir).Assert();
235 writeFileContext = new WriteFileContext(streamName, templateStreamName);
237 if (File.Exists(streamName)) {
238 FileInfo fi = new FileInfo(streamName);
239 FileAttributes attrs = fi.Attributes;
240 if ((int)(attrs & InvalidAttributesForWrite) != 0) {
241 throw new IOException(SR.GetString(SR.Config_invalid_attributes_for_write, streamName));
246 stream = new FileStream(writeFileContext.TempNewFilename, FileMode.Create, FileAccess.Write, FileShare.Read);
248 // Wrap all exceptions so that we provide a meaningful filename - otherwise the end user
249 // will just see the temporary file name, which is meaningless.
250 catch (Exception e) {
251 throw new ConfigurationErrorsException(SR.GetString(SR.Config_write_failed, streamName), e);
255 if (writeFileContext != null) {
256 writeFileContext.Complete(streamName, false);
262 CodeAccessPermission.RevertAssert();
266 writeContext = writeFileContext;
271 Stream IInternalConfigHost.OpenStreamForWrite(string streamName, string templateStreamName, ref object writeContext) {
272 return ((IInternalConfigHost)this).OpenStreamForWrite(streamName, templateStreamName, ref writeContext, false);
276 Stream IInternalConfigHost.OpenStreamForWrite(string streamName, string templateStreamName, ref object writeContext, bool assertPermissions) {
277 return StaticOpenStreamForWrite(streamName, templateStreamName, ref writeContext, assertPermissions);
281 // assertPermissions - If true, then we'll assert all required permissions. Used by ClientSettingsConfigurationHost.
282 // to allow low-trust apps to use ClientSettingsStore.
283 static internal void StaticWriteCompleted(string streamName, bool success, object writeContext, bool assertPermissions) {
284 WriteFileContext writeFileContext = (WriteFileContext) writeContext;
285 bool revertAssert = false;
287 if (assertPermissions) {
288 // If asked to assert permissions, we will assert allAccess on the streamName, the temporary file
289 // created by WriteContext, and also the directory itself. The last one is needed because
290 // WriteFileContext will call TempFileCollection.Dispose, which will remove a .tmp file it created.
291 string dir = Path.GetDirectoryName(streamName);
292 string[] filePaths = new string[] {streamName, writeFileContext.TempNewFilename, dir};
293 FileIOPermission fileIOPerm = new FileIOPermission(FileIOPermissionAccess.AllAccess, AccessControlActions.View | AccessControlActions.Change, filePaths);
299 writeFileContext.Complete(streamName, success);
303 CodeAccessPermission.RevertAssert();
308 void IInternalConfigHost.WriteCompleted(string streamName, bool success, object writeContext) {
309 ((IInternalConfigHost)this).WriteCompleted(streamName, success, writeContext, false);
312 void IInternalConfigHost.WriteCompleted(string streamName, bool success, object writeContext, bool assertPermissions) {
313 StaticWriteCompleted(streamName, success, writeContext, assertPermissions);
316 static internal void StaticDeleteStream(string streamName) {
317 File.Delete(streamName);
320 void IInternalConfigHost.DeleteStream(string streamName) {
321 StaticDeleteStream(streamName);
324 // ConfigurationErrorsException support
325 static internal bool StaticIsFile(string streamName) {
326 // We want to avoid loading configuration before machine.config
327 // is instantiated. Referencing the Uri class will cause config
328 // to be loaded, so we use Path.IsPathRooted.
329 return Path.IsPathRooted(streamName);
332 bool IInternalConfigHost.IsFile(string streamName) {
333 return StaticIsFile(streamName);
336 // change notification support - runtime only
337 bool IInternalConfigHost.SupportsChangeNotifications {
341 object IInternalConfigHost.StartMonitoringStreamForChanges(string streamName, StreamChangeCallback callback) {
342 throw ExceptionUtil.UnexpectedError("IInternalConfigHost.StartMonitoringStreamForChanges");
345 void IInternalConfigHost.StopMonitoringStreamForChanges(string streamName, StreamChangeCallback callback) {
346 throw ExceptionUtil.UnexpectedError("IInternalConfigHost.StopMonitoringStreamForChanges");
349 // RefreshConfig support - runtime only
350 bool IInternalConfigHost.SupportsRefresh {
355 bool IInternalConfigHost.SupportsPath {
359 bool IInternalConfigHost.IsDefinitionAllowed(string configPath, ConfigurationAllowDefinition allowDefinition, ConfigurationAllowExeDefinition allowExeDefinition) {
363 void IInternalConfigHost.VerifyDefinitionAllowed(string configPath, ConfigurationAllowDefinition allowDefinition, ConfigurationAllowExeDefinition allowExeDefinition, IConfigErrorInfo errorInfo) {
366 // Do we support location tags?
367 bool IInternalConfigHost.SupportsLocation {
371 bool IInternalConfigHost.IsAboveApplication(string configPath) {
372 throw ExceptionUtil.UnexpectedError("IInternalConfigHost.IsAboveApplication");
375 string IInternalConfigHost.GetConfigPathFromLocationSubPath(string configPath, string locationSubPath) {
376 throw ExceptionUtil.UnexpectedError("IInternalConfigHost.GetConfigPathFromLocationSubPath");
379 bool IInternalConfigHost.IsLocationApplicable(string configPath) {
380 throw ExceptionUtil.UnexpectedError("IInternalConfigHost.IsLocationApplicable");
383 bool IInternalConfigHost.IsTrustedConfigPath(string configPath) {
384 throw ExceptionUtil.UnexpectedError("IInternalConfigHost.IsTrustedConfigPath");
387 // Default implementation: ensure that the caller has full trust.
388 bool IInternalConfigHost.IsFullTrustSectionWithoutAptcaAllowed(IInternalConfigRecord configRecord) {
389 return TypeUtil.IsCallerFullTrust;
393 void IInternalConfigHost.GetRestrictedPermissions(IInternalConfigRecord configRecord, out PermissionSet permissionSet, out bool isHostReady) {
394 permissionSet = null;
398 IDisposable IInternalConfigHost.Impersonate() {
403 bool IInternalConfigHost.PrefetchAll(string configPath, string streamName) {
407 bool IInternalConfigHost.PrefetchSection(string sectionGroupName, string sectionName) {
412 object IInternalConfigHost.CreateDeprecatedConfigContext(string configPath) {
413 throw ExceptionUtil.UnexpectedError("IInternalConfigHost.CreateDeprecatedConfigContext");
419 IInternalConfigHost.CreateConfigurationContext( string configPath,
420 string locationSubPath )
422 throw ExceptionUtil.UnexpectedError("IInternalConfigHost.CreateConfigurationContext");
425 // Encrypt/decrypt support
426 string IInternalConfigHost.DecryptSection(string encryptedXml, ProtectedConfigurationProvider protectionProvider, ProtectedConfigurationSection protectedConfigSection) {
427 return ProtectedConfigurationSection.DecryptSection(encryptedXml, protectionProvider);
430 string IInternalConfigHost.EncryptSection(string clearTextXml, ProtectedConfigurationProvider protectionProvider, ProtectedConfigurationSection protectedConfigSection) {
431 return ProtectedConfigurationSection.EncryptSection(clearTextXml, protectionProvider);
435 Type IInternalConfigHost.GetConfigType(string typeName, bool throwOnError) {
436 return Type.GetType(typeName, throwOnError);
439 string IInternalConfigHost.GetConfigTypeName(Type t) {
440 return t.AssemblyQualifiedName;
443 bool IInternalConfigHost.IsRemote {