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