Using Existing Security Mechanisms

for RuBoard

Before we dig down into the mechanics of requesting security services, let's recap a little of what we know about security permissions.

Permissions describe a level of access to a resource. In general, for each resource type to be protected, there is a corresponding permission type. The .NET Framework provides many pre-baked permissions for such standard resources as files, registry keys, and environment variables , as well as abstract resources, such as the ability to manipulate the security system itself or execute unmanaged code. Permission types can also be implemented by your own code or third parties to cover new resources or situations where security is appropriate.

While some permission types are binary in nature (they can only be granted or not granted, nothing in between), others are much more detailed. For example, the standard permission type FileIOPermission can specify exactly which files are affected and which type of access is granted (read, write, and so on) in each case. This subsidiary information is known as permission state. The amount of additional state a permission type possesses is obviously specific to the function of the permission (and to the resource it's guarding).

All permissions, however, do share two special states ”empty and unrestricted. These correspond to the logical extremes of the access possible ”no access at all versus complete and unqualified access. We now see that binary permission types are simply those that have no other states than these.

If permission types describe the scope of access possible to a resource, an instance of a permission type describes a specific concrete level of access. The permission state is encoded as member data in the permission object (in whatever manner the implementation of the permission type sees fit). Note that the existence of a particular permission instance ( FileIOPermission for read access to foo.dat , for example) does not imply that the permission is granted or demanded in any way. It merely describes a possible permission state, in much the same way that a filename describes a file without saying what action is to be performed on it.

Often, you may want to perform the same operation with multiple permission instances at the same time (for clarity or for performance). This can be accomplished through the use of permission sets, embodied by the System.Security.PermissionSet class. This class is a container for multiple permission instances and supports exactly the same sorts of operations that a single permission instance would.

The operations that can be performed on permissions or permission sets include demand (asking whether the associated permission(s) are granted) and security stack walk modifiers ( Assert , Deny , PermitOnly ”see Chapter 7). Code cannot ask to be granted permissions; the set of granted permissions for each assembly is calculated automatically by the security system the first time a security demand is made against that assembly. An assembly can request to be granted less permissions than the security system and the current security policy would normally give out, and the mechanism for doing this will be discussed in the section titled "Using Assembly-Level Declarative Security," later in this chapter.

There are two distinct ways in which security requests can be invoked within the .NET Framework runtime ”declarative and imperative. For some operations ( Demand , Assert , and so on), both techniques offer identical semantics, while others ( LinkDemand and InheritanceDemand ) are only available declaratively . There are also other tradeoffs in the use of declarative versus imperative; we'll compare and contrast these techniques in the following sections.

Using Imperative Security

Imperative security operations are those initiated explicitly in the caller's source code via a standard method call. The instance will be a security permission or permission set, while the method describes the operation desired. The following example shows one way of checking that your callers have permission to call unmanaged code:

 new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand(); 

If the call succeeded, no value is returned; execution simply proceeds as normal to the next line. If, however, the operation failed (in the sense that one of your callers did not have the permission requested ), a SecurityException is thrown. We'll discuss these exceptions in greater depth in the "Implementing Your Own Permissions" section later in this chapter.

The instance of SecurityPermission that we made the call on can simply be discarded afterward; it's no longer needed. In the interest of performance, demands or other security operations on common code paths can cache specific permission instances and use them over and over again. This is legal (and thread safe) because the permission instance is never altered in any way by any of the security operations ”it merely serves as a read-only copy of the permission state that is being demanded, asserted, and so on.

SecurityPermission is a standard .NET Framework permission type that is actually a grab bag of various binary "sub permissions" that relate to the system itself (for example, the ability to skip IL verification, control the policy system, override security evidence for an assembly, and so on). The SecurityPermission class allows these various states to be set directly on construction of an instance via the SecurityPermissionFlag enumeration; all the subpermissions required can simply be OR 'd together to achieve the desired result. This is common for permission types; much of their state is settable from constructor args, allowing the sort of simple, single line invocation seen in previous examples.

Other, more complex, permissions may require additional fields or properties set after initial object creation and before an operation is invoked:

 FileIOPermission perm = new FileIOPermission(PermissionState.None); perm.SetPathList(FileIOPermissionAccess.Read, "foo.dat"); perm.SetPathList(FileIOPermissionAccess.Write, "bar.dat"); perm.Demand(); 

Here we created a permission instance with no state whatsoever, and then added read and write access to separate files before finally invoking a Demand on the resulting fully formed permission. These setup steps can be arbitrarily complex; the permission objects are normal managed objects, just like any other in the .NET Framework. This demonstrates one of the strengths of imperative security requests ”flexibility. Most of the important details of the request can be computed dynamically at runtime, something (as we shall see) that is not possible declaratively. The following is an example of state being set up dynamically:

 public void AccessFile(String filename, bool create) {     FileIOPermission perm;     perm = new FileIOPermission(PermissionState.None);     if (create)         perm.SetPathList(FileIOPermissionAccess.Write, filename);     else         perm.SetPathList(FileIOPermissionAccess.Read, filename);     perm.Demand();     ... } 

The details of how permission state is set will vary depending on the implementation of the permission type, but all implementations should follow the general model previously described. In addition, all permission types should provide a constructor that takes the PermissionState enumeration. This has two values ” None and Unrestricted ”and allows the permission to be created with either no or full access, respectively.

 new FileIOPermission(PermissionState.Unrestricted).Assert(); 

PermissionState.None is generally only useful as a base state for a permission (when you intend to set other state through fields or properties). That's because operations on empty permissions are typically no-ops (an empty permission is always considered granted).

Operations on sets of permissions are constructed in much the same manner as the single permission case. Simply create an empty PermissionSet , instantiate individual permission instances as before, and include them in the set with PermissionSet.AddPermission . The desired operation ( Demand , Assert , and so on) can then be performed on the permission set instance itself, which will automatically take care of the business of applying the operation to each of the embedded permissions (and generally in a far more efficient manner than performing the operation manually on each permission). The following are a couple of examples demonstrating how to compose complex permission sets:

 PermissionSet pset = new PermissionSet(); EnvironmentPermission eperm; eperm = new EnvironmentPermission(PermissionState.Unrestricted); pset.AddPermission(eperm); FileIOPermission fperm; fperm = new FileIOPermission(PermissionState.None); fperm.SetPathList(FileIOPermissionAccess.Read, "foo.dat"); fperm.SetPathList(FileIOPermissionAccess.Write, "bar.dat"); pset.AddPermission(fperm); pset.Demand(); 

Permission sets also have the notion of the unrestricted state (there's a constructor overload on PermissionSet that takes the same PermissionState enumeration as the individual permissions). It means more or less the same thing but at a higher level ”unrestricted access to all resources (that is, all permissions with their state set to unrestricted).

However, it does not make sense to include all permission types in this unrestricted set. Examples of this are the identity permissions ”permissions that represent the right to be known by specific pieces of identity evidence, such as the StrongNameIdentityPermission granted to assemblies signed with a strong name . These don't fit well into a model using unrestricted sets; no assembly is granted all identities, so demanding a set containing all of them would be guaranteed to fail.

Therefore, permissions are divided into two sets ”those which can be considered part of the unrestricted set (the normal permissions) and those that are not (such as the identity permissions). These two types of permissions are distinguished by whether the IUnrestrictedPermission interface is implemented by the permission type; if so, the permission is considered part of the unrestricted set and will be implicitly included every time a PermissionSet is created with PermissionState.Unrestricted . The IUnrestrictedPermission interface will be discussed further in the "Implementing Your Own Permissions" section later in this chapter.

Using Declarative Security

Whereas imperative security is performed through the use of method calls in the code, declarative security is specified in the metadata. Specifically , this means adding attributes to classes and methods . These attributes have the same source code format as custom attributes; they use the same syntax that your language would for those entities. For example, in C#

 [ReflectionPermission(SecurityAction.Demand, ReflectionEmit=true)] public void CreateAssembly() {     ... } 

Here, ReflectionPermission actually refers to the ReflectionPermissionAttribute class (any permission that can be used declaratively must implement a complementary permission attribute class; by convention, the attribute classname is the same as the permission classname with an Attribute suffix). C# allows the Attribute suffix to be omitted for readability.

Security attribute classes always have a single constructor that takes one argument ”an enumeration of type SecurityAction . This enumeration indicates the type of operation to be performed with the permission or permission set specified with the rest of the attribute declaration. Consequently, the action corresponds to the final method call made in imperative usage (that is, SecurityAction.Demand is equivalent to permission.Demand() ).

After the action code, a number of name/value pairs can be specified. Each of these corresponds to a field or property that will be set to the given value in the permission prior to enacting the action. This allows complex state to be set up in the permission (such as indicating specific files and access modes in a FileIOPermission ).

 [FileIOPermission(SecurityAction.Assert,                   Read="c:\foo.dat",                   Write="c:\log.txt")] 

The previous declarative statement could be written imperatively as follows :

 FileIOPermission perm = new FileIOPermission(PermissionState.None); perm.SetPathList(FileIOPermissionAccess.Read, "c:\foo.dat"); perm.SetPathList(FileIOPermissionAccess.Write, "c:\log.txt"); perm.Assert(); 

Note that the state of the permission is initially empty. That is, at least one state item should be supplied to make the statement useful. The following demands nothing at all (an empty RegistryPermission ):

 [RegistryPermission(SecurityAction.Demand)] 

This demand will always pass, regardless of the permissions granted to the calling assemblies. The following will demand the highest possible level of access to the registry:

 [RegistryPermission(SecurityAction.Demand, Unrestricted=true)] 

The Unrestricted property is common to all permissions; it is the equivalent of specifying PermissionState.Unrestricted to the permission constructor in the imperative case:

 new RegistryPermission(PermissionState.Unrestricted).Demand(); 

We have stated that declarative security can be applied to methods or classes. When applied to a method, the action ( Demand , Assert , and so on) is performed just prior to the execution of that method (the exceptions are LinkDemand and InheritanceDemand ; we'll cover those in the sections titled "Using Declarative LinkDemand" and "Using Declarative InheritanceDemand"). A declarative attribute on a class is, with one exception, simply shorthand for the same attribute applied to every method defined by that class. This includes constructors, properties (getters and setters), and events (add and remove). The exception is InheritanceDemand , which has different semantics at the class and method level and will be discussed in the "Using Declarative InheritanceDemand" section later in this chapter.

 [SecurityPermission(SecurityAction.Demand, ControlThread=true)] class MyThreadPackage {     public void Foo() { }     private void Bar() { }     protected void Baz() { } } 

In this example, all three methods ( Foo , Bar , and Baz ) will demand SecurityPermission 's ControlThread (we commonly refer to this as SecurityPermission.ControlThread ). The implicit default constructor supplied by the compiler will also demand this permission. Hence, the mere act of creating a new MyThreadPackage instance will demand the permission (later in this section, we'll discuss an important caveat about the use of this technique with value types).

The level of control over declarative security placed on "special" methods (property getters or setters and implicit event methods) will differ from language to language. In C#, declarative security must be applied directly to property setters or getters and will not work (or even compile) when applied to the property itself:

 String MyProperty {     [MyPropertyPermission(SecurityAction.Demand, ReadAccess=true)]     get {  return m_string; }     [MyPropertyPermission(SecurityAction.Demand, WriteAccess=true)]     set {  m_string = value; } } // The following will not compile. [MyPropertyPermission(SecurityAction.Demand, Unrestricted=true)] String BrokenMyProperty {     get {  return m_string; }     set {  m_string = value; } } 

When dealing with events, the add and remove methods are implicitly defined by the compiler, so declarative security must be specified on the event itself, using the method qualifier on the attribute:

 [method: MyPermission(SecurityAction.Demand, Unrestricted=true)] public event EventHandler MyEvent; 

The attribute will be applied to both add and remove methods; there is no way to limit the action to one of the methods or apply different attributes to each.

There are some important caveats when applying declarative security to classes:

  • In most cases, declarative security for a given action specified at the method level will completely override and replace declarative security for the same action specified at the class level. This is not the case for InheritanceDemand (whose semantic varies depending on the placement) and LinkDemand (which we'll discuss later in the "Using Declarative InheritanceDemand" and "Using Declarative LinkDemand" sections, respectively). This means that if a Demand is made at both class and method level, the method will demand only the permissions specifically attached to that method.

    A special case of this is using a null declaration on the method to cancel the effect of a class level declaration for one or more methods.

     [SecurityPermission(SecurityAction.Demand, ControlThread=true)] class MyThreadPackage {     public void Foo() { }     [SecurityPermission(SecurityAction.Demand)]     private void Bar() { }     protected void Baz() { } } 

    In this code, the class level demand for SecurityPermission.ControlThread is cancelled for the Bar method only.

    However, different actions can coexist peacefully between the levels. A Demand at the class level will not replace an Assert at the method level; both will be evaluated prior to the execution of the method.

    Note that multiple security attributes can be applied to a single class or method (and they will merge as expected). This can be used to work around cases where two distinct demands are being made at the class and method levels, and both are required on the method:

     [RegistryPermission(SecurityAction.Demand, Unrestricted=true)] class RegistryClass {     public void Foo() { }     [RegistryPermission(SecurityAction.Demand, Unrestricted=true),      SecurityPermission(SecurityAction.Demand, UnmanagedCode=true)]     public void Bar() { } } 

    This works even if the two demands are for the same permission type. That is, the following two demands are equivalent:

     [FileIOPermission(SecurityAction.Demand,                   Read="c:\foo.dat",                   Write="c:\bar.dat")] [FileIOPermission(SecurityAction.Demand,                   Read="c:\foo.dat"),  FileIOPermission(SecurityAction.Demand,                   Write="c:\bar.dat")] 
  • A class level demand is sometimes assumed to protect instantiation of an instance of that class (due to the constructor, even if implicit, being automatically decorated with the demand). This assumption does not always hold for value types (structs, in C# parlance). Instantiating simple value types (that is, without supplying any parameters) won't invoke a constructor; the initial state (zeroed memory) is assumed to be sufficient. So the following structure can be created (and manipulated, because fields never have declarative security applied) by untrusted code:

     [SecurityPermission(SecurityAction.Demand, UnmanagedCode=true)] public struct DangerousStructure {     public IntPtr pointerToData;     public int lengthOfData; } 

    The only guarantee you get with value types is that the initial state is all zeros. This fact can be used to police whether a given value type instance was created in a "blessed" manner (that is, through a secured method):

     public struct DangerousStructure {     // A field indicating whether an instance has been initialized     // through a "secured" method -- Allocate below. This field is     // private so that it can't be set directly and will be     // initialized to false by the runtime at instance creation time.     private bool authenticated;     // Allocate an instance of this structure that is authenticated.     [SecurityPermission(SecurityAction.Demand, UnmanagedCode=true)]     public static DangerousStructure Allocate()     {         DangerousStructure ds = new DangerousStructure();         ds.authenticated = true;         return ds;     }     // Check whether a given instance was authenticated (i.e.     // allocated through Allocate).     public bool IsAuthenticated()     {         return authenticated;     }     public IntPtr pointerToData;     public int lengthOfData; } 
  • Declarative security on a class will not propagate to any nested types (or their methods) defined within that class.

     [EnvironmentPermission(SecurityAction.Assert, Unrestricted=true)] class Outer {     void OuterFoo() { }     class Inner     {         void InnerFoo() { }     } } 

    In this example, the Assert will propagate to OuterFoo , but not to InnerFoo . To work around this, the attribute needs to be manually propagated:

     [EnvironmentPermission(SecurityAction.Assert, Unrestricted=true)] class Outer {     void OuterFoo() { }     [EnvironmentPermission(SecurityAction.Assert, Unrestricted=true)]     class Inner     {         void InnerFoo() { }     } } 

Demands of any sort (declarative or imperative) should not be relied on when applied to a static class constructor. Such one-time constructor calls are initiated automatically by the runtime, and the state of the stack at the time can be non-deterministic (or, at least, hard to predict). It's even worse for the declarative LinkDemand operation (we'll look at this in detail in the "Using Declarative LinkDemand" section later in this chapter, but part of the semantics of its operation is that the LinkDemand is evaluated at the point the caller is compiled from IL to native code). Take a look at the following code:

 using System; using System.Security.Permissions; class Test {     public static void Main(String[] args)     {         Console.WriteLine("In Main");     }     [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode=true)]     static Test()     {         Console.WriteLine("In static class constructor");     } } 

Even when run from a network share (default security policy will deny SecurityPermission.UnmanagedCode to such assemblies), the code will execute successfully (both messages will be output). This is because the LinkDemand is ignored completely; there is no managed caller of the static class initializer at all ”it is called implicitly by the runtime, and is compiled to initiate the demand. While the results of performing a normal Demand are somewhat less dramatic, it is still unwise to rely on the state of the stack during such operations.

Because static class constructors are run only once, and logically this is done on behalf of all code that will ever use the class, it is doubtful that any meaningful demands could be made anyway. Any malicious assembly could simply wait until another, more trusted assembly accesses the class and then proceeds unhindered.

There are a couple of security actions available declaratively that are not available imperatively. These are InheritanceDemand and LinkDemand , and the following sections will look at them in more detail.

Using Declarative InheritanceDemand

As we've previously mentioned in passing, InheritanceDemand is the one declarative action whose semantics differ when used at the class versus method level.

When applied to a class, InheritanceDemand controls subclassing of that class. That is, an assembly attempting to derive from the decorated class must be granted the permissions specified by the attribute. If assembly A has the following declaration:

 [EnvironmentPermission(SecurityAction.InheritanceDemand, Unrestricted=true)] public class Base { } 

And assembly B has:

 class Derived : Base { } 

Then assembly B would have unrestricted EnvironmentPermission demanded of it. The check is performed once, at the point when class Derived is first used in any way (via a method call, field access, reflection, and so on). If the check fails, a SecurityException will be thrown.

The effect of an InheritanceDemand is transitive, that is, all subclassers of the attributed class will be checked, regardless of whether the subclassing was direct or indirect. So if assembly C contains the following code,

 class MoreDerived : Derived { } 

then C is also subject to the check for EnvironmentPermission .

The second form of InheritanceDemand occurs when the attribute is applied to a virtual method (it has no meaning when applied to a non-virtual method).

 [MyPermission(SecurityAction.InheritanceDemand, Unrestricted=true)] public virtual void SetPassword(String password) {     m_password = password; } 

Here, the attribute controls the overriding of the SetPassword method. Only assemblies granted unrestricted MyPermission will be allowed to override the implementation of SetPassword . Like an InheritanceDemand applied to a class, this check is performed once at the point at which the class containing the overriding definition of SetPassword is first used.

InheritanceDemand is also applied transitively when used on a method. So an overriding implementation of SetPassword that's defined in any subclass, direct or indirect, will trigger the check.

InheritanceDemand , in both its forms, is useful when it's impractical to seal a class (or mark a method as final) ”perhaps because your own internal code needs the flexibility of subclassing and/or overriding.

Using Declarative LinkDemand

LinkDemand can be seen as a special form of Demand . It performs the same basic operation (checking the given permissions against the grant set of the caller) but with two important differences:

  • Only the immediate caller is checked, not the entire stack of callers. So given three assemblies, A , B , and C , defining methods Foo , Bar , and Baz respectively, if Foo calls Bar calls Baz and Baz is decorated with a LinkDemand , only assembly B (containing method Bar ) will be checked for the permissions demanded. Under a full Demand , assembly A would be checked as well.

  • The check is made only once per call site of the decorated method, not every single time the method is invoked. By call site, we mean the position in the IL of a calling method where the actual invocation of the protected method takes place (this will be a call or callvirt instruction). The check actually takes place during the JIT (Just In Time compilation) of the calling method. Consequently, given the following definitions:

     [MyPermission(SecurityAction.LinkDemand, Unrestricted=true)] public String Foo() {     return "Hello World!"; } public String Bar() {     return Foo() + Foo(); } 

    When the method Bar is compiled (this is commonly referred to as jitting within the .NET Framework), the check for MyPermission will be performed twice for Bar 's assembly ”once for each invocation of Foo . The jitting of Bar will probably occur on the first invocation of the method, but further invocations will not cause any additional checks (because there's no need to compile the method more than once).

One important aspect of the implementation of LinkDemand checks in .NET Framework Version 1 is that a failing check will cause an immediate security exception. Ideally, the JIT would detect the failure and generate code to throw the same exception at the time the method (and, more specifically, the point in that method) is invoked. The current implementation means that you cannot call a method that might possibly call another method it does not have access to, regardless of whether the code would have really attempted the second call. Additionally, the JIT often compiles methods well ahead of their actual use for the purpose of inlining. This has a noticeable effect in a couple of cases:

  • The stack trace in the security exception may stop short of the real location of the failure (because we haven't actually called the method decorated with the LinkDemand yet, and the JIT may perform multiple levels of inlining in advance of calls).

  • Exception handlers that attempt to catch any resulting security exceptions may not work, for the same reason: We may not have entered the try section of code prior to a call to the decorated method.

Take the following example:

 using System; using System.Security; using System.Security.Permissions; class LinkDemandTest {     public static void Main(String[] args)     {         try         {             ProtectedMethod();         }         catch (SecurityException)         {             Console.WriteLine("Caught security exception");         }     }     [SecurityPermission(SecurityAction.LinkDemand, Unrestricted=true)]     static void ProtectedMethod()     {     } } 

When run in a restricted environment (such as from a network share), an unhandled exception message will be displayed on the console (that is, our handler is never invoked).

It's rare that such behavior will impact your code to any degree (usually code is written expecting to be granted a certain level of permissions, and failure to get them is catastrophic, not something to be worked around via the use of exception handling). If you want to ensure that deterministic evaluation of LinkDemands are made, use an intermediate method to make the dangerous call, and force the JIT to suspend inlining for that method using the System.Runtime.CompilerServices.MethodImpl custom attribute:

 using System; using System.Security; using System.Security.Permissions; using System.Runtime.CompilerServices; class LinkDemandTest {     public static void Main(String[] args)     {         try         {             CallProtectedMethod();         }         catch (SecurityException)         {             Console.WriteLine("Caught security exception");         }     }     [MethodImpl(MethodImplOptions.NoInlining)]     static void CallProtectedMethod()     {         ProtectedMethod();     }     [SecurityPermission(SecurityAction.LinkDemand, Unrestricted=true)]     static void ProtectedMethod()     {     } } 

The demand is evaluated at the JIT of the caller rather than the callee for two reasons. First, it would be easy to circumvent LinkDemand in the alternate scenario; simply wait for trusted code to invoke the target method (and thus, JIT the method and trigger the demand) and then invoke the method at will without ever having had the demand directed at your assembly. Second, at the time we JIT the caller, the runtime has precise information about the identity of the calling and callee methods (and thus their respective assemblies). This implies that we have no need to walk the stack to determine the caller, which saves a lot of time.

This brings us to a particularly important point ”the dangers of using LinkDemand . LinkDemand is tempting because of its performance characteristics; it's evaluated infrequently relative to the number of times the decorated method is called, effectively amortizing the cost of the check. Even when the check is performed, it does not involve a costly stack walk. But this comes with a subtle cost; LinkDemand is not always intuitive in its operation and inadvertent security holes can result.

The two main points to remember are

  • Because only the immediate caller is checked, it is easy to wrap the functionality of a protected method and re-export it to potentially untrusted callers. This allows such callers to side-step the security by using your assembly as a trusted proxy. The following code is an example of this sort of security hole:

     [MyPermission(SecurityAction.LinkDemand, Unrestricted=true)] public String Foo(String[] args) {  ... } public String FooWrapper(String[] args) {      return Foo(args); } 

    There are two cases to bear in mind here. You must document clearly when your public methods are decorated with LinkDemand rather than Demand (so that others do not inadvertently open up access to your methods), and you must pay special attention to which methods you call using a LinkDemand (to avoid creating such wrappers yourself).

    If it becomes necessary to build such a wrapper, one method of maintaining security is to simply add the LinkDemand to the wrapper itself:

     [MyPermission(SecurityAction.LinkDemand, Unrestricted=true)] public String Foo(String[] args) {  ... } [MyPermission(SecurityAction.LinkDemand, Unrestricted=true)] public String FooWrapper(String[] args) {      return Foo(args); } 

    In the absence of good documentation for assemblies and methods you rely on, the PermView utility supplied with the .NET Framework SDK, along with the /decl command line option, will allow the viewing of all declarative security present in the target assembly. Compile the following code into an assembly called PropertiesAndEvents.exe :

     using System; using System.Security.Permissions; class Hello {     String m_datum;     String MyProperty     {         [EnvironmentPermission(SecurityAction.Demand, Unrestricted=true)]         get {  return m_datum; }         [RegistryPermission(SecurityAction.Demand, Unrestricted=true)]         set {  m_datum = value; }     }     public delegate void EventHandler();     [method: FileIOPermission(SecurityAction.Demand, Unrestricted=true)]     public event EventHandler MyEvent;     public static void Main(String[] args)     {     } } 

    The PermView utility can then be used as follows:

     PermView /decl PropertiesAndEvents.exe Microsoft (R) .NET Framework Permission Request Viewer. Version 1.0.3612.0 Copyright (C) Microsoft Corporation 1998-2001. All rights reserved. Method Hello::get_MyProperty() Demand permission set: <PermissionSet class="System.Security.PermissionSet"                version="1">    <IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, graphics/ccc.gif Version=1.0.3300.0, Culture=neutral, PublicKeyToken= b77a5c561934e089"                 version="1"                 Unrestricted="true"/> </PermissionSet> Method Hello::set_MyProperty() Demand permission set: <PermissionSet class="System.Security.PermissionSet"                version="1">    <IPermission class="System.Security.Permissions.RegistryPermission, mscorlib, graphics/ccc.gif Version=1.0.3300.0, Culture=neutral, PublicKeyToken= b77a5c561934e089"                 version="1"                 Unrestricted="true"/> </PermissionSet> Method Hello::add_MyEvent() Demand permission set: <PermissionSet class="System.Security.PermissionSet"                version="1">    <IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=1. graphics/ccc.gif 0.3300.0, Culture=neutral, PublicKeyToken= b77a5c561934e089"                 version="1"                 Unrestricted="true"/> </PermissionSet> Method Hello::remove_MyEvent() Demand permission set: <PermissionSet class="System.Security.PermissionSet"                version="1">    <IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=1. graphics/ccc.gif 0.3300.0, Culture=neutral, PublicKeyToken= b77a5c561934e089"                 version="1"                 Unrestricted="true"/> </PermissionSet> 
  • At the JIT time of the caller, information about the callee is sometimes not as precise as we'd like it to be. This is true for all virtual calls (which include calls through interfaces or on abstract classes/methods). In these cases, the runtime will know which slot in the vtable or interface table the final method will reside in, which is enough to compile the calling code, but the actual choice of target method is deferred until runtime (and may indeed vary on every call). For this reason, it is very hazardous to place a LinkDemand on a virtual (or override) method or a method implementing an interface or an abstract method.

    In the following example, the first call invokes the LinkDemand as expected; the second does not.

     public interface IFoo {     String Foo(String[] args); } public class MyClass : IFoo {     [MyPermission(SecurityAction.LinkDemand, Unrestricted=true)]     public String Foo(String[] args) {  ... } } public class Test {     public static void Main(String[] args)     {         MyClass mc = new MyClass();         // Call method on class directly.         mc.Foo(args);         // Call method through interface.         ((IFoo)mc).Foo(args);     } } 

    One workaround to this problem is to attach the same LinkDemand to all possible routes to the method. For example, in the previous example, we could add the LinkDemand to the method declaration in IFoo :

     public interface IFoo {     [MyPermission(SecurityAction.LinkDemand, Unrestricted=true)]     String Foo(String[] args); } 

    Likewise, LinkDemands can be propagated from abstract methods to their implementations and virtuals to their overrides . But such schemes can be complex, error prone, and may not give satisfactory semantics (for example, it is rarely acceptable ”or even possible ”to propagate a LinkDemand from a method implementing an interface to the interface definition itself). In such cases, it may be time to think about using a Demand instead. This will incur a performance penalty, but will be evaluated at runtime, when there is no ambiguity as to which method is being called.

Link demands on methods that are being invoked via reflection (for example, Type.InvokeMember or MethodInfo.Invoke ) must have the demands emulated by the reflection subsystem. That's because at the JIT time of the caller, the runtime has no idea which method is going to be invoked (that's the entire point of late binding in the first place). In Version 1 of the .NET Framework, the emulation is not precise; the permission set demanded is checked against the grant set of all assemblies on the stack rather than just the assembly of the method that initiated the reflection call. This behavior should not be relied on; in future releases, the semantics of link demands, whether invocation is late or early bound, are likely to converge.

Building Declarative Actions Against Permission Sets

It is occasionally necessary to specify multiple permissions to which a single declarative action will apply on a given method or class. This can be achieved via multiple single permission attributes as follows:

 [RegistryPermission(SecurityAction.Demand, Unrestricted=true),  FileIOPermission(SecurityAction.Demand, Unrestricted=true)] public void Foo() 

However, the runtime also presents another method of achieving the same result. PermissionSetAttribute (as usual, this can be abbreviated in C# to PermissionSet ) will allow the specification of an entire permission set in one attribute declaration. There are four ways the set can be specified:

  • If the Unrestricted property is set to true, the unrestricted permission set is created. You'll recall that this is the set of all permissions that implement the IUnrestrictedPermission interface (that is, most non-identity permissions).

     [PermissionSet(SecurityAction.Demand, Unrestricted=true)] 
  • The XML property can be set to a string containing the XML serialized encoding of the permission set. This is the same encoding that can be seen by calling ToString on a permission set instance. Remember to escape any characters your compiler treats specially (such as quote or backslash).

     [PermissionSet(SecurityAction.Demand,                XML="<PermissionSet class=\"System.Security.PermissionSet\" graphics/ccc.gif version=\"1\"><IPermission class=\"System.Security.Permissions. FileIOPermission, graphics/ccc.gif mscorlib, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" graphics/ccc.gif version=\"1\ " Unrestricted=\ "true\ "/> <IPermission class=\ "System.Security. graphics/ccc.gif Permissions.RegistryPermission, mscorlib, Version=1.0.3300.0, Culture=neutral, graphics/ccc.gif PublicKeyToken= b77a5c561934e089\ " version=\ "1\ " Unrestricted=\ "true\ "/> </ graphics/ccc.gif PermissionSet>")] 
  • The File property can be set to the name of a file containing the same kind of XML encoding described for the XML property. The file contents may be encoded in ASCII, UTF8, or Unicode; the runtime will automatically detect which format is used.

     [PermissionSet(SecurityAction.Demand, File="c:\ \ PermSet.xml")] 
  • The Name property can be set to the name of a well known permission set (one of the built-in ones, such as Nothing, Everything, or LocalIntranet, or any named permission set configured into security policy).

     [PermissionSet(SecurityAction.Demand, Name="LocalIntranet")] 
Using Assembly Level Declarative Security

Until now, you've looked at declarative security applied to classes and methods. It is also possible to apply security attributes at the assembly level. However, the set of actions applicable at this level is entirely different from the actions discussed so far. That is, you cannot demand or assert permissions for every method over an entire assembly (in practice, it is unlikely this would be a useful operation anyway).

Rather, there is a set of actions specific to the assembly level. These all provide extra data for policy resolution (the mechanism whereby the .NET Framework security system grants permissions to an assembly). Because assemblies cannot be trusted to assign their own trust level, these requests can at most reduce the set of permissions that policy would grant. This can be useful for testing, self-documenting trust requirements, and limiting the impact of security bugs in the assembly code.

There are three such security actions:

  • RequestMinimum ” This specifies the set of permissions the assembly considers essential for its operation. If the policy system is unwilling to grant all of these, the assembly is not loaded at all. On a failure to load the assembly for this reason, a System.Security.Policy.PolicyException will be thrown. The default is to require no permissions at all (that is, the PolicyException will never be thrown).

  • RequestOptional ” These are permissions the assembly would like, but are not essential for operation. This allows for the possibility of code that attempts an operation one way (say, storing data in a file) and then detects any security violation by catching the security exception and backs off to an alternative solution requiring less trust (such as storing the data as in-memory arrays). The default is to request unrestricted permissions ”that is, anything the policy system will give the assembly is accepted. In actual fact, the policy system will ask only for the permissions in this set (together with any permissions in the RequestMinimum set). This attribute can be used to limit the grant set of an assembly to a specific set of permissions (if that set is required rather than optional, RequestMinimum with the set and then RequestOptional with the empty set):

     [assembly: FileIOPermission(SecurityAction.RequestMinimum, Unrestricted=true)  PermissionSet(SecurityAction.RequestOptional, Name="Nothing")] 
  • RequestRefuse ” This allows you to list permissions that should specifically not be granted to the assembly. This is sometimes easier than explicitly listing the permissions that are desired.

So let's take an example. Say the assembly you've just written absolutely needs to access the registry and environment variables, would like to access files but can live without it, and wants to make sure it can never expose any abuses of unmanaged code. The following attribute should fit the bill (like any assembly level attribute, this should be placed near the top of one of your source files, after any using declarations):

 [assembly:  RegistryPermission(SecurityAction.RequestMinimal, Unrestricted=true),  EnvironmentPermission(SecurityAction.RequestMinimal, Unrestricted=true),  FileIOPermission(SecurityAction.RequestOptional, Unrestricted=true),  SecurityPermission(SecurityAction.RequestRefuse, UnmanagedCode=true)] 
Implicit Declarative Demands

There are two situations in which declarative demands are implicitly added to a method by the runtime ”P/Invoke and interop methods. P/Invoke (Platform Invoke) methods are calls from managed code into unmanaged code. They must be declared on the managed side so that the runtime knows how to marshal (convert) the types of the arguments and result back and forth between the two worlds . Interop methods are calls on classic COM objects (sometimes called servers) and thus represent a more structured means of calling into unmanaged code. COM interop can also be used to call from the unmanaged world into the .NET Framework runtime, but we're not concerned with method calls going in that direction in this section (primarily because unmanaged callers are usually considered to be fully trusted).

Take the following example of a P/Invoke declaration:

 [DllImport("Kernel32.dll")]     public static extern int GetTickCount(); 

Any time the GetTickCount method is called, a demand for SecurityPermission.UnmanagedCode is made. This is because any use of unmanaged code is potentially a security hole (unmanaged code is outside the jurisdiction of the runtime, so can potentially subvert it). Because the runtime cannot determine which unmanaged APIs are "safe," it demands this permission for all of them.

Interop calls from managed code to an unmanaged COM server are the other route into unmanaged code, and these methods are similarly protected via a demand for SecurityPermission.UnmanagedCode . Both the creation of the server instance object and any method calls against that instance are protected. In the following code, two demands are made ( assuming ComServer is an unmanaged COM class):

 ComServer server = new ComServer(); server.DoStuff(); 

By default, the implicit demands described here are per-call, full-stack walking demands. This is nice and secure (it will catch cases where code unthinkingly wraps a public managed method around a dangerous unmanaged method, such as CreateFile ). But there is certainly a high performance penalty here. This may not be an issue depending on the nature of your application and the frequency with which calls to unmanaged methods are made.

If performance does turn out to be critical, there exists a technique whereby the full demand can be mutated into a link demand. Because this is checked less frequently and does not involve a stack walk, the average performance overhead will approach zero as the method is repeatedly called. This does mean, however, that special care must be taken when calling such methods to ensure that your code cannot be coerced into performing unmanaged operations on behalf of an untrusted caller.

The mechanism that allows the demands to be modified into link demands is a custom attribute ” System.Security . SuppressUnmanagedCodeSecurityAttribute . For P/Invokes, the attribute can be added to either the specific P/Invoke method or the class in which the P/Invoke is defined (in which case it applies to every P/Invoke method in that class):

 [DllImport("Kernel32.dll"), SuppressUnmanagedCodeSecurity]     public static extern int GetTickCount(); 

The story is slightly different for interop calls. Here, the switch can only be made at the interface level (all interop calls are dispatched through interfaces, because the method implementation is in unmanaged code). SuppressUnmanagedCodeSecurity can be applied to the interface definition to make all calls through that interface use a link demand. If the attribute is applied to an interface method, it will be ignored.

 [SuppressUnmanagedCodeSecurity] public interface IComServer {     ... } 

A non-obvious side effect of applying SuppressUnmanagedCodeSecurity to an interface is that the runtime cannot tell at JIT time whether the interface implementation will really be managed or unmanaged. Therefore, the link demand for SecurityPermission.UnmanagedCode will be evaluated regardless. This is not an issue with the default full stack walk, because the evaluation of the demand is made at runtime and the implementation is known at that point. In the following example, both calls to Foo will result in a link time demand at JIT time:

 [SuppressUnmanagedCodeSecurity] public interface IFoo {     void Foo(); } class Test {     IFoo obj1 = new UnmanagedFoo();     IFoo obj2 = new ManagedFoo();     obj1.Foo();     obj2.Foo(); } 

If the interface used in interop calls has been generated via the tlbimp utility, the suppress attribute can be automatically added by specifying the /unsafe switch to the tlbimp command line:

 tlbimp /unsafe ComServer.tlb 

The SuppressUnmanagedCodeSecurity attribute will only modify the behavior of implicit demands made for P/Invoke and interop calls; no other declarative security is affected.

Allowing Untrusted Callers

As has been discussed previously, one of the principal problems when trying to secure code is managing interactions with code of unknown or lower trust levels. To try and reduce the probability of bugs in an assembly's code leading to such security holes, the runtime adds additional security demands to certain methods if the following conditions all hold:

  • The defining assembly is strong named (these are considered to be critical assemblies because strong naming is a prerequisite of installation into the Fusion Global Assembly Cache (GAC), and security policy may well base trust decisions on strong names ).

  • The assembly is not marked with the System.Security . AllowPartiallyTrustedCallersAttribute custom attribute. This attribute should only be used when it has been determined that all code in the assembly has been thoroughly reviewed for security holes in the face of attacks from malicious untrusted callers. The syntax of the attribute (in C#) is as follows:

     [assembly: AllowPartiallyTrustedCallers] 
  • The method in question is publicly visible (the containing class is public and the method itself is public, protected, and so on). Only publicly visible methods have implicit security checks added because they're the only available entry point for callers from another assembly.

If all the previous conditions are satisfied, the runtime adds a link demand for the unrestricted permission set (that is, all permissions that implement IUnrestrictedPermission , sometimes referred to as full trust). This link demand is special in that if at JIT time the runtime can determine that the caller is actually from the same assembly as the callee, the link demand will be skipped . This ensures that the assembly can't be called from untrusted assemblies (anything below full trust), but that intra-assembly calls will always succeed, even if the assembly itself isn't granted full trust.

Consequently, given the following example (assuming the assembly is strong named and does not possess the AllowPartiallyTrustedCallers attribute)

 public class PublicClass {     public void PublicMethod() { }     private void PrivateMethod() { } } class PrivateClass {     public void PublicMethod() { }     private void PrivateMethod() { } } 

only PublicClass.PublicMethod will be decorated with the implicit link demand for full trust. If this method is called from anywhere inside the same assembly, the link demand will be ignored.

A more precise check would be to compare the trust level of the caller to the callee and fail only when the caller is less trusted. However, this is an expensive operation (involving many set operations on the respective grant sets). Therefore, the link demand for full trust is seen as a compromise between efficiency, flexibility, and safety.

Note that, due to these simplified rules, a partially trusted, signed assembly (one downloaded from the Internet via Internet Explorer, for example) cannot have callers from partial trust assemblies. This is particularly likely to cause problems if a single application is written as a set of assemblies with intra-assembly communication via method calls. The only resort in these cases is to review the assemblies for security correctness and add the AllowPartiallyTrustedCallers attribute to each assembly. This may not be all that difficult, given that assemblies designed to operate in partial trust scenarios are unlikely to manipulate security sensitive resources heavily.

As previously mentioned, link demands on methods that are invoked via late bound mechanisms (reflection) will be converted into full stack walking demands (in Version 1 of the .NET Framework, at least). This applies to the implicit link demands described in this section as well.

An Assert can be used to simulate the correct link demand behavior in these cases:

 public void CallSomeMethod() {     // Assume that we've already located the method to call prior to     // now, and the target MethodInfo has been placed in a static     // variable. We believe the method is safe to call, but resides     // in an assembly that does not allow untrusted callers. We     // presume this assembly will be granted full trust, but our     // callers may not. Since we're guaranteeing the safety of this     // operation, it seems unfair to require full trust of our callers.     // Therefore we'll assert full trust prior to making the call to     // the target method. This will halt the stack walk initiated by     // the reflection system.     new PermissionSet(PermissionState.Unrestricted).Assert();     s_targetMethod.Invoke(null, new Object[]); } 

Identity Demands and Their Uses

One particularly useful form of a security demand is a demand for an identity permission. You'll recall that identity permissions are those granted to an assembly based on the evidence used to establish trust in the policy database. For each piece of evidence used (download URL, strong name, Authenticode signature, and so on), a corresponding identity permission is granted.

It's not usually all that useful to perform a full stack walking demand for an identity permission (there are limited scenarios where you'd expect to see the same identity all the way down the stack). But it's certainly possible, as the following code snippet shows:

 try {     new ZoneIdentityPermission(SecurityZone.MyComputer).Demand();     Console.WriteLine("All assemblies located in the MyComputer zone"); } catch (SecurityException) {     Console.WriteLine("Assembly from different zone found"); } 

Where identity demands really become useful is when using declarative link demands. This is because only the immediate caller is checked (and, as an additional benefit, the cost of the check is low and amortized toward zero the more times the method is called).

One particularly useful example of this technique is to perform a link demand for a strong name identity permission on a public method. This gives the ability to export methods that are only callable from your other assemblies ”something akin to a version of internal that crosses assembly boundaries.

The following is an example of a method that can only be called by an assembly with the given strong name:

 [StrongNameIdentityPermission(SecurityAction.LinkDemand,                               PublicKey="0x00000000000000000400000000000000")] public void MyInternalMethod() 

The PublicKey property contains the full public key encoded in hex format. The ECMA public key has been used in the previous code for brevity; most public keys are a lot longer. Use the secutil utility to determine the hex string format of a public key for a given assembly:

 secutil -hex -s mscorlib.dll 

The strong name identity demand can be made more specific with name and/or version information:

 [StrongNameIdentityPermission(SecurityAction.LinkDemand,                               PublicKey="0x00000000000000000400000000000000"),                               Name="MyOtherAssembly",                               Version="1.0.0.0"] public void MyInternalMethod() 

Remember that when using such specific identities, the assembly declaring the method may be unable to call the method itself. This can be worked around using an internal method (used within the assembly itself) and a public wrapper with the link demand (to be used between assemblies):

 [StrongNameIdentityPermission(SecurityAction.LinkDemand,                               PublicKey="0x00000000000000000400000000000000"),                               Name="MyOtherAssembly"] public void MyInternalMethod() {     MyInternalMethodImplementation(); } internal void MyInternalMethodImplementation() {     ... } 

Note that this technique has the limitation that only one identity can be checked (if you specify two identity demands for a given method, both demands must be satisfied for access to succeed, which is unlikely to be the semantic you're after). This is one reason for omitting the name and version information in a strong name identity demand; the strong name will usually remain the same throughout a given product, without being available to code from another company.

If a set of specific assemblies must be identified, one possible workaround is to use a wrapper technique similar to that described earlier in this section, but with separate wrappers for each individual calling assembly.

for RuBoard


. NET Framework Security
.NET Framework Security
ISBN: 067232184X
EAN: 2147483647
Year: 2000
Pages: 235

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