Chapter 17

image

Security Attributes

As a platform for massively distributed operations, the Microsoft .NET Framework must have an adequate security mechanism. We all know that distributed platforms, especially those exposed to the Internet, are the favorite targets of all sorts of pranks and mischief, which can sometimes be very destructive.

The security system of the .NET Framework includes two major components: security policies and embedded security requirements. Security policies are part of the .NET Framework setup and reflect the opinions of the system administrator and the system user regarding what managed applications can and cannot do. Which policies are established can depend in part on the general origin of the application (for example, whether the application resides on the local drive of a machine, is taken from a closed intranet, or comes from the Internet), on the software publisher (for example, whether the system administrator feels differently about applications published by Microsoft or IBM and those published by Tailspintoys.com), on the URL specifying the application’s origin, on a particular application, and so forth. Important as they are, these security policies and their definitions are beyond the scope of this book, so with regret, I will forgo a detailed discussion of this topic.

Embedded security requirements are embedded in the applications themselves. Effectively, the embedded security requirements tell the common language runtime which rights an application needs in order to execute. The runtime checks the application’s security requirements against the policy under which the application is executed and decides whether it’s a go or a no-go.

Embedded security requirements are of two kinds: imperative security, which is part of the application’s code, and declarative security, which is part of the application’s metadata. Imperative security explicitly describes the operations necessary to perform a security check—for example, calling a method to demand the right to write a file. Declarative security is a set of security attributes assigned to certain metadata items (the assembly as a whole or a certain class or method). Each of these attributes describes the rights that the corresponding item needs in order to be loaded and executed.

This chapter concentrates on declarative security because it is an important part of metadata and because you need to know how it is defined in ILAsm. Besides, I have a feeling that many aspects of imperative security, and even security policies, can be deduced from an analysis of declarative security.

All aspects of security (at least those applicable to versions 1.0 and 1.1 of the CLR and most of them applicable to version 2.0) are exhaustively analyzed in the excellent book .NET Framework Security (Pearson, 2002) by Brian A. LaMacchia, Sebastian Lange, Matthew Lyons, Rudi Martin, and Kevin T. Price. The book was written by the folks who created the .NET security, and I am happy to referyou to it.

Declarative Security

Compared to imperative security, declarative security has two main advantages:

  • Being part of the metadata, declarative security can be identified and assessed without exhaustive analysis of the application’s IL code.
  • Declarative security can be developed and modified independent of the functional code. As a result, a division of labor is possible: developer X, the functionality guru, writes the application, and developer Y, the security guru, tinkers with the security attributes.

A disadvantage of declarative security is its coarse targeting. Declarative security can be attributed to a class as a whole but not to the parts of the class and not to specific instances. Declarative security can be attributed to a method as a whole, without the exact specification of when and under what circumstances the special rights might be needed. Imperative security, in contrast, allows the method to behave more flexibly: “Can I do this? No? OK, then I’ll do it some other way. Let’s see. Can I do that?”

Declarative Actions

A declarativesecurity attribute has three characteristics: the target, the metadata item to which it is attributed; the permission, a description of the rights that interest the target; and the action, a description of the precise way the target is interested in these rights.

The nine declarative security actions are intended for different targets and take effect at different stages of the application execution. The earliest stage of execution is the initial loading of the assembly’s prime module and analysis of its manifest. Three declarative actions, targeting the assembly, take effect at this stage:

  • Request Minimum: This action specifies that the permission is a minimum requirement for the assembly to be executed. If the minimal permissions are not specified, the assembly is granted all rights according to the existing security policy. These rights, however, might be reduced by other already running parts of the application, by means of a Deny or Permit Only action.
  • Request Optional: This action specifies that the permission is useful to have but is not vital for the assembly execution.
  • Request Refuse: This action specifies that the permission should not be granted even if the security policy is willing to grant it. This action might be used to ensure that the assembly does not have rights it does not need, thus providing a shield against possible bugs in the assembly itself and against malicious code that might try to coerce the assembly to do something it shouldn’t.

All three above actions were deemed obsolete in .NET v4.0 and have no effect in versions 4.0 and later.

