Custom Code Access Permissions


This article has been excerpted from book "The Complete Visual C# Programmer's Guide" from the Authors of C# Corner.

You can provide custom permissions within an application or library. Any custom permission you create must be added to the security policy on the computers where the application using the permission runs. 

To create custom code access permissions, just create an XML representation of the configuration you want the custom permission to have and then import the XML into your security policy. The .NET Framework security system uses XML to serialize permissions. The XML representations of the permissions that make up your security policy are stored in the policy configuration files. 

To add a custom permission to a security policy, perform the following steps:

  1. Load the assembly into GAC.
  2. Trust the assembly.
  3. Use the CASPOL tool, Notepad, or your favorite editor to generate an XML file that represents the type of permission. This can be a difficult task so, from the start, you should learn the relevant specific XML tags by copying them from existing files. Follow Microsoft's recommendation and use either the CASPOL or mscorcfg.msc tool instead of editing these files manually.
  4. Apply the XML file to the security policy.

You can see whether all of your callers have been granted a specific permission by demanding the permissions for them. Demands can be made either imperatively through code or declaratively through attributes. 

The Assert Method 

For code access permissions, the CLR walks through the call stack, checking each caller in the stack one by one for the proper permissions to the requested resources. Yet, demanding permissions from each caller every time a call is made can create considerable overhead in big programs. Depending on how many callers are in the stack, you may encounter an unexpectedly high number of permission checks that can degrade performance. For example, if your code accesses a secure method five times, and five callers are currently above it in the call stack, 25 permission evaluations will be performed. The examples later in this chapter show how to implement code access security in situations like this, and they also walk you through a remedy to the tedious stack walk-ups: the Assert method. Using this method in your code to assert the specific permission can reduce the number of evaluations to be performed up the call stack. However, you can use the Assert method only if your code is to have the permission both to perform the request being made and to make assertions. 

The Assert method is evaluated at runtime. All of the code access permission classes and permission sets may implement the Assert method. Figure 22.7 demonstrates file permissions and how the Assert method affects the call stack. Assume that assembly 2 is your DLL module and has permission to read all files on the drive. Assembly 3 can only read files with an XML extension, and assembly 1 has not been granted file I/O permissions. Assembly 1 makes a call to the library module (assembly 2), which in turn calls assembly 3. 

Figure-22.7.gif

Figure 22.7: Assert Method Illustration 

Under normal circumstances, when assembly 3 tries to read an XML file, the runtime security mechanism checks the permissions of its callers (assembly 2 and assembly 1). The call fails and throws a security exception because assembly 1, which originated the call, does not have the proper permissions. Using the Assert method, you can have assembly 2 assert its permissions to read files. Then when assembly 3 tries to read an XML file, the security mechanism's stack walk stops at assembly 2 because that assembly uses assertion on the same permission type (FileIOPermissions). 

In this way, you can reduce the number of permission evaluations performed, but your performance gain will vary depending on the number of callers and accesses. In the scenario above, a performance gain can be realized; but a closer look reveals that assembly number 1, which would not otherwise have file I/O permissions, gains such permission when the security check stops at assembly 2. When assembly 2 asserts its file permissions, it passes those permissions along to the callers above it in the stack. The security checks for this asserted permission do not proceed beyond the assembly making the assertion. 

Using the Assert method allows your code to retain permissions that it normally has but eventually allows other callers upstream in the call stack to have access that they would not normally have. 

You should use Assert very carefully so as to avoid opening up backdoor security risks. It is tempting to use assertions to attain performance gains, but you should provide additional security measures when Assert is used. You should explicitly trace and evaluate the security holes that could be opened up through the use of assertion. 

Imperative Demand 

With imperative demand, developers can write the security code into the logic of the applications. You place an imperative demand in your method code body to protect the rest of the code in a method from which the Demand method is called. If the caller does not satisfy the Demand, an exception will be thrown. Demand method of CodeAccessPermission class (or any of its derived classes) will throw a SecurityException during the program execution if the function invoking assemblies in the assembly stack have not been granted the permission requested by the class object. 

