Evidence-Based Security


To learn about imperative CAS, and particularly about evidence-based security, let's begin by looking at the Evidence class. We will then see how to obtain an Evidence class object of the currently running application domain. Finally, we will see how to enumerate its contents and then make imperative programming decisions based on the discovered evidence.

The Evidence Class

Before we look at the ImperativeCASComponent example, we should have a good understanding of the Evidence class itself, since it is the crucial ingredient in that code example. The Evidence class, found in the System.Security.Policy namespace, encapsulates the set of evidence information that can be used to enforce security policy decisions.

Terminology: Evidence and Security Policy

Evidence is the set of security characteristics associated with running code, such as an assembly's digital signature, zone, and site of origin. Evidence is used to categorize assemblies into code groups to which permissions are granted or denied based on established security policy.

Security policy is the set of rules established by an administrator that grants or denies permissions for managed code, effectively determining what operations an assembly is allowed to perform.

For security reasons, the Evidence class is sealed, meaning that it cannot be used as the superclass for any new derived classes. Just imagine the skullduggery you could unleash if you were able to trick the CLR into substituting your own derived class in place of the highly trusted Evidence class!

The types of evidence that can be obtained via an Evidence object may include digital signature, point of origin, or even custom evidence information that may be useful in explicitly making imperative CAS decisions.

The Evidence class implements the two interfaces ICollection and IEnumerable . As we will see shortly, the ICollection interface specifies members for containing a collection of objects, and the IEnumerable interface provides access to those objects via the IEnumerator interface. In the case of the Evidence class, the contained objects represent distinct pieces of host evidence and assembly evidence. Here is the declaration for the Evidence class.

 public sealed class Evidence :    ICollection, IEnumerable 

Terminology: Host Evidence and Assembly Evidence

When evaluating code access permissions, security policy can make use of two sources of evidence, known as host evidence and assembly evidence.

Host evidence is evidence provided by the host computer that provided the assembly. This evidence provides information about the origin (i.e., the URL, site, and zone) of the assembly as well as the identity of the assembly (i.e., hash or, in the case of a signed assembly, a digital signature or certificate).

Assembly evidence is additional evidence contained within the assembly itself and may be incorporated into security policy by an administrator or programmer. Assembly evidence extends the set of evidence available for making security policy decisions. The default security policy ignores assembly evidence, but security policy can be configured to make use of it.

EVIDENCE CONSTRUCTORS

There are three constructors in the Evidence class. The Evidence constructor, which takes no parameters, initializes a new empty instance of the Evidence class. Of course, an Evidence object that contains no evidence information is not too useful. However, we shall see that the Evidence class provides methods for adding evidence. The constructor that takes a single Evidence parameter provides a shallow copy. The third constructor takes two Object array parameters, which are used to initialize the new Evidence instance with two arrays of host and assembly evidence objects.

 public Evidence(); //initialize a new empty instance public Evidence(    Evidence evidence //shallow copy ); public Evidence(    object[] hostEvidence, //host evidence array    object[] assemblyEvidence //assembly evidence array ); 
EVIDENCE PROPERTIES

In the Evidence class, there are five public properties named Count, Locked, IsSynchronized, SyncRoot , and IsReadOnly . Strangely, only the two properties Count and Locked are of any actual use. As we previously mentioned, the Evidence class implements the ICollection interface, which specifies the public properties Count, IsSynchronized , and SyncRoot , as well as the public method CopyTo . Since the Evidence class encapsulates a set of evidence information objects, it makes sense that Count is a read-only property that represents the number of these pieces of contained evidence.

The IsSynchronized method is normally used on collections to determine if the collection is thread-safe. [20] In the case of the Evidence class, IsSynchronized always returns false, since thread-safe evidence sets are not supported. This actually makes sense, since it would be quite difficult to imagine why threads would be messing around with sensitive, security- related operations at the same time using the same evidence. In any case the IsSynchronized property is effectively useless and therefore is not used.

[20] A thread-safe collection can keep its cool while multiple threads add, remove, and modify its elements simultaneously . If a collection is not thread-safe, then accessing it from simultaneous threads can lead to data corruption or exceptions being thrown.