The next stage of the application execution is the loading of its classes and their members. Only one declarative action, targeting classes and methods, plays a role at this stage:

  • Inheritance Demand: For classes, this action specifies the permission that all classes descending from this one must have. For methods, this action specifies the permission that all methods overriding this one must have. Obviously, this action affects virtual methods only.

After the classes and their members have been loaded, the IL code of the invoked methods is JIT compiled. The declarative action targeting classes and methods takes effect at this stage:

  • Link Demand: This action specifies the permission that all callers of this method must have—or, if the target is a class, the permission that any method of this class must have. For example, if you have a method that formats the system drive, you want to ensure that this method cannot be successfully called from some rogue code that has no right to do so. This action is limited to the immediate caller only. If method A link-demands permission P and method B calling A has this permission but method C calling B does not, the call will go through.

The last stage of the application execution is the runtime, when the JIT-compiled code is actually executed. The declarative actions taking effect at this last stage and targeting classes and methods are as follows:

  • Demand: This action is similar to Link Demand, but it demands that all callers in the call chain have the specified permission.
  • Assert: This action specifies the permission that the current method must have. Even if the callers of this method higher on the call stack don’t have the specified permission, the security check succeeds. This action obviously weakens the declarative security model and should be applied with caution. You cannot apply this action unless the code has the access permission SecurityPermission, which is discussed in the next section.
  • Deny: This action specifies the permission that must be disabled for all callees down the call stack for the duration of the called method. If a callee never had the specified permission in the first place, the action has no effect on it. This action was deemed obsolete in .NET v4.0, and applying it results in a NotSupportedException thrown at the execution time.
  • Permit Only: This action specifies the permission that must not be disabled for all callees down the call stack, presuming that the rest of the permissions must be disabled. The action seems excessively cruel (to strip the poor callees of all their privileges except one), but you must not forget that the target might have multiple security attributes. Using a series of Permit Only actions, you can create a set of permissions that remain for the callees to enjoy while all other permissions are temporarily revoked. To clarify this, consider the following example. If the called method has security attributes Deny P and Deny Q, all methods it calls (and the methods those methods call, and so on) will have their permissions P and Q suspended. If the called method has security attributes Permit Only P and Permit Only Q, all permissions except P and Q of all callees will be suspended.

And now, let’s see what these Ps and Qs stand for.

Security Permissions

Security permissions define the kinds of activities the code requests (or demands, or denies, and so on) the right to perform. The same permissions are used in security policy definitions, specifying what sorts of applications have the right to perform these activities and under what circumstances.

Special classes of the .NET Framework class library represent these permissions. Each permission class is accompanied by a permission attribute class, whose instance constructor is used as a type of security custom attribute. Applying a security custom attribute to a metadata item leads to instantiation of the security object targeting the associated metadata item.

In some sense, it’s easier to describe the permissions in terms of the accompanying attribute classes because the permission classes have instance constructors of different signatures, whereas the instance constructors of the security attribute classes invariably have one parameter—the security action code. All the parameters of the instance constructor(s) of the respective permission class are represented by the attribute’s properties, set through name/value pairs.

The permissions form three groups. The first group includes the permissions related to access rights to certain resources. The second group consists of permissions related to identity characteristics of the code, including its origin. The third group represents custom permissions, invented by .NET Framework users for their particular purposes. It seems to be a general principle of the .NET Framework that if you can’t find something satisfactory within the framework, it at least provides you with the means to build your own better mousetrap.

Most of the permission classes belong to the namespace System.Security.Permissions, of the Mscorlib.dll assembly, so I’ve specified the assembly and namespace in the following sections only when they are different.

Access Permissions

