Configuring Authorization via Permissions

The CLR determines whether an application can access resources and perform certain actions based on the permissions granted to it and its callers. The .NET Framework uses Permission objects to represent three types of permissions:

  • Code-access permissions, which are the capabilities that can be granted to applications

  • Identity permissions, which describe the identity and origin of the code

  • Role-based security permissions, which describe the groups that the caller of the code might, or might not, be a member of

Most of these permissions are organized within the System.Security.Permissions namespace. As you will see from the partial list in tables 9.3 and 9.4, the .NET Framework enables you to grant or examine permissions on a very granular level. For instance, you might wish to restrict your service from accessing environment settings or the system Registry, because if an attacker devised a way to exploit your service, you would not want them to be able to find out the details about your server that are exposed in those locations.

Knowledge of how permissions work is important for understanding how they are used to implement two of the three .NET platform security models, so next you will look at them in greater detail before learning more about the security models themselves.

Introduction to Permissions

Code-access permissions, which are part of the code access security model discussed later in this chapter, tend to focus on controlling access to specific system resources. They specify the types of actions that the code is permitted to perform. The CodeAccessPermission class is defined within the System.Security namespace. Each class derived from the CodeAccessPermission class has one or more public properties through which you can customize the behavior of that permission. For example, you can selectively allow access to areas of the file system, the Clipboard, and certain types of user interface windows, the default printer or all printers, and so forth. Table 9.3 lists some of the most common code access permission classes you might encounter as a .NET platform service developer.

Table 9.3: Common .NET Code Access Permission Classes

Permission

Gives Permission To…

EnvironmentPermission

Read and/or write environment variables.

FileDialogPermission

Display the file dialog, which if displayed, can enable the user to see files in directories and navigate the file system.

FileIOPermission

Read, write, and/or append to files or folders.

IsolatedStoragePermission

Read and/or write files in a specially isolated area of the file system, enabling the application to save data to the file system without giving it access to the entire file system. You can also set a quota governing the maximum amount of isolated storage that can be used by the application.

PrintingPermission

Print. (This permission is found in the System.Drawing.Printing namespace.)

RegistryPermission

Read and/or write to the system Registry.

SecurityPermission

Manipulate the security subsystem, such as asserting permissions, electing to skip code verification, and allowing the assembly to call unmanaged code.

SQLClientPermission

Access SQL databases as a client.

UIPermission

Create user interface elements. (This is an example of a right that a service will generally not require.)

Because it is useful for developers to be able to perform actions in standardized ways, the permission concept is also used to express information about the origin and identity of code. Table 9.4 lists common identity permissions, which are also found in the System.Security.Permissions namespace.

Table 9.4: Common .NET Framework Identity Permission Objects

Permission

Contains

PublisherIdentityPermission

Code publisher’s digital signature

SiteIdentityPermission

Site from which the code originated

StrongNameIdentityPermission

Assembly’s strong name

URLIdentityPermission

URL from which the code originated

ZoneIdentityPermission

Zone from which the code originated

Most of these permissions should be self-explanatory. The ZoneIdentityPermission object’s possible values parallel the zones offered in Internet Explorer: Local Intranet, Trusted Sites, Internet, Restricted Sites, and Local Machine.

There is only one role-based permission, PrincipalPermission. By passing identity information (username and/or role), a PrincipalPermission object can be used to verify the identity currently in effect or to verify that identity is a member of a specified role.

The permissions listed in tables 9.3 and 9.4 are just a subset of the types of permissions available in .NET. For ease of use, permissions can be grouped into permission sets, which specify a collection of one or more types of permissions, as you learned earlier in this chapter. Permission sets can be named or unnamed. The .NET Framework furnishes a number of conveniently named permission sets, listed in Table 9.5, which feature useful combinations of permissions. The permissions assigned to these named sets are fixed, though if you like, you can create your own named permission sets and define custom combinations of permissions specific to your application.

Table 9.5: Common .NET Named Permission Sets

Permission Set

Description

Execute

Permission to execute (but not any other .NET permissions).

Everything

All built-in permissions.

FullTrust

All built-in permissions plus all user-defined permissions.

Internet

Permissions useful for trusted Internet-based applications. (Check the .NET Framework version on which you are deploying for specifics, as the permissions in this set have changed with new releases.) Granted by default to code in the Trusted_Zones code group.

LocalIntranet

Permissions useful for trusted intranet-based applications. Currently includes all Internet permissions as well as the ability to discover the local user identity, read files from the application’s directory, and access the event log. Again, you might wish to verify the current LocalIntranet permission set in the version of the .NET Framework that you are using. Granted by default to code in the LocalIntranet_Zone code group.

Nothing

