Ridicilous!

  • Thread starter Thread starter Ayende Rahien
  • Start date Start date
Stay away from ACL based File access checks in user applications, they are
an order of magnitude slower than exception.

Willy.
 
Jon said:
The first exception thrown (of some types) *can* be slow, depending on
what resources need to be loaded. After that, they're pretty fast. I
seem to remember my box is able to throw about a hundred thousand
exceptions a second. Are you really expecting to fail to write to
*that* many files?

If you're worried about the first exception being slow, you could
always start a new thread at the start of program execution so that it
gets that over and done with in the background - then you won't face
the performance hit later on if you get an exception for some other
reason.

Oh, well, I think that I don't have a choice in the matter.
I will simply cache the exception's result and live with the inefficency.
 
I'm the "author" of this example, but be warned, you should never ever use
this to check NTFS file permissions in user applications (other than FS
management applications), in order to determine whether you have read, write
or whatever access to a file.
Just open the file in the mode you need and let the filesystem do it's job,
all you need to do is handle the exception thrown.

Willy.

Michael Bray said:
<rant warning="I'm pissed off and have to vent" request="need help>
For the last couple of hours I'm struggling with a very *annoying*
problem. How to check if a user has a write access to a file?
Considerring that .Net is supposed to be a system for writing
applications for servers & clients, which in both cases has may have
multi-users, I'm amazed that this is not possible in the framework.
Initially I looked for something like:

System.IO.File.Access(string Filename, PermissionAccess);

Nothing!


I happened to run across some code in this very same newsgroup asking
basically the same question - I'll repost it for you assuming that you
didn't see it. This isn't my code and I haven't tested it, so use at your
own risk... I've included a few of the headers as well.

------------------

Subject: Re: Get ACL
From: "Willy Denoyette [MVP]" <[email protected]>
Newsgroups: microsoft.public.dotnet.languages.csharp

You shouldn't use "unsupported" stuff like Win32Security.dll, use the
System.DirectoryServices (XP and higher) or System.Management namespace
instead.
Next is a complete example illustrating how to dump the ACE's from a File
object DACL using System.Management classes.

using System;
using System.Management;
using System.Collections;
// Access mask (see AccessMask property)
[Flags]
enum Mask : uint
{
FileReadData = 0x00000001,
FileWriteData = 0x00000002,
FileAppendData = 0x00000004,
FileReadEA = 0x00000008,
FileWriteEA = 0x00000010,
FileExecute = 0x00000020,
FileDeleteChild = 0x00000040,
FileReadAttributes = 0x00000080,
FileWriteAttributes= 0x00000100,

Delete = 0x00010000,
ReadControl = 0x00020000,
WriteDac = 0x00040000,
WriteOwner = 0x00080000,
Synchronize = 0x00100000,

AccessSystemSecurity = 0x01000000,
MaximumAllowed = 0x02000000,

GenericAll = 0x10000000,
GenericExecute= 0x20000000,
GenericWrite = 0x40000000,
GenericRead = 0x80000000
}
[Flags]
enum AceFlags : int
{
ObjectInheritAce = 1,
ContainerInheritAce = 2,
NoPropagateInheritAce = 4,
InheritOnlyAce = 8,
InheritedAce = 16
}

