Is it possible in C#/.NET to determine where an inherited System.Security.AccessControl.FileSystemAccessRule was actually inherited from? If so, how do I do that? I want to create an output that is like in the Windows security property, where you can see to which object an inherited ACE was attached to.
You will have to walk through the file or folder's path to find where the rule originated. Here's a crude set of functions that will print all access rules and where the originated from. You can easily modify this to create a more useful API (in other words, not just print to Console).
void PrintAccessRules(string path)
{
var security = File.GetAccessControl(path);
var accessRules = security.GetAccessRules(true, true, typeof(NTAccount));
foreach (var rule in accessRules.Cast<FileSystemAccessRule>())
{
if (!rule.IsInherited)
{
Console.WriteLine("{0} {1} to {2} was set on {3}.", rule.AccessControlType, rule.FileSystemRights, rule.IdentityReference, path);
continue;
}
FindInheritedFrom(rule, Directory.GetParent(path).FullName);
}
}
void FindInheritedFrom(FileSystemAccessRule rule, string path)
{
var security = File.GetAccessControl(path);
var accessRules = security.GetAccessRules(true, true, typeof(NTAccount));
var matching = accessRules.OfType<FileSystemAccessRule>()
.FirstOrDefault(r => r.AccessControlType == rule.AccessControlType && r.FileSystemRights == rule.FileSystemRights && r.IdentityReference == rule.IdentityReference);
if (matching != null)
{
if (matching.IsInherited) FindInheritedFrom(rule, Directory.GetParent(path).FullName);
else Console.WriteLine("{0} {1} to {2} is inherited from {3}", rule.AccessControlType, rule.FileSystemRights, rule.IdentityReference, path);
}
}
For example:
PrintAccessRules(#"C:\projects\mg\lib\repositories.config");
Prints the following for me:
Allow FullControl to SkipTyler\Mike was set on C:\projects\mg\lib\repositories.config.
Allow ReadAndExecute, Synchronize to SkipTyler\Mike is inherited from C:\projects\mg
Allow FullControl to BUILTIN\Administrators is inherited from C:\
Allow FullControl to NT AUTHORITY\SYSTEM is inherited from C:\
Allow ReadAndExecute, Synchronize to BUILTIN\Users is inherited from C:\
Related
Hi I want to check if a particular folder is shared with a user.
for the normal share rights, I can the info by
DirectoryInfo di = new DirectoryInfo(path);
DirectorySecurity acl = di.GetAccessControl(AccessControlSections.All);
AuthorizationRuleCollection rules = acl.GetAccessRules(true, true, typeof(NTAccount));
//Go through the rules returned from the DirectorySecurity
foreach (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)
{
Console.WriteLine(string.Format("{0} has write access to {1}", NtAccountName, path));
}
else
{
Console.WriteLine(string.Format("{0} does not have write access to {1}", NtAccountName, path));
}
}
}
But the problem with this approach is that the if shared the folder using the "Advanced Share" option, as shown in image, it doesn't show any entry into the log.
How to calculate it?
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;
}
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;
}
}
}
Having an issue with a Windows service that needs to monitor/have access to a set of folders, and move files around between those folders.
There's have a bit of boilerplate code that's been used in the past, which will check a given folder for the specific granular permissions for the given user. The odd thing is that I discovered through testing that if I manually deny all permissions on that folder for the account the service is running under, and then run the code, it reports that all is well and the user does in fact have those permissions, even though it's obvious (and demonstrable) that he doesn't.
At first I thought this might be because the service was running under the local System account, but the same issue crops up if it is run with NetworkService as well as with a local user account. This is on Windows 7/2008 R2.
Boilerplate method:
public static void ValidateFolderPermissions(WindowsIdentity userId, string folder, FileSystemRights[] requiredAccessRights)
{
SecurityIdentifier secId;
StringBuilder sb = new StringBuilder();
bool permissionsAreSufficient = false;
bool notAuthorized = false;
String errorMsg = String.Empty;
IdentityReferenceCollection irc = userId.Groups;
foreach (IdentityReference ir in irc)
{
secId = ir.Translate(typeof(SecurityIdentifier)) as SecurityIdentifier;
try
{
DirectoryInfo dInfo = new DirectoryInfo(folder);
DirectorySecurity dSecurity = dInfo.GetAccessControl();
AuthorizationRuleCollection rules = dSecurity.GetAccessRules(true, true, typeof(SecurityIdentifier));
foreach (FileSystemAccessRule ar in rules)
{
if (secId.CompareTo(ar.IdentityReference as SecurityIdentifier) == 0)
{
sb.AppendLine(ar.FileSystemRights.ToString());
foreach (FileSystemRights right in requiredAccessRights)
{
if (right == ar.FileSystemRights)
{
permissionsAreSufficient = true;
break;
}
}
}
}
}
catch (UnauthorizedAccessException)
{
notAuthorized = true;
errorMsg = "user not authorized";
}
catch (SecurityException)
{
// If we failed authorization do not update error
if (!notAuthorized)
errorMsg = "security error";
}
catch (Exception)
{
// If we failed authorization do not update error
if (!notAuthorized)
errorMsg = "invalid folder or folder not accessible";
}
}
if (!permissionsAreSufficient)
{
if (!String.IsNullOrEmpty(errorMsg))
throw new Exception(String.Format("User {0} does not have required access to folder {1}. The error is {2}.", userId.Name, folder, errorMsg));
else
throw new Exception(String.Format("User {0} does not have required access rights to folder {1}.", userId.Name, folder));
}
}
And the calling snippet:
FileSystemRights[] requireAccessRights =
{
FileSystemRights.Delete,
FileSystemRights.Read,
FileSystemRights.FullControl
};
try
{
FolderPermissionValidator.ValidateFolderPermissions(WindowsIdentity.GetCurrent(), inputFolder, requireAccessRights);
Log.Debug("In ServiceConfigurationValidator: {0}, {1}", WindowsIdentity.GetCurrent().Name, inputFolder);
}
catch (Exception ex)
{
Log.Debug("Throwing exception {0}", ex.Message);
}
I don't see anything in ValidateFolderPermissions to check for denials before checking for allowed permissions. If a deny entry prevents access then no amount of allow entries can override it.
This code enumerates the entries in the ACL as FileSystemAccessRule objects, but doesn't bother to check whether AccessControlType is allow or deny.
I also note that the logic returns true if any ACE exactly matches any of the elements of the requiredAccessRights array; I suspect the intended behaviour is that it return true if all of the specified rights are present. This could cause false positives if only some of the requested rights are present, but because it only looks for exact matches it could also cause a false negative, e.g., if the ACE actually gives more rights than are being requested. (Not such a problem in the example given, though, because you're asking for Full Control.)
Another flaw is that it only checks for access entries matching groups the user belongs to; access entries for the user account itself will be ignored. (I'm not sure what the behaviour of WindowsIdentity.Groups is for security primitives such as SYSTEM and NetworkService that are not actual user accounts, although it sounds like that part was working as desired.)
Note that because it is very hard to cope properly with all the possible situations (consider, e.g., an access control entry for Everyone, or for SERVICE) it would be wise to allow the administrator to override the check if it is mistakenly reporting that the account doesn't have the necessary access.
I have the following code:
DirectoryInfo directory = new DirectoryInfo(#"C:\Program Files\Company\Product");
if (!directory.Exists) { directory.Create(); }
DirectorySecurity directorySecurity = directory.GetAccessControl();
SecurityIdentifier securityIdentifier = new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null);
directorySecurity.AddAccessRule(
new FileSystemAccessRule(
securityIdentifier,
FileSystemRights.Write,
InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit,
PropagationFlags.None,
AccessControlType.Allow));
directory.SetAccessControl(directorySecurity);
The call to AddAccessRule throws an InvalidOperationException with the following stack trace:
System.InvalidOperationException: This access control list is not in canonical form and therefore cannot be modified.
at System.Security.AccessControl.CommonAcl.ThrowIfNotCanonical()
at System.Security.AccessControl.CommonAcl.AddQualifiedAce(SecurityIdentifier sid, AceQualifier qualifier, Int32 accessMask, AceFlags flags, ObjectAceFlags objectFlags, Guid objectType, Guid inheritedObjectType)
at System.Security.AccessControl.DiscretionaryAcl.AddAccess(AccessControlType accessType, SecurityIdentifier sid, Int32 accessMask, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags)
at System.Security.AccessControl.CommonObjectSecurity.ModifyAccess(AccessControlModification modification, AccessRule rule, Boolean& modified)
at System.Security.AccessControl.CommonObjectSecurity.AddAccessRule(AccessRule rule)
at System.Security.AccessControl.FileSystemSecurity.AddAccessRule(FileSystemAccessRule rule)
This only happens on some systems (I've seen Windows XP and Windows 7). In the situations where the error occurs, viewing the security permissions for the directory using Windows Explorer usually causes a message box to be shown with the following text:
The permissions on are incorrectly ordered, which may
cause some entries to be ineffective. Press OK to continue and sort
the permissions correctly, or Cancel to reset the permissions.
Clicking OK at this point fixes the problem. What's going on here? How does a system get into this state, and is there any way to detect/fix it programmatically (i.e. without having the user manually use Explorer to fix this)?
Update
I did a bit more research about ACL, what canonical form is, and why it's necessary. I'm still not sure how a file would normally get into this state, but I found that the Icacls tool can be used to create a directory with a non-canonical ACL by saving the permission list, altering it to be out-of-order, and restoring it. Now I just need a way to fix it without requiring user interaction.
I found the solution to this in an MSDN blog post: Say wwhhhaaaat? - The access control list is not canonical. Basically, you need to construct a new DACL with the same permissions, but in the correct canonical order:
static void Main(string[] args)
{
// directory with known ACL problem (created using Icacls)
DirectoryInfo directoryInfo = new DirectoryInfo("acltest");
var directorySecurity = directoryInfo.GetAccessControl(AccessControlSections.Access);
CanonicalizeDacl(directorySecurity);
directoryInfo.SetAccessControl(directorySecurity);
}
static void CanonicalizeDacl(NativeObjectSecurity objectSecurity)
{
if (objectSecurity == null) { throw new ArgumentNullException("objectSecurity"); }
if (objectSecurity.AreAccessRulesCanonical) { return; }
// A canonical ACL must have ACES sorted according to the following order:
// 1. Access-denied on the object
// 2. Access-denied on a child or property
// 3. Access-allowed on the object
// 4. Access-allowed on a child or property
// 5. All inherited ACEs
RawSecurityDescriptor descriptor = new RawSecurityDescriptor(objectSecurity.GetSecurityDescriptorSddlForm(AccessControlSections.Access));
List<CommonAce> implicitDenyDacl = new List<CommonAce>();
List<CommonAce> implicitDenyObjectDacl = new List<CommonAce>();
List<CommonAce> inheritedDacl = new List<CommonAce>();
List<CommonAce> implicitAllowDacl = new List<CommonAce>();
List<CommonAce> implicitAllowObjectDacl = new List<CommonAce>();
foreach (CommonAce ace in descriptor.DiscretionaryAcl)
{
if ((ace.AceFlags & AceFlags.Inherited) == AceFlags.Inherited) { inheritedDacl.Add(ace); }
else
{
switch (ace.AceType)
{
case AceType.AccessAllowed:
implicitAllowDacl.Add(ace);
break;
case AceType.AccessDenied:
implicitDenyDacl.Add(ace);
break;
case AceType.AccessAllowedObject:
implicitAllowObjectDacl.Add(ace);
break;
case AceType.AccessDeniedObject:
implicitDenyObjectDacl.Add(ace);
break;
}
}
}
Int32 aceIndex = 0;
RawAcl newDacl = new RawAcl(descriptor.DiscretionaryAcl.Revision, descriptor.DiscretionaryAcl.Count);
implicitDenyDacl.ForEach(x => newDacl.InsertAce(aceIndex++, x));
implicitDenyObjectDacl.ForEach(x => newDacl.InsertAce(aceIndex++, x));
implicitAllowDacl.ForEach(x => newDacl.InsertAce(aceIndex++, x));
implicitAllowObjectDacl.ForEach(x => newDacl.InsertAce(aceIndex++, x));
inheritedDacl.ForEach(x => newDacl.InsertAce(aceIndex++, x));
if (aceIndex != descriptor.DiscretionaryAcl.Count)
{
System.Diagnostics.Debug.Fail("The DACL cannot be canonicalized since it would potentially result in a loss of information");
return;
}
descriptor.DiscretionaryAcl = newDacl;
objectSecurity.SetSecurityDescriptorSddlForm(descriptor.GetSddlForm(AccessControlSections.Access), AccessControlSections.Access);
}
There are extention methods for 'RawAcl' which seem to canonalize wrong ACEs.
But it's kind of mysterious. The methods are just present and I haven't found any documentation. Looking at the sourcode of .net 4.8 DirectoryObjectSecurity the author complains: A better way would be to have an internal method that would canonicalize the ACL and call it once
This is the signature of the methods:
{
//
// Summary:
// Canonicalizes the specified Access Control List.
//
// Parameter:
// acl:
// The Access Control List.
public static void Canonicalize(this RawAcl acl);
//
// Summary:
// Sort ACEs according to canonical form for this System.Security.AccessControl.ObjectSecurity.
//
// Parameter:
// objectSecurity:
// The object security whose DiscretionaryAcl will be made canonical.
public static void CanonicalizeAccessRules(this ObjectSecurity objectSecurity);
}
But As we know, there are ACEs which cannot be canonalized without lost of informations. These extention methods have no return value and seem not to throw any exception for this case. Therefore it may come to loss of informations using them. And the great answer from Kevin Kibler ma be the better way doing this.