The order of processes required to make an imperative demand is as follows:

  • Create a new instance of a permission object and then set the properties you want on the permission object. This can typically be done in a single call to the constructor for most of the security objects.
  • Call the object's Demand method. In most instances, the Demand method is called in a try block so that if the demand fails, the resulting exception is caught.

If the demand fails for any reason (possibly your permission is revoked), a security exception is thrown. If the demand works as planned, execution continues after the call. 

Declarative Demand 

Declarative security is implemented by using attributes on assemblies, classes, or methods. These security attributes, which are stored as metadata in assemblies and modules, can be either built in or custom defined. Declarative security allows developers to specify most of the same kinds of security requirements that imperative security allows. However, as you will see in the examples, the syntaxes are quite different. You can apply Assert, Demand, and Deny methods or permit only certain permissions by placing security attributes in your classes or methods. 

As you will see, all built-in permissions have a corresponding attribute. A declarative security attribute follows the syntax shown: 

[<Permission>Attribute(<SecurityAction>, 
<PublicProp1> = <Value1>)] 

In this pseudocode, <Permission> obviously represents the name of the permission you want to use, and <SecurityAction> is a member of the SecurityAction enumeration, such as SecurityAction.Assert. This action describes the type of security operation to be performed, with the possible actions including LinkDemand, InheritanceDemand, Demand, Assert, Deny, PermitOnly, RequestMinimum, RequestOptional, and RequestRefuse. 

Permission requests are specified by using attributes for an assembly, stored in the metadata of an assembly. You have already learned that an assembly's permission requests are examined when the assembly is loaded, and the CLR continues based on the kind of permission request the assembly makes. 

When you fail to make any permission requests, you are requesting the maximum amount of permissions the local security policy will allow for your code. On the other hand, when you request limited permissions to be granted to your code, you will be testing your code against the known security limitations. Where security of your code is of concern, you must be careful about requesting full set of permissions because it can affect the ability of your code to run. For example, you should consider the case, where the security policy allows your code to do something (such as read and write a file), and the requested permissions do not. You can use the RequestMinimum optional assembly attribute when you want to ask for those permissions that your assembly will probably need as a minimum to load. You can use the RequestOptional optional assembly attribute when your assembly provides features that require additional permissions. You can use the RequestRefuse optional assembly attribute when you want to deny particular permissions that will not be needed by your code within your assembly even though your assembly is granted to them by the local policy. 

The three kinds of optional attribute permission requests are as follows:

  • Minimum permissions (RequestMinimum) - In your programs, you should request only necessary permissions. You can use RequestMinimum to discover minimum-security requirements and output the requirements of a target assembly. So you can fine-tune policy to meet the requirements. An assembly must be granted permission to at least its minimum requirements to be loaded. Once the assembly is loaded, you are certain that it satisfied at least the minimum permissions requested.
  • Optional permissions (RequestOptional) - These optional requests for additional permissions are not required for running.
  • Refused permissions (RequestRefuse) - This allows you to refuse unnecessary permissions that invite abuse; thus you can effectively defend your programs and classes.

The following example makes a minimum permission request for permission to call unmanaged code. The assembly will not load if it is not granted the permission to execute unmanaged code. 

[assembly:SecurityPermissionAttribute( SecurityAction.RequestMinimum, UnmanagedCode = true)] 

Permission requests affect the final permission that an assembly receives. Microsoft offers the following official formula to calculate granted permissions, where n equals intersection, ? equals union, and the minus sign equals exclusion: 

Granted Permissions = (maximum allowed permission n (minimum permission request? optional permission request)) - refused permission request 

It is assumed that the minimum permission request for an assembly is available all the time when calculating granted permissions. 

Choosing Imperative or Declarative Security 

Declarative security is simpler to implement than imperative security. Declarative security attributes can be viewed with Permview.exe or mscorcfg.msc, allowing the callers of code to see what permissions the code requires. Imperative security is not evident to the callers of code. Because the permissions requirements are embedded inside the code, you cannot see what is happening unless coding a demand. 

In some cases the choice between declarative and imperative security is obvious because some security actions can be performed in only one of the two ways. For example, minimum, optional, and refused permission requests are only available declaratively. 

