Do you like this site? Why can't you tell it to your Friend :)

Thursday, June 23, 2016

[ C# ] -Declarative and Imperative techniques for security actions

1. Introduction


In the previous article, we looked into “Assembly level security”. In this article, we are going to explore method level security actions with the help of permission to access environment variable.

In windows operating system, permissions are first monitored by the Operating system through its user account and role(s). For Example, a user account that belongs to administrators group can have access to the system32 folder and can read sensitive information such as system environment variable whilst same cannot be done by a Guest User. Assembly level security (Discussed in the previous article) comes next to this OS security. The third level of security can be achieved through methods. In dotnet application world, to access a secured resource, first we should have access to the resource from Operating System (Decided by the logged in user and his/her membership with a role like admin or guest) standpoint, then the dotnet assembly should have the proper grant(s) for the access and finally comes the method which can claim for the access or even reject the access. This kind of layering is shown in the below picture:

Fig 1. Code Access Security - Layers
Fig 1. Code Access Security - Layers

The “Method Level Security” comes with different security actions and those are listed below:


  1. Demand Action
  2. Link Demand Action
  3. Inheritance Demand Action
  4. Deny Action
  5. PermitOnly Action
  6. Assert Action

All the above said security actions can be employed through two different methods. One is “Declarative Method” and the other one is “Imperative Method”. In this article, we will explore each method with the help of sample application and demonstration videos.

2. The example

Have a look at the example application screenshot which is given below:

Fig 2. Example - CAS Method Level Security
Fig 2. Example - CAS Method Level Security

The textboxes (Marked as 1, 2) are used to display the value of the environment variables UserName and SecTest. The two read buttons (Marked as 3, 4) try to read those environment variables and sets the retrieved values in the corresponding textboxes 1 and 2.  

The Radio button options (Marked as 5) invoke the methods which had applied with the corresponding security attributes. We are going to debug the methods, by picking an option here (marked as 5) and hitting the Read buttons. 

The check box (Marked as 6) is used to explain how the security actions like Deny and Demand are invoked through imperative method. One can use the same imperative technique for other security actions also.

3. Declarative Method


In the declarative method, security restrictions are enforced through security attributes. These attributes can be applied at the assembly level, class level and also at methods level. In the previous article, we saw the security attributes applied at the assembly level. In this article, we will learn how to apply "Environment Permission" at class method and class level. We are going to explore above said six security actions in this article. Like the previous article, we are going to use the EnvironmentPermission to read the UserName and SecTest environment variables. Let us start one by one.

3.1 Demand and Deny Security Actions


The “Demand Security Action” looks for the permission grant, in all the methods which participate in the call stack. For example, if Function “fx” demands the permission to read a particular environment variable say; X, then all the functions in the call stack should have the demanded permission. The “Deny Action” is used to explicitly refuse any security permission. Before we explore these, first we need to add the required using directives as shown in the below code:

//Sample 01: Required Using Directive
using System.Security;
using System.Security.Permissions;
using System.Diagnostics;

After adding the required using directive, in the read button handler below piece of codes are added. Both the click event handlers create the object of SecMethodsCheck and call the function GetEnv_DemandLevel1 by passing the corresponding Environment variable names.

//Sample 02: Get Environment Variables
private void btnReadUName_Click(object sender, EventArgs e)
{
//2.1: Demand Security action
if (radDemand.Checked == true)
{
    SecMethodsCheck obj = new SecMethodsCheck();
    txtUserName.Text = obj.GetEnv_DemandLevel1("UserName");
}
.
.

private void btnReadSecTest_Click(object sender, EventArgs e)
{
if (radDemand.Checked == true)
{
    SecMethodsCheck obj = new SecMethodsCheck();
    txtSecTest.Text = obj.GetEnv_DemandLevel1("SecTest");
}
.
.

Below is the code for the class SecMethodsCheck which examines the Demand as well as Deny security actions:

public class SecMethodsCheck
{
    //Sample 03: DEMAND & Deny SECURITY ACTION
    //Sample 3.1: Deny Sectest Permission
    [EnvironmentPermission(SecurityAction.Deny, Read = "SecTest")]
    public string GetEnv_DemandLevel1(string EnVarName)
    {
        string EnvVal = "None";
        try
        {
            EnvVal = GetEnv_DemandLevel2(EnVarName);
        }
        catch (System.Security.SecurityException Ex)
        {
            Debugger.Log(1, "Information", Ex.Message);
        }
        return EnvVal;
    }

    //Sample 3.2: Catch the Exception
    private string GetEnv_DemandLevel2(string EnVarName)
    {
        string EnvVal = "None";
        try
        {
            EnvVal = GetEnvDemand(EnVarName);
        }
        catch (System.Security.SecurityException Ex)
        {
            Debugger.Log(1, "Information", Ex.Message);
        }
        return EnvVal;
    }

    //Sample 3.3: Make sure all the callers in the call stack has permission
    //              for accessing Sectest, UserName Env. Variables
    [EnvironmentPermission(SecurityAction.Demand, Read = "SecTest")]
    [EnvironmentPermission(SecurityAction.Demand, Read = "UserName")]
    private string GetEnvDemand(string EnVarName)
    {
        return Environment.GetEnvironmentVariable(EnVarName);
    }
};

Here, we have three member functions. The function GetEnv_DemandLevel1 calls GetEnv_DemandLevel2 and which in turn calls GetEnvDemand. At the very first method in the call stack, we denied the permission to read the SecTest environment variable.  The EnvironmentPermission attribute is used to deny the reading of SecTest environment variable by specifying the Security action SecurityAction.Deny. In the last function, we are demanding the read access to both the environment variables SecTest and UserName. The demand action is specified using the enumeration constant SecurityAction.Demand.

The first two methods invoke the other methods inside the Try block. In the catch block the SecurityException is caught and the message from it is printed to the debugger output window.

In this example, we will get the exception in the method GetEnv_DemandLevel2. This is because; GetEnvDemand, demands the Read permission for both SecTest and UserName. But, in the call stack, the method GetEnv_DemandLevel1 denies the read access to the environment variable SecTest. This makes calling the function GetEnvDemand, which is marked with Demand action, a failure. The Demand and Deny security actions are explained in the below video.


Video 1: SecurityAction.Demand and SecurityAction.Deny




3.2 Permit Only and LinkDemand Security Actions


The security action, ”Permit only” ensures to allow only the requested permission and it also denies all the other permissions to make sure it is permitting only the requested permission. The security action, “Link Demand” makes sure just the previous function in the call stack has the requested permission.  Unlike Demand action, the link demand checks the permission only one level deep in the call stack. Simply, it ensures the function, which called the current function to have the requested security permission.

In the SecMethodsCheck class, we added three functions to explore the usage of the PermitOnly and LinkDemand security actions. Now, have a look at the function given below:

//Sample 04: PERMIT & LINK DEMAND SECURITY ACTION
//Sample 4.1: By Saying permit only SecTest, we are skipping the default
//            Username permission given by the assembly

[EnvironmentPermission(SecurityAction.PermitOnly, Read = "SecTest")]
public string GetEnv_PermitOnlyLevel1(string EnVarName)
{
    string EnvVal = "None";
    try
    {
        EnvVal = GetEnv_PermitOnlyLevel2(EnVarName);
    }
    catch (System.Security.SecurityException Ex)
    {
        Debugger.Log(1, "Information", Ex.Message);
    }
    return EnvVal;
}

//Sample 4.2: Allow both SecTest and UserName
[EnvironmentPermission(SecurityAction.PermitOnly, Read = "UserName")]
[EnvironmentPermission(SecurityAction.PermitOnly, Read = "SecTest")]
private string GetEnv_PermitOnlyLevel2(string EnVarName)
{
    string EnvVal = "None";
    try
    {
        EnvVal = GetEnv_PermitOnly(EnVarName);
    }
    catch (System.Security.SecurityException Ex)
    {
        Debugger.Log(1, "Information", Ex.Message);
    }
    return EnvVal;
}

//Sample 4.3: Demand Sectest permission and Link Demand UserName Permission
[EnvironmentPermission(SecurityAction.Demand, Read = "SecTest")]
[EnvironmentPermission(SecurityAction.LinkDemand, Read = "UserName")]
//[EnvironmentPermission(SecurityAction.Demand, Read = "UserName")]
private string GetEnv_PermitOnly(string EnVarName)
{
    return Environment.GetEnvironmentVariable(EnVarName);
}

In the above example, the function GetEnv_PermitOnlyLevel1 is allowing only the Read access to the environment variable SecTest. As the security action is “Permit Only”, and hence the function automatically denies all other permissions. This means that it will deny the permission to read the UserName environment variable here even though it has the grant from Zone (Remember, our application is configured as Intranet Zone).

The second function, GetEnv_PermitOnlyLevel2 allows access to both the environment variable UserName and SecTest. Note that the PermitOnly security action grants the permission to enter the function only and it does not have the capability of removing the denied access. For Example, the deny set in the UserName environment variable by the previous function (GetEnv_PermitOnlyLevel1) in the call stack cannot be removed.

The third function GetEnv_PermitOnly is demanding the permission to read SecTest environment variable and the demand will succeed as all the functions in the call stack is having demanded permission. If a demand succeeds, the caller is allowed to enter the function. In the case of Link Demand, the check will be stopped at the immediate caller of the function. In our example, this last function is marked with “LinkDemand” for the UserName environment variable. Hence, the username permission check will be stopped at the immediate caller say, GetEnv_PermitOnlyLevel2Since the immediate caller has the Demanded Permission for UserName, and the call to the function GetEnv_PermitOnly is allowed (But Reading the Environment variable is not allowed. This is explained in the video).

Video 2: SecurityAction.PermitOnly and SecurityAction.LinkDemand




3.3 InheritanceDemand Security Action


The base class can enforce security permission on all its derived class by making use of the “InheritanceDemand Security Action”. Say, for example, let us consider a class called “BaseA”, which marks the entire Class with FileIOPermission to claim Read access to C:\system32\Users.txt. Furthermore, we also consider that the class sets the Inheritance Demand security action to the FileIOPermission. Now, all the classes derived from the BaseA should also have the grant for FileIOPermission to read the text file. Let us explore this with our sample application.

The application uses a class library project to explain the InheritanceDemand security action. Before we dig into the code, look at the below screenshot:

Fig 3. Inheritance Demand Example - Class Relations
Fig 3. Inheritance Demand Example - Class Relations

The project, InhDemand is a VC# class library project (Marked as 1). It has a class called InhDemandBase (Marked as 3), which sets InheritanceDemand Security action on the EnvironmentPermission. This environment permission claims read access to SecTest environment variable. Our SecMethods windows form project (Marked as 2) defines a class called InhDemandDerived (Marked a 4), which is derived from the class InhDemandBase (Marked as 5). The below screen shot shows creating the class library project:

Fig 4. Adding Class Library Project

Once the class library project is created, we can add a method to the class so that it can be accessed outside of this class library project. Have a look at the code below:

//Sample 06: Required Using Directive
using System.Security;
using System.Security.Permissions;
using System.Diagnostics;
namespace InhDemand
{
    //Sample 07: Inheritance Demand
    [EnvironmentPermission(SecurityAction.InheritanceDemand, Read = "SecTest")]
    public class InhDemandBase
    {
        public string ReadEnvBase(string envName)
        {
            Debugger.Log(1, "Information", Environment.GetEnvironmentVariable(envName));
            Debugger.Log(1, "Information", Environment.NewLine);
            return Environment.GetEnvironmentVariable(envName);
        }
    }
}


Here, we claimed the Environment permission to read the SecTest environment variable. Also, we specified the InheritanceDemand security action by using the enumeration constant SecurityAction.InheritanceDemand in the Security attribute. The method ReadEnvBase, just like our other examples, reads the SecTest environment variable and returns that to the caller.

After defining the class, add the class library project to our executable project. The steps are shown in the below screenshot:

Fig 5. Adding a reference to the Class Library Project

After the reference is added to the project, a using directive is added to the SecMethods.cs project. That code is shown below:

//Sample 08: Referring different Assembly
using InhDemand;

Now, look at the below code which written in our Exe project. The class InhDemandDerived is derived from class InhDemandBase which is defined in the class library project.

public class InhDemandDerived : InhDemandBase
{
    public string ReadEnv(string EnvName)
    {
        Debugger.Log(1, "Information", Environment.GetEnvironmentVariable(EnvName));
        Debugger.Log(1, "Information", Environment.NewLine);
        return Environment.GetEnvironmentVariable(EnvName);
    }
}

Here, we derived the class InhDemandDerived from our base class InhDemandBase. In this class, we are accessing the base class method GetEnvironmentVariable

While the client code creates the instance of InhDemandDerived, the security action enforced by the base class is checked. If the class InhDemandDerived had granted the permission for reading the SecTest environment variable, then we don’t have any issues. Otherwise, an exception is thrown when we try to enter a method which tries to create the instance of InhDemandDerived. This securityAction is explained in the below video:

Video 3: Inheritance Demand Security Action



3.4 Assert Security Action


The “Assert Action” is a security relaxing action. With this security action, a denied permission can be relaxed to make an entry to the function. Consider the below code now:

//sample 8.0: ASSERT SECURITY ACTION
[EnvironmentPermission(SecurityAction.Deny, Read = "UserName")]
[EnvironmentPermission(SecurityAction.Deny, Read = "SecTest")]
public string GetEnv_AssertLevel1(string EnVarName)
{
    string EnvVal = "None";
    try
    {
        EnvVal = GetEnv_AssertLevel2(EnVarName);
    }
    catch (System.Security.SecurityException Ex)
    {
        Debugger.Log(1, "Information", Ex.Message);
    }
    return EnvVal;
}

[EnvironmentPermission(SecurityAction.Assert, Read = "SecTest")]
public string GetEnv_AssertLevel2(string EnVarName)
{
    string EnvVal = "None";
    try
    {
        EnvVal = GetEnv_Assert(EnVarName);
    }
    catch (System.Security.SecurityException Ex)
    {
        Debugger.Log(1, "Information", Ex.Message);
    }
    return EnvVal;
}

//Sample 8.1: First Try Demanding Only UserName and then try with only SecTest
//            Here, Assert overrides the deny permission
[EnvironmentPermission(SecurityAction.Demand, Read = "SecTest")]
//[EnvironmentPermission(SecurityAction.Demand, Read = "UserName")]
public string GetEnv_Assert(string EnVarName)
{
    return Environment.GetEnvironmentVariable(EnVarName);
}

In the above example, read access to both the environment variables SecTest and UserName are denied at the function GetEnv_AssertLevel1. Then, in the next function GetEnv_AssertLevel2, the security action denied in the previous function call is revoked by setting the “Assert Security Action” on the security permission EnvironmentPermission.

In the final function call, we can attempt our claim for demanding only the  UserName access permission and then make an attempt to claim only the Read access to SecTest. How an “Assert Permission” behaves is shown in the below-given video.

Video 4: Assert Security Action





4. Security Actions through Imperative Method

 
In this method, the security actions are carried out by Dotnet Framework’s API function calls. First, we create a permission objects and then we will carry out the security actions through a function call. For Example, in the imperative method, to deny the “Read Access” to UserName environment variable, we need to create EnvironmentPermission object and then perform the required action like deny. Have a look at the below C-Sharp code:

//Sample 9.0: Demand and Deny Imperative Technique
public string GetEnv_Imperative_DemandLevel1(string EnVarName)
{
    string EnvVal = "None";
    try
    {
        //9.1: Deny the Sectest permission - Imperative
        EnvironmentPermission envP = new EnvironmentPermission(EnvironmentPermissionAccess.Read, "SecTest");
        envP.Deny();
        EnvVal = GetEnv_Imperative_DemandLevel2(EnVarName);
    }
    catch (System.Security.SecurityException Ex)
    {
        Debugger.Log(1, "Information", Ex.Message);
    }
    return EnvVal;
}

private string GetEnv_Imperative_DemandLevel2(string EnVarName)
{
    string EnvVal = "None";
    try
    {
        EnvVal = GetEnvDemand_Imperative(EnVarName);
    }
    catch (System.Security.SecurityException Ex)
    {
        Debugger.Log(1, "Information", Ex.Message);
    }
    return EnvVal;
}

private string GetEnvDemand_Imperative(string EnVarName)
{
    //9.2: Demand the Sectest permission - Imperative
    EnvironmentPermission envP = new EnvironmentPermission(EnvironmentPermissionAccess.Read, EnVarName);
    envP.Demand();
    return Environment.GetEnvironmentVariable(EnVarName);
}
}

In the above code, we can see how the read access to the SecTest Environment variable is denied. First, an object of type EnvironmentPermission is created by making use of enumeration constant EnvironmentPermissionAccess.Read. After the object is created, a call to “Deny() method” is performed and this will deny the permission to Read value from the SecTest Environment variable. Similarly, in the final function GetEnvDemand_Imperative, We create Permission Object and then we make a call to "Demand() method". Note how the “Demand Security Action” is performed at runtime and the demand may be for SecTest or UserName depending what is passed to the function. Applying the security demand like this is called "Imperative Method".

In the above code example, one cannot read the username environment variable as it is denied in the function GetEnv_Imperative_DemandLevel1. Furthermore, the call to Demand() throws an exception when the demand is for SecTest. This is explained in the below video.

Video 5: Security Actions through Imperative Method




Source Code: Download

   

Like this site? Tell it to your Firend :)