//------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // //------------------------------------------------------------------------------ namespace System.Web.Hosting { using System; using System.Collections; using System.Collections.Generic; using System.Configuration; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.ExceptionServices; using System.Runtime.InteropServices; using System.Runtime.Remoting; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Security.Policy; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Web; using System.Web.Compilation; using System.Web.Configuration; using System.Web.Util; public enum HostSecurityPolicyResults { DefaultPolicy = 0, FullTrust = 1, AppDomainTrust = 2, Nothing = 3 }; [PermissionSet(SecurityAction.InheritanceDemand, Unrestricted = true)] public class HostSecurityPolicyResolver { public virtual HostSecurityPolicyResults ResolvePolicy(Evidence evidence) { return HostSecurityPolicyResults.DefaultPolicy; } } internal class LockableAppDomainContext { internal HostingEnvironment HostEnv { get; set; } internal string PreloadContext { get; set; } internal bool RetryingPreload { get; set; } internal LockableAppDomainContext() { } } public sealed class ApplicationManager : MarshalByRefObject { private const string _clrQuirkAppSettingsAppContextPrefix = "AppContext.SetSwitch:"; private const string _regexMatchTimeoutKey = "REGEX_DEFAULT_MATCH_TIMEOUT"; private static readonly StrongName _mwiV1StrongName = GetMicrosoftWebInfrastructureV1StrongName(); private static Object _applicationManagerStaticLock = new Object(); // open count (when last close goes to 0 it shuts down everything) int _openCount = 0; bool _shutdownInProgress = false; // table of app domains (LockableAppDomainContext objects) by app id // To simplify per-appdomain synchronization we will never remove LockableAppDomainContext objects from this table even when the AD is unloaded // We may need to fix it if profiling shows a noticeable impact on performance private Dictionary _appDomains = new Dictionary(StringComparer.OrdinalIgnoreCase); // count of HostingEnvironment instances that is referenced in _appDomains collection private int _accessibleHostingEnvCount; // could differ from _appDomains or _accessibleHostingEnvCount count (host env is active some time after it is removed) private int _activeHostingEnvCount; // pending callback to respond to ping (typed as Object to do Interlocked operations) private Object _pendingPingCallback; // delegate OnRespondToPing private WaitCallback _onRespondToPingWaitCallback; // single instance of app manager private static ApplicationManager _theAppManager; // single instance of cache manager private static CacheManager _cm; // store fatal exception to assist debugging private static Exception _fatalException = null; internal ApplicationManager() { _onRespondToPingWaitCallback = new WaitCallback(this.OnRespondToPingWaitCallback); // VSWhidbey 555767: Need better logging for unhandled exceptions (http://support.microsoft.com/?id=911816) // We only add a handler in the default domain because it will be notified when an unhandled exception // occurs in ANY domain. // WOS 1983175: (weird) only the handler in the default domain is notified when there is an AV in a native module // while we're in a call to MgdIndicateCompletion. AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(OnUnhandledException); } private void InitCacheManager(long privateBytesLimit) { if (_cm == null) { lock (_applicationManagerStaticLock) { if (_cm == null && !_shutdownInProgress) { _cm = new CacheManager(this, privateBytesLimit); } } } } private void DisposeCacheManager() { if (_cm != null) { lock (_applicationManagerStaticLock) { if (_cm != null) { _cm.Dispose(); _cm = null; } } } } // Each cache must update the total with the difference between it's current size and it's previous size. // To reduce cross-domain costs, this also returns the updated total size. internal long GetUpdatedTotalCacheSize(long sizeUpdate) { CacheManager cm = _cm; return (cm != null) ? cm.GetUpdatedTotalCacheSize(sizeUpdate) : 0; } internal long TrimCaches(int percent) { long trimmedOrExpired = 0; Dictionary apps = CloneAppDomainsCollection(); foreach (LockableAppDomainContext ac in apps.Values) { lock (ac) { HostingEnvironment env = ac.HostEnv; if (_shutdownInProgress) { break; } if (env == null) { continue; } trimmedOrExpired += env.TrimCache(percent); } } return trimmedOrExpired; } internal bool ShutdownInProgress { get { return _shutdownInProgress; } } internal static void RecordFatalException(Exception e) { RecordFatalException(AppDomain.CurrentDomain, e); } internal static void RecordFatalException(AppDomain appDomain, Exception e) { // store the exception from the first caller to assist debugging object originalValue = Interlocked.CompareExchange(ref _fatalException, e, null); if (originalValue == null) { // create event log entry Misc.WriteUnhandledExceptionToEventLog(appDomain, e); } } private static void OnUnhandledException(Object sender, UnhandledExceptionEventArgs eventArgs) { // if the CLR is not terminating, ignore the notification if (!eventArgs.IsTerminating) { return; } Exception exception = eventArgs.ExceptionObject as Exception; if (exception == null) { return; } AppDomain appDomain = sender as AppDomain; if (appDomain == null) { return; } RecordFatalException(appDomain, exception); } public override Object InitializeLifetimeService() { return null; // never expire lease } // // public ApplicationManager methods // public static ApplicationManager GetApplicationManager() { if (_theAppManager == null) { lock (_applicationManagerStaticLock) { if (_theAppManager == null) { if (HostingEnvironment.IsHosted) _theAppManager = HostingEnvironment.GetApplicationManager(); if (_theAppManager == null) _theAppManager = new ApplicationManager(); } } } return _theAppManager; } [SecurityPermission(SecurityAction.Demand, Unrestricted=true)] public void Open() { Interlocked.Increment(ref _openCount); } [SecurityPermission(SecurityAction.Demand, Unrestricted = true)] public void Close() { if (Interlocked.Decrement(ref _openCount) > 0) return; // need to shutdown everything ShutdownAll(); } private string CreateSimpleAppID(VirtualPath virtualPath, string physicalPath, string siteName) { // Put together some unique app id string appId = String.Concat(virtualPath.VirtualPathString, physicalPath); if (!String.IsNullOrEmpty(siteName)) { appId = String.Concat(appId, siteName); } return appId.GetHashCode().ToString("x", CultureInfo.InvariantCulture); } [SecurityPermission(SecurityAction.Demand, Unrestricted = true)] public IRegisteredObject CreateObject(IApplicationHost appHost, Type type) { if (appHost == null) { throw new ArgumentNullException("appHost"); } if (type == null) { throw new ArgumentNullException("type"); } string appID = CreateSimpleAppID(appHost); return CreateObjectInternal(appID, type, appHost, false); } [SecurityPermission(SecurityAction.Demand, UnmanagedCode=true)] public IRegisteredObject CreateObject(String appId, Type type, string virtualPath, string physicalPath, bool failIfExists) { return CreateObject(appId, type, virtualPath, physicalPath, failIfExists, false /*throwOnError*/); } [SecurityPermission(SecurityAction.Demand, Unrestricted = true)] public IRegisteredObject CreateObject(String appId, Type type, string virtualPath, string physicalPath, bool failIfExists, bool throwOnError) { // check args if (appId == null) throw new ArgumentNullException("appId"); SimpleApplicationHost appHost = new SimpleApplicationHost(VirtualPath.CreateAbsolute(virtualPath), physicalPath); // if throw on error flag is set, create hosting parameters accordingly HostingEnvironmentParameters hostingParameters = null; if (throwOnError) { hostingParameters = new HostingEnvironmentParameters(); hostingParameters.HostingFlags = HostingEnvironmentFlags.ThrowHostingInitErrors; } // call the internal method return CreateObjectInternal(appId, type, appHost, failIfExists, hostingParameters); } [SecurityPermission(SecurityAction.Demand, Unrestricted = true)] internal IRegisteredObject CreateObjectInternal(String appId, Type type, IApplicationHost appHost, bool failIfExists) { // check args if (appId == null) throw new ArgumentNullException("appId"); if (type == null) throw new ArgumentNullException("type"); if (appHost == null) throw new ArgumentNullException("appHost"); // call the internal method return CreateObjectInternal(appId, type, appHost, failIfExists, null /*hostingParameters*/); } [PermissionSet(SecurityAction.Assert, Unrestricted = true)] internal IRegisteredObject CreateObjectInternal( String appId, Type type, IApplicationHost appHost, bool failIfExists, HostingEnvironmentParameters hostingParameters) { // check that type is as IRegisteredObject if (!typeof(IRegisteredObject).IsAssignableFrom(type)) throw new ArgumentException(SR.GetString(SR.Not_IRegisteredObject, type.FullName), "type"); // get hosting environment HostingEnvironment env = GetAppDomainWithHostingEnvironment(appId, appHost, hostingParameters); // create the managed object in the worker app domain // When marshaling Type, the AppDomain must have FileIoPermission to the assembly, which is not // always the case, so we marshal the assembly qualified name instead ObjectHandle h = env.CreateWellKnownObjectInstance(type.AssemblyQualifiedName, failIfExists); return (h != null) ? h.Unwrap() as IRegisteredObject : null; } internal IRegisteredObject CreateObjectWithDefaultAppHostAndAppId( String physicalPath, string virtualPath, Type type, out String appId, out IApplicationHost appHost) { return CreateObjectWithDefaultAppHostAndAppId(physicalPath, VirtualPath.CreateNonRelative(virtualPath), type, out appId, out appHost); } internal IRegisteredObject CreateObjectWithDefaultAppHostAndAppId( String physicalPath, VirtualPath virtualPath, Type type, out String appId, out IApplicationHost appHost) { HostingEnvironmentParameters hostingParameters = new HostingEnvironmentParameters(); hostingParameters.HostingFlags = HostingEnvironmentFlags.DontCallAppInitialize; return CreateObjectWithDefaultAppHostAndAppId( physicalPath, virtualPath, type, false, hostingParameters, out appId, out appHost); } internal IRegisteredObject CreateObjectWithDefaultAppHostAndAppId( String physicalPath, VirtualPath virtualPath, Type type, bool failIfExists, HostingEnvironmentParameters hostingParameters, out String appId, out IApplicationHost appHost) { #if !FEATURE_PAL // FEATURE_PAL does not enable IIS-based hosting features if (physicalPath == null) { // If the physical path is null, we use an ISAPIApplicationHost based // on the virtual path (or metabase id). // Make sure the static HttpRuntime is created so isapi assembly can be loaded properly. HttpRuntime.ForceStaticInit(); ISAPIApplicationHost isapiAppHost = new ISAPIApplicationHost(virtualPath.VirtualPathString, null, true, null, hostingParameters.IISExpressVersion); appHost = isapiAppHost; appId = isapiAppHost.AppId; virtualPath = VirtualPath.Create(appHost.GetVirtualPath()); physicalPath = FileUtil.FixUpPhysicalDirectory(appHost.GetPhysicalPath()); } else { #endif // !FEATURE_PAL // If the physical path was passed in, don't use an Isapi host. Instead, // use a simple app host which does simple virtual to physical mappings // Put together some unique app id appId = CreateSimpleAppID(virtualPath, physicalPath, null); appHost = new SimpleApplicationHost(virtualPath, physicalPath); } string precompTargetPhysicalDir = hostingParameters.PrecompilationTargetPhysicalDirectory; if (precompTargetPhysicalDir != null) { // Change the appID so we use a different codegendir in precompile for deployment scenario, // this ensures we don't use or pollute the regular codegen files. Also, use different // ID's depending on whether the precompilation is Updatable (VSWhidbey 383239) if ((hostingParameters.ClientBuildManagerParameter != null) && (hostingParameters.ClientBuildManagerParameter.PrecompilationFlags & PrecompilationFlags.Updatable) == 0) appId = appId + "_precompile"; else appId = appId + "_precompile_u"; } return CreateObjectInternal(appId, type, appHost, failIfExists, hostingParameters); } [SecurityPermission(SecurityAction.Demand, Unrestricted = true)] public IRegisteredObject GetObject(String appId, Type type) { // check args if (appId == null) throw new ArgumentNullException("appId"); if (type == null) throw new ArgumentNullException("type"); LockableAppDomainContext ac = GetLockableAppDomainContext(appId); lock (ac) { HostingEnvironment env = ac.HostEnv; if (env == null) return null; // find the instance by type // When marshaling Type, the AppDomain must have FileIoPermission to the assembly, which is not // always the case, so we marshal the assembly qualified name instead ObjectHandle h = env.FindWellKnownObject(type.AssemblyQualifiedName); return (h != null) ? h.Unwrap() as IRegisteredObject : null; } } [SecurityPermission(SecurityAction.Demand, Unrestricted = true)] public AppDomain GetAppDomain(string appId) { if (appId == null) { throw new ArgumentNullException("appId"); } LockableAppDomainContext ac = GetLockableAppDomainContext(appId); lock (ac) { HostingEnvironment env = ac.HostEnv; if (env == null) { return null; } return env.HostedAppDomain; } } public AppDomain GetAppDomain(IApplicationHost appHost) { if (appHost == null) { throw new ArgumentNullException("appHost"); } string appID = CreateSimpleAppID(appHost); return GetAppDomain(appID); } [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "This isn't a dangerous method.")] private string CreateSimpleAppID(IApplicationHost appHost) { if (appHost == null) { throw new ArgumentNullException("appHost"); } return CreateSimpleAppID(VirtualPath.Create(appHost.GetVirtualPath()), appHost.GetPhysicalPath(), appHost.GetSiteName()); } // if a "well-known" object of the specified type already exists in the application, // remove the app from the managed application table. This is // used in IIS7 integrated mode when IIS7 determines that it is necessary to create // a new application and shutdown the old one. internal void RemoveFromTableIfRuntimeExists(String appId, Type runtimeType) { // check args if (appId == null) throw new ArgumentNullException("appId"); if (runtimeType == null) throw new ArgumentNullException("runtimeType"); LockableAppDomainContext ac = GetLockableAppDomainContext(appId); lock (ac) { // get hosting environment HostingEnvironment env = ac.HostEnv; if (env == null) return; // find the instance by type // When marshaling Type, the AppDomain must have FileIoPermission to the assembly, which is not // always the case, so we marshal the assembly qualified name instead ObjectHandle h = env.FindWellKnownObject(runtimeType.AssemblyQualifiedName); if (h != null) { // ensure that it is removed from _appDomains by calling // HostingEnvironmentShutdownInitiated directly. HostingEnvironmentShutdownInitiated(appId, env); } } } [SecurityPermission(SecurityAction.Demand, Unrestricted = true)] public void StopObject(String appId, Type type) { // check args if (appId == null) throw new ArgumentNullException("appId"); if (type == null) throw new ArgumentNullException("type"); LockableAppDomainContext ac = GetLockableAppDomainContext(appId); lock (ac) { HostingEnvironment env = ac.HostEnv; if (env != null) { // When marshaling Type, the AppDomain must have FileIoPermission to the assembly, which is not // always the case, so we marshal the assembly qualified name instead env.StopWellKnownObject(type.AssemblyQualifiedName); } } } public bool IsIdle() { Dictionary apps = CloneAppDomainsCollection(); foreach (LockableAppDomainContext ac in apps.Values) { lock (ac) { HostingEnvironment env = ac.HostEnv; bool idle = (null == env) ? true : env.IsIdle(); if (!idle) return false; } } return true; } [SecurityPermission(SecurityAction.Demand, Unrestricted = true)] public void ShutdownApplication(String appId) { if (appId == null) throw new ArgumentNullException("appId"); LockableAppDomainContext ac = GetLockableAppDomainContext(appId); lock (ac) { if (ac.HostEnv != null) { ac.HostEnv.InitiateShutdownInternal(); } } } [SecurityPermission(SecurityAction.Demand, Unrestricted = true)] public void ShutdownAll() { _shutdownInProgress = true; Dictionary oldTable = null; DisposeCacheManager(); lock (this) { oldTable = _appDomains; // don't keep references to hosting environments anymore _appDomains = new Dictionary(StringComparer.OrdinalIgnoreCase); } foreach (KeyValuePair p in oldTable) { LockableAppDomainContext ac = p.Value; lock (ac) { HostingEnvironment env = ac.HostEnv; if (null != env) { env.InitiateShutdownInternal(); } } } for (int iter=0; _activeHostingEnvCount > 0 && iter < 3000; iter++) // Wait at most 5 minutes Thread.Sleep(100); } [SecurityPermission(SecurityAction.Demand, Unrestricted = true)] public ApplicationInfo[] GetRunningApplications() { ArrayList appList = new ArrayList(); Dictionary apps = CloneAppDomainsCollection(); foreach (LockableAppDomainContext ac in apps.Values) { lock (ac) { HostingEnvironment env = ac.HostEnv; if (env != null) { appList.Add(env.GetApplicationInfo()); } } } int n = appList.Count; ApplicationInfo[] result = new ApplicationInfo[n]; if (n > 0) { appList.CopyTo(result); } return result; } [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "This method fails due to serialization issues if not called by ASP.NET.")] internal AppDomainInfo [] GetAppDomainInfos() { ArrayList appList = new ArrayList(); Dictionary apps = CloneAppDomainsCollection(); foreach (LockableAppDomainContext ac in apps.Values) { lock (ac) { HostingEnvironment hostEnv = ac.HostEnv; if (hostEnv == null) { continue; } IApplicationHost appHost = hostEnv.InternalApplicationHost; ApplicationInfo appInfo = hostEnv.GetApplicationInfo(); int siteId = 0; if (appHost != null) { try { siteId = Int32.Parse(appHost.GetSiteID(), CultureInfo.InvariantCulture); } catch { } } AppDomainInfo appDomainInfo = new AppDomainInfo(appInfo.ID, appInfo.VirtualPath, appInfo.PhysicalPath, siteId, hostEnv.GetIdleValue()); appList.Add(appDomainInfo); } } return (AppDomainInfo[]) appList.ToArray(typeof(AppDomainInfo)); } // // APIs for the process host to suspend / resume all running applications // [SuppressMessage("Microsoft.Reliability", "CA2002:DoNotLockOnObjectsWithWeakIdentity", Justification = "'this' is never a MBRO proxy object.")] internal object SuspendAllApplications() { LockableAppDomainContext[] allAppDomainContexts; lock (this) { allAppDomainContexts = _appDomains.Values.ToArray(); } ApplicationResumeStateContainer[] resumeContainers = Task.WhenAll(allAppDomainContexts.Select(CreateSuspendTask)).Result; return resumeContainers; } private static Task _dummyCompletedSuspendTask = Task.FromResult(null); private static Task CreateSuspendTask(LockableAppDomainContext appDomainContext) { // dictionary contained a null entry? if (appDomainContext == null) { return _dummyCompletedSuspendTask; } HostingEnvironment hostEnv; lock (appDomainContext) { hostEnv = appDomainContext.HostEnv; } // Quick check: is this a dummy context that had no associated application? if (hostEnv == null) { return _dummyCompletedSuspendTask; } // QUWI since we want to run each application's suspend method in parallel. // Unsafe since we don't care about impersonation, identity, etc. // Don't use Task.Run since it captures the EC and could execute inline. TaskCompletionSource tcs = new TaskCompletionSource(); ThreadPool.UnsafeQueueUserWorkItem(_ => { // We're not locking on the appDomainContext here. The reason for this is two-fold: // a) We don't want to cause a potential deadlock issue whereby Suspend could kick // off user code that tries calling InitiateShutdown and thus taking a lock on // appDomainContext. // b) It's easier to try calling into the captured HostingEnvironment and just // ---- the "no AD" exception than it is to try to synchronize the Suspend, // Resume, and Stop methods. The CLR protects us from ourselves here. // // We need to use the captured 'hostEnv' to prevent null refs. IntPtr state; try { state = hostEnv.SuspendApplication(); } catch (AppDomainUnloadedException) { // AD unloads aren't considered a failure tcs.TrySetResult(null); return; } tcs.TrySetResult(new ApplicationResumeStateContainer(hostEnv, state)); }, null); return tcs.Task; } internal void ResumeAllApplications(object state) { foreach (var resumeContainer in (ApplicationResumeStateContainer[])state) { if (resumeContainer != null) { // could be null if the application went away resumeContainer.Resume(); } } } // // ping implementation // // called from process host internal void Ping(IProcessPingCallback callback) { if (callback == null || _pendingPingCallback != null) return; // remember active callback but only if none is remembered already if (Interlocked.CompareExchange(ref _pendingPingCallback, callback, null) == null) { // queue a work item to respond to ping ThreadPool.QueueUserWorkItem(_onRespondToPingWaitCallback); } } // threadpool callback (also called on some activity from hosting environment) internal void OnRespondToPingWaitCallback(Object state) { RespondToPingIfNeeded(); } // respond to ping on callback internal void RespondToPingIfNeeded() { IProcessPingCallback callback = _pendingPingCallback as IProcessPingCallback; // make sure we call the callback once if (callback != null) { if (Interlocked.CompareExchange(ref _pendingPingCallback, null, callback) == callback) { callback.Respond(); } } } // ApplicationManager is loaded into the default AppDomain // ASP.NET doesn't set string hash randomization for the defaul AppDomain, so we can assume the existing CLR implementation here is unchanged // Note, it won't work if is used, because the entire process is subject to string hash randomization internal int GetNonRandomizedStringComparerHashCode(string s, bool ignoreCase) { StringComparer comparer = ignoreCase ? StringComparer.InvariantCultureIgnoreCase : StringComparer.InvariantCulture; return comparer.GetHashCode(s); } // // communication with hosting environments // internal void HostingEnvironmentActivated(long privateBytesLimit) { int count = Interlocked.Increment(ref _activeHostingEnvCount); // initialize CacheManager once, without blocking if (count == 1) { InitCacheManager(privateBytesLimit); } } internal void HostingEnvironmentShutdownComplete(String appId, IApplicationHost appHost) { try { if (appHost != null) { // make sure application host can be GC'd MarshalByRefObject realApplicationHost = appHost as MarshalByRefObject; if (realApplicationHost != null) { RemotingServices.Disconnect(realApplicationHost); } } } finally { Interlocked.Decrement(ref _activeHostingEnvCount); } } internal void HostingEnvironmentShutdownInitiated(String appId, HostingEnvironment env) { if (!_shutdownInProgress) { // don't bother during shutdown (while enumerating) LockableAppDomainContext ac = GetLockableAppDomainContext (appId); lock (ac){ if (!env.HasBeenRemovedFromAppManagerTable) { env.HasBeenRemovedFromAppManagerTable = true; ac.HostEnv = null; Interlocked.Decrement(ref _accessibleHostingEnvCount); // Autorestart the application right away if (ac.PreloadContext != null && !ac.RetryingPreload) { ProcessHost.PreloadApplicationIfNotShuttingdown(appId, ac); } } } } } internal int AppDomainsCount { get { return _accessibleHostingEnvCount; } } internal void ReduceAppDomainsCount(int limit) { // Dictionary apps = CloneAppDomainsCollection(); while (_accessibleHostingEnvCount >= limit && !_shutdownInProgress) { LockableAppDomainContext bestCandidateForShutdown = null; int bestCandidateLruScore = 0; foreach (LockableAppDomainContext ac in apps.Values) { // Don't lock on LockableAppDomainContext before we check that ac.HostEnv != null. // Otherwise we may end up with a deadlock between 2 app domains trying to unload each other HostingEnvironment h = ac.HostEnv; if (h == null) { continue; } lock (ac) { h = ac.HostEnv; // Avoid ---- by checking again under lock if (h == null) { continue; } int newLruScore = h.LruScore; if (bestCandidateForShutdown == null || bestCandidateForShutdown.HostEnv == null || newLruScore < bestCandidateLruScore) { bestCandidateLruScore = newLruScore; bestCandidateForShutdown = ac; } } } if (bestCandidateForShutdown == null) break; lock (bestCandidateForShutdown) { if (bestCandidateForShutdown.HostEnv != null) { bestCandidateForShutdown.HostEnv.InitiateShutdownInternal(); } } } } // // helper to support legacy APIs (AppHost.CreateAppHost) // internal ObjectHandle CreateInstanceInNewWorkerAppDomain( Type type, String appId, VirtualPath virtualPath, String physicalPath) { Debug.Trace("AppManager", "CreateObjectInNewWorkerAppDomain, type=" + type.FullName); IApplicationHost appHost = new SimpleApplicationHost(virtualPath, physicalPath); HostingEnvironmentParameters hostingParameters = new HostingEnvironmentParameters(); hostingParameters.HostingFlags = HostingEnvironmentFlags.HideFromAppManager; HostingEnvironment env = CreateAppDomainWithHostingEnvironmentAndReportErrors(appId, appHost, hostingParameters); // When marshaling Type, the AppDomain must have FileIoPermission to the assembly, which is not // always the case, so we marshal the assembly qualified name instead return env.CreateInstance(type.AssemblyQualifiedName); } // // helpers to facilitate app domain creation // private HostingEnvironment GetAppDomainWithHostingEnvironment(String appId, IApplicationHost appHost, HostingEnvironmentParameters hostingParameters) { LockableAppDomainContext ac = GetLockableAppDomainContext (appId); lock (ac) { HostingEnvironment env = ac.HostEnv; if (env != null) { try { env.IsUnloaded(); } catch(AppDomainUnloadedException) { env = null; } } if (env == null) { env = CreateAppDomainWithHostingEnvironmentAndReportErrors(appId, appHost, hostingParameters); ac.HostEnv = env; Interlocked.Increment(ref _accessibleHostingEnvCount); } return env; } } private HostingEnvironment CreateAppDomainWithHostingEnvironmentAndReportErrors( String appId, IApplicationHost appHost, HostingEnvironmentParameters hostingParameters) { try { return CreateAppDomainWithHostingEnvironment(appId, appHost, hostingParameters); } catch (Exception e) { Misc.ReportUnhandledException(e, new string[] {SR.GetString(SR.Failed_to_initialize_AppDomain), appId}); throw; } } [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "We carefully control the callers.")] [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.")] private HostingEnvironment CreateAppDomainWithHostingEnvironment( String appId, IApplicationHost appHost, HostingEnvironmentParameters hostingParameters) { String physicalPath = appHost.GetPhysicalPath(); if (!StringUtil.StringEndsWith(physicalPath, Path.DirectorySeparatorChar)) physicalPath = physicalPath + Path.DirectorySeparatorChar; String domainId = ConstructAppDomainId(appId); String appName = (StringUtil.GetStringHashCode(String.Concat(appId.ToLower(CultureInfo.InvariantCulture), physicalPath.ToLower(CultureInfo.InvariantCulture)))).ToString("x", CultureInfo.InvariantCulture); VirtualPath virtualPath = VirtualPath.Create(appHost.GetVirtualPath()); Debug.Trace("AppManager", "CreateAppDomainWithHostingEnvironment, path=" + physicalPath + "; appId=" + appId + "; domainId=" + domainId); IDictionary bindings = new Hashtable(20); AppDomainSetup setup = new AppDomainSetup(); AppDomainSwitches switches = new AppDomainSwitches(); PopulateDomainBindings(domainId, appId, appName, physicalPath, virtualPath, setup, bindings); // Create the app domain AppDomain appDomain = null; Dictionary appDomainAdditionalData = new Dictionary(); Exception appDomainCreationException = null; string siteID = appHost.GetSiteID(); string appSegment = virtualPath.VirtualPathStringNoTrailingSlash; bool inClientBuildManager = false; Configuration appConfig = null; PolicyLevel policyLevel = null; PermissionSet permissionSet = null; List fullTrustAssemblies = new List(); string[] defaultPartialTrustVisibleAssemblies = new[] { "System.Web, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293", "System.Web.Extensions, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9", "System.Web.Abstractions, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9", "System.Web.Routing, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9", "System.Web.DynamicData, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9", "System.Web.DataVisualization, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9", "System.Web.ApplicationServices, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" }; Exception appDomainStartupConfigurationException = null; ImpersonationContext ictxConfig = null; IntPtr uncTokenConfig = IntPtr.Zero; HostingEnvironmentFlags hostingFlags = HostingEnvironmentFlags.Default; if (hostingParameters != null) { hostingFlags = hostingParameters.HostingFlags; if ((hostingFlags & HostingEnvironmentFlags.ClientBuildManager) != 0) { inClientBuildManager = true; // The default hosting policy in VS has changed (from MultiDomainHost to MultiDomain), // so we need to specify explicitly to allow generated assemblies // to be unloaded subsequently. (Dev10 bug) setup.LoaderOptimization = LoaderOptimization.MultiDomainHost; } } try { bool requireHostExecutionContextManager = false; bool requireHostSecurityManager = false; uncTokenConfig = appHost.GetConfigToken(); if (uncTokenConfig != IntPtr.Zero) { ictxConfig = new ImpersonationContext(uncTokenConfig); } try { // Did the custom loader fail to load? ExceptionDispatchInfo customLoaderException = ProcessHost.GetExistingCustomLoaderFailureAndClear(appId); if (customLoaderException != null) { customLoaderException.Throw(); } // We support randomized string hash code, but not for the default AppDomain // Don't allow string hash randomization for the defaul AppDomain (i.e. ) // Application should use AppSettings instead to opt-in. if (EnvironmentInfo.IsStringHashCodeRandomizationDetected) { throw new ConfigurationErrorsException(SR.GetString(SR.Require_stable_string_hash_codes)); } bool skipAdditionalConfigChecks = false; if (inClientBuildManager && hostingParameters.IISExpressVersion != null) { permissionSet = new PermissionSet(PermissionState.Unrestricted); setup.PartialTrustVisibleAssemblies = defaultPartialTrustVisibleAssemblies; appConfig = GetAppConfigIISExpress(siteID, appSegment, hostingParameters.IISExpressVersion); skipAdditionalConfigChecks = true; } else { //Hosted by IIS, we already have an IISMap. if (appHost is ISAPIApplicationHost) { string cacheKey = System.Web.Caching.CacheInternal.PrefixMapPath + siteID + virtualPath.VirtualPathString; MapPathCacheInfo cacheInfo = (MapPathCacheInfo)HttpRuntime.CacheInternal.Remove(cacheKey); appConfig = WebConfigurationManager.OpenWebConfiguration(appSegment, siteID); } // For non-IIS hosting scenarios, we need to get config map from application host in a generic way. else { appConfig = GetAppConfigGeneric(appHost, siteID, appSegment, virtualPath, physicalPath); } } HttpRuntimeSection httpRuntimeSection = (HttpRuntimeSection)appConfig.GetSection("system.web/httpRuntime"); if (httpRuntimeSection == null) { throw new ConfigurationErrorsException(SR.GetString(SR.Config_section_not_present, "httpRuntime")); } // DevDiv #403846 - Change certain config defaults if exists in config. // We store this information in the AppDomain data because certain configuration sections (like ) // are loaded before config is "baked" in the child AppDomain, and if we make and other sections // dependent on which may not have been loaded yet, we risk introducing ----s. Putting this value // in the AppDomain data guarantees that it is available before the first call to the config system. FrameworkName targetFrameworkName = httpRuntimeSection.GetTargetFrameworkName(); if (targetFrameworkName != null) { appDomainAdditionalData[System.Web.Util.BinaryCompatibility.TargetFrameworkKey] = targetFrameworkName; } if (!skipAdditionalConfigChecks) { // DevDiv #71268 - Add configuration attribute if (httpRuntimeSection.DefaultRegexMatchTimeout != TimeSpan.Zero) { appDomainAdditionalData[_regexMatchTimeoutKey] = httpRuntimeSection.DefaultRegexMatchTimeout; } // DevDiv #258274 - Add support for CLR quirks mode to ASP.NET if (targetFrameworkName != null) { setup.TargetFrameworkName = targetFrameworkName.ToString(); } // DevDiv #286354 - Having a Task-friendly SynchronizationContext requires overriding the AppDomain's HostExecutionContextManager. // DevDiv #403846 - If we can't parse the switch, use the setting to determine the default. AppSettingsSection appSettingsSection = appConfig.AppSettings; KeyValueConfigurationElement useTaskFriendlySynchronizationContextElement = appSettingsSection.Settings["aspnet:UseTaskFriendlySynchronizationContext"]; if (!(useTaskFriendlySynchronizationContextElement != null && Boolean.TryParse(useTaskFriendlySynchronizationContextElement.Value, out requireHostExecutionContextManager))) { requireHostExecutionContextManager = new System.Web.Util.BinaryCompatibility(targetFrameworkName).TargetsAtLeastFramework45 ? true : false; } // DevDiv #390704 - Add support for randomized string hash algorithm KeyValueConfigurationElement useRandomizedStringHashAlgorithmElement = appSettingsSection.Settings["aspnet:UseRandomizedStringHashAlgorithm"]; bool useRandomizedStringHashAlgorithm = false; if (useRandomizedStringHashAlgorithmElement != null && Boolean.TryParse(useRandomizedStringHashAlgorithmElement.Value, out useRandomizedStringHashAlgorithm)) { switches.UseRandomizedStringHashAlgorithm = useRandomizedStringHashAlgorithm; } // DevDiv #1041102 - Allow specifying quirks via switches // The keys must begin with "AppContext.SetSwitch" and have a non-zero key name length, // and the values must be parseable as Booleans. Dictionary clrQuirks = null; foreach (KeyValueConfigurationElement element in appSettingsSection.Settings) { if (element.Key != null && element.Key.Length > _clrQuirkAppSettingsAppContextPrefix.Length && element.Key.StartsWith(_clrQuirkAppSettingsAppContextPrefix, StringComparison.OrdinalIgnoreCase)) { bool value; if (Boolean.TryParse(element.Value, out value)) { if (clrQuirks == null) { clrQuirks = new Dictionary(); } clrQuirks[element.Key.Substring(_clrQuirkAppSettingsAppContextPrefix.Length)] = value; } } } if (clrQuirks != null && clrQuirks.Count > 0) { if (hostingParameters == null) { hostingParameters = new HostingEnvironmentParameters(); } hostingParameters.ClrQuirksSwitches = clrQuirks.ToArray(); } // DevDiv #248126 - Allow configuration of FileChangeMonitor behavior if (httpRuntimeSection.FcnMode != FcnMode.NotSet) { if (hostingParameters == null) { hostingParameters = new HostingEnvironmentParameters(); } hostingParameters.FcnMode = httpRuntimeSection.FcnMode; } // DevDiv #322858 - Allow FileChangesMonitor to skip reading DACLs as a perf improvement KeyValueConfigurationElement disableFcnDaclReadElement = appSettingsSection.Settings["aspnet:DisableFcnDaclRead"]; if (disableFcnDaclReadElement != null) { bool skipReadingAndCachingDacls; Boolean.TryParse(disableFcnDaclReadElement.Value, out skipReadingAndCachingDacls); if (skipReadingAndCachingDacls) { if (hostingParameters == null) { hostingParameters = new HostingEnvironmentParameters(); } hostingParameters.FcnSkipReadAndCacheDacls = true; } } // If we were launched from a development environment, we might want to enable the application to do things // it otherwise wouldn't normally allow, such as enabling an administrative control panel. For security reasons, // we only do this check if [the default value] is specified, since the // element can only be set at machine-level in a hosted environment. DeploymentSection deploymentSection = (DeploymentSection)appConfig.GetSection("system.web/deployment"); bool isDevEnvironment = false; if (deploymentSection != null && !deploymentSection.Retail && EnvironmentInfo.WasLaunchedFromDevelopmentEnvironment) { appDomainAdditionalData[".devEnvironment"] = true; isDevEnvironment = true; // DevDiv #275724 - Allow LocalDB support in partial trust scenarios // Normally LocalDB requires full trust since it's the equivalent of unmanaged code execution. If this is // a development environment and not a retail deployment, we can assume that the user developing the // application is actually in charge of the host, so we can trust him with LocalDB execution. // Technically this also means that the developer could have set in his application, // but he might want to deploy his application on a Medium-trust server and thus test how the rest of his // application works in a partial trust environment. (He would use SQL in production, whch is safe in // partial trust.) appDomainAdditionalData["ALLOW_LOCALDB_IN_PARTIAL_TRUST"] = true; } TrustSection trustSection = (TrustSection)appConfig.GetSection("system.web/trust"); if (trustSection == null || String.IsNullOrEmpty(trustSection.Level)) { throw new ConfigurationErrorsException(SR.GetString(SR.Config_section_not_present, "trust")); } switches.UseLegacyCas = trustSection.LegacyCasModel; if (inClientBuildManager) { permissionSet = new PermissionSet(PermissionState.Unrestricted); setup.PartialTrustVisibleAssemblies = defaultPartialTrustVisibleAssemblies; } else { if (!switches.UseLegacyCas) { if (trustSection.Level == "Full") { permissionSet = new PermissionSet(PermissionState.Unrestricted); setup.PartialTrustVisibleAssemblies = defaultPartialTrustVisibleAssemblies; } else { SecurityPolicySection securityPolicySection = (SecurityPolicySection)appConfig.GetSection("system.web/securityPolicy"); CompilationSection compilationSection = (CompilationSection)appConfig.GetSection("system.web/compilation"); FullTrustAssembliesSection fullTrustAssembliesSection = (FullTrustAssembliesSection)appConfig.GetSection("system.web/fullTrustAssemblies"); policyLevel = GetPartialTrustPolicyLevel(trustSection, securityPolicySection, compilationSection, physicalPath, virtualPath, isDevEnvironment); permissionSet = policyLevel.GetNamedPermissionSet(trustSection.PermissionSetName); if (permissionSet == null) { throw new ConfigurationErrorsException(SR.GetString(SR.Permission_set_not_found, trustSection.PermissionSetName)); } // read full trust assemblies and populate the strong name list if (fullTrustAssembliesSection != null) { FullTrustAssemblyCollection fullTrustAssembliesCollection = fullTrustAssembliesSection.FullTrustAssemblies; if (fullTrustAssembliesCollection != null) { fullTrustAssemblies.AddRange(from FullTrustAssembly fta in fullTrustAssembliesCollection select CreateStrongName(fta.AssemblyName, fta.Version, fta.PublicKey)); } } // DevDiv #27645 - We need to add future versions of Microsoft.Web.Infrastructure to so that ASP.NET // can version out-of-band releases. We should only do this if V1 of M.W.I is listed. if (fullTrustAssemblies.Contains(_mwiV1StrongName)) { fullTrustAssemblies.AddRange(CreateFutureMicrosoftWebInfrastructureStrongNames()); } // Partial-trust AppDomains using a non-legacy CAS model require our special HostSecurityManager requireHostSecurityManager = true; } } if (trustSection.Level != "Full") { PartialTrustVisibleAssembliesSection partialTrustVisibleAssembliesSection = (PartialTrustVisibleAssembliesSection)appConfig.GetSection("system.web/partialTrustVisibleAssemblies"); string[] partialTrustVisibleAssemblies = null; if (partialTrustVisibleAssembliesSection != null) { PartialTrustVisibleAssemblyCollection partialTrustVisibleAssembliesCollection = partialTrustVisibleAssembliesSection.PartialTrustVisibleAssemblies; if (partialTrustVisibleAssembliesCollection != null && partialTrustVisibleAssembliesCollection.Count != 0) { partialTrustVisibleAssemblies = new string[partialTrustVisibleAssembliesCollection.Count + defaultPartialTrustVisibleAssemblies.Length]; for (int i = 0; i < partialTrustVisibleAssembliesCollection.Count; i++) { partialTrustVisibleAssemblies[i] = partialTrustVisibleAssembliesCollection[i].AssemblyName + ", PublicKey=" + NormalizePublicKeyBlob(partialTrustVisibleAssembliesCollection[i].PublicKey); } defaultPartialTrustVisibleAssemblies.CopyTo(partialTrustVisibleAssemblies, partialTrustVisibleAssembliesCollection.Count); } } if (partialTrustVisibleAssemblies == null) { partialTrustVisibleAssemblies = defaultPartialTrustVisibleAssemblies; } setup.PartialTrustVisibleAssemblies = partialTrustVisibleAssemblies; } } } } catch (Exception e) { appDomainStartupConfigurationException = e; permissionSet = new PermissionSet(PermissionState.Unrestricted); } // Set the AppDomainManager if needed Type appDomainManagerType = AspNetAppDomainManager.GetAspNetAppDomainManagerType(requireHostExecutionContextManager, requireHostSecurityManager); if (appDomainManagerType != null) { setup.AppDomainManagerType = appDomainManagerType.FullName; setup.AppDomainManagerAssembly = appDomainManagerType.Assembly.FullName; } // Apply compatibility switches switches.Apply(setup); try { if (switches.UseLegacyCas) { appDomain = AppDomain.CreateDomain(domainId, #if FEATURE_PAL // FEATURE_PAL: hack to avoid non-supported hosting features null, #else // FEATURE_PAL GetDefaultDomainIdentity(), #endif // FEATURE_PAL setup); } else { appDomain = AppDomain.CreateDomain(domainId, #if FEATURE_PAL // FEATURE_PAL: hack to avoid non-supported hosting features null, #else // FEATURE_PAL GetDefaultDomainIdentity(), #endif // FEATURE_PAL setup, permissionSet, fullTrustAssemblies.ToArray() /* fully trusted assemblies list: empty list means only trust GAC assemblies */); } foreach (DictionaryEntry e in bindings) appDomain.SetData((String)e.Key, (String)e.Value); foreach (var entry in appDomainAdditionalData) appDomain.SetData(entry.Key, entry.Value); } catch (Exception e) { Debug.Trace("AppManager", "AppDomain.CreateDomain failed", e); appDomainCreationException = e; } } finally { if (ictxConfig != null) { ictxConfig.Undo(); ictxConfig = null; } if (uncTokenConfig != IntPtr.Zero) { UnsafeNativeMethods.CloseHandle(uncTokenConfig); uncTokenConfig = IntPtr.Zero; } } if (appDomain == null) { throw new SystemException(SR.GetString(SR.Cannot_create_AppDomain), appDomainCreationException); } // Create hosting environment in the new app domain Type hostType = typeof(HostingEnvironment); String module = hostType.Module.Assembly.FullName; String typeName = hostType.FullName; ObjectHandle h = null; // impersonate UNC identity, if any ImpersonationContext ictx = null; IntPtr uncToken = IntPtr.Zero; // // fetching config can fail due to a ---- with the // native config reader // if that has happened, force a flush // int maxRetries = 10; int numRetries = 0; while (numRetries < maxRetries) { try { uncToken = appHost.GetConfigToken(); // no throw, so break break; } catch (InvalidOperationException) { numRetries++; System.Threading.Thread.Sleep(250); } } if (uncToken != IntPtr.Zero) { try { ictx = new ImpersonationContext(uncToken); } catch { } finally { UnsafeNativeMethods.CloseHandle(uncToken); } } try { // Create the hosting environment in the app domain #if DBG try { h = Activator.CreateInstance(appDomain, module, typeName); } catch (Exception e) { Debug.Trace("AppManager", "appDomain.CreateInstance failed; identity=" + System.Security.Principal.WindowsIdentity.GetCurrent().Name, e); throw; } #else h = Activator.CreateInstance(appDomain, module, typeName); #endif } finally { // revert impersonation if (ictx != null) ictx.Undo(); if (h == null) { AppDomain.Unload(appDomain); } } HostingEnvironment env = (h != null) ? h.Unwrap() as HostingEnvironment : null; if (env == null) throw new SystemException(SR.GetString(SR.Cannot_create_HostEnv)); // initialize the hosting environment IConfigMapPathFactory configMapPathFactory = appHost.GetConfigMapPathFactory(); if (appDomainStartupConfigurationException == null) { env.Initialize(this, appHost, configMapPathFactory, hostingParameters, policyLevel); } else { env.Initialize(this, appHost, configMapPathFactory, hostingParameters, policyLevel, appDomainStartupConfigurationException); } return env; } private static string NormalizePublicKeyBlob(string publicKey) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < publicKey.Length; i++) { if (!Char.IsWhiteSpace(publicKey[i])) { sb.Append(publicKey[i]); } } publicKey = sb.ToString(); return publicKey; } private static StrongName CreateStrongName(string assemblyName, string version, string publicKeyString) { byte[] publicKey = null; StrongName strongName = null; publicKeyString = NormalizePublicKeyBlob(publicKeyString); int publicKeySize = publicKeyString.Length / 2; publicKey = new byte[publicKeySize]; for (int i = 0; i < publicKeySize; i++) { publicKey[i] = Byte.Parse(publicKeyString.Substring(i * 2, 2), System.Globalization.NumberStyles.HexNumber, CultureInfo.InvariantCulture); } StrongNamePublicKeyBlob keyBlob = new StrongNamePublicKeyBlob(publicKey); strongName = new StrongName(keyBlob, assemblyName, new Version(version)); return strongName; } // For various reasons, we can't add any entries to the list until .NET 5, which at the time of this writing is a few // years out. But since ASP.NET releases projects out-of-band from the .NET Framework as a whole (using Microsoft.Web.Infrasturcture), we // 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 // list, and if it is then v2 - v10 are implicitly added. If v1 is not present in the list, then we'll not // treat v2 - v10 as implicitly added. See DevDiv #27645 for more information. private static StrongName GetMicrosoftWebInfrastructureV1StrongName() { return CreateStrongName( assemblyName: "Microsoft.Web.Infrastructure", version: "1.0.0.0", publicKeyString: "0024000004800000940000000602000000240000525341310004000001000100B5FC90E7027F67871E773A8FDE8938C81DD402BA65B9201D60593E96C492651E889CC13F1415EBB53FAC1131AE0BD333C5EE6021672D9718EA31A8AEBD0DA0072F25D87DBA6FC90FFD598ED4DA35E44C398C454307E8E33B8426143DAEC9F596836F97C8F74750E5975C64E2189F45DEF46B2A2B1247ADC3652BF5C308055DA9"); } private static IEnumerable CreateFutureMicrosoftWebInfrastructureStrongNames() { string asmName = _mwiV1StrongName.Name; StrongNamePublicKeyBlob publicKey = _mwiV1StrongName.PublicKey; for (int i = 2; i <= 10; i++) { yield return new StrongName(publicKey, asmName, new Version(i, 0, 0, 0)); } } // devdiv 1038337: execution permission cannot be acquired under partial trust with ctrl-f5. // We build the codegen path in default domain. In order to build the right path, // caller must pass in a flag indicating whether it is under dev environment. [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "We carefully control this method's caller.")] private static PolicyLevel GetPartialTrustPolicyLevel( TrustSection trustSection, SecurityPolicySection securityPolicySection, CompilationSection compilationSection, string physicalPath, VirtualPath virtualPath, bool isDevEnvironment) { if (securityPolicySection == null || securityPolicySection.TrustLevels[trustSection.Level] == null) { throw new ConfigurationErrorsException(SR.GetString(SR.Unable_to_get_policy_file, trustSection.Level), String.Empty, 0); } String configFile = (String)securityPolicySection.TrustLevels[trustSection.Level].PolicyFileExpanded; if (configFile == null || !FileUtil.FileExists(configFile)) { throw new HttpException(SR.GetString(SR.Unable_to_get_policy_file, trustSection.Level)); } PolicyLevel policyLevel = null; String appDir = FileUtil.RemoveTrailingDirectoryBackSlash(physicalPath); String appDirUrl = HttpRuntime.MakeFileUrl(appDir); // setup $CodeGen$ replacement string tempDirectory = null; // These variables are used for error handling string tempDirAttribName = null; string configFileName = null; int configLineNumber = 0; if (compilationSection != null && !String.IsNullOrEmpty(compilationSection.TempDirectory)) { tempDirectory = compilationSection.TempDirectory; compilationSection.GetTempDirectoryErrorInfo(out tempDirAttribName, out configFileName, out configLineNumber); } if (tempDirectory != null) { tempDirectory = tempDirectory.Trim(); if (!Path.IsPathRooted(tempDirectory)) { // Make sure the path is not relative (VSWhidbey 260075) tempDirectory = null; } else { try { // Canonicalize it to avoid problems with spaces (VSWhidbey 229873) tempDirectory = new DirectoryInfo(tempDirectory).FullName; } catch { tempDirectory = null; } } if (tempDirectory == null) { throw new ConfigurationErrorsException( SR.GetString(SR.Invalid_temp_directory, tempDirAttribName), configFileName, configLineNumber); } // Create the config-specified directory if needed try { Directory.CreateDirectory(tempDirectory); } catch (Exception e) { throw new ConfigurationErrorsException( SR.GetString(SR.Invalid_temp_directory, tempDirAttribName), e, configFileName, configLineNumber); } } else { tempDirectory = Path.Combine(RuntimeEnvironment.GetRuntimeDirectory(), HttpRuntime.codegenDirName); } // If we don't have write access to the codegen dir, use the TEMP dir instead. // This will allow non-admin users to work in hosting scenarios (e.g. Venus, aspnet_compiler) if (!System.Web.UI.Util.HasWriteAccessToDirectory(tempDirectory)) { // Don't do this if we're in a service (!UserInteractive), as TEMP // could point to unwanted places. if (!Environment.UserInteractive) { throw new HttpException(SR.GetString(SR.No_codegen_access, System.Web.UI.Util.GetCurrentAccountName(), tempDirectory)); } tempDirectory = Path.GetTempPath(); Debug.Assert(System.Web.UI.Util.HasWriteAccessToDirectory(tempDirectory)); tempDirectory = Path.Combine(tempDirectory, HttpRuntime.codegenDirName); } String simpleAppName = System.Web.Hosting.AppManagerAppDomainFactory.ConstructSimpleAppName( VirtualPath.GetVirtualPathStringNoTrailingSlash(virtualPath), isDevEnvironment); String binDir = Path.Combine(tempDirectory, simpleAppName); binDir = FileUtil.RemoveTrailingDirectoryBackSlash(binDir); String binDirUrl = HttpRuntime.MakeFileUrl(binDir); String originUrl = trustSection.OriginUrl; FileStream file = new FileStream(configFile, FileMode.Open, FileAccess.Read); StreamReader reader = new StreamReader(file, Encoding.UTF8); String strFileData = reader.ReadToEnd(); reader.Close(); strFileData = strFileData.Replace("$AppDir$", appDir); strFileData = strFileData.Replace("$AppDirUrl$", appDirUrl); strFileData = strFileData.Replace("$CodeGen$", binDirUrl); if (originUrl == null) originUrl = String.Empty; strFileData = strFileData.Replace("$OriginHost$", originUrl); String gacLocation = null; if (strFileData.IndexOf("$Gac$", StringComparison.Ordinal) != -1) { gacLocation = HttpRuntime.GetGacLocation(); if (gacLocation != null) gacLocation = HttpRuntime.MakeFileUrl(gacLocation); if (gacLocation == null) gacLocation = String.Empty; strFileData = strFileData.Replace("$Gac$", gacLocation); } #pragma warning disable 618 // ASP is reading their grant set out of legacy policy level files policyLevel = SecurityManager.LoadPolicyLevelFromString(strFileData, PolicyLevelType.AppDomain); if (policyLevel == null) { throw new ConfigurationErrorsException(SR.GetString(SR.Unable_to_get_policy_file, trustSection.Level)); } // Found GAC Token if (gacLocation != null) { // walk the code groups at the app domain level and look for one that grants // access to the GAC with an UrlMembershipCondition. CodeGroup rootGroup = policyLevel.RootCodeGroup; bool foundGacCondition = false; foreach (CodeGroup childGroup in rootGroup.Children) { if (childGroup.MembershipCondition is GacMembershipCondition) { foundGacCondition = true; // if we found the GAC token and also have the GacMembershipCondition // the policy file needs to be upgraded to just include the GacMembershipCondition Debug.Assert(!foundGacCondition); break; } } // add one as a child of the toplevel group after // some sanity checking to make sure it's an ASP.NET policy file // which always begins with a FirstMatchCodeGroup granting nothing // this might not upgrade some custom policy files if (!foundGacCondition) { if (rootGroup is FirstMatchCodeGroup) { FirstMatchCodeGroup firstMatch = (FirstMatchCodeGroup)rootGroup; if (firstMatch.MembershipCondition is AllMembershipCondition && firstMatch.PermissionSetName == "Nothing") { PermissionSet fullTrust = new PermissionSet(PermissionState.Unrestricted); CodeGroup gacGroup = new UnionCodeGroup(new GacMembershipCondition(), new PolicyStatement(fullTrust)); // now, walk the current groups and insert our new group immediately before the old Gac group // we'll need to use heuristics for this: it will be an UrlMembershipCondition group with full trust CodeGroup newRoot = new FirstMatchCodeGroup(rootGroup.MembershipCondition, rootGroup.PolicyStatement); foreach (CodeGroup childGroup in rootGroup.Children) { // is this the target old $Gac$ group? // insert our new GacMembershipCondition group ahead of it if ((childGroup is UnionCodeGroup) && (childGroup.MembershipCondition is UrlMembershipCondition) && childGroup.PolicyStatement.PermissionSet.IsUnrestricted()) { if (null != gacGroup) { newRoot.AddChild(gacGroup); gacGroup = null; } } // append this group to the root group // AddChild itself does a deep Copy to get any // child groups so we don't need one here newRoot.AddChild(childGroup); } policyLevel.RootCodeGroup = newRoot; } } } } return policyLevel; #pragma warning restore 618 } private sealed class ApplicationResumeStateContainer { private static readonly WaitCallback _tpCallback = ResumeCallback; private readonly HostingEnvironment _hostEnv; private readonly IntPtr _resumeState; internal ApplicationResumeStateContainer(HostingEnvironment hostEnv, IntPtr resumeState) { _hostEnv = hostEnv; _resumeState = resumeState; } // schedules resume for execution on a new thread // unsafe since we don't care about impersonation, identity, etc. internal void Resume() { ThreadPool.UnsafeQueueUserWorkItem(_tpCallback, this); } private static void ResumeCallback(object state) { ApplicationResumeStateContainer container = (ApplicationResumeStateContainer)state; try { container._hostEnv.ResumeApplication(container._resumeState); } catch (AppDomainUnloadedException) { // AD unloads aren't considered a failure, ---- } } } [SecurityPermission(SecurityAction.LinkDemand, Unrestricted = true)] private static class AspNetAppDomainManager { internal static Type GetAspNetAppDomainManagerType(bool overrideHostExecutionContextManager, bool overrideHostSecurityManager) { if (!overrideHostExecutionContextManager && !overrideHostSecurityManager) { // A custom AppDomainManager isn't necessary for this AppDomain. return null; } else { // A custom AppDomainManager is necessary for this AppDomain. // See comment on the generic type for further information. Type openGenericType = typeof(AspNetAppDomainManagerImpl<,>); Type closedGenericType = openGenericType.MakeGenericType( (overrideHostExecutionContextManager) ? typeof(AspNetHostExecutionContextManager) : typeof(object), (overrideHostSecurityManager) ? typeof(AspNetHostSecurityManager) : typeof(object) ); return closedGenericType; } } // This AppDomainManager may have been set because we need it for Task support, because this is a partial-trust // AppDomain, or both. Normally we would store this data in the AppDomain's ambient data store (AppDomain.SetData), // but the AppDomainManager instance is initialized in the new AppDomain before the original AppDomain has a chance to // call SetData, so in this AppDomain the call to GetData is useless. However, we can use the AppDomainManager type // itself to carry the necessary information in the form of a generic type parameter. If a custom // HostExecutionContextManager or HostSecurityManager is necessary, the generic type parameters can tell us that. // A generic type parameter of "object" means that this particular sub-manager isn't necessary. private sealed class AspNetAppDomainManagerImpl : AppDomainManager where THostExecutionContextManager : class, new() where THostSecurityManager : class, new() { private readonly HostExecutionContextManager _hostExecutionContextManager = CreateHostExecutionContextManager(); private readonly HostSecurityManager _hostSecurityManager = CreateHostSecurityManager(); public override HostExecutionContextManager HostExecutionContextManager { get { return _hostExecutionContextManager ?? base.HostExecutionContextManager; } } public override HostSecurityManager HostSecurityManager { get { return _hostSecurityManager ?? base.HostSecurityManager; } } private static HostExecutionContextManager CreateHostExecutionContextManager() { object hostExecutionContextManager = new THostExecutionContextManager(); Debug.Assert(hostExecutionContextManager is HostExecutionContextManager || hostExecutionContextManager.GetType() == typeof(object), "THostExecutionContextManager was an unexpected type!"); return hostExecutionContextManager as HostExecutionContextManager; } private static HostSecurityManager CreateHostSecurityManager() { object hostSecurityManager = new THostSecurityManager(); Debug.Assert(hostSecurityManager is HostSecurityManager || hostSecurityManager.GetType() == typeof(object), "THostSecurityManager was an unexpected type!"); return hostSecurityManager as HostSecurityManager; } } private sealed class AspNetHostSecurityManager : HostSecurityManager { private PermissionSet Nothing = new PermissionSet(PermissionState.None); private PermissionSet FullTrust = new PermissionSet(PermissionState.Unrestricted); private HostSecurityPolicyResolver hostSecurityPolicyResolver = null; public override HostSecurityManagerOptions Flags { get { return HostSecurityManagerOptions.HostResolvePolicy; } } [SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.SerializationFormatter)] public override PermissionSet ResolvePolicy(Evidence evidence) { if (base.ResolvePolicy(evidence).IsUnrestricted()) { return FullTrust; } if (!String.IsNullOrEmpty(HttpRuntime.HostSecurityPolicyResolverType) && hostSecurityPolicyResolver == null) { hostSecurityPolicyResolver = Activator.CreateInstance( Type.GetType(HttpRuntime.HostSecurityPolicyResolverType)) as HostSecurityPolicyResolver; } if (hostSecurityPolicyResolver != null) { switch (hostSecurityPolicyResolver.ResolvePolicy(evidence)) { case HostSecurityPolicyResults.FullTrust: return FullTrust; case HostSecurityPolicyResults.AppDomainTrust: return HttpRuntime.NamedPermissionSet; case HostSecurityPolicyResults.Nothing: return Nothing; case HostSecurityPolicyResults.DefaultPolicy: break; } } if (HttpRuntime.PolicyLevel == null || HttpRuntime.PolicyLevel.Resolve(evidence).PermissionSet.IsUnrestricted()) return FullTrust; else if (HttpRuntime.PolicyLevel.Resolve(evidence).PermissionSet.Equals(Nothing)) return Nothing; else return HttpRuntime.NamedPermissionSet; } } } private static void PopulateDomainBindings(String domainId, String appId, String appName, String appPath, VirtualPath appVPath, AppDomainSetup setup, IDictionary dict) { // assembly loading settings // We put both the old and new bin dir names on the private bin path setup.PrivateBinPathProbe = "*"; // disable loading from app base setup.ShadowCopyFiles = "true"; setup.ApplicationBase = appPath; setup.ApplicationName = appName; setup.ConfigurationFile = HttpConfigurationSystem.WebConfigFileName; // Disallow code download, since it's unreliable in services (ASURT 123836/127606) setup.DisallowCodeDownload = true; // internal settings dict.Add(".appDomain", "*"); dict.Add(".appId", appId); dict.Add(".appPath", appPath); dict.Add(".appVPath", appVPath.VirtualPathString); dict.Add(".domainId", domainId); } private static Evidence GetDefaultDomainIdentity() { Evidence evidence = AppDomain.CurrentDomain.Evidence; // CurrentDomain.Evidence returns a clone so we can modify it if we need bool hasZone = evidence.GetHostEvidence() != null; bool hasUrl = evidence.GetHostEvidence() != null; if (!hasZone) evidence.AddHostEvidence(new Zone(SecurityZone.MyComputer)); if (!hasUrl) evidence.AddHostEvidence(new Url("ms-internal-microsoft-asp-net-webhost-20")); return evidence; } private static int s_domainCount = 0; private static Object s_domainCountLock = new Object(); private static String ConstructAppDomainId(String id) { int domainCount = 0; lock (s_domainCountLock) { domainCount = ++s_domainCount; } return id + "-" + domainCount.ToString(NumberFormatInfo.InvariantInfo) + "-" + DateTime.UtcNow.ToFileTime().ToString(); } internal LockableAppDomainContext GetLockableAppDomainContext (string appId) { lock (this) { LockableAppDomainContext ac; if (!_appDomains.TryGetValue(appId, out ac)) { ac = new LockableAppDomainContext(); _appDomains.Add (appId, ac); } return ac; } } // take a copy of _appDomains collection so that it can be used without locking on ApplicationManager private Dictionary CloneAppDomainsCollection() { lock (this) { return new Dictionary(_appDomains, StringComparer.OrdinalIgnoreCase); } } private static Configuration GetAppConfigCommon(IConfigMapPath configMapPath, string siteID, string appSegment) { WebConfigurationFileMap fileMap = new WebConfigurationFileMap(); string dir = null; string fileName = null; string subDir = "/"; // add root mapping configMapPath.GetPathConfigFilename(siteID, subDir, out dir, out fileName); if (dir != null) { fileMap.VirtualDirectories.Add(subDir, new VirtualDirectoryMapping(Path.GetFullPath(dir), true)); } // add subdir mappings string[] subDirs = appSegment.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries); foreach (string s in subDirs) { subDir = subDir + s; configMapPath.GetPathConfigFilename(siteID, subDir, out dir, out fileName); if (dir != null) { fileMap.VirtualDirectories.Add(subDir, new VirtualDirectoryMapping(Path.GetFullPath(dir), true)); } subDir = subDir + "/"; } // open mapped web config for application return WebConfigurationManager.OpenMappedWebConfiguration(fileMap, appSegment, siteID); } [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Only ever called by the full-trust parent AppDomain.")] private static Configuration GetAppConfigGeneric(IApplicationHost appHost, string siteID, string appSegment, VirtualPath virtualPath, string physicalPath) { WebConfigurationFileMap fileMap = new WebConfigurationFileMap(); IConfigMapPathFactory configMapPathFactory2 = appHost.GetConfigMapPathFactory(); IConfigMapPath configMapPath = configMapPathFactory2.Create(virtualPath.VirtualPathString, physicalPath); return GetAppConfigCommon(configMapPath, siteID, appSegment); } private static Configuration GetAppConfigIISExpress(string siteID, string appSegment, string iisExpressVersion) { ExpressServerConfig serverConfig = (ExpressServerConfig)ServerConfig.GetDefaultDomainInstance(iisExpressVersion); return GetAppConfigCommon(serverConfig, siteID, appSegment); } private sealed class AppDomainSwitches { public bool UseLegacyCas; public bool UseRandomizedStringHashAlgorithm; public void Apply(AppDomainSetup setup) { List switches = new List(); if (UseLegacyCas) { // Enables the AppDomain to use the legacy CAS model for compatibility switches.Add("NetFx40_LegacySecurityPolicy"); } if (UseRandomizedStringHashAlgorithm) { switches.Add("UseRandomizedStringHashAlgorithm"); } if (switches.Count > 0) { setup.SetCompatibilitySwitches(switches); } } } // This class holds information about the environment that is hosting ASP.NET. The particular design of this class // is that the information is computed once and stored, and the methods which compute the information are private. // This prevents accidental misuse of this type via querying the environment after user code has had a chance to // run, which could potentially affect the environment itself. private static class EnvironmentInfo { public static readonly bool IsStringHashCodeRandomizationDetected = GetIsStringHashCodeRandomizationDetected(); public static readonly bool WasLaunchedFromDevelopmentEnvironment = GetWasLaunchedFromDevelopmentEnvironmentValue(); private static bool GetIsStringHashCodeRandomizationDetected() { // known test vector return (StringComparer.InvariantCultureIgnoreCase.GetHashCode("The quick brown fox jumps over the lazy dog.") != 0x703e662e); } // Visual Studio / WebMatrix will set DEV_ENVIRONMENT=1 when launching an ASP.NET host in a development environment. private static bool GetWasLaunchedFromDevelopmentEnvironmentValue() { try { string envVar = Environment.GetEnvironmentVariable("DEV_ENVIRONMENT", EnvironmentVariableTarget.Process); return String.Equals(envVar, "1", StringComparison.Ordinal); } catch { // We don't care if we can't read the environment variable; just treat it as not present. return false; } } } } }