But how do you choose which approach to use when both approaches provide equivalent support for the functions you need? If a security action can be performed both declaratively and imperatively, the decision depends on the developer's preference. The performance difference between the two is insignificant, for the most part, however, you can would have to choose imperative syntax over declarative syntax when you are going to construct the initial permission state of security in your code with information that is only available at run time. 

A declarative demand is a way of invoking a security check without adding any statements to your code. You make a declarative demand by placing information at the class or member level, indicating the permissions you want callers to have. You can also implement Deny actions declaratively. If you place a declarative security check at the class level, it applies to each class member, including methods, properties, and fields. If you place a declarative security check at the member level, it applies to only that member and overrides any permission specified at the class level. Declarative demands degrade performance only at JIT compilation. In fact, the use of declarative demands can actually increase the performance of an application in which a demand is made many times. 

The imperative demand that you place in your code protects all of the remaining code in the method from which the Demand method is called. The security check is performed when the Demand method executes; if the security check fails, a security exception is thrown and the rest of the code in that method or member is not executed unless the exception is caught and handled. Consider using an imperative (rather than declarative) demand when information that you need to specify permission becomes known only at runtime. 

Identity Permissions 

When an assembly is requested, the CLR searches for evidence of the assembly's origin. In simple terms, the CLR asks the following questions:

  • From which site does the assembly come?
  • From which URL does the assembly come?
  • From which zone does the assembly come?
  • Who has signed the assembly?
  • What is the strong (public/private key-signed assembly) name of the assembly?

Based on the evidence it collects, the CLR learns the identity of the assembly and assigns the appropriate identity permissions. These identity permissions "earn" an assembly its membership to a code group. Developers and administrators do not have influence over the identity permissions of an assembly. The CLR just takes a requested assembly, examines its evidence, and assigns the identity permissions based on that evidence. To associate a set of identity permissions with a code group, the administrator must set up security policies, as described earlier in this chapter. 

Some of the identity permission classes in the framework are

  • SiteIdentityPermission, which represents the Web site from which the assembly originated;
  • URLIdentityPermission, which represents the URL from which the assembly originated;
  • ZoneIdentityPermission, which represents the security zone from which the assembly originated. The zone identity is one of the values of the System.Security.SecurityZone enumeration.
  • PublisherIdentityPermission, which represents the Authenticode publisher of the assembly; and
  • StrongNameIdentityPermission, which represents the strong name of the assembly.

Even if identity permissions can be demanded, they are granted only if an assembly can show evidence of the proper identity. This also holds true for code access permissions. Identity and code access permissions are very closely related in that they share the same underlying concept and derive from the same base class, CodeAccessSecurity. 

Link Demands 

Link demands, the permissions a caller must have when calling code, can be used to limit which classes can be linked to your classes. You must set link demands declaratively because they can be evaluated at JIT compilation time by the system and performed against the caller of the code. If the caller does not possess enough permission to satisfy the link demand, the system throws an exception. The CLR does not perform a stack walk for link demands, so you can avoid the potential impact on performance. 

Link demands can be associated with identity permissions to control who or what can call your code. For example, you can develop assemblies that can be called only by other assemblies from a specific publisher. For syntax, see the examples provided in the class descriptions section later in this chapter. 

Listing 22.6 illustrates how you can implement LinkDemand declarative security. 

Listing 22.6 -LinkDemand Illustration 

            // code to restrict callers
            [ZoneIdentityPermission(SecurityAction.LinkDemand,
            Zone=SecurityZone.MyComputer)]

            public class test
            {
            //some code
            }

Inheritance Demands 

Inheritance demands are similar to link demands except that they are used to restrict how entities inherit characteristics at the class level. For example, you can place inheritance demands declaratively at the class level to ensure that only code with the specified permission can inherit from your class. You can place inheritance demands on methods to ensure that any code that overloads the method has the specified permission to do so. 

Listing 22.7 illustrates how you can implement class-level InheritanceDemand declarative security. 