The access permissions control access rights to various resources. The group includes the following nine permissions:

  • [System.DirectoryServices]System.DirectoryServices.DirectoryServicesPermission: This permission defines access to the Active Directory. The attribute class has two properties:
    • Path (type string) indicates the path for which the permission is specified.
    • PermissionAccess (type int32) specifies the type of access: a value of 0 indicates no access, a value of 2 indicates browse access, and a value of 6 indicates write access.
  • [System]System.Net.DnsPermission: This permission defines the right to use the Domain Name System (DNS). The attribute class has no properties because there are no details to specify: either you can use DNS or you can’t.
  • EnvironmentPermission: This permission defines the right to access the environment variables. The attribute class has three properties, all of type string, which specify the names of the environment variables affected:
    • All specifies the name of the environment variable that can be accessed in any way.
    • Read specifies the name of the environment variable that can be read.
    • Write specifies the name of the environment variable that can be written to.
  • FileDialogPermission: This permission definesthe right to access a file selected through the standard Open or Save As dialog box. The attribute class has two properties, both of type bool, for which true indicates that the access is to be granted and false indicates that it is to be denied:
    • Open grants or denies the right to read the file.
    • Save grants or denies the right to write to the file.
  • FileIOPermission: This permission defines theright to access specified directories or individual files. The attribute class has five properties, all of type string, which contain either a path or a file specification (with a full path). If the path is specified, the permission is propagated to the whole directory subtree starting at this path. The attribute class properties are as follows:
    • All indicates full access to the specified path or file.
    • Read indicates read access to the specified path or file.
    • Write indicates write access, including file overwriting and new file creation.
    • Append indicates append access—in other words, the existing file can be appended but not overwritten, and a new file can be created.
    • PathDiscovery indicates browse access—for example, querying the current directory, getting a filename back from the file dialog box, and so on.
  • IsolatedStorageFilePermission: This permission definesthe right to access the isolated storage. Briefly, the isolated storage is a storage space allocated specifically for the user’s application, providing a data store independent of the structure of the local file system, a sort of “sandbox” for the application to play in without touching the rest of the file system. Data compartments within the isolated storage are defined by the identity of the application or component code. Thus, there’s no need to work magic with the file paths to ensure that the data storages specific to different applications don’t overlap. The attribute class has two properties:
    • UsageAllowed (int32-based enumeration IsolatedStorageContainment) indicates the isolated storage type. The UsageAllowed property can be assigned the following int32 values: None (0x00), DomainIsolationByUser (0x10), ApplicationIsolationByUser (ver.2.0+) (0x15), AssemblyIsolationByUser (0x20), DomainIsolationByMachine (ver.2.0+) (0x30), AssemblyIsolationByMachine (ver.2.0+) (0x40), ApplicationIsolationByMachine (ver.2.0+) (0x45), DomainIsolationByRoamingUser (0x50), AssemblyIsolationByRoamingUser (0x60), ApplicationIsolationByRoamingUser (ver.2.0+) (0x65), AdministerIsolatedStorageByUser (0x70), and UnrestrictedIsolatedStorage (0xF0).
    • UserQuota (type int64) indicates the maximum size in bytes of the isolated storage that can be allocated for one user.
  • ReflectionPermission: This permission definesthe right to invoke Reflection methods on nonpublic class members and to create dynamic assemblies at runtime using the methods of Reflection.Emit. The attribute class has four properties:
    • MemberAccess (type bool): Grants or denies the right to access the nonpublic members through Reflection methods.
    • TypeInformation (type bool) grants or denies the right to invoke Reflection methods to retrieve information about the class, including information about the nonpublic members.
    • ReflectionEmit (type bool) grants or denies the right to invoke Reflection.Emit methods.
    • Flags (int32-based enumeration ReflectionPermissionFlag) summarizes the three preceding properties, using a binary OR combination of flags 0x01 for TypeInformation, 0x02 for MemberAccess, and 0x04 for ReflectionEmit.
  • RegistryPermission: This permission defines theright to manipulate the registry keys and values. This permission is analogous in all ways to FileIOPermission except that it specifies the access rights to the registry rather than to the file system. The attribute class has four properties, all of type string, which contain the registry path:
    • Create grants the right to create the keys and values anywhere in the registry subtree, starting with the node specified in the property.
    • Read grants the right to read the keys and values in that subtree.
    • Write grants the right to change the existing keys and values in that subtree.
    • All grants all of the three preceding rights in that subtree.
  • SecurityPermission: This permission defines a setof 13 essential rights to modify the behavior of the common language runtime security subsystem itself. The attribute class has 13 properties of type bool (one for each right) plus one property (int32-based enumeration SecurityPermissionFlag) representing an OR combination of binary flags corresponding to the Boolean properties:
    • Assertion defines the right to override a security check for any granted permission. The respective binary flag is 0x0001.
    • UnmanagedCode defines the right to invoke the native unmanaged code, such as through P/Invoke or COM interoperation (flag 0x0002). If this right is granted, it is asserted every time the unmanaged code is invoked, which results in a significant performance hit. To avoid this, the custom attribute System.Security.SuppressUnmanagedCodeSecurityAttribute can be used. The presence of this attribute suppresses the repetitive security checks when the unmanaged code is invoked.
    • SkipVerification defines the right to run the code without the IL verification procedures at JIT compilation time (flag 0x0004). This is an extremely dangerous right. To avoid inviting trouble, this right should be granted only to code that is known to be safe and that comes from a trusted source.
    • Execution defines the right to run the code (flag 0x0008). This right, which is granted to almost any code, is the opposite of SkipVerification. The right can be revoked by the administrator or by user security policies regarding specific applications or specific sources that are known for or suspected of being the purveyors of malicious code.
    • ControlThreaddefines the right to perform thread control operations, such as suspending a thread, interrupting a thread, stopping a thread, changing the thread priority, and so on (flag 0x0010).
    • ControlEvidencedefines the right of the domain host to give evidence to the applications loaded in the domains created by this host (flag 0x0020). The evidence in question usually includes information about the origin and strong name signature of the loaded assembly. If the domain host does not have this right, it gives its own evidence instead. Starting with .NET v4.0, the types of evidence objects passed between the host and applications must be derived from [mscorlib]System.Security.Policy.EvidenceBase.
    • ControlPolicydefines the right to access and modify security policies, both user-specific and machinewide (flag 0x0040). This is another extremely dangerous right that must be granted with great caution.
    • SerializationFormatter defines the right to perform the serialization formatting operations and to retrieve and change the characteristics of any nontransient members of the serializable types, regardless of the accessibility of these members (flag 0x0080). This permission resembles ReflectionPermission in the sense that both are of a very low opinion about the accessibility rules and allow you to access and invoke private class members at will.
    • ControlDomainPolicy defines the right of the domain host to specify a domainwide security policy (flag 0x0100).
    • ControlPrincipal defines the right to replace the Principal object (carrying the user’s identity characteristics) for a given thread, such as in order to implement role-based security (flag 0x0200). In the role-based security model, the security actions depend on the identity (Principal object) of the “code runner” and the role in which the code runner operates.
    • ControlAppDomain defines the right to create and manipulate the application domains (flag 0x0400).
    • RemotingConfiguration defines the right to configure the remoting types and channels (flag 0x0800).
    • Infrastructure defines the right to plug the code into the common language runtime infrastructure, such as adding remoting context sinks, envoy sinks, and dynamic sinks (flag 0x1000).
    • Flags is a summary binary representation of the 13 rights just listed. The validity mask is 0x1FFF.

