There’s a new version of the PAC 4.0 Preview available on the TechNet Script Repository. There’s still no official documentation in the new version, so I’ll briefly mention some of the changes below. If you missed it, the first post on the 4.0 preview is here:
Modification Cmdlets
The following cmdlets are now available:
- New-AccessControlEntry
- Add-AccessControlEntry
- Remove-AccessControlEntry
- Enable-AclInheritance
- Disable-AclInheritance
- Set-Owner
- Set-SecurityDescriptor
Like in previous versions, these commands can be used to work with native .NET security descriptor objects (output from Get-Acl), PAC security descriptor objects (output from Get-SecurityDescriptor), or directly with a whole bunch of objects. Here are some examples of what I’m talking about:
Working with .NET Security Descriptor Objects
You’re probably familiar with using the native PowerShell and .NET commands to work with security descriptors. You do something like this:
$Acl = Get-Acl C:\powershell $Ace = New-Object System.Security.AccessControl.FileSystemAccessRule( "Everyone", "Write", "ContainerInherit, ObjectInherit", "None", "Allow" ) $Acl.AddAccessRule($Ace) $Acl | Set-Acl
That’s a lot of work to add a single Allow ACE giving Everyone Write access. You can use the PAC module to shorten that code to this:
$Acl = Get-Acl C:\powershell $Ace = New-AccessControlEntry -Principal Everyone -FolderRights Write $Acl.AddAccessRule($Ace) $Acl | Set-Acl
You can also just cut out the New-AccessControlEntry call completely, which would shorten the snippet to this:
$Acl = Get-Acl C:\powershell $Acl | Add-AccessControlEntry -Principal Everyone -FolderRights Write $Acl | Set-Acl
And finally, one more way to shorten that:
Get-Acl C:\powershell | Add-AccessControlEntry -Principal Everyone -FolderRights Write -Apply
When you use -Apply like that, the module will actually call Set-SecurityDescriptor, so you’re not just using native PowerShell and .NET commands at that point.
Working with PAC Security Descriptor Objects
This actually looks just like working with the .NET security descriptor objects, except you use Get-SecurityDescriptor instead of Get-Acl, and Set-SecurityDescriptor instead of Set-Acl.
Working With Objects Directly
You don’t even need to use Get-Acl/Set-Acl or Get-SecurityDescriptor/Set-SecurityDescriptor. There are a ton of .NET and WMI instances that the module knows how to work with. These commands would be valid:
# This defaults to enabling inheritance on the DACL, but the SACL can be controlled, too dir C:\powershell -Recurse | Enable-AclInheritance -PassThru | Remove-AccessControlEntry -RemoveAllAccessEntries -Apply # -Apply isn't necessary here because the input object isn't a security descriptor. -Force # would stop it from prompting you before saving the security descriptor. Get-Service bits | Add-AccessControlEntry -Principal Users -ServiceRights Start, Stop Get-SmbShare share | Add-AccessControlEntry -Principal Everyone -AccessMask ([ROE.PowerShellAccessControl.Enums.ShareRights]::FullControl)
PacSDOption Common Parameter
Most of the commands in the module have a parameter named -PacSDOption. That’s how you control things like recursing through child items (where supported), getting the SACL, bypassing the ACL check (the -BypassAclCheck parameter from the last post doesn’t exist as a direct cmdlet parameter anymore). The parameter’s input is from the New-PacCommandOption cmdlet. Here’s an example:
# Get the DACL and SACL entries for C:\powershell, even if you don't have permission to view them Get-AccessControlEntry C:\powershell -PacSDOption (New-PacCommandOption -BypassAclCheck -Audit) # Get the DACL and SACL entries for C:\powershell and any child folders (even if long paths are there): Get-AccessControlEntry C:\powershell -PacSDOption (New-PacCommandOption -Recurse -Directory)
Formatting
The default formatting of a security descriptor now shows both the DACL and the SACL:
The module will also check for the existence of a hash table named $PacOptions, and change how ACEs are displayed depending on its value. For now, there’s a single display option ‘DontAbbreviateAppliesTo’ that let’s you control how the AppliesTo column is displayed on ACEs. Here’s an example of how to create the hash table and change the AppliesTo setting:
Remember that this is still a preview version, so you’ll probably come across some things that don’t work the way they’re supposed to. If you find a problem, have a question about how to do something, or have a suggestion, please either post a comment below or send me an e-mail (magicrohn -at- outlook.com). Since there’s no documentation yet, I really don’t have a problem answering any questions.
Hey Rohn,
Just attended your session in PS Summit 2015. That was outstanding. Thanks for a great module.
Sam
Sam,
I’m glad you enjoyed the session. Don’t forget to let me know if you have any issues or suggestions for the module (there are still some bugs and missing features).
Thanks,
Rohn
I really enjoyed both of your tracks at the PowerShell Summit 2015 where I first saw you demo this personally. Great stuff! I didn’t realize that I would need to use it so soon until I ran across an authorization issue using Set-ACL with a colleague. This module worked like a charm! Thanks for making it!
Awesome! I’m glad you found it useful. As always, let me know if you have any suggestions to make it better!
Rohn, thank you so very much for these tools. They’ve greatly reduced my workload. I had a question about the Get-EffectiveAccess cmdlet. In the Windows UI, when doing effective access lookups, you can select the “Include Group Membership” option. I found this useful when a user was getting his share permissions via one group (i.e. Domain Users), and the file system object permissions via another group. Is there any way to accomplish the same in your cmdlet?
I’m planning on adding the ability to check against additional groups and claims, so you will be able to say something like this (in version 4.0):
PS> Get-PacEffectiveAccess \\server\share -Principal UserName -GroupClaims Group1, Group2
That would give you effective access for the ‘UserName’ user with the groups they are already a member of, plus it would treat them as if they were in the ‘Group1’ and ‘Group2’ groups (even if they aren’t in those groups).
That being said, are you talking about being able to see why a user isn’t being granted permission over an object? If so, the command should already do that. For version 3, use the -ListAllRights switch on a share path. It looks like version 4.0 currently has an issue with UNC file/folder paths that needs to be fixed, otherwise you’d use the -Detailed switch for the same functionality (there will be an alias so it matches the version 3 switch name).
Hi,
first of all great module!
Please publish the source.
greetings
Carsten
Thanks! The source can be found here: https://github.com/rohnedwards/PowerShellAccessControl/tree/v4.0_devel/
(It still needs to be cleaned up a ton, but I’ve been saying for months how I’d release the source, and now seems like as good a time as any)
Hey Rohn,
Any update on conditional ACEs?
Thanks,
Sol
Not yet. If I were to throw something together this weekend, would you be willing to help me test it out?
[…] Sol Birnbaum on PowerShell Access Control Modu… […]
PAC is a fine-looking piece of software and nicely and thoroughly put together, but. . .Get-EffectiveAccess seems to be missing a feature that’d be useful to me: it lists the same effective access with I’m running as admin or not. So I’ve cobbled together a cheezy little function that handles this for the corner case I’m interested in (letting whoami and SysInternals’ AccessChk do most of the work).
function Test-Access
{
[CmdletBinding()]
[OutputType([bool])]
Param(
[Parameter(Position=0)]
[string]
$FileName,
[Parameter(Position=1)]
[ValidateSet(‘Read’, ‘Write’)]
[string]
$AccessRequested
)
process {
$groups = @(whoami) + $(whoami /groups /fo csv `
| ConvertFrom-Csv|?{($_.attributes -split ‘, ‘) -contains ‘Enabled group’}|%{$_.’Group Name’.Trim()})
$accessList = @{}
$mode = @(‘-‘ + $AccessRequested[0].ToString().ToLower())
accesschk.exe -q -u @mode $FileName `
| ForEach-Object `
{
$startsWithPrintingChar, $accessType, $principal = ([regex]’\s+’).split($_, 3)
if (!$startsWithPrintingChar -and $accessType -and $principal)
{
$accessList[$principal] = $accessType
}
}
return [bool]($accessList[$groups] | ?{$_})
}
}