.NET Framework Security

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:

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:

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:

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

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:

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:

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:

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

Категории