Identity Permissions

The access permissions discussed in the previous section describe the resources to be accessed and the actions to be performed. The identity permissions, in contrast, describe the identities of the agents that are accessing these resources and performing these actions. As a trivial example, suppose you’ve created a method or a component so atrocious that you want only components written by your company to be able to access it because you can’t trust anyone else to keep the beast in check.

It’s a good practice to use identity permissions to extend rather than limit the rights granted to the code of a specific origin. Limiting the rights on the basis of the code’s identity is a poor protection technique because the identity information of the code can easily be suppressed. A software publisher you particularly dislike could simply neglect to sign its malicious software, for instance, and you’d never know that this particular code must be treated with extra caution. Or the obnoxious snooping marketing site you’d love to block could start operating through a different web server or spoof its IP address.

The five identity permissions all belong to the namespace System.Security.Permissions and are defined in the Mscorlib.dll assembly:

  • ZoneIdentityPermission: This permission identifiesthe zone from which the calling code originates. The zones are defined and mapped from the URLs by APIs of IInternetSecurityManager and related interfaces. The zones are not overlapping, and any particular URL can belong to only one zone. The attribute class has one property, Zone (int32-based enumeration [mscorlib]System.Security.SecurityZone). The values of the enumeration are as follows:
    • MyComputer (0x0) means that the application runs from the local drive.
    • Intranet (0x1) means that the application runs from a closed intranet.
    • Trusted (0x2) means that the application runs from a trusted server.
    • Internet (0x3) means that the application originates from the Internet.
    • Untrusted (0x4) means that the application’s origin is suspicious and that a high level of security is required.
    • NoZone (0xFFFFFFFF) means that no zone information is available.
  • StrongNameIdentityPermission: This permission identifies an assembly by its strong name attributes, namely by the assembly name, the assembly version, and the public encryption key. The public encryption key of the assembly must exactly match the one specified in the permission.
  • The assembly name, however, might only partially match the one specified in the permission because a wildcard character (*) can be used in the assembly name specification in the permission. The name of the assembly is usually a dotted name, such as System.DirectoryServices, and any right part of the name can be replaced with the wildcard character. Thus, System.DirectoryServices denotes this specific assembly only, System.* denotes any assembly whose name starts with System. (including the assembly System), and * denotes any assembly. If, for example, the permission includes the Microsoft private encryption key and the assembly name is given as System.DirectoryServices, the permission identifies the assembly System.DirectoryServices from the .NET Framework. If the assembly name included is System.*, the permission identifies it as any Microsoft assembly whose name begins with System. If the assembly name is given simply as *, the permission identifies it as any assembly produced and signed by Microsoft. It is illegal to replace the left part of the name with the wildcard character (for example, *.DirectoryServices).
  • The assembly version includes four components: the major version, the minor version, the build number, and the revision number. The fourth component or both the third and the fourth components can be omitted, but the first two components must be specified, unless the version is not specified at all. The attribute class has three properties, all of type string:
    • Name is the name of the assembly, possibly with a wildcard character in the right part.
    • PublicKey is the encoded hexadecimal representation of the public encryption key.
    • Version is the literal representation of the version, for example, 1.12.123.1 or 1.12.
  • PublisherIdentityPermission: This permission specifiesthe software publisher’s identity, based on the public key defined by an X509v3 certificate. This certificate is issued by a trusted certification authority and contains encrypted information authenticating the publisher’s public encryption key. The name of the publisher is ignored. The associated attribute class has three properties, all of type string. Only one of the properties can be set because they represent alternate ways of obtaining the certificate:
    • X509Certificate contains the explicit X509v3 certificate in a coded form.
    • CertFile contains the name of the file containing the certificate.
    • SignedFile contains the name of the file strong name signed with this certificate so that the certificate can be obtained from the file’s strong name signature.
  • SiteIdentityPermission: This permission identifies the web site from which the code originates. The attribute class has one property, Site, of type string, which contains part of the web site’s URL with a stripped protocol specification at the start and the filename at the end, such as www.microsoft.com in the URL http://www.microsoft.com/ms.htm. The protocol is presumed to be HTTP, HTTPS, or FTP. The wildcard character (*) is allowed in the site specifications, this time as the left part of the specification. For example, *.microsoft.com would cover www.microsoft.com, msdn.microsoft.com, windows.microsoft.com, etc.
  • UrlIdentityPermission: This permission identifiesthe full URL of the site from which the code originates. The attribute class has one property, Url, of type string, which contains the full URL specification, including the protocol specification and file specification, such as http://oursite.microsoft.com/apps/foo/zzz.html. The wildcard character is permitted, this time as the right part of the specification, for example, http://oursite.microsoft.com/apps/foo/*.