The SyncRoot property normally provides an object on which you can synchronize access to a collection. However, since synchronization of evidence collections is not supported, SyncRoot only returns this . Therefore, the SyncRoot property is also of no practical use and is not used. In spite of being useless, both IsSynchronized and SyncRoot are defined by the ICollection , and therefore the Evidence class must expose them even if they serve no useful purpose!

The IsReadOnly property always returns false , because read-only evidence sets are not supported. Since this return value is a forgone conclusion, IsReadOnly is also a useless property, and it is therefore not used.

The Locked property gets or sets a true or false value indicating whether the evidence is currently locked. If this property is set to false, then the contained evidence can be modified by calling the AddHost or Merge methods, which we discuss shortly. If the Locked property is set to true, however, then a SecurityException exception is thrown by the Merge or AddHost [21] methods, unless the code has been granted the ControlEvidence security permission. In fact, it turns out that you need the ControlEvidence security permission to set the Locked property to a new value in the first place. The default value for the Locked property is false. If you are not planning to add or merge evidence information to an Evidence object, then you can simply ignore the Locked property altogether.

[21] Strangely, this is true for the Merge and AddHost methods, but not for the AddAssembly method.

The following list briefly describes each of these Evidence properties. Again, note that three of them are of no use because they return hardwired results, and also note that Count is read-only, but Locked is read/write.

  • Count gets the number of items in the evidence collection (read-only).

  • Locked gets or sets a value indicating whether the evidence collection is locked (read/write).

  • IsSynchronized indicates whether the collection is thread-safe (always returns false).

  • SyncRoot is normally used to synchronize access to the collection (always returns this).

  • IsReadOnly indicates whether the evidence is read-only (always returns false).

EVIDENCE METHODS

In the Evidence class there are seven [22] public methods: GetEnumerator, CopyTo, AddAssembly, AddHost, GetAssemblyEnumerator, GetHostEnumerator , and Merge . The basic operations that these methods support are enumerating and copying, as well as adding and merging evidence information.

[22] The Evidence class has seven public methods, not including those originating from the Object superclass. All classes inherit, and frequently override, these Object class public methods named Equals , GetHashCode , GetType , and ToString , as well as the protected methods Finalize and MemberwiseClone .

One of these methods, GetEnumerator , originates in the IEnumerable interface. The GetEnumerator method simply returns an IEnumerator interface that can be used to walk through the evidence collection via its Current property and the MoveNext and Reset methods. Another method, CopyTo , originates in the ICollection interface. The CopyTo method simply copies the elements of the evidence collection to an Object array, starting at a particular index position. The remaining six methods are specific to the Evidence class itself.

  • GetEnumerator provides access to all the contained host and assembly evidence.

  • CopyTo copies evidence to an Object array.

  • AddAssembly adds specified assembly evidence to the collection.

  • AddHost adds specified host evidence to the collection.

  • GetHostEnumerator provides access to the host evidence.

  • GetAssemblyEnumerator provides access to the assembly evidence.

  • Merge combines the two evidence collections into a single evidence collection.

Obtaining the Current Application Domain Evidence

Although the Evidence class does provide constructors, you typically want to obtain a ready-made Evidence object that reflects the current runtime situation. The way to get such an Evidence object is to access the Evidence property of the current application domain object. The AppDomain.CurrentDomain static property provides the current application domain for the current thread. This is shown in the following code.

 Evidence evidence =  AppDomain.CurrentDomain.Evidence  ; 

Enumerating Evidence

You can walk through the available evidence by enumerating the contents of the current application domain Evidence object. If you do that, you will find security zone and URL information, which is determined by the physical origin of the assembly. You will also find hash code information, which provides identity evidence relating to the binary contents of the assembly itself. If, and only if, the assembly has been digitally signed, you will also find cryptographically strong name evidence. A strong name allows the signer to be mathematically verified and ensures that the assembly cannot be forged, tampered with, or repudiated. To be effective, these guarantees depend on a trusted certificate authority that vouches for the digital signature.

These types of evidence are represented by the following classes, all of which are defined in the System.Security.Policy namespace:

  • Zone specifies a security zone, such as MyComputer, Internet , and so on.

  • Url specifies the protocol and path information that specifies the assembly's origin.

  • Hash contains the SHA1 and MD5 hash values for the assembly.

  • StrongName contains, if digitally signed, the name, version, and public key of the assembly.

  • Site specifies, if loaded via an Internet protocol, the site from which the assembly originated.

