Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Configuration / System / Configuration / Internal / InternalConfigHost.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="InternalConfigHost.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6
7 namespace System.Configuration.Internal {
8     using Microsoft.Win32;      
9     using System.Diagnostics.CodeAnalysis;
10     using System.CodeDom.Compiler;
11     using System.Configuration;
12     using System.IO;
13     using System.Reflection;
14     using System.Security;
15 #if !FEATURE_PAL
16     using System.Security.AccessControl;
17 #endif
18     using System.Security.Permissions;
19     using System.Security.Policy;
20     using System.Threading;
21
22     //
23     // An IInternalConfigHost with common implementations of some file functions.
24     //
25     internal sealed class InternalConfigHost : IInternalConfigHost {
26         private IInternalConfigRoot _configRoot;
27
28         internal InternalConfigHost() {
29         }
30
31         void IInternalConfigHost.Init(IInternalConfigRoot configRoot, params object[] hostInitParams) {
32             _configRoot = configRoot;
33         }
34
35         void IInternalConfigHost.InitForConfiguration(ref string locationSubPath, out string configPath, out string locationConfigPath, 
36                 IInternalConfigRoot configRoot, params object[] hostInitConfigurationParams) {
37
38             _configRoot = configRoot;
39             configPath = null;
40             locationConfigPath = null;
41         }
42
43         // config path support
44         bool IInternalConfigHost.IsConfigRecordRequired(string configPath) {
45             return true;
46         }
47
48         bool IInternalConfigHost.IsInitDelayed(IInternalConfigRecord configRecord) {
49             return false;
50         }
51
52         void IInternalConfigHost.RequireCompleteInit(IInternalConfigRecord configRecord) {
53         }
54
55         // IsSecondaryRoot
56         //
57         // In the default there are no secondary root's
58         //
59         public bool IsSecondaryRoot(string configPath) {
60             return false;
61         }
62
63         // stream support
64         string IInternalConfigHost.GetStreamName(string configPath) {
65             throw ExceptionUtil.UnexpectedError("IInternalConfigHost.GetStreamName");
66         }
67
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) {
71             //
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.
82             //
83             
84             // don't allow relative paths for stream name
85             if (!Path.IsPathRooted(streamName)) {
86                 throw ExceptionUtil.ParameterInvalid("streamName");
87             }
88
89             // get the path part of the original stream
90             streamName = Path.GetFullPath(streamName);
91             string dirStream = UrlPath.GetDirectoryOrRootName(streamName);
92
93             // combine with the new config source
94             string result = Path.Combine(dirStream, configSource);
95             result = Path.GetFullPath(result);
96
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));
101             }
102
103             return result;
104         }
105
106         string IInternalConfigHost.GetStreamNameForConfigSource(string streamName, string configSource) {
107             return StaticGetStreamNameForConfigSource(streamName, configSource);
108         }
109
110         static internal object StaticGetStreamVersion(string streamName) {
111             bool exists = false;
112             long fileSize = 0;
113             DateTime utcCreationTime = DateTime.MinValue;
114             DateTime utcLastWriteTime = DateTime.MinValue;
115
116             UnsafeNativeMethods.WIN32_FILE_ATTRIBUTE_DATA data;
117             if (    UnsafeNativeMethods.GetFileAttributesEx(streamName, UnsafeNativeMethods.GetFileExInfoStandard, out data) &&
118                     (data.fileAttributes & (int) FileAttributes.Directory) == 0) {
119                 exists = true;
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);
123             }
124
125             return new FileVersion(exists, fileSize, utcCreationTime, utcLastWriteTime);
126         }
127
128         object IInternalConfigHost.GetStreamVersion(string streamName) {
129             return StaticGetStreamVersion(streamName);
130         }
131
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");
137             }
138
139             if (!FileUtil.FileExists(streamName, true))
140                 return null;
141
142             // consider: FileShare.Delete is not exposed by FileShare enumeration
143             return new FileStream(streamName, FileMode.Open, FileAccess.Read, FileShare.Read);
144         }
145
146         Stream IInternalConfigHost.OpenStreamForRead(string streamName) {
147             return ((IInternalConfigHost)this).OpenStreamForRead(streamName, false);
148         }
149         
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;
155
156             //
157             // Runtime config: assert access to the file
158             // Designtime config: require caller to have all required permissions
159             //
160             // assertPermissions: if true, we'll assert permission.  Used by ClientSettingsConfigurationHost.
161             //
162             if (assertPermissions || !_configRoot.IsDesignTime) {
163                 new FileIOPermission(FileIOPermissionAccess.Read | FileIOPermissionAccess.PathDiscovery, streamName).Assert();
164                 revertAssert = true;
165             }
166
167             try {
168                 stream = StaticOpenStreamForRead(streamName);
169             }
170             finally {
171                 if (revertAssert) {
172                     CodeAccessPermission.RevertAssert();
173                 }
174             }
175
176             return stream;
177         }
178
179         const FileAttributes InvalidAttributesForWrite = (FileAttributes.ReadOnly | FileAttributes.Hidden);
180
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.
183         //
184         // Parameters:
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;
189             
190             if (string.IsNullOrEmpty(streamName)) {
191                 throw new ConfigurationErrorsException(SR.GetString(SR.Config_no_stream_to_write));
192             }
193
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);
197             try {
198                 if (!Directory.Exists(dir)) {
199                     // 
200
201
202
203
204
205                     if (assertPermissions) {
206                         new FileIOPermission(PermissionState.Unrestricted).Assert();
207                         revertAssert = true;
208                     }
209                     
210                     Directory.CreateDirectory(dir);
211                 }
212             }
213             catch {
214             }
215             finally {
216                 if (revertAssert) {
217                     CodeAccessPermission.RevertAssert();
218                 }
219             }
220
221             Stream stream;
222             WriteFileContext writeFileContext = null;
223             revertAssert = false;
224
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();
231                 revertAssert = true;
232             }
233
234             try {
235                 writeFileContext = new WriteFileContext(streamName, templateStreamName);
236
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));
242                     }
243                 }
244
245                 try {
246                     stream = new FileStream(writeFileContext.TempNewFilename, FileMode.Create, FileAccess.Write, FileShare.Read);
247                 }
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);
252                 }
253             }
254             catch {
255                 if (writeFileContext != null) {
256                     writeFileContext.Complete(streamName, false);
257                 }
258                 throw;
259             }
260             finally {
261                 if (revertAssert) {
262                     CodeAccessPermission.RevertAssert();
263                 }
264             }
265
266             writeContext = writeFileContext;
267             return stream;
268         }
269
270         
271         Stream IInternalConfigHost.OpenStreamForWrite(string streamName, string templateStreamName, ref object writeContext) {
272             return ((IInternalConfigHost)this).OpenStreamForWrite(streamName, templateStreamName, ref writeContext, false);
273         }
274
275         
276         Stream IInternalConfigHost.OpenStreamForWrite(string streamName, string templateStreamName, ref object writeContext, bool assertPermissions) {
277             return StaticOpenStreamForWrite(streamName, templateStreamName, ref writeContext, assertPermissions);
278         }
279
280         // Parameters:
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;
286
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);
294                 fileIOPerm.Assert();
295                 revertAssert = true;
296             }
297
298             try {
299                 writeFileContext.Complete(streamName, success);
300             }
301             finally {
302                 if (revertAssert) {
303                     CodeAccessPermission.RevertAssert();
304                 }
305             }
306         }
307
308         void IInternalConfigHost.WriteCompleted(string streamName, bool success, object writeContext) {
309             ((IInternalConfigHost)this).WriteCompleted(streamName, success, writeContext, false);
310         }
311
312         void IInternalConfigHost.WriteCompleted(string streamName, bool success, object writeContext, bool assertPermissions) {
313             StaticWriteCompleted(streamName, success, writeContext, assertPermissions);
314         }
315
316         static internal void StaticDeleteStream(string streamName) {
317             File.Delete(streamName);
318         }
319
320         void IInternalConfigHost.DeleteStream(string streamName) {
321             StaticDeleteStream(streamName);
322         }
323
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);
330         }
331
332         bool IInternalConfigHost.IsFile(string streamName) {
333             return StaticIsFile(streamName);
334         }
335
336         // change notification support - runtime only
337         bool IInternalConfigHost.SupportsChangeNotifications {
338             get {return false;}
339         }
340
341         object IInternalConfigHost.StartMonitoringStreamForChanges(string streamName, StreamChangeCallback callback) {
342             throw ExceptionUtil.UnexpectedError("IInternalConfigHost.StartMonitoringStreamForChanges");
343         }
344
345         void IInternalConfigHost.StopMonitoringStreamForChanges(string streamName, StreamChangeCallback callback) {
346             throw ExceptionUtil.UnexpectedError("IInternalConfigHost.StopMonitoringStreamForChanges");
347         }
348
349         // RefreshConfig support - runtime only
350         bool IInternalConfigHost.SupportsRefresh {
351             get {return false;}
352         }
353
354         // path support
355         bool IInternalConfigHost.SupportsPath {
356             get {return false;}
357         }
358
359         bool IInternalConfigHost.IsDefinitionAllowed(string configPath, ConfigurationAllowDefinition allowDefinition, ConfigurationAllowExeDefinition allowExeDefinition) {
360             return true;
361         }
362
363         void IInternalConfigHost.VerifyDefinitionAllowed(string configPath, ConfigurationAllowDefinition allowDefinition, ConfigurationAllowExeDefinition allowExeDefinition, IConfigErrorInfo errorInfo) {
364         }
365
366         // Do we support location tags?
367         bool IInternalConfigHost.SupportsLocation {
368             get {return false;}
369         }
370
371         bool IInternalConfigHost.IsAboveApplication(string configPath) {
372             throw ExceptionUtil.UnexpectedError("IInternalConfigHost.IsAboveApplication");
373         }
374
375         string IInternalConfigHost.GetConfigPathFromLocationSubPath(string configPath, string locationSubPath) {
376             throw ExceptionUtil.UnexpectedError("IInternalConfigHost.GetConfigPathFromLocationSubPath");
377         }
378
379         bool IInternalConfigHost.IsLocationApplicable(string configPath) {
380             throw ExceptionUtil.UnexpectedError("IInternalConfigHost.IsLocationApplicable");
381         }
382
383         bool IInternalConfigHost.IsTrustedConfigPath(string configPath) {
384             throw ExceptionUtil.UnexpectedError("IInternalConfigHost.IsTrustedConfigPath");
385         }
386
387         // Default implementation: ensure that the caller has full trust.
388         bool IInternalConfigHost.IsFullTrustSectionWithoutAptcaAllowed(IInternalConfigRecord configRecord) {
389             return TypeUtil.IsCallerFullTrust;
390         }
391
392         // security support
393         void IInternalConfigHost.GetRestrictedPermissions(IInternalConfigRecord configRecord, out PermissionSet permissionSet, out bool isHostReady) {
394             permissionSet = null;
395             isHostReady = true;
396         }
397
398         IDisposable IInternalConfigHost.Impersonate() {
399             return null;
400         }
401
402         // prefetch support
403         bool IInternalConfigHost.PrefetchAll(string configPath, string streamName) {
404             return false;
405         }
406
407         bool IInternalConfigHost.PrefetchSection(string sectionGroupName, string sectionName) {
408             return false;
409         }
410
411         // context support
412         object IInternalConfigHost.CreateDeprecatedConfigContext(string configPath) {
413             throw ExceptionUtil.UnexpectedError("IInternalConfigHost.CreateDeprecatedConfigContext");
414         }
415
416         // New Context
417         //
418         object 
419         IInternalConfigHost.CreateConfigurationContext( string configPath,
420                                                         string locationSubPath )
421         {
422             throw ExceptionUtil.UnexpectedError("IInternalConfigHost.CreateConfigurationContext");
423         }
424
425         // Encrypt/decrypt support
426         string IInternalConfigHost.DecryptSection(string encryptedXml, ProtectedConfigurationProvider protectionProvider, ProtectedConfigurationSection protectedConfigSection) {
427             return ProtectedConfigurationSection.DecryptSection(encryptedXml, protectionProvider);
428         }
429
430         string IInternalConfigHost.EncryptSection(string clearTextXml, ProtectedConfigurationProvider protectionProvider, ProtectedConfigurationSection protectedConfigSection) {
431             return ProtectedConfigurationSection.EncryptSection(clearTextXml, protectionProvider);
432         }
433
434         // Type name support
435         Type IInternalConfigHost.GetConfigType(string typeName, bool throwOnError) {
436             return Type.GetType(typeName, throwOnError);
437         }
438
439         string IInternalConfigHost.GetConfigTypeName(Type t) {
440             return t.AssemblyQualifiedName;
441         }
442
443         bool IInternalConfigHost.IsRemote {
444             get {
445                 return false;
446             }
447         }
448
449     }
450 }
451
452