Granted by default to all code, and includes no permissions.

Understanding How Permission-Checking Works

Each executing .NET managed code process has an associated call stack, which contains information about all methods that have been called and have not yet ended, including the permissions granted to that method (or stack frame). To determine the code access permissions in effect at the current time, the CLR performs a stack walk. That is, it examines the permissions granted to the current stack frame and then starts traveling upward on the stack, examining the permissions granted at successively higher levels of the call stack, for all method calls currently executing. In most cases, if a permission is not granted at all higher levels of the call stack, the permission is not considered to be in effect, even if it has been granted to the currently executing code.

Let’s take a look at an analogy to show how this might work in everyday business. Suppose the chief executive officer of the company you work for places no restrictions on who can travel first class on business trips. However, the chief technical officer reporting to that CEO is carefully minding her budget and specifies that all staff in her area of the organization must travel coach or business class. She passes this policy down to employees who report directly to her, including the director of the application development group. Meanwhile, the project managers reporting to the director haven’t heard about the new policy yet and are still encouraging their staff to travel first class on long business trips. One day, you find yourself needing to travel to a client site to debug a challenging application configuration problem. You submit your first-class travel plans to your project manager, who approves them and passes them up the line to the director of application development. The director calls your manager to let him know that the request is being denied because of policy, and very soon your manager lets you know that the request was not approved. Unbeknownst to you, somewhere above you in the organizational hierarchy “stack,” the permission had been denied. This situation is illustrated in Figure 9.4.

click to expand
Figure 9.4: A corporate stack walk

Now, relate this example to coding. Say you have an application that is granted all permissions by default, which calls a component that is explicitly denied permission to modify the value of the Path environment variable. That component might call a third-party component that in some cases tries to modify the Path's value, because it was written by a developer who did not anticipate it would ever be called by a method that did not want its Path's value modified. When the third-party component attempts to modify the value of the Path, a security exception is thrown, because the component does not have permission to do so. Figure 9.5 summarizes how this works.

click to expand
Figure 9.5: A stack walk in code

Why base effective permissions on those granted to callers as well as those granted to the currently executing method? Such a mechanism supports flexible configuration for modern, network-based components and reflects the notion that not all callers of a method are trusted equally. .NET handles permissions in this way so that the same code can run with different permissions depending on the caller. For example, a method might opt to write only to small areas of isolated storage if called from a process across the Internet, and write to other areas of the file system if called from a process on the local intranet, because it trusts the intentions of local callers more than it trusts those of random Internet users.

Now that you understand what permissions are and how .NET evaluates them to enforce security, next you’ll see how they are used in .NET applications.

Using Permissions in Code

.NET programmers can interact with permissions by using declarative (attribute-based) and imperative (traditional code-based) techniques. Why have two styles of working with security permissions? Attribute-based programming is convenient, enabling the developer to make use of a lot of .NET functionality without having to write additional lines of procedural code. Applying attributes to assemblies and methods also aids in documentation—the permissions required by that code are clearly noted. However, not all permission interaction can be accomplished via attributes (for example, choosing between two program behaviors based on the permissions currently in effect at runtime can be accomplished only imperatively), and not all programmers prefer notating their code with attributes to explicitly writing code to perform functions. Conversely, there are also some permission-related functions available via declarative notation that are not available in imperative code, so sometimes the attribute-based method of permission manipulation is required.

You are free to combine declarative and imperative methods of working with permissions, so that you can implement the desired functionality in the most convenient way. For example, you might want to use an attribute to declaratively demand permission for your assembly to read a file, but at runtime, use imperative method calls to restrict the writing of that file to users in the Administrator role. You’ll now look at what you can do with permissions, first through examples in the imperative style, then in the declarative style.

start sidebar
Real World Scenario—Declarative Permissions, Classes, and Methods

Be aware that if you assign declarative permissions to a method, these override any conflicting declarative permissions assigned for the class that contains the method. Possible security actions are defined by the SecurityAction enumeration in the System.Security.Permissions namespace.

For example, if you demand, via attributes at the class-level permission, to access the Registry and then demand, via attributes on a method, permission to read a certain file, the demand for Registry access will not be in effect for that method—it has been replaced by the demand for Read permission on a file.

Because this is somewhat confusing, it is recommended that you not mix class and method permission attributes.

end sidebar

Methods Common to All Permission Objects

There are many ways in which .NET applications can interact with permissions. Table 9.6 lists methods available to all types of Permission objects.

Table 9.6: Selected .NET Permission Object Methods

Method

Description

Demand

Verifies that all callers higher in the call stack have been granted this permission; if not, a security exception is generated.

Intersect

Creates a Permission object that contains the permissions common to both the specified permission and the current permission.