Once you have the current application domain's Evidence object, you can obtain an IEnumerator interface on it and walk through the evidence information, making any appropriate security decisions that you deem necessary. To do this, simply call on the Evidence object's GetEnumerator method and enter a while loop that calls MoveNext in each iteration.

 //walking thru the evidence  IEnumerator enumerator = evidence.GetEnumerator();  while (  enumerator.MoveNext()  ) {    object item =  enumerator.Current  ;    //make decisions based on evidence item    if (...)    {       ...    } } 

The WalkingThruEvidence Example

Let's look at the WalkingThruEvidence example to see how this can be done in practice. This program obtains the current Evidence object and then displays the evidence information that it contains. In this example we have a digitally signed assembly, so we can see the additional StrongName evidence that is produced by the presence of a digital signature.

How to Sign an Assembly

The WalkingThruEvidence example is a digitally signed assembly. There are two ways to digitally sign an assembly. One way is to use Al.exe (Assembly Linker) to add the signature to an existing assembly. The other way is to use either the AssemblyKeyFileAttribute or the AssemblyKeyNameAttributeassembly attribute to define the signature directly in your source code.

In any case you must first generate a public/private key pair before you can sign an assembly with either of these techniques. Since we are interested in authentication rather than secrecy , we must digitally sign the assembly with the private key and then make the assembly and the associated public key publicly available. You can create this key pair using Sn.exe (Strong Name utility). For example, the following command line creates a new key pair file called MyKeyPair.snk. Sn.exe can also write the resulting keys to a named key container managed by a CSP (cryptographic service provider). Check the tool's documentation for details.

 sn -k MyKeyPair.snk 

The resulting file contains both public and private keys, but it must be kept secret, since it does contain the private key. To expose only the public key in the key pair file, you must extract and copy it to a separate file. The following command line extracts the public key from MyKeyPair.snk and places it into MyPublicKey.snk .

 sn -p MyKeyPair.snk MyPublicKey.snk 

To sign a DLL or an EXE assembly, you can use the Assembly Linker utility Al.exe . The following command line specifies that the WalkingThruEvidence.exe assembly is to be signed and the MyKeyPair.snk key pair file that contains the private key is to be used for the digital signature. The Assembly Linker utility looks for the key pair relative to the current and output directories.

 al /out:WalkingThruEvidence.exe /keyfile:MyKeyPair.snk 

The other technique for signing an assembly is to use code attributes. You can add either the AssemblyKeyFileAttribute or the AssemblyKeyNameAttributeassembly attribute to your source code. The AssemblyKeyFileAttribute specifies the name of the file that contains the desired key pair. The AssemblyKeyNameAttributeassembly specifies the name of a key container within the CSP that contains the key pair. The following code uses the AssemblyKeyFileAttribute with the key file named MyKeyPair.snk . The full path must be specified, but in this example it has been shortened to fit onto a single line in this book. This attribute is typically added to the source file named AssemblyInfo.cs , which is automatically generated for you when you create a C# project in Visual Studio .NET.

 [assembly:AssemblyKeyFileAttribute(@"...\MyKeyPair.snk ")] 

The WalkingThruEvidence example first obtains the current Evidence object, and then it loops through all the evidence that it contains. Then, for each piece of evidence, it displays the details found. Here is the source code.

 //obtain appdomain security evidence Evidence evidence =    AppDomain.CurrentDomain.Evidence; //obtain evidence enumerator IEnumerator enumerator = evidence.GetEnumerator(); //walk thru evidence while (enumerator.MoveNext()) {    object item = enumerator.Current;    //display the evidence    Type type = item.GetType();    Console.WriteLine(type.Name + ": ");    if (type == typeof(Url))    {       Console.WriteLine(          "   Value: " +          ((Url)item).Value);    }    if (type == typeof(Zone))    {       Console.WriteLine(          "   SecurityZone: " +          ((Zone)item).SecurityZone);    }    if (type == typeof(Hash))    {       Console.WriteLine(          "   MD5: " +          BitConverter.ToString(((Hash)item).MD5));       Console.WriteLine(          "   " + "SHA1: " +          BitConverter.ToString(((Hash)item).SHA1));    }    if (type == typeof(StrongName))    {       Console.WriteLine(          "   Name: " +          ((StrongName)item).Name);       Console.WriteLine(          "   Version: " +          ((StrongName)item).Version);       Console.WriteLine(          "   PublicKey: " +          ((StrongName)item).PublicKey);    }    if (type == typeof(Site))    {       Console.WriteLine(          "   Name: " +          ((Site)item).Name);    } } 

