diff --git a/CHANGELOG.md b/CHANGELOG.md index 89cc288..f5bfddb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -103,5 +103,11 @@ The format is based on is omitted, and `-SecurityDescriptor` returns the effective access of the security descriptor; before, all three returned nothing or ignored the parameter +- Fix `Get-NTFSOrphanedAccess`, `Get-NTFSOrphanedAudit`, and + `Get-NTFSSimpleAccess`, which ignored `-Account` and `-SecurityDescriptor`; + `Get-NTFSOrphanedAudit` now writes one object per entry instead of one + collection per item, `Get-NTFSOrphanedAccess` no longer repeats the entries + of the previous item after a failed read, and the output of + `Get-NTFSSimpleAccess` has a table view [Unreleased]: https://github.com/raandree/NTFSSecurity/compare/4.2.6...HEAD diff --git a/Docs/Cmdlets/Get-NTFSOrphanedAccess.md b/Docs/Cmdlets/Get-NTFSOrphanedAccess.md index 22e89db..4207c87 100644 --- a/Docs/Cmdlets/Get-NTFSOrphanedAccess.md +++ b/Docs/Cmdlets/Get-NTFSOrphanedAccess.md @@ -33,7 +33,7 @@ An entry counts as orphaned only as long as the name resolution fails, and the c Relative paths are resolved against the current location, and the current location is searched when `-Path` is omitted. By default both explicit and inherited entries are returned, which means that the same orphaned entry appears on every item that inherits it; `-ExcludeInherited` reports it only on the item where it is defined. With `-Verbose`, the cmdlet reports the number of orphaned entries per item and the total at the end. -The `-Account` and `-SecurityDescriptor` parameters are inherited from `Get-NTFSAccess` and have no effect on this cmdlet. Entries are never filtered by account, and a security descriptor passed to `-SecurityDescriptor` is ignored; the cmdlet reads the current location instead. +`-Account` limits the result to the entries of one account, which you specify by its SID. With `-SecurityDescriptor`, the cmdlet examines a `Security2.FileSystemSecurity2` object that `Get-NTFSSecurityDescriptor` returned instead of reading the item again. ## EXAMPLES @@ -65,7 +65,7 @@ This command deletes the orphaned entries from the items they are defined on. Th ### -Account -This parameter is inherited from `Get-NTFSAccess` and has no effect. The cmdlet always returns the entries of all accounts that cannot be resolved. +Specifies the account whose orphaned entries are returned. Because the account cannot be resolved, specify it by its SID. When you omit the parameter, the cmdlet returns the entries of all accounts that cannot be resolved. ```yaml Type: IdentityReference2 @@ -129,7 +129,7 @@ Accept wildcard characters: False ### -SecurityDescriptor -This parameter is inherited from `Get-NTFSAccess` and has no effect. A security descriptor passed here is ignored, and the cmdlet searches the path in `-Path` or the current location instead. +Specifies one or more security descriptors that `Get-NTFSSecurityDescriptor` returned. The cmdlet examines the in-memory objects instead of reading the items again. A security descriptor contains information about the owner of the object, and the primary group of an object. The security descriptor also contains two access control lists (ACL). The first list is called the discretionary access control lists (DACL), and describes who should have access to an object and what type of access to grant. The second list is called the system access control lists (SACL) and defines what type of auditing to record for an object. @@ -156,11 +156,11 @@ One or more paths of files or folders, piped by value or by the property `FullNa ### Security2.FileSystemSecurity2[] -Security descriptors are accepted by the parameter binder but ignored by this cmdlet. +You can pipe the security descriptors that `Get-NTFSSecurityDescriptor` returns to this cmdlet. ### Security2.IdentityReference2 -An account is accepted by the parameter binder but ignored by this cmdlet. +An account name or a SID string binds to `-Account`. ## OUTPUTS @@ -174,6 +174,8 @@ When the module setting `EnablePrivileges` is `$true` (the default in the `Priva If the ACL of an item cannot be read because access is denied, the cmdlet tries once more after making the current account the owner of the item, and restores the previous owner afterwards. Changing the owner of an item requires the Take Ownership and Restore privileges, so this fallback only succeeds in an elevated session of an account that holds them. +Before 5.0.0, the cmdlet ignored `-Account` and `-SecurityDescriptor`, and after a path whose ACL could not be read, it returned the orphaned entries of the previous item again. + ## RELATED LINKS [Get-NTFSAccess](Get-NTFSAccess.md) diff --git a/Docs/Cmdlets/Get-NTFSOrphanedAudit.md b/Docs/Cmdlets/Get-NTFSOrphanedAudit.md index bf967d6..59a3229 100644 --- a/Docs/Cmdlets/Get-NTFSOrphanedAudit.md +++ b/Docs/Cmdlets/Get-NTFSOrphanedAudit.md @@ -33,7 +33,7 @@ An entry is reported as orphaned whenever the name resolution fails at that mome The cmdlet is built on `Get-NTFSAudit` and reads the SACL of every item in `-Path`, using the current location when you omit the parameter. `-Path` accepts pipeline input by value and by property name through its `FullName` alias, so the output of `Get-ChildItem`, `Get-ChildItem2`, and `Get-Item2` binds to it. `-ExcludeExplicit` and `-ExcludeInherited` narrow the entries that are examined, and `-Verbose` reports how many orphaned entries each item has and their total. -`Get-NTFSOrphanedAudit` inherits the `-Account` and `-SecurityDescriptor` parameters from `Get-NTFSAudit`, but it does not evaluate them. The entries are always read from the items in `-Path`, so a command that passes `-SecurityDescriptor` examines the current location instead of the descriptor. +`-Account` limits the result to the entries of one account, which you specify by its SID. With `-SecurityDescriptor`, the cmdlet examines a `Security2.FileSystemSecurity2` object that `Get-NTFSSecurityDescriptor` returned; a descriptor that was read without the Security privilege doesn't contain the audit entries, and the cmdlet writes an error for it. ## EXAMPLES @@ -60,7 +60,7 @@ PS C:\> $orphaned = Get-NTFSOrphanedAudit -Path C:\Data -Verbose PS C:\> $orphaned | Select-Object FullName, Account, AccessRights, AuditFlags ``` -This command stores the result in a variable and then lists the item, the unresolved SID, the audited rights, and the audit flags of every orphaned entry. Storing the result first is necessary because the cmdlet writes one collection per item rather than one object per entry. +This command stores the result in a variable and then lists the item, the unresolved SID, the audited rights, and the audit flags of every orphaned entry. ### Example 4: Check the current location @@ -74,7 +74,7 @@ This command examines the current location, because `-Path` is omitted. ### -Account -Specifies an account in the base cmdlet `Get-NTFSAudit`. `Get-NTFSOrphanedAudit` inherits the parameter but does not evaluate it, so the result always contains the entries of every account whose SID cannot be resolved. +Specifies the account whose orphaned audit entries are returned. Because the account cannot be resolved, specify it by its SID. When you omit the parameter, the cmdlet returns the entries of all accounts that cannot be resolved. ```yaml Type: IdentityReference2 @@ -138,7 +138,7 @@ Accept wildcard characters: False ### -SecurityDescriptor -Specifies one or more security descriptors in the base cmdlet `Get-NTFSAudit`. `Get-NTFSOrphanedAudit` inherits the parameter but does not read from it; the cmdlet always examines the items in `-Path` and therefore the current location when `-Path` is omitted. Use `Get-NTFSAudit -SecurityDescriptor` to inspect the audit entries of a security descriptor. +Specifies one or more security descriptors that `Get-NTFSSecurityDescriptor` returned. The cmdlet examines the in-memory objects instead of reading the items again. ```yaml Type: FileSystemSecurity2[] @@ -167,13 +167,13 @@ Security descriptors bind to the inherited `-SecurityDescriptor` parameter, but ### Security2.IdentityReference2 -An account name or a SID string binds to the inherited `-Account` parameter, which this cmdlet does not evaluate. +An account name or a SID string binds to `-Account`. ## OUTPUTS ### Security2.FileSystemAuditRule2 -The cmdlet returns the audit entries whose account SID cannot be translated into a name, each with the item, the unresolved account, the audited access rights, the audit flags, and the inheritance information. The entries of an item are written as a single collection rather than one object per entry, so store the result in a variable before you filter or format it; an item without orphaned entries still produces one empty collection, and a command placed directly after this cmdlet in the pipeline receives the collection instead of the individual entries. +The cmdlet returns the audit entries whose account SID cannot be translated into a name, each with the item, the unresolved account, the audited access rights, the audit flags, and the inheritance information. The cmdlet writes one object per entry. ## NOTES @@ -183,6 +183,8 @@ Reading the SACL requires the Security privilege (`SeSecurityPrivilege`, "Manage If an item cannot be read, the cmdlet writes a warning and continues with the next item. Unlike `Get-NTFSAudit`, it does not try to take ownership of the item when access is denied. +Before 5.0.0, the cmdlet ignored `-Account` and `-SecurityDescriptor` and wrote the entries of each item as one collection. + ## RELATED LINKS [Get-NTFSAudit](Get-NTFSAudit.md) diff --git a/Docs/Cmdlets/Get-NTFSSimpleAccess.md b/Docs/Cmdlets/Get-NTFSSimpleAccess.md index ebe3c12..9cb6423 100644 --- a/Docs/Cmdlets/Get-NTFSSimpleAccess.md +++ b/Docs/Cmdlets/Get-NTFSSimpleAccess.md @@ -33,7 +33,7 @@ The second simplification is that repetitions are left out. The first folder the `-IncludeRootFolder` is on by default and adds the parent folder of the first path as the baseline for the comparison, which is why the first result usually belongs to the folder above the one that was asked for. Use `-IncludeRootFolder:$false` to start the comparison at the first path itself. -The cmdlet only processes folders; a path that points to a file is skipped silently. Relative paths are resolved against the current location, and the current location is used when `-Path` is omitted. `-ExcludeInherited` and `-ExcludeExplicit` work as in `Get-NTFSAccess`, while `-Account` and `-SecurityDescriptor` are inherited from that cmdlet and have no effect here. +The cmdlet only processes folders; a path that points to a file is skipped silently. Relative paths are resolved against the current location, and the current location is used when `-Path` is omitted. `-ExcludeInherited`, `-ExcludeExplicit`, and `-Account` work as in `Get-NTFSAccess`. With `-SecurityDescriptor`, the cmdlet reports the entries of a `Security2.FileSystemSecurity2` object that `Get-NTFSSecurityDescriptor` returned, without comparing them with a parent folder. ## EXAMPLES @@ -65,7 +65,7 @@ This command shows the explicit entries of `C:\Data` in simplified form and leav ### -Account -This parameter is inherited from `Get-NTFSAccess` and has no effect. The cmdlet always returns the entries of all accounts. +Specifies the account whose entries are returned. When you omit the parameter, the cmdlet returns the entries of all accounts. ```yaml Type: IdentityReference2 @@ -145,7 +145,7 @@ Accept wildcard characters: False ### -SecurityDescriptor -This parameter is inherited from `Get-NTFSAccess` and has no effect. A security descriptor passed here is ignored, and the cmdlet reads the path in `-Path` or the current location instead. +Specifies one or more security descriptors that `Get-NTFSSecurityDescriptor` returned. The cmdlet reports the entries of the in-memory objects instead of reading the items again. A security descriptor contains information about the owner of the object, and the primary group of an object. The security descriptor also contains two access control lists (ACL). The first list is called the discretionary access control lists (DACL), and describes who should have access to an object and what type of access to grant. The second list is called the system access control lists (SACL) and defines what type of auditing to record for an object. @@ -172,17 +172,17 @@ One or more paths of folders, piped by value or by the property `FullName`. ### Security2.FileSystemSecurity2[] -Security descriptors are accepted by the parameter binder but ignored by this cmdlet. +You can pipe the security descriptors that `Get-NTFSSecurityDescriptor` returns to this cmdlet. ### Security2.IdentityReference2 -An account is accepted by the parameter binder but ignored by this cmdlet. +An account name or a SID string binds to `-Account`. ## OUTPUTS ### Security2.SimpleFileSystemAccessRule -One object per reported entry, with the folder in `FullName` and `Name`, the account in `Identity`, the access type in `AccessControlType`, and the simplified rights `Read`, `Write`, and `Delete` in `AccessRights`. +One object per reported entry, with the folder in `FullName` and `Name`, the account in `Identity`, the access type in `AccessControlType`, and the simplified rights `Read`, `Write`, and `Delete` in `AccessRights`. The default view is a table with the `Account`, `Access Rights`, and `Type` columns, grouped by folder. ## NOTES @@ -190,6 +190,8 @@ When the module setting `EnablePrivileges` is `$true` (the default in the `Priva The simplified rights hide which exact rights an account holds. Use `Get-NTFSAccess` when you need the full access control entry, and `Get-NTFSEffectiveAccess` when you need the rights that result from all entries together. +Before 5.0.0, the cmdlet ignored `-Account` and `-SecurityDescriptor`, and its output had no table view. + ## RELATED LINKS [Get-NTFSAccess](Get-NTFSAccess.md) diff --git a/NTFSSecurity/AccessCmdlets/GetOrphanedAccess.cs b/NTFSSecurity/AccessCmdlets/GetOrphanedAccess.cs index 3e7ba2d..fbe88ce 100644 --- a/NTFSSecurity/AccessCmdlets/GetOrphanedAccess.cs +++ b/NTFSSecurity/AccessCmdlets/GetOrphanedAccess.cs @@ -15,11 +15,21 @@ namespace NTFSSecurity protected override void ProcessRecord() { - IEnumerable acl = null; - FileSystemInfo item = null; + if (ParameterSetName == "SD") + { + foreach (var sd in securityDescriptors) + { + WriteOrphanedAces(FileSystemAccessRule2.GetFileSystemAccessRules(sd, !ExcludeExplicit, !ExcludeInherited, getInheritedFrom), sd.FullName); + } + + return; + } foreach (var path in paths) { + FileSystemInfo item = null; + IEnumerable acl = null; + try { item = this.GetFileSystemInfo2(path); @@ -50,25 +60,33 @@ namespace NTFSSecurity catch (Exception ex2) { this.WriteError(new ErrorRecord(ex2, "AddAceError", ErrorCategory.WriteError, path)); + continue; } } catch (Exception ex) { this.WriteWarning(string.Format("Could not read item {0}. The error was: {1}", path, ex.Message)); + continue; } - finally - { - if (acl != null) - { - var orphanedAces = acl.Where(ace => string.IsNullOrEmpty(ace.Account.AccountName)); - orphanedSidCount += orphanedAces.Count(); - WriteVerbose(string.Format("Item {0} knows about {1} orphaned SIDs in its ACL", path, orphanedAces.Count())); + WriteOrphanedAces(acl, path); + } + } - orphanedAces.ForEach(ace => WriteObject(ace)); - } - } + private void WriteOrphanedAces(IEnumerable acl, string path) + { + var orphanedAces = acl.Where(ace => string.IsNullOrEmpty(ace.Account.AccountName)); + if (Account != null) + { + orphanedAces = orphanedAces.Where(ace => ace.Account == Account); } + + var orphanedAceList = orphanedAces.ToList(); + orphanedSidCount += orphanedAceList.Count; + + WriteVerbose(string.Format("Item {0} knows about {1} orphaned SIDs in its ACL", path, orphanedAceList.Count)); + + orphanedAceList.ForEach(ace => WriteObject(ace)); } protected override void EndProcessing() @@ -77,4 +95,4 @@ namespace NTFSSecurity base.EndProcessing(); } } -} +} \ No newline at end of file diff --git a/NTFSSecurity/AuditCmdlets/Get-OrphanedAudit.cs b/NTFSSecurity/AuditCmdlets/Get-OrphanedAudit.cs index 91ce03f..336adab 100644 --- a/NTFSSecurity/AuditCmdlets/Get-OrphanedAudit.cs +++ b/NTFSSecurity/AuditCmdlets/Get-OrphanedAudit.cs @@ -15,11 +15,28 @@ namespace NTFSSecurity.AuditCmdlets protected override void ProcessRecord() { - IEnumerable acl; - FileSystemInfo item = null; + if (ParameterSetName == "SD") + { + foreach (var sd in securityDescriptors) + { + if (!sd.HasAuditSection) + { + var ex = new InvalidOperationException(string.Format( + "The security descriptor of '{0}' doesn't contain the audit entries, because it was read without the Security privilege.", sd.FullName)); + WriteError(new ErrorRecord(ex, "ReadSecurityError", ErrorCategory.InvalidData, sd)); + continue; + } + + WriteOrphanedAces(FileSystemAuditRule2.GetFileSystemAuditRules(sd, !ExcludeExplicit, !ExcludeInherited, getInheritedFrom), sd.FullName); + } + + return; + } foreach (var p in paths) { + FileSystemInfo item = null; + try { item = this.GetFileSystemInfo2(p); @@ -32,13 +49,7 @@ namespace NTFSSecurity.AuditCmdlets try { - acl = FileSystemAuditRule2.GetFileSystemAuditRules(item, !ExcludeExplicit, !ExcludeInherited, getInheritedFrom); - - var orphanedAces = acl.Where(ace => string.IsNullOrEmpty(ace.Account.AccountName)); - orphanedSidCount += orphanedAces.Count(); - - this.WriteVerbose(string.Format("Item {0} knows about {1} orphaned SIDs in its ACL", p, orphanedAces.Count())); - this.WriteObject(orphanedAces); + WriteOrphanedAces(FileSystemAuditRule2.GetFileSystemAuditRules(item, !ExcludeExplicit, !ExcludeInherited, getInheritedFrom), p); } catch (Exception ex) { @@ -47,10 +58,27 @@ namespace NTFSSecurity.AuditCmdlets } } + private void WriteOrphanedAces(IEnumerable acl, string path) + { + var orphanedAces = acl.Where(ace => string.IsNullOrEmpty(ace.Account.AccountName)); + if (Account != null) + { + orphanedAces = orphanedAces.Where(ace => ace.Account == Account); + } + + var orphanedAceList = orphanedAces.ToList(); + orphanedSidCount += orphanedAceList.Count; + + this.WriteVerbose(string.Format("Item {0} knows about {1} orphaned SIDs in its ACL", path, orphanedAceList.Count)); + + // One object per entry, not one collection per item + orphanedAceList.ForEach(ace => WriteObject(ace)); + } + protected override void EndProcessing() { WriteVerbose(string.Format("Total orphaned Access Control Enties: {0}", orphanedSidCount)); base.EndProcessing(); } } -} +} \ No newline at end of file diff --git a/NTFSSecurity/NTFSSecurity.format.ps1xml b/NTFSSecurity/NTFSSecurity.format.ps1xml index 455df7c..5bea1cb 100644 --- a/NTFSSecurity/NTFSSecurity.format.ps1xml +++ b/NTFSSecurity/NTFSSecurity.format.ps1xml @@ -234,6 +234,56 @@ + + Security2.SimpleFileSystemAccessRule + + Security2.SimpleFileSystemAccessRule + + + FullName + SimpleFileSystemAccessRule2Grouping + + + + + 35 + + + + + + + + + + + + + + + + if ((Get-Module NTFSSecurity).PrivateData.ShowAccountSid) + { + '{0} ({1})' -f $_.Identity.AccountName, $_.Identity.Sid + } + else + { + $_.Identity.ToString() + } + + + + AccessRights + + + AccessControlType + + + + + + + Security2.SimpleFileSystemAuditRule2 diff --git a/NTFSSecurity/SimpleAccessCmdlets/SimpleAccessCmdlets.cs b/NTFSSecurity/SimpleAccessCmdlets/SimpleAccessCmdlets.cs index 123e04a..43970ec 100644 --- a/NTFSSecurity/SimpleAccessCmdlets/SimpleAccessCmdlets.cs +++ b/NTFSSecurity/SimpleAccessCmdlets/SimpleAccessCmdlets.cs @@ -32,6 +32,18 @@ namespace NTFSSecurity protected override void ProcessRecord() { + if (ParameterSetName == "SD") + { + foreach (var sd in securityDescriptors) + { + var sdAcl = FilterAccount(FileSystemAccessRule2.GetFileSystemAccessRules(sd, !ExcludeExplicit, !ExcludeInherited).Select(ace => ace.ToSimpleFileSystemAccessRule2())).ToList(); + aceList.AddRange(sdAcl); + sdAcl.ForEach(ace => WriteObject(ace)); + } + + return; + } + //as this cmdlet retreives also the current working folder to show the permissions. if (includeRootFolder & isFirstFolder) { @@ -57,7 +69,7 @@ namespace NTFSSecurity WriteVerbose(string.Format("New folder: {0}", item.FullName)); directoryList.Add(item); - var acl = FileSystemAccessRule2.GetFileSystemAccessRules(item, !ExcludeExplicit, !ExcludeInherited).Select(ace => ace.ToSimpleFileSystemAccessRule2()); + var acl = FilterAccount(FileSystemAccessRule2.GetFileSystemAccessRules(item, !ExcludeExplicit, !ExcludeInherited).Select(ace => ace.ToSimpleFileSystemAccessRule2())).ToList(); try { @@ -112,6 +124,11 @@ namespace NTFSSecurity } } } + + private IEnumerable FilterAccount(IEnumerable acl) + { + return Account == null ? acl : acl.Where(ace => ace.Identity == Account); + } } #endregion diff --git a/NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml b/NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml index bdc029f..f0ac9a8 100644 --- a/NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml +++ b/NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml @@ -5513,7 +5513,7 @@ PS C:\> Get-NTFSAudit -SecurityDescriptor $sd Reads the discretionary access control list (DACL) of a file or a folder like `Get-NTFSAccess` and returns only the access control entries whose account name is empty. The account name of an entry is empty when Windows cannot translate the SID stored in the entry into an account name, which is what remains after the account the entry was created for has been deleted. An entry counts as orphaned only as long as the name resolution fails, and the cmdlet cannot tell a deleted account from an account that cannot be looked up right now. A domain controller that is unreachable, a broken trust, or a SID from a domain the computer does not know make intact entries look orphaned as well. Confirm that the accounts are really gone before you remove anything, and run the search from a computer that can resolve all domains involved. Relative paths are resolved against the current location, and the current location is searched when `-Path` is omitted. By default both explicit and inherited entries are returned, which means that the same orphaned entry appears on every item that inherits it; `-ExcludeInherited` reports it only on the item where it is defined. With `-Verbose`, the cmdlet reports the number of orphaned entries per item and the total at the end. - The `-Account` and `-SecurityDescriptor` parameters are inherited from `Get-NTFSAccess` and have no effect on this cmdlet. Entries are never filtered by account, and a security descriptor passed to `-SecurityDescriptor` is ignored; the cmdlet reads the current location instead. + `-Account` limits the result to the entries of one account, which you specify by its SID. With `-SecurityDescriptor`, the cmdlet examines a `Security2.FileSystemSecurity2` object that `Get-NTFSSecurityDescriptor` returned instead of reading the item again. @@ -5533,7 +5533,7 @@ PS C:\> Get-NTFSAudit -SecurityDescriptor $sd Account - This parameter is inherited from `Get-NTFSAccess` and has no effect. The cmdlet always returns the entries of all accounts that cannot be resolved. + Specifies the account whose orphaned entries are returned. Because the account cannot be resolved, specify it by its SID. When you omit the parameter, the cmdlet returns the entries of all accounts that cannot be resolved. IdentityReference2 @@ -5570,7 +5570,7 @@ PS C:\> Get-NTFSAudit -SecurityDescriptor $sd SecurityDescriptor - This parameter is inherited from `Get-NTFSAccess` and has no effect. A security descriptor passed here is ignored, and the cmdlet searches the path in `-Path` or the current location instead. + Specifies one or more security descriptors that `Get-NTFSSecurityDescriptor` returned. The cmdlet examines the in-memory objects instead of reading the items again. A security descriptor contains information about the owner of the object, and the primary group of an object. The security descriptor also contains two access control lists (ACL). The first list is called the discretionary access control lists (DACL), and describes who should have access to an object and what type of access to grant. The second list is called the system access control lists (SACL) and defines what type of auditing to record for an object. FileSystemSecurity2[] @@ -5583,7 +5583,7 @@ PS C:\> Get-NTFSAudit -SecurityDescriptor $sd Account - This parameter is inherited from `Get-NTFSAccess` and has no effect. The cmdlet always returns the entries of all accounts that cannot be resolved. + Specifies the account whose orphaned entries are returned. Because the account cannot be resolved, specify it by its SID. When you omit the parameter, the cmdlet returns the entries of all accounts that cannot be resolved. IdentityReference2 @@ -5620,7 +5620,7 @@ PS C:\> Get-NTFSAudit -SecurityDescriptor $sd Account - This parameter is inherited from `Get-NTFSAccess` and has no effect. The cmdlet always returns the entries of all accounts that cannot be resolved. + Specifies the account whose orphaned entries are returned. Because the account cannot be resolved, specify it by its SID. When you omit the parameter, the cmdlet returns the entries of all accounts that cannot be resolved. IdentityReference2 @@ -5668,7 +5668,7 @@ PS C:\> Get-NTFSAudit -SecurityDescriptor $sd SecurityDescriptor - This parameter is inherited from `Get-NTFSAccess` and has no effect. A security descriptor passed here is ignored, and the cmdlet searches the path in `-Path` or the current location instead. + Specifies one or more security descriptors that `Get-NTFSSecurityDescriptor` returned. The cmdlet examines the in-memory objects instead of reading the items again. A security descriptor contains information about the owner of the object, and the primary group of an object. The security descriptor also contains two access control lists (ACL). The first list is called the discretionary access control lists (DACL), and describes who should have access to an object and what type of access to grant. The second list is called the system access control lists (SACL) and defines what type of auditing to record for an object. FileSystemSecurity2[] @@ -5693,7 +5693,7 @@ PS C:\> Get-NTFSAudit -SecurityDescriptor $sd Security2.FileSystemSecurity2[] - Security descriptors are accepted by the parameter binder but ignored by this cmdlet. + You can pipe the security descriptors that `Get-NTFSSecurityDescriptor` returns to this cmdlet. @@ -5701,7 +5701,7 @@ PS C:\> Get-NTFSAudit -SecurityDescriptor $sd Security2.IdentityReference2 - An account is accepted by the parameter binder but ignored by this cmdlet. + An account name or a SID string binds to `-Account`. @@ -5719,6 +5719,7 @@ PS C:\> Get-NTFSAudit -SecurityDescriptor $sd When the module setting `EnablePrivileges` is `$true` (the default in the `PrivateData` section of NTFSSecurity.psd1), this cmdlet tries to enable the Backup, Restore, Take Ownership, and Security privileges while it runs and disables the privileges it enabled when it finishes. These privileges are only available in an elevated session of an account that holds them, such as a member of the local Administrators group. If a privilege cannot be enabled, the cmdlet continues without it and writes a debug message. If the ACL of an item cannot be read because access is denied, the cmdlet tries once more after making the current account the owner of the item, and restores the previous owner afterwards. Changing the owner of an item requires the Take Ownership and Restore privileges, so this fallback only succeeds in an elevated session of an account that holds them. + Before 5.0.0, the cmdlet ignored `-Account` and `-SecurityDescriptor`, and after a path whose ACL could not be read, it returned the orphaned entries of the previous item again. @@ -5784,7 +5785,7 @@ PS C:\> Get-NTFSAudit -SecurityDescriptor $sd The `Get-NTFSOrphanedAudit` cmdlet returns the audit entries of a file or folder whose account cannot be translated into a name. An entry is called orphaned when its security identifier (SID) is still stored in the system access control list (SACL) but Windows cannot map that SID to a user or group, which usually happens after the account was deleted. Orphaned entries are shown with their SID instead of a name, and they keep auditing a security principal that no longer exists. An entry is reported as orphaned whenever the name resolution fails at that moment, not only when the account is really gone. A domain account whose domain controller cannot be reached, an account from a domain whose trust relationship is broken, and an account from a forest the computer currently cannot contact all look exactly like a deleted account. Verify that an account no longer exists before you remove its entries with `Remove-NTFSAudit`. The cmdlet is built on `Get-NTFSAudit` and reads the SACL of every item in `-Path`, using the current location when you omit the parameter. `-Path` accepts pipeline input by value and by property name through its `FullName` alias, so the output of `Get-ChildItem`, `Get-ChildItem2`, and `Get-Item2` binds to it. `-ExcludeExplicit` and `-ExcludeInherited` narrow the entries that are examined, and `-Verbose` reports how many orphaned entries each item has and their total. - `Get-NTFSOrphanedAudit` inherits the `-Account` and `-SecurityDescriptor` parameters from `Get-NTFSAudit`, but it does not evaluate them. The entries are always read from the items in `-Path`, so a command that passes `-SecurityDescriptor` examines the current location instead of the descriptor. + `-Account` limits the result to the entries of one account, which you specify by its SID. With `-SecurityDescriptor`, the cmdlet examines a `Security2.FileSystemSecurity2` object that `Get-NTFSSecurityDescriptor` returned; a descriptor that was read without the Security privilege doesn't contain the audit entries, and the cmdlet writes an error for it. @@ -5804,7 +5805,7 @@ PS C:\> Get-NTFSAudit -SecurityDescriptor $sd Account - Specifies an account in the base cmdlet `Get-NTFSAudit`. `Get-NTFSOrphanedAudit` inherits the parameter but does not evaluate it, so the result always contains the entries of every account whose SID cannot be resolved. + Specifies the account whose orphaned audit entries are returned. Because the account cannot be resolved, specify it by its SID. When you omit the parameter, the cmdlet returns the entries of all accounts that cannot be resolved. IdentityReference2 @@ -5841,7 +5842,7 @@ PS C:\> Get-NTFSAudit -SecurityDescriptor $sd SecurityDescriptor - Specifies one or more security descriptors in the base cmdlet `Get-NTFSAudit`. `Get-NTFSOrphanedAudit` inherits the parameter but does not read from it; the cmdlet always examines the items in `-Path` and therefore the current location when `-Path` is omitted. Use `Get-NTFSAudit -SecurityDescriptor` to inspect the audit entries of a security descriptor. + Specifies one or more security descriptors that `Get-NTFSSecurityDescriptor` returned. The cmdlet examines the in-memory objects instead of reading the items again. FileSystemSecurity2[] @@ -5853,7 +5854,7 @@ PS C:\> Get-NTFSAudit -SecurityDescriptor $sd Account - Specifies an account in the base cmdlet `Get-NTFSAudit`. `Get-NTFSOrphanedAudit` inherits the parameter but does not evaluate it, so the result always contains the entries of every account whose SID cannot be resolved. + Specifies the account whose orphaned audit entries are returned. Because the account cannot be resolved, specify it by its SID. When you omit the parameter, the cmdlet returns the entries of all accounts that cannot be resolved. IdentityReference2 @@ -5890,7 +5891,7 @@ PS C:\> Get-NTFSAudit -SecurityDescriptor $sd Account - Specifies an account in the base cmdlet `Get-NTFSAudit`. `Get-NTFSOrphanedAudit` inherits the parameter but does not evaluate it, so the result always contains the entries of every account whose SID cannot be resolved. + Specifies the account whose orphaned audit entries are returned. Because the account cannot be resolved, specify it by its SID. When you omit the parameter, the cmdlet returns the entries of all accounts that cannot be resolved. IdentityReference2 @@ -5938,7 +5939,7 @@ PS C:\> Get-NTFSAudit -SecurityDescriptor $sd SecurityDescriptor - Specifies one or more security descriptors in the base cmdlet `Get-NTFSAudit`. `Get-NTFSOrphanedAudit` inherits the parameter but does not read from it; the cmdlet always examines the items in `-Path` and therefore the current location when `-Path` is omitted. Use `Get-NTFSAudit -SecurityDescriptor` to inspect the audit entries of a security descriptor. + Specifies one or more security descriptors that `Get-NTFSSecurityDescriptor` returned. The cmdlet examines the in-memory objects instead of reading the items again. FileSystemSecurity2[] @@ -5970,7 +5971,7 @@ PS C:\> Get-NTFSAudit -SecurityDescriptor $sd Security2.IdentityReference2 - An account name or a SID string binds to the inherited `-Account` parameter, which this cmdlet does not evaluate. + An account name or a SID string binds to `-Account`. @@ -5980,7 +5981,7 @@ PS C:\> Get-NTFSAudit -SecurityDescriptor $sd Security2.FileSystemAuditRule2 - The cmdlet returns the audit entries whose account SID cannot be translated into a name, each with the item, the unresolved account, the audited access rights, the audit flags, and the inheritance information. The entries of an item are written as a single collection rather than one object per entry, so store the result in a variable before you filter or format it; an item without orphaned entries still produces one empty collection, and a command placed directly after this cmdlet in the pipeline receives the collection instead of the individual entries. + The cmdlet returns the audit entries whose account SID cannot be translated into a name, each with the item, the unresolved account, the audited access rights, the audit flags, and the inheritance information. The cmdlet writes one object per entry. @@ -5989,6 +5990,7 @@ PS C:\> Get-NTFSAudit -SecurityDescriptor $sd When the module setting `EnablePrivileges` is `$true` (the default in the `PrivateData` section of NTFSSecurity.psd1), this cmdlet tries to enable the Backup, Restore, Take Ownership, and Security privileges while it runs and disables the privileges it enabled when it finishes. These privileges are only available in an elevated session of an account that holds them, such as a member of the local Administrators group. If a privilege cannot be enabled, the cmdlet continues without it and writes a debug message. Reading the SACL requires the Security privilege (`SeSecurityPrivilege`, "Manage auditing and security log"), so run this cmdlet in an elevated session of an account that holds that privilege. Without it the cmdlet reads the security descriptor without its SACL and reports no orphaned entries at all, which looks the same as a tree that has none. If an item cannot be read, the cmdlet writes a warning and continues with the next item. Unlike `Get-NTFSAudit`, it does not try to take ownership of the item when access is denied. + Before 5.0.0, the cmdlet ignored `-Account` and `-SecurityDescriptor` and wrote the entries of each item as one collection. @@ -6011,7 +6013,7 @@ PS C:\> Get-NTFSAudit -SecurityDescriptor $sd PS C:\> $orphaned = Get-NTFSOrphanedAudit -Path C:\Data -Verbose PS C:\> $orphaned | Select-Object FullName, Account, AccessRights, AuditFlags - This command stores the result in a variable and then lists the item, the unresolved SID, the audited rights, and the audit flags of every orphaned entry. Storing the result first is necessary because the cmdlet writes one collection per item rather than one object per entry. + This command stores the result in a variable and then lists the item, the unresolved SID, the audited rights, and the audit flags of every orphaned entry. @@ -6360,7 +6362,7 @@ PS C:\Data> Get-NTFSSecurityDescriptor Reads the access control entries of folders and writes them as `Security2.SimpleFileSystemAccessRule` objects whose rights are reduced to the three values `Read`, `Write`, and `Delete`. Reading rights such as `ReadAttributes` or `Traverse` become `Read`, changing rights such as `CreateFiles`, `WriteAttributes`, `ChangePermissions`, or `TakeOwnership` become `Write`, and `Delete` and `DeleteSubdirectoriesAndFiles` become `Delete`; `FullControl` becomes all three. The result answers who may read, change, or delete in a folder without the detail of the full ACL. The second simplification is that repetitions are left out. The first folder the cmdlet processes is reported with all of its entries, and for every folder that follows only the entries are reported that its parent folder does not already cover. An entry is covered when the parent has an entry for the same account and access type that includes at least the same simple rights. This makes a recursive listing show where permissions actually change instead of repeating the inherited ones on every level, and it requires the parent folder to be processed before its children, which `Get-ChildItem`, `Get-ChildItem2`, and `Get-Item2` do by default. `-IncludeRootFolder` is on by default and adds the parent folder of the first path as the baseline for the comparison, which is why the first result usually belongs to the folder above the one that was asked for. Use `-IncludeRootFolder:$false` to start the comparison at the first path itself. - The cmdlet only processes folders; a path that points to a file is skipped silently. Relative paths are resolved against the current location, and the current location is used when `-Path` is omitted. `-ExcludeInherited` and `-ExcludeExplicit` work as in `Get-NTFSAccess`, while `-Account` and `-SecurityDescriptor` are inherited from that cmdlet and have no effect here. + The cmdlet only processes folders; a path that points to a file is skipped silently. Relative paths are resolved against the current location, and the current location is used when `-Path` is omitted. `-ExcludeInherited`, `-ExcludeExplicit`, and `-Account` work as in `Get-NTFSAccess`. With `-SecurityDescriptor`, the cmdlet reports the entries of a `Security2.FileSystemSecurity2` object that `Get-NTFSSecurityDescriptor` returned, without comparing them with a parent folder. @@ -6380,7 +6382,7 @@ PS C:\Data> Get-NTFSSecurityDescriptor Account - This parameter is inherited from `Get-NTFSAccess` and has no effect. The cmdlet always returns the entries of all accounts. + Specifies the account whose entries are returned. When you omit the parameter, the cmdlet returns the entries of all accounts. IdentityReference2 @@ -6428,7 +6430,7 @@ PS C:\Data> Get-NTFSSecurityDescriptor SecurityDescriptor - This parameter is inherited from `Get-NTFSAccess` and has no effect. A security descriptor passed here is ignored, and the cmdlet reads the path in `-Path` or the current location instead. + Specifies one or more security descriptors that `Get-NTFSSecurityDescriptor` returned. The cmdlet reports the entries of the in-memory objects instead of reading the items again. A security descriptor contains information about the owner of the object, and the primary group of an object. The security descriptor also contains two access control lists (ACL). The first list is called the discretionary access control lists (DACL), and describes who should have access to an object and what type of access to grant. The second list is called the system access control lists (SACL) and defines what type of auditing to record for an object. FileSystemSecurity2[] @@ -6441,7 +6443,7 @@ PS C:\Data> Get-NTFSSecurityDescriptor Account - This parameter is inherited from `Get-NTFSAccess` and has no effect. The cmdlet always returns the entries of all accounts. + Specifies the account whose entries are returned. When you omit the parameter, the cmdlet returns the entries of all accounts. IdentityReference2 @@ -6489,7 +6491,7 @@ PS C:\Data> Get-NTFSSecurityDescriptor Account - This parameter is inherited from `Get-NTFSAccess` and has no effect. The cmdlet always returns the entries of all accounts. + Specifies the account whose entries are returned. When you omit the parameter, the cmdlet returns the entries of all accounts. IdentityReference2 @@ -6549,7 +6551,7 @@ PS C:\Data> Get-NTFSSecurityDescriptor SecurityDescriptor - This parameter is inherited from `Get-NTFSAccess` and has no effect. A security descriptor passed here is ignored, and the cmdlet reads the path in `-Path` or the current location instead. + Specifies one or more security descriptors that `Get-NTFSSecurityDescriptor` returned. The cmdlet reports the entries of the in-memory objects instead of reading the items again. A security descriptor contains information about the owner of the object, and the primary group of an object. The security descriptor also contains two access control lists (ACL). The first list is called the discretionary access control lists (DACL), and describes who should have access to an object and what type of access to grant. The second list is called the system access control lists (SACL) and defines what type of auditing to record for an object. FileSystemSecurity2[] @@ -6574,7 +6576,7 @@ PS C:\Data> Get-NTFSSecurityDescriptor Security2.FileSystemSecurity2[] - Security descriptors are accepted by the parameter binder but ignored by this cmdlet. + You can pipe the security descriptors that `Get-NTFSSecurityDescriptor` returns to this cmdlet. @@ -6582,7 +6584,7 @@ PS C:\Data> Get-NTFSSecurityDescriptor Security2.IdentityReference2 - An account is accepted by the parameter binder but ignored by this cmdlet. + An account name or a SID string binds to `-Account`. @@ -6592,7 +6594,7 @@ PS C:\Data> Get-NTFSSecurityDescriptor Security2.SimpleFileSystemAccessRule - One object per reported entry, with the folder in `FullName` and `Name`, the account in `Identity`, the access type in `AccessControlType`, and the simplified rights `Read`, `Write`, and `Delete` in `AccessRights`. + One object per reported entry, with the folder in `FullName` and `Name`, the account in `Identity`, the access type in `AccessControlType`, and the simplified rights `Read`, `Write`, and `Delete` in `AccessRights`. The default view is a table with the `Account`, `Access Rights`, and `Type` columns, grouped by folder. @@ -6600,6 +6602,7 @@ PS C:\Data> Get-NTFSSecurityDescriptor When the module setting `EnablePrivileges` is `$true` (the default in the `PrivateData` section of NTFSSecurity.psd1), this cmdlet tries to enable the Backup, Restore, Take Ownership, and Security privileges while it runs and disables the privileges it enabled when it finishes. These privileges are only available in an elevated session of an account that holds them, such as a member of the local Administrators group. If a privilege cannot be enabled, the cmdlet continues without it and writes a debug message. The simplified rights hide which exact rights an account holds. Use `Get-NTFSAccess` when you need the full access control entry, and `Get-NTFSEffectiveAccess` when you need the rights that result from all entries together. + Before 5.0.0, the cmdlet ignored `-Account` and `-SecurityDescriptor`, and its output had no table view. diff --git a/Tests/Access.Tests.ps1 b/Tests/Access.Tests.ps1 index 33468f6..7927ac1 100644 --- a/Tests/Access.Tests.ps1 +++ b/Tests/Access.Tests.ps1 @@ -78,6 +78,76 @@ Describe 'Get-NTFSEffectiveAccess' { } } +Describe 'Get-NTFSOrphanedAccess' { + BeforeAll { + $orphanedFile = New-TestSandboxItem -Sandbox $sandbox -Name 'Orphaned' + Assert-TestSandboxPath -Sandbox $sandbox -Path $orphanedFile + $acl = Get-Acl -LiteralPath $orphanedFile + foreach ($sid in 'S-1-5-21-1-2-3-1001', 'S-1-5-21-1-2-3-1002') { + $acl.AddAccessRule((New-Object -TypeName 'System.Security.AccessControl.FileSystemAccessRule' -ArgumentList ( + (New-Object -TypeName 'System.Security.Principal.SecurityIdentifier' -ArgumentList $sid), + [System.Security.AccessControl.FileSystemRights]::ReadData, [System.Security.AccessControl.AccessControlType]::Allow + ))) + } + Set-Acl -LiteralPath $orphanedFile -AclObject $acl + } + + It 'Should return the entries whose account cannot be resolved' { + @(Get-NTFSOrphanedAccess -Path $orphanedFile) | Should -HaveCount 2 + } + + It 'Should return only the entries of -Account' { + $result = @(Get-NTFSOrphanedAccess -Path $orphanedFile -Account 'S-1-5-21-1-2-3-1002') + + $result | Should -HaveCount 1 + $result[0].Account.Sid | Should -Be 'S-1-5-21-1-2-3-1002' + } + + It 'Should read the entries of a security descriptor' { + $sd = Get-NTFSSecurityDescriptor -Path $orphanedFile + + $result = @(Get-NTFSOrphanedAccess -SecurityDescriptor $sd) + + $result | Should -HaveCount 2 + $result | ForEach-Object -Process { $_.FullName | Should -Be $orphanedFile } + } +} + +Describe 'Get-NTFSSimpleAccess' { + BeforeAll { + $simpleFolder = New-TestSandboxItem -Sandbox $sandbox -Name 'Simple' -Directory + Assert-TestSandboxPath -Sandbox $sandbox -Path $simpleFolder + $acl = Get-Acl -LiteralPath $simpleFolder + $acl.AddAccessRule((New-Object -TypeName 'System.Security.AccessControl.FileSystemAccessRule' -ArgumentList ( + (New-Object -TypeName 'System.Security.Principal.SecurityIdentifier' -ArgumentList 'S-1-1-0'), + [System.Security.AccessControl.FileSystemRights]::ReadData, [System.Security.AccessControl.AccessControlType]::Allow + ))) + Set-Acl -LiteralPath $simpleFolder -AclObject $acl + } + + It 'Should return only the entries of -Account' { + $result = @(Get-NTFSSimpleAccess -Path $simpleFolder -Account 'S-1-1-0' -IncludeRootFolder:$false) + + $result | Should -Not -BeNullOrEmpty + $result | ForEach-Object -Process { $_.Identity.Sid | Should -Be 'S-1-1-0' } + } + + It 'Should read the entries of a security descriptor' { + $sd = Get-NTFSSecurityDescriptor -Path $simpleFolder + + $result = @(Get-NTFSSimpleAccess -SecurityDescriptor $sd) + + $result | Should -Not -BeNullOrEmpty + $result | ForEach-Object -Process { $_.FullName | Should -Be $simpleFolder } + } + + It 'Should show the entries as a table with the account, the rights, and the type' { + $text = Get-NTFSSimpleAccess -Path $simpleFolder -IncludeRootFolder:$false | Out-String -Width 200 + + $text | Should -Match 'Account\s+Access Rights\s+Type' + } +} + Describe 'Security descriptor parameter sets' { BeforeAll { $folder = New-TestSandboxItem -Sandbox $sandbox -Name 'ParameterSets' -Directory diff --git a/Tests/Audit.Tests.ps1 b/Tests/Audit.Tests.ps1 index 5043c43..43516ef 100644 --- a/Tests/Audit.Tests.ps1 +++ b/Tests/Audit.Tests.ps1 @@ -123,6 +123,30 @@ Describe 'Add-NTFSAudit' { } } +Describe 'Get-NTFSOrphanedAudit' { + BeforeAll { + $orphanedFile = New-TestSandboxItem -Sandbox $sandbox -Name 'OrphanedAudit' + } + + # Before 5.0.0, the cmdlet wrote the entries of an item as one collection and ignored -Account. + It 'Should return one object per entry whose account cannot be resolved' -Skip:(-not $canReadAudit) { + foreach ($sid in 'S-1-5-21-1-2-3-1001', 'S-1-5-21-1-2-3-1002') { + Add-NTFSAudit -Path $orphanedFile -Account $sid -AccessRights ReadData -InheritanceFlags None -PropagationFlags None + } + + $result = @(Get-NTFSOrphanedAudit -Path $orphanedFile) + + $result | Should -HaveCount 2 + $result | ForEach-Object -Process { $_ | Should -BeOfType [Security2.FileSystemAuditRule2] } + } + + It 'Should return only the entries of -Account' -Skip:(-not $canReadAudit) { + $result = @(Get-NTFSOrphanedAudit -Path $orphanedFile -Account 'S-1-5-21-1-2-3-1002') + + $result | Should -HaveCount 1 + } +} + Describe 'Remove-NTFSAudit' { Context 'With -PassThru' { It 'Should return the audit entries of the item, not its access entries' -Skip:(-not $canReadAudit) {