IsSubsetOf

Determines whether the current permission is a subset of the specified permission.

Union

Creates a Permission object that contains the permissions in the specified permission and the current permission.

It is good programming practice to specifically check for the permissions in code before they are needed. Using the Permission.Demand method, the programmer can verify that the currently executing code has the permission(s) to perform the anticipated functions and, if the code does not have the appropriate permission(s), to fail gracefully. The developer can even check which permissions are available and vary the code path, based on the current permissions. By varying the code path you are increasing the flexibility of your application with regards to the permissions that are required for the code to run. Another reason to use Demand is to verify that the required permissions for an action performed late in a method call are present before you perform resource-intensive setup code.

Listing 9.1 demonstrates the imperative use of the Demand method.

Listing 9.1: Imperative Use of the Permission.Demand Method

start example
Imports System Imports System.Security Imports System.Security.Permissions Public Class Example1 Private Sub WriteToLog() Dim LogFilePermission as New FileIOPermission _       (FileIOPermissionAccess.Write, "C:\example1.log") Try     LogFilePermission.Demand()  Catch    ' handle exception here End Try End Sub End Class
end example

Now that you’ve seen how to implement Permission.Demand via imperative method calls, look at the declarative attribute-based technique in Listing 9.2. Notice that the name of the attribute is the permission name plus Attribute. The first argument is the Demand action, and the second is the permission property being verified.

Listing 9.2: Declarative Use of the Permission.Demand Method

start example
Imports System Imports System.Security Imports System.Security.Permissions Public Class Example1 <FileIOPermissionAttribute(SecurityAction.Demand, _       Write:="C:\example1.log")> _  Private Sub WriteToLog() End Sub End Class
end example

The Intersect, IsSubsetOf, and Union methods all allow manipulation of Permission objects for additional flexibility, because some permissions are actually subsets or supersets of other permissions, and possession of one of the higher-level permissions implies possession of lower ones.

Methods Available to Code Access Security Permission Objects

Code access Permission objects have additional methods, described in Table 9.7. These methods are used to alter the stack-walk behavior when the CLR is checking code access security permissions.

Table 9.7: Selected .NET Code-Access Permission Object Methods

Method

Description

Assert

Asserts that this permission is granted even if callers higher in the stack do not possess it, as long as the executing code has been granted the specified permission. By default, only code in the intranet zone and fully trusted code can call Assert.

Deny

Causes any Demand method that passes through this stack frame (via a stack walk) for a specific permission in the current permission set to fail.

PermitOnly

Causes any Demand method that passes through this stack frame for a permission that is not a subset of the current permission set to fail.

RevertAll

Removes any permission overrides for the current frame.

RevertAssert

Causes any previous Assert method for the current frame to be removed.

RevertDeny

Causes any previous Deny method for the current frame to be removed.

RevertPermitOnly

Causes any previous PermitOnly method for the current frame to be removed.

Some programmers, upon seeing the Assert method, might be tempted to confuse it with Demand. However, its functionality is different. Whereas Demand causes the system to walk up the call stack verifying that code has a certain permission, the Assert method states that the code is permitted to access the resource specified by the current permissions of the calling code, even if callers higher in the stack haven't directly been granted permission to access the resource, thus no stack walk is required.

To understand how this works in practice, let’s revisit our analogy regarding travel permissions. Suppose your project manager doesn’t want to bother the busy director by asking her to approve your request for first-class travel, and simply rubber-stamps it approved and hands it back to you. Your project manager has taken upon himself the responsibility that those reporting to him will use the permission to travel in first class responsibly (for example, by using it only when traveling on flights longer than three hours). In .NET Framework terms, he asserts that those below him in the hierarchy should be given this permission, regardless of the views or restrictions put in place by those above him. Figure 9.6 shows this in graphical form.

click to expand
Figure 9.6: Asserting permission

The use of Assert results in an increase in security vulnerability, in that you’ve just increased the chances that this code, when called from an untrusted process, can be used to do more than the programmer might have anticipated. Therefore, consider the implications carefully before using Assert in your code. Although both Assert and Demand will fail if the code in question doesn’t possess the specified permission, only Demand will fail if the code’s callers don’t possess the permission.

To assert a particular permission, you might use imperative code such as the following:

Dim LogFilePermission as New FileIOPermission _       (FileIOPermissionAccess.Write, "C:\example1.log") Try    LogFilePermission.Assert()  Catch    ' handle exception here End Try

Alternatively, you could use declarative notation:

<FileIOPermissionAttribute(SecurityAction.Assert, _       Write:="C:\example1.log")> _  Private Sub WriteToLog() End Sub

