Question

I think I'm stuck with using .net 4.0 new security model. In general, I just want to load 3rd party assemblies into a sandbox. Sounds easy, right? Nevertheless...

I have 2 projects in my solution: CrossAppDomain.exe and UntrustedCode.dll. In CrossAppdomain I created following base class for all my marshallables:

public abstract class Marshallable : MarshalByRefObject, IDisposable
{
    [SecurityCritical]
    public override object InitializeLifetimeService()
    {
        return null;
    }

    public void Dispose()
    {
        if (!RemotingServices.IsTransparentProxy(this))
        {
            RemotingServices.Disconnect(this);
        }
    }
}

And created base class for objects i will work with

public abstract class BaseClass : Marshallable
{
}

In UntrustedCode.dll I created derrived class

public class UntrustedClass : BaseClass
{
}

To create instance of UntrustedClass i use following factory:

   public sealed class Factory
{
    public BaseClass Create(string assName, string typeName)
    {
        var domain = CreateAppDomain();

        ObjectHandle handle;
        try
        {
            // This throws SecurityException with informational message "RequestFailed"
            handle = domain.CreateInstance(typeof(AppDomainWorker).Assembly.FullName, typeof(AppDomainWorker).FullName);
        }
        catch (SecurityException)
        {
            // While this works fine...
            handle = Activator.CreateInstanceFrom(domain,
                                                      typeof(AppDomainWorker).Assembly.ManifestModule.FullyQualifiedName,
                                                      typeof(AppDomainWorker).FullName);
        }
        var worker = (AppDomainWorker)handle.Unwrap();
        worker.LoadAssemblies();

        var obj = worker.Create(assName, typeName);
        worker.Dispose();
        return obj;
    }

    private AppDomain CreateAppDomain()
    {
        var name = Guid.NewGuid().ToString();
        var permissions = new PermissionSet(PermissionState.None);
        permissions.AddPermission(new SecurityPermission(PermissionState.Unrestricted));
        permissions.AddPermission(new ReflectionPermission(PermissionState.Unrestricted));
        permissions.AddPermission(new FileIOPermission(PermissionState.Unrestricted));
        var appSetup = new AppDomainSetup
            {
                ApplicationName = name,
                ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase,
                ShadowCopyDirectories = Path.GetFullPath(@"..\..\..\UntrustedCode\bin"),
                ShadowCopyFiles = "true"
            };
        // Since Marshallable.InitializeLifetimeServices is overriden and marked with [SecurityCritical] 
        // we should add this assembly in full trusted list.
        // Otherwise. TypeLoadException is thrown with message "Inheritance security rules violated while overriding member: 
        // 'CrossAppDomains.Marshallable.InitializeLifetimeService()'. Security accessibility of the overriding method must 
        // match the security accessibility of the method being overriden.
        var sn = typeof (AppDomainWorker).Assembly.Evidence.GetHostEvidence<StrongName>();
        var domain = AppDomain.CreateDomain(name, null, appSetup, permissions, sn);
        return domain;
    }

    private sealed class AppDomainWorker : Marshallable
    {
        public BaseClass Create(string assName, string typeName)
        {
            var assembly = AppDomain.CurrentDomain.GetAssemblies()
                                    .Single(a => assName.StartsWith(a.GetName().Name));

            // Here TypeLoadException is thrown: Inheritance security rules violated by type: 'UntrustedCode.UntrustedClass'. 
            // Derived types must either match the security accessibility of the base type or be less accessible.
            var obj = (BaseClass)Activator.CreateInstanceFrom(assembly.Location, typeName).Unwrap();
            Debug.Assert(!RemotingServices.IsTransparentProxy(obj));
            return obj;
        }

        public void LoadAssemblies()
        {
            var assemblyName = AssemblyName.GetAssemblyName(Path.GetFullPath(@"..\..\..\UntrustedCode\bin\Debug\UntrustedCode.dll"));
            Assembly.Load(assemblyName);
        }
    }
}

And here where problem comes:

  1. In Factory.Create() i successed creating AppDomainWorker class only when I used Activator.CreateInstance from. While more straightforward AppDomain.CreateInstanceAndUnwrap failed. This looks so unsteady, I mean this is either bug or security hole. But okay, the workaround works

  2. In AppDomainWorker.Create() i get TypeLoadException: Inheritance security rules violated by type: 'UntrustedCode.UntrustedClass'. Derived types must either match the security accessibility of the base type or be less accessible. I dont know how to fix it. This is my problem

    P.S. I know about [assembly: SecurityRules(SecurityRuleSet.Level1)] , but I'd like to know how to get things working in .net 4.0 security model

Edit: After adding [assembly: AllowPartialTrustCallers] I got a bunk of new problems: I need explicitly mark with [SecuritySafeCritical] all code which creates nlog loggger using LogManager.GetCurrentClassLogger() and then all code that uses the initialized field with logger. It is not acceptable. So maybe there are other ways around?

Was it helpful?

Solution

I think, I just have to move classes used by UntrustedClass and code that use security critical code into separate assembly.

I.e. Move BaseClass, Marshallable, Factory into some Api.dll. Then mark the assembly with [APTCA] and decorate neccessary methods with [SecuritySafe]. Then any existing code should work without problems since [APTCA] is applied only for Api.dll.

This approach works on the sample project, but I cant say for sure if it suits to my current project. As soon as I implement it, i let you know.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top