Merge pull request #5714 from alexischr/update_bockbuild
[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                             if (settings == null || !int.TryParse(settings["aspnet:RequestQueueLimitPerSession"], out _requestQueueLimitPerSession) || _requestQueueLimitPerSession < 0)
134                                 _requestQueueLimitPerSession = BinaryCompatibility.Current.TargetsAtLeastFramework463 ? DefaultRequestQueueLimitPerSession : UnlimitedRequestsPerSession;
135
136                             _settingsInitialized = true;
137                         }
138                     }
139                 }
140             }
141         }
142
143         [ConfigurationPermission(SecurityAction.Assert, Unrestricted = true)]
144         [SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Justification = "This assert is safe since we control the callers; they're not leaking information.")]
145         private static NameValueCollection GetAppSettingsSection() {
146             // DevDiv #353926 - Fall back to reading App.config if System.Web types are being consumed outside an ASP.NET application
147             if (!HostingEnvironment.IsHosted) {
148                 return ConfigurationManager.AppSettings;
149             }
150
151             // Check the app-level config. Ignore configuration errors
152             CachedPathData appPathData = CachedPathData.GetApplicationPathData();
153
154             if (appPathData != null && appPathData.ConfigRecord != null)
155                 return appPathData.ConfigRecord.GetSection("appSettings") as NameValueCollection;
156
157             // nothing found
158             return null;
159         }
160
161         // helper function to read a tri-state boolean from config
162         private static bool? GetNullableBooleanValue(NameValueCollection settings, string key) {
163             bool value;
164             return (settings != null && Boolean.TryParse(settings[key], out value)) ? value : (bool?)null;
165         }
166
167         // helper function to read a nullable int from config
168         private static int? GetNullableIntegerValue(NameValueCollection settings, string key) {
169             int value;
170             return (settings != null && int.TryParse(settings[key], out value)) ? value : (int?)null;
171         }
172
173         private static  bool _useHostHeaderForRequestUrl;
174         internal static bool UseHostHeaderForRequestUrl {
175             get {
176                 EnsureSettingsLoaded();
177                 return _useHostHeaderForRequestUrl;
178             }
179         }
180
181         private static  bool _allowAnonymousImpersonation;
182         internal static bool AllowAnonymousImpersonation {
183             get {
184                 EnsureSettingsLoaded();
185                 return _allowAnonymousImpersonation;
186             }
187         }
188
189         // false [default] to allow ScriptResource.axd to serve static files only if they are *.js; 
190         // true to allow ScriptResource.axd to serve any static file (bad practice)
191         private static  bool _scriptResourceAllowNonJsFiles;
192         internal static bool ScriptResourceAllowNonJsFiles {
193             get {
194                 EnsureSettingsLoaded();
195                 return _scriptResourceAllowNonJsFiles;
196             }
197         }
198
199         // false [default] to use encrypt+sign for all except Membership and MachineKey public API
200         // true to use encrypt only (dangerous!)
201         private static  bool _useLegacyEncryption;
202         internal static bool UseLegacyEncryption {
203             get {
204                 EnsureSettingsLoaded();
205                 return _useLegacyEncryption;
206             }
207         }
208
209         // false [default] to use encrypt+sign for MachineKey public API
210         // true to use encrypt only for MachineKey public API (dangerous!)
211         private static  bool _useLegacyMachineKeyEncryption;
212         internal static bool UseLegacyMachineKeyEncryption {
213             get {
214                 EnsureSettingsLoaded();
215                 return _useLegacyMachineKeyEncryption;
216             }
217         }
218
219         // false [default] to use secure semantics when generating FormsAuthentication tickets
220         // true to allow ticketCompatibilityMode="Framework20" (dangerous!)
221         private static bool _useLegacyFormsAuthenticationTicketCompatibility;
222         internal static bool UseLegacyFormsAuthenticationTicketCompatibility {
223             get {
224                 EnsureSettingsLoaded();
225                 return _useLegacyFormsAuthenticationTicketCompatibility;
226             }
227         }
228
229         // false [default] to use a cryptographic hash algorithm when generating the __EVENTVALIDATION field
230         // true to use String.GetHashCode() instead (dangerous!)
231         // more info: DevDiv #233564 (http://vstfdevdiv:8080/DevDiv2/web/wi.aspx?id=233564)
232         private static bool _useLegacyEventValidationCompatibility;
233         internal static bool UseLegacyEventValidationCompatibility {
234             get {
235                 EnsureSettingsLoaded();
236                 return _useLegacyEventValidationCompatibility;
237             }
238         }
239
240         // false to always enforce EnableViewStateMac=false
241         // true to allow the developer to specify EnableViewStateMac=false (dangerous, leads to RCE!)
242         // null [default] to make the decision based on a registry key
243         // more info: DevDiv #461378 (http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/461378)
244         private static bool? _allowInsecureDeserialization;
245         internal static bool? AllowInsecureDeserialization {
246             get {
247                 EnsureSettingsLoaded();
248                 return _allowInsecureDeserialization;
249             }
250         }
251
252         // false [default] to use the default heuristic for determining when to suppress __VIEWSTATE MAC validation errors and when to display a YSOD
253         // true to always ---- errors and never show a YSOD
254         private static bool _alwaysIgnoreViewStateValidationErrors;
255         internal static bool AlwaysIgnoreViewStateValidationErrors {
256             get {
257                 EnsureSettingsLoaded();
258                 return _alwaysIgnoreViewStateValidationErrors;
259             }
260         }
261
262         // false [default] to restrict dangerous characters in any user name passed to native code
263         // true to allow dangerous characters
264         private static bool _allowRelaxedHttpUserName;
265         internal static bool AllowRelaxedHttpUserName {
266             get {
267                 EnsureSettingsLoaded();
268                 return _allowRelaxedHttpUserName;
269             }
270         }
271
272         // false [default] to encode '&' characters in HttpUtility.JavaScriptStringEncode()
273         // true to not encode '&' characters (bad practice)
274         private static bool _javaScriptDoNotEncodeAmpersand;
275         internal static bool JavaScriptDoNotEncodeAmpersand {
276             get {
277                 EnsureSettingsLoaded();
278                 return _javaScriptDoNotEncodeAmpersand;
279             }
280         }
281
282         // false [default] to use the updated AspNetSynchronizationContext type (needed for TAP methods)
283         // true to use LegacyAspNetSynchronizationContext (needed for some back-compat scenarios)
284         private static bool _useTaskFriendlySynchronizationContext ;
285         internal static bool UseTaskFriendlySynchronizationContext {
286             get {
287                 EnsureSettingsLoaded();
288                 return _useTaskFriendlySynchronizationContext ;
289             }
290         }
291
292         // false [default] if the updated AspNetSynchronizationContext type should throw when it encounters invalid operations
293         // true to allow some invalid operations through the system (needed for some back-compat scenarios)
294         // more info: see doc on HttpContext.AllowAsyncDuringSyncStages
295         private static bool _allowAsyncDuringSyncStages;
296         internal static bool AllowAsyncDuringSyncStages {
297             get {
298                 EnsureSettingsLoaded();
299                 return _allowAsyncDuringSyncStages;
300             }
301         }
302
303          // maximum number of keys a HttpValueCollection, HttpFileCollection or HttpCookieCollection is allowed to have
304          private const int DefaultMaxHttpCollectionKeys = Int32.MaxValue;
305          private static int _maxHttpCollectionKeys = DefaultMaxHttpCollectionKeys;
306          internal static int MaxHttpCollectionKeys {
307              get {
308                  EnsureSettingsLoaded();
309                  return _maxHttpCollectionKeys;
310              }
311          }
312
313          // maximum number of entries a Json deserialized dictionary is allowed to have
314          private const int DefaultMaxJsonDeserializerMembers = Int32.MaxValue;
315          private static int _maxJsonDeserializerMembers = DefaultMaxJsonDeserializerMembers;
316          internal static int MaxJsonDeserializerMembers {
317              get {
318                  EnsureSettingsLoaded();
319                  return _maxJsonDeserializerMembers;
320              }
321          }
322
323         // false [default] to call HttpApplication.Dispose() on all HttpApplication instances instantiated by ASP.NET
324         // true to suppress calling HttpApplication.Dispose() on "special" (i.e., associated with App_Start, App_End, etc.) HttpApplication instances
325         // see DevDiv #109006 (http://vstfdevdiv:8080/DevDiv2/web/wi.aspx?id=109006): HttpModules attached to the "Special" HttpApplication instance are not disposed
326         private static bool _doNotDisposeSpecialHttpApplicationInstances;
327         internal static bool DoNotDisposeSpecialHttpApplicationInstances {
328             get {
329                 EnsureSettingsLoaded();
330                 return _doNotDisposeSpecialHttpApplicationInstances;
331             }
332         }
333
334         // none [default] to use the existing /loging.aspx?ReturnUrl=
335         // <FormsAuthReturnUrlVar> to use /loging.aspx?<FormsAuthReturnUrlVar>=
336         private static string _formsAuthReturnUrlVar;
337         internal static string FormsAuthReturnUrlVar {
338             get {
339                 EnsureSettingsLoaded();
340                 return _formsAuthReturnUrlVar;
341             }
342         }
343
344         // false [default] to use potentially unsafe System.Xml API's that were in use for 1.0 through 4.0
345         // true to avoid using vulnerable System.Xml API's.
346         private static bool _restrictXmlControls;
347         internal static bool RestrictXmlControls {
348             get {
349                 EnsureSettingsLoaded();
350                 return _restrictXmlControls;
351             }
352         }
353
354         // false [default] - requires relative url to be more strict
355         // true - use less strict policy
356         private static bool _allowRelaxedRelativeUrl;
357         internal static bool AllowRelaxedRelativeUrl {
358             get {
359                 EnsureSettingsLoaded();
360                 return _allowRelaxedRelativeUrl;
361             }
362         }
363
364         // false [default] - attempts to re-encode HttpRequest.Url so that it is equivalent to the actual incoming URL 
365         // true - does not attempt to re-encode the Url, which could allow the Uri class to perform double-decoding (dangerous!)
366         // See also DevDiv #703232 (http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/703232)
367         private static bool _useLegacyRequestUrlGeneration;
368         internal static bool UseLegacyRequestUrlGeneration {
369             get {
370                 EnsureSettingsLoaded();
371                 return _useLegacyRequestUrlGeneration;
372             }
373         }
374
375         // false [default] - disallow UTF-7 as an incoming ContentEncoding
376         // true - allow UTF-7 as an incoming ContentEncoding (dangerous!)
377         private static bool _allowUtf7RequestContentEncoding;
378         internal static bool AllowUtf7RequestContentEncoding {
379             get {
380                 EnsureSettingsLoaded();
381                 return _allowUtf7RequestContentEncoding;
382             }
383         }
384
385         // false [default] - disallow invalid UTF-16 (like unpaired surrogates) when deserializing JSON strings and inside UrlDecode
386         // true - allow malformed strings like "\ud800" and "%ud800" to be deserialized (dangerous!)
387         private static bool _allowRelaxedUnicodeDecoding;
388         internal static bool AllowRelaxedUnicodeDecoding {
389             get {
390                 EnsureSettingsLoaded();
391                 return _allowRelaxedUnicodeDecoding;
392             }
393         }
394
395         // false - use UrlEncodeUnicode for some URL generation within ASP.NET, which can produce non-compliant results
396         // true - use UTF8 encoding for things like <form action>, which works with modern browsers
397         // defaults to true when targeting >= 4.5.2, otherwise false
398         // See DevDiv #762975 for more information.
399         private static bool _dontUsePercentUUrlEncoding;
400         internal static bool DontUsePercentUUrlEncoding {
401             get {
402                 EnsureSettingsLoaded();
403                 return _dontUsePercentUUrlEncoding;
404             }
405         }
406
407         // maximum length for UpdatePanel script block
408         private static int _updatePanelMaxScriptLength;
409         internal static int UpdatePanelMaxScriptLength {
410             get {
411                 EnsureSettingsLoaded();
412                 return _updatePanelMaxScriptLength;
413             }
414         }
415
416        // maximum number of concurrent compilations
417        private static int? _maxConcurrentCompilations;
418        internal static int? MaxConcurrentCompilations {
419            get {
420                EnsureSettingsLoaded();
421                return _maxConcurrentCompilations;
422            }
423        }
424
425         // Controls how deep we look when trying to get a CultureInfo from an Accept-Language header.
426         // For example, a value of 3 with Accept-Language: en-us, en, fr-FR, zh-CN will cause us to
427         // look for "en-us", "en", and "fr-FR" in order, taking the first hit, but we won't look
428         // for "zh-CN" if the first three fail. Setting this value too high could result in DoS.
429         private const int DefaultMaxAcceptLanguageFallbackCount = 3;
430         private static int _maxAcceptLanguageFallbackCount;
431         internal static int MaxAcceptLanguageFallbackCount {
432             get {
433                 EnsureSettingsLoaded();
434                 return _maxAcceptLanguageFallbackCount;
435             }
436         }
437
438        // false [default] 
439        // true to allow 
440        private static bool _portableCompilationOutput;
441        internal static bool PortableCompilationOutput {
442            get {
443                EnsureSettingsLoaded();
444                return _portableCompilationOutput;
445            }
446        }
447
448        // null [default] 
449        // complete type name as string
450        private static string _portableCompilationOutputSnapshotType;
451        internal static string PortableCompilationOutputSnapshotType {
452            get {
453                EnsureSettingsLoaded();
454                return _portableCompilationOutputSnapshotType;
455            }
456        }
457
458        // null [default] 
459        // options
460        private static string _portableCompilationOutputSnapshotTypeOptions;
461        internal static string PortableCompilationOutputSnapshotTypeOptions {
462            get {
463                EnsureSettingsLoaded();
464                return _portableCompilationOutputSnapshotTypeOptions;
465            }
466        }
467
468        // false [default] - allow delayed store of session state item (possibly concurrent access on new session)
469        // true - ensure the session state item is stored before another request is allowed to proceed with the same session
470        private static bool _ensureSessionStateLockedOnFlush;
471        internal static bool EnsureSessionStateLockedOnFlush {
472            get {
473                EnsureSettingsLoaded();
474                return _ensureSessionStateLockedOnFlush;
475            }
476        }
477
478        // false [default] - don't force randomized hash code algorithm. Exisiting behavior
479        // true - use randomized hash code algorithm
480        private static bool _useRandomizedStringHashAlgorithm;
481        internal static bool UseRandomizedStringHashAlgorithm {
482            get {
483                EnsureSettingsLoaded();
484                return _useRandomizedStringHashAlgorithm;
485            }
486        }
487
488        // false - disable async model binding
489        // true - enable async model binding
490        // defaults to true when targeting >= 4.6, otherwise false
491        private static bool _enableAsyncModelBinding;
492        internal static bool EnableAsyncModelBinding {
493            get {
494                EnsureSettingsLoaded();
495                return _enableAsyncModelBinding;
496            }
497        }
498
499         internal const int UnlimitedRequestsPerSession = Int32.MaxValue;
500         internal const int DefaultRequestQueueLimitPerSession = 50;
501         // Limit of queued requests per session
502         private static int _requestQueueLimitPerSession;
503         internal static int RequestQueueLimitPerSession {
504             get {
505                 EnsureSettingsLoaded();
506                 return _requestQueueLimitPerSession;
507             }
508         }
509     }
510 }