none
How to change permissions of AppDomain? RRS feed

  • Question

  • I use special method to create sandbox:

    internal static class Helper
    {
        public static AppDomain CreateSandbox()
        {
            Contract.Ensures(Contract.Result<AppDomain>() != null);
    
            var platform = Assembly.GetExecutingAssembly();
            var name = platform.FullName + ": Sandbox " + Guid.NewGuid();
            var setup = new AppDomainSetup { ApplicationBase = platform.Location };
            var permissions = new PermissionSet(PermissionState.None);
            permissions.AddPermission(new FileIOPermission(FileIOPermissionAccess.Read | FileIOPermissionAccess.PathDiscovery, platform.Location));
            var sandbox = AppDomain.CreateDomain(name, null, setup, permissions);
    
            Contract.Assume(sandbox != null);
    
            return sandbox;
        }
    }


    When I use created sandbox, I want to change permissions of it:

    sandbox = Security.Helper.CreateSandbox();
    sandbox.SetupInformation.ApplicationBase = Path.GetDirectoryName(path);
    sandbox.PermissionSet.AddPermission(new FileIOPermission(FileIOPermissionAccess.Read | FileIOPermissionAccess.PathDiscovery, path));

    But when I load assembly to it, I recieve exception:

    "Request for the permission of type 'System.Security.Permissions.FileIOPermission, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' failed."

    How to change permissions of AppDomain AFTER it creation?

    Saturday, November 12, 2011 8:58 PM

Answers

  • Hi Alex,

    After you create an AppDomain whith the overload taking a PermissionSet, you cannot change those permissions subsequently by adding new permissions. Also, in order to load an executable file, you will need to add a SecurityPermission(SecurityPermissionFlag.Execution) to your AppDomain's permission set. As a side note: AppDomain.Load() should only be used when you need to load an assembly into the current AppDomain. Use instead AppDomain.CreateInstanceAndUnwrap() like in the code below:

    // AppDomainPermissions.exe
    namespace AppDomainPermissions {
    
        using System;
        using System.Diagnostics.Contracts;
        using System.IO;
        using System.Reflection;
        using System.Security.Permissions;
    
        class Program {
            static void Main(string[] args) {
                string path = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
                string testFile = Path.Combine(path, "Test.txt");
                var sandbox = Helper.CreateSandbox();
                string assemblyName = "FileIO, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f730643f3bd3ff62"; // change to real token
                FileIO.Utils utils = (FileIO.Utils) sandbox.CreateInstanceAndUnwrap(assemblyName, "FileIO.Utils");
                Console.WriteLine(utils.GetFileText(testFile));
                Console.ReadKey(true);
            }
        }
    
        internal static class Helper {
            public static AppDomain CreateSandbox() {
                Contract.Ensures(Contract.Result<AppDomain>() != null);
                var platform = Assembly.GetExecutingAssembly();
                var name = platform.FullName + ": Sandbox " + Guid.NewGuid();
                var setup = new AppDomainSetup { ApplicationBase = Path.GetDirectoryName(platform.Location) };
    PermissionSet permissionSet = new PermissionSet();


    permissionSet.AddPermission(new FileIOPermission(FileIOPermissionAccess.Read | FileIOPermissionAccess.PathDiscovery, Path.GetDirectoryName(platform.Location))); permissionSet.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution)); var sandbox = AppDomain.CreateDomain(name, null, setup, permissionSet); Contract.Assume(sandbox != null); return sandbox; } } } // FileIO.dll namespace FileIO { using System; public class Utils : MarshalByRefObject { public string GetFileText(string textFileName) { return System.IO.File.ReadAllText(textFileName); } } }

    When posting exceptions, please make sure you include the InnerException and the stack trace.

    Marcel

    Sunday, November 13, 2011 3:05 PM
  • Hi Alex,

    In a homogenuous AppDomain the only allowed permission sets are FullTrust and the permission set defined when creating the domain. Once granted, the permission set cannot be extended anymore (AFAIK).

    It's understandable that you don't want to give your plugins FullTrust. So what you are left with is either the option of dynamically creating AppDomains that suit the needs of your specific plugins (before loading the plugin and using the simple sandboxing API), or of creating a shareable service library that the plugins can link against (if they need functionality that is not available to them because of the sandbox).

    Let's take a closer look at the second option: The class library used by the plugins would be decorated with the AllowPartiallyTrustedCallersAttribute and be marked with the SecuritySafeCriticalAttribute as needed and after careful considerations of all related security concerns. Before calling the method that needs the elevated permission, you would assert it (don't forget to revert back). Then you would install the library to the GAC. That's it. Now your sandboxed plugins can use elevated functionality without having to relax the sandbox.

    Edit: I would make use of [assembly: AllowPartiallyTrustedCallers(PartialTrustVisibilityLevel=NotVisibleByDefault)] in the commonly used class library and set AppDomainSetup.PartialTrustVisibleAssemblies as specified by the documentation. As SecuritySafeCritical is very dangerous, you should also put some effort in validating the library's caller.

    Marcel


    Sunday, November 20, 2011 7:10 PM

