Code Access Permissions


We have just seen several examples showing how the Evidence class can be used in a direct manner to make CAS decisions within your code according to the discovery of host and assembly evidence. The other way of approaching CAS is to let the code access permission classes automatically detect any mismatch between the current security policy and the permissions required by the running code. Then, if your program attempts something that it is not permitted to perform, a SecurityException is automatically thrown.

CodeAccessPermission Derived Classes

CAS programming usually involves using the classes derived from CodeAccessPermission , which are shown in the following list. Since these classes are not all contained within the same namespace, their fully qualified names are provided here for clarity. Most of the CodeAccessPermission -derived classes have meanings that are made obvious by their names. For example, DBDataPermission controls access to a database, PrintingPermission controls access to printers, SocketPermission represents the permission for making or accepting TCP/IP connections, and so on.

A subset of these code access permission classes are known as identity permissions, since they do not deal with controlling access to resources but rather specialize in dealing with host evidence pertaining to assembly identity. You can easily recognize these classes, since their names contain the word Identity, such as SiteIdentityPermission and ZoneIdentityPermission .

Note that this list of CodeAccessPermission -derived classes does not contain the PrincipalPermission class discussed in the previous chapter. This is because PrincipalPermission is a peer class derived from Object , and it is not a code access permission but a permission that encapsulates user -based security.

  • System.Data.Common. DBDataPermission

  • System.Drawing.Printing. PrintingPermission

  • System.Messaging. MessageQueuePermission

  • System.Net. DnsPermission

  • System.Net. SocketPermission

  • System.Net. WebPermission

  • System.Security.Permissions. EnvironmentPermission

  • System.Security.Permissions. FileDialogPermission

  • System.Security.Permissions. FileIOPermission

  • System.Security.Permissions. IsolatedStoragePermission

  • System.Security.Permissions. PublisherIdentityPermission

  • System.Security.Permissions. ReflectionPermission

  • System.Security.Permissions. RegistryPermission

  • System.Security.Permissions. ResourcePermissionBase

  • System.Security.Permissions. SecurityPermission

  • System.Security.Permissions. SiteIdentityPermission

  • System.Security.Permissions. StrongNameIdentityPermission

  • System.Security.Permissions. UIPermission

  • System.Security.Permissions. UrlIdentityPermission

  • System.Security.Permissions. ZoneIdentityPermission

The CodeAccessPermission Class

