programming4us
programming4us
SECURITY

Programmatic Security (part 2) - Stack-Walk Modifiers

- How To Install Windows Server 2012 On VirtualBox
- How To Bypass Torrent Connection Blocking By Your ISP
- How To Install Actual Facebook App On Kindle Fire
1/11/2011 11:40:26 AM

2. Stack-Walk Modifiers

An object can modify the behavior of the stack walk as it passes through it. Each permission class implements the IStackWalk interface, defined as:

    public interface IStackWalk
{
void Assert( );
void Demand( );
void Deny( );
void PermitOnly( );
}

The Demand( ) method of IStackWalk triggers a stack walk, and the permission classes channel the implementation to that of IPermission.Demand( ). The other three methods, Assert( ), Deny( ), and PermitOnly( ), each install a stack-walk modifier— an instruction modifying the behavior of the walk. At any given stack frame (i.e., the scope of a method), there can be only a single stack-walk modifier. Trying to install a second modifier results in an exception of type SecurityException. The stack modifier removes itself automatically once the method returns or when a call to a static reversion method of CodeAccessPermission is called, such as RevertDeny( ), RevertAssert( ), RevertPermitOnly( ), or RevertAll( ). Stack-walk modifiers are very useful for optimizing and tightening the current security policy, serving as a programmatic override to the configuration set by the administrator.

2.1. Denying and permitting permissions

In general, a component vendor can't assume that its components will always be deployed in a properly configured and secure environment. Sometimes you should be able to override the administrative security policy and programmatically enforce stricter policies—for example, when the global security policy is turned off, too liberal for your sensitive needs, or simply unknown. Other cases may involve dealing with components from questionable origin. Calling IStackWalk.Deny( ) denies the permission represented by the underlying permission class and aborts the stack walk, even if the global security policy configuration grants the calling assembly that permission. Example 3 demonstrates denying write permission to all the drives on the machine before invoking a method on a questionable component.

Example 3. Explicitly denying permissions
public void SomeMethod(  )
{
string[] drives = Environment.GetLogicalDrives( );
 
IStackWalk stackWalker;
stackWalker = new FileIOPermission(FileIOPermissionAccess.Write,drives);
 
stackWalker.Deny( );
 
QuestionableComponent obj = new QuestionableComponent( );
obj.DoSomething( );
 
CodeAccessPermission.RevertDeny( );
 
/* Do more work */
}

In this example, SomeMethod( ) constructs a new FileIOPermission object, targeting all the drives on the machine. Using the IStackWalk interface, SomeMethod( ) denies all write access to these drives. Any attempt by the questionable component to write to these drives results in an exception of type SecurityException. The permission denial remains in effect until the method that called Deny( ) returns. If you want to revert the denial in the scope of the calling method (for example, to do some file I/O yourself), you need to call the static method RevertDeny( ) of the CodeAccessPermission class, as shown in Example 3.

Sometimes it's simpler to just list what you permit, using the PermitOnly( ) method of IStackWalk. When a stack walk reaches a method that called PermitOnly( ), only that permission is presented, even if other permissions are granted administratively. Example 12-4 demonstrates permitting access only to the C:\temp directory, but nothing else.

Example 4. Permitting a particular permission only
public void SomeMethod(  )
{
string path = @"C:\temp";
IStackWalk stackWalker;
stackWalker = new FileIOPermission(FileIOPermissionAccess.AllAccess,path);
 
stackWalker.PermitOnly( );
 
QuestionableComponent obj = new QuestionableComponent( );
obj.DoSomething( );
 
CodeAccessPermission.RevertPermitOnly( );
 
/* Do more work */
}

Like Deny( ), PermitOnly( ) remains in effect until the calling method returns or until the method calls the static method RevertPermitOnly( ) of CodeAccessPermission.

When using a delegate to fire an event at unknown subscribers you can explicitly deny or permit only some permissions, which reduces the risk of calling a delegate that may have lured you into calling it.


2.2. Asserting permissions

A stack walk to verify security permissions is a powerful and elegant idea, but it doesn't come without a cost. A stack walk is expensive, and in intense calling patterns or when the call stack is long, it results in a performance and throughput penalty. Consider the following code:

    void SaveString(string text,string fileName)
{
StreamWriter stream = new StreamWriter(fileName,true);//append text
using(stream)
{
stream.WriteLine(text);
}
}
 
string[] array = new string[9];
 
array[0] = "Every";
array[1] = "string";
array[2] = "in";
array[3] = "this";
array[4] = "array";
array[5] = "causes";
array[6] = "a";
array[7] = "stack";
array[8] = "walk";
 
string fileName = @"C:\Temp\MyStrings.txt";
 
