views:

382

answers:

3

Hi,

In this code, I'd like the ReadFileSystem method to be forbidden to Assert a permission on the filesystem.

I expected this will throw at fileIo.Assert(), but it doesn't. Why?

using System.Security.Permissions;
static void Main(string[] args)
{
    var fileIo = new FileIOPermission(PermissionState.Unrestricted);
    var secuPerm = new SecurityPermission(SecurityPermissionFlag.Assertion);
    PermissionSet set = new PermissionSet(PermissionState.Unrestricted);
    set.AddPermission(fileIo);
    set.AddPermission(secuPerm);
    set.Deny();
    ReadFileSystem();
    Console.Read();
}

private static void ReadFileSystem()
{
    var fileIo = newFileIOPermission(PermissionState.Unrestricted);
    fileIo.Assert();

    DirectoryInfo dir = new DirectoryInfo("C:/");
    dir.GetDirectories();
}

Update

Great link here on CAS : http://blogs.msdn.com/shawnfa/archive/2004/08/25/220458.aspx

+1  A: 

Good question for sure. I ran the code and am a bit mystified too. I just spend a hour or 2 studying the docs and googling but to no avail. Note that MSDN cops out by doing a Demand on the Assertion permission.

Edit: binarycoder's answer pointed me into the right direction.

The Asserion rights are simply not effective in a Full-thrust assembly. Only if you add the following at the top of the file:

[assembly: SecurityPermissionAttribute(SecurityAction.RequestRefuse, Assertion = true)]

the call to fileIo.Assert() will fail.

But otherwise, trying to Deny() the Assertion permission and the following attribute are totally ineffective (unless you explicitly Demand() the Assertion rights).

[SecurityPermissionAttribute(SecurityAction.Deny, Assertion = true)]
private static void ReadFileSystem() {}

The documentation does state that Assert() 'has some security issues' and the recommendation is to always Demand-before-Assert:

fileIo.Demand();
fileIo.Assert();

but I still have to re-adjust my mindset about applying the principle of least privilege.

Henk Holterman
Why should we Demand and assert after ?Can you give me your source ? Because if Demand is Ok, I don't understand why we should do an assert after. I remembered reading something about this pattern, it was to convert permissions (IOPermission + SecurityPermission => CustomPermission for example).
Nicolas Dorier
The only source I've gotr is a powerpoint: http://www.bristowe.com/presentations/Code%20Access%20Security.pptBasically, the Demand is your check and the Assert an speed-up for downstream code.
Henk Holterman
+7  A: 

The subsequent Assert negates the effects of the Deny.

The ability to assert FileIOPermission mainly depends on whether your assembly is trusted. It is not affected by a previous Deny of FileIOPermission. It turns out that it is also not affected by the previous Deny of the Assertion SecurityPermission. This is because SecurityPermissionFlag.Assertion is checked as a link time demand. This is not clearly documented; I found it here.

To force the CLR to not trust your assembly for FileIOPermission, you can use the following at the top of your file following the using statements. When you add this to your file, the assert will not take effect. This affects the entire assembly. There is no finer granularity.

[assembly:FileIOPermission(SecurityAction.RequestRefuse, Unrestricted=true)]
binarycoder
binarycoder is exactly right. .NET rarely Asserts internally... It usually Demands. Demand is what does the stack walk that would find the deny on the stack and fail. For all file IO .NET does a Demand. But as soon as it finds an Assert on the stack the stack walk is stopped short.
Andrew Arnott
And as binarycoder suggested, the only way to keep Assert from doing its work is to deny the entire assembly the permissions you are trying to assert, effectively making your assembly run with partial trust. Partial trust is good, but if you don't want to be able to call Assert, just don't call it?
Andrew Arnott
@Andrew, I wanted to be sure that any call after my Assert can not access the hard drive. Because my use case was to call another assembly via reflection (not trusted), without having to configure the evidence when I load it. Finally, it's what I've done. But this behavior of CAS intrigued me.
Nicolas Dorier
+2  A: 

I think you may misunderstand the purpose of Asserting permissions. When you Assert a permission set in CAS, you are effectively saying "I know what I'm doing... I don't care what the permissions are deeper in the stack." That's almost never what you want. You generally want to Demand a permission set. That causes a stack walk, which would find the Deny on the stack and then cause a security exception.

However, since .NET has virtually all the necessary Demands built-in, there's rarely a need to actually Demand anything unless you're doing Asserts (again, that's rare) or you've written your own custom Permission class.

Andrew Arnott
Yes I know that, but I thought that when we do Assert, there was a stack walk for the SecurityPermission. (The MSDN says clearly that you can assert if your assembly has the permission on the asserted permission AND that you have SecurityPermission granted on your assembly).
Nicolas Dorier
Neither of those things that MSDN says occurs requires a stack walk.
Andrew Arnott
Sure, it was my mistake. But your assembly should be allowed with the SecurityPermission and the Assertion flag. (In the config of .NET or within your assembly code like binary coder said)
Nicolas Dorier
See http://blogs.msdn.com/shawnfa/archive/2004/08/25/220458.aspx Myth #3
Nicolas Dorier