FullTrust means Full Trust

The text below is provided "AS IS", without any responsibilities attached to it. It represents author's personal opinion and knowledge, and does not necessarily reflect recommended best practices of Microsoft. Author does not assume any responsibility caused by the use of the following information.

=================================

Well, here is another post after a long, long break. This time it's about a set of recent Whidbey changes affecting Permissions and Policy functionality.

In .NET Framework, all Permission classes can be categorized into 3 distinct buckets: "normal" CAS Permisisons, Identity Permissions and non-CAS Permissions.

Non-CAS permissions do not perform stack walk when Demand on them is invoked. In .NET 1.x and 2.0, there is only one such class which is PrincipalPermission.

"Normal" CAS Permisisons, examples of which would be FileIOPermission or SecurityPermission, do check all the callers when Demand is invoked. Their behavior is well known and described in thousands of samples, docs, etc.

Identity Permissions [like StrongNameIdentityPermission] are very similar in their nature to "normal" CAS ones. However, a number of subtle differences puts them into "special" category and oftentimes requires special treatment.

First of all, Identity permissions are granted to assemblies by Policy not through the process of determining their Code Groups membership, but rather as simple reflection of assembly's identity. So you can't grant ZoneIdentityPermission for MyComputer to a particular Code Group [e.g., Internet] via Policy configuration. Instead, all the code "arriving" from Internet will be automatically grated ZoneIdentityPermission for Internet Zone, no matter how the machine code groups are set up.

Identities which have corresponding permissions are: PublisherIdentity [Authenticode signature], Site, StrongName, Url, Zone. In 2.0 version of the .NET Framework, there is also Gac identity added.

The second and more important is that in versions 1.x of the .NET Identity Permissions [unless empty] were not considered to be subset of Unrestricted PermissionSet. Therefore, for instance, even if your assembly was running from LocalComputer in FullTrust, Demands for StrongNameIdentityPermission for any public key X would have failed unless your assembly was signed with the key X. Period.

At the first glance, this looks logical. If I want my component to be callable only by known third parties, I put StrongNameIdentityPermission, or PublisherIdentityPermission {Link|Inheritance}Demand on it, and thus make sure that nobody else can ever access it. Everything works fine, I'm happy.

Unfortunately, the assumption above is not correct. Moreover, it can cause Security problems bubbling up at the very late stages of the product cycle, throwing the original design into waste.

FullTrust really means Full Trust. That's it, there is no CAS protection, ever, against malicious code granted FullTrust. Identity Permissions are not the exception from this rule.

If malicious fully trusted code Evil.exe wants to invoke your code protected with some kind of Identity Demand, it can do tons of things to achieve that goal. The most simple one would be to create an AppDomain, then call in it Assembly.Load(AssemblyName, Evidence), and pass as parameters assembly Evil.exe and Evidence created based on Identity requested by your code. After it's done, Evil.dll will "look" just like it has proper Identity, therefore it will be granted corresponding Identity Pemission, what will make the Demand pass.

There are other ways, more or less simple. Evil code can, for example, switch the Security off [unless it's not prevented by set of measures outlined by Shawn in his article: https://blogs.msdn.com/shawnfa/archive/2005/05/04/414686.aspx\], or recompile the protected code, etc., etc., etc.

The bottom line is, Identity permissions Demands could not [and should not] be used as measure of Security protection against highly privileged code. The best they provide in Full Trust is an illusion of protection, what can be even worse than no protection at all.

There were other problems with their behavior in 1.x. The most obvious was performance cost: when Identity permission is demanded, Security checks have to be performed even in FullTrust environment, even though [as we saw] they do not make much sense there.

To fix those problems, in Whidbey it was decided to come up with the set of changes known internally under the name "FullTrust means Full Trust". In essence, they consist of 3 parts:

1. Identity Permissions in any state become subset of Unrestricted PermissionSet.

2. Set logics for IdentityPermissions changes.

3. LinkDemands are optimized out in FullTrust.

Here are the details on each item:


1. Identity Permissions in any state become subset of Unrestricted PermissionSet.

Effectively that means that Demand for any Identity Permission will now pass in FullTrust. This improves FullTrust performance through simplificaiton of many Security checks.

This also resolves a painful problems with Delegates around system code protected by LinkDemands. Such Delegates are often created and then called by 3rd party Full Trust applications [such as WinForms UI registering a system function to be invoked as result of button click]. When application runs, it eventually invokes a system method. At that moment, LinkDemand is evaluated, and since the application is not signed with Microsoft public key, the execution blows up with SecurityException.

Now, such problems are gone to the past.

Of course, the change also means that no Identity Permissions Demands can be used to protect one code from being accessed by another in FullTrust. However, this just makes explicit what was hidden before: there is no CAS protection against Full Trust.

Note that the change affects FullTrust code only. The old behavior still stands strong for partially trusted scenarios -- for example, for default Security for Internet Zone.


2. Set logics for IdentityPermissions changes.

This is essentially a consequence of #1. Here are some details:

2.1 Now you can create IdentityPermissions in Unrestricted state:

StrongNameIdentityPermission P = new StrongNameIdentityPermission(PermissionState.Unrestricted);

2.2. You can union two different Identity Pemrissions and get a meaningful result storing both used states:

ZoneIdentityPermission Z1 = new ZoneIdentityPermission(SecurityZone.MyComputer);

ZoneIdentityPermission Z2 = new ZoneIdentityPermission(SecurityZone.Internet);

ZoneIdentityPermission Z3 = (ZoneIdentityPermission) Z1.Union(Z2);

The result is:

<IPermission class="System.Security.Permissions.ZoneIdentityPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"

version="1">

<Zone Zone="MyComputer"/>

Zone Zone="Internet"/>

</IPermission>

Obviously such permission states are mostly useless. Then why I seem to be so happy about this functionality? Because it simplifies the life of developer. You won't have to specialcase Identity Permisisons anymore when performing set logics operations, or creating them. Everything is straighforward from the beginning. At least, this is my opinion :)


3. LinkDemands are optimized out in FullTrust.

This change is important, as it may break the code of some [hopefully very, very few] people.

LinkDemands do not know anything about stack walk modifiers like Deny, Assert, and PermitOnly. And they check only the grant set of the immediate caller. Combined together, two previous statements inevitably mean that any LinkDemand for any Identity or "normal" CAS Permission will now pass in FullTrust.

If so, why waste CPU cycles on doing checks with result already known? So the decision was made to simply skip any LinkDemands if they happen in FullTrust environment. Of course, this does not apply to scenarios where methods are called through Reflection and LinkDemands need to be converted to full Demands.

This change was a clear win for JIT and startup time, but cut off some corner scenarios with non-CAS permissions. Since they don't do a stackwalk and may implement just any kind of arbitrary logics of their Demands, there is no guarantee their LinkDemands will always succeed in FullTrust. Which means the code has to maintain record of them, even in FullTrust environment. But due to the details of internal CAS implementation, doing so kills most of the performance benefits achieved from LinkDemands optimization for other types of permissions.

So finally a tough decision was made to simply ignore non-CAS LinkDemands in FullTrust, too. For PrincipalPermission this was not a big deal, since LinkDemand operation for it was forbidden long time ago anyway. The only potentially affected code was custom non-CAS permissions.

So far, we are not aware of any people who found good use of non-CAS custom permissions LinkDemands. Ironically, it seems like the only person suffered from this change was... me -- simply because my sample of non-CAS permission put on the Web about a year ago [https://blogs.msdn.com/eugene_bobukh/archive/2004/03/10/87645.aspx] will not work in FullTrust anymore :)