0
votes

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?

2
I think this answer will probably help you. stackoverflow.com/a/1281638/1181408 Essentially you'll want to call GetAccessRules on your DirectorySecurity objects and then iterate over the resulting collection.cgotberg
@cgotberg That sounds rather expensive. Get both collections. Check if users in collection1 exist in collection2 and other way around. And then foreach user check if access is equal. That was exactly my starting point, but I'm hoping someone here has a better solution.nl-x
I suppose it's expensive but in this concept of occasionally needing to migrate code it probably doesn't matter. You can use this generic .net compare objects code to do a deep compare and that's probably as expensive as it will ever get. comparenetobjects.codeplex.comcgotberg
@cgotberg I ended up just doing it. Maybe you have some feedback on the code I pasted in my own answer belownl-x

2 Answers

2
votes

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;
}
1
votes

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;
                }
            }
        }