C# Security : Permissions

The Framework uses permissions for both sandboxing and authorization. A permission acts as a gate that conditionally prevents code from executing. Sandboxing uses code access permissions; authorization uses identity and role permissions.

Although both follow a similar model, they feel quite different to use. Part of the reason for this is that they typically put you on a different side of the fence: with code access security, you're usually the untrusted party; with identity and role security, you're usually the untrusting party. Code access security is most often forced upon you by the CLR (for the benefit of the end user) whereas authorization is usually something you implement to prevent unprivileged callers from accessing your program.

As an application developer, you'll need to understand code access security to deal elegantly with execution restrictions. The CLR, by default, places execution restrictions on assemblies that run directly from a network path or web site. The solution—in one sentence—is to determine your assembly's security requirements and then declare them upfront with assembly attributes. Another use for code access security is to sandbox a less trusted assembly.

The main scenario for identity and role security is when writing middle tier or web application servers. You typically decide on a set of roles, and then for each method that you expose, you demand that callers are members of a particular role.



CodeAccessPermission and PrincipalPermission

There are two types of permissions:

CodeAccessPermission
The abstract base class for all code access permissions, such as FileIOPermission, ReflectionPermission, or PrintingPermission

PrincipalPermission
Describes an identity and/or role (e.g., "Mary" or "Human Resources")

The term permission is somewhat misleading in the case of CodeAccessPermission, because it suggests something has been granted. This is not the case. A CodeAccessPermission object describes a privileged operation.

For instance, a FileIOPermission object describes the privilege of being able to Read, Write, or Append to a particular set of files or directories. Such an object can be used in a variety of ways:

• To verify that you and all your callers have the rights to perform these actions (Demand)

• To verify that your immediate caller has the rights to perform these actions (LinkDemand)

• To declare your assembly's need to perform these actions (RequestMinimum)

• To request that your assembly be refused permission to perform these actions (RequestRefuse)

• To Assert your assembly-given rights to perform these actions, regardless of callers' privileges

• To Deny rights to perform these actions to code that you call

PrincipalPermission is much simpler. Its only security method is Demand, which checks that the specified user or role is valid given the current execution thread.



IPermission
Both CodeAccessPermission and PrincipalPermission implement the IPermission interface:

public interface IPermission
{
void Demand( );
IPermission Intersect (IPermission target);
IPermission Union (IPermission target);
bool IsSubsetOf (IPermission target);
IPermission Copy( );
}

The crucial method here is Demand. It performs a spot-check to see whether the permission or privileged operation is currently permitted, and it throws a SecurityException if not. If you're the untrusting party, you will be Demanding. If you're the untrusted party, code that you call will be Demanding.

For example, to ensure that only Mary can run management reports, you could write this:

new PrincipalPermission ("Mary", null).Demand( );
// ... run management reports

In contrast, suppose your assembly was sandboxed such that file I/O was prohibited, so the following line threw a SecurityException:

using (FileStream fs = new FileStream ("test.txt", FileMode.Create))
...

The Demand, in this case, is made by code that you call—in other words, FileStream's constructor. Decompiling FileStream reveals this:

...
new FileIOPermission (...).Demand( );

The Intersect and Union methods combine two same-typed permission objects into one. The result of Intersect is more restrictive when Demanded, requiring both permissions to be met. The result of Union is less restrictive when Demanded, requiring either permission to be met.

IsSubsetOf returns true if the given target is at least as permissive:
PrincipalPermission jay = new PrincipalPermission ("Jay", null);
PrincipalPermission sue = new PrincipalPermission ("Sue", null);

PrincipalPermission jayOrSue = (PrincipalPermission) jay.Union (sue);
Console.WriteLine (jay.IsSubsetOf (jayOrSue)); // True

In this example, calling Intersect on jay and sue would generate an empty permission, because they don't overlap.



PermissionSet
A PermissionSet represents a collection of differently typed IPermission objects. The following creates a permission set with three code access permissions, and then Demands all of them in one hit:

PermissionSet ps = new PermissionSet (PermissionState.None);

ps.AddPermission (new UIPermission (PermissionState.Unrestricted));

ps.AddPermission (new SecurityPermission (
SecurityPermissionFlag.UnmanagedCode));

ps.AddPermission (new FileIOPermission (
FileIOPermissionAccess.Read, @"c:\docs"));

ps.Demand( );


PermissionSet's constructor accepts a PermissionState enum, which indicates whether the set should be considered "unrestricted." An unrestricted permission set is treated as though it contained every possible permission (even though its collection is empty).

When you call AddPermission, the permission set looks to see whether a same-typed permission is already present. If so, it Unions the new and existing permissions; otherwise, it adds the new permission to its collection. Calling AddPermission on an unrestricted permission set has no effect.

You can Union and Intersect permission sets just as you can with IPermission objects.



Declarative Versus Imperative Security
So far, we manually instantiated permission objects and called Demand on them. This is imperative security. You can achieve the same result by adding attributes to a method, constructor, class, struct, or assembly—this is declarative security. Although imperative security is more flexible, declarative security has a couple of advantages:

• It can mean less coding.

• It allows the CLR to determine in advance what permissions your assembly requires.
Here's an example:

[PrincipalPermission (SecurityAction.Demand, Name="Mary")]
public void GetReports( )
{
...
}

This works because every permission type has a sister attribute type in the .NET Framework. PrincipalPermission has a PrincipalPermissionAttribute sister. The first argument of the attribute's constructor is always a SecurityAction, which indicates what security method to call once the permission object is constructed (usually Demand). The remaining named parameters mirror the properties on the corresponding permission object.

0 comments


Subscribe to Developer Techno ?
Enter your email address:

Delivered by FeedBurner