foreach(string item in array)
{
SaveString(item,fileName);
}


Every time the code writes to the file, it triggers a walk all the way up the stack. After the first stack walk, all the subsequent stack walks are redundant, because the call chain doesn't change between loop iterations. To efficiently handle such cases, use the Assert( ) method of IStackWalk:
    string fileName = @"C:\Temp\MyStrings.txt";
 
IStackWalk stackWalker;
stackWalker = new FileIOPermission(FileIOPermissionAccess.Write,fileName);
stackWalker.Assert( );
 
foreach(string itemin array)
{
SaveString(item,fileName);
}
CodeAccessPermission.RevertAssert( );


When asserting security permission, stack walks demanding it stop in the current stack frame and don't proceed up. The assertion remains in effect until the method returns or until the static method RevertAssert( ) is called. Note that you can assert a single permission only in the same method scope (unless RevertAssert( ) or RevertAll( ) is called).

Because the stack walk stops at the level that asserts the permission, it's quite possible that callers further up the call chain that initiated the call don't have the permission to do the operations carried out by the downstream objects. It looks as if the ability to assert permissions may look like a technique that circumvents .NET's code access security policy—that is, a malicious assembly can assert whatever security permission it wants and then start roaming on the machine. Fortunately, there are two safeguards. First, if the asserting assembly isn't granted the permission it tries to assert, the assertion has no effect. When code down the call chain demands the asserted permission, it triggers a stack walk. When the stack walk reaches the assertion stack-walk modifier, it also verifies that the asserting assembly has that permission. If it doesn't, the stack walk is aborted, and the call demanding permission throws a security exception. The second safeguard is that not all code can assert permissions. Only code granted the Security permission, with the right to assert permissions, can call Assert( ). If the permission to assert isn't granted, a security exception is thrown on the assertion attempt. .NET doesn't use a stack walk to verify that permission to assert is granted. Instead, it verifies that the asserting assembly has that permission at link time.

Only the most trustworthy code should be granted the right to assert, because of the level of risk involved in not completing stack walks.


Another important point regarding permission assertion is that it doesn't always guarantee stopping the stack walk, because the asserted permission may be only a subset of the permission demand that triggered the stack walk. In that case, the assert instruction only stops the stack walk for its particular type and value. The stack walk may proceed up the call stack looking for other permission grants.

2.3. Asserting unmanaged code access permission

Performance penalties aside, the more significant side effect of the stack walk is that it can prevent code that should run from operating. Nowhere is this more evident than when it comes to unmanaged code access.

A potential security loophole opens when you call outside the managed-code environment using the interoperation (interop) layer. The interop layer allows managed code to invoke calls on COM components, or simply call DLL entry points using the platform-specific invocation mechanism (P-Invoke). Unmanaged code is completely exempt from .NET code access policies because it executes directly against the operating system. Using interop, a malicious managed component can have the unmanaged code do its dirty work on its behalf. Naturally, only the most trustworthy assemblies should be granted unmanaged code permission. Accordingly, the managed side of the interop layer demands that all code accessing it have the Security permission with the right to access unmanaged code.

The problem is, all .NET Framework classes that rely on the underlying services of the operating system require the interop layer. Consider the case of the FileStream class. To call it, code requires only file I/O permission, which is a more liberal and less powerful permission than the unmanaged code access permission. However, because the FileStream class uses P-Invoke to call the Win32 API on behalf of the caller, any attempt to use the FileStream class triggers a demand for unmanaged code access permission by the interop layer. To shield the caller, the FileStream class asserts the unmanaged code access permission, so it doesn't propagate the demand for that permission to its clients. Without the assertion, only the most trusted components could have used FileStream. Because it's signed with the Microsoft public key, the FileStream class is granted the FullTrust permission set, and its assertion of unmanaged code access succeeds. Instead of unmanaged code access demand, the FileStream class demands only file I/O permission.

The next question is, how should you handle your own interop calls to the unmanaged world? For example, consider the following code that uses P-Invoke to import the definition of the MessageBoxA Win32 API call, which displays a message box to the user:

    using System.Runtime.InteropServices;
 
public class MsgBox
{
[DllImport("user32",EntryPoint="MessageBoxA")]
public static extern int Show(HandleRef handle,string text,string caption,
int msgType);
}
//Client side:
HandleRef handle = new HandleRef(null,IntPtr.Zero);
MsgBox.Show(handle,"Called using P-Invoke","Some Caption",0);


