Imperative Versus Declarative CAS
Just as we saw in the previous chapter on user-based security, CAS can also be implemented in either an imperative or a declarative manner. We will also see shortly that, just as was the case in
The first of these imperative approaches involves the explicit use of the
Evidence
class. This approach is sometimes referred to as explicit evidence-based security, because you explicitly evaluate host and assembly security evidence in making programmatic decisions. The other imperative approach makes use of various permission classes, derived from
CodeAccessPermission
, that automatically throw a
SecurityException
where appropriate. In a later section, we will also see how to implement CAS in a declarative way by making use of permission attributes applied to
|
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
The Evidence ClassBefore 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.
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
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
public sealed class Evidence : ICollection, IEnumerable
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
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 PROPERTIESIn 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-
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.
The following list
EVIDENCE METHODSIn 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.
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.
Obtaining the Current Application Domain EvidenceAlthough 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
These types of evidence are represented by the following classes, all of which are defined in the System.Security.Policy namespace:
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 ExampleLet'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.
The
WalkingThruEvidence
example first obtains the current
Evidence
object, and then it
//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\
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.
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.
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
Zone: SecurityZone: Intranet Site: Name: localhost Url: Value: http://localhost/WalkingThruEvidence.exe Imperative CAS
Let's
THE IMPERATIVECAS EXAMPLE
The
ImperativeCAS
example program, found together with the associated
TrustedClient
and
EvilClient
programs in the
ImperativeCAS
directory,
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
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
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
//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"); } } |