Как вы программно исправляете неканонический ACL?
У меня есть следующий код:
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);
Вызов AddAccessRule создает исключение InvalidOperationException со следующей трассировкой стека:
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)
Это происходит только в некоторых системах (я видел Windows XP и Windows 7). В ситуациях, когда возникает ошибка, при просмотре разрешений безопасности для каталога с помощью проводника Windows обычно отображается окно сообщения со следующим текстом:
Разрешения на неправильно упорядочены, что может привести к неэффективности некоторых записей. Нажмите OK, чтобы продолжить и правильно отсортировать разрешения, или Отмена, чтобы сбросить разрешения.
Нажатие ОК в этой точке решает проблему. Что тут происходит? Как система попадает в это состояние, и есть ли способ обнаружить / исправить ее программно (т.е. без необходимости вручную использовать проводник для исправления этого)?
Обновить
Я немного больше изучил ACL, что такое каноническая форма и почему это необходимо. Я до сих пор не уверен, как файл обычно попадет в это состояние, но я обнаружил, что инструмент Icacls можно использовать для создания каталога с неканоническим списком ACL, сохранив список разрешений, изменив его как выходящий из-под контроля. порядок и восстановление его. Теперь мне просто нужен способ это исправить, не требуя взаимодействия с пользователем.
1 ответ
Я нашел решение этой проблемы в сообщении блога MSDN: " Скажите, что?" - Список контроля доступа не является каноническим. По сути, вам нужно создать новый DACL с теми же разрешениями, но в правильном каноническом порядке:
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);
}
Существуют методы расширения для RawAcl, которые, кажется, канонализируют неправильные ACE.
Но это загадочно. Методы просто присутствуют, и я не нашел никакой документации. Глядя на исходный код .net 4.8 DirectoryObjectSecurity, автор жалуется:
A better way would be to have an internal method that would canonicalize the ACL and call it once
Это сигнатура методов:
{
//
// 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);
}
Но, как мы знаем, есть ACE, которые нельзя канонизировать без потери информации. Эти методы расширения не имеют
return value
и вроде не бросаю
exception
для этого случая. Поэтому при их использовании может быть потеряна информация. И отличный ответ от Кевина Киблера - лучший способ сделать это.