Custom Permissions

The custom permissions, similarto those already defined in the .NET Framework class library, describe access rights to various resources. Once defined, a custom permission can be used in the same way as any “standard” permission. Custom permissions are introduced, as a rule, when it’s necessary to describe access to some new kind of resource not covered by existing permissions, such as a new input or output device.

image Tip  It’s a bad practice to try to redefine existing permissions as custom permissions. It is possible to do so, but having multiple permissions pertaining to the same resource can only create pain for the system administrators, who must then keep an eye on all alternative “doors” leading to the resource. As a matter of practical advice, don’t make system administrators any unhappier than they already are; it might cost you.

To define a custom permission, you’ll need to do the following.

  1. Define the new permission class.
  2. Define constructors and methods of the permission class according to the permission semantics.
  3. Define the methods implementing the [mscorlib]System.Security.IPermission interface: Copy, Intersect, Union, IsSubsetOf, and Demand.
  4. If, in principle, full access to the resource can be granted, define the IsUnrestricted method implementing the [mscorlib]System.Security.IUnrestrictedPermission interface.
  5. Define the methods implementing the [mscorlib]System.Security.ISecurityEncodable interface that provide the XML encoding and decoding of the permission: FromXml and ToXml.
  6. If necessary, define the GetObjectData method implementing the [mscorlib]System.Runtime.Serialization.ISerializable interface.
  7. Define the accompanying attribute class.
  8. Add support for declarative security.
  9. Add the mechanism enforcing the permission wherever the associated resource is exposed.
  10. Modify the security policies to take your new permission into account.