[Flags]
enum AceType : int
{
AccessAllowed = 0,
AccessDenied = 1,
Audit = 2
}
class Tester {
public static void Main() {
string fileObject = @"c:\\pipo\\t.txt"; // Watch the double Backslashes
using(ManagementObject lfs = new
ManagementObject(@"Win32_LogicalFileSecuritySetting.Path=" + "'" +
fileObject + "'"))
{
// Get the security descriptor for this object
// Dump all trustees (this includes owner)
ManagementBaseObject outParams =
lfs.InvokeMethod("GetSecurityDescriptor", null, null);
if (((uint)(outParams.Properties["ReturnValue"].Value)) == 0) // if
success
{
ManagementBaseObject secDescriptor =
((ManagementBaseObject)(outParams.Properties["Descriptor"].Value));
//The DACL is an array of Win32_ACE objects.
ManagementBaseObject[] dacl =
((ManagementBaseObject[])(secDescriptor.Properties["Dacl"].Value));
DumpACEs(dacl);

}
}
}

static void DumpACEs(ManagementBaseObject[] dacl)
{
foreach(ManagementBaseObject mbo in dacl){
Console.WriteLine("\n---------\nMask: {0:X} - Flags: {1} - Type: {2}",
mbo["AccessMask"], mbo["AceFlags"], mbo["AceType"]);
// Access allowed/denied ACE
if(Convert.ToInt32(mbo["AceType"]) == (int)AceType.AccessDenied)
Console.WriteLine("DENIED ACE TYPE");
else
Console.WriteLine("ALLOWED ACE TYPE");
// Dump trustees
ManagementBaseObject Trustee = ((ManagementBaseObject)(mbo["Trustee"]));
Console.WriteLine("Name: {0} - Domain: {1} - SID {2}\n",
Trustee.Properties["Name"].Value,
Trustee.Properties["Domain"].Value,
Trustee.Properties["SIDString"].Value);
// Dump ACE mask in readable form
UInt32 mask = (UInt32)mbo["AccessMask"];
Console.WriteLine(Enum.Format(typeof(Mask), mask, "g"));
}
}
}
 
First, to check the ACL's the NTFS Filesystem subsystem has to read them
from the filesystem object (File or Directory), and return a
Securitydescriptor (SD) to the caller.
Then you (the caller) have to scan each ACE in the DACL contained in the SD,
for the callers SID, if an entry is found with his SID, you can check the
required access privilege from this entry (but you are not done yet).
If the direct SID is not found in one of the ACE's, you have to check
whether the caller's SID is a member of the group SID's, if so, check the
required access privilege from this (group) entry. This step is extremely
expensive has you have to check for group membership on the local SD
database (for local groups) or the DOMAIN Security database (for global
groups).

Now, you need to scan the SACL exactly the same way as the DACL, if the
required access is explicitly denied, you cannot use the result obtained in
previous steps, else you can use the result obtained from previous steps.
(Well actually you need to to the SACL scan first, but I hope you got the
point)
Willy.
 
Evan Freeman said:
Heyas

FileIOPermission is a Code Access Security permission, not a Windows
role-based security permission. What you want is the ability to examine
the
security descriptor on the file and examine the rights of the various
Windows trustees in the entries in the DACL.

There are a couple of ways to do this. You could use COM interop and the
IADsSecurityUtility interface in ADSI, or you could use WMI, or use
the Win32 security .NET wrapper on GotDotNet that talks directly to the
Windows APIs.

Hint: The wrapper is the one you want.

-Enjoy!!

-Evan


While you can use both System.Management (WMI) and System.Directory (and
ADSI interop) to perform access checks on File objects, I would suggest you
never do this in user applications other than "FS management" (ACLEDIT)
style applications.

Note:the upcoming version of the framework will have this all included in
the System.Security.AccessCheck namespace, but using the new classes is just
as complex as using System.Management (WMI) and System.Directory (and ADSI),
just because ACL programming is really a complex matter.

Willy.
 
Ayende Rahien said:
It's a very small utility, and I'm going to do a very intensive use of it.
Exceptions are *slow*, really(!!) slow.

I simply question the need for your utility. What would happen to your
application if you write your access check like this:

public bool CheckAccess(FileInfo fi)
{
return true;
}

What would probably happen is that most of your write accesses would
succeed, but that some would fail and throw an exception. This is the way
to go. Let the operating system tell you whether or not you can write to a
particular file. In fact, if you go checking ACLs, you could find that you
have write access to a file but that you cannot write to it at all. This
could be because your users disk quota is exhausted, because the disk is
full, because the disk is write-locked, or for any other reason you've never
heard of and can't imagine. The OS has a better imagination than you do. :-)
 
Back
Top