Merge pull request #2964 from ludovic-henry/sgen-monocontext
[mono.git] / mcs / class / referencesource / System.Web / Util / AppSettings.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="AppSettings.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6
7 // AppSettings.cs
8 //
9
10 using System;
11 using System.Collections.Specialized;
12 using System.Configuration;
13 using System.Diagnostics.CodeAnalysis;
14 using System.Security.Permissions;
15 using System.Web.Configuration;
16 using System.Web.Hosting;
17
18 namespace System.Web.Util {
19
20     internal static class AppSettings
21     {
22         private static volatile bool _settingsInitialized = false;
23         private static object _appSettingsLock = new object();
24         private static void EnsureSettingsLoaded() {
25
26             if (!_settingsInitialized) {
27                 lock (_appSettingsLock) {
28                     if (!_settingsInitialized) {
29
30                         NameValueCollection settings = null;
31
32                         try {
33                             settings = GetAppSettingsSection();
34                         } finally {
35             
36                             // GetApplicationPathData may throw.  That's fine.  Let the user see the exception
37                             // once, but just fall back on default settings for the future.
38                             if (settings == null || !Boolean.TryParse(settings["aspnet:UseHostHeaderForRequestUrl"], out _useHostHeaderForRequestUrl))
39                                 _useHostHeaderForRequestUrl = false;
40
41                             if (settings == null || !Boolean.TryParse(settings["aspnet:AllowAnonymousImpersonation"], out _allowAnonymousImpersonation))
42                                 _allowAnonymousImpersonation = false;
43
44                             if (settings == null || !Boolean.TryParse(settings["aspnet:ScriptResourceAllowNonJsFiles"], out _scriptResourceAllowNonJsFiles))
45                                 _scriptResourceAllowNonJsFiles = false;
46
47                             if (settings == null || !Boolean.TryParse(settings["aspnet:UseLegacyEncryption"], out _useLegacyEncryption))
48                                 _useLegacyEncryption = false;
49
50                             if (settings == null || !Boolean.TryParse(settings["aspnet:UseLegacyMachineKeyEncryption"], out _useLegacyMachineKeyEncryption))
51                                 _useLegacyMachineKeyEncryption = false;
52
53                             if (settings == null || !Boolean.TryParse(settings["aspnet:UseLegacyFormsAuthenticationTicketCompatibility"], out _useLegacyFormsAuthenticationTicketCompatibility))
54                                 _useLegacyFormsAuthenticationTicketCompatibility = false;
55
56                             if (settings == null || !Boolean.TryParse(settings["aspnet:UseLegacyEventValidationCompatibility"], out _useLegacyEventValidationCompatibility))
57                                 _useLegacyEventValidationCompatibility = false;
58
59                             _allowInsecureDeserialization = GetNullableBooleanValue(settings, "aspnet:AllowInsecureDeserialization");
60
61                             if (settings == null || !Boolean.TryParse(settings["aspnet:AlwaysIgnoreViewStateValidationErrors"], out _alwaysIgnoreViewStateValidationErrors))
62                                 _alwaysIgnoreViewStateValidationErrors = false;
63
64                             if (settings == null || !Boolean.TryParse(settings["aspnet:AllowRelaxedHttpUserName"], out _allowRelaxedHttpUserName))
65                                 _allowRelaxedHttpUserName = false;
66
67                             if (settings == null || !Boolean.TryParse(settings["aspnet:JavaScriptDoNotEncodeAmpersand"], out _javaScriptDoNotEncodeAmpersand))
68                                 _javaScriptDoNotEncodeAmpersand = false;
69
70                             if (settings == null || !Boolean.TryParse(settings["aspnet:UseTaskFriendlySynchronizationContext"], out _useTaskFriendlySynchronizationContext))
71                                 _useTaskFriendlySynchronizationContext = (BinaryCompatibility.Current.TargetsAtLeastFramework45) ? true : false;
72
73                             if (settings == null || !Boolean.TryParse(settings["aspnet:AllowAsyncDuringSyncStages"], out _allowAsyncDuringSyncStages))
74                                 _allowAsyncDuringSyncStages = false;
75
76                             if (settings == null || !int.TryParse(settings["aspnet:MaxHttpCollectionKeys"], out _maxHttpCollectionKeys) || _maxHttpCollectionKeys < 0)
77                                 _maxHttpCollectionKeys = DefaultMaxHttpCollectionKeys;
78
79                             if (settings == null || !int.TryParse(settings["aspnet:MaxJsonDeserializerMembers"], out _maxJsonDeserializerMembers) || _maxJsonDeserializerMembers < 0)
80                                 _maxJsonDeserializerMembers = DefaultMaxJsonDeserializerMembers;
81
82                             if (settings == null || !Boolean.TryParse(settings["aspnet:DoNotDisposeSpecialHttpApplicationInstances"], out _doNotDisposeSpecialHttpApplicationInstances))
83                                 _doNotDisposeSpecialHttpApplicationInstances = false;
84
85                             if (settings != null)
86                                 _formsAuthReturnUrlVar = settings["aspnet:FormsAuthReturnUrlVar"];
87             
88                             if (settings == null || !Boolean.TryParse(settings["aspnet:RestrictXmlControls"], out _restrictXmlControls))
89                                 _restrictXmlControls = false;
90
91                             if (settings == null || !Boolean.TryParse(settings["aspnet:AllowRelaxedRelativeUrl"], out _allowRelaxedRelativeUrl))
92                                 _allowRelaxedRelativeUrl = false;
93
94                             if (settings == null || !Boolean.TryParse(settings["aspnet:UseLegacyRequestUrlGeneration"], out _useLegacyRequestUrlGeneration))
95                                 _useLegacyRequestUrlGeneration = false;
96
97                             if (settings == null || !Boolean.TryParse(settings["aspnet:AllowUtf7RequestContentEncoding"], out _allowUtf7RequestContentEncoding))
98                                 _allowUtf7RequestContentEncoding = false;
99
100                             if (settings == null || !Boolean.TryParse(settings["aspnet:AllowRelaxedUnicodeDecoding"], out _allowRelaxedUnicodeDecoding))
101                                 _allowRelaxedUnicodeDecoding = false;
102
103                             if (settings == null || !Boolean.TryParse(settings["aspnet:DontUsePercentUUrlEncoding"], out _dontUsePercentUUrlEncoding))
104                                 _dontUsePercentUUrlEncoding = BinaryCompatibility.Current.TargetsAtLeastFramework452; // default value is keyed off of <httpRuntime targetFramework="4.5.2" />
105
106                             if (settings == null || !int.TryParse(settings["aspnet:UpdatePanelMaxScriptLength"], out _updatePanelMaxScriptLength) || _updatePanelMaxScriptLength < 0)
107                                 _updatePanelMaxScriptLength = 0;
108                             
109                             // AppSettings override allows users to build against 4.5 but run against 4.0 or 4.5
110                             _maxConcurrentCompilations = GetNullableIntegerValue(settings, "aspnet:MaxConcurrentCompilations");
111
112                             if (settings == null || !int.TryParse(settings["aspnet:MaxAcceptLanguageFallbackCount"], out _maxAcceptLanguageFallbackCount) || _maxAcceptLanguageFallbackCount <= 0)
113                                 _maxAcceptLanguageFallbackCount = DefaultMaxAcceptLanguageFallbackCount;
114
115                             if (settings == null || !Boolean.TryParse(settings["aspnet:PortableCompilationOutput"], out _portableCompilationOutput))
116                                 _portableCompilationOutput = false;
117
118                             if (settings == null || string.IsNullOrWhiteSpace(_portableCompilationOutputSnapshotType = settings["aspnet:PortableCompilationOutputSnapshotType"]))
119                                 _portableCompilationOutputSnapshotType = null;
120
121                             if (settings == null || string.IsNullOrWhiteSpace(_portableCompilationOutputSnapshotTypeOptions = settings["aspnet:PortableCompilationOutputSnapshotTypeOptions"]))
122                                 _portableCompilationOutputSnapshotTypeOptions = null;
123
124                             if (settings == null || !Boolean.TryParse(settings["aspnet:EnsureSessionStateLockedOnFlush"], out _ensureSessionStateLockedOnFlush))
125                                 _ensureSessionStateLockedOnFlush = false;
126
127                             if (settings == null || !Boolean.TryParse(settings["aspnet:UseRandomizedStringHashAlgorithm"], out _useRandomizedStringHashAlgorithm))
128                                 _useRandomizedStringHashAlgorithm = false;
129
130                             if (settings == null || !Boolean.TryParse(settings["aspnet:EnableAsyncModelBinding"], out _enableAsyncModelBinding))
131                                 _enableAsyncModelBinding = BinaryCompatibility.Current.TargetsAtLeastFramework46;
132
133                             _settingsInitialized = true;
134                         }
135                     }
136                 }
137             }
138         }
139
140         [ConfigurationPermission(SecurityAction.Assert, Unrestricted = true)]
141         [SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Justification = "This assert is safe since we control the callers; they're not leaking information.")]
142         private static NameValueCollection GetAppSettingsSection() {
143             // DevDiv #353926 - Fall back to reading App.config if System.Web types are being consumed outside an ASP.NET application
144             if (!HostingEnvironment.IsHosted) {
145                 return ConfigurationManager.AppSettings;
146             }
147
148             // Check the app-level config. Ignore configuration errors
149             CachedPathData appPathData = CachedPathData.GetApplicationPathData();
150
151             if (appPathData != null && appPathData.ConfigRecord != null)
152                 return appPathData.ConfigRecord.GetSection("appSettings") as NameValueCollection;
153
154             // nothing found
155             return null;
156         }
157
158         // helper function to read a tri-state boolean from config
159         private static bool? GetNullableBooleanValue(NameValueCollection settings, string key) {
160             bool value;
161             return (settings != null && Boolean.TryParse(settings[key], out value)) ? value : (bool?)null;
162         }
163
164         // helper function to read a nullable int from config
165         private static int? GetNullableIntegerValue(NameValueCollection settings, string key) {
166             int value;
167             return (settings != null && int.TryParse(settings[key], out value)) ? value : (int?)null;
168         }
169
170         private static  bool _useHostHeaderForRequestUrl;
171         internal static bool UseHostHeaderForRequestUrl {
172             get {
173                 EnsureSettingsLoaded();
174                 return _useHostHeaderForRequestUrl;
175             }
176         }
177
178         private static  bool _allowAnonymousImpersonation;
179         internal static bool AllowAnonymousImpersonation {
180             get {
181                 EnsureSettingsLoaded();
182                 return _allowAnonymousImpersonation;
183             }
184         }
185
186         // false [default] to allow ScriptResource.axd to serve static files only if they are *.js; 
187         // true to allow ScriptResource.axd to serve any static file (bad practice)
188         private static  bool _scriptResourceAllowNonJsFiles;
189         internal static bool ScriptResourceAllowNonJsFiles {
190             get {
191                 EnsureSettingsLoaded();
192                 return _scriptResourceAllowNonJsFiles;
193             }
194         }
195
196         // false [default] to use encrypt+sign for all except Membership and MachineKey public API
197         // true to use encrypt only (dangerous!)
198         private static  bool _useLegacyEncryption;
199         internal static bool UseLegacyEncryption {
200             get {
201                 EnsureSettingsLoaded();
202                 return _useLegacyEncryption;
203             }
204         }
205
206         // false [default] to use encrypt+sign for MachineKey public API
207         // true to use encrypt only for MachineKey public API (dangerous!)
208         private static  bool _useLegacyMachineKeyEncryption;
209         internal static bool UseLegacyMachineKeyEncryption {
210             get {
211                 EnsureSettingsLoaded();
212                 return _useLegacyMachineKeyEncryption;
213             }
214         }
215
216         // false [default] to use secure semantics when generating FormsAuthentication tickets
217         // true to allow ticketCompatibilityMode="Framework20" (dangerous!)
218         private static bool _useLegacyFormsAuthenticationTicketCompatibility;
219         internal static bool UseLegacyFormsAuthenticationTicketCompatibility {
220             get {
221                 EnsureSettingsLoaded();
222                 return _useLegacyFormsAuthenticationTicketCompatibility;
223             }
224         }
225
226         // false [default] to use a cryptographic hash algorithm when generating the __EVENTVALIDATION field
227         // true to use String.GetHashCode() instead (dangerous!)
228         // more info: DevDiv #233564 (http://vstfdevdiv:8080/DevDiv2/web/wi.aspx?id=233564)
229         private static bool _useLegacyEventValidationCompatibility;
230         internal static bool UseLegacyEventValidationCompatibility {
231             get {
232                 EnsureSettingsLoaded();
233                 return _useLegacyEventValidationCompatibility;
234             }
235         }
236
237         // false to always enforce EnableViewStateMac=false
238         // true to allow the developer to specify EnableViewStateMac=false (dangerous, leads to RCE!)
239         // null [default] to make the decision based on a registry key
240         // more info: DevDiv #461378 (http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/461378)
241         private static bool? _allowInsecureDeserialization;
242         internal static bool? AllowInsecureDeserialization {
243             get {
244                 EnsureSettingsLoaded();
245                 return _allowInsecureDeserialization;
246             }
247         }
248
249         // false [default] to use the default heuristic for determining when to suppress __VIEWSTATE MAC validation errors and when to display a YSOD
250         // true to always ---- errors and never show a YSOD
251         private static bool _alwaysIgnoreViewStateValidationErrors;
252         internal static bool AlwaysIgnoreViewStateValidationErrors {
253             get {
254                 EnsureSettingsLoaded();
255                 return _alwaysIgnoreViewStateValidationErrors;
256             }
257         }
258
259         // false [default] to restrict dangerous characters in any user name passed to native code
260         // true to allow dangerous characters
261         private static bool _allowRelaxedHttpUserName;
262         internal static bool AllowRelaxedHttpUserName {
263             get {
264                 EnsureSettingsLoaded();
265                 return _allowRelaxedHttpUserName;
266             }
267         }
268
269         // false [default] to encode '&' characters in HttpUtility.JavaScriptStringEncode()
270         // true to not encode '&' characters (bad practice)
271         private static bool _javaScriptDoNotEncodeAmpersand;
272         internal static bool JavaScriptDoNotEncodeAmpersand {
273             get {
274                 EnsureSettingsLoaded();
275                 return _javaScriptDoNotEncodeAmpersand;
276             }
277         }
278
279         // false [default] to use the updated AspNetSynchronizationContext type (needed for TAP methods)
280         // true to use LegacyAspNetSynchronizationContext (needed for some back-compat scenarios)
281         private static bool _useTaskFriendlySynchronizationContext ;
282         internal static bool UseTaskFriendlySynchronizationContext {
283             get {
284                 EnsureSettingsLoaded();
285                 return _useTaskFriendlySynchronizationContext ;
286             }
287         }
288
289         // false [default] if the updated AspNetSynchronizationContext type should throw when it encounters invalid operations
290         // true to allow some invalid operations through the system (needed for some back-compat scenarios)
291         // more info: see doc on HttpContext.AllowAsyncDuringSyncStages
292         private static bool _allowAsyncDuringSyncStages;
293         internal static bool AllowAsyncDuringSyncStages {
294             get {
295                 EnsureSettingsLoaded();
296                 return _allowAsyncDuringSyncStages;
297             }
298         }
299
300          // maximum number of keys a HttpValueCollection, HttpFileCollection or HttpCookieCollection is allowed to have
301          private const int DefaultMaxHttpCollectionKeys = Int32.MaxValue;
302          private static int _maxHttpCollectionKeys = DefaultMaxHttpCollectionKeys;
303          internal static int MaxHttpCollectionKeys {
304              get {
305                  EnsureSettingsLoaded();
306                  return _maxHttpCollectionKeys;
307              }
308          }
309
310          // maximum number of entries a Json deserialized dictionary is allowed to have
311          private const int DefaultMaxJsonDeserializerMembers = Int32.MaxValue;
312          private static int _maxJsonDeserializerMembers = DefaultMaxJsonDeserializerMembers;
313          internal static int MaxJsonDeserializerMembers {
314              get {
315                  EnsureSettingsLoaded();
316                  return _maxJsonDeserializerMembers;
317              }
318          }
319
320         // false [default] to call HttpApplication.Dispose() on all HttpApplication instances instantiated by ASP.NET
321         // true to suppress calling HttpApplication.Dispose() on "special" (i.e., associated with App_Start, App_End, etc.) HttpApplication instances
322         // see DevDiv #109006 (http://vstfdevdiv:8080/DevDiv2/web/wi.aspx?id=109006): HttpModules attached to the "Special" HttpApplication instance are not disposed
323         private static bool _doNotDisposeSpecialHttpApplicationInstances;
324         internal static bool DoNotDisposeSpecialHttpApplicationInstances {
325             get {
326                 EnsureSettingsLoaded();
327                 return _doNotDisposeSpecialHttpApplicationInstances;
328             }
329         }
330
331         // none [default] to use the existing /loging.aspx?ReturnUrl=
332         // <FormsAuthReturnUrlVar> to use /loging.aspx?<FormsAuthReturnUrlVar>=
333         private static string _formsAuthReturnUrlVar;
334         internal static string FormsAuthReturnUrlVar {
335             get {
336                 EnsureSettingsLoaded();
337                 return _formsAuthReturnUrlVar;
338             }
339         }
340
341         // false [default] to use potentially unsafe System.Xml API's that were in use for 1.0 through 4.0
342         // true to avoid using vulnerable System.Xml API's.
343         private static bool _restrictXmlControls;
344         internal static bool RestrictXmlControls {
345             get {
346                 EnsureSettingsLoaded();
347                 return _restrictXmlControls;
348             }
349         }
350
351         // false [default] - requires relative url to be more strict
352         // true - use less strict policy
353         private static bool _allowRelaxedRelativeUrl;
354         internal static bool AllowRelaxedRelativeUrl {
355             get {
356                 EnsureSettingsLoaded();
357                 return _allowRelaxedRelativeUrl;
358             }
359         }
360
361         // false [default] - attempts to re-encode HttpRequest.Url so that it is equivalent to the actual incoming URL 
362         // true - does not attempt to re-encode the Url, which could allow the Uri class to perform double-decoding (dangerous!)
363         // See also DevDiv #703232 (http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/703232)
364         private static bool _useLegacyRequestUrlGeneration;
365         internal static bool UseLegacyRequestUrlGeneration {
366             get {
367                 EnsureSettingsLoaded();
368                 return _useLegacyRequestUrlGeneration;
369             }
370         }
371
372         // false [default] - disallow UTF-7 as an incoming ContentEncoding
373         // true - allow UTF-7 as an incoming ContentEncoding (dangerous!)
374         private static bool _allowUtf7RequestContentEncoding;
375         internal static bool AllowUtf7RequestContentEncoding {
376             get {
377                 EnsureSettingsLoaded();
378                 return _allowUtf7RequestContentEncoding;
379             }
380         }
381
382         // false [default] - disallow invalid UTF-16 (like unpaired surrogates) when deserializing JSON strings and inside UrlDecode
383         // true - allow malformed strings like "\ud800" and "%ud800" to be deserialized (dangerous!)
384         private static bool _allowRelaxedUnicodeDecoding;
385         internal static bool AllowRelaxedUnicodeDecoding {
386             get {
387                 EnsureSettingsLoaded();
388                 return _allowRelaxedUnicodeDecoding;
389             }
390         }
391
392         // false - use UrlEncodeUnicode for some URL generation within ASP.NET, which can produce non-compliant results
393         // true - use UTF8 encoding for things like <form action>, which works with modern browsers
394         // defaults to true when targeting >= 4.5.2, otherwise false
395         // See DevDiv #762975 for more information.
396         private static bool _dontUsePercentUUrlEncoding;
397         internal static bool DontUsePercentUUrlEncoding {
398             get {
399                 EnsureSettingsLoaded();
400                 return _dontUsePercentUUrlEncoding;
401             }
402         }
403
404         // maximum length for UpdatePanel script block
405         private static int _updatePanelMaxScriptLength;
406         internal static int UpdatePanelMaxScriptLength {
407             get {
408                 EnsureSettingsLoaded();
409                 return _updatePanelMaxScriptLength;
410             }
411         }
412
413        // maximum number of concurrent compilations
414        private static int? _maxConcurrentCompilations;
415        internal static int? MaxConcurrentCompilations {
416            get {
417                EnsureSettingsLoaded();
418                return _maxConcurrentCompilations;
419            }
420        }
421
422         // Controls how deep we look when trying to get a CultureInfo from an Accept-Language header.
423         // For example, a value of 3 with Accept-Language: en-us, en, fr-FR, zh-CN will cause us to
424         // look for "en-us", "en", and "fr-FR" in order, taking the first hit, but we won't look
425         // for "zh-CN" if the first three fail. Setting this value too high could result in DoS.
426         private const int DefaultMaxAcceptLanguageFallbackCount = 3;
427         private static int _maxAcceptLanguageFallbackCount;
428         internal static int MaxAcceptLanguageFallbackCount {
429             get {
430                 EnsureSettingsLoaded();
431                 return _maxAcceptLanguageFallbackCount;
432             }
433         }
434
435        // false [default] 
436        // true to allow 
437        private static bool _portableCompilationOutput;
438        internal static bool PortableCompilationOutput {
439            get {
440                EnsureSettingsLoaded();
441                return _portableCompilationOutput;
442            }
443        }
444
445        // null [default] 
446        // complete type name as string
447        private static string _portableCompilationOutputSnapshotType;
448        internal static string PortableCompilationOutputSnapshotType {
449            get {
450                EnsureSettingsLoaded();
451                return _portableCompilationOutputSnapshotType;
452            }
453        }
454
455        // null [default] 
456        // options
457        private static string _portableCompilationOutputSnapshotTypeOptions;
458        internal static string PortableCompilationOutputSnapshotTypeOptions {
459            get {
460                EnsureSettingsLoaded();
461                return _portableCompilationOutputSnapshotTypeOptions;
462            }
463        }
464
465        // false [default] - allow delayed store of session state item (possibly concurrent access on new session)
466        // true - ensure the session state item is stored before another request is allowed to proceed with the same session
467        private static bool _ensureSessionStateLockedOnFlush;
468        internal static bool EnsureSessionStateLockedOnFlush {
469            get {
470                EnsureSettingsLoaded();
471                return _ensureSessionStateLockedOnFlush;
472            }
473        }
474
475        // false [default] - don't force randomized hash code algorithm. Exisiting behavior
476        // true - use randomized hash code algorithm
477        private static bool _useRandomizedStringHashAlgorithm;
478        internal static bool UseRandomizedStringHashAlgorithm {
479            get {
480                EnsureSettingsLoaded();
481                return _useRandomizedStringHashAlgorithm;
482            }
483        }
484
485        // false - disable async model binding
486        // true - enable async model binding
487        // defaults to true when targeting >= 4.6, otherwise false
488        private static bool _enableAsyncModelBinding;
489        internal static bool EnableAsyncModelBinding {
490            get {
491                EnsureSettingsLoaded();
492                return _enableAsyncModelBinding;
493            }
494        }
495     }
496 }