Needless to say, the preceding list is meant to discourage you from defining custom permissions. . . .

The best way to define a custom permission is to pick a standard permission whose semantics resemble your intended semantics most closely and use it as an example. It’s always a good idea to derive the custom permission classes from [mscorlib]System.Security.CodeAccessPermission and the accompanying attribute classes from [mscorlib]System.Security.Permissions.CodeAccessSecurityAttribute.

One of the major design problems in defining a custom permission is the question of the granularity of the resource access description. In other words, what level of detail is adequate to describe the protected resource? If you were designing RegistryPermission, for example, your choice of granularity could range from a 1-bit indication of whether full access to the registry is granted to a detailed description of a specific kind of access to a specific registry node.

Generally, four basic principles should guide your approach to permission granularity:

  • Total Boolean, which grants or denies access to the resource
  • Total enumerated, which grants one of the specified (enumerated) forms of access to the resource
  • Listed Boolean, which grants or denies access to the resource components listed in the permission declaration
  • Listed enumerated, which grants one of the specified forms of access to the resource components listed in the permission

Although additional questions might arise about the level of detail involved in the access form enumeration and the resource components list, the four basic principles, I think, stand. You are welcome to introduce a fifth and put me to shame.

A custom permission class must implement the ISecurityEncodable interface, with its methods ToXml and FromXml, to encode the permission in XML form and restore the permission object from the XML text. The outermost tag of the XML encoding is Permission:

<Permission  class="MyPermission,  MyOtherAssembly.dll"   version="1">
      ...
</Permission>

To support the declarative security mechanism built into the common language runtime, the custom permission class must be accompanied by the attribute class. The attribute class must have properties that correspond to the parameters of the permission class’s constructors. The attribute class must also implement at least one variant of the CreatePermission method. The custom attribute System.AttributeUsageAttribute must be assigned to the attribute class, defining its possible targets, inheritance, and multiplicity, as described in Chapter 16.

Enforcing a newly created custom permission is the easy part; the items dealing with the new resource must create security objects from the custom permission and also security actions, such as Demand, Assert, and so on. The simplest way to do this is to assign the security custom attribute to the respective item.

The last step in creating a custom permission is updating the security policies to include the permission. This is done by writing an XML descriptor of the custom permission and invoking the code access security policy tool, Caspol.exe:

caspol  –addset  cust_perm.xml  cust_perm_name

Then, again by using the Caspol.exe utility, a new code group must be added, or the existing one changed, to specify the code identities that will be granted the custom permission. Operating the Caspol.exe utility is rather complicated and well beyond the scope of this book; for information, you can refer to the documentation on Caspol.exe and security administration included in the .NET Framework SDK.

Permission Sets

Individual permissionobjects (the instances of the permission classes) can be combined into permission sets. A permission set is an instance of the [mscorlib]System.Security.PermissionSet class or of the [mscorlib]System.Security.NamedPermissionSet class, which is derived from the former. A permission set can be constructed, such as by combining all permissions relevant to a certain resource or to a certain metadata item (the assembly, a class, or a method).

The PermissionSet class, after its constituent permission classes, implements the interface IPermission with its methods Copy, Intersect, Union, IsSubsetOf, and Demand.

The declarative security is represented in the metadata by the unnamed permission sets, grouped by the security action. Each such permission set is attributed to one metadata item (assembly, class, or method).

Changes in Declarative Security in v4.0

There was quite a revision of the declarative security mechanism in .NET v4.0, significantly simplifying the overall picture.

First of all, the computer-wide security policies (remember the caspol.exe utility I mentioned before?) are not supported anymore, meaning they are not enforced by .NET. As an alternative, the system administrators should look at OS-level security solutions such as Windows Software Restriction Policies (SRP) and AppLocker, which are simpler to use and besides apply to both managed and unmanaged code.

