The code access security classes include several methods . In addition to the Demand method is the Assert method. .NET security checks to make sure that all callers in a method's call stack have permission to perform a desired operation. This prevents callers that do have permission to perform an operation from performing that operation on behalf of a caller that doesn't have permission. For example, if assembly A (which has permission to modify the registry) calls assembly B's registry modification method, assembly B must have permission to modify the registry too.
Walking the stack to make sure callers have permissions is how the .NET security framework behaves unless you invoke the Assert operation. Assert takes the responsibility for the permission on behalf of the callers. Repeating our scenario, then, if assembly A uses an Assert operation on registry modification, assembly A will be able to modify the registry on behalf of assembly B, even though assembly B doesn't have permission to modify the registry. However, if an assembly asserts a permission it hasn't been grantedthat is, if a demand for that permission would failthen the Assert operation fails too. Thus, for example, assembly A cannot assert registry modification permissions on behalf of assembly B if assembly A has not been granted those permissions itself.
Sandboxing Assemblies for Testing Purposes
By default, assemblies created on your computer will fall into the My_Computer zone, resulting in those assemblies having full trust. An assembly with no restrictions is difficult to test against security actions. To reconcile testing security actions for a likely deployment zone or specific security restrictions, we can sandbox the assembly into a zone with greater restrictions or a zone that implements a custom permission set.
To demonstrate the Assert security action, we will sandbox AssertExample.exe into the Internet_Zone. By default the Internet_Zone is the most restricted zone, and it excludes registry permissions. Follow the steps below to associate the policy for Internet assemblies with the AssertExample.exe assembly.
After completing each step successfully, you should see a warning bubble (Figure 18.5) that indicates that AssertExample.exe is running in a partially trusted context, which might disable some functionality. (You have seen this message before if you downloaded and ran the Wahoo! game in Chapter 10.)
Figure 18.5. The message that appears when an assembly is running in a partially trusted context and security restrictions apply.
Now we are ready to test the Assert security action. Suppose that every other assembly on the computer, except AssertExample.exe , is running with full trust. This means that AssertExample.exe will not be permitted to interact with the registry, for example, but other assemblies created on this computer will be able to do so. (Keep in mind that if the Internet permission set on your computer has been modified to permit registry access, you need to create or use a permission set that doesn't permit registry access for this example to work correctly.)
Demonstrating the Behavior of an Assert Action
If you downloaded an assembly from the Internet and this assembly attempted to interact with your computer's registry, a security exception would be raised. In the preceding subsection we sandboxed AssertExample.exe into the Internet_Zone to simulate this behavior. As a result, AssertExample.exe will throw a security exception when it tries to access the registry directly. A second class library project available for download, AssertPermission.dll , contains a method that accesses the registry. If AsssertExample.exe calls into AssertPermission.dll , which in turn attempts to access the registry, a security exception will be raised. The exception occurs because the CLR will perform a stack walk and determine that AssertExample.exe does not have registry permissions. However, as you will see in the example, if AssertPermission.dll uses an Assert operation on registry permissions, the stack walk won't get as far as checking the AssertExample.exe for registry permissions, and registry access will be permitted.
Listing 18.4 contains the code for AssertExample.exe . The example is a Windows Forms application with a single button. The Button.Click event tries to demand registry permissions and read from the registry. This call fails for the reason described above. In the same Click event handler, a second call is made into AssertPermission.dll . This call succeeds, again as explained above.
Listing 18.4 The Code for AssertExample.exe
1: Imports System.Security.Permissions 2: Imports System.Threading.Thread 3: Imports System.Security.Principal 4: Imports Microsoft.Win32 5: 6: 7: Public Class Form1 8: Inherits System.Windows.Forms.Form 9: 10: [ Windows Form Designer generated code ] 11: 12: Private Sub Button1_Click(ByVal sender As System.Object, _ 13: ByVal e As System.EventArgs) Handles Button1.Click 14: 15: Try 16: ReadRegistry() 17: Catch 18: MessageBox.Show("Code does not have registry read permission") 19: End Try 20: 21: Try 22: AssertPermission.AssertsForme.ReadRegistry() 23: Catch 24: MessageBox.Show("Code does not have registry read permission") 25: End Try 26: 27: End Sub 28: 29: Private Sub ReadRegistry() 30: Dim Permission As RegistryPermission = _ 31: New RegistryPermission(RegistryPermissionAccess.Read, _ 32: "*") 33: 34: Permission.Demand() 35: 36: Dim Key As RegistryKey = _ 37: Registry.CurrentUser.OpenSubKey("Volatile Environment") 38: Try 39: MessageBox.Show(Key.GetValue("LOGONSERVER")) 40: Finally 41: Key.Close() 42: End Try 43: End Sub 44: 45: End Class
Lines 12 through 27 implement the Button.Click event handler. Lines 15 through 19 wrap the call to AssertExample.exe 's attempt to access the registry. Lines 21 through 25 wrap the call to AssertPermission.dll 's attempt to access the registry. The Catch block in lines 17 through 19 will run when ReadRegistry is called because AssertExample.exe does not have permission to access the registry. (In a real application we would allow this to fail if registry access were required and not permitted. Our example catches the exception to facilitate comparing the Assert behavior with the nonpermitted behavior.) Line 22 calls into AssertPermission.dll 's AssertsForme.ReadRegistry shared method, which we will cover in a moment.
AssertExample.exe 's ReadRegistry method creates a RegistryPermission object imperatively and demands read permission. The demand causes a security exception because AssertExample.exe has not been granted registry permission. Even if we removed the call to Demand , an exception would be raised when OpenSubKey is called in line 37.
Listing 18.5 shows the code for AssertPermission.dll , including the AssertsForme class. Note that since this code asserts registry permission, it also potentially opens a security hole.
Listing 18.5 The Code for AssertPermission.dll
1: Imports System.Security.Permissions 2: Imports Microsoft.Win32 3: 4: Public Class AssertsForme 5: 6: Public Shared Sub ReadRegistry() 7: Dim Permission As RegistryPermission = _ 8: New RegistryPermission(PermissionState.Unrestricted) 9: 10: Permission.Assert() 11: 12: DoReadRegistry() 13: End Sub 14: 15: Private Shared Sub DoReadRegistry() 16: 17: Dim Key As RegistryKey = _ 18: Registry.CurrentUser.OpenSubKey("Volatile Environment") 19: Try 20: MessageBox.Show(Key.GetValue("LOGONSERVER")) 21: Finally 22: Key.Close() 23: End Try 24: End Sub 25: 26: End Class
The private shared method DoReadRegistry performs the task identical to ReadRegistry in Listing 18.4; it simply reads an arbitrary value from the registry. The real action in Listing 18.5 occurs in the shared method AssertsForme.ReadRegistry .
AssertsForme.ReadRegistry (lines 6 through 13) is contained in an assembly created on a computer that has full trust. That is, this assembly can read from the registry. However, if AssertExample.exe calls this method, a security exception will still be thrown because the caller does not have registry permissions. This is true unless registry permissions are asserted in AssertPermission.dll . In Listing 18.5, lines 7 through 10 do in fact assert registry permission, so the call to AssertPermission.AssertsForme.ReadRegistry in line 22 of Listing 18.4 succeeds. (Remove the Permission.Assert statement in line 10 of Listing 18.5 and you will get the expected security exception.)
Assume that we passed in the registry key and returned the read value in AssertsForme.ReadRegistry or that we passed in a key and a new value to write. From the example you can ascertain that a trusted assembly could take on the responsibility of yielding permissions to a less trusted assembly, resulting in an egregious security breach.
To avoid an embarrassing hole in security, use the Assert action sparingly and carefully . Rampant use will likely result in several holes in the security policy and some unhappy customers.