|
|
|
|
using Alphaleonis.Win32.Filesystem;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Security.AccessControl;
|
|
|
|
|
|
|
|
|
|
namespace Security2
|
|
|
|
|
{
|
|
|
|
|
public partial class FileSystemAccessRule2
|
|
|
|
|
{
|
|
|
|
|
public static FileSystemAccessRule2 AddFileSystemAccessRule(FileSystemSecurity2 sd, IdentityReference2 account, FileSystemRights2 rights, AccessControlType type, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags)
|
|
|
|
|
{
|
|
|
|
|
if (type == AccessControlType.Allow)
|
|
|
|
|
rights = rights | FileSystemRights2.Synchronize;
|
|
|
|
|
|
|
|
|
|
FileSystemAccessRule ace = null;
|
|
|
|
|
|
|
|
|
|
if (sd.IsFile)
|
|
|
|
|
{
|
|
|
|
|
ace = (FileSystemAccessRule)sd.SecurityDescriptor.AccessRuleFactory(account, (int)rights, false, InheritanceFlags.None, PropagationFlags.None, type);
|
|
|
|
|
((FileSecurity)sd.SecurityDescriptor).AddAccessRule(ace);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ace = (FileSystemAccessRule)sd.SecurityDescriptor.AccessRuleFactory(account, (int)rights, false, inheritanceFlags, propagationFlags, type);
|
|
|
|
|
((DirectorySecurity)sd.SecurityDescriptor).AddAccessRule(ace);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ace;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static FileSystemAccessRule2 AddFileSystemAccessRule(FileSystemInfo item, IdentityReference2 account, FileSystemRights2 rights, AccessControlType type, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags)
|
|
|
|
|
{
|
|
|
|
|
if (type == AccessControlType.Allow)
|
|
|
|
|
rights = rights | FileSystemRights2.Synchronize;
|
|
|
|
|
|
fix: write only the sections of the security descriptor that a cmdlet changes
The access and audit cmdlets wrote the owner of an item back with the
entries they changed. For a DACL without the auto-inherit flag, Windows
returns the owner and the group even when only the DACL is read, and the
cmdlets wrote every section that the descriptor held. Without the Restore
privilege, or on a file server that refuses the owner, the write failed
with error 1307 (#34).
- Add-NTFSAccess, Clear-NTFSAccess, Add-NTFSAudit, and Clear-NTFSAudit
read only the DACL or the SACL. FileSystemSecurity2.Write(),
Remove-NTFSAccess, and Remove-NTFSAudit write only the sections they
read, which also fixes the access inheritance cmdlets.
- Read together with the SACL, the inherited entries of such a DACL lose
their inherited flag when the parent folder has no SACL, and the
cmdlets stored them as explicit copies. Get-NTFSSecurityDescriptor now
reads the DACL in a separate call.
- Set-NTFSSecurityDescriptor writes only the sections that changed since
they were read; an unchanged descriptor writes nothing (maintainer
decision of 2026-10-06).
- Clear-NTFSAudit writes nothing for an item without a SACL, and reports
an error without the Security privilege (maintainer decision).
The regression tests failed before and pass after the fix in Windows
PowerShell 5.1 and PowerShell 7, elevated and as a basic user.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
4 weeks ago
|
|
|
// Only the DACL, so that the owner isn't written back (#34) and the inherited entries keep their flag
|
|
|
|
|
var sd = new FileSystemSecurity2(item, AccessControlSections.Access);
|
|
|
|
|
|
|
|
|
|
var ace = AddFileSystemAccessRule(sd, account, rights, type, inheritanceFlags, propagationFlags);
|
|
|
|
|
|
|
|
|
|
sd.Write();
|
|
|
|
|
|
|
|
|
|
return ace;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static IEnumerable<FileSystemAccessRule2> AddFileSystemAccessRule(FileSystemInfo item, List<IdentityReference2> accounts, FileSystemRights2 rights, AccessControlType type, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags)
|
|
|
|
|
{
|
|
|
|
|
var aces = new List<FileSystemAccessRule2>();
|
|
|
|
|
|
|
|
|
|
foreach (var account in accounts)
|
|
|
|
|
{
|
|
|
|
|
aces.Add(AddFileSystemAccessRule(item, account, rights, type, inheritanceFlags, propagationFlags));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return aces;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static IEnumerable<FileSystemAccessRule2> AddFileSystemAccessRule(FileSystemSecurity2 sd, List<IdentityReference2> accounts, FileSystemRights2 rights, AccessControlType type, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags)
|
|
|
|
|
{
|
|
|
|
|
var aces = new List<FileSystemAccessRule2>();
|
|
|
|
|
|
|
|
|
|
foreach (var account in accounts)
|
|
|
|
|
{
|
|
|
|
|
aces.Add(AddFileSystemAccessRule(sd, account, rights, type, inheritanceFlags, propagationFlags));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return aces;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static FileSystemAccessRule2 AddFileSystemAccessRule(string path, IdentityReference2 account, FileSystemRights2 rights, AccessControlType type, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags)
|
|
|
|
|
{
|
|
|
|
|
if (type == AccessControlType.Allow)
|
|
|
|
|
rights = rights | FileSystemRights2.Synchronize;
|
|
|
|
|
|
|
|
|
|
FileSystemAccessRule ace = null;
|
|
|
|
|
|
|
|
|
|
if (File.Exists(path))
|
|
|
|
|
{
|
|
|
|
|
var item = new FileInfo(path);
|
|
|
|
|
ace = AddFileSystemAccessRule(item, account, rights, type, inheritanceFlags, propagationFlags);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
var item = new DirectoryInfo(path);
|
|
|
|
|
ace = AddFileSystemAccessRule(item, account, rights, type, inheritanceFlags, propagationFlags);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ace;
|
|
|
|
|
}
|
|
|
|
|
|
fix(security2): make the path overloads of AddFileSystemAccessRule and AddFileSystemAuditRule write at once
AddFileSystemAccessRule(string, List<IdentityReference2>, ...) and
AddFileSystemAuditRule(string, List<IdentityReference2>, ...) were
iterator methods (yield return), so nothing happened until the caller
enumerated the result: called as a statement, or assigned to a variable
that the caller never reads, they changed nothing. Their siblings, the
overloads for FileSystemInfo and FileSystemSecurity2 with a list, and
the single-account overloads, already built a list and wrote at once.
Both overloads now build the list and add the entries before returning,
like their siblings; a call that is never enumerated now adds the
entries just the same. An exception that adding an entry raises changes
only in timing: it is now thrown by the call itself, not by the later
enumeration of its result. The returned collection still holds one rule
per account, in the order of the account list, for both a file path and
a folder path.
Public signatures are unchanged.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
2 weeks ago
|
|
|
/// <summary>
|
|
|
|
|
/// Adds one access entry per account to the item at the path, and returns them in the order of
|
|
|
|
|
/// <paramref name="accounts"/>. Before 5.0.0, this overload was an iterator (a yield return in the loop
|
|
|
|
|
/// below), so nothing happened until the caller enumerated the result; called as a statement, or assigned to
|
|
|
|
|
/// a variable that is never read, it changed nothing. It now builds the list and writes the entries before
|
|
|
|
|
/// returning, like its FileSystemInfo and FileSystemSecurity2 siblings; an exception that adding an entry
|
|
|
|
|
/// raises is now thrown by this call, not by the later enumeration.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static IEnumerable<FileSystemAccessRule2> AddFileSystemAccessRule(string path, List<IdentityReference2> accounts, FileSystemRights2 rights, AccessControlType type, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags)
|
|
|
|
|
{
|
|
|
|
|
if (type == AccessControlType.Allow)
|
|
|
|
|
rights = rights | FileSystemRights2.Synchronize;
|
|
|
|
|
|
fix(security2): make the path overloads of AddFileSystemAccessRule and AddFileSystemAuditRule write at once
AddFileSystemAccessRule(string, List<IdentityReference2>, ...) and
AddFileSystemAuditRule(string, List<IdentityReference2>, ...) were
iterator methods (yield return), so nothing happened until the caller
enumerated the result: called as a statement, or assigned to a variable
that the caller never reads, they changed nothing. Their siblings, the
overloads for FileSystemInfo and FileSystemSecurity2 with a list, and
the single-account overloads, already built a list and wrote at once.
Both overloads now build the list and add the entries before returning,
like their siblings; a call that is never enumerated now adds the
entries just the same. An exception that adding an entry raises changes
only in timing: it is now thrown by the call itself, not by the later
enumeration of its result. The returned collection still holds one rule
per account, in the order of the account list, for both a file path and
a folder path.
Public signatures are unchanged.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
2 weeks ago
|
|
|
var aces = new List<FileSystemAccessRule2>();
|
|
|
|
|
|
|
|
|
|
if (File.Exists(path))
|
|
|
|
|
{
|
|
|
|
|
var item = new FileInfo(path);
|
|
|
|
|
foreach (var account in accounts)
|
|
|
|
|
{
|
fix(security2): make the path overloads of AddFileSystemAccessRule and AddFileSystemAuditRule write at once
AddFileSystemAccessRule(string, List<IdentityReference2>, ...) and
AddFileSystemAuditRule(string, List<IdentityReference2>, ...) were
iterator methods (yield return), so nothing happened until the caller
enumerated the result: called as a statement, or assigned to a variable
that the caller never reads, they changed nothing. Their siblings, the
overloads for FileSystemInfo and FileSystemSecurity2 with a list, and
the single-account overloads, already built a list and wrote at once.
Both overloads now build the list and add the entries before returning,
like their siblings; a call that is never enumerated now adds the
entries just the same. An exception that adding an entry raises changes
only in timing: it is now thrown by the call itself, not by the later
enumeration of its result. The returned collection still holds one rule
per account, in the order of the account list, for both a file path and
a folder path.
Public signatures are unchanged.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
2 weeks ago
|
|
|
aces.Add(AddFileSystemAccessRule(item, account, rights, type, inheritanceFlags, propagationFlags));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
var item = new DirectoryInfo(path);
|
|
|
|
|
foreach (var account in accounts)
|
|
|
|
|
{
|
fix(security2): make the path overloads of AddFileSystemAccessRule and AddFileSystemAuditRule write at once
AddFileSystemAccessRule(string, List<IdentityReference2>, ...) and
AddFileSystemAuditRule(string, List<IdentityReference2>, ...) were
iterator methods (yield return), so nothing happened until the caller
enumerated the result: called as a statement, or assigned to a variable
that the caller never reads, they changed nothing. Their siblings, the
overloads for FileSystemInfo and FileSystemSecurity2 with a list, and
the single-account overloads, already built a list and wrote at once.
Both overloads now build the list and add the entries before returning,
like their siblings; a call that is never enumerated now adds the
entries just the same. An exception that adding an entry raises changes
only in timing: it is now thrown by the call itself, not by the later
enumeration of its result. The returned collection still holds one rule
per account, in the order of the account list, for both a file path and
a folder path.
Public signatures are unchanged.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
2 weeks ago
|
|
|
aces.Add(AddFileSystemAccessRule(item, account, rights, type, inheritanceFlags, propagationFlags));
|
|
|
|
|
}
|
|
|
|
|
}
|
fix(security2): make the path overloads of AddFileSystemAccessRule and AddFileSystemAuditRule write at once
AddFileSystemAccessRule(string, List<IdentityReference2>, ...) and
AddFileSystemAuditRule(string, List<IdentityReference2>, ...) were
iterator methods (yield return), so nothing happened until the caller
enumerated the result: called as a statement, or assigned to a variable
that the caller never reads, they changed nothing. Their siblings, the
overloads for FileSystemInfo and FileSystemSecurity2 with a list, and
the single-account overloads, already built a list and wrote at once.
Both overloads now build the list and add the entries before returning,
like their siblings; a call that is never enumerated now adds the
entries just the same. An exception that adding an entry raises changes
only in timing: it is now thrown by the call itself, not by the later
enumeration of its result. The returned collection still holds one rule
per account, in the order of the account list, for both a file path and
a folder path.
Public signatures are unchanged.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
2 weeks ago
|
|
|
|
|
|
|
|
return aces;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static void AddFileSystemAccessRule(FileSystemAccessRule2 rule)
|
|
|
|
|
{
|
|
|
|
|
AddFileSystemAccessRule(rule.fullName,
|
|
|
|
|
rule.Account,
|
|
|
|
|
rule.AccessRights,
|
|
|
|
|
rule.AccessControlType,
|
|
|
|
|
rule.InheritanceFlags,
|
|
|
|
|
rule.PropagationFlags);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|