The actions RequestMinimum, RequestOptional, and RequestRefuse are deemed obsolete and are ignored. RequestMinimum cannot be used effectively outside the application scope; it cannot be used on library assemblies because different types and members in the assembly as a rule have different security requirements; when used on an executable assembly, this action, on security check failure, results in a FileLoadException containing no useful actionable information. RequestOptional action is too enigmatic and its use often leads to surprising results. RequestRefuse is ineffective for least-privilege enforcement because the developer needs to specify all not wanted permissions, and if some of those are omitted, or some new permissions are introduced. . . well, you see. Besides, RequestOptional and RequestRefuse offer the developers an opportunity to break a homogenous appdomain by creating multiple contradicting permission sets within the domain.

The action Deny is deemed obsolete and its usage leads to CLR throwing NotImplementedException at the execution time. This action can easily be overridden by an Assert action, so it is ineffective and misleading.

And finally, as I mentioned before, the types of evidence objects passed between the host and applications must be derived from [mscorlib]System.Security.Policy.EvidenceBase.

Declarative Security Metadata

The declarative security metadata resides in the metadata table DeclSecurity. A record in this table has these three entries:

  • Action (2-byte unsigned integer): The security action code.
  • Parent (coded token of type HasDeclSecurity): The index to the Assembly, TypeDef, or Method metadata table, indicating the metadata item with which the DeclSecurity record is associated.
  • PermissionSet (offset in the #Blob stream): Encoded representation of the permission set associated with a specific security action and a specific metadata item.

The following security action codes and their respective ILAsm keywords are defined for the security actions listed in the “Declarative Actions” section of this chapter and for special-purpose security actions:

  • Request: Code 0x0001. ILAsm keyword request.
  • Demand: Code 0x0002. ILAsm keyword demand.
  • Assert: Code 0x0003. ILAsm keyword assert.
  • Deny: Code 0x0004. ILAsm keyword deny.
  • Permit Only: Code 0x0005. ILAsm keyword permitonly.
  • Link Demand: Code 0x0006. ILAsm keyword linkcheck.
  • Inheritance Demand: Code 0x0007. ILAsm keyword inheritcheck.
  • Request Minimum: Code 0x0008. ILAsm keyword reqmin.
  • Request Optional: Code 0x0009. ILAsm keyword reqopt.
  • Request Refuse: Code 0x000A. ILAsm keyword reqrefuse.
  • Pre-JIT Grant (persisted grant, set at pre-JIT compilation time by the Ngen.exe utility): Code 0x000B. ILAsm keyword prejitgrant.
  • Pre-JIT Deny (persisted denial, set at pre-JIT compilation time): Code 0x000C. ILAsm keyword prejitdeny.
  • Non-CAS Demand: Code 0x000D. ILAsm keyword noncasdemand. This action is similar to Demand, but the permission classes that make up the permission set must not be derived from System.Security.Permissions.CodeAccessPermission.
  • Non-CAS Link Demand: Code 0x000E. ILAsm keyword noncaslinkdemand. This action is similar to Link Demand but has the same limitation as Non-CAS Demand.
  • Non-CAS Inheritance Demand: Code 0x000F. ILAsm keyword noncasinheritance. This action is similar to Inheritance Demand but has the same limitation as Non-CAS Demand.

Permission Set Blob Encoding

The blobindexed in the PermissionSet entry of the DeclSecurity record contains an encoded representation of the permission set object. In versions 1.0 and 1.1 of the common language runtime, the blob contained simply a Unicode-encoded XML description of the permission set.

In version 2.0 of the CLR, new, more economical binary encoding has been introduced. The blob begins with byte 0x2E (character .), followed by a compressed number of permissions in the set, followed by the encoded permissions. An XML text cannot begin with a dot, so the system identifies the type of encoding (XML or binary) by the very first byte.

A permission encoding begins with the compressed length of the fully qualified class name in Reflection notation, followed by the name itself in UTF-8 encoding and without the zero terminator. After that comes the compressed size of the permission’s blob, followed by the compressed number of properties to be set (can be 0), followed by the property encodings (if any). Unlike custom attributes in general, which allow both fields and properties to be initialized via name/value pairs, security attributes allow only properties to be set.

The property encoding follows the same pattern as the property name/value pair encoding in custom attributes: it begins with byte SERIALIZATION_TYPE_PROPERTY (0x54), followed by the property type, followed by the compressed length of the property name and the name itself, and followed by the encoded value.

To summarize,

  • Permission set blob encoding:
.// dot character
<compressed_uint>// number of permissions in the set
{ <permission> }// set of permission encodings
  • Permission blob encoding:
<compressed_uint>// length of the class name (follows)
<class_name>      // fully qualified class name in Reflection notation
<compressed_uint>// size of initialization blob
<compressed_uint>  // number of properties, can be 0
[ { <property> } ]// set of properties, absent if the number=0
  • Permission property encoding:
SERIALIZATION_TYPE_PROPERTY// 1 byte, 0x54
<type_of_the_property>      // property signature
<compressed_uint>           // length of the property name (follows)
<property_name>
<encoded_value>

Security Attribute Declaration

ILAsm syntax offers two forms of security attribute declarations: separate permissions and permission sets. You can use the form you find more convenient; the IL assembler will automatically combine the separate permissions into permission sets. The owner of the security attribute is the item whose scope contains the security attribute declaration. The syntax for the permission declaration is

.permission  <sec_action>  <class_ref>  [(<name_value_pairs>)]

where <sec_action> is one of the security action keywords listed in the preceding section, <class_ref> is a class reference to the attribute class associated with the permission class, and the optional <name_value_pairs> defines the values of the attribute class’s properties, as shown here:

<name_value_pairs>  ::=  <nv_pair>[,<nv_pair>*]
<nv_pair>  ::=  <prop_name>  =  <prop_value>

<prop_name> is the propertyname of the attribute class, specified as a quoted string. The form of <prop_value> depends on the type of property:

<prop_value>  ::=  true  |  false  // For Boolean properties
      |  <int32>  |  int32(<int32>)  // For integer properties
   |  <class_ref>  (<int32>)  // For enumerated properties,
                   //  <class_ref> specifies the enumerator
   |  <class_ref>(<int_type>  :  <int32>)  //  <int_type>::=int8
                                         //  | int16 | int32
   |  <quoted_string>  // For string properties

For example,

.method  private  void  WriteToSystemDrive(string  Str2BWritten )
{
      .permission  demand
      [mscorlib]System.Security.Permissions.FileIOPermissionAttribute
            = ("Write"="C:\")
    ...
}

The IL assembler combines separate .permission declarations into permission sets before emitting the DeclSecurity metadata. However, a permission set can be declared explicitly using

.permissionset  <sec_action>  =  (  <hexbytes>  )

where <hexbytes> is a bytearray representing the PermissionSet blob. This byte array is usually fairly long; a “live” example would take a couple of pages. To see such an example, you can simply disassemble any .NET Framework assembly (Mscorlib.dll or System.dll, for instance) and have a look.

The new form of the permission set blob encoding, introduced in version 2.0, is expressed in ILAsm as follows:

.permissionset<sec_action> = {<class_ref> [ = {<prop_value> [<prop_value>...]}]...}

For example,

.permissionset reqmin=
    {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute
          = { property bool'SkipVerification' = bool(true)}}

The IL disassembler always uses the .permissionset directive to reflect the DeclSecurity metadata records.

Summary of Metadata Validity Rules

A record of the DeclSecurity metadata table has three entries: Action, the security action code; Parent, the metadata item to which the security record is attached; and PermissionSet, the blob containing the XML descriptor of the permission set. The metadata validity rules for the DeclSecurity metadata records are as follows:

  • [runtime] The Action entry must hold a valid security action code in the range from 0x1 through 0xF.
  • The Parent entry must hold a valid reference to the Assembly, TypeDef, or Method tables.
  • If the Parent entry references a TypeDef record, this record must not define an interface.
  • If the Parent entry references a TypeDef or Method record, the metadata item referenced in the Parent entry must have its respective HasSecurity flag set (0x00040000 for TypeDef records and 0x4000 for Method records).
  • [runtime] The PermissionSet entry must hold a valid offset in the #Blob heap. The blob at this offset must contain a legal XML representation of the permission set, Unicode-encoded, or a binary representation of the permission set, encoded according to the scheme described previously.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.98.153