The Deny method performs a function similar to Assert, but in the other direction. Whereas Assert indicates that the indicated permission should be considered granted, Deny indicates that the specified permission should be considered disallowed. To go back to our travel example, the chief technical officer is issuing a real-world denial of first-class travel permission to those reporting to him when he sends out the policy memo. It can be useful to denyany permissions that are not absolutely required before calls to a third-party class library, to help ensure that any implementation flaws in that library don’t compromise the security of your code. To deny a permission imperatively, you would use code like the following:

Dim LogFilePermission as New FileIOPermission _       (FileIOPermissionAccess.Write, "C:\example1.log") Try    LogFilePermission.Deny  Catch    ' handle exception here End Try

And to deny declaratively, you would use code similar to this:

<FileIOPermissionAttribute(SecurityAction.Deny, _       Write:="C:\example1.log")> _  Private Sub WriteToLog() End Sub

Similarly, the PermitOnly method applies the stack-walk-avoiding idea to the concept of explicitly specified permissions. The effect of PermitOnly is to deny all permissions except those explicitly included in the specified permission. Because only one PermitOnly is allowed per frame, PermitOnly is most likely to be used with PermissionSet objects rather than an individual Permission object, because code generally needs more than a single permission for proper operation. For example, it might require both file access permissions and environment access permissions.

The Assert, Deny, and PermitOnly methods can be used to increase the efficiency of code because they reduce the amount of time spent performing stack walks when explicitly testing permissions with Demand or attempting to execute code requiring permissions. However, as previously mentioned, because they prevent the CLR from considering security-related evidence that might be provided by the code’s callers, they should be used with care.

Normally, the effects of these functions last only until the stack frame is removed (for example, when the method finishes execution). The Revert methods listed in Table 9.7 can be used to “turn off” any Assert, Deny, or PermitOnly calls that have been made before that point, if desired. If you want to permanently deny a specific permission to an assembly and all of the code it calls, note that a specific permission is required for proper application operation. Or if you want to request that a specific permission be made available for the assembly, you must use declarative security at the assembly level. For example, to deny an assembly the permission to read drive C:, you would include the following attribute within the assembly’s code:

<Assembly: FileIOPermissionAttribute _    (SecurityAction.RequestRefuse, _    Read:="C:\") > 

To require permission to read drive C:, you could include an attribute such as the following within the assembly’s code:

<Assembly: FileIOPermissionAttribute _    (SecurityAction.RequestMinimum, _    Read:="C:\") >

To optionally request (but not require) Read access to drive C:, you could use the following attribute:

<Assembly: FileIOPermissionAttribute _    (SecurityAction.RequestOptional, _    Read:="C:\") >

Now that you’ve seen the basics of interacting with individual permissions, let’s take a look at interacting with permission sets.

Using Permission Sets

The Permission object's methods, displayed in Table 9.8, and the Permission object's attributes in the previous examples are also available for PermissionSet objects, and their use is similar.

In addition, PermissionSet objects feature several other methods for manipulating collections of permissions, as listed Table 9.8.

Table 9.8: Selected .NET Framework PermissionSet Object Methods

Method

Description

AddPermission

Adds a specified permission to the PermissionSet

RemovePermission

Removes a specified permission from the PermissionSet

SetPermission

Sets a permission in the PermissionSet, replacing any permission of the same type

To build a permission set, you can call AddPermission repeatedly to fill the permission set with your desired permissions. The following code creates a permission set allowing only user interface element, environment, and file access. Notice that the permission set starts out empty, and that a permission set can contain multiple types of permissions.

Dim myPermissionSet as New PermissionSet( _            PermissionState.None) Dim myEnvPerm as new EnvironmentPermission( _           PermissionState.Unrestricted) Dim myFilePerm as new FileIOPermission( _           PermissionState.Unrestricted) Dim myUIPerm as New UIPermission( _           PersmissionState.Unrestricted) myPermissionSet.AddPermission(myEnvPerm) myPermissionSet.AddPermission(myFilePerm) myPermissionSet.AddPermission(myUIPerm) myPermissionSet.PermitOnly()

Because some permissions might be required for only a short time, you can use RemovePermission to take a specific permission out of the permission set. For example, if you no longer needed permission to access the environment, you could use the following code to remove that permission:

MyPermissionSet.RemovePermission(myEnvPerm)

You can also use SetPermission to replace an existing Permission object in the permission set, with a new one of the same type.



MCAD/MCSD(c) Visual Basic. NET XML Web Services and Server Components Study Guide
MCAD/MCSD: Visual Basic .NET XML Web Services and Server Components Study Guide
ISBN: 0782141935
EAN: 2147483647
Year: 2005
Pages: 153

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net