compare windows file (or folder) permissions - c#

I occasionally migrate some website from one web server to another.
After copying all files from the old server to the new server, it takes me quite some time to get (re)acquainted with which folders or files need to be writable by IIS. (Sounds familiar, by the way ? :) )
I have written a WinForms application that allows me to select a starting directory. The application should (recursively) compare if the security permissions of each file/directory are equal to that of its parent directory.
I want to use this application on the old server to scan for directories with different permissions.
Example: C:\MySites\Uploads does not have the same permissions set as its parent directory. (This folder was writable for the IIS user 'IUSR', while its parent folder was only readable.)
The application is almost complete in the sense that I manage to traverse all directories and files. I just need to compare their permissions!
Can you please help? Here is an excerpt of where I need your help.
string results = "";
string parentFolderPath = "c:\\someParentDir";
string childItemPath = "c:\\someParentDir\\SomeChildDir.ext";
DirectorySecurity parentFolderAccessControl = Directory.GetAccessControl(parentFolderPath);
DirectorySecurity childItemAccessControl = Directory.GetAccessControl(childItemPath);
if (!parentFolderAccessControl.Equals(childItemAccessControl)) // <-- D'oh here
{
results += childItemPath + " does not have the same permissions set as its parent directory.\n";
}
The if is always true, because the DirectorySecurities are never equal. (I understand why that is: reference to different memory allocations ... blah blah.) But what would be the best way to compare the DirectorySecurities?