Here is the output of the WalkingThruEvidence example that results from running it directly from the local file system. Output lines that are too long to fit on the printed page have been shortened where necessary. As you can see, the zone is the local computer, and the URL specifies the file protocol followed by the file path where the assembly is located. Because the assembly was digitally signed, you can see the strong name evidence, including the public key. Finally, the MD5 and SHA-1 hash information is present. If you were to rebuild the assembly without the digital signature, then the strong name evidence would be missing. Since this was run directly from the local file system, and not via Internet Explorer, the Web site evidence is absent.

 Zone:    SecurityZone: MyComputer Url:    Value: file://C:/.../WalkingThruEvidence.exe StrongName:    Name: WalkingThruEvidence    Version: 1.0.1010.20177    PublicKey: 00240000048000009400000006020000002400005253...      5C5703B8AEEA06C1CFD72327CD0F35FD650345ACA6806F7 Hash:    MD5: A6-AB-D6-AD-42-41-38-67-BF-57-32-4C-55-A4-6C-A4    SHA1: F6-E1-17-1A-4B-6C-BE-DB-4B-ED-...-E4-E2-C3-37 

Accessing the WalkingThruEvidence Example Via IIS

Let's try something slightly different now by publishing the WalkingThruEvidence assembly on the local IIS Web site and then executing it from within Internet Explorer via http. To publish a file on an IIS Web site, you simply copy the file to the \inetpub\ wwwroot directory. Then, to access it in Internet Explorer, you enter the URL in the form of http:// servername /filename . To do this locally, you can specify localhost as your server name. The URL that you will then enter in Internet Explorer will therefore be http://localhost/WalkingThruEvidence.exe .

Assuming that you have deployed the assembly to the IIS root directory, if you try to run this program in this way, Internet Explorer will attempt to load and run the assembly, but this will only result in a SecurityException being thrown. This happens because the assembly is no longer being loaded from the My Computer zone, which is fully trusted, but instead is being loaded from the Local Internet zone, which is not granted full trust by default.

To see this program work properly, you must change the trust level granted to the Local Internet zone to full trust. Warning : This is an experiment only. You should never set the intranet zone to full trust in a production environment! This experiment should only be done temporarily on a non-networked development machine. Once you are done, you should set the trust level back to its original default level to avoid an obvious security risk. To change this trust level, select Start Settings Control Panel Administrative Tools Microsoft .NET Framework Wizards, and then select Adjust .NET Security, which opens the Security Adjustment Wizard, as shown in Figure 8-19

Figure 8-19. The Security Adjustment Wizard.

graphics/08fig19.gif

In the Security Adjustment Wizard, select the Make changes to this computer radio button and click Next. Click on the Local Intranet icon and adjust the level of trust to Full Trust. This is shown in Figure 8-20

Figure 8-20. Local Intranet zone set to full trust.

graphics/08fig20.gif

Click Next, and then, to complete the wizard, click Finish. Once you have done this, you can again try running the program using Internet Explorer, with the result shown in the following output. The zone, which was previously MyComputer, is now changed to Intranet. The URL, which was previously file://C:/.../WalkingThruEvidence.exe , is now http://localhost/WalkingThruEvidence.exe . From this, you can see clearly that a completely different protocol (http rather than file) was used this time to locate and load the assembly. The Web site evidence, which was completely missing before, now specifies the localhost machine, and the strong name and hash evidence is no longer available. This all clearly shows that we are now dealing with an entirely different code group .

 Zone:    SecurityZone: Intranet Site:    Name: localhost Url:    Value: http://localhost/WalkingThruEvidence.exe 

Imperative CAS

Let's turn our attention now to the imperative approach to CAS. We first consider how this is done by browsing through the available evidence, and later we will see how to do the same thing using CodeAccessPermission derived classes.

