1 //------------------------------------------------------------------------------
2 // <copyright file="ApplicationManager.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6 
7 
8 namespace System.Web.Hosting {
9     using System;
10     using System.Collections;
11     using System.Collections.Generic;
12     using System.Collections.Specialized;
13     using System.Configuration;
14     using System.Configuration.Provider;
15     using System.Diagnostics.CodeAnalysis;
16     using System.Globalization;
17     using System.IO;
18     using System.Linq;
19     using System.Reflection;
20     using System.Runtime.ExceptionServices;
21     using System.Runtime.InteropServices;
22     using System.Runtime.Remoting;
23     using System.Runtime.Versioning;
24     using System.Security;
25     using System.Security.Permissions;
26     using System.Security.Policy;
27     using System.Text;
28     using System.Threading;
29     using System.Threading.Tasks;
30     using System.Web;
31     using System.Web.Compilation;
32     using System.Web.Configuration;
33     using System.Web.Util;
34 
35     public enum HostSecurityPolicyResults {
36         DefaultPolicy = 0,
37         FullTrust = 1,
38         AppDomainTrust = 2,
39         Nothing = 3
40     };
41 
42     [PermissionSet(SecurityAction.InheritanceDemand, Unrestricted = true)]
43     public class HostSecurityPolicyResolver {
ResolvePolicy(Evidence evidence)44         public virtual HostSecurityPolicyResults ResolvePolicy(Evidence evidence) {
45             return HostSecurityPolicyResults.DefaultPolicy;
46         }
47     }
48 
49 
50 
51     internal class LockableAppDomainContext {
52         internal HostingEnvironment HostEnv { get; set; }
53         internal string PreloadContext { get; set; }
54         internal bool RetryingPreload { get; set; }
55 
LockableAppDomainContext()56         internal LockableAppDomainContext() {
57         }
58     }
59 
60     public sealed class ApplicationManager : MarshalByRefObject {
61 
62         private const string _clrQuirkAppSettingsAppContextPrefix = "AppContext.SetSwitch:";
63         private const string _regexMatchTimeoutKey = "REGEX_DEFAULT_MATCH_TIMEOUT";
64         private const string _configBuildersIgnoreLoadFailuresSwitch = "ConfigurationBuilders.IgnoreLoadFailure";   // Keep in sync with System.Configuration
65         private static readonly StrongName _mwiV1StrongName = GetMicrosoftWebInfrastructureV1StrongName();
66 
67         private static Object _applicationManagerStaticLock = new Object();
68 
69         // open count (when last close goes to 0 it shuts down everything)
70         int _openCount = 0;
71         bool _shutdownInProgress = false;
72 
73         // table of app domains (LockableAppDomainContext objects) by app id
74         // To simplify per-appdomain synchronization we will never remove LockableAppDomainContext objects from this table even when the AD is unloaded
75         // We may need to fix it if profiling shows a noticeable impact on performance
76         private Dictionary <string, LockableAppDomainContext> _appDomains = new Dictionary<string, LockableAppDomainContext>(StringComparer.OrdinalIgnoreCase);
77         // count of HostingEnvironment instances that is referenced in _appDomains collection
78         private int _accessibleHostingEnvCount;
79 
80         // could differ from _appDomains or _accessibleHostingEnvCount count (host env is active some time after it is removed)
81         private int _activeHostingEnvCount;
82 
83         // pending callback to respond to ping (typed as Object to do Interlocked operations)
84         private Object _pendingPingCallback;
85         // delegate OnRespondToPing
86         private WaitCallback _onRespondToPingWaitCallback;
87 
88         // flag indicates whether any fatal exception has been recorded
89         private bool _fatalExceptionRecorded = false;
90 
91         // single instance of app manager
92         private static ApplicationManager _theAppManager;
93 
94         // store fatal exception to assist debugging
95         private static Exception _fatalException = null;
96 
ApplicationManager()97         internal ApplicationManager() {
98             _onRespondToPingWaitCallback = new WaitCallback(this.OnRespondToPingWaitCallback);
99 
100             // VSWhidbey 555767: Need better logging for unhandled exceptions (http://support.microsoft.com/?id=911816)
101             // We only add a handler in the default domain because it will be notified when an unhandled exception
102             // occurs in ANY domain.
103             // WOS 1983175: (weird) only the handler in the default domain is notified when there is an AV in a native module
104             // while we're in a call to MgdIndicateCompletion.
105             AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(OnUnhandledException);
106         }
107 
108         internal bool ShutdownInProgress {
109             get {
110                 return _shutdownInProgress;
111             }
112         }
113 
114         private bool FatalExceptionRecorded
115         {
116             get {
117                 return _fatalExceptionRecorded;
118             }
119             set {
120                 _fatalExceptionRecorded = value;
121             }
122         }
123 
RecordFatalException(Exception e)124         internal static void RecordFatalException(Exception e) {
125             RecordFatalException(AppDomain.CurrentDomain, e);
126         }
127 
RecordFatalException(AppDomain appDomain, Exception e)128         internal static void RecordFatalException(AppDomain appDomain, Exception e) {
129             // store the exception from the first caller to assist debugging
130             object originalValue = Interlocked.CompareExchange(ref _fatalException, e, null);
131 
132             if (originalValue == null) {
133                 // create event log entry
134                 Misc.WriteUnhandledExceptionToEventLog(appDomain, e);
135             }
136         }
137 
OnUnhandledException(Object sender, UnhandledExceptionEventArgs eventArgs)138         internal static void OnUnhandledException(Object sender, UnhandledExceptionEventArgs eventArgs) {
139             // if the CLR is not terminating, ignore the notification
140             if (!eventArgs.IsTerminating) {
141                 return;
142             }
143 
144             Exception exception = eventArgs.ExceptionObject as Exception;
145             if (exception == null) {
146                 return;
147             }
148 
149             AppDomain appDomain = sender as AppDomain;
150             if (appDomain == null) {
151                 return;
152             }
153 
154             // If any fatal exception was recorded in applicaiton AppDomains,
155             // we wouldn't record exceptions in the default AppDomain.
156             var appManager = GetApplicationManager();
157             if (AppDomain.CurrentDomain.IsDefaultAppDomain() && appManager.FatalExceptionRecorded) {
158                 return;
159             }
160 
161             appManager.FatalExceptionRecorded = true;
162 
163             RecordFatalException(appDomain, exception);
164         }
165 
InitializeLifetimeService()166         public override Object InitializeLifetimeService() {
167             return null; // never expire lease
168         }
169 
170         //
171         // public ApplicationManager methods
172         //
173 
174 
GetApplicationManager()175         public static ApplicationManager GetApplicationManager() {
176             if (_theAppManager == null) {
177                 lock (_applicationManagerStaticLock) {
178                     if (_theAppManager == null) {
179                         if (HostingEnvironment.IsHosted)
180                             _theAppManager = HostingEnvironment.GetApplicationManager();
181 
182                         if (_theAppManager == null)
183                             _theAppManager = new ApplicationManager();
184                     }
185                 }
186             }
187 
188             return _theAppManager;
189         }
190 
191 
192         [SecurityPermission(SecurityAction.Demand, Unrestricted=true)]
Open()193         public void Open() {
194             Interlocked.Increment(ref _openCount);
195         }
196 
197 
198         [SecurityPermission(SecurityAction.Demand, Unrestricted = true)]
Close()199         public void Close() {
200             if (Interlocked.Decrement(ref _openCount) > 0)
201                 return;
202 
203             // need to shutdown everything
204             ShutdownAll();
205         }
206 
CreateSimpleAppID(VirtualPath virtualPath, string physicalPath, string siteName)207         private string CreateSimpleAppID(VirtualPath virtualPath, string physicalPath, string siteName) {
208             // Put together some unique app id
209             string appId = String.Concat(virtualPath.VirtualPathString, physicalPath);
210 
211             if (!String.IsNullOrEmpty(siteName)) {
212                 appId = String.Concat(appId, siteName);
213             }
214 
215             return appId.GetHashCode().ToString("x", CultureInfo.InvariantCulture);
216         }
217 
218         [SecurityPermission(SecurityAction.Demand, Unrestricted = true)]
CreateObject(IApplicationHost appHost, Type type)219         public IRegisteredObject CreateObject(IApplicationHost appHost, Type type) {
220             if (appHost == null) {
221                 throw new ArgumentNullException("appHost");
222             }
223             if (type == null) {
224                 throw new ArgumentNullException("type");
225             }
226 
227             string appID = CreateSimpleAppID(appHost);
228             return CreateObjectInternal(appID, type, appHost, false);
229         }
230 
231         [SecurityPermission(SecurityAction.Demand, UnmanagedCode=true)]
CreateObject(String appId, Type type, string virtualPath, string physicalPath, bool failIfExists)232         public IRegisteredObject CreateObject(String appId, Type type, string virtualPath, string physicalPath, bool failIfExists) {
233             return CreateObject(appId, type, virtualPath, physicalPath, failIfExists, false /*throwOnError*/);
234         }
235 
236         [SecurityPermission(SecurityAction.Demand, Unrestricted = true)]
CreateObject(String appId, Type type, string virtualPath, string physicalPath, bool failIfExists, bool throwOnError)237         public IRegisteredObject CreateObject(String appId, Type type, string virtualPath, string physicalPath,
238                                               bool failIfExists, bool throwOnError) {
239             // check args
240             if (appId == null)
241                 throw new ArgumentNullException("appId");
242 
243             SimpleApplicationHost appHost = new SimpleApplicationHost(VirtualPath.CreateAbsolute(virtualPath), physicalPath);
244 
245             // if throw on error flag is set, create hosting parameters accordingly
246             HostingEnvironmentParameters hostingParameters = null;
247 
248             if (throwOnError) {
249                 hostingParameters = new HostingEnvironmentParameters();
250                 hostingParameters.HostingFlags = HostingEnvironmentFlags.ThrowHostingInitErrors;
251 
252             }
253 
254             // call the internal method
255             return CreateObjectInternal(appId, type, appHost, failIfExists, hostingParameters);
256         }
257 
258         [SecurityPermission(SecurityAction.Demand, Unrestricted = true)]
CreateObjectInternal(String appId, Type type, IApplicationHost appHost, bool failIfExists)259         internal IRegisteredObject CreateObjectInternal(String appId, Type type, IApplicationHost appHost, bool failIfExists) {
260             // check args
261             if (appId == null)
262                 throw new ArgumentNullException("appId");
263 
264             if (type == null)
265                 throw new ArgumentNullException("type");
266 
267             if (appHost == null)
268                 throw new ArgumentNullException("appHost");
269 
270             // call the internal method
271             return CreateObjectInternal(appId, type, appHost, failIfExists, null /*hostingParameters*/);
272         }
273 
274         [PermissionSet(SecurityAction.Assert, Unrestricted = true)]
CreateObjectInternal( String appId, Type type, IApplicationHost appHost, bool failIfExists, HostingEnvironmentParameters hostingParameters)275         internal IRegisteredObject CreateObjectInternal(
276                                         String appId,
277                                         Type type,
278                                         IApplicationHost appHost,
279                                         bool failIfExists,
280                                         HostingEnvironmentParameters hostingParameters) {
281 
282             // check that type is as IRegisteredObject
283             if (!typeof(IRegisteredObject).IsAssignableFrom(type))
284                 throw new ArgumentException(SR.GetString(SR.Not_IRegisteredObject, type.FullName), "type");
285 
286             // get hosting environment
287             HostingEnvironment env = GetAppDomainWithHostingEnvironment(appId, appHost, hostingParameters);
288 
289             // create the managed object in the worker app domain
290             // When marshaling Type, the AppDomain must have FileIoPermission to the assembly, which is not
291             // always the case, so we marshal the assembly qualified name instead
292             ObjectHandle h = env.CreateWellKnownObjectInstance(type.AssemblyQualifiedName, failIfExists);
293             return (h != null) ? h.Unwrap() as IRegisteredObject : null;
294         }
295 
CreateObjectWithDefaultAppHostAndAppId( String physicalPath, string virtualPath, Type type, out String appId, out IApplicationHost appHost)296         internal IRegisteredObject CreateObjectWithDefaultAppHostAndAppId(
297                                         String physicalPath,
298                                         string virtualPath,
299                                         Type type,
300                                         out String appId,
301                                         out IApplicationHost appHost) {
302             return CreateObjectWithDefaultAppHostAndAppId(physicalPath,
303                 VirtualPath.CreateNonRelative(virtualPath), type, out appId, out appHost);
304         }
305 
CreateObjectWithDefaultAppHostAndAppId( String physicalPath, VirtualPath virtualPath, Type type, out String appId, out IApplicationHost appHost)306         internal IRegisteredObject CreateObjectWithDefaultAppHostAndAppId(
307                                         String physicalPath,
308                                         VirtualPath virtualPath,
309                                         Type type,
310                                         out String appId,
311                                         out IApplicationHost appHost) {
312 
313             HostingEnvironmentParameters hostingParameters = new HostingEnvironmentParameters();
314             hostingParameters.HostingFlags = HostingEnvironmentFlags.DontCallAppInitialize;
315 
316             return CreateObjectWithDefaultAppHostAndAppId(
317                         physicalPath,
318                         virtualPath,
319                         type,
320                         false,
321                         hostingParameters,
322                         out appId,
323                         out appHost);
324         }
325 
CreateObjectWithDefaultAppHostAndAppId( String physicalPath, VirtualPath virtualPath, Type type, bool failIfExists, HostingEnvironmentParameters hostingParameters, out String appId, out IApplicationHost appHost)326         internal IRegisteredObject CreateObjectWithDefaultAppHostAndAppId(
327                                         String physicalPath,
328                                         VirtualPath virtualPath,
329                                         Type type,
330                                         bool failIfExists,
331                                         HostingEnvironmentParameters hostingParameters,
332                                         out String appId,
333                                         out IApplicationHost appHost) {
334 
335 #if !FEATURE_PAL // FEATURE_PAL does not enable IIS-based hosting features
336             if (physicalPath == null) {
337 
338                 // If the physical path is null, we use an ISAPIApplicationHost based
339                 // on the virtual path (or metabase id).
340 
341                 // Make sure the static HttpRuntime is created so isapi assembly can be loaded properly.
342                 HttpRuntime.ForceStaticInit();
343 
344                 ISAPIApplicationHost isapiAppHost = new ISAPIApplicationHost(virtualPath.VirtualPathString, null, true, null, hostingParameters.IISExpressVersion);
345 
346                 appHost = isapiAppHost;
347                 appId = isapiAppHost.AppId;
348                 virtualPath = VirtualPath.Create(appHost.GetVirtualPath());
349                 physicalPath = FileUtil.FixUpPhysicalDirectory(appHost.GetPhysicalPath());
350             }
351             else {
352 #endif // !FEATURE_PAL
353                 // If the physical path was passed in, don't use an Isapi host. Instead,
354                 // use a simple app host which does simple virtual to physical mappings
355 
356                 // Put together some unique app id
357                 appId = CreateSimpleAppID(virtualPath, physicalPath, null);
358 
359                 appHost = new SimpleApplicationHost(virtualPath, physicalPath);
360             }
361 
362             string precompTargetPhysicalDir = hostingParameters.PrecompilationTargetPhysicalDirectory;
363             if (precompTargetPhysicalDir != null) {
364                 // Change the appID so we use a different codegendir in precompile for deployment scenario,
365                 // this ensures we don't use or pollute the regular codegen files.  Also, use different
366                 // ID's depending on whether the precompilation is Updatable (VSWhidbey 383239)
367                 if ((hostingParameters.ClientBuildManagerParameter != null) &&
368                     (hostingParameters.ClientBuildManagerParameter.PrecompilationFlags & PrecompilationFlags.Updatable) == 0)
369                     appId = appId + "_precompile";
370                 else
371                     appId = appId + "_precompile_u";
372             }
373 
374             return CreateObjectInternal(appId, type, appHost, failIfExists, hostingParameters);
375         }
376 
377 
378         [SecurityPermission(SecurityAction.Demand, Unrestricted = true)]
GetObject(String appId, Type type)379         public IRegisteredObject GetObject(String appId, Type type) {
380             // check args
381             if (appId == null)
382                 throw new ArgumentNullException("appId");
383             if (type == null)
384                 throw new ArgumentNullException("type");
385 
386             LockableAppDomainContext ac = GetLockableAppDomainContext(appId);
387             lock (ac) {
388                 HostingEnvironment env = ac.HostEnv;
389                 if (env == null)
390                     return null;
391 
392                 // find the instance by type
393                 // When marshaling Type, the AppDomain must have FileIoPermission to the assembly, which is not
394                 // always the case, so we marshal the assembly qualified name instead
395                 ObjectHandle h = env.FindWellKnownObject(type.AssemblyQualifiedName);
396                 return (h != null) ? h.Unwrap() as IRegisteredObject : null;
397             }
398         }
399 
400         [SecurityPermission(SecurityAction.Demand, Unrestricted = true)]
GetAppDomain(string appId)401         public AppDomain GetAppDomain(string appId) {
402             if (appId == null) {
403                 throw new ArgumentNullException("appId");
404             }
405 
406             LockableAppDomainContext ac = GetLockableAppDomainContext(appId);
407             lock (ac) {
408                 HostingEnvironment env = ac.HostEnv;
409                 if (env == null) {
410                     return null;
411                 }
412 
413                 return env.HostedAppDomain;
414             }
415         }
416 
GetAppDomain(IApplicationHost appHost)417         public AppDomain GetAppDomain(IApplicationHost appHost) {
418             if (appHost == null) {
419                 throw new ArgumentNullException("appHost");
420             }
421             string appID = CreateSimpleAppID(appHost);
422             return GetAppDomain(appID);
423         }
424 
GetDefaultAppDomain()425         internal AppDomain GetDefaultAppDomain() {
426             return AppDomain.CurrentDomain;
427         }
428 
429         [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "This isn't a dangerous method.")]
CreateSimpleAppID(IApplicationHost appHost)430         private string CreateSimpleAppID(IApplicationHost appHost) {
431             if (appHost == null) {
432                 throw new ArgumentNullException("appHost");
433             }
434             return CreateSimpleAppID(VirtualPath.Create(appHost.GetVirtualPath()),
435                                      appHost.GetPhysicalPath(), appHost.GetSiteName());
436         }
437 
438 
439         // if a "well-known" object of the specified type already exists in the application,
440         // remove the app from the managed application table.  This is
441         // used in IIS7 integrated mode when IIS7 determines that it is necessary to create
442         // a new application and shutdown the old one.
RemoveFromTableIfRuntimeExists(String appId, Type runtimeType)443         internal void RemoveFromTableIfRuntimeExists(String appId, Type runtimeType) {
444             // check args
445             if (appId == null)
446                 throw new ArgumentNullException("appId");
447             if (runtimeType == null)
448                 throw new ArgumentNullException("runtimeType");
449 
450             LockableAppDomainContext ac = GetLockableAppDomainContext(appId);
451             lock (ac) {
452                 // get hosting environment
453                 HostingEnvironment env = ac.HostEnv;
454                 if (env == null)
455                     return;
456 
457                 // find the instance by type
458                 // When marshaling Type, the AppDomain must have FileIoPermission to the assembly, which is not
459                 // always the case, so we marshal the assembly qualified name instead
460                 ObjectHandle h = env.FindWellKnownObject(runtimeType.AssemblyQualifiedName);
461                 if (h != null)
462                 {
463                     // ensure that it is removed from _appDomains by calling
464                     // HostingEnvironmentShutdownInitiated directly.
465                     HostingEnvironmentShutdownInitiated(appId, env);
466                 }
467             }
468         }
469 
470         [SecurityPermission(SecurityAction.Demand, Unrestricted = true)]
StopObject(String appId, Type type)471         public void StopObject(String appId, Type type) {
472             // check args
473             if (appId == null)
474                 throw new ArgumentNullException("appId");
475             if (type == null)
476                 throw new ArgumentNullException("type");
477 
478             LockableAppDomainContext ac = GetLockableAppDomainContext(appId);
479             lock (ac) {
480                 HostingEnvironment env = ac.HostEnv;
481                 if (env != null) {
482                     // When marshaling Type, the AppDomain must have FileIoPermission to the assembly, which is not
483                     // always the case, so we marshal the assembly qualified name instead
484                     env.StopWellKnownObject(type.AssemblyQualifiedName);
485                 }
486             }
487         }
488 
489 
IsIdle()490         public bool IsIdle() {
491             Dictionary<string, LockableAppDomainContext> apps = CloneAppDomainsCollection();
492 
493             foreach (LockableAppDomainContext ac in apps.Values) {
494                 lock (ac) {
495                     HostingEnvironment env = ac.HostEnv;
496                     bool idle = (null == env) ? true : env.IsIdle();
497 
498                     if (!idle)
499                         return false;
500                 }
501             }
502 
503             return true;
504         }
505 
506 
507         [SecurityPermission(SecurityAction.Demand, Unrestricted = true)]
ShutdownApplication(String appId)508         public void ShutdownApplication(String appId) {
509             if (appId == null)
510                 throw new ArgumentNullException("appId");
511 
512             LockableAppDomainContext ac = GetLockableAppDomainContext(appId);
513             lock (ac) {
514                 if (ac.HostEnv != null) {
515                     ac.HostEnv.InitiateShutdownInternal();
516                 }
517             }
518         }
519 
520 
521         [SecurityPermission(SecurityAction.Demand, Unrestricted = true)]
ShutdownAll()522         public void ShutdownAll() {
523             _shutdownInProgress = true;
524             Dictionary <string, LockableAppDomainContext> oldTable = null;
525 
526             lock (this) {
527                 oldTable = _appDomains;
528                 // don't keep references to hosting environments anymore
529                 _appDomains = new Dictionary<string, LockableAppDomainContext>(StringComparer.OrdinalIgnoreCase);
530             }
531 
532 
533             foreach (KeyValuePair <string, LockableAppDomainContext> p in oldTable) {
534                 LockableAppDomainContext ac = p.Value;
535                 lock (ac) {
536                     HostingEnvironment env = ac.HostEnv;
537                     if (null != env) {
538                         env.InitiateShutdownInternal();
539                     }
540                 }
541             }
542 
543             for (int iter=0; _activeHostingEnvCount > 0 && iter < 3000; iter++) // Wait at most 5 minutes
544                 Thread.Sleep(100);
545         }
546 
547 
548         [SecurityPermission(SecurityAction.Demand, Unrestricted = true)]
GetRunningApplications()549         public ApplicationInfo[] GetRunningApplications() {
550             ArrayList appList = new ArrayList();
551 
552             Dictionary<string, LockableAppDomainContext> apps = CloneAppDomainsCollection();
553 
554             foreach (LockableAppDomainContext ac in apps.Values) {
555                 lock (ac) {
556                     HostingEnvironment env = ac.HostEnv;
557                     if (env != null) {
558                         appList.Add(env.GetApplicationInfo());
559                     }
560                 }
561             }
562 
563             int n = appList.Count;
564             ApplicationInfo[] result = new ApplicationInfo[n];
565 
566             if (n > 0) {
567                 appList.CopyTo(result);
568             }
569 
570             return result;
571         }
572 
573         [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "This method fails due to serialization issues if not called by ASP.NET.")]
GetAppDomainInfos()574         internal AppDomainInfo [] GetAppDomainInfos()
575         {
576             ArrayList appList = new ArrayList();
577             Dictionary<string, LockableAppDomainContext> apps = CloneAppDomainsCollection();
578 
579             foreach (LockableAppDomainContext ac in apps.Values) {
580                 lock (ac) {
581                     HostingEnvironment hostEnv = ac.HostEnv;
582                     if (hostEnv == null) {
583                         continue;
584                     }
585 
586                     IApplicationHost appHost = hostEnv.InternalApplicationHost;
587                     ApplicationInfo appInfo = hostEnv.GetApplicationInfo();
588                     int siteId = 0;
589 
590                     if (appHost != null) {
591                         try {
592                             siteId = Int32.Parse(appHost.GetSiteID(), CultureInfo.InvariantCulture);
593                         }
594                         catch {
595                         }
596                     }
597 
598                     AppDomainInfo appDomainInfo = new AppDomainInfo(appInfo.ID,
599                                                       appInfo.VirtualPath,
600                                                       appInfo.PhysicalPath,
601                                                       siteId,
602                                                       hostEnv.GetIdleValue());
603 
604                     appList.Add(appDomainInfo);
605                 }
606             }
607 
608             return (AppDomainInfo[]) appList.ToArray(typeof(AppDomainInfo));
609         }
610 
611         //
612         // APIs for the process host to suspend / resume all running applications
613         //
614 
615         [SuppressMessage("Microsoft.Reliability", "CA2002:DoNotLockOnObjectsWithWeakIdentity", Justification = "'this' is never a MBRO proxy object.")]
SuspendAllApplications()616         internal object SuspendAllApplications() {
617             LockableAppDomainContext[] allAppDomainContexts;
618 
619             lock (this) {
620                 allAppDomainContexts = _appDomains.Values.ToArray();
621             }
622 
623             ApplicationResumeStateContainer[] resumeContainers = Task.WhenAll(allAppDomainContexts.Select(CreateSuspendTask)).Result;
624             return resumeContainers;
625         }
626 
627         private static Task<ApplicationResumeStateContainer> _dummyCompletedSuspendTask = Task.FromResult<ApplicationResumeStateContainer>(null);
CreateSuspendTask(LockableAppDomainContext appDomainContext)628         private static Task<ApplicationResumeStateContainer> CreateSuspendTask(LockableAppDomainContext appDomainContext) {
629             // dictionary contained a null entry?
630             if (appDomainContext == null) {
631                 return _dummyCompletedSuspendTask;
632             }
633 
634             HostingEnvironment hostEnv;
635             lock (appDomainContext) {
636                 hostEnv = appDomainContext.HostEnv;
637             }
638 
639             // Quick check: is this a dummy context that had no associated application?
640             if (hostEnv == null) {
641                 return _dummyCompletedSuspendTask;
642             }
643 
644             // QUWI since we want to run each application's suspend method in parallel.
645             // Unsafe since we don't care about impersonation, identity, etc.
646             // Don't use Task.Run since it captures the EC and could execute inline.
647             TaskCompletionSource<ApplicationResumeStateContainer> tcs = new TaskCompletionSource<ApplicationResumeStateContainer>();
648             ThreadPool.UnsafeQueueUserWorkItem(_ => {
649 
650                 // We're not locking on the appDomainContext here. The reason for this is two-fold:
651                 // a) We don't want to cause a potential deadlock issue whereby Suspend could kick
652                 //    off user code that tries calling InitiateShutdown and thus taking a lock on
653                 //    appDomainContext.
654                 // b) It's easier to try calling into the captured HostingEnvironment and just
655                 //    ---- the "no AD" exception than it is to try to synchronize the Suspend,
656                 //    Resume, and Stop methods. The CLR protects us from ourselves here.
657                 //
658                 // We need to use the captured 'hostEnv' to prevent null refs.
659 
660                 IntPtr state;
661                 try {
662                     state = hostEnv.SuspendApplication();
663                 }
664                 catch (AppDomainUnloadedException) {
665                     // AD unloads aren't considered a failure
666                     tcs.TrySetResult(null);
667                     return;
668                 }
669 
670                 tcs.TrySetResult(new ApplicationResumeStateContainer(hostEnv, state));
671             }, null);
672             return tcs.Task;
673         }
674 
ResumeAllApplications(object state)675         internal void ResumeAllApplications(object state) {
676             foreach (var resumeContainer in (ApplicationResumeStateContainer[])state) {
677                 if (resumeContainer != null) { // could be null if the application went away
678                     resumeContainer.Resume();
679                 }
680             }
681         }
682 
683         //
684         // ping implementation
685         //
686 
687         // called from process host
Ping(IProcessPingCallback callback)688         internal void Ping(IProcessPingCallback callback) {
689             if (callback == null || _pendingPingCallback != null)
690                 return;
691 
692             // remember active callback but only if none is remembered already
693             if (Interlocked.CompareExchange(ref _pendingPingCallback, callback, null) == null) {
694                 // queue a work item to respond to ping
695                 ThreadPool.QueueUserWorkItem(_onRespondToPingWaitCallback);
696             }
697         }
698 
699         // threadpool callback (also called on some activity from hosting environment)
OnRespondToPingWaitCallback(Object state)700         internal void OnRespondToPingWaitCallback(Object state) {
701             RespondToPingIfNeeded();
702         }
703 
704         // respond to ping on callback
RespondToPingIfNeeded()705         internal void RespondToPingIfNeeded() {
706             IProcessPingCallback callback = _pendingPingCallback as IProcessPingCallback;
707 
708             // make sure we call the callback once
709             if (callback != null) {
710                 if (Interlocked.CompareExchange(ref _pendingPingCallback, null, callback) == callback) {
711                     callback.Respond();
712                 }
713             }
714         }
715 
716         // ApplicationManager is loaded into the default AppDomain
717         // ASP.NET doesn't set string hash randomization for the defaul AppDomain, so we can assume the existing CLR implementation here is unchanged
718         // Note, it won't work if <runtime/UseRandomizedStringHashAlgorithm enabled="1"> is used, because the entire process is subject to string hash randomization
GetNonRandomizedStringComparerHashCode(string s, bool ignoreCase)719         internal int GetNonRandomizedStringComparerHashCode(string s, bool ignoreCase) {
720             StringComparer comparer = ignoreCase ? StringComparer.InvariantCultureIgnoreCase : StringComparer.InvariantCulture;
721             return comparer.GetHashCode(s);
722         }
723 
724         //
725         // communication with hosting environments
726         //
727 
HostingEnvironmentActivated()728         internal void HostingEnvironmentActivated() {
729             int count = Interlocked.Increment(ref _activeHostingEnvCount);
730         }
731 
HostingEnvironmentShutdownComplete(String appId, IApplicationHost appHost)732         internal void HostingEnvironmentShutdownComplete(String appId, IApplicationHost appHost) {
733             try {
734                 if (appHost != null) {
735                     // make sure application host can be GC'd
736                     MarshalByRefObject realApplicationHost = appHost as MarshalByRefObject;
737                     if (realApplicationHost != null) {
738                         RemotingServices.Disconnect(realApplicationHost);
739                     }
740                 }
741             }
742             finally {
743                 Interlocked.Decrement(ref _activeHostingEnvCount);
744             }
745         }
746 
HostingEnvironmentShutdownInitiated(String appId, HostingEnvironment env)747         internal void HostingEnvironmentShutdownInitiated(String appId, HostingEnvironment env) {
748             if (!_shutdownInProgress) { // don't bother during shutdown (while enumerating)
749                 LockableAppDomainContext ac = GetLockableAppDomainContext (appId);
750 
751                 lock (ac){
752                     if (!env.HasBeenRemovedFromAppManagerTable) {
753                         env.HasBeenRemovedFromAppManagerTable = true;
754 
755                         ac.HostEnv = null;
756                         Interlocked.Decrement(ref _accessibleHostingEnvCount);
757 
758                         // Autorestart the application right away
759                         if (ac.PreloadContext != null && !ac.RetryingPreload) {
760                             ProcessHost.PreloadApplicationIfNotShuttingdown(appId, ac);
761                         }
762                     }
763                 }
764             }
765         }
766 
767         internal int AppDomainsCount {
768             get { return _accessibleHostingEnvCount; }
769         }
770 
771 
ReduceAppDomainsCount(int limit)772         internal void ReduceAppDomainsCount(int limit) {
773             //
774 
775 
776 
777 
778             Dictionary<string, LockableAppDomainContext> apps = CloneAppDomainsCollection();
779             while (_accessibleHostingEnvCount >= limit && !_shutdownInProgress)
780             {
781                 LockableAppDomainContext bestCandidateForShutdown = null;
782                 int bestCandidateLruScore = 0;
783 
784                 foreach (LockableAppDomainContext ac in apps.Values) {
785                     // Don't lock on LockableAppDomainContext before we check that ac.HostEnv != null.
786                     // Otherwise we may end up with a deadlock between 2 app domains trying to unload each other
787                     HostingEnvironment h = ac.HostEnv;
788                     if (h == null) {
789                         continue;
790                     }
791                     lock (ac) {
792                         h = ac.HostEnv;
793 
794                         // Avoid ---- by checking again under lock
795                         if (h == null) {
796                             continue;
797                         }
798                         int newLruScore = h.LruScore;
799 
800                         if (bestCandidateForShutdown == null || bestCandidateForShutdown.HostEnv == null ||
801                                 newLruScore < bestCandidateLruScore) {
802 
803                             bestCandidateLruScore = newLruScore;
804                             bestCandidateForShutdown = ac;
805                         }
806                     }
807                 }
808 
809                 if (bestCandidateForShutdown == null)
810                     break;
811 
812                 lock (bestCandidateForShutdown) {
813                     if (bestCandidateForShutdown.HostEnv != null) {
814                         bestCandidateForShutdown.HostEnv.InitiateShutdownInternal();
815                     }
816                 }
817             }
818         }
819 
820         //
821         // helper to support legacy APIs (AppHost.CreateAppHost)
822         //
823 
CreateInstanceInNewWorkerAppDomain( Type type, String appId, VirtualPath virtualPath, String physicalPath)824         internal ObjectHandle CreateInstanceInNewWorkerAppDomain(
825                                 Type type,
826                                 String appId,
827                                 VirtualPath virtualPath,
828                                 String physicalPath) {
829 
830             Debug.Trace("AppManager", "CreateObjectInNewWorkerAppDomain, type=" + type.FullName);
831 
832             IApplicationHost appHost = new SimpleApplicationHost(virtualPath, physicalPath);
833 
834             HostingEnvironmentParameters hostingParameters = new HostingEnvironmentParameters();
835             hostingParameters.HostingFlags = HostingEnvironmentFlags.HideFromAppManager;
836 
837             HostingEnvironment env = CreateAppDomainWithHostingEnvironmentAndReportErrors(appId, appHost, hostingParameters);
838             // When marshaling Type, the AppDomain must have FileIoPermission to the assembly, which is not
839             // always the case, so we marshal the assembly qualified name instead
840             return env.CreateInstance(type.AssemblyQualifiedName);
841         }
842 
843         //
844         // helpers to facilitate app domain creation
845         //
GetAppDomainWithHostingEnvironment(String appId, IApplicationHost appHost, HostingEnvironmentParameters hostingParameters)846         private HostingEnvironment GetAppDomainWithHostingEnvironment(String appId, IApplicationHost appHost, HostingEnvironmentParameters hostingParameters) {
847             LockableAppDomainContext ac = GetLockableAppDomainContext (appId);
848 
849             lock (ac) {
850                 HostingEnvironment env = ac.HostEnv;
851 
852                 if (env != null) {
853                     try {
854                         env.IsUnloaded();
855                     } catch(AppDomainUnloadedException) {
856                         env = null;
857                     }
858                 }
859                 if (env == null) {
860                     env = CreateAppDomainWithHostingEnvironmentAndReportErrors(appId, appHost, hostingParameters);
861                     ac.HostEnv = env;
862                     Interlocked.Increment(ref _accessibleHostingEnvCount);
863                 }
864 
865                 return env;
866             }
867 
868         }
869 
CreateAppDomainWithHostingEnvironmentAndReportErrors( String appId, IApplicationHost appHost, HostingEnvironmentParameters hostingParameters)870         private HostingEnvironment CreateAppDomainWithHostingEnvironmentAndReportErrors(
871                                         String appId,
872                                         IApplicationHost appHost,
873                                         HostingEnvironmentParameters hostingParameters) {
874             try {
875                 return CreateAppDomainWithHostingEnvironment(appId, appHost, hostingParameters);
876             }
877             catch (Exception e) {
878                 Misc.ReportUnhandledException(e, new string[] {SR.GetString(SR.Failed_to_initialize_AppDomain), appId});
879                 throw;
880             }
881         }
882 
883         [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "We carefully control the callers.")]
884         [SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", MessageId = "System.Boolean.TryParse(System.String,System.Boolean@)", Justification = "Sets parameter to default(bool) on conversion failure, which is semantic we need.")]
CreateAppDomainWithHostingEnvironment( String appId, IApplicationHost appHost, HostingEnvironmentParameters hostingParameters)885         private HostingEnvironment CreateAppDomainWithHostingEnvironment(
886                                                 String appId,
887                                                 IApplicationHost appHost,
888                                                 HostingEnvironmentParameters hostingParameters) {
889 
890             String physicalPath = appHost.GetPhysicalPath();
891             if (!StringUtil.StringEndsWith(physicalPath, Path.DirectorySeparatorChar))
892                 physicalPath = physicalPath + Path.DirectorySeparatorChar;
893 
894             String domainId = ConstructAppDomainId(appId);
895             String appName = (StringUtil.GetStringHashCode(String.Concat(appId.ToLower(CultureInfo.InvariantCulture),
896                 physicalPath.ToLower(CultureInfo.InvariantCulture)))).ToString("x", CultureInfo.InvariantCulture);
897             VirtualPath virtualPath = VirtualPath.Create(appHost.GetVirtualPath());
898 
899             Debug.Trace("AppManager", "CreateAppDomainWithHostingEnvironment, path=" + physicalPath + "; appId=" + appId + "; domainId=" + domainId);
900 
901             IDictionary bindings = new Hashtable(20);
902             AppDomainSetup setup = new AppDomainSetup();
903             AppDomainSwitches switches = new AppDomainSwitches();
904             PopulateDomainBindings(domainId, appId, appName, physicalPath, virtualPath, setup, bindings);
905 
906             //  Create the app domain
907 
908             AppDomain appDomain = null;
909             Dictionary<string, object> appDomainAdditionalData = new Dictionary<string, object>();
910             Exception appDomainCreationException = null;
911 
912             string siteID = appHost.GetSiteID();
913             string appSegment = virtualPath.VirtualPathStringNoTrailingSlash;
914             bool inClientBuildManager = false;
915             Configuration appConfig = null;
916             PolicyLevel policyLevel = null;
917             PermissionSet permissionSet = null;
918             List<StrongName> fullTrustAssemblies = new List<StrongName>();
919             string[] defaultPartialTrustVisibleAssemblies = new[] { "System.Web, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293",
920                                                                     "System.Web.Extensions, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9",
921                                                                     "System.Web.Abstractions, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9",
922                                                                     "System.Web.Routing, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9",
923                                                                     "System.Web.DynamicData, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9",
924                                                                     "System.Web.DataVisualization, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9",
925                                                                     "System.Web.ApplicationServices, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" };
926 
927             Exception appDomainStartupConfigurationException = null;
928             ImpersonationContext ictxConfig = null;
929             IntPtr uncTokenConfig = IntPtr.Zero;
930             HostingEnvironmentFlags hostingFlags = HostingEnvironmentFlags.Default;
931             if (hostingParameters != null) {
932                 hostingFlags = hostingParameters.HostingFlags;
933                 if ((hostingFlags & HostingEnvironmentFlags.ClientBuildManager) != 0) {
934                     inClientBuildManager = true;
935                     // The default hosting policy in VS has changed (from MultiDomainHost to MultiDomain),
936                     // so we need to specify explicitly to allow generated assemblies
937                     // to be unloaded subsequently. (Dev10 bug)
938                     setup.LoaderOptimization = LoaderOptimization.MultiDomainHost;
939                 }
940             }
941             try {
942                 bool requireHostExecutionContextManager = false;
943                 bool requireHostSecurityManager = false;
944 
945                 AppDomain.CurrentDomain.SetData(_configBuildersIgnoreLoadFailuresSwitch, true);
946 
947                 uncTokenConfig = appHost.GetConfigToken();
948                 if (uncTokenConfig != IntPtr.Zero) {
949                     ictxConfig = new ImpersonationContext(uncTokenConfig);
950                 }
951 
952                 try {
953                     // Did the custom loader fail to load?
954                     ExceptionDispatchInfo customLoaderException = ProcessHost.GetExistingCustomLoaderFailureAndClear(appId);
955                     if (customLoaderException != null) {
956                         customLoaderException.Throw();
957                     }
958 
959                     // We support randomized string hash code, but not for the default AppDomain
960                     // Don't allow string hash randomization for the defaul AppDomain (i.e. <runtime/UseRandomizedStringHashAlgorithm enabled="1">)
961                     // Application should use AppSettings instead to opt-in.
962                     if (EnvironmentInfo.IsStringHashCodeRandomizationDetected) {
963                         throw new ConfigurationErrorsException(SR.GetString(SR.Require_stable_string_hash_codes));
964                     }
965 
966                     bool skipAdditionalConfigChecks = false;
967                     if (inClientBuildManager && hostingParameters.IISExpressVersion != null) {
968                         permissionSet = new PermissionSet(PermissionState.Unrestricted);
969                         setup.PartialTrustVisibleAssemblies = defaultPartialTrustVisibleAssemblies;
970                         appConfig = GetAppConfigIISExpress(siteID, appSegment, hostingParameters.IISExpressVersion);
971                         skipAdditionalConfigChecks = true;
972                     }
973                     else {
974                         //Hosted by IIS, we already have an IISMap.
975                         if (appHost is ISAPIApplicationHost) {
976                             string cacheKey = System.Web.Caching.CacheInternal.PrefixMapPath + siteID + virtualPath.VirtualPathString;
977                             MapPathCacheInfo cacheInfo = (MapPathCacheInfo)HttpRuntime.Cache.InternalCache.Remove(cacheKey);
978                             appConfig = WebConfigurationManager.OpenWebConfiguration(appSegment, siteID);
979                         }
980                         // For non-IIS hosting scenarios, we need to get config map from application host in a generic way.
981                         else {
982                             appConfig = GetAppConfigGeneric(appHost, siteID, appSegment, virtualPath, physicalPath);
983                         }
984                     }
985 
986                     HttpRuntimeSection httpRuntimeSection = (HttpRuntimeSection)appConfig.GetSection("system.web/httpRuntime");
987                     if (httpRuntimeSection == null) {
988                         throw new ConfigurationErrorsException(SR.GetString(SR.Config_section_not_present, "httpRuntime"));
989                     }
990 
991                     // DevDiv #403846 - Change certain config defaults if <httpRuntime targetFramework="4.5" /> exists in config.
992                     // We store this information in the AppDomain data because certain configuration sections (like <compilation>)
993                     // are loaded before config is "baked" in the child AppDomain, and if we make <compilation> and other sections
994                     // dependent on <httpRuntime> which may not have been loaded yet, we risk introducing ----s. Putting this value
995                     // in the AppDomain data guarantees that it is available before the first call to the config system.
996                     FrameworkName targetFrameworkName = httpRuntimeSection.GetTargetFrameworkName();
997                     if (targetFrameworkName != null) {
998                         appDomainAdditionalData[System.Web.Util.BinaryCompatibility.TargetFrameworkKey] = targetFrameworkName;
999                     }
1000 
1001                     if (!skipAdditionalConfigChecks) {
1002                         // DevDiv #71268 - Add <httpRuntime defaultRegexMatchTimeout="HH:MM:SS" /> configuration attribute
1003                         if (httpRuntimeSection.DefaultRegexMatchTimeout != TimeSpan.Zero) {
1004                             appDomainAdditionalData[_regexMatchTimeoutKey] = httpRuntimeSection.DefaultRegexMatchTimeout;
1005                         }
1006 
1007                         // DevDiv #258274 - Add support for CLR quirks mode to ASP.NET
1008                         if (targetFrameworkName != null) {
1009                             setup.TargetFrameworkName = targetFrameworkName.ToString();
1010                         }
1011 
1012                         // DevDiv #286354 - Having a Task-friendly SynchronizationContext requires overriding the AppDomain's HostExecutionContextManager.
1013                         // DevDiv #403846 - If we can't parse the <appSettings> switch, use the <httpRuntime/targetFramework> setting to determine the default.
1014                         AppSettingsSection appSettingsSection = appConfig.AppSettings;
1015                         KeyValueConfigurationElement useTaskFriendlySynchronizationContextElement = appSettingsSection.Settings["aspnet:UseTaskFriendlySynchronizationContext"];
1016                         if (!(useTaskFriendlySynchronizationContextElement != null && Boolean.TryParse(useTaskFriendlySynchronizationContextElement.Value, out requireHostExecutionContextManager))) {
1017                             requireHostExecutionContextManager = new System.Web.Util.BinaryCompatibility(targetFrameworkName).TargetsAtLeastFramework45 ? true : false;
1018                         }
1019 
1020                         // DevDiv #390704 - Add support for randomized string hash algorithm
1021                         KeyValueConfigurationElement useRandomizedStringHashAlgorithmElement = appSettingsSection.Settings["aspnet:UseRandomizedStringHashAlgorithm"];
1022                         bool useRandomizedStringHashAlgorithm = false;
1023                         if (useRandomizedStringHashAlgorithmElement != null && Boolean.TryParse(useRandomizedStringHashAlgorithmElement.Value, out useRandomizedStringHashAlgorithm)) {
1024                             switches.UseRandomizedStringHashAlgorithm = useRandomizedStringHashAlgorithm;
1025                         }
1026 
1027                         // DevDiv #1041102 - Allow specifying quirks via <appSettings> switches
1028                         // The keys must begin with "AppContext.SetSwitch" and have a non-zero key name length,
1029                         // and the values must be parseable as Booleans.
1030                         Dictionary<string, bool> clrQuirks = null;
1031                         foreach (KeyValueConfigurationElement element in appSettingsSection.Settings) {
1032                             if (element.Key != null && element.Key.Length > _clrQuirkAppSettingsAppContextPrefix.Length && element.Key.StartsWith(_clrQuirkAppSettingsAppContextPrefix, StringComparison.OrdinalIgnoreCase)) {
1033                                 bool value;
1034                                 if (Boolean.TryParse(element.Value, out value)) {
1035                                     if (clrQuirks == null) {
1036                                         clrQuirks = new Dictionary<string, bool>();
1037                                     }
1038 
1039                                     clrQuirks[element.Key.Substring(_clrQuirkAppSettingsAppContextPrefix.Length)] = value;
1040                                 }
1041                             }
1042                         }
1043 
1044                         if (clrQuirks != null && clrQuirks.Count > 0) {
1045                             if (hostingParameters == null) {
1046                                 hostingParameters = new HostingEnvironmentParameters();
1047                             }
1048 
1049                             hostingParameters.ClrQuirksSwitches = clrQuirks.ToArray();
1050                         }
1051 
1052                         // DevDiv #248126 - Allow configuration of FileChangeMonitor behavior
1053                         if (httpRuntimeSection.FcnMode != FcnMode.NotSet) {
1054                             if (hostingParameters == null) {
1055                                 hostingParameters = new HostingEnvironmentParameters();
1056                             }
1057                             hostingParameters.FcnMode = httpRuntimeSection.FcnMode;
1058                         }
1059 
1060                         // DevDiv #322858 - Allow FileChangesMonitor to skip reading DACLs as a perf improvement
1061                         KeyValueConfigurationElement disableFcnDaclReadElement = appSettingsSection.Settings["aspnet:DisableFcnDaclRead"];
1062                         if (disableFcnDaclReadElement != null) {
1063                             bool skipReadingAndCachingDacls;
1064                             Boolean.TryParse(disableFcnDaclReadElement.Value, out skipReadingAndCachingDacls);
1065                             if (skipReadingAndCachingDacls) {
1066                                 if (hostingParameters == null) {
1067                                     hostingParameters = new HostingEnvironmentParameters();
1068                                 }
1069                                 hostingParameters.FcnSkipReadAndCacheDacls = true;
1070                             }
1071                         }
1072 
1073                         // Allow apps to use their own CacheStoreProvider implementations
1074                         CacheSection cacheConfig = (CacheSection)appConfig.GetSection("system.web/caching/cache");
1075                         if (cacheConfig != null && cacheConfig.DefaultProvider != null && !String.IsNullOrWhiteSpace(cacheConfig.DefaultProvider)) {
1076                             ProviderSettingsCollection cacheProviders = cacheConfig.Providers;
1077                             if (cacheProviders == null || cacheProviders.Count < 1) {
1078                                 throw new ProviderException(SR.GetString(SR.Def_provider_not_found));
1079                             }
1080 
1081                             ProviderSettings cacheProviderSettings = cacheProviders[cacheConfig.DefaultProvider];
1082                             if (cacheProviderSettings == null) {
1083                                 throw new ProviderException(SR.GetString(SR.Def_provider_not_found));
1084                             } else {
1085                                 NameValueCollection settings = cacheProviderSettings.Parameters;
1086                                 settings["name"] = cacheProviderSettings.Name;
1087                                 settings["type"] = cacheProviderSettings.Type;
1088                                 appDomainAdditionalData[".defaultObjectCacheProvider"] = settings;
1089                             }
1090                         }
1091 
1092                         // If we were launched from a development environment, we might want to enable the application to do things
1093                         // it otherwise wouldn't normally allow, such as enabling an administrative control panel. For security reasons,
1094                         // we only do this check if <deployment retail="false" /> [the default value] is specified, since the
1095                         // <deployment> element can only be set at machine-level in a hosted environment.
1096                         DeploymentSection deploymentSection = (DeploymentSection)appConfig.GetSection("system.web/deployment");
1097                         bool isDevEnvironment = false;
1098                         if (deploymentSection != null && !deploymentSection.Retail && EnvironmentInfo.WasLaunchedFromDevelopmentEnvironment) {
1099                             appDomainAdditionalData[".devEnvironment"] = true;
1100                             isDevEnvironment = true;
1101 
1102                             // DevDiv #275724 - Allow LocalDB support in partial trust scenarios
1103                             // Normally LocalDB requires full trust since it's the equivalent of unmanaged code execution. If this is
1104                             // a development environment and not a retail deployment, we can assume that the user developing the
1105                             // application is actually in charge of the host, so we can trust him with LocalDB execution.
1106                             // Technically this also means that the developer could have set <trust level="Full" /> in his application,
1107                             // but he might want to deploy his application on a Medium-trust server and thus test how the rest of his
1108                             // application works in a partial trust environment. (He would use SQL in production, whch is safe in
1109                             // partial trust.)
1110                             appDomainAdditionalData["ALLOW_LOCALDB_IN_PARTIAL_TRUST"] = true;
1111                         }
1112 
1113                         TrustSection trustSection = (TrustSection)appConfig.GetSection("system.web/trust");
1114                         if (trustSection == null || String.IsNullOrEmpty(trustSection.Level)) {
1115                             throw new ConfigurationErrorsException(SR.GetString(SR.Config_section_not_present, "trust"));
1116                         }
1117 
1118                         switches.UseLegacyCas = trustSection.LegacyCasModel;
1119 
1120                         if (inClientBuildManager) {
1121                             permissionSet = new PermissionSet(PermissionState.Unrestricted);
1122                             setup.PartialTrustVisibleAssemblies = defaultPartialTrustVisibleAssemblies;
1123                         }
1124                         else {
1125                             if (!switches.UseLegacyCas) {
1126                                 if (trustSection.Level == "Full") {
1127                                     permissionSet = new PermissionSet(PermissionState.Unrestricted);
1128                                     setup.PartialTrustVisibleAssemblies = defaultPartialTrustVisibleAssemblies;
1129                                 }
1130                                 else {
1131                                     SecurityPolicySection securityPolicySection = (SecurityPolicySection)appConfig.GetSection("system.web/securityPolicy");
1132                                     CompilationSection compilationSection = (CompilationSection)appConfig.GetSection("system.web/compilation");
1133                                     FullTrustAssembliesSection fullTrustAssembliesSection = (FullTrustAssembliesSection)appConfig.GetSection("system.web/fullTrustAssemblies");
1134                                     policyLevel = GetPartialTrustPolicyLevel(trustSection, securityPolicySection, compilationSection, physicalPath, virtualPath, isDevEnvironment);
1135                                     permissionSet = policyLevel.GetNamedPermissionSet(trustSection.PermissionSetName);
1136                                     if (permissionSet == null) {
1137                                         throw new ConfigurationErrorsException(SR.GetString(SR.Permission_set_not_found, trustSection.PermissionSetName));
1138                                     }
1139 
1140                                     // read full trust assemblies and populate the strong name list
1141                                     if (fullTrustAssembliesSection != null) {
1142                                         FullTrustAssemblyCollection fullTrustAssembliesCollection = fullTrustAssembliesSection.FullTrustAssemblies;
1143                                         if (fullTrustAssembliesCollection != null) {
1144                                             fullTrustAssemblies.AddRange(from FullTrustAssembly fta in fullTrustAssembliesCollection
1145                                                                          select CreateStrongName(fta.AssemblyName, fta.Version, fta.PublicKey));
1146                                         }
1147                                     }
1148 
1149                                     // DevDiv #27645 - We need to add future versions of Microsoft.Web.Infrastructure to <fullTrustAssemblies> so that ASP.NET
1150                                     // can version out-of-band releases. We should only do this if V1 of M.W.I is listed.
1151                                     if (fullTrustAssemblies.Contains(_mwiV1StrongName)) {
1152                                         fullTrustAssemblies.AddRange(CreateFutureMicrosoftWebInfrastructureStrongNames());
1153                                     }
1154 
1155                                     // Partial-trust AppDomains using a non-legacy CAS model require our special HostSecurityManager
1156                                     requireHostSecurityManager = true;
1157                                 }
1158                             }
1159                             if (trustSection.Level != "Full") {
1160                                 PartialTrustVisibleAssembliesSection partialTrustVisibleAssembliesSection = (PartialTrustVisibleAssembliesSection)appConfig.GetSection("system.web/partialTrustVisibleAssemblies");
1161                                 string[] partialTrustVisibleAssemblies = null;
1162                                 if (partialTrustVisibleAssembliesSection != null) {
1163                                     PartialTrustVisibleAssemblyCollection partialTrustVisibleAssembliesCollection = partialTrustVisibleAssembliesSection.PartialTrustVisibleAssemblies;
1164                                     if (partialTrustVisibleAssembliesCollection != null && partialTrustVisibleAssembliesCollection.Count != 0) {
1165                                         partialTrustVisibleAssemblies = new string[partialTrustVisibleAssembliesCollection.Count + defaultPartialTrustVisibleAssemblies.Length];
1166                                         for (int i = 0; i < partialTrustVisibleAssembliesCollection.Count; i++) {
1167                                             partialTrustVisibleAssemblies[i] = partialTrustVisibleAssembliesCollection[i].AssemblyName +
1168                                                 ", PublicKey=" +
1169                                                 NormalizePublicKeyBlob(partialTrustVisibleAssembliesCollection[i].PublicKey);
1170                                         }
1171                                         defaultPartialTrustVisibleAssemblies.CopyTo(partialTrustVisibleAssemblies, partialTrustVisibleAssembliesCollection.Count);
1172                                     }
1173                                 }
1174                                 if (partialTrustVisibleAssemblies == null) {
1175                                     partialTrustVisibleAssemblies = defaultPartialTrustVisibleAssemblies;
1176                                 }
1177                                 setup.PartialTrustVisibleAssemblies = partialTrustVisibleAssemblies;
1178                             }
1179                         }
1180                     }
1181                 }
1182                 catch (Exception e) {
1183                     appDomainStartupConfigurationException = e;
1184                     permissionSet = new PermissionSet(PermissionState.Unrestricted);
1185                 }
1186 
1187                 // Set the AppDomainManager if needed
1188                 Type appDomainManagerType = AspNetAppDomainManager.GetAspNetAppDomainManagerType(requireHostExecutionContextManager, requireHostSecurityManager);
1189                 if (appDomainManagerType != null) {
1190                     setup.AppDomainManagerType = appDomainManagerType.FullName;
1191                     setup.AppDomainManagerAssembly = appDomainManagerType.Assembly.FullName;
1192                 }
1193 
1194                 // Apply compatibility switches
1195                 switches.Apply(setup);
1196 
1197                 try {
1198                     if (switches.UseLegacyCas) {
1199                         appDomain = AppDomain.CreateDomain(domainId,
1200 #if FEATURE_PAL // FEATURE_PAL: hack to avoid non-supported hosting features
1201                                                            null,
1202 #else // FEATURE_PAL
1203 GetDefaultDomainIdentity(),
1204 #endif // FEATURE_PAL
1205 setup);
1206                     }
1207                     else {
1208                         appDomain = AppDomain.CreateDomain(domainId,
1209 #if FEATURE_PAL // FEATURE_PAL: hack to avoid non-supported hosting features
1210                                                            null,
1211 #else // FEATURE_PAL
1212 GetDefaultDomainIdentity(),
1213 #endif // FEATURE_PAL
1214 setup,
1215                                                            permissionSet,
1216                                                            fullTrustAssemblies.ToArray() /* fully trusted assemblies list: empty list means only trust GAC assemblies */);
1217                     }
1218                     foreach (DictionaryEntry e in bindings)
1219                         appDomain.SetData((String)e.Key, (String)e.Value);
1220                     foreach (var entry in appDomainAdditionalData)
1221                         appDomain.SetData(entry.Key, entry.Value);
1222                 }
1223                 catch (Exception e) {
1224                     Debug.Trace("AppManager", "AppDomain.CreateDomain failed", e);
1225                     appDomainCreationException = e;
1226                 }
1227             }
1228             finally {
1229                 if (ictxConfig != null) {
1230                     ictxConfig.Undo();
1231                     ictxConfig = null;
1232                 }
1233                 if (uncTokenConfig != IntPtr.Zero) {
1234                     UnsafeNativeMethods.CloseHandle(uncTokenConfig);
1235                     uncTokenConfig = IntPtr.Zero;
1236                 }
1237             }
1238 
1239             if (appDomain == null) {
1240                 throw new SystemException(SR.GetString(SR.Cannot_create_AppDomain), appDomainCreationException);
1241             }
1242 
1243             // Create hosting environment in the new app domain
1244 
1245             Type hostType = typeof(HostingEnvironment);
1246             String module = hostType.Module.Assembly.FullName;
1247             String typeName = hostType.FullName;
1248             ObjectHandle h = null;
1249 
1250             // impersonate UNC identity, if any
1251             ImpersonationContext ictx = null;
1252             IntPtr uncToken = IntPtr.Zero;
1253 
1254             //
1255             // fetching config can fail due to a ---- with the
1256             // native config reader
1257             // if that has happened, force a flush
1258             //
1259             int maxRetries = 10;
1260             int numRetries = 0;
1261 
1262             while (numRetries < maxRetries) {
1263                 try {
1264                     uncToken = appHost.GetConfigToken();
1265                     // no throw, so break
1266                     break;
1267                 }
1268                 catch (InvalidOperationException) {
1269                     numRetries++;
1270                     System.Threading.Thread.Sleep(250);
1271                 }
1272             }
1273 
1274 
1275             if (uncToken != IntPtr.Zero) {
1276                 try {
1277                     ictx = new ImpersonationContext(uncToken);
1278                 }
1279                 catch {
1280                 }
1281                 finally {
1282                     UnsafeNativeMethods.CloseHandle(uncToken);
1283                 }
1284             }
1285 
1286             try {
1287 
1288                 // Create the hosting environment in the app domain
1289 #if DBG
1290                 try {
1291                     h = Activator.CreateInstance(appDomain, module, typeName);
1292                 }
1293                 catch (Exception e) {
1294                     Debug.Trace("AppManager", "appDomain.CreateInstance failed; identity=" + System.Security.Principal.WindowsIdentity.GetCurrent().Name, e);
1295                     throw;
1296                 }
1297 #else
1298                 h = Activator.CreateInstance(appDomain, module, typeName);
1299 #endif
1300             }
1301             finally {
1302                 // revert impersonation
1303                 if (ictx != null)
1304                     ictx.Undo();
1305 
1306                 if (h == null) {
1307                     AppDomain.Unload(appDomain);
1308                 }
1309             }
1310 
1311             HostingEnvironment env = (h != null) ? h.Unwrap() as HostingEnvironment : null;
1312 
1313             if (env == null)
1314                 throw new SystemException(SR.GetString(SR.Cannot_create_HostEnv));
1315 
1316             // initialize the hosting environment
1317             IConfigMapPathFactory configMapPathFactory = appHost.GetConfigMapPathFactory();
1318             if (appDomainStartupConfigurationException == null) {
1319                 env.Initialize(this, appHost, configMapPathFactory, hostingParameters, policyLevel);
1320             }
1321             else {
1322                 env.Initialize(this, appHost, configMapPathFactory, hostingParameters, policyLevel, appDomainStartupConfigurationException);
1323             }
1324             return env;
1325         }
1326 
NormalizePublicKeyBlob(string publicKey)1327         private static string NormalizePublicKeyBlob(string publicKey) {
1328             StringBuilder sb = new StringBuilder();
1329             for (int i = 0; i < publicKey.Length; i++) {
1330                 if (!Char.IsWhiteSpace(publicKey[i])) {
1331                     sb.Append(publicKey[i]);
1332                 }
1333             }
1334             publicKey = sb.ToString();
1335             return publicKey;
1336         }
1337 
CreateStrongName(string assemblyName, string version, string publicKeyString)1338         private static StrongName CreateStrongName(string assemblyName, string version, string publicKeyString) {
1339             byte[] publicKey = null;
1340             StrongName strongName = null;
1341             publicKeyString = NormalizePublicKeyBlob(publicKeyString);
1342             int publicKeySize = publicKeyString.Length / 2;
1343             publicKey = new byte[publicKeySize];
1344             for (int i = 0; i < publicKeySize; i++) {
1345                 publicKey[i] = Byte.Parse(publicKeyString.Substring(i * 2, 2), System.Globalization.NumberStyles.HexNumber, CultureInfo.InvariantCulture);
1346             }
1347             StrongNamePublicKeyBlob keyBlob = new StrongNamePublicKeyBlob(publicKey);
1348             strongName = new StrongName(keyBlob, assemblyName, new Version(version));
1349             return strongName;
1350         }
1351 
1352         // For various reasons, we can't add any entries to the <fullTrustAssemblies> list until .NET 5, which at the time of this writing is a few
1353         // years out. But since ASP.NET releases projects out-of-band from the .NET Framework as a whole (using Microsoft.Web.Infrasturcture), we
1354         // need to be sure that future versions of M.W.I have the ability to assert full trust. This code works by seeing if M.W.I v1 is in the
1355         // <fullTrustAssemblies> list, and if it is then v2 - v10 are implicitly added. If v1 is not present in the <fTA> list, then we'll not
1356         // treat v2 - v10 as implicitly added. See DevDiv #27645 for more information.
1357 
GetMicrosoftWebInfrastructureV1StrongName()1358         private static StrongName GetMicrosoftWebInfrastructureV1StrongName() {
1359             return CreateStrongName(
1360                     assemblyName: "Microsoft.Web.Infrastructure",
1361                     version: "1.0.0.0",
1362                     publicKeyString: "0024000004800000940000000602000000240000525341310004000001000100B5FC90E7027F67871E773A8FDE8938C81DD402BA65B9201D60593E96C492651E889CC13F1415EBB53FAC1131AE0BD333C5EE6021672D9718EA31A8AEBD0DA0072F25D87DBA6FC90FFD598ED4DA35E44C398C454307E8E33B8426143DAEC9F596836F97C8F74750E5975C64E2189F45DEF46B2A2B1247ADC3652BF5C308055DA9");
1363         }
1364 
CreateFutureMicrosoftWebInfrastructureStrongNames()1365         private static IEnumerable<StrongName> CreateFutureMicrosoftWebInfrastructureStrongNames() {
1366             string asmName = _mwiV1StrongName.Name;
1367             StrongNamePublicKeyBlob publicKey = _mwiV1StrongName.PublicKey;
1368             for (int i = 2; i <= 10; i++) {
1369                 yield return new StrongName(publicKey, asmName, new Version(i, 0, 0, 0));
1370             }
1371         }
1372 
1373 
1374          // devdiv 1038337: execution permission cannot be acquired under partial trust with ctrl-f5.
1375          // We build the codegen path in default domain. In order to build the right path,
1376          // caller must pass in a flag indicating whether it is under dev environment.
1377         [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "We carefully control this method's caller.")]
GetPartialTrustPolicyLevel( TrustSection trustSection, SecurityPolicySection securityPolicySection, CompilationSection compilationSection, string physicalPath, VirtualPath virtualPath, bool isDevEnvironment)1378         private static PolicyLevel GetPartialTrustPolicyLevel(
1379                 TrustSection trustSection, SecurityPolicySection securityPolicySection,
1380                 CompilationSection compilationSection, string physicalPath, VirtualPath virtualPath, bool isDevEnvironment) {
1381             if (securityPolicySection == null || securityPolicySection.TrustLevels[trustSection.Level] == null) {
1382                 throw new ConfigurationErrorsException(SR.GetString(SR.Unable_to_get_policy_file, trustSection.Level), String.Empty, 0);
1383             }
1384             String configFile = (String)securityPolicySection.TrustLevels[trustSection.Level].PolicyFileExpanded;
1385             if (configFile == null || !FileUtil.FileExists(configFile)) {
1386                 throw new HttpException(SR.GetString(SR.Unable_to_get_policy_file, trustSection.Level));
1387             }
1388             PolicyLevel policyLevel = null;
1389             String appDir = FileUtil.RemoveTrailingDirectoryBackSlash(physicalPath);
1390             String appDirUrl = HttpRuntime.MakeFileUrl(appDir);
1391 
1392             // setup $CodeGen$ replacement
1393             string tempDirectory = null;
1394             // These variables are used for error handling
1395             string tempDirAttribName = null;
1396             string configFileName = null;
1397             int configLineNumber = 0;
1398             if (compilationSection != null && !String.IsNullOrEmpty(compilationSection.TempDirectory)) {
1399                 tempDirectory = compilationSection.TempDirectory;
1400                 compilationSection.GetTempDirectoryErrorInfo(out tempDirAttribName,
1401                     out configFileName, out configLineNumber);
1402             }
1403             if (tempDirectory != null) {
1404                 tempDirectory = tempDirectory.Trim();
1405                 if (!Path.IsPathRooted(tempDirectory)) {
1406                     // Make sure the path is not relative (VSWhidbey 260075)
1407                     tempDirectory = null;
1408                 }
1409                 else {
1410                     try {
1411                         // Canonicalize it to avoid problems with spaces (VSWhidbey 229873)
1412                         tempDirectory = new DirectoryInfo(tempDirectory).FullName;
1413                     }
1414                     catch {
1415                         tempDirectory = null;
1416                     }
1417                 }
1418                 if (tempDirectory == null) {
1419                     throw new ConfigurationErrorsException(
1420                         SR.GetString(SR.Invalid_temp_directory, tempDirAttribName),
1421                         configFileName, configLineNumber);
1422                 }
1423                 // Create the config-specified directory if needed
1424                 try {
1425                     Directory.CreateDirectory(tempDirectory);
1426                 }
1427                 catch (Exception e) {
1428                     throw new ConfigurationErrorsException(
1429                         SR.GetString(SR.Invalid_temp_directory, tempDirAttribName),
1430                         e,
1431                         configFileName, configLineNumber);
1432                 }
1433             }
1434             else {
1435                 tempDirectory = Path.Combine(RuntimeEnvironment.GetRuntimeDirectory(), HttpRuntime.codegenDirName);
1436             }
1437             // If we don't have write access to the codegen dir, use the TEMP dir instead.
1438             // This will allow non-admin users to work in hosting scenarios (e.g. Venus, aspnet_compiler)
1439             if (!System.Web.UI.Util.HasWriteAccessToDirectory(tempDirectory)) {
1440                 // Don't do this if we're in a service (!UserInteractive), as TEMP
1441                 // could point to unwanted places.
1442                 if (!Environment.UserInteractive) {
1443                     throw new HttpException(SR.GetString(SR.No_codegen_access,
1444                         System.Web.UI.Util.GetCurrentAccountName(), tempDirectory));
1445                 }
1446                 tempDirectory = Path.GetTempPath();
1447                 Debug.Assert(System.Web.UI.Util.HasWriteAccessToDirectory(tempDirectory));
1448                 tempDirectory = Path.Combine(tempDirectory, HttpRuntime.codegenDirName);
1449             }
1450             String simpleAppName = System.Web.Hosting.AppManagerAppDomainFactory.ConstructSimpleAppName(
1451                 VirtualPath.GetVirtualPathStringNoTrailingSlash(virtualPath), isDevEnvironment);
1452             String binDir = Path.Combine(tempDirectory, simpleAppName);
1453             binDir = FileUtil.RemoveTrailingDirectoryBackSlash(binDir);
1454             String binDirUrl = HttpRuntime.MakeFileUrl(binDir);
1455 
1456             String originUrl = trustSection.OriginUrl;
1457             FileStream file = new FileStream(configFile, FileMode.Open, FileAccess.Read);
1458             StreamReader reader = new StreamReader(file, Encoding.UTF8);
1459             String strFileData = reader.ReadToEnd();
1460             reader.Close();
1461             strFileData = strFileData.Replace("$AppDir$", appDir);
1462             strFileData = strFileData.Replace("$AppDirUrl$", appDirUrl);
1463             strFileData = strFileData.Replace("$CodeGen$", binDirUrl);
1464             if (originUrl == null)
1465                 originUrl = String.Empty;
1466             strFileData = strFileData.Replace("$OriginHost$", originUrl);
1467             String gacLocation = null;
1468             if (strFileData.IndexOf("$Gac$", StringComparison.Ordinal) != -1) {
1469                 gacLocation = HttpRuntime.GetGacLocation();
1470                 if (gacLocation != null)
1471                     gacLocation = HttpRuntime.MakeFileUrl(gacLocation);
1472                 if (gacLocation == null)
1473                     gacLocation = String.Empty;
1474                 strFileData = strFileData.Replace("$Gac$", gacLocation);
1475             }
1476 #pragma warning disable 618 // ASP is reading their grant set out of legacy policy level files
1477             policyLevel = SecurityManager.LoadPolicyLevelFromString(strFileData, PolicyLevelType.AppDomain);
1478             if (policyLevel == null) {
1479                 throw new ConfigurationErrorsException(SR.GetString(SR.Unable_to_get_policy_file, trustSection.Level));
1480             }
1481             // Found GAC Token
1482             if (gacLocation != null) {
1483                 // walk the code groups at the app domain level and look for one that grants
1484                 // access to the GAC with an UrlMembershipCondition.
1485                 CodeGroup rootGroup = policyLevel.RootCodeGroup;
1486                 bool foundGacCondition = false;
1487                 foreach (CodeGroup childGroup in rootGroup.Children) {
1488                     if (childGroup.MembershipCondition is GacMembershipCondition) {
1489                         foundGacCondition = true;
1490                         // if we found the GAC token and also have the GacMembershipCondition
1491                         // the policy file needs to be upgraded to just include the GacMembershipCondition
1492                         Debug.Assert(!foundGacCondition);
1493                         break;
1494                     }
1495                 }
1496                 // add one as a child of the toplevel group after
1497                 // some sanity checking to make sure it's an ASP.NET policy file
1498                 // which always begins with a FirstMatchCodeGroup granting nothing
1499                 // this might not upgrade some custom policy files
1500                 if (!foundGacCondition) {
1501                     if (rootGroup is FirstMatchCodeGroup) {
1502                         FirstMatchCodeGroup firstMatch = (FirstMatchCodeGroup)rootGroup;
1503                         if (firstMatch.MembershipCondition is AllMembershipCondition &&
1504                             firstMatch.PermissionSetName == "Nothing") {
1505                             PermissionSet fullTrust = new PermissionSet(PermissionState.Unrestricted);
1506                             CodeGroup gacGroup = new UnionCodeGroup(new GacMembershipCondition(),
1507                                                                     new PolicyStatement(fullTrust));
1508                             // now, walk the current groups and insert our new group immediately before the old Gac group
1509                             // we'll need to use heuristics for this: it will be an UrlMembershipCondition group with full trust
1510                             CodeGroup newRoot = new FirstMatchCodeGroup(rootGroup.MembershipCondition, rootGroup.PolicyStatement);
1511                             foreach (CodeGroup childGroup in rootGroup.Children) {
1512                                 // is this the target old $Gac$ group?
1513                                 // insert our new GacMembershipCondition group ahead of it
1514                                 if ((childGroup is UnionCodeGroup) &&
1515                                     (childGroup.MembershipCondition is UrlMembershipCondition) &&
1516                                     childGroup.PolicyStatement.PermissionSet.IsUnrestricted()) {
1517                                     if (null != gacGroup) {
1518                                         newRoot.AddChild(gacGroup);
1519                                         gacGroup = null;
1520                                     }
1521                                 }
1522                                 // append this group to the root group
1523                                 // AddChild itself does a deep Copy to get any
1524                                 // child groups so we don't need one here
1525                                 newRoot.AddChild(childGroup);
1526                             }
1527                             policyLevel.RootCodeGroup = newRoot;
1528                         }
1529                     }
1530                 }
1531             }
1532             return policyLevel;
1533 #pragma warning restore 618
1534         }
1535 
1536         private sealed class ApplicationResumeStateContainer {
1537             private static readonly WaitCallback _tpCallback = ResumeCallback;
1538 
1539             private readonly HostingEnvironment _hostEnv;
1540             private readonly IntPtr _resumeState;
1541 
ApplicationResumeStateContainer(HostingEnvironment hostEnv, IntPtr resumeState)1542             internal ApplicationResumeStateContainer(HostingEnvironment hostEnv, IntPtr resumeState) {
1543                 _hostEnv = hostEnv;
1544                 _resumeState = resumeState;
1545             }
1546 
1547             // schedules resume for execution on a new thread
1548             // unsafe since we don't care about impersonation, identity, etc.
Resume()1549             internal void Resume() {
1550                 ThreadPool.UnsafeQueueUserWorkItem(_tpCallback, this);
1551             }
1552 
ResumeCallback(object state)1553             private static void ResumeCallback(object state) {
1554                 ApplicationResumeStateContainer container = (ApplicationResumeStateContainer)state;
1555                 try {
1556                     container._hostEnv.ResumeApplication(container._resumeState);
1557                 }
1558                 catch (AppDomainUnloadedException) {
1559                     // AD unloads aren't considered a failure, ----
1560                 }
1561             }
1562         }
1563 
1564         [SecurityPermission(SecurityAction.LinkDemand, Unrestricted = true)]
1565         private static class AspNetAppDomainManager {
GetAspNetAppDomainManagerType(bool overrideHostExecutionContextManager, bool overrideHostSecurityManager)1566             internal static Type GetAspNetAppDomainManagerType(bool overrideHostExecutionContextManager, bool overrideHostSecurityManager) {
1567                 if (!overrideHostExecutionContextManager && !overrideHostSecurityManager) {
1568                     // A custom AppDomainManager isn't necessary for this AppDomain.
1569                     return null;
1570                 }
1571                 else {
1572                     // A custom AppDomainManager is necessary for this AppDomain.
1573                     // See comment on the generic type for further information.
1574                     Type openGenericType = typeof(AspNetAppDomainManagerImpl<,>);
1575                     Type closedGenericType = openGenericType.MakeGenericType(
1576                         (overrideHostExecutionContextManager) ? typeof(AspNetHostExecutionContextManager) : typeof(object),
1577                         (overrideHostSecurityManager) ? typeof(AspNetHostSecurityManager) : typeof(object)
1578                         );
1579                     return closedGenericType;
1580                 }
1581             }
1582 
1583             // This AppDomainManager may have been set because we need it for Task support, because this is a partial-trust
1584             // AppDomain, or both. Normally we would store this data in the AppDomain's ambient data store (AppDomain.SetData),
1585             // but the AppDomainManager instance is initialized in the new AppDomain before the original AppDomain has a chance to
1586             // call SetData, so in this AppDomain the call to GetData is useless. However, we can use the AppDomainManager type
1587             // itself to carry the necessary information in the form of a generic type parameter. If a custom
1588             // HostExecutionContextManager or HostSecurityManager is necessary, the generic type parameters can tell us that.
1589             // A generic type parameter of "object" means that this particular sub-manager isn't necessary.
1590             private sealed class AspNetAppDomainManagerImpl<THostExecutionContextManager, THostSecurityManager> : AppDomainManager
1591                 where THostExecutionContextManager : class, new()
1592                 where THostSecurityManager : class, new() {
1593 
1594                 private readonly HostExecutionContextManager _hostExecutionContextManager = CreateHostExecutionContextManager();
1595                 private readonly HostSecurityManager _hostSecurityManager = CreateHostSecurityManager();
1596 
1597                 public override HostExecutionContextManager HostExecutionContextManager {
1598                     get {
1599                         return _hostExecutionContextManager ?? base.HostExecutionContextManager;
1600                     }
1601                 }
1602 
1603                 public override HostSecurityManager HostSecurityManager {
1604                     get {
1605                         return _hostSecurityManager ?? base.HostSecurityManager;
1606                     }
1607                 }
1608 
CreateHostExecutionContextManager()1609                 private static HostExecutionContextManager CreateHostExecutionContextManager() {
1610                     object hostExecutionContextManager = new THostExecutionContextManager();
1611                     Debug.Assert(hostExecutionContextManager is HostExecutionContextManager || hostExecutionContextManager.GetType() == typeof(object), "THostExecutionContextManager was an unexpected type!");
1612                     return hostExecutionContextManager as HostExecutionContextManager;
1613                 }
1614 
CreateHostSecurityManager()1615                 private static HostSecurityManager CreateHostSecurityManager() {
1616                     object hostSecurityManager = new THostSecurityManager();
1617                     Debug.Assert(hostSecurityManager is HostSecurityManager || hostSecurityManager.GetType() == typeof(object), "THostSecurityManager was an unexpected type!");
1618                     return hostSecurityManager as HostSecurityManager;
1619                 }
1620             }
1621 
1622             private sealed class AspNetHostSecurityManager : HostSecurityManager {
1623                 private PermissionSet Nothing = new PermissionSet(PermissionState.None);
1624                 private PermissionSet FullTrust = new PermissionSet(PermissionState.Unrestricted);
1625                 private HostSecurityPolicyResolver hostSecurityPolicyResolver = null;
1626 
1627                 public override HostSecurityManagerOptions Flags {
1628                     get {
1629                         return HostSecurityManagerOptions.HostResolvePolicy;
1630                     }
1631                 }
1632 
1633                 [SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.SerializationFormatter)]
ResolvePolicy(Evidence evidence)1634                 public override PermissionSet ResolvePolicy(Evidence evidence) {
1635                     if (base.ResolvePolicy(evidence).IsUnrestricted()) {
1636                         return FullTrust;
1637                     }
1638 
1639                     if (!String.IsNullOrEmpty(HttpRuntime.HostSecurityPolicyResolverType) && hostSecurityPolicyResolver == null) {
1640                         hostSecurityPolicyResolver = Activator.CreateInstance(
1641                             Type.GetType(HttpRuntime.HostSecurityPolicyResolverType)) as HostSecurityPolicyResolver;
1642                     }
1643 
1644                     if (hostSecurityPolicyResolver != null) {
1645                         switch (hostSecurityPolicyResolver.ResolvePolicy(evidence)) {
1646                             case HostSecurityPolicyResults.FullTrust:
1647                                 return FullTrust;
1648                             case HostSecurityPolicyResults.AppDomainTrust:
1649                                 return HttpRuntime.NamedPermissionSet;
1650                             case HostSecurityPolicyResults.Nothing:
1651                                 return Nothing;
1652                             case HostSecurityPolicyResults.DefaultPolicy:
1653                                 break;
1654                         }
1655                     }
1656 
1657                     if (HttpRuntime.PolicyLevel == null || HttpRuntime.PolicyLevel.Resolve(evidence).PermissionSet.IsUnrestricted())
1658                         return FullTrust;
1659                     else if (HttpRuntime.PolicyLevel.Resolve(evidence).PermissionSet.Equals(Nothing))
1660                         return Nothing;
1661                     else
1662                         return HttpRuntime.NamedPermissionSet;
1663                 }
1664             }
1665         }
1666 
PopulateDomainBindings(String domainId, String appId, String appName, String appPath, VirtualPath appVPath, AppDomainSetup setup, IDictionary dict)1667         private static void PopulateDomainBindings(String domainId, String appId, String appName,
1668                                                     String appPath, VirtualPath appVPath,
1669                                                     AppDomainSetup setup, IDictionary dict) {
1670             // assembly loading settings
1671 
1672             // We put both the old and new bin dir names on the private bin path
1673             setup.PrivateBinPathProbe   = "*";  // disable loading from app base
1674             setup.ShadowCopyFiles       = "true";
1675             setup.ApplicationBase       = appPath;
1676             setup.ApplicationName       = appName;
1677             setup.ConfigurationFile     = HttpConfigurationSystem.WebConfigFileName;
1678 
1679             // Disallow code download, since it's unreliable in services (ASURT 123836/127606)
1680             setup.DisallowCodeDownload  = true;
1681 
1682             // internal settings
1683             dict.Add(".appDomain",     "*");
1684             dict.Add(".appId",         appId);
1685             dict.Add(".appPath",       appPath);
1686             dict.Add(".appVPath",      appVPath.VirtualPathString);
1687             dict.Add(".domainId",      domainId);
1688         }
1689 
GetDefaultDomainIdentity()1690         private static Evidence GetDefaultDomainIdentity() {
1691             Evidence evidence = AppDomain.CurrentDomain.Evidence; // CurrentDomain.Evidence returns a clone so we can modify it if we need
1692             bool hasZone = evidence.GetHostEvidence<Zone>() != null;
1693             bool hasUrl = evidence.GetHostEvidence<Url>() != null;
1694 
1695             if (!hasZone)
1696                 evidence.AddHostEvidence(new Zone(SecurityZone.MyComputer));
1697 
1698             if (!hasUrl)
1699                 evidence.AddHostEvidence(new Url("ms-internal-microsoft-asp-net-webhost-20"));
1700 
1701             return evidence;
1702         }
1703 
1704         private static int s_domainCount = 0;
1705         private static Object s_domainCountLock = new Object();
1706 
ConstructAppDomainId(String id)1707         private static String ConstructAppDomainId(String id) {
1708             int domainCount = 0;
1709             lock (s_domainCountLock) {
1710                 domainCount = ++s_domainCount;
1711             }
1712             return id + "-" + domainCount.ToString(NumberFormatInfo.InvariantInfo) + "-" + DateTime.UtcNow.ToFileTime().ToString();
1713         }
1714 
GetLockableAppDomainContext(string appId)1715         internal LockableAppDomainContext GetLockableAppDomainContext (string appId) {
1716             lock (this) {
1717                 LockableAppDomainContext ac;
1718                 if (!_appDomains.TryGetValue(appId, out ac)) {
1719                     ac = new LockableAppDomainContext();
1720                     _appDomains.Add (appId, ac);
1721                 }
1722 
1723                 return ac;
1724             }
1725         }
1726 
1727         // take a copy of _appDomains collection so that it can be used without locking on ApplicationManager
CloneAppDomainsCollection()1728         private Dictionary<string, LockableAppDomainContext> CloneAppDomainsCollection() {
1729             lock (this) {
1730                 return new Dictionary<string, LockableAppDomainContext>(_appDomains, StringComparer.OrdinalIgnoreCase);
1731             }
1732         }
1733 
GetAppConfigCommon(IConfigMapPath configMapPath, string siteID, string appSegment)1734         private static Configuration GetAppConfigCommon(IConfigMapPath configMapPath, string siteID, string appSegment) {
1735             WebConfigurationFileMap fileMap = new WebConfigurationFileMap();
1736             string dir = null;
1737             string fileName = null;
1738             string subDir = "/";
1739             // add root mapping
1740             configMapPath.GetPathConfigFilename(siteID, subDir, out dir, out fileName);
1741             if (dir != null) {
1742                 fileMap.VirtualDirectories.Add(subDir, new VirtualDirectoryMapping(Path.GetFullPath(dir), true));
1743             }
1744             // add subdir mappings
1745             string[] subDirs = appSegment.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
1746             foreach (string s in subDirs) {
1747                 subDir = subDir + s;
1748                 configMapPath.GetPathConfigFilename(siteID, subDir, out dir, out fileName);
1749                 if (dir != null) {
1750                     fileMap.VirtualDirectories.Add(subDir, new VirtualDirectoryMapping(Path.GetFullPath(dir), true));
1751                 }
1752                 subDir = subDir + "/";
1753             }
1754             // open mapped web config for application
1755             return WebConfigurationManager.OpenMappedWebConfiguration(fileMap, appSegment, siteID);
1756         }
1757 
1758         [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Only ever called by the full-trust parent AppDomain.")]
GetAppConfigGeneric(IApplicationHost appHost, string siteID, string appSegment, VirtualPath virtualPath, string physicalPath)1759         private static Configuration GetAppConfigGeneric(IApplicationHost appHost, string siteID, string appSegment, VirtualPath virtualPath, string physicalPath) {
1760             WebConfigurationFileMap fileMap = new WebConfigurationFileMap();
1761             IConfigMapPathFactory configMapPathFactory2 = appHost.GetConfigMapPathFactory();
1762             IConfigMapPath configMapPath = configMapPathFactory2.Create(virtualPath.VirtualPathString, physicalPath);
1763             return GetAppConfigCommon(configMapPath, siteID, appSegment);
1764         }
1765 
GetAppConfigIISExpress(string siteID, string appSegment, string iisExpressVersion)1766         private static Configuration GetAppConfigIISExpress(string siteID, string appSegment, string iisExpressVersion) {
1767             ExpressServerConfig serverConfig = (ExpressServerConfig)ServerConfig.GetDefaultDomainInstance(iisExpressVersion);
1768             return GetAppConfigCommon(serverConfig, siteID, appSegment);
1769         }
1770 
1771         private sealed class AppDomainSwitches {
1772             public bool UseLegacyCas;
1773             public bool UseRandomizedStringHashAlgorithm;
1774 
Apply(AppDomainSetup setup)1775             public void Apply(AppDomainSetup setup) {
1776                 List<string> switches = new List<string>();
1777 
1778                 if (UseLegacyCas) {
1779                     // Enables the AppDomain to use the legacy CAS model for compatibility <trust/legacyCasModel>
1780                     switches.Add("NetFx40_LegacySecurityPolicy");
1781                 }
1782 
1783                 if (UseRandomizedStringHashAlgorithm) {
1784                     switches.Add("UseRandomizedStringHashAlgorithm");
1785                 }
1786 
1787                 if (switches.Count > 0) {
1788                     setup.SetCompatibilitySwitches(switches);
1789                 }
1790             }
1791         }
1792 
1793         // This class holds information about the environment that is hosting ASP.NET. The particular design of this class
1794         // is that the information is computed once and stored, and the methods which compute the information are private.
1795         // This prevents accidental misuse of this type via querying the environment after user code has had a chance to
1796         // run, which could potentially affect the environment itself.
1797         private static class EnvironmentInfo {
1798             public static readonly bool IsStringHashCodeRandomizationDetected = GetIsStringHashCodeRandomizationDetected();
1799             public static readonly bool WasLaunchedFromDevelopmentEnvironment = GetWasLaunchedFromDevelopmentEnvironmentValue();
1800 
GetIsStringHashCodeRandomizationDetected()1801             private static bool GetIsStringHashCodeRandomizationDetected() {
1802                 // known test vector
1803                 return (StringComparer.InvariantCultureIgnoreCase.GetHashCode("The quick brown fox jumps over the lazy dog.") != 0x703e662e);
1804             }
1805 
1806             // Visual Studio / WebMatrix will set DEV_ENVIRONMENT=1 when launching an ASP.NET host in a development environment.
GetWasLaunchedFromDevelopmentEnvironmentValue()1807             private static bool GetWasLaunchedFromDevelopmentEnvironmentValue() {
1808                 try {
1809                     string envVar = Environment.GetEnvironmentVariable("DEV_ENVIRONMENT", EnvironmentVariableTarget.Process);
1810                     return String.Equals(envVar, "1", StringComparison.Ordinal);
1811                 }
1812                 catch {
1813                     // We don't care if we can't read the environment variable; just treat it as not present.
1814                     return false;
1815                 }
1816             }
1817         }
1818 
1819     }
1820 }
1821