The .NET security administration tools are sufficient for most users and security
administrators to configure their security policy, but to have
complete control over all security policy features, you must do so
programmatically. As with evidence and permissions, .NET represents
each of the security policy elements with classes, meaning that you
can manipulate them in your own code very easily.
In the following sections, we provide explanations of how to program
the key components of security policy, starting with code groups and
membership conditions, then moving on to policy levels, and finally
the security manager. We conclude with an example that brings
together all elements of security policy programming; we demonstrate
how to manipulate the policy of an application domain to control the
policy resolution process applied to the assemblies loaded into it.
8.2.1. Programming Code Groups
The abstract
System.Security.Policy.CodeGroup class provides the base representation of
a code group and defines the functionality that lies at the heart of
the policy resolution process. Four noninheritable subclasses extend
CodeGroup to provide concrete implementations that
you can use in security policy programming; these are shown in Figure 1.
Membership condition
-
An object that implements the
System.Security.Policy.IMembershipCondition interface provides the functionality
to determine whether an assembly or application domain qualifies for
membership to the code group. You must specify the membership
condition at CodeGroup instantiation but can
change it later using the
MembershipCondition property.
Policy statement
-
The policy statement contains values that
specify the effect the CodeGroup has on assemblies
and application domains that are members of the group. The
System.Security.Policy.PolicyStatement class
represents a code group's policy statement. It
contains a value from the
System.Security.Policy.PolicyStatementAttribute enumeration to represent the code
group's attributes, and a
System.Security.PermissionSet that specifies the
set of permissions granted by the code group to its members.
Children
-
Each CodeGroup contains an ordered list of child
CodeGroup objects. In turn, each child
CodeGroup can have its own children, creating a
tree structure. The
Children property gets or sets the
code group's children using a
System.Collections.IList containing the set of
child CodeGroup objects.
The most important method of the CodeGroup class
is Resolve, which takes an
Evidence collection as an argument. Policy
resolution of an assembly (or application domain) consists of the
runtime calling the Resolve method on the root
CodeGroup in each policy level and passing it the
assembly's Evidence collection.
In the Resolve method, the
CodeGroup is responsible for determining if the
assembly's evidence qualifies it for membership, how
to apply any attributes, and how or if the
CodeGroup should use its children to continue the
policy resolution process. Resolve returns a
PolicyStatement that represents the net effect of
all code groups in the tree to which the assembly qualified for
membership. The key difference between each of the
CodeGroup subclasses is how they process the
Resolve method:
UnionCodeGroup
-
This is the most
commonly used code group type, which implements policy resolution.
Members are tested against all child code groups for membership. The
resulting PolicyStatement contains the union of
the code group's permission set and the permission
sets of each child of which the assembly is also a member. If a code
group has the Exclusive attribute, the
PolicyStatement will include only those
permissions granted by that group.
FileCodeGroup
-
This operates the same as
UnionCodeGroup with respect to matching members
against its children and how the resulting
PolicyStatement is calculated. However,
FileCodeGroup does not support attributes and does
not contain a statically defined permission set. With each call to
Resolve, if the evidence of the assembly contains
"file://"-based
Url evidence, the FileCodeGroup
dynamically generates a permission set that contains a
System.Security.Permissions.FileIOPermission
granting access to the directory specified in the
Url evidence.
NetCodeGroup
-
This operates the same as
FileCodeGroup, but
with each call to Resolve, if the evidence of the
assembly contains "http://" or
"https://" Url
(or Site) evidence, the
NetCodeGroup dynamically generates a permission
set that contains a
System.Security.Permissions.WebPermission granting
connect access to the specified web site.
FirstMatchCodeGroup
-
This operates the same
UnionCodeGroup but
evaluates members against its children only until it finds the first
matching child group. The resulting
PolicyStatement contains the permission granted by
the FirstMatchCodeGroup and at most one of its
child groups; this makes the order of the child elements important.
As important as Resolve is to the runtime, both
Resolve and the similar
ResolveMatchingCodeGroups method provide minimal value when used
directly. You cannot use them to drive the policy resolution process
manually, and therefore they are useful only for testing and
debugging. Table 1 summarizes the properties and
methods of CodeGroup and
highlights any special behavior implemented by the subclasses. No
specific code-access permissions are required to use the members of
the code group classes.
Table 1. Members of the code group classes
Member
|
Description
|
---|
Properties
| |
AttributeString
|
Returns a String representing the code
group's attributes. FileCodeGroup
and NetCodeGroup do not use attributes and always
return null (C#) or Nothing
(Visual Basic .NET).
|
Children
|
Gets or sets the code group's child code groups. The
child CodeGroup elements are contained in an
IList. For FirstMatchCodeGroup
groups, the order of the children in the IList is
important.
|
Description
|
Gets or sets the description of the code group as a
String.
|
MembershipCondition
|
Gets or sets the membership condition for the code group using a
System.Security.Policy.IMembershipCondition
instance.
|
MergeLogic
|
Returns a String that describes the logic used to
merge the permissions of the code group and its children; returns
"First Match" for
FirstMatchCodeGroup and
"Union" for the other three types
of code groups.
|
Name
|
Gets or sets the name of the code group using a
String.
|
PermissionSetName
|
Gets a String containing the name of the code
group's permission set. This is
null (C#) or Nothing (Visual
Basic .NET) if the permission set does not have a name. The
NetCodeGroup class always returns the name
"Same site Web" and the
FileCodeGroup always returns the name
"Same directory FileIO-" appended
with the type of file access granted.
|
PolicyStatement
|
Gets or sets the PolicyStatement of the code group.
|
Methods
| |
AddChild
|
Creates a copy of the specified CodeGroup and adds
it to the end of the current list of children.
|
Copy
|
Creates a deep copy of the CodeGroup, including
all child code groups.
|
Equals
|
Basic equality is determined by comparing the
Name, Description, and
MembershipCndition of two
CodeGroup objects. It is also possible to force a
comparison that includes all child code groups.
|
FromXml
|
Reconstructs a CodeGroup from XML previously
generated using the ToXml method.
|
RemoveChild
|
Removes the specified CodeGroup from the list of
immediate children.
|
Resolve
|
Returns a PolicyStatement containing the
permissions granted by the CodeGroup based on a
specified Evidence collection. As discussed
earlier, the type of code group determines how the permissions of
child code groups affect the final
PolicyStatement.
|
ResolveMatchingCodeGroups
|
Returns a CodeGroup tree containing all of the
child code groups to which a specified Evidence
collection qualifies for membership. The type of code group
determines the set of child code groups contained in the final
collection.
|
ToXml
|
Returns a SecurityElement containing the XML
object model for the CodeGroup.
ToXml is useful for writing the contents of a code
group to an XML file so that you can import it when using the
administrative tools. |
Before demonstrating the creation and manipulation of code groups, we
provide details of membership conditions and policy statements.
1.1. Programming membership conditions
Membership conditions are classes that
implement the
IMembershipCondition interface. All four types of
code group contain a single IMembershipCondition
instance that you must specify as an argument to the code
group's constructor; you can get and set the
IMembershipCondition through the
CodeGroup.MembershipCondition property after
construction.
The IMembershipCondition interface defines a
method named Check, which takes an
Evidence collection argument and returns a Boolean
value indicating whether the values of the contained evidence objects
satisfy a configurable condition:
# C#
bool Check(Evidence evidence);
# Visual Basic .NET
Function Check(ByVal evidence As Evidence) As Boolean
A CodeGroup calls the Check
method of its IMembershipCondtion during policy
resolution to evaluate whether an assembly or application domain
qualifies for membership to the code group.
The .NET Framework includes the eight standard membership condition
classes listed in Table 2; all are members of
the System.Security.Policy namespace. Seven of
these classes contain the logic necessary to test the values of
standard evidence classes. One additional membership condition class,
named AllMembershipCondition always returns true
when Check is called, regardless of the evidence
provided. This means a code group using
AllMembershipCondition as its membership condition
will contain all assemblies and application domains tested against
it.
Table 2. Standard membership condition classes
Membership class
|
Membership condition
|
---|
AllMembershipCondition
|
All code irrespective of evidence.
|
ApplicationDirectoryMembershipCondition
|
Evidence collection contains both
ApplicationDirectory and Url
evidence. The Url evidence represents a location
that is a child of that represented by the
ApplicationDirectory evidence.
|
HashMembershipCondition
|
Evidence collection contains a
Hash class with the specified hash value.
|
PublisherMembershipCondition
|
Evidence collection contains a
Publisher class with the specified publisher
certificate.
|
SiteMembershipCondition
|
Evidence collection contains a
Site class with the specified site name.
|
StrongNameMembershipCondition
|
Evidence collection contains a
StrongName class with the specified hash value.
|
UrlMembershipCondition
|
Evidence collection contains a
Url class with the specified URL location.
|
ZoneMembershipCondition
|
Evidence collection contains a
Zone class with the specified security zone.
|
The constructors for each type of membership condition vary depending
on the evidence types they evaluate, but all of them are relatively
straightforward to use if you understand the standard evidence types.
You should consult the .NET Framework SDK documentation for complete
details, but here are some examples of how to create membership
conditions:
# C#
// Create a membership condition to match all code.
IMembershipCondition m1 = new AllMembershipCondition( );
// Create a membership condition to match all code with
// Internet Zone evidence.
IMembershipCondition m2 =
new ZoneMembershipCondition(SecurityZone.Internet);
// Create a membership condition to match all code from
// all "oreilly.com" Sites.
IMembershipCondition m3 =
new SiteMembershipCondition("*.oreilly.com");
// Create a membership condition to match all code with
// the same Publisher certificate as was used to sign
// the SomeFile.exe assembly.
IMembershipCondition m4 =
new PublisherMembershipCondition(
X509Certificate.CreateFromSignedFile("SomeFile.exe")
);
# Visual Basic .NET
' Create a membership condition to match all code.
Dim m1 As IMembershipCondition = New AllMembershipCondition( )
' Create a membership condition to match all code with
' Internet Zone evidence.
Dim m2 As IMembershipCondition = _
New ZoneMembershipCondition(SecurityZone.Internet)
' Create a membership condition to match all code from
' all "oreilly.com" Sites.
Dim m3 As IMembershipCondition = _
New SiteMembershipCondition("*.oreilly.com")
' Create a membership condition to match all code with
' the same Publisher certificate as was used to sign
' the SomeFile.exe assembly.
Dim m4 As IMembershipCondition = _
New PublisherMembershipCondition( _
X509Certificate.CreateFromSignedFile("SomeFile.exe"))
1.2. Programming policy statements
For the UnionCodeGroup
and FirstMatchCodeGroup classes, a
PolicyStatement class represents the effect the
code group has on its members, including the code
group's attributes and its permission set. The
FileCodeGroup and NetCodeGroup
classes do not require you to set a policy statement, because they
generate their permission sets dynamically and do not support
attributes.
Provide a PolicyStatement as an argument to the
UnionCodeGroup and
FirstMatchCodeGroup constructors, and you can get
and set the PolicyStatement after construction
through the CodeGroup.PolicyStatement property.
The PolicyStatement class provides two
constructors. The first takes a
System.Security.PermissionSet argument specifying
the permissions a code group grants to its members. The second
constructor takes both a PermissionSet and a
member of the
System.Security.Policy.PolicyStatementAttribute
enumeration, representing the code group's set of
attributes. We list the possible values of
PolicyStatementAttribute in Table 3.
Table 3. Members of the PolicyStatementAttribute enumeration
Member
|
Description
|
---|
All
|
The code group has both the Exclusive and
LevelFinal attributes
|
Exclusive
|
The code group has only the Exclusive attribute.
See "Code Group Attributes" for
details
|
LevelFinal
|
The code group has only the LevelFinal attribute.
See "Code Group Attributes" for
details
|
Nothing
|
The code group has no attributes
|
The properties listed in Table 4 provide access
to the content of the PolicyStatement after
instantiation.
Table 4. Properties of PolicyStatement
Property
|
Description
|
---|
Attributes
|
Gets or sets the attributes contained in the policy statement as a
member of the PolicyStatementAttribute
enumeration.
|
AttributeString
|
Gets a human-readable String representing the
attributes contained in the PolicyStatement.
|
PermissionSet
|
Gets or sets the permission contained in the
PolicyStatement as a
PermissionSet.
|
The following examples demonstrate how to create
PolicyStatement objects.
# C#
// Create a PolicyStatement that grants Unrestricted access
// to everything
PolicyStatement p1 = new PolicyStatement(
new PermissionSet(PermissionState.Unrestricted)
);
// Create a PolicyStatement that grants read access to the
// file "C:\File.txt" and specifies the LevelFinal attribute.
PermissionSet pset = new PermissionSet(
new FileIOPermission(FileIOPermissionAccess.Read,@"C:\File.txt"));
PolicyStatement p2 = new PolicyStatement(
pset, PolicyStatementAttribute.LevelFinal);
# Visual Basic .NET
' Create a PolicyStatement that grants Unrestricted access
' to everything
Dim p1 As PolicyStatement = New PolicyStatement( _
New PermissionSet(PermissionState.Unrestricted))
' Create a PolicyStatement that grants read access to the
' file "C:\File.txt" and specifies the LevelFinal attribute.
Dim pset As PermissionSet = New PermissionSet( _
New FileIOPermission(FileIOPermissionAccess.Read,"C:\File.txt"))
Dim p2 As PolicyStatement = New PolicyStatement _
(pset,PolicyStatementAttribute.LevelFinal)
1.3. Creating code groups
With the knowledge of how to create policy
statements and membership conditions, creating code groups is
straightforward. You create UnionCodeGroup and
FirstMatchCodeGroup objects by providing an
IMembershipCondition and a
PolicyStatement as constructor arguments. The
following example creates a UnionCodeGroup with
the Exclusive attribute that matches all code
downloaded from any web site in the oreilly.com
domain and grants it unrestricted access to the filesystem:
# C#
// Create the permission set and add unrestricted file access.
PermissionSet pset = new PermissionSet(PermissionState.None);
pset.AddPermission(new FileIOPermission(PermissionState.Unrestricted));
// Create the policy statement and set the Exclusive attribute.
PolicyStatement pstate =
new PolicyStatement(pset, PolicyStatementAttribute.Exclusive);
// Create the membership condition to match all "*.oreilly.com" sites.
IMembershipCondition mcon =
new SiteMembershipCondition("*.oreilly.com");
// Create the UnionCodeGroup
UnionCodeGroup cg = new UnionCodeGroup(mcon, pstate);
# Visual Basic .NET
' Create the permission set and add unrestricted file access.
Dim pset As PermissionSet = New PermissionSet(PermissionState.None)
pset.AddPermission(New FileIOPermission(PermissionState.Unrestricted))
' Create the policy statement and set the Exclusive attribute.
Dim pstate As PolicyStatement = _
New PolicyStatement(pset, PolicyStatementAttribute.Exclusive)
' Create the membership condition to match all "*.oreilly.com" sites.
Dim mcon As IMembershipCondition = _
New SiteMembershipCondition("*.oreilly.com")
' Create the UnionCodeGroup
Dim cg As UnionCodeGroup = New UnionCodeGroup(mcon,pstate)
The NetCodeGroup class
constructor takes only an IMembershipCondition,
because it calculates dynamically its permission set and does not
support attributes; therefore, there is no need for a
PolicyStatement. The following example creates a
NetCodeGroup that allows all code signed with the
publisher certificate contained in the
Publisher.cer file to connect back to the web site
from where it is loaded.
# C#
NetCodeGroup cg = new NetCodeGroup(
new PublisherMembershipCondition(
X509Certificate.CreateFromCerFile("Publisher.cer")
));
# Visual Basic .NET
Dim cg As NetCodeGroup = New NetCodeGroup( _
New PublisherMembershipCondition( _
X509Certificate.CreateFromCerFile("Publisher.cer")))
The FileCodeGroup
constructor takes an IMembershipCondition and a
System.Security.Permissions.FileIOPermissionAccess
argument. The FileIOPermissionAccess specifies the
type of file access permission the FileCodeGroup
should include in its dynamically generated permission set; see Table 5 for possible values.
Table 5. Members of the FileIOPermissionAccess enumeration
Property
|
Description
|
---|
AllAccess
|
All access
|
Append
|
Access to append content to an existing file or directory
|
NoAccess
|
No access
|
PathDiscovery
|
Access to the information contained in the path of a directory or file
|
Read
|
Access to read from a file or directory
|
Write
|
Access to write to a file or directory, including overwriting and
deleting existing files and directories
|
The following code creates a FileCodeGroup that
allows all code signed with the publisher certificate contained in
the Publisher.cer file to write to the directory
from which it was loaded:
# C#
FileCodeGroup cg = new FileCodeGroup(
new PublisherMembershipCondition(
X509Certificate.CreateFromCertFile("Publisher.cer")),
FileIOPermissionAccess.Write
);
# Visual Basic .NET
Dim cg As FileCodeGroup = New FileCodeGroup( _
New PublisherMembershipCondition( _
X509Certificate.CreateFromCertFile("Publisher.cer")),
FileIOPermissionAccess.Write)