Every time the Show( ) method is called, it triggers a demand for unmanaged code access permission. This has the detrimental effect of both a performance penalty and a functionality impasse if the caller doesn't have generic unmanaged code access permission but is trusted to carry out the specific imported unmanaged call. There are a few solutions. The first is to mimic the behavior of the .NET Framework classes and assert the unmanaged code access permission, using a managed wrapping method around the imported unmanaged call. Example 12-5 demonstrates this technique.
Example 5. Asserting unmanaged code access permission around an interop method
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
 
public class MsgBox
{
[DllImport("user32",EntryPoint="MessageBoxA")]
private static extern int Show(HandleRef handle,string text,string caption,
int msgType);
public static void Show(string text,string caption)
{
IStackWalk stackWalker;
stackWalker = new SecurityPermission(SecurityPermissionFlag.UnmanagedCode);
stackWalker.Assert( );
 
HandleRef handle = new HandleRef(null,IntPtr.Zero);
Show(handle,text,caption,0);
}
}
//Client side:
MsgBox.Show("Called using P-Invoke","Some Caption");

To assert the unmanaged code access permission, you assert the SecurityPermission permission, constructed with the SecurityPermissionFlag.UnmanagedCode enum value. The recommended practice, however, is never to assert one permission without demanding another permission in its place, as shown in Example 6.

Example 6. Asserting unmanaged code permission and demanding UI permission
public static void Show(string text,string caption)
{
IStackWalk stackWalker;
stackWalker = new SecurityPermission(SecurityPermissionFlag.UnmanagedCode);
stackWalker.Assert( );
 
IPermission permission;
permission = new UIPermission(UIPermissionWindow.SafeSubWindows);
permission.Demand( );
 
HandleRef handle = new HandleRef(null,IntPtr.Zero);
Show(handle,text,caption,0);
}



Note that the code in Example 6 installs what amounts to a stack-walk filter. As the stack walk makes its way through the stack frame, the filter allows only the user-interface part of the unmanaged code demand to go up the call stack. The assertion converts a generic demand to a more specific demand. This is perfectly safe, because you know that the client is not going to use the Show( ) method for all the things unmanaged code enables—the caller will use it only for user-interface purposes.

The second solution is to suppress the interop layer's demand for unmanaged code access permission altogether. .NET provides a special attribute called SuppressUnmanagedCodeSecurityAttribute, defined in the System.Security namespace:

    [AttributeUsage(AttributeTargets.Class|AttributeTargets.Method|
AttributeTargets.Interface|AttributeTargets.Delegate,
AllowMultiple = true,Inherited = false)]
public sealed class SuppressUnmanagedCodeSecurityAttribute : Attribute
{
public SuppressUnmanagedCodeSecurityAttribute( );
}

You can apply the SuppressUnmanagedCodeSecurity attribute only to an interop method, to a class that contains interop methods, to a delegate used to invoke an interop method, or to an interface that the class implements. It is ignored in all other cases. The following example shows how to apply the attribute to an interop method:

						[SuppressUnmanagedCodeSecurity]
[DllImport("user32",EntryPoint="MessageBoxA")]
public static extern int Show(HandleRef handle,string text,string caption,
int msgType);


The interop layer now will not demand that unmanaged code access permission be granted when the Show( ) method is invoked. The only safeguard is that at runtime, during the link phase to the interop method, .NET will demand unmanaged code access permission from the immediate caller up the stack (you will see how to demand permission at link time later). This allows callers up the call chain without unmanaged code access permission to call other clients with that permission and actually invoke the interop method.

A similar issue arises when importing COM objects to .NET, and you can also suppress demands for unmanaged code access permission by the imported COM objects. The TlbImp command-line utility provides the /unsafe switch:

    tlbimp <COM TLB or DLL name> /out:<interop assembly name> /unsafe

When you use this switch, the Runtime Callable Wrapper (RCW)—the managed code wrapped around the COM object—will only perform link-time demands for the unmanaged code access permission, instead of doing stack walks on every call. Needless to say, you should use SuppressUnmanagedCodeSecurityAttribute and /unsafe with extreme caution, and only when you know that the call chain to the interop method is secure.

Since you should never assert a permission without demanding a different permission instead, in the case of using the /unsafe switch with imported COM objects, you should build a wrapper around the RCW and have it demand the specific required permissions.


The third solution is to use security permission attributes. The next section examines this option.

Other  
 
Top 10
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 2) - Wireframes,Legends
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 1) - Swimlanes
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Formatting and sizing lists
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Adding shapes to lists
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Sizing containers
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 3) - The Other Properties of a Control
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 2) - The Data Properties of a Control
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 1) - The Format Properties of a Control
- Microsoft Access 2010 : Form Properties and Why Should You Use Them - Working with the Properties Window
- Microsoft Visio 2013 : Using the Organization Chart Wizard with new data
Video Sports
programming4us programming4us
programming4us
 
 
programming4us