I mentioned earlier that evidence is a collection of information. This was meant very literally. Evidence is represented by the System.Security.Policy.Evidence class, and this class implements the System.Collections.ICollection interface. This means that it is a container class for instances of other classes. Actually, each evidence object has two separate collections to cover two different sources of evidence:
TIP
To gain a more practical understanding of what evidence looks like on an assembly, compile and run the program from Listing 5.1. It simply looks at the Evidence property on a System.Reflection.Assembly object.
Host-provided evidence is the evidence that has been discussed so far. For purposes here, a “host” is one of two things. First, a host could be something that initiates the Common Language Runtime. This is some unmanaged code that calls CorBindToRuntimeEx (an exported function from mscoree.dll). Second, a host could be a managed piece of code that can launch other managed pieces of code.
Regardless of the type of host being discussed, the Common Language Runtime provides some types of evidence by default. Table 5.2 lists those default types of evidence. Thus, a host does not necessarily have to provide any additional evidence itself. If a host does provide evidence that contradicts some evidence provided by the Common Language Runtime, then the host-provided evidence overrides the other evidence. Any host-provided evidence that doesn't override evidence provided by the Common Language Runtime is just merged into the collection of host-provided evidence.
An unmanaged host is necessary to run any .NET Framework application. Certainly, something must start the Common Language Runtime. The .NET Framework ships with several different hosts. Some examples are ASP.NET, Internet Explorer, and the command-line host. All three of these hosts are capable of starting a .NET application, and all three can provide evidence about the applications they start.
The security functionality of the .NET Framework depends on another entity providing prior knowledge of managed code. The host provides the .NET Framework with information such as the root directory for an application, because the Common Language Runtime cannot determine this information. This can be done by using the ICorRuntimeHost interface. When a call is made to CorBindToRuntimeEx to start the Common Language Runtime, an interface pointer to ICorRuntimeHost is returned. Using that pointer, calls can be made to CreateEvidence and CreateDomain/CreateDomainEx to create evidence objects and apply them to an app domain, respectively.
An unmanaged host can only provide evidence on an app domain. There is no unmanaged method that loads an assembly, but the unmanaged host can transfer control to a managed piece of code that will load and provide evidence about assemblies.
To learn more about unmanaged hosts, see the .NET Framework SDK documentation under “Programming with the .NET Framework,” “Hosting the Common Language Runtime.”
A managed host is simply some code that loads and executes other arbitrary, managed code. Not all managed code can do this due to security mechanisms that will be explained later in this section. Suffice it to say that it takes a high degree of trust for a piece of managed code to act as a host.
Most unmanaged hosts will also have a managed piece with which to work. This is because it is simpler and more efficient to transfer control to a managed host that then launches managed user code. Thus, a likely scenario would be to have a host use its unmanaged code to set evidence on an app domain, while its managed code sets evidence on user assemblies.
Table 5.1 lists several methods a managed host might call to provide evidence on assemblies and app domains. Note that these methods often have several forms, some of which do not have arguments for passing evidence. The forms without evidence arguments simply use the default evidence provided by the Common Language Runtime.
While hosts provide trusted evidence regarding an assembly, an assembly is permitted to provide evidence about itself. This evidence, however, cannot override any evidence provided by a host because that would be a security hole. In fact, all default evidence classes used in assembly evidence are completely ignored by the Common Language Runtime.
Because the Evidence class uses generic collections to store information, it can contain any object as evidence. Consequently, an assembly could provide evidence as plain as an integer or as rich as a signed XML statement. They are all equally simple to add to evidence, though they may not all be equally useful.
TIP
If you want to author a class that will be assembly evidence, there are two optional interfaces to consider implementing. The first is IIdentityPermissionFactory. (Permissions are discussed in detail in Chapter 6, “Permissions: The Workhorse of Code Access Security.”) If you have an identity permission class that corresponds to your evidence, this interface defines a standard way for the Common Language Runtime to create a corresponding identity permission object. The second interface is InormalizeForIsolatedStorage. (IsolatedStorage is discussed in detail in Chapter 29, “Writing a Semi-Trusted Application.”) If you want to create an IsolatedStorage class that uses your custom evidence for isolation, this interface provides a way for the Common Language Runtime to compare serialized evidence objects.
Assembly-provided evidence can be used to state more information about how much an assembly should be trusted. For example, an assembly developer may have submitted his or her application to a third-party software certification lab. The certification lab could provide a signed statement that the developer could attach to his or her assembly as evidence. This statement could say that the lab certifies that the application can safely be granted a high level of trust.
Note that simply providing additional evidence on an assembly will do nothing by itself. To see the real benefits of providing assembly evidence, the Common Language Runtime will have to do something with it. That will probably mean implementing a custom membership condition and modifying the default security policy. (Both of these topics are discussed in Chapter 8.) With the signed statement example, the security policy could look for assemblies with signed statements and assign them more trust than other assemblies.
There is a good whitepaper and some example code that goes into detail about how to add assembly evidence to your assembly. It is located on http://www.gotdotnet.com. I encourage you to read the whitepaper and its example code if you want to provide evidence on some assembly you are writing. Basically, the steps are:
1. |
Create an object that represents the evidence you want your assembly to provide. This will probably be an instance of a class that you created. |
2. |
Serialize that object to a file using the System.Runtime.Serialization.Formatters.Binary class. |
3. |
Compile your .NET Framework code to a module or several modules. For the C# and VB compilers, this is done by using the /target:module option. Modules are much like .lib files used in C and C++. They are pieces of executable files or libraries. |
4. |
Combine the module(s) and the serialized evidence into an assembly using the ALink tool (al.exe) from the .NET Framework SDK. ALink has a /evidence option that is used to point to a serialized evidence file. |
Because assembly evidence is something added explicitly by developers, there is no assembly evidence by default in any assemblies. You can see this if you compile and execute the code from Listing 5.1.
3.145.163.58