Assembly Level Declarative Security
Assembly level declarative security comes in three forms, RequestMinimum, RequestOptional, and RequestRefuse. The three can be briefly defined as:
- RequestMinimum -- the set of permissions that are absolutely required for this assembly to run
- RequestOptional -- the set of permissions that would be nice to run with, but are not required
- RequestRefuse -- a set of permissions that should never be granted to the assembly
Each of these actions can be applied on multiple attributes in each assembly, the final set of permissions in the action will be the union of all of the attributes with the given action.
Most people intuitively understand RequestRefuse. Any permissions that are in this set are added to the assembly's refused set, and the assembly will never execute with these permissions granted. The other two can become a bit dicey however, especially in their side effects. In order to demonstrate RequestMinimum and RequestOptional, I'll use the following sample code, and apply different assembly-level declarative security to it.
public static class DeclarativeSecurity
{
public static void Main()
{
Console.WriteLine("In Main");
if(HaveFileIO())
Console.WriteLine("Have full FileIO permission");
else
Console.WriteLine("Do not have full FileIO permission");
}
private static bool HaveFileIO()
{
try
{
new FileIOPermission(PermissionState.Unrestricted).Demand();
}
catch(SecurityException)
{
return false;
}
return true;
}
}
RequestMinimum tells the CLR that your code cannot run without the given set of permissions. So, if I were to attach the following attribute to my assembly:
[assembly: FileIOPermission(SecurityAction.RequestMinimum, Unrestricted = true)]
And run it from the Intranet zone, the code would never execute. Since the CLR sees that this assembly cannot run without unrestricted FileIO permission, and the Intranet permission set won't grant it that permission set, the loader refuses to even load the assembly. Instead, you'll see a FileLoadException with an inner PolicyException:
Unhandled Exception: System.IO.FileLoadException: Could not load file or assembly 'ReqMin, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' or one of itsdependencies. HRESULT 0x80131417 (CORSEC_E_MIN_GRANT_FAIL) Failed to grant minimum permission requests.
File name: 'ReqMin, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' --->
System.Security.Policy.PolicyException: Required permissions cannot be acquired.
at System.Security.SecurityManager.ResolvePolicy(Evidence evidence, PermissionSet reqdPset, PermissionSet optPset, PermissionSet denyPset, PermissionSet& denied, Boolean checkExecutionPermission)
at System.Security.SecurityManager.ResolvePolicy(Evidence evidence, PermissionSet reqdPset, PermissionSet optPset, PermissionSet denyPset, PermissionSet& denied, Int32& grantedIsUnrestricted, Boolean checkExecutionPermission)
RequestOptional has a different effect. Changing the security attribute to:
[assembly: FileIOPermission(SecurityAction.RequestOptional, Unrestricted = true)]
And running from the Intranet zone, I'll now see:
In Main
Do not have full FileIO permission
So the application loaded and ran, but was not granted FileIO permission. This is what I would expect, since Intranet assemblies are not granted unrestricted FileIO, however the RequestOptional action told the CLR that my code would still be able to run without these permissions. Generally code that works with RequestOptional tends to try a privileged operation in a try ... catch(SecurityException) block, and fall back to alternate behavior if the optional permissions are not granted.
So far, so good. However, now we come to the interesting side effect, one that oftentimes confuses people new to assembly level declarative security. Namely, using any declarative security at all has the potential to decrease the permission set that your assembly is running with. This is obvious in the case of RequestRefuse, however with RequestMinimum and RequestOptional, the reasoning is far less clear.
Internally, the CLR takes the union of the RequestMinimum and RequestOptional sets (and adds to that execution permission), and that resulting set is the only set of permissions that your application will have when it's running. For instance, demanding unrestricted Registry permission will fail if either of the above declarative requests are in place.
The reasoning behind this is that the CLR takes RequestMinimum and RequestOptional to be the complete set of permissions that your assembly requires to run. If you think about what you're saying to the runtime, this makes a lot of sense. In effect you're saying "here's the minimum set of permissions I require to run, but I'll also provide enhanced behavior with these other permissions." The combination of those sets should result in every permission your assembly requires. Limiting the set of permissions that an assembly is running with increases the security of your application, since there's now no way that your code can access resources that it doesn't need access to. In the above example, there would be no way for an attacker to trick my code into, say, accessing the registry.
Because of the above, its often better to over-estimate the permissions you need (ask for Unrestricted FileIO rather than just FileIO to a specific path), since if you under-estimate users of your application will run into SecurityExceptions. In fact, the FileIO case mentioned above is one instance where you often do have to over-estimate, because generally your application won't know what the full path to the file it wants to work with is until runtime. Additionally, it's important to note that these restrictions are per-assembly. The only way that the restricted grant to this assembly could affect other assemblies in my application is if a method from this assembly happened to be on a call stack when a demand took place.
As if this wasn't getting confusing enough already, there's one more sticky point that can trap you. It's important to realize that the defaults for the various permission sets are:
- RequestMinimum -- Nothing
- RequestOptional -- FullTrust
- RequestRefuse -- Nothing
Since the permission set restriction is calculated by taking the minimum request and unioning with the optional request, and the default optional request is FullTrust, unless an Optional request is made, the union of the minimum request and the FullTrust request will result in a FullTrust set. What this boils down to is that unless you specify a RequestOptional set, the permissions your application runs under will not be limited in the fashion stated above.
A common question that arises after people start to understand that they don't get any more permissions than they specifically ask for is what use is RequestRefuse? It seems silly for me to RequestRefuse RegistryPermission if I do a RequestOptional on FileIO permission since the refusal will have no net effect. Generally, RequestRefuse is there for the times when it's easier to say what permissions you don't need than it is to specifically call out the permissions that you do need.
Update: Added information about defaults in order to clarify the example.
Comments
Anonymous
August 30, 2004
The comment has been removedAnonymous
August 30, 2004
Good catch -- I was actually in the process of updating the entry when you posted this. I'd forgotten to point out the default values of each set, which can lead to the confusion that you mention.
-ShawnAnonymous
September 06, 2004
The comment has been removedAnonymous
September 06, 2004
OK, I think I understand.
In fact the "Request" security actions act as a filter on the code group policy.
Everythink that don't pass the filter is ignore. So I can't grant myself permissions that security policy would refuse me.Anonymous
September 06, 2004
The comment has been removedAnonymous
September 06, 2004
Ooops! Wrong link...
Here's the right one:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpcondetermininggrantedpermissions.aspAnonymous
September 07, 2004
Hi Alfred,
Sorry for the confusing code snippet above, I'll update it. You're right that without a RequestOptional your RequestRefuse will have no affect. I've covered that in my section about defaults:
<Quote>Since the permission set restriction is calculated by taking the minimum request and unioning with the optional request, and the default optional request is FullTrust, unless an Optional request is made, the union of the minimum request and the FullTrust request will result in a FullTrust set. What this boils down to is that unless you specify a RequestOptional set, the permissions your application runs under will not be limited in the fashion stated above.</Quote>
You're also right that this does not provide a way to increase your security. If you say RequestMinimum for FullTrust, and the CLR won't grant you FullTrust through the CAS policy, your assembly won't even load. This is only a way to restrict your permission sets.
-ShawnAnonymous
September 07, 2004
The comment has been removedAnonymous
September 08, 2004
Hi Alfred,
RequestMinimum actually does have the potential to reduce your permission set. Like I said above, the set that you'll get is basically:
(Grant intersect (RequestMinimum union RequestOptional) ) minus RequestRefuse
Now, we need to add in the default values of each item to see how things really work. By default RequestMinimum is empty, and RequestOptional is the FullTrust set. Therefore that union produces FullTrust, intersected with the granted set produces the same grant set. Subtract out the refused set (by default the emtpy set), and you can see how by default we end up with the granted set by default.
Now, lets move on to your sample. You keep RequestRefuse empty, so we can drop that part of the equation. Next, you up RequestMinimum to FileIO, but don't touch RequestOptional which stays at FullTrust. The union of FileIO and FullTrust is FullTrust. Intersect that with the grant set, and you'll get the grant set back.
In the second part of your sample you reduce the RequestOptional set to be FileIO, and keep RequestMinimum at its default empty state. Now the union of FileIO and empty is FileIO. Intersect that with the granted set, and you'll end up with just FileIO.
Does that help clarify things a bit? If you want to further demonstrate this, change your sample to RequestMinimum for UI, and RequestOptional for something unrelated, say Registry. You'll see that you get an exception when you demand the FileIO permissions.
-ShawnAnonymous
September 08, 2004
Hi Shawn,
<Quote>RequestMinimum actually does have the potential to reduce your permission set. Like I said above, the set that you'll get is basically:</Quote>
We agree in all but the statement above.
Since the default value of RequestMinimum is Nothing anything you put there (except Nothing) will be more than Nothing.
Since RequestOptional defaults to FullTrust, anything you put there (except FullTrust it self) will be less than FullTrust.
AlfredAnonymous
September 08, 2004
The comment has been removedAnonymous
May 27, 2005
Speaking of dynamic IL generation ...
Before Whidbey, the framework supplied two ways of creating code...Anonymous
September 14, 2005
Another interesting question arose today.&nbsp; An assembly was granted FullTrust by policy, which was...Anonymous
August 06, 2006
I am getting following error when using the fileIO permisson
See the end of this message for details on invoking
just-in-time (JIT) debugging instead of this dialog box.
************** Exception Text
System.Security.SecurityException: Request failed.
at Act.Framework.ActFramework.FailLogOn(Exception ex, TraceCategory tCat, TraceLevel tLevel)
at Act.Framework.ActFramework.LogOn(String userName, String password, String databaseType, String databaseHost, String databaseName, Boolean fireEvents, Boolean suppressTierCheck, Boolean suppressSchemaCheck, Boolean suppressLicenseCheck, Boolean allowTrialModeDeparture)
at Act.Framework.ActFramework.LogOn(String userName, String password, String databaseType, String databaseHost, String databaseName, Boolean fireEvents)
at Act.Framework.ActFramework.LogOn(String userName, String password, String databaseType, String databaseHost, String databaseName)
at Act.Framework.ActFramework.LogOn(String xmlPADFile, String userName, String password)
at UserControl.UserControl1.LoginToACTDatabase()
at UserControl.UserControl1.button1_Click(Object sender, EventArgs e)
at System.Windows.Forms.Control.OnClick(EventArgs e)
at System.Windows.Forms.Button.OnClick(EventArgs e)
at System.Windows.Forms.Button.OnMouseUp(MouseEventArgs mevent)
at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
at System.Windows.Forms.Control.WndProc(Message& m)
at System.Windows.Forms.ButtonBase.WndProc(Message& m)
at System.Windows.Forms.Button.WndProc(Message& m)
at System.Windows.Forms.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
The granted set of the failing assembly was:
<PermissionSet class="System.Security.PermissionSet"
version="1">
<IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
version="1"
Unrestricted="true"/>
<IPermission class="System.Security.Permissions.FileDialogPermission, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
version="1"
Unrestricted="true"/>
<IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
version="1"
Unrestricted="true"/>
<IPermission class="System.Security.Permissions.IsolatedStorageFilePermission, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
version="1"
Unrestricted="true"/>
<IPermission class="System.Security.Permissions.ReflectionPermission, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
version="1"
Unrestricted="true"/>
<IPermission class="System.Security.Permissions.RegistryPermission, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
version="1"
Unrestricted="true"/>
<IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
version="1"
Flags="Assertion, UnmanagedCode, Execution, ControlThread, ControlEvidence, ControlPolicy, SerializationFormatter, ControlDomainPolicy, ControlPrincipal, ControlAppDomain, RemotingConfiguration, Infrastructure, BindingRedirects"/>
<IPermission class="System.Security.Permissions.UIPermission, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
version="1"
Unrestricted="true"/>
<IPermission class="System.Net.DnsPermission, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
version="1"
Unrestricted="true"/>
<IPermission class="System.Drawing.Printing.PrintingPermission, System.Drawing, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
version="1"
Unrestricted="true"/>
<IPermission class="System.Diagnostics.EventLogPermission, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
version="1"
Unrestricted="true"/>
<IPermission class="System.Net.SocketPermission, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
version="1"
Unrestricted="true"/>
<IPermission class="System.Net.WebPermission, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
version="1"
Unrestricted="true"/>
<IPermission class="System.Diagnostics.PerformanceCounterPermission, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
version="1"
Unrestricted="true"/>
<IPermission class="System.DirectoryServices.DirectoryServicesPermission, System.DirectoryServices, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
version="1"
Unrestricted="true"/>
<IPermission class="System.Messaging.MessageQueuePermission, System.Messaging, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
version="1"
Unrestricted="true"/>
<IPermission class="System.ServiceProcess.ServiceControllerPermission, System.ServiceProcess, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
version="1"
Unrestricted="true"/>
<IPermission class="System.Data.OleDb.OleDbPermission, System.Data, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
version="1"
Unrestricted="true"/>
<IPermission class="System.Data.SqlClient.SqlClientPermission, System.Data, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
version="1"
Unrestricted="true"/>
<IPermission class="System.Security.Permissions.SiteIdentityPermission, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
version="1"
Site="localhost"/>
<IPermission class="System.Security.Permissions.UrlIdentityPermission, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
version="1"
Url="http://localhost/"/>
<IPermission class="System.Security.Permissions.ZoneIdentityPermission, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
version="1"
Zone="Trusted"/>
</PermissionSet>
Loaded Assemblies
mscorlib
Assembly Version: 1.0.5000.0
Win32 Version: 1.1.4322.2032
CodeBase: file:///c:/windows/microsoft.net/framework/v1.1.4322/mscorlib.dll
----------------------------------------
System
Assembly Version: 1.0.5000.0
Win32 Version: 1.1.4322.2032
CodeBase: file:///c:/windows/assembly/gac/system/1.0.5000.0__b77a5c561934e089/system.dll
----------------------------------------
System.Drawing
Assembly Version: 1.0.5000.0
Win32 Version: 1.1.4322.2032
CodeBase: file:///c:/windows/assembly/gac/system.drawing/1.0.5000.0__b03f5f7f11d50a3a/system.drawing.dll
----------------------------------------
System.DirectoryServices
Assembly Version: 1.0.5000.0
Win32 Version: 1.1.4322.2032
CodeBase: file:///c:/windows/assembly/gac/system.directoryservices/1.0.5000.0__b03f5f7f11d50a3a/system.directoryservices.dll
----------------------------------------
System.Messaging
Assembly Version: 1.0.5000.0
Win32 Version: 1.1.4322.2032
CodeBase: file:///c:/windows/assembly/gac/system.messaging/1.0.5000.0__b03f5f7f11d50a3a/system.messaging.dll
----------------------------------------
System.ServiceProcess
Assembly Version: 1.0.5000.0
Win32 Version: 1.1.4322.2032
CodeBase: file:///c:/windows/assembly/gac/system.serviceprocess/1.0.5000.0__b03f5f7f11d50a3a/system.serviceprocess.dll
----------------------------------------
System.Data
Assembly Version: 1.0.5000.0
Win32 Version: 1.1.4322.2032
CodeBase: file:///c:/windows/assembly/gac/system.data/1.0.5000.0__b77a5c561934e089/system.data.dll
----------------------------------------
RegexAssembly18_0
Assembly Version: 0.0.0.0
Win32 Version: n/a
CodeBase:
----------------------------------------
UserControl
Assembly Version: 1.0.0.0
Win32 Version: n/a
CodeBase: http://localhost/ActiveXTestWeb/UserControl.dll
----------------------------------------
System.Windows.Forms
Assembly Version: 1.0.5000.0
Win32 Version: 1.1.4322.2032
CodeBase: file:///c:/windows/assembly/gac/system.windows.forms/1.0.5000.0__b77a5c561934e089/system.windows.forms.dll
----------------------------------------
Act.Framework
Assembly Version: 8.2.82.0
Win32 Version: 8.2.82.0
CodeBase: file:///c:/windows/assembly/gac/act.framework/8.2.82.0__ebf6b2ff4d0a08aa/act.framework.dll
----------------------------------------
Act.Shared.Diagnostics
Assembly Version: 8.2.82.0
Win32 Version: 8.2.82.0
CodeBase: file:///c:/windows/assembly/gac/act.shared.diagnostics/8.2.82.0__ebf6b2ff4d0a08aa/act.shared.diagnostics.dll
----------------------------------------
Act.Shared.Utilities
Assembly Version: 8.2.82.0
Win32 Version: 8.2.82.0
CodeBase: file:///c:/windows/assembly/gac/act.shared.utilities/8.2.82.0__ebf6b2ff4d0a08aa/act.shared.utilities.dll
----------------------------------------
Act.Shared.Config
Assembly Version: 8.2.82.0
Win32 Version: 8.2.82.0
CodeBase: file:///c:/windows/assembly/gac/act.shared.config/8.2.82.0__ebf6b2ff4d0a08aa/act.shared.config.dll
----------------------------------------
System.Xml
Assembly Version: 1.0.5000.0
Win32 Version: 1.1.4322.2032
CodeBase: file:///c:/windows/assembly/gac/system.xml/1.0.5000.0__b77a5c561934e089/system.xml.dll
----------------------------------------
Microsoft.mshtml
Assembly Version: 7.0.3300.0
Win32 Version: 7.0.3300.0
CodeBase: file:///c:/windows/assembly/gac/microsoft.mshtml/7.0.3300.0__b03f5f7f11d50a3a/microsoft.mshtml.dll
----------------------------------------
Accessibility
Assembly Version: 1.0.5000.0
Win32 Version: 1.1.4322.573
CodeBase: file:///c:/windows/assembly/gac/accessibility/1.0.5000.0__b03f5f7f11d50a3a/accessibility.dll
----------------------------------------
Act.Shared.Licensing
Assembly Version: 8.2.82.0
Win32 Version: 8.2.82.0
CodeBase: file:///c:/windows/assembly/gac/act.shared.licensing/8.2.82.0__ebf6b2ff4d0a08aa/act.shared.licensing.dll
----------------------------------------
Act.Data
Assembly Version: 8.2.82.0
Win32 Version: 8.2.82.0
CodeBase: file:///c:/windows/assembly/gac/act.data/8.2.82.0__ebf6b2ff4d0a08aa/act.data.dll
----------------------------------------
Act.Data.ActDb
Assembly Version: 8.2.82.0
Win32 Version: 8.2.82.0
CodeBase: file:///c:/windows/assembly/gac/act.data.actdb/8.2.82.0__ebf6b2ff4d0a08aa/act.data.actdb.dll
----------------------------------------
Act.Data.Resources
Assembly Version: 8.2.82.0
Win32 Version: 8.2.82.0
CodeBase: file:///c:/windows/assembly/gac/act.data.resources/8.2.82.0__ebf6b2ff4d0a08aa/act.data.resources.dll
----------------------------------------
System.EnterpriseServices
Assembly Version: 1.0.5000.0
Win32 Version: 1.1.4322.2032
CodeBase: file:///c:/windows/assembly/gac/system.enterpriseservices/1.0.5000.0__b03f5f7f11d50a3a/system.enterpriseservices.dll
----------------------------------------
Act.Shared.LicProvider
Assembly Version: 8.2.82.0
Win32 Version: 8.2.82.0
CodeBase: file:///c:/windows/assembly/gac/act.shared.licprovider/8.2.82.0__ebf6b2ff4d0a08aa/act.shared.licprovider.dll
----------------------------------------
Act.Shared.Windows.Forms
Assembly Version: 8.2.82.0
Win32 Version: 8.2.82.0
CodeBase: file:///c:/windows/assembly/gac/act.shared.windows.forms/8.2.82.0__ebf6b2ff4d0a08aa/act.shared.windows.forms.dll
----------------------------------------
JIT Debugging **************
To enable just in time (JIT) debugging, the config file for this
application or machine (machine.config) must have the
jitDebugging value set in the system.windows.forms section.
The application must also be compiled with debugging
enabled.
For example:
<configuration>
<system.windows.forms jitDebugging="true" />
</configuration>
When JIT debugging is enabled, any unhandled exception
will be sent to the JIT debugger registered on the machine
rather than being handled by this dialog.Anonymous
January 12, 2007
I've read ( http://www.bluevisionsoftware.com/WebSite/TipsAndTricksDetails.aspx?Name=PermissionRequests ) that the following attribute means "... makes an optional request for no permission set. This means that no permission set will be granted to the code by the code access security policy. All permissions that should be granted must be requested using additional assembly-level attributes.": [assembly: PermissionSet(SecurityAction.RequestOptional, Unrestricted = false)] I'm having a hard time seeing why it has that effect. Likewise with each of the other variations (as applied individually to an assembly); I can't figure out what they mean and why: [assembly: PermissionSet(SecurityAction.RequestOptional, Unrestricted = true)] [assembly: PermissionSet(SecurityAction.RequestMinimum, Unrestricted = false)] [assembly: PermissionSet(SecurityAction.RequestMinimum, Unrestricted = true)] [assembly: PermissionSet(SecurityAction.RequestRefuse, Unrestricted = false)] [assembly: PermissionSet(SecurityAction.RequestRefuse, Unrestricted = true)] I'd be very grateful for some enlightenment on this - it's been puzzling me for months. There seems to be lots of information about using RequestOptional/Minimimum/Refuse with Unrestricted/true/false on specific permissions but not on "no permission sets".Anonymous
January 17, 2007
Hi Graham, PermissionSet(Unrestricted = true) means FullTrust, so that's esentially doing the declarative action on every permission. If you were to do it on PermissionSet(Unrestricted = false), that's the empty permission set, so you can translate it to doing the corresponding action on no permissions. The only action that PermissionSet(Unrestricted = false) will have an affect on is RequestOptional since that defaults to FullTrust, and you're overriding it with Nothing. -ShawnAnonymous
January 19, 2007
Hi Shawn, thank you so much for clearing that up for me. Graham.Anonymous
February 20, 2007
The story so far … And now for more of the adventures of Jack Bauer! ;). So since I posted the first