1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
|
//------------------------------------------------------------------------------
// <copyright file="CustomLoaderHelper.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
namespace System.Web.Hosting {
using System;
using System.Globalization;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.Remoting;
using System.Runtime.Versioning;
// Used to locate a custom loader implementation within a bin-deployed assembly.
internal sealed class CustomLoaderHelper : MarshalByRefObject {
// the first framework version where the custom loader feature was implemented
private static readonly string _customLoaderTargetFrameworkName = new FrameworkName(".NETFramework", new Version(4, 5, 1)).ToString();
private static readonly string _customLoaderAssemblyName = typeof(CustomLoaderHelper).Assembly.FullName;
private static readonly string _customLoaderTypeName = typeof(CustomLoaderHelper).FullName;
private static readonly Guid IID_ICustomLoader = new Guid("50A3CE65-2F9F-44E9-9094-32C6C928F966");
// Instances of this type should only ever be created via reflection (see call to CreateObjectAndUnwrap
// in GetCustomLoader).
private CustomLoaderHelper() { }
internal static IObjectHandle GetCustomLoader(ICustomLoaderHelperFunctions helperFunctions, string appConfigMetabasePath, string configFilePath, string customLoaderPhysicalPath, out AppDomain newlyCreatedAppDomain) {
// Step 1: Does the host allow custom loaders?
bool? customLoaderIsEnabled = helperFunctions.CustomLoaderIsEnabled;
if (customLoaderIsEnabled.HasValue) {
if ((bool)customLoaderIsEnabled) {
// The custom loader is enabled; move on to the next step.
}
else {
// The custom loader is disabled, fail.
throw new NotSupportedException(ApplicationServicesStrings.CustomLoader_ForbiddenByHost);
}
}
else {
// The host hasn't set a policy, so we'll fall back to our default logic of checking the application's trust level.
if (!IsFullyTrusted(helperFunctions, appConfigMetabasePath)) {
throw new NotSupportedException(ApplicationServicesStrings.CustomLoader_NotInFullTrust);
}
}
// Step 2: Create the new AD
string binFolderPhysicalPath = helperFunctions.MapPath("/bin/");
AppDomainSetup setup = new AppDomainSetup() {
PrivateBinPathProbe = "*", // disable loading from app base
PrivateBinPath = binFolderPhysicalPath,
ApplicationBase = helperFunctions.AppPhysicalPath,
TargetFrameworkName = _customLoaderTargetFrameworkName
};
if (configFilePath != null) {
setup.ConfigurationFile = configFilePath;
}
AppDomain newAppDomainForCustomLoader = AppDomain.CreateDomain("aspnet-custom-loader-" + Guid.NewGuid(), null, setup);
try {
// Step 3: Instantiate helper in new AD so that we can get a reference to the loader
CustomLoaderHelper helper = (CustomLoaderHelper)newAppDomainForCustomLoader.CreateInstanceAndUnwrap(_customLoaderAssemblyName, _customLoaderTypeName,
ignoreCase: false,
bindingAttr: BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.CreateInstance | BindingFlags.Instance,
binder: null,
args: null,
culture: null,
activationAttributes: null);
ObjectHandle ohCustomLoader = helper.GetCustomLoaderImpl(customLoaderPhysicalPath);
// If we got this far, success!
newlyCreatedAppDomain = newAppDomainForCustomLoader;
return ohCustomLoader;
}
catch {
// If something went wrong, kill the new AD.
AppDomain.Unload(newAppDomainForCustomLoader);
throw;
}
}
private ObjectHandle GetCustomLoaderImpl(string customLoaderPhysicalPath) {
// Step 4: Find the implementation in the custom loader assembly
// Since we have set the private bin path, we can use this call to Assembly.Load
// to avoid the load-from context, which has weird behaviors.
AssemblyName customLoaderAssemblyName = AssemblyName.GetAssemblyName(customLoaderPhysicalPath);
Assembly customLoaderAssembly = Assembly.Load(customLoaderAssemblyName);
CustomLoaderAttribute customLoaderAttribute = customLoaderAssembly.GetCustomAttribute<CustomLoaderAttribute>();
if (customLoaderAttribute == null) {
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, ApplicationServicesStrings.CustomLoader_NoAttributeFound, customLoaderAssemblyName));
}
// Step 5: Instantiate the custom loader and return a reference back to native code
object customLoader = Activator.CreateInstance(customLoaderAttribute.CustomLoaderType);
// This check isn't strictly necessary since the unmanaged layer will handle QueryInterface failures
// appropriately, but we have an opportunity to provide a better error message at this layer.
if (!ObjectImplementsComInterface(customLoader, IID_ICustomLoader)) {
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, ApplicationServicesStrings.CustomLoader_MustImplementICustomLoader, customLoader.GetType()));
}
return new ObjectHandle(customLoader);
}
private static bool IsFullyTrusted(ICustomLoaderHelperFunctions helperFunctions, string appConfigMetabasePath) {
// The managed configuration system hasn't yet been instantiated but the IIS native config system understands
// ASP.NET configuration and honors hierarchy and section locking.
try {
// Must exactly match <trust level="Full" />, as this is what ApplicationManager expects.
string trustLevel = helperFunctions.GetTrustLevel(appConfigMetabasePath);
return String.Equals("Full", trustLevel, StringComparison.Ordinal);
}
catch {
// If any of the sections are locked or there is a config error, bail.
return false;
}
}
private static bool ObjectImplementsComInterface(object o, Guid iid) {
IntPtr pUnknown = IntPtr.Zero;
IntPtr pInterface = IntPtr.Zero;
try {
pUnknown = Marshal.GetIUnknownForObject(o); // AddRef
int hr = Marshal.QueryInterface(pUnknown, ref iid, out pInterface); // AddRef
return (hr == 0 && pInterface != IntPtr.Zero);
}
finally {
if (pUnknown != IntPtr.Zero) {
Marshal.Release(pUnknown);
}
if (pInterface != IntPtr.Zero) {
Marshal.Release(pInterface);
}
}
}
}
}
|