The CodeAccessPermission class has several methods that must be understood , since they are found in all of the derived permission classes that you will be working with. We look at a few of these methods in more detail and see example code demonstrating how they can be used. But first, let's take a look at some brief descriptions of these methods. The methods that are simply inherited from the Object class are not shown here.

  • Assert allows the specified permission in the current method and methods further down the call stack even if code in higher stackframes [23] have denied the permission. This method works by stopping a stack walk from proceeding further up the call stack, preventing a potential SecurityException from being thrown. This method is successful only if the calling code passes the security checks required for granting the specified permission according to security policy and the code is granted assert permission. An assertion is in effect from the time that Assert is called until the method that called Assert returns or the RevertAssert method is called on the current stackframe. Only one assert can be active on a given stackframe, and calling Assert more than once on the same stackframe will throw a SecurityException . This method should be used judiciously, since it liberalizes permission usage and may introduce luring -attack security risks, but it can be very convenient . For example, you can aggressively deny permissions higher on the stack and then selectively and briefly assert specific permissions in selected methods further down the stack where required.

    [23] The call stack is conventionally viewed as growing down. Therefore, methods lower in the call stack are called by methods higher in the call stack. Each method being called has its own area on the stack, called a stackframe, which contains the parameters and local variables of the current method.

  • Copy is an abstract method that each derived class must implement to provide a copy of the permission object.

  • Demand is used to ensure that calling methods have the specified permission. It does this by performing a stack walk, checking the permissions of each method found higher on the call stack. This method throws a SecurityException if any of the calling methods do not have the specified permission. This method is used as an upfront test for the specified permission before performing an operation that requires that permission. Many .NET Framework classes make extensive use of the Demand method to ensure that they perform only permissible operations according to established security policy. For example, many FileStream methods create a FileIOPermission object for the appropriate path and operation, and then call its Demand method to ensure that it has the necessary permission to carry on with the intended operation. You can also define your own permission classes and call on the Demand method for implementing your own security protection mechanisms.

  • Deny prevents the specified permission in the current method and methods further down the call stack even if code in higher stackframes have been granted the permission. It does this by causing the stack walk to fail on the current stackframe for the specified permission. A denial is in effect from the time that Deny is called until the method that called Deny returns or the RevertDeny method is called on the current stackframe. Only one denial can be active on a given stackframe, and calling Deny more than once on the same stackframe will throw a SecurityException . This method is used for defensive programming, where you would like to prevent something from happening that you know has no good reason for being permitted within the current and downstream methods.

  • FromXml is an abstract method that each derived class must implement to reconstruct a permission object from an XML representation.

  • Intersect is an abstract method that each derived class must implement to create a permission object that is the intersection of two existing permission objects.

  • IsSubsetOf is an abstract method that each derived class must implement to determine whether one permission object is a logical subset of another permission object.

  • PermitOnly allows only the specified permission in the current method and methods further down the call stack even if methods in higher stackframes have been granted other permissions. This method is similar to Deny except that Deny disallows a specific permission and PermitOnly disallows all but a specific permission. This method works by causing a stack walk to fail.

  • RevertAll is a static method that nullifies all calls to Assert, Deny , and PermitOnly on the current stackframe.

  • RevertAssert is a static method that nullifies any call to Assert on the current stackframe.

  • RevertDeny is a static method that nullifies any call to Deny on the current stackframe.

  • RevertPermitOnly is a static method that nullifies any call to PermitOnly on the current stackframe.

  • ToXml is an abstract method that each derived class must implement to create an XML representation of the specified permission object.

  • Union is an abstract method that each derived class must implement to create a permission object that is the logical union of two existing permission objects.

Notice that certain methods, such as Copy and IsSubsetOf are abstract. You do not normally call these abstract methods in application code. Instead, these methods are called by .NET Framework CAS- related code. If you implement your own custom permission class, you will need to implement these abstract methods so that it can work properly with .NET Framework CAS functionality.

To give you an idea of how a couple of these methods work under the covers, the following source code is found in the codeaccesspermission.cs file, provided by the Rotor BCL Documentation. [24]

[24] Copyright 2002 Microsoft Corporation. The Rotor BCL Documentation is made publicly available by Microsoft and may be used in certain specified ways, but it is not open source in the GNU General Public License sense. Please see the Rotor BCL Documentation for details on this copyright.

[View full width]
 
[View full width]
[DynamicSecurityMethodAttribute [25] ()] public void Deny () { CodeAccessSecurityEngine icase = SecurityManager.GetCodeAccessSecurityEngine(); if (icase != null) { StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; icase.Deny(this, ref stackMark); } } ... public static void RevertDeny () { SecurityRuntime isr = SecurityManager.GetSecurityRuntime(); if (isr != null) { StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; isr.RevertDeny(ref stackMark); } } ... [DynamicSecurityMethodAttribute()] public void Demand () { CodeAccessSecurityEngine icase = SecurityManager.GetCodeAccessSecurityEngine(); if (icase != null && !this.IsSubsetOf( null )) { StackCrawlMark stackMark = StackCrawlMark.LookForMyCallersCaller; icase.Check(this, ref stackMark); } }

[25] This attribute indicates that space must be allocated on graphics/ccc.gif the caller's stack for a security object to be used for security stack-walking purposes. graphics/ccc.gif This gives a small clue how special methods such as Deny and Demand really are.

A Great Learning Resource: The Rotor BCL Documentation

