You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

411 lines
18 KiB

using Alphaleonis.Win32.Filesystem;
using System;
using System.Collections.Generic;
using System.Security.AccessControl;
namespace Security2
{
public class FileSystemSecurity2
{
protected FileSystemInfo item;
protected FileSystemSecurity sd;
protected AccessControlSections sections;
protected bool isFile = false;
// The SDDL form of each section as it was read or last written, so that WriteChanges writes only the
// sections that changed since.
private Dictionary<AccessControlSections, string> sectionsAsRead;
public FileSystemInfo Item
{
get { return item; }
set { item = value; }
}
public string FullName { get { return item.FullName; } }
public string Name { get { return item.Name; } }
public bool IsFile { get { return isFile; } }
public FileSystemSecurity2(FileSystemInfo item, AccessControlSections sections)
{
this.sections = sections;
this.item = item;
isFile = item is FileInfo;
sd = GetSecurity(item, sections);
RememberSections();
}
public FileSystemSecurity2(FileSystemInfo item)
{
this.item = item;
isFile = item is FileInfo;
try
{
sd = GetSecurity(item, AccessControlSections.All);
sections = AccessControlSections.All;
}
catch
{
try
{
sd = GetSecurity(item, AccessControlSections.Access | AccessControlSections.Owner | AccessControlSections.Group);
sections = AccessControlSections.Access | AccessControlSections.Owner | AccessControlSections.Group;
}
catch
{
sd = GetSecurity(item, AccessControlSections.Access);
sections = AccessControlSections.Access;
}
}
// Read together with the SACL, the inherited entries of a DACL without the auto-inherit flag lose their
// inherited flag when the parent folder has no SACL, and writing such a DACL back stores them as explicit
// entries. Read alone, the DACL keeps the flags.
if (HasAuditSection)
{
var accessSecurity = GetSecurity(item, AccessControlSections.Access);
sd.SetSecurityDescriptorBinaryForm(accessSecurity.GetSecurityDescriptorBinaryForm(), AccessControlSections.Access);
}
RememberSections();
}
// For the root of a drive, the methods of DirectoryInfo read and write the security descriptor of the volume,
// a device object, instead of that of its root folder (#41). The methods that take the path keep the trailing
// backslash and reach the root folder.
internal static FileSystemSecurity GetSecurity(FileSystemInfo item, AccessControlSections sections)
{
var file = item as FileInfo;
if (file != null)
{
return file.GetAccessControl(sections);
}
string root;
if (TryGetDriveRoot(item, out root))
{
return Directory.GetAccessControl(root, sections);
}
return ((DirectoryInfo)item).GetAccessControl(sections);
}
internal static void SetSecurity(FileSystemInfo item, FileSystemSecurity security, AccessControlSections sections)
{
var file = item as FileInfo;
if (file != null)
{
file.SetAccessControl((FileSecurity)security, sections);
return;
}
string root;
if (TryGetDriveRoot(item, out root))
{
Directory.SetAccessControl(root, (DirectorySecurity)security, sections);
return;
}
((DirectoryInfo)item).SetAccessControl((DirectorySecurity)security, sections);
}
private static bool TryGetDriveRoot(FileSystemInfo item, out string root)
{
var fullName = item.FullName.TrimEnd('\\');
// A drive letter, such as C:
var isDriveLetter = fullName.Length == 2 && fullName[1] == ':' &&
((fullName[0] >= 'A' && fullName[0] <= 'Z') || (fullName[0] >= 'a' && fullName[0] <= 'z'));
// A volume name, such as \\?\Volume{9f122b1b-858a-49bd-aec4-dfbe8978fe16}, without a folder below it
var isVolumeName = fullName.StartsWith(@"\\?\Volume{", StringComparison.OrdinalIgnoreCase) &&
fullName.EndsWith("}", StringComparison.Ordinal) && fullName.IndexOf('\\', 4) < 0;
if (isDriveLetter || isVolumeName)
{
root = fullName + "\\";
return true;
}
root = null;
return false;
}
// Without the Security privilege, the security descriptor is read without its SACL.
internal bool HasAuditSection
{
get { return (sections & AccessControlSections.Audit) == AccessControlSections.Audit; }
}
// An item without audit entries can have no SACL at all, also when the SACL was read.
internal bool HasSystemAcl
{
get { return new RawSecurityDescriptor(sd.GetSecurityDescriptorBinaryForm(), 0).SystemAcl != null; }
}
// The sections that differ from the ones that were read or last written.
internal AccessControlSections ChangedSections
{
get
{
var changed = AccessControlSections.None;
foreach (var section in sectionsAsRead)
{
if (sd.GetSecurityDescriptorSddlForm(section.Key) != section.Value)
{
changed |= section.Key;
}
}
return changed;
}
}
private void RememberSections()
{
sectionsAsRead = new Dictionary<AccessControlSections, string>();
foreach (var section in new[] { AccessControlSections.Access, AccessControlSections.Audit, AccessControlSections.Owner, AccessControlSections.Group })
{
sectionsAsRead[section] = sd.GetSecurityDescriptorSddlForm(section);
}
}
public FileSystemSecurity SecurityDescriptor
{
get
{
return sd;
}
}
// Writes the sections that the descriptor was read with. Windows can return the owner and the group with a DACL
// that is read alone, and writing them back fails for an owner that the user cannot assign (#34).
public void Write()
{
SetSecurity(item, sd, sections);
RememberSections();
}
// Writes only the sections that changed since they were read or last written, so that, for example, an
// unchanged owner that the user cannot assign isn't written back (#34). Without a change, it writes nothing.
internal void WriteChanges()
{
var changedSections = ChangedSections;
if (changedSections == AccessControlSections.None)
{
return;
}
SetSecurity(item, sd, changedSections);
RememberSections();
}
// Writes the sections that the descriptor was read with to another item, like Write().
public void Write(FileSystemInfo item)
{
SetSecurity(item, sd, sections);
}
public void Write(string path)
{
FileSystemInfo item = null;
if (File.Exists(path))
{
item = new FileInfo(path);
}
else if (Directory.Exists(path))
{
item = new DirectoryInfo(path);
}
else
{
throw new System.IO.FileNotFoundException("File not found", path);
}
Write(item);
}
#region Conversion
// The descriptor is in sd. Before 5.0.0-rc6, these conversions returned fields that were never set, so they
// gave null, and the hash code threw a NullReferenceException.
public static implicit operator FileSecurity(FileSystemSecurity2 fs2)
{
return fs2.sd as FileSecurity;
}
public static implicit operator FileSystemSecurity2(FileSecurity fs)
{
return new FileSystemSecurity2(new FileInfo(""));
}
public static implicit operator DirectorySecurity(FileSystemSecurity2 fs2)
{
return fs2.sd as DirectorySecurity;
}
public static implicit operator FileSystemSecurity2(DirectorySecurity fs)
{
return new FileSystemSecurity2(new DirectoryInfo(""));
}
// Like the descriptors of .NET, two objects are equal when they wrap the same descriptor; a descriptor of .NET
// is never equal, so that equality is the same in both directions. A cast instead of "as" threw an
// InvalidCastException.
public override bool Equals(object obj)
{
var other = obj as FileSystemSecurity2;
return other != null && ReferenceEquals(sd, other.sd);
}
public override int GetHashCode()
{
return sd != null ? sd.GetHashCode() : 0;
}
#endregion
public static void ConvertToFileSystemFlags(ApplyTo ApplyTo, out InheritanceFlags inheritanceFlags, out PropagationFlags propagationFlags)
{
inheritanceFlags = InheritanceFlags.None;
propagationFlags = PropagationFlags.None;
switch (ApplyTo)
{
case ApplyTo.FilesOnly:
inheritanceFlags = InheritanceFlags.ObjectInherit;
propagationFlags = PropagationFlags.InheritOnly;
break;
case ApplyTo.SubfoldersAndFilesOnly:
inheritanceFlags = InheritanceFlags.ObjectInherit | InheritanceFlags.ContainerInherit;
propagationFlags = PropagationFlags.InheritOnly;
break;
case ApplyTo.SubfoldersOnly:
inheritanceFlags = InheritanceFlags.ContainerInherit;
propagationFlags = PropagationFlags.InheritOnly;
break;
case ApplyTo.ThisFolderAndFiles:
inheritanceFlags = InheritanceFlags.ObjectInherit;
propagationFlags = PropagationFlags.None;
break;
case ApplyTo.ThisFolderAndSubfolders:
inheritanceFlags = InheritanceFlags.ContainerInherit;
propagationFlags = PropagationFlags.None;
break;
case ApplyTo.ThisFolderOnly:
inheritanceFlags = InheritanceFlags.None;
propagationFlags = PropagationFlags.None;
break;
case ApplyTo.ThisFolderSubfoldersAndFiles:
inheritanceFlags = InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit;
propagationFlags = PropagationFlags.None;
break;
case ApplyTo.FilesOnlyOneLevel:
inheritanceFlags = InheritanceFlags.ObjectInherit;
propagationFlags = PropagationFlags.InheritOnly | PropagationFlags.NoPropagateInherit;
break;
case ApplyTo.SubfoldersAndFilesOnlyOneLevel:
inheritanceFlags = InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit;
propagationFlags = PropagationFlags.InheritOnly | PropagationFlags.NoPropagateInherit;
break;
case ApplyTo.SubfoldersOnlyOneLevel:
inheritanceFlags = InheritanceFlags.ContainerInherit;
propagationFlags = PropagationFlags.InheritOnly | PropagationFlags.NoPropagateInherit;
break;
case ApplyTo.ThisFolderAndFilesOneLevel:
inheritanceFlags = InheritanceFlags.ObjectInherit;
propagationFlags = PropagationFlags.NoPropagateInherit;
break;
case ApplyTo.ThisFolderAndSubfoldersOneLevel:
inheritanceFlags = InheritanceFlags.ContainerInherit;
propagationFlags = PropagationFlags.NoPropagateInherit;
break;
case ApplyTo.ThisFolderSubfoldersAndFilesOneLevel:
inheritanceFlags = InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit;
propagationFlags = PropagationFlags.NoPropagateInherit;
break;
}
}
public static ApplyTo ConvertToApplyTo(InheritanceFlags InheritanceFlags, PropagationFlags PropagationFlags)
{
if (InheritanceFlags == InheritanceFlags.ObjectInherit & PropagationFlags == PropagationFlags.InheritOnly)
return ApplyTo.FilesOnly;
else if (InheritanceFlags == (InheritanceFlags.ObjectInherit | InheritanceFlags.ContainerInherit) & PropagationFlags == PropagationFlags.InheritOnly)
return ApplyTo.SubfoldersAndFilesOnly;
else if (InheritanceFlags == InheritanceFlags.ContainerInherit & PropagationFlags == PropagationFlags.InheritOnly)
return ApplyTo.SubfoldersOnly;
else if (InheritanceFlags == InheritanceFlags.ObjectInherit & PropagationFlags == PropagationFlags.None)
return ApplyTo.ThisFolderAndFiles;
else if (InheritanceFlags == InheritanceFlags.ContainerInherit & PropagationFlags == PropagationFlags.None)
return ApplyTo.ThisFolderAndSubfolders;
else if (InheritanceFlags == InheritanceFlags.None & PropagationFlags == PropagationFlags.None)
return ApplyTo.ThisFolderOnly;
else if (InheritanceFlags == (InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit) & PropagationFlags == PropagationFlags.None)
return ApplyTo.ThisFolderSubfoldersAndFiles;
else if (InheritanceFlags == (InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit) & PropagationFlags == PropagationFlags.NoPropagateInherit)
return ApplyTo.ThisFolderSubfoldersAndFilesOneLevel;
else if (InheritanceFlags == InheritanceFlags.ContainerInherit & PropagationFlags == PropagationFlags.NoPropagateInherit)
return ApplyTo.ThisFolderAndSubfoldersOneLevel;
else if (InheritanceFlags == InheritanceFlags.ObjectInherit & PropagationFlags == PropagationFlags.NoPropagateInherit)
return ApplyTo.ThisFolderAndFilesOneLevel;
else if (InheritanceFlags == (InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit) & PropagationFlags == (PropagationFlags.InheritOnly | PropagationFlags.NoPropagateInherit))
return ApplyTo.SubfoldersAndFilesOnlyOneLevel;
else if (InheritanceFlags == InheritanceFlags.ContainerInherit & PropagationFlags == (PropagationFlags.InheritOnly | PropagationFlags.NoPropagateInherit))
return ApplyTo.SubfoldersOnlyOneLevel;
else if (InheritanceFlags == InheritanceFlags.ObjectInherit & PropagationFlags == (PropagationFlags.InheritOnly | PropagationFlags.NoPropagateInherit))
return ApplyTo.FilesOnlyOneLevel;
throw new RightsConverionException("The combination of InheritanceFlags and PropagationFlags could not be translated");
}
public static FileSystemRights MapGenericRightsToFileSystemRights(uint originalRights)
{
try
{
var r = Enum.Parse(typeof(FileSystemRights), (originalRights).ToString());
if (r.ToString() == originalRights.ToString())
{
throw new ArgumentOutOfRangeException();
}
var fileSystemRights = (FileSystemRights)originalRights;
return fileSystemRights;
}
catch (Exception)
{
FileSystemRights rights = 0;
if (Convert.ToBoolean(originalRights & (uint)GenericRights.GENERIC_EXECUTE))
{
rights |= (FileSystemRights)MappedGenericRights.FILE_GENERIC_EXECUTE;
originalRights ^= (uint)GenericRights.GENERIC_EXECUTE;
}
if (Convert.ToBoolean(originalRights & (uint)GenericRights.GENERIC_READ))
{
rights |= (FileSystemRights)MappedGenericRights.FILE_GENERIC_READ;
originalRights ^= (uint)GenericRights.GENERIC_READ;
}
if (Convert.ToBoolean(originalRights & (uint)GenericRights.GENERIC_WRITE))
{
rights |= (FileSystemRights)MappedGenericRights.FILE_GENERIC_WRITE;
originalRights ^= (uint)GenericRights.GENERIC_WRITE;
}
if (Convert.ToBoolean(originalRights & (uint)GenericRights.GENERIC_ALL))
{
rights |= (FileSystemRights)MappedGenericRights.FILE_GENERIC_ALL;
originalRights ^= (uint)GenericRights.GENERIC_ALL;
}
//throw new RightsConverionException("Cannot convert GenericRights into FileSystemRights");
var remainingRights = (FileSystemRights)Enum.Parse(typeof(FileSystemRights), (originalRights).ToString());
rights |= remainingRights;
return rights;
}
}
}
}