3

Reading docs/prototyping and it seems I do not understand the big picture in the new .NET 4 security model. Ok, I understand and even prototyped the 3-layer security model (transparent code, security-safe-critical code, and security-critical code). Works fine.

What I cannot understand is how it relates to the permissions. I need to have SecurityTransparent code that does not have any permissions except to execute and then I need to have SecurityCritical code that has all permissions. I tried playing in the ConsoleApp. In the ConsoleApp in the default domain SecurityTransparent code has all permissions and so could actually do whatever it wants, e.g. create files, write reg keys, etc. And if I create "sandbox" (aka AppDomain) and restrict permissions all code running the domain has such permissions, for example, both SecurityTransparent or SecurityCritical has no access to e.g. Files, Registry and etc. So, I clearly do not understand what is this 3 layers security model (transparent code, security-safe-critical code, and security-critical code) gives us if security critical code when running in the sandbox has same permissions level as security transparent.

Another question is what is "Full Trust" assembly property reeeeeeallly means and when it could be anything other than Full Trust and how it all affects security.

Update 1 - Code

class Program
{
    public static PermissionSet GetPermissionSet()
    {
        var permissions = new PermissionSet(PermissionState.None);
        permissions.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));

        // Just to be able to write to the Console
        permissions.AddPermission(new FileIOPermission(PermissionState.Unrestricted));

        return permissions;
    }

    public static string GetDomainInfo(AppDomain domain)
    {
        StringBuilder sb = new StringBuilder();
        //check the domain trust
        sb.AppendFormat("Domain Is Full Trusted: {0} \n", domain.IsFullyTrusted);
        //show the number of the permission granted to the assembly
        sb.AppendFormat("\nPermissions Count: {0} \n\n\n", domain.PermissionSet.Count);
        sb.AppendFormat("\nPermissions Is Homogenous: {0} \n\n\n", domain.IsHomogenous);
        return sb.ToString();
    }

    public static StrongName GetTypeAssemblyStringName(Type type)
    {
        var name = type.Assembly.GetName();
        return new StrongName(new StrongNamePublicKeyBlob(name.GetPublicKey()), name.Name, name.Version);
    }

    public static AppDomain CreateDomain()
    {
        //create  the AppDomainSetup
        AppDomainSetup info = new AppDomainSetup();

        //set the path to the assembly to load.
        info.ApplicationBase = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

        //create the domain
        AppDomain domain = AppDomain.CreateDomain(
            "MyCoolDomain", 
            null, 
            info, 
            GetPermissionSet(),
            GetTypeAssemblyStringName(typeof(SecurityCriticalApi1Class)), /* Get's full trust but operates as partial trust */
            GetTypeAssemblyStringName(typeof(SecurityCriticalApi2Class)), /*                       same                     */
            GetTypeAssemblyStringName(typeof(SecuritySafeApiClass))       /*                       same                     */
            );

        return domain;

    }

    public static void ShowAssemblyInfo(Type type)
    {
        var a = Assembly.GetAssembly(type);
        ShowAssemblyInfo(a);
    }

    public static void ShowAssemblyInfo(Assembly a)
    {
        var sb = new StringBuilder();

        //show the transparency level
        sb.AppendFormat("\nAssembly: {0}\n", a.GetName().Name);
        sb.AppendFormat("Security Rule Set: {0}\n", a.SecurityRuleSet);

        //show if it is full trusted
        sb.AppendFormat("Is Fully Trusted: {0}\n", a.IsFullyTrusted);

        try
        {
            sb.AppendFormat("Permissions Count: {0} \n", a.PermissionSet.Count);
        }
        catch (Exception ex)
        {
            sb.AppendFormat("Error while trying to get the Permission Count: {0} \n", ex.Message);
        }

        Console.WriteLine(sb);
    }

    static void Main(string[] args)
    {
        try
        {
            Console.WriteLine("Started");

            var domain = CreateDomain();

            Console.WriteLine(GetDomainInfo(domain));

            Type t = typeof(TransparentApiClass);
            ObjectHandle handle = Activator.CreateInstanceFrom(domain, t.Assembly.ManifestModule.FullyQualifiedName, t.FullName);
            var api = (TransparentApiClass)handle.Unwrap();
            api.DoIt();

            Console.WriteLine("Stopped");
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
        }
    }
}

[SecuritySafeCritical]
public class SecuritySafeApiClass
{
    public void DoIt()
    {
        Console.WriteLine("SecuritySafeApiClass calling SecurityCritical API");

        var api = new SecurityCriticalApi1Class();
        api.DoIt();

        Console.WriteLine("SecuritySafeApiClass Done");
    }
}


public class SecurityCriticalApi1Class
{
    public void DoIt()
    {
        Console.WriteLine("SecurityCriticalApi1Class");

        // Throws exception - require permission
        var key = Registry.ClassesRoot.GetSubKeyNames();

        Console.WriteLine("SecurityCriticalApi1Class Done");
    }
}

If I understand things right my problem description could be shorten - in my case code loaded with Full Trust by specifying corresponding strong names in the domain setup acts as partial trust, mean operates with same permissions set as doman.

Update 2
I tried to use Assert