All replies

  • Hi Alex,

    After you create an AppDomain whith the overload taking a PermissionSet, you cannot change those permissions subsequently by adding new permissions. Also, in order to load an executable file, you will need to add a SecurityPermission(SecurityPermissionFlag.Execution) to your AppDomain's permission set. As a side note: AppDomain.Load() should only be used when you need to load an assembly into the current AppDomain. Use instead AppDomain.CreateInstanceAndUnwrap() like in the code below:

    // AppDomainPermissions.exe
    namespace AppDomainPermissions {
    
        using System;
        using System.Diagnostics.Contracts;
        using System.IO;
        using System.Reflection;
        using System.Security.Permissions;
    
        class Program {
            static void Main(string[] args) {
                string path = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
                string testFile = Path.Combine(path, "Test.txt");
                var sandbox = Helper.CreateSandbox();
                string assemblyName = "FileIO, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f730643f3bd3ff62"; // change to real token
                FileIO.Utils utils = (FileIO.Utils) sandbox.CreateInstanceAndUnwrap(assemblyName, "FileIO.Utils");
                Console.WriteLine(utils.GetFileText(testFile));
                Console.ReadKey(true);
            }
        }
    
        internal static class Helper {
            public static AppDomain CreateSandbox() {
                Contract.Ensures(Contract.Result<AppDomain>() != null);
                var platform = Assembly.GetExecutingAssembly();
                var name = platform.FullName + ": Sandbox " + Guid.NewGuid();
                var setup = new AppDomainSetup { ApplicationBase = Path.GetDirectoryName(platform.Location) };
    PermissionSet permissionSet = new PermissionSet();


    permissionSet.AddPermission(new FileIOPermission(FileIOPermissionAccess.Read | FileIOPermissionAccess.PathDiscovery, Path.GetDirectoryName(platform.Location))); permissionSet.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution)); var sandbox = AppDomain.CreateDomain(name, null, setup, permissionSet); Contract.Assume(sandbox != null); return sandbox; } } } // FileIO.dll namespace FileIO { using System; public class Utils : MarshalByRefObject { public string GetFileText(string textFileName) { return System.IO.File.ReadAllText(textFileName); } } }

    When posting exceptions, please make sure you include the InnerException and the stack trace.

    Marcel

    Sunday, November 13, 2011 3:05 PM
  • That is, if I want to add privileges to a domain in run-time, I can't do it?
    Sunday, November 13, 2011 4:33 PM
  • The documentation on the subject is very scarce, and since SetAppDomainPolicy() has been depracated, I don't know for another way.

    "One important point to note is that setting the AppDomain policy must be done before any code is loaded into the AppDomain, and may only be done once.  As soon as code gets loaded into the AppDomain any changes to the security policy of that domain will no longer have any effect."
    (http://blogs.msdn.com/b/shawnfa/archive/2004/10/25/247379.aspx=

    But why do you need to change the wheels under the car while driving? Isn't it enough to conditionally initialize an AppDomain to different default permissions, according to the applications requirements?

    Marcel

    Sunday, November 13, 2011 5:36 PM
  • My application is composed of host and plug-ins. I would like to limit the plugin's rights and increase it only for a while, at the request of the plugin. For example, I would like to disable the ability to play video in plug-ins. If plugin requires this possibility, I would like to prompt the user and give the right, based on the decision, to plugin.
    Sunday, November 13, 2011 11:21 PM
  • Alex,

    Have you considered modifying the design so that a plug-in that requires additional permissions may "request" these permissions via metadata that your application can read before creating the appdomain for the plug-in?

    Nicole

    Monday, November 14, 2011 5:31 PM
  • Hi Nicole,

    The problem, as I understand it, is that Alex wants the permissions to be granted/revoked on-the-fly based on the loaded plugin's request (something very much like UAC but for AppDomains). IMHO if this was possible (i.e. if we could dynamically elevate/downgrade the permissions on the sandboxed domain), this would "relax" the concept of a sandbox to the extent of rendering it meaningless. The whole idea is very similar to creating limited permissions AppDomains that do allow for full trust on specified, strong-named assemblies - a feature that allows evading the sandbox. The meta-data approach (like it is practices in technologies like MEF) is just a generalization of the latter. I think this is no real gain from a security point of view.

    Marcel

    Monday, November 14, 2011 6:46 PM
  • Hi!
    Nicole, Marcel rights, I want that the permissions to be granted/revoked on-the-fly based on the loaded plugin's request (like UAC).
    Nicole, Why do you say that this idea meaningless? I'll never give full rights to the plugin, but I would like to give certain rights as necessary.
    Tuesday, November 15, 2011 2:27 AM
  • Hi Alex,

    In a homogenuous AppDomain the only allowed permission sets are FullTrust and the permission set defined when creating the domain. Once granted, the permission set cannot be extended anymore (AFAIK).

    It's understandable that you don't want to give your plugins FullTrust. So what you are left with is either the option of dynamically creating AppDomains that suit the needs of your specific plugins (before loading the plugin and using the simple sandboxing API), or of creating a shareable service library that the plugins can link against (if they need functionality that is not available to them because of the sandbox).

    Let's take a closer look at the second option: The class library used by the plugins would be decorated with the AllowPartiallyTrustedCallersAttribute and be marked with the SecuritySafeCriticalAttribute as needed and after careful considerations of all related security concerns. Before calling the method that needs the elevated permission, you would assert it (don't forget to revert back). Then you would install the library to the GAC. That's it. Now your sandboxed plugins can use elevated functionality without having to relax the sandbox.

    Edit: I would make use of [assembly: AllowPartiallyTrustedCallers(PartialTrustVisibilityLevel=NotVisibleByDefault)] in the commonly used class library and set AppDomainSetup.PartialTrustVisibleAssemblies as specified by the documentation. As SecuritySafeCritical is very dangerous, you should also put some effort in validating the library's caller.

    Marcel


    Sunday, November 20, 2011 7:10 PM
  • Thank you, Marcel, for the excellent idea!
    Tuesday, November 22, 2011 3:33 AM