This actually became much more complex while I was working it out, because Windows rights can:
split up into Allow and Deny
Fragmented over multiple entries (multiple entries per user per Allow/Deny)
Eventually, this is what I made out of it:
private bool compareAccessControls(
DirectorySecurity parentAccessControl,
DirectorySecurity childAccessControl,
out Dictionary<IdentityReference, FileSystemRights> accessAllowRulesGainedByChild,
out Dictionary<IdentityReference, FileSystemRights> accessDenyRulesGainedByChild,
out Dictionary<IdentityReference, FileSystemRights> accessAllowRulesGainedByParent,
out Dictionary<IdentityReference, FileSystemRights> accessDenyRulesGainedByParent
)
{
// combine parent access rules
Dictionary<IdentityReference, FileSystemRights> combinedParentAccessAllowRules = new Dictionary<IdentityReference, FileSystemRights>();
Dictionary<IdentityReference, FileSystemRights> combinedParentAccessDenyRules = new Dictionary<IdentityReference, FileSystemRights>();
foreach (FileSystemAccessRule parentAccessRule in parentAccessControl.GetAccessRules(true, true, typeof(NTAccount)))
{
if (parentAccessRule.AccessControlType == AccessControlType.Allow)
if (combinedParentAccessAllowRules.ContainsKey(parentAccessRule.IdentityReference))
combinedParentAccessAllowRules[parentAccessRule.IdentityReference] = combinedParentAccessAllowRules[parentAccessRule.IdentityReference] | parentAccessRule.FileSystemRights;
else
combinedParentAccessAllowRules.Add(parentAccessRule.IdentityReference, parentAccessRule.FileSystemRights);
else
if (combinedParentAccessDenyRules.ContainsKey(parentAccessRule.IdentityReference))
combinedParentAccessDenyRules[parentAccessRule.IdentityReference] = combinedParentAccessDenyRules[parentAccessRule.IdentityReference] | parentAccessRule.FileSystemRights;
else
combinedParentAccessDenyRules.Add(parentAccessRule.IdentityReference, parentAccessRule.FileSystemRights);
}
// combine child access rules
Dictionary<IdentityReference, FileSystemRights> combinedChildAccessAllowRules = new Dictionary<IdentityReference, FileSystemRights>();
Dictionary<IdentityReference, FileSystemRights> combinedChildAccessDenyRules = new Dictionary<IdentityReference, FileSystemRights>();
foreach (FileSystemAccessRule childAccessRule in childAccessControl.GetAccessRules(true, true, typeof(NTAccount)))
{
if (childAccessRule.AccessControlType == AccessControlType.Allow)
if (combinedChildAccessAllowRules.ContainsKey(childAccessRule.IdentityReference))
combinedChildAccessAllowRules[childAccessRule.IdentityReference] = combinedChildAccessAllowRules[childAccessRule.IdentityReference] | childAccessRule.FileSystemRights;
else
combinedChildAccessAllowRules.Add(childAccessRule.IdentityReference, childAccessRule.FileSystemRights);
else
if (combinedChildAccessDenyRules.ContainsKey(childAccessRule.IdentityReference))
combinedChildAccessDenyRules[childAccessRule.IdentityReference] = combinedChildAccessDenyRules[childAccessRule.IdentityReference] | childAccessRule.FileSystemRights;
else
combinedChildAccessDenyRules.Add(childAccessRule.IdentityReference, childAccessRule.FileSystemRights);
}
// compare combined rules
accessAllowRulesGainedByChild = new Dictionary<IdentityReference, FileSystemRights>();
foreach (KeyValuePair<IdentityReference, FileSystemRights> combinedChildAccessAllowRule in combinedChildAccessAllowRules)
{
if (combinedParentAccessAllowRules.ContainsKey(combinedChildAccessAllowRule.Key))
{
FileSystemRights accessAllowRuleGainedByChild = combinedChildAccessAllowRule.Value & ~combinedParentAccessAllowRules[combinedChildAccessAllowRule.Key];
if (accessAllowRuleGainedByChild != default(FileSystemRights))
accessAllowRulesGainedByChild.Add(combinedChildAccessAllowRule.Key, accessAllowRuleGainedByChild);
}
else
{
accessAllowRulesGainedByChild.Add(combinedChildAccessAllowRule.Key, combinedChildAccessAllowRule.Value);
}
}
accessDenyRulesGainedByChild = new Dictionary<IdentityReference, FileSystemRights>();
foreach (KeyValuePair<IdentityReference, FileSystemRights> combinedChildAccessDenyRule in combinedChildAccessDenyRules)
{
if (combinedParentAccessDenyRules.ContainsKey(combinedChildAccessDenyRule.Key))
{
FileSystemRights accessDenyRuleGainedByChild = combinedChildAccessDenyRule.Value & ~combinedParentAccessDenyRules[combinedChildAccessDenyRule.Key];
if (accessDenyRuleGainedByChild != default(FileSystemRights))
accessDenyRulesGainedByChild.Add(combinedChildAccessDenyRule.Key, accessDenyRuleGainedByChild);
}
else
{
accessDenyRulesGainedByChild.Add(combinedChildAccessDenyRule.Key, combinedChildAccessDenyRule.Value);
}
}
accessAllowRulesGainedByParent = new Dictionary<IdentityReference, FileSystemRights>();
foreach (KeyValuePair<IdentityReference, FileSystemRights> combinedParentAccessAllowRule in combinedParentAccessAllowRules)
{
if (combinedChildAccessAllowRules.ContainsKey(combinedParentAccessAllowRule.Key))
{
FileSystemRights accessAllowRuleGainedByParent = combinedParentAccessAllowRule.Value & ~combinedChildAccessAllowRules[combinedParentAccessAllowRule.Key];
if (accessAllowRuleGainedByParent != default(FileSystemRights))
accessAllowRulesGainedByParent.Add(combinedParentAccessAllowRule.Key, accessAllowRuleGainedByParent);
}
else
{
accessAllowRulesGainedByParent.Add(combinedParentAccessAllowRule.Key, combinedParentAccessAllowRule.Value);
}
}
accessDenyRulesGainedByParent = new Dictionary<IdentityReference, FileSystemRights>();
foreach (KeyValuePair<IdentityReference, FileSystemRights> combinedParentAccessDenyRule in combinedParentAccessDenyRules)
{
if (combinedChildAccessDenyRules.ContainsKey(combinedParentAccessDenyRule.Key))
{
FileSystemRights accessDenyRuleGainedByParent = combinedParentAccessDenyRule.Value & ~combinedChildAccessDenyRules[combinedParentAccessDenyRule.Key];
if (accessDenyRuleGainedByParent != default(FileSystemRights))
accessDenyRulesGainedByParent.Add(combinedParentAccessDenyRule.Key, accessDenyRuleGainedByParent);
}
else
{
accessDenyRulesGainedByParent.Add(combinedParentAccessDenyRule.Key, combinedParentAccessDenyRule.Value);
}
}
if (accessAllowRulesGainedByChild.Count > 0 || accessDenyRulesGainedByChild.Count > 0 || accessAllowRulesGainedByParent.Count > 0 || accessDenyRulesGainedByParent.Count > 0)
return false;
else
return true;
}