If you ever wanted to drill down and really understand what's going on in the .NET Framework or you would like to see good examples of C# programming techniques, there is no better place to look than the Rotor Base Class Library Documentation, which contains the source code for the whole .NET library! This is available for viewing at http://dotnet.di.unipi.it/Content/sscli/docs/doxygen/fx/bcl/index.html.

In fact, you can download the entire Rotor source tree and build yourself a runtime environment that allows you to step through the .NET Framework code using the Cordbg.exe command-line debugger on Windows. You can even get it up and running on FreeBSD! This is all available at http://msdn.microsoft.com/downloads/default.asp?URL=/downloads/sample.asp?url=/msdn-files/027/001/901/msdncompositedoc.xml.

Another alternative is from Ximian, sponsor of the Mono project, which is an Open Source effort to implement the .NET Framework, the CLR, and the C# compiler. For details, go to www.go-mono.com/.

Happy spelunking!

The UrlIdentityPermission Class

In this section we focus on just one of the CodeAccessPermission -derived classes, UrlIdentityPermission . This class encapsulates a permission based on the URL from which the assembly originates. We also study the UrlIdentityPermission example to see exactly how this class can be used, but we must first understand a bit about the UrlIdentityPermission class itself. The UrlIdentityPermission class provides implementations for each of the abstract methods of CodeAccessPermission described earlier. It also overrides a few nonabstract CodeAccessPermission methods. It is sufficient to review the descriptions provided in the previous section to understand the purpose of each method. Beyond that, the UrlIdentityPermission does not add much in terms of new functionality. The only new members that have been added are two constructors and one string property named Url .

THE URLIDENTITYPERMISSION CONSTRUCTORS

