
Combining Security Demands
To optimize code that makes security demands, you can, in some situations, use a technique for combining demands.
For example, if:
your code performs a number of operations within a single method, and
when performing each of those operations, your code calls into a managed class library that demands that your code have the same permission on each call to the library,
then:
If the call stack depth above the method is large, using this technique can result in a significant performance gain.
To illustrate how this works, suppose method M performs 100 operations. Each operation calls into a library that makes a security demand requiring your code and all its callers to have the X permission. Because of the security demands, each operation causes the runtime to walk the entire call stack examining each caller's permissions, to determine whether X permission was actually granted to each caller. If the call stack above method M is n levels deep, 100n comparisons are required.
To optimize, you can do the following in method M:
Demand X, which results in the runtime performing a stack walk (of depth n) to ensure that all callers indeed have permission X.
Then, assert permission X, which causes subsequent stack walks to stop at method M and succeed, thereby reducing the number of permission comparisons by 99n.
In the following code example, the GetFileCreationTime method takes a string representation of a directory as a parameter and displays the name and creation date of every file in that directory. The static File..::.GetCreationTime method reads information from the files but requires a demand and stack walk for every file it reads. The method creates a new instance of the FileIOPermission object, performs a demand to check the permissions of all callers on the stack, and then asserts the permission if the demand is successful. If the demand succeeds, only one stack walk is performed and the method reads the creation time from every file in the passed directory.
using System;
using System.IO;
using System.Security;
using System.Security.Permissions;
namespace OptimizedSecurity
{
public class FileUtil
{
public FileUtil()
{
}
public void GetFileCreationTime(string Directory)
{
//Initialize DirectoryInfo object to the passed directory.
DirectoryInfo DirFiles = new DirectoryInfo(Directory);
//Create a DateTime object to be initialized below.
DateTime TheTime;
//Get a list of files for the current directory.
FileInfo[] Files = DirFiles.GetFiles();
//Create a new instance of FileIOPermission with read
//permission to the current directory.
FileIOPermission FilePermission = new FileIOPermission(FileIOPermissionAccess.Read, Directory);
try
{
//Check the stack by making a demand.
FilePermission.Demand();
//If the demand succeeded, assert permission and
//perform the operation.
FilePermission.Assert();
for(int x = 0 ; x<= Files.Length -1 ; x++)
{
TheTime = File.GetCreationTime(Files[x].FullName);
Console.WriteLine("File: {0} Created: {1:G}", Files[x].Name,TheTime );
}
// Revert the Assert when the operation is complete.
CodeAccessPermission.RevertAssert();
}
//Catch a security exception and display an error.
catch(SecurityException)
{
Console.WriteLine("You do not have permission to read this directory.");
}
}
}
}
Option Explicit
Option Strict
Imports System
Imports System.IO
Imports System.Security
Imports System.Security.Permissions
Namespace OptimizedSecurity
Public Class FileUtil
Public Sub New()
End Sub
Public Sub GetFileCreationTime(directory As String)
'Initialize DirectoryInfo object to the passed directory.
Dim dirFiles As New DirectoryInfo(directory)
'Create a DateTime object to be initialized below.
Dim theTime As DateTime
'Get a list of files for the current directory.
Dim files As FileInfo() = dirFiles.GetFiles()
'Create a new instance of FileIOPermission with read
'permission to the current directory.
Dim filePermission As New FileIOPermission(FileIOPermissionAccess.Read, Directory)
Try
'Check the stack by making a demand.
filePermission.Demand()
'If the demand succeeded, assert permission and
'perform the operation.
filePermission.Assert()
Dim x As Integer
For x = 0 To Files.Length - 1
theTime = file.GetCreationTime(files(x).FullName)
Console.WriteLine("File: {0} Created: {1:G}", files(x).Name, theTime)
Next x
' Revert the Assert when the operation is complete.
CodeAccessPermission.RevertAssert()
'Catch a security exception and display an error.
Catch
Console.WriteLine("You do not have permission to read this directory.")
End Try
End Sub
End Class
End Namespace
If the demand in the previous example succeeds, every file and its creation date and time are displayed for the passed directory. If the demand fails, the security exception is intercepted and the following message is displayed to the console:
You do not have permission to read this directory.