THE IMPERATIVECAS EXAMPLE

The ImperativeCAS example program, found together with the associated TrustedClient and EvilClient programs in the ImperativeCAS directory, demonstrates the explicit imperative approach for protecting a component by allowing it to defend itself from being called by certain untrusted client applications. The EvilClient and TrustedClient programs both attempt to call into the DoSomethingForClient method exposed by the ImperativeCASComponent assembly, but only the TrustedClient is successful.

This example makes explicit use of the Evidence class and chooses between two alternative actions (i.e., execute normally or throw a SecurityException ) based on an if statement that tests a particular detail of the available security evidence. Shortly, we will see another imperative security example using permission objects rather than explicitly perusing the contents of an Evidence object.

The EvilClient program is very simple. It just calls into a static method named DoSomethingForClient on an object defined by a class named ImperativeCASComponent in a separate assembly named ImperativeCASComponent.dll .

 //  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       {          ImperativeCASComponent.  DoSomethingForClient  ();       }       catch (SecurityException se)       {          Console.WriteLine(             "SecurityException: " + se.Message);       }    } } 

Notice that the following output shows the unsuccessful result of running the EvilClient program, indicating that the client is not considered trustworthy. We will see how this happens when we study the code in the DoSomethingForClient method.

 DoSomethingForClient called  SecurityException  : Client is not trustworthy 

Before we study the DoSomethingForClient method, let's look at what happens when we run another program that appears to be identical to the EvilClient program. As you can see in the following code listing, the TrustedClient program looks virtually identical to the EvilClient program in every detail. Yet when you run it, you get an entirely different result.

 //  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       {          ImperativeCASComponent.  DoSomethingForClient  ();       }       catch (SecurityException se)       {          Console.WriteLine(             "SecurityException: " + se.Message);       }    } } 

The following output shows the result of running the TrustedClient program. Notice that this time, the output shows a successful result, indicating that the client is considered trustworthy. We will see how this happens as well by studying the code in the DoSomethingForClient method.

 DoSomethingForClient called Permitted: Client is  trustworthy  

Let's now look at the code that implements the DoSomethingForClient method. This static method is implemented in the class named ImperativeCASComponent . Both the TrustedClient and EvilClient attempt to call this method in exactly the same way.

The DoSomethingForClient method starts by obtaining the current application domain's Evidence object. It then obtains an IEnumerator interface and enters a while loop where each piece of available evidence is inspected. As we have seen, there are several types of evidence that may be provided by the Evidence object, but the type of evidence in which we are interested in this example is represented by the Url class, defined in the System.Security.Policy namespace. Therefore, we use an if statement to determine if the type of the evidence is a Url , and, if it is, we test to see if its value ends with the string TrustedClient.exe . Only if this match is found do we recognize the client as being trustworthy. All clients with names other than TrustedClient.exe are rejected by throwing a SecurityException . Of course, this is a simplified example that focuses on concepts rather than realism . In a more realistic scenario, you would probably want to make more elaborate decisions based on a combination of the available evidence.

 //ImperativeCASComponent.cs using System;  using System.Security;   using System.Collections;   using System.Security.Policy;  using System.Windows.Forms; public class ImperativeCASComponent {    //this method only works for TrustedClient.exe    public static void  DoSomethingForClient  ()    {       Console.WriteLine(          "DoSomethingForClient called");       //obtain appdomain security evidence  Evidence evidence =   AppDomain.CurrentDomain.Evidence;  //obtain evidence enumerator  IEnumerator enumerator = evidence.GetEnumerator();  bool trustworthy = false; //assume the worst       while (enumerator.MoveNext()) //walk thru evidence       {          object item = enumerator.Current;          //test to see if Url is acceptable          Type type = item.GetType();  if (type == typeof(System.Security.Policy.Url))  {             String strUrl =                ((Url)item).Value.ToString();  if (strUrl.EndsWith("TrustedClient.exe"))  {                trustworthy = true; //good news                break;             }          }       }       //throw exception if no good evidence found       if (!trustworthy)  throw new SecurityException  (             "Client is not trustworthy");       //if we got this far then all went OK       Console.WriteLine(          "Permitted: Client is trustworthy");    } } 


.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