One of the UrlIdentityPermission constructors initializes the new permission object with a PermissionState parameter. PermissionState defines two values: None and Unrestricted . However, the UrlIdentityPermission constructor does not allow the Unrestricted value, and you must therefore specify None to avoid throwing an ArgumentException .

 public UrlIdentityPermission(    PermissionState state //None is the only valid    permission state ); 

The other constructor initializes a new permission object based on a string parameter that represents a specific URL. The string may contain an optional wild card in the final position. However, the string must not be null and must contain a valid URL syntax, or an ArgumentNullException, FormatException , or ArgumentException will be thrown.

 public UrlIdentityPermission(    string site //URL that may contain a wildcard ); 
THE URLIDENTITYPERMISSION URL PROPERTY

The Url read/write property is a string that includes the protocol, such as http or ftp, followed by a colon and two forward slashes, followed by a path and filename separated with single forward slashes . URL matching may be exact or it may make use of a wildcard at the rightmost position. Here are a few examples of valid URL strings.

 http://www.SomeWebSite.com/SomePath/TrustedClient.exe http://www.SomeWebSite.com/SomePath/* file://C:/SomePath/TrustedClient.exe 

Working with Code Access Permissions

We have already pointed out that, just as was the case with user-based security, there are also two slightly different styles that can be used in the imperative approach. As we saw in the previous ImperativeCAS example, you can make security decisions explicitly by choosing the execution path using an if statement based on current application domain evidence. The decision was made between two execution branches, where one is successful and the other throws a SecurityException . This technique may be quite familiar to many traditional programmers, but the additional code required makes it slightly cumbersome.

In the new-style imperative approach, you create a CodeAccessPermission -derived object representing the code access permission that you wish to discriminate on, and then you call on that permission object's Demand method. Then, any methods that you call will automatically throw a security exception if the specified permission is not honored. In other words, within the remainder of the current stackframe as well as any called method stackframes, the SecurityException will be automatically thrown where appropriate. The advantage of doing it this way is that the code is a little more simple and clean-looking, since there is no visible evidence of inspecting loop, if statement, or exception-throwing code.

Just like the previous ImperativeCAS example, this UrlIdentityPermission example is an imperative rather than a declarative approach to CAS. The UrlIdentityPermission directory contains the UrlIdentityPermissionComponent project along with the EvilClient and TrustedClient projects that use the ImperativeCASComponent assembly.

THE URLIDENTITYPERMISSION EXAMPLE

The UrlIdentityPermission example is similar to the ImperativeCAS example in that it discriminates on the basis of URL evidence, but the UrlIdentityPermission class is used instead of laboriously enumerating through the evidence information in a loop. The UrlIdentityPermission class is used to ensure that the only client code that may successfully call into the component's DoSomethingForClient method is the client from a specific URL. Again, this demonstrates a technique for limiting client code to only those clients that are considered trustworthy based on specified evidence. To test this component, we use two programs: TrustedClient and EvilClient . These two programs are virtually identical in every way in terms of source code. The only significant difference is the URL from which they originate. We now see how a URL is used to represent the protocol, path, and filename of each of these client programs. Here is the code for the EvilClient program.

 //  EvilClient  .cs using System; using System.Security; class EvilClient {    static void Main(string[] args)    {       //NOTE: need ref to ImperativeCASComponent.dll       //try to call on the component       try       {          UrlIdentityPermissionComponent.  DoSomethingForClient  ();       }       catch (SecurityException se)       {          Console.WriteLine("Error: " + se.Message);       }    } } 

When you run this EvilClient program, you will see the following output. Note that it does in fact throw an exception, indicating that it originates from a URL that differs from the one that is trusted by the server component assembly. As you can see, the specific exception thrown is a SecurityException , and its message property states that a request for the UrlIdentityPermission failed. When we see the code in the UrlIdentityPermissionComponent assembly, we will see why this occurs.

 Error: Request for the permission of type System.Security.Permissions.UrlIdentityPermission, mscorlib, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 failed. 

Here is the source code for the TrustedClient program. As you can see, it is indeed virtually identical to the EvilClient code. But its output is surprisingly different.

 //  TrustedClient  .cs using System; using System.Security; class TrustedClient {    static void Main(string[] args)    {       //NOTE: need ref to ImperativeCASComponent.dll       //try to call on the component       try       {          UrlIdentityPermissionComponent.  DoSomethingForClient  ();       }       catch (SecurityException se)       {          Console.WriteLine("Error: " + se.Message);       }    } } 

Here is the output that results from running the TrustedClient program. This time, no exception is thrown, so the error message is not displayed. The only line of output results from a Console.WriteLine method in the UrlIdentityPermissionComponent assembly, showing that it is quite happy with being called by this particular client program.

 Client call permitted 

Why do these apparently identical programs result in such different behavior? To understand this, let's look at the code in the UrlIdentityPermissionComponent.dll assembly. As you can see, we create the UrlIdentityPermission object, specifying the desired (i.e., trusted) client application, and then call the Demand method. Then, it simply goes about its business, and, if the client does not match the URL from which the trusted client should originate, then a security exception is automatically thrown. There is no fussing about with an Evidence object or iterating over an enumeration in a loop. You just let CAS do its job.

 //  UrlIdentityPermissionComponent  .cs using System; using System.Windows.Forms; using System.Security.Permissions; public class UrlIdentityPermissionComponent {    public static void DoSomethingForClient()    {  UrlIdentityPermission urlidperm =   new UrlIdentityPermission(   "file://C:/... /TrustedClient.exe");   urlidperm.Demand();  //if we got this far then all is OK       Console.WriteLine(          "Client call permitted");    } } 

In the actual source code, the path in the previous listing is fully specified in the UrlIdentityPermission object. Since it is too long to display properly in this book, it has been trimmed down in size using three-dot notation. In this code listing the Demand method ensures that the CLR will detect any mismatch between the desired permission and the actual current permission in effect at runtime.

Recall that when an assembly is loaded, the CLR reviews all the available host evidence and assigns the assembly all of its identity permissions based on that evidence. The example we have just looked at used the UrlIdentityPermission class, which is only one of the available identity permission classes. We could just as easily have used any of the following identity permission classes. Recall that these identity permissions refer to where the assembly came from (site, URL, and zone) or who digitally signed it (strong name and publisher).

  • PublisherIdentityPermission ” X.509 certificate

  • SiteIdentityPermission ” Hostname part of the URL

  • StrongNameIdentityPermission ” Cryptographic signature

  • URLIdentityPermission ” Entire URL in its raw form

  • ZoneIdentityPermission ” MyComputer, LocalIntranet, Internet, Restricted, or Trusted

THE FILEIOPERMISSION EXAMPLE

Let's turn our attention now to an example of a permission class that is not an identity permission. The FileIOPermission example shows how the FileIOPermission class can be used to control file IO operations in a method. The FileIOPermission directory contains three projects: FileIOPermission , which is an EXE, as well as AttemptIO and AvoidIO , which are DLLs. The FileIOPermission program creates a FileIOPermission object that represents unrestricted file access, but it then calls on its Deny method, effectively disallowing all file IO privileges. It then calls into the DoNoFileIO and DoFileIO methods of the two DLL assemblies.

Although the Deny method is called in a different assembly than where the IO will actually be attempted, the call stack will be walked back up to the Main method, where the security system will discover that the file IO permission is denied, causing a security exception to be thrown. The source code for all three projects follows .

 //FileIOPermission.cs //must add ref to AvoidIO.dll //must add ref to AttemptIO.dll using System; using System.IO; using System.Security.Permissions; using System.Security; class FileIOPermissionExample {    public static void Main()    {  FileIOPermission fiop = new FileIOPermission(   PermissionState.Unrestricted);  fiop.  Deny  ();       try       {          AvoidIO avoidio = new AvoidIO();          avoidio.  DoNoFileIO  ();          AttemptIO attemptio = new AttemptIO();          attemptio.  DoFileIO  ();       }       catch(SecurityException se)       {          Console.WriteLine(se.Message);       }    } } //AvoidIO.cs using System; public class AvoidIO {    public void  DoNoFileIO()  {       Console.WriteLine("DoNoFileIO called...");       Console.WriteLine("Nothing written.");    } } //AttemptIO.cs using System; using System.IO; public class AttemptIO {    public void  DoFileIO()  {       Console.WriteLine("DoFileIO called...");       String text = "Here is some data to write";       FileStream fs = new FileStream(          "outputdata.txt",          FileMode.Create, FileAccess.Write);       StreamWriter sw = new StreamWriter(fs);       sw.Write(text);       sw.Close();       fs.Close();       Console.WriteLine(          "Written to outputdata.txt: " + text);    } } 

In the previous example, using the UrlIdentityPermission class, we explicitly called the Demand method to determine whether or not we had that particular permission before proceeding. In contrast, in the above source listings, we never call the Demand method explicitly. This is because the FileStream class that we are using calls the Demand method for us where necessary. In general, the predefined permission classes that do not relate to identity evidence do not require you to call the Demand method in your own code. The .NET Framework generally knows when to do that for you. You might want to call the Demand method yourself if you would like to test for the permission earlier to improve efficiency or simplicity. If you implement your own custom permission classes, you must take on the responsibility to make the appropriate calls on the Demand method where necessary.

The output from running the FileIOPermission program follows. As you can see, the method that did not attempt IO worked fine, but the method that attempted file IO threw a FileIOPermission exception.

  DoNoFileIO  called... Nothing written.  DoFileIO  called... Request for the permission of type   System.Security.Permissions.  FileIOPermission  ,   mscorlib, Version=1.0.3300.0, Culture=neutral,   PublicKeyToken=b77a5c561934e089  failed  . 


.NET Security and Cryptography
.NET Security and Cryptography
ISBN: 013100851X
EAN: 2147483647
Year: 2003
Pages: 126

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