Listing 22.7 -InheritanceDemand Illustration 

            // code to restrict subclassing
            [StrongNameIdentityPermission(SecurityAction.InheritanceDemand,
            PublicKey={........)]

            public class test2
            {
            // Some code
            }

Role-Based Permissions 

You know now how the CLR controls the permissions of your code. Now it's time to explore how you can control the rights of a user. 

Every role you have - whether parent or child, shopper or billpayer, developer or administrator - has certain behaviors and responsibilities associated with it. Security designers have recognized how useful this concept of role-associated behaviors can be in IT and have designed a security check that grants permissions based on roles. 

When you write a software system, the design phase usually reveals certain roles that users play based upon actual workflow. Role-based security allows the developer to check whether a user is functioning within a certain role when requesting access to resources. 

Unlike identity permissions and code access permissions, role-based permissions have nothing to do with the rights a piece of code possesses but rather with the workflow-based permission of a user. The term workflow-based permission implies a sequence of steps or hierarchical permissions that colleagues require to see a task through to completion. For example, a bellhop with no permissions takes a guest's bags to a hotel room designated by a desk clerk with specific permissions. A housekeeper with very few permissions freshens the room; and at checkout, the desk manager's more extensive permissions are required to remove unauthorized charges from the guest's bill and to discount the room rate. 

Role-based permissions are not derived from the CodeAccessPermission class, as identity permissions and code access permissions are. Instead they share the same base class with the common principal-based (also known as Windows identity) permissions; both are derived from the PrincipalPermission class. To understand role-based permissions, we must look at two other classes: Identity and Principal. 

The Identity class represents users in a software or operating system. In contrast, the Principal class represents the security context (Windows user group) of a user (Windows identity. Basically, Principal represents all the roles to which a user is assigned. A PrincipalPermission is the object used to demand a role from a user. If the user belongs to the necessary Principal class (i.e., if the user has been assigned the requisite roles), the request succeeds. PrincipalPermission is therefore an easy way to check role assignments. 

We have seen the different security concepts related to a specified software system. Yet, that software system is embedded in the operating system and the .NET runtime host, so there must be other permissions that manage your rights to control the runtime. 

Security Permissions 

All permissions related to the runtime environment and the controlling of it are represented through the SecurityPermission class found in the System.Security.Permissions namespace. With this class, you can request runtime environment permissions. 

The class takes a SecurityPermissionFlag enumeration in its constructor. This enumeration contains an item called Assertion, which is responsible for trying to execute the assertion for permission. If an assembly does not possess Assertion rights, it is not possible to call the Assert method to assert specific permissions and therefore you cannot disable the stack walk of the code access permissions. 

Treat with extreme caution any permissions represented by SecurityPermission objects. If you assign rights through this enumeration, be sure you know what the code does and whether it really needs these rights. If you obtain code over the Internet or from other uncontrollable sources, never assign it permissions if you don't know what the code contains. 

Exceptions 

In .NET, any security violation throws one of three exception types -security, policy, or verification -which can be caught and handled appropriately by exception-handling routines. An object is created to represent exceptions as they occur. All three security-related exceptions that follow inherit from Object.Exception.SystemException.

  • SecurityException is thrown when a security violation is detected, typically at runtime.
  • PolicyException is thrown when policy forbids code to run. This exception, which usually occurs as the code is loading, is thrown when the code requests more permission than the policy will grant or the policy is configured to prohibit running the code.
  • VerificationException can occur either when code that is verifiable does not pass verification rules or when code that is not verifiable is run without SkipVerify permissions enabled for that particular zone, such as the intranet zone. Verification includes memory allocations, proper casting of variables, and type safety. Code can be verified during JIT compilation because it contains nothing but MSIL statements. However, code in which SkipVerify permission is set is omitted.

Conclusion

Hope this article would have helped you in understandingCustom Code Access Permissions in C#. See other articles on the website on .NET and C#.

visual C-sharp.jpg
The Complete Visual C# Programmer's Guide covers most of the major components that make up C# and the .net environment. The book is geared toward the intermediate programmer, but contains enough material to satisfy the advanced developer.

Up Next
    Ebook Download
    View all
    Learn
    View all