You cannot use Equals() since this method is inherited from Object.
You need to find a identifying attribute on that DirectorySecurity class. I think
String GetSecurityDescriptorSddlForm()
should do your job. You can invoke Equals() on that.
Edit: Well sorry, this method needs a parameter for invocation. Try finding another attribute on the DirectorySecurity which is better for comparison.
Edit2: I'm not familar with .NET Security Framework and Right-Management, but something like this should be your approach. You can do != resp: == on FileSystemAccessRule.FileSystemRights because that attribute is an enum (internally an int).
ArrayList notIdenticalList = new ArrayList();
DirectorySecurity parentFolderAccessControl = Directory.GetAccessControl(null);
DirectorySecurity childItemAccessControl = Directory.GetAccessControl(null);
foreach (FileSystemAccessRule parentRule in parentFolderAccessControl.GetAccessRules(true, true, typeof(NTAccount)))
{
foreach (FileSystemAccessRule childRule in childItemAccessControl.GetAccessRules(true, true, typeof(NTAccount)))
{
if (parentRule.FileSystemRights != childRule.FileSystemRights)
{
// add to not identical-list
notIdenticalList.Add(fileToAdd...);
break;
}
}
}

Related

List all files on all disks and all directories except the files in system directories in C# .Net

I have a program written in C# which lists all files in all directories except that ones where you need admin rights. It has no errors but always gives me an access denied exception for a directory that I already ignore. The access denied exception is for "C:$Recycle.Bin\S-1-5-18".
Actually, I ignrore this directory AND run the program with admin rights but the exception is still here.Can anyone help me? Thanks
static void List()
{
List<string> files = new List<string>();
List<string> nofiles = new List<string>();
foreach (var drives in DriveInfo.GetDrives())
{
var filez = Directory.GetFiles(drives.Name,"*",SearchOption.AllDirectories);
foreach (string f in filez)
{
if (f.StartsWith(#"C:\Windows"))
{
}
else if (f.StartsWith(#"C:\Config.Msi"))
{
}
else if (f.StartsWith(#"C:\Program Files"))
{
}
else if (f.StartsWith(#"C:\Program Files (x86)"))
{
}
else if (f.StartsWith(#"C:\DumpStack.log"))
{
}
else if(f.StartsWith(#"C:\$Recycle.Bin\S-1-5-18"))
{
}
else if(f.StartsWith(#"C:\Documents and Settings"))
{
}
else
{
files.Add(f);
}
}
foreach (string fl in files)
{
var c = Path.GetFullPath(fl);
Console.WriteLine(c);
Console.ReadKey();
}
Console.ReadKey();
}
}
Direct access almost always ends with access denied somewhere you should specify the search rules more precisely with the enumeration options, like below:
foreach (var drive in DriveInfo.GetDrives())
{
if (drive.IsReady == false)
{
continue;
}
var filez = Directory.GetFiles(drive.Name, "*", new EnumerationOptions
{
AttributesToSkip = FileAttributes.Hidden | FileAttributes.System,
IgnoreInaccessible = true,
RecurseSubdirectories = true,
ReturnSpecialDirectories = true,
});
// TODO: implement the rest of your code hier ...
}
where the FileAttributes.Hidden is not realy required.
... hope that helps!
With Directory.GetFiles(drives.Name,"*",SearchOption.AllDirectories); you already iterate trough all the directories. The filtering is done afterwards and only used to decide whether you add the directory name (that you already have!) to your list.
To filter the way you want, you need to implement the recursion yourself and catch the exception on each step.

UnauthorizedAccessException When trying get access rules using DirectorySecurity library

I am trying to get a particular folder write access for users using a method that gets access rules and checks wheather the logged in user has write access for that folder. But when trying to do this I am getting an error stating UnauthorizedException.I have checked the folder access and everything looks good.I Below is the screenshot of the error:
private bool AccessPackerPlanTemplate()
{
bool result = true;
UserBUList userBUList = new UserBUList();
try
{
string PackerPlanTemplate = System.Configuration.ConfigurationSettings.AppSettings["PackerPlanTemplate"];
userBUList.UserName = (HttpContext.Current.User.Identity).Name.Split('\\')[1].ToString();
string path = PackerPlanTemplate;
string NtAccountName = userBUList.UserName;
DirectoryInfo di = new DirectoryInfo(path);
DirectorySecurity acl = di.GetAccessControl(AccessControlSections.All); //I AM GETTING ERROR ON THIS LINE
System.Security.AccessControl.AuthorizationRuleCollection rules = acl.GetAccessRules(true, true, typeof(NTAccount));
//Go through the rules returned from the DirectorySecurity
foreach (System.Security.AccessControl.AuthorizationRule rule in rules)
{
//If we find one that matches the identity we are looking for
if (rule.IdentityReference.Value.Equals(NtAccountName, StringComparison.CurrentCultureIgnoreCase))
{
//Cast to a FileSystemAccessRule to check for access rights
if ((((FileSystemAccessRule)rule).FileSystemRights & FileSystemRights.WriteData) > 0)
{
result = true ;
}
}
}
}
catch (UnauthorizedAccessException)
{
result = false;
}
return result;
}
Does somebody has any suggestion regarding Where am I going wrong? Is there a better way to do this?

Check network file permission in .NET

I have to check if user, that login into system and run the application, have a specified permissions on some file.
User that run the application is in "BUILTIN\Administrators" group.
While file is local all going fine. I use that code (adopted version from that answers Checking for directory and file write permissions in .NET):
private static bool HasPermission(FileSystemRights permission, AuthorizationRuleCollection accessRules )
{
var allow = false;
var inheritedDeny = false;
var inheritedAllow = false;
if (accessRules == null)
return false;
var currentUser = WindowsIdentity.GetCurrent();
var currentPrincipal = new WindowsPrincipal(currentUser);
foreach (FileSystemAccessRule rule in accessRules)
{
if ((permission & rule.FileSystemRights) != permission)
continue;
if (!currentPrincipal.IsInRole(rule.IdentityReference.Value))
{
continue;
}
if (rule.AccessControlType == AccessControlType.Allow)
{
if (rule.IsInherited)
inheritedAllow = true;
else
allow = true;
}
else if (rule.AccessControlType == AccessControlType.Deny)
{
if (!rule.IsInherited)
return false;
inheritedDeny = true;
}
}
var combined = allow || (inheritedAllow && !inheritedDeny);
return combined;
}
But when I try to check permissions on network shared file I have issue.
For example file shared with FullControl access rule for remote computer user, that remote user also in "BUILTIN/Administrators" group. For "Everyone" group user it is ReadOnly file.
So when I check this with my current, local, log in user by using that code:
if (!currentPrincipal.IsInRole(rule.IdentityReference.Value))
{
continue;
}
I do not go inside of IF condition due my log-in user also in "BUILTIN/Administrators" group.
So code returns TRUE, but in real life I have no write access to that file.
How do can I distinct local and remote Administrator's group users?
PS: I do not want to use exceptions to check accessibility, this will be the "last hope code"

What algorithm can I use to recursively load an entire directory, starting from a specified path?

I'm building a custom file dialog, and my problem is its taking too long to load.
The dialog begins with an InitialDirectory property, and I am looking to find a way to load the directory tree for the InitialDirectory first, followed by the rest of the directories in a background thread.
For example, if the InitialDirectory was C:\Users\User12345\MyDocuments, then it should load all folders in
C:\
C:\Users
C:\User12345
C:\Users\User12345\MyDocuments
then kick off a background thread to load all the remaining directories.
Is there an fast and easy way to use recursion to load first the InitialDirectory, than everything else, without duplicating any items?
I'm struggling to find a high-performing way to do this, since checking for the existance of a folder with code like if (!Directory.Contains(f => f.FullName == folder.FullName)) slows down the load by quite a bit.
My current code to load the full directory looks something like this:
private void LoadDirectory()
{
string root = #"C:\";
var rootNode = new DirectoryModel() { Name = root, FullName = root };
this.Directory.Add(rootNode);
DirectoryInfo info = new DirectoryInfo(root);
IEnumerable<DirectoryInfo> subDirectories = info.GetDirectories()
.Where(d => ((d.Attributes & FileAttributes.Hidden) != FileAttributes.Hidden)
&& ((d.Attributes & FileAttributes.System) != FileAttributes.System));
LoadDirectories(subDirectories, root);
}
private void LoadDirectories(IEnumerable<DirectoryInfo> subDirs, string parentName)
{
IEnumerable<DirectoryInfo> subdirectories;
foreach (DirectoryInfo folder in subDirs)
{
var node = new DirectoryModel() { Name = folder.Name, FullName = folder.FullName, ParentName = parentName };
Directory.Add(node);
try
{
subdirectories = folder.GetDirectories("*", SearchOption.TopDirectoryOnly)
.Where(d => ((d.Attributes & FileAttributes.Hidden) != FileAttributes.Hidden)
&& ((d.Attributes & FileAttributes.System) != FileAttributes.System));
}
catch (UnauthorizedAccessException e)
{
continue;
}
catch (System.IO.DirectoryNotFoundException e)
{
continue;
}
if (subdirectories.Count() != 0)
LoadDirectories(subdirectories, folder.FullName);
}
}
Note that the Directory collection is a flat collection - there is no hierarchy in the data models.
It depends on how you present to the user your data, imo.
If you use a TreeView control, you can easily avoid to load all data in background, but just load only the root of your tree (like you already do as much as I understood).
In this way, having a procedure that loads all sub-directory names and files of a specified directory you just need to wait until user clicks on the [+] in order to see the directory content interesting to him. So you run your procedure and populate the tree.
This is actually highest performant way known to me to deal with this kind of stuff, and actually you will find this pattern almost in all "explorer like" products.
The performance here achieved not by perfectly refined algorithm, but just by defining more convenient behavioral model.
Hope this helps.
To give a face to Tigran's solution, one way to accomplish this is by using something like the following:
Subscribe to the tree's BeforeExpand event.
private void BeforeExpand(object sender, BeforeExpandEventArgs e)
{
TreeListNode current = e.Node;
if (current.Nodes.Count > 0)
return;
if (current.Tag is DriveInfo)
{
exampleTree.BeginUpdate();
DriveInfo driveInfo = current.Tag as DriveInfo;
LoadDirectories(current, driveInfo.RootDirectory);
exampleTree.EndUpdate(true);
}
else if (current.Tag is DirectoryInfo)
{
exampleTree.BeginUpdate();
LoadDirectories(current, (DirectoryInfo)current.Tag);
exampleTree.EndUpdate(true);
}
}
And here is the LoadDirectories() method:
private void LoadDirectories(TreeListNode parent, DirectoryInfo directoryInfo)
{
DirectoryInfo[] directories = directoryInfo.GetDirectories();
foreach (DirectoryInfo directory in directories)
{
if ((directory.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden) continue;
TreeListNode child = new TreeListNode() { Tag = directory, Text = directory.Name };
parent.Nodes.Add(child);
}
FileInfo[] files = directoryInfo.GetFiles();
foreach (FileInfo file in files)
{
TreeListNode child = new TreeListNode() { Tag = file, Text = file.Name };
parent.Nodes.Add(child);
}
}
HTH,
-saige-

what is the best way to check in C# .NET if a directory has access to list files or a unauthorized access exception would rise

how would I check in the best way in .NET 2.0 C# if I have access to a specified directory
for listing top directory files e.g. a system directory or system volume information folder etc.
My code for it looks now like this, but I think it is not the best way to check for it since it produces an exception each time which is handled by the check function and returning based on it a result.
I would like to use a function which doesn't throw an error to check if in the specified directory is access to list files or maybe my code can be improved or optimized. I might have to check through a thousand directories if exists an access or not. Raising thousand exceptions might cause a problem, but I don't know.
//here my code using System.IO;
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show(DirectoryCanListFiles("C:\\Windows\\Prefetch").ToString());
}
public static bool DirectoryCanListFiles(string DirectoryPath)
{
try
{
Directory.GetFiles(DirectoryPath, "*", SearchOption.TopDirectoryOnly);
}
catch { return false; }
return true;
}
The best way to check the permission, is try to access the direcoty (read/write/list) & catch the UnauthorizedAccessException.
However for some reason out there, if you want to check permissions, following code should satisfy your need.
You need to read Access Rules for the directory.
private bool DirectoryCanListFiles(string folder)
{
bool hasAccess = false;
//Step 1. Get the userName for which, this app domain code has been executing
string executingUser = System.Security.Principal.WindowsIdentity.GetCurrent().Name;
NTAccount acc = new NTAccount(executingUser);
SecurityIdentifier secId = acc.Translate(typeof(SecurityIdentifier)) as SecurityIdentifier;
DirectorySecurity dirSec = Directory.GetAccessControl(folder);
//Step 2. Get directory permission details for each user/group
AuthorizationRuleCollection authRules = dirSec.GetAccessRules(true, true, typeof(SecurityIdentifier));
foreach (FileSystemAccessRule ar in authRules)
{
if (secId.CompareTo(ar.IdentityReference as SecurityIdentifier) == 0)
{
var fileSystemRights = ar.FileSystemRights;
Console.WriteLine(fileSystemRights);
//Step 3. Check file system rights here, read / write as required
if (fileSystemRights == FileSystemRights.Read ||
fileSystemRights == FileSystemRights.ReadAndExecute ||
fileSystemRights == FileSystemRights.ReadData ||
fileSystemRights == FileSystemRights.ListDirectory)
{
hasAccess = true;
}
}
}
return hasAccess;
}

Categories