Coding with Security Policy in .NET 4.0 – Implicit uses of CAS policy

Last week we looked at sandboxing and the v4 CLR – with the key change being that the CLR now defers exclusively to the host application when setting up sandboxed domains by moving away from the old CAS policy model, and moving instead to simple sandboxed AppDomains.

This leads to an interesting situation when your program calls APIs that assume the presence of CAS policy, either implicitly [for example, Assembly.Load(string, Evidence)] or explicitly [for example SecurityManager.PolicyHierarchy].   These APIs require CAS policy in order to return correct results, however by default there is no longer CAS policy to apply behind the scenes anymore.

Let’s take a look at what happens if these APIs are called, and what should be done to update your code to take into account the new security policy model.

(In addition to this blog post, the CLR security test team is preparing a set of blog posts about how they moved our test code base forward to deal with these and other v4 security changes – those posts will provide additional advice about how to replace uses of obsolete APIs based upon the real world examples they’ve seen).

In general, APIs that assume the presence of CAS policy have been marked obsolete, and will give a compiler warning when you build against them:

Microsoft (R) Visual C# 2010 Compiler version 4.0.20506
Copyright (C) Microsoft Corporation. All rights reserved.

obsolete.cs(32,1): warning CS0618: '<API Name> ' is
        obsolete: 'This method is obsolete and will be removed in a future
        release of the .NET Framework. Please use <suggested alternate API> . See
        https://go.microsoft.com/fwlink/?LinkId=131738 for more information.'

Additionally, these APIs will throw a NotSupportedException if they are called at runtime:

Unhandled Exception: System.NotSupportedException: This method uses CAS policy, which has been obsoleted by the .NET Framework. In order to enable CAS policy for compatibility reasons, please use the NetFx40_LegacySecurityPolicy configuration switch. Please see https://go.microsoft.com/fwlink/?LinkId=131738 for more information.

(In the beta 1 release, this message is slightly different:)

Unhandled Exception: System.NotSupportedException: This method uses CAS policy, which has been obsoleted by the .NET Framework. In order to enable CAS policy for compatibility reasons, please use the legacyCasPolicy configuration switch. Please see https://go2.microsoft.com/fwlink/?LinkId=131738 for more information.

Let’s take a look at the set of APIs which make implicit use of CAS policy first, and then see what they might be replaced with in a v4.0 application.

The general way to recognize an API which is implicitly using CAS policy is that they tend to take an Evidence parameter which was used to resolve against CAS policy and provide a grant set for an assembly.  For instance:

  • Activator.CreateInstance and Activator.CreateInstanceFrom overloads which take an Evidence parameter
  • AppDomain.CreateInstance, AppDomain.CreateInstanceFrom, AppDomain.CreateInstanceAndUnwrap, and AppDomain.CreateInstanceAndUnwrap overloads which take an Evidence parameter
  • AppDomain.DefineDynamicAssembly overloads which take an Evidence parameter
  • AppDomain.ExecuteAssembly and AppDomain.ExecuteAssemblyByName overloads which take an Evidence parameter
  • AppDomain.Load and AppDomain.LoadFrom overloads which take an Evidence parameter
  • Assembly.Load and Assembly.LoadFrom overloads which take an Evidence parameter

It’s important to note that although these APIs all take Evidence parameters, the concept of Evidence itself is not deprecated and continues to exist (and even enhanced in v4.0 – but that’s another show).  Evidence itself is still a useful tool for hosts to use when figuring out what grant sets they want to give assemblies.  The common thread with these APIs is that they used the Evidence to resolve against CAS policy – and it’s the CAS policy portion that’s been deprecated in v4.

Let’s say that your application is using one of the Evidence-taking overloads of these APIs, and thus had an implicit dependency on CAS policy.  Figuring out what to replace the API call with depends upon what your application was trying to accomplish with the API call.

We’ve found that commonly the goal of calling one of these APIs was not to sandbox the assembly being loaded, but rather to access other parameters on the overload which may not be available without also providing Evidence.  In these cases, you can go ahead and just drop the Evidence parameter from the API.  We’ve ensured that all of the above APIs now have overloads that provide the full set of parameters without requiring an Evidence parameter.

Additionally, in many cases we’ve found that code passes in Assembly.GetExecutingAssembly().Evidence or simply null to the Evidence parameter.  In both of those cases, it’s safe to simply call an overload of the API which does not require an Evidence parameter as well.

The other reason to provide Evidence when calling these APIs is to sandbox the assembly in question.  The correct way to do this in v4 (and the best way to do this in v2.0 and higher of the .NET Framework) is to simply load the assembly into a simple sandboxed AppDomain.  The assembly will then be sandboxed by virtue of the fact that it’s loaded in the sandboxed domain, and you will no longer need to load the assembly with an Evidence parameter to restrict its grant set.

I’ve listed the benefits of using simple sandboxed domains before, and they continue to apply in this scenario.  For example, using a simple sandbox rather than an Evidence resolve to sandbox assemblies allows your application:

  • To be in charge of its own sandbox.  The load-with-Evidence route took a dependency on what the grant set that the CLR would give the assembly was.  That grant set could change from version to version of the CLR (since each version has independent CAS policies), and even from user to user.  This makes supporting your application more difficult than it needs to be – with simple sandboxing there are no external dependencies for grant set resolution – your application is in charge of its own sandboxes
  • To setup real isolation boundaries – hosting multiple levels of partial trust code within a single AppDomain turns out to be incredibly difficult to do correctly.  Further, hosting partial trust code in a domain wtih full trust code that does not expect to be run along with partial trust code also turns out to be problematic from a security perspective.  By isolating the partial trust code in its own sandboxed domain, a real isolation boundary is setup for the code and your application is kept much more secure by default.
  • To have version and bitness independence – I touched on this in the first point, but to reiterate it, your application is no longer dependent upon each version of the CLR’s security policy to be setup in the same way, as well as each bitness of the policy within a single version.

So, to summarize, if you’re using one of the Evidence taking APIs which would have resolved an assembly’s grant set against CAS policy in the past:

Use Replacement
Passing null, Assembly.GetExecutingAssembly().Evidence, or AppDomain.CurrentDomain.Evidence Call an overload which does not require an Evidence parameter.
Using a parameter of the API which was only available on an overload taking an Evidence parameter as well. Call one of the newly added overloads which provides access to your parameter without requiring Evidence.
Sandboxing the assembly being loaded. Load the assembly into a sandboxed AppDomain, and let the domain do the sandboxing.  This will remove the need for the Evidence parameter.

Next time, I’ll look at the explicit uses of CAS policy, and what their replacements should be.