public class SecuritySafeApiClass
{
    public void Assert()
    {
        var permissions = new PermissionSet(PermissionState.Unrestricted);
        permissions.AddPermission(new RegistryPermission(PermissionState.Unrestricted));
        permissions.Assert();
    }

    public void DoIt()
    {
        Console.WriteLine("SecuritySafeApiClass calling SecurityCritical API");

        Assert();

        var api = new SecurityCriticalApi1Class();
        api.DoIt();

        Console.WriteLine("SecuritySafeApiClass Done");
    }
}

And it not helped :( :(
The error I'm getting is:

Failed to execute action. error System.Security.SecurityException: Request for the permission of type 'System.Security.Permissions.RegistryPermission, mscorlib, Version=4.0.0.0, Culture=neutral,PublicKeyToken=b77a5c561934e089' failed.
  at System.Security.CodeAccessSecurityEngine.Check(Object demand, StackCrawlMark& stackMark, Boolean isPermSet)
  at System.Security.CodeAccessSecurityEngine.Check(CodeAccessPermission cap, StackCrawlMark& stackMark)
  at System.Security.CodeAccessPermission.Demand()
  at Microsoft.Win32.RegistryKey.CheckPermission(RegistryInternalCheck check, String item, Boolean subKeyWritable, RegistryKeyPermissionCheck subKeyCheck)
  at Microsoft.Win32.RegistryKey.GetSubKeyNames()
  at SecurityCriticalApi1.SecurityCriticalApi1Class.DoIt() in C:\Dev\SecurityProofOfConcept\SecurityCriticalApi1\SecurityCriticalApi1Class.cs:line 20
  at SecuritySafeApi.SecuritySafeApiClass.DoIt() in C:\Dev\SecurityProofOfConcept\SecuritySafeApi\SecuritySafeApiClass.cs:line 29
  at TransparentApi.TransparentApiClass.b__2_0()
  at TransparentApi.TransparentApiClass.TryToExecute(Action a, String caller)
The action that failed was:
Demand
The type of the first permission that failed was:
System.Security.Permissions.RegistryPermission
The Zone of the assembly that failed was:
MyComputer
Glenn Slayden
  • 17,543
  • 3
  • 114
  • 108
Tom3m
  • 81
  • 5
  • 1
    Could you show the code, which demonstrate, that *security critical code when running in the sandbox has same permissions level as security transparent*. – user4003407 Apr 04 '16 at 17:51
  • Added code to the post – Tom3m Apr 04 '16 at 18:14
  • 1
    You forgot to `Assert` required permissions. – user4003407 Apr 04 '16 at 18:38
  • Sorry, just to clarify, do you mean even if assemblt is loaded with Full Trust I must Assert required permission? Are there any links/docs to read about it? – Tom3m Apr 04 '16 at 18:44
  • 2
    Security transparent code are not allowed to do `Assert` and all security critical code are Full Trust. So that, you must do `Assert` from Full Trust assembly because it is the only place where `Assert` allowed. And you must do `Assert` to prevent stack walk from reaching your caller, which does not have requested permission. `Assert` is a way to say to security system: *it is I, who will do this privileged thing, does not validate permissions of caller*. – user4003407 Apr 04 '16 at 21:03
  • 2
    Stack walk modifiers (`Assert` and `PermitOnly`) are placed in current stack frame. That means, when method with stack walk modifiers returns to the caller, then all modifiers are implicitly reverted. So that, having method with only purpose to set modifier is useless, as all modifiers will be reverted when method returns. You must `Assert` from `DoIt` method directly, not from some method, called from `DoIt`. – user4003407 Apr 04 '16 at 21:14
  • 1
    Thank you PetSerAi! I have changed the way I assert to: (new PermissionSet(PermissionState.Unrestricted)).Assert(); and it started working as expected. But honestly I still feel confused. I always thought .NET CAS is based on the assemblies permissions. There is even Assembly.PermissionSet property. And I thought if assembly was loaded with Full Trust I don't need to do anything else as .NET already know my assebly has all permissions. And it seems I completely wrong (( What is the point from .NEt point of view to force me to call Assert if is already knows my assembly is Full Trus. – Tom3m Apr 05 '16 at 17:26
  • 2
    Purpose of `Demand` to validate whole call chain (not only immediate caller as `LinkDemand` does) to prevent low trusted code from luring high trusted code into doing things, which low trusted code not allowed to do. So that, if you want to lower security and allow some operation even if your caller does not allowed to do it, then you have to be explicit about it and call `Assert`. – user4003407 Apr 09 '16 at 10:07
  • I think I finally got it. Could you please comment on the assemblies & permissions. Am I write that now in Level 1 and Level 2 permissions are granted to the whole app domain and cannot be different for different assemblies? So, in other words code could be or full trust if loaded with full trust or could have permissions set specified for the app domain. PS: Thank you VERY MUCH for your answers! – Tom3m Apr 10 '16 at 11:58
  • 2
    Given that [the policy portion of code access security (CAS) has been made obsolete in the .NET Framework version 4](https://msdn.microsoft.com/en-us/library/ee191568%28v=vs.100%29.aspx), I do not see a way, how can you get per-assembly permissions in current .NET Framework version, unless you enable compatibility option. – user4003407 Apr 10 '16 at 18:47

0 Answers0