1 #!powershell
2 
3 # Copyright: (c) 2015, Phil Schwartz <schwartzmx@gmail.com>
4 # Copyright: (c) 2015, Trond Hindenes
5 # Copyright: (c) 2015, Hans-Joachim Kliemeck <git@kliemeck.de>
6 # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
7 
8 #Requires -Module Ansible.ModuleUtils.Legacy
9 #Requires -Module Ansible.ModuleUtils.PrivilegeUtil
10 #Requires -Module Ansible.ModuleUtils.SID
11 
12 $ErrorActionPreference = "Stop"
13 
14 # win_acl module (File/Resources Permission Additions/Removal)
15 
16 #Functions
Get-UserSID()17 function Get-UserSID {
18     param(
19         [String]$AccountName
20     )
21 
22     $userSID = $null
23     $searchAppPools = $false
24 
25     if ($AccountName.Split("\").Count -gt 1) {
26         if ($AccountName.Split("\")[0] -eq "IIS APPPOOL") {
27             $searchAppPools = $true
28             $AccountName = $AccountName.Split("\")[1]
29         }
30     }
31 
32     if ($searchAppPools) {
33         Import-Module -Name WebAdministration
34         $testIISPath = Test-Path -LiteralPath "IIS:"
35         if ($testIISPath) {
36             $appPoolObj = Get-ItemProperty -LiteralPath "IIS:\AppPools\$AccountName"
37             $userSID = $appPoolObj.applicationPoolSid
38         }
39     }
40     else {
41         $userSID = Convert-ToSID -account_name $AccountName
42     }
43 
44     return $userSID
45 }
46 
47 $params = Parse-Args $args
48 
SetPrivilegeTokens()49 Function SetPrivilegeTokens() {
50     # Set privilege tokens only if admin.
51     # Admins would have these privs or be able to set these privs in the UI Anyway
52 
53     $adminRole=[System.Security.Principal.WindowsBuiltInRole]::Administrator
54     $myWindowsID=[System.Security.Principal.WindowsIdentity]::GetCurrent()
55     $myWindowsPrincipal=new-object System.Security.Principal.WindowsPrincipal($myWindowsID)
56 
57 
58     if ($myWindowsPrincipal.IsInRole($adminRole)) {
59         # Need to adjust token privs when executing Set-ACL in certain cases.
60         # e.g. d:\testdir is owned by group in which current user is not a member and no perms are inherited from d:\
61         # This also sets us up for setting the owner as a feature.
62         # See the following for details of each privilege
63         # https://msdn.microsoft.com/en-us/library/windows/desktop/bb530716(v=vs.85).aspx
64         $privileges = @(
65             "SeRestorePrivilege",  # Grants all write access control to any file, regardless of ACL.
66             "SeBackupPrivilege",  # Grants all read access control to any file, regardless of ACL.
67             "SeTakeOwnershipPrivilege"  # Grants ability to take owernship of an object w/out being granted discretionary access
68         )
69         foreach ($privilege in $privileges) {
70             $state = Get-AnsiblePrivilege -Name $privilege
71             if ($state -eq $false) {
72                 Set-AnsiblePrivilege -Name $privilege -Value $true
73             }
74         }
75     }
76 }
77 
78 
79 $result = @{
80     changed = $false
81 }
82 
83 $path = Get-AnsibleParam -obj $params -name "path" -type "str" -failifempty $true
84 $user = Get-AnsibleParam -obj $params -name "user" -type "str" -failifempty $true
85 $rights = Get-AnsibleParam -obj $params -name "rights" -type "str" -failifempty $true
86 
87 $type = Get-AnsibleParam -obj $params -name "type" -type "str" -failifempty $true -validateset "allow","deny"
88 $state = Get-AnsibleParam -obj $params -name "state" -type "str" -default "present" -validateset "absent","present"
89 
90 $inherit = Get-AnsibleParam -obj $params -name "inherit" -type "str"
91 $propagation = Get-AnsibleParam -obj $params -name "propagation" -type "str" -default "None" -validateset "InheritOnly","None","NoPropagateInherit"
92 
93 # We mount the HKCR, HKU, and HKCC registry hives so PS can access them.
94 # Network paths have no qualifiers so we use -EA SilentlyContinue to ignore that
95 $path_qualifier = Split-Path -Path $path -Qualifier -ErrorAction SilentlyContinue
96 if ($path_qualifier -eq "HKCR:" -and (-not (Test-Path -LiteralPath HKCR:\))) {
97     New-PSDrive -Name HKCR -PSProvider Registry -Root HKEY_CLASSES_ROOT > $null
98 }
99 if ($path_qualifier -eq "HKU:" -and (-not (Test-Path -LiteralPath HKU:\))) {
100     New-PSDrive -Name HKU -PSProvider Registry -Root HKEY_USERS > $null
101 }
102 if ($path_qualifier -eq "HKCC:" -and (-not (Test-Path -LiteralPath HKCC:\))) {
103     New-PSDrive -Name HKCC -PSProvider Registry -Root HKEY_CURRENT_CONFIG > $null
104 }
105 
106 If (-Not (Test-Path -LiteralPath $path)) {
107     Fail-Json -obj $result -message "$path file or directory does not exist on the host"
108 }
109 
110 # Test that the user/group is resolvable on the local machine
111 $sid = Get-UserSID -AccountName $user
112 if (!$sid) {
113     Fail-Json -obj $result -message "$user is not a valid user or group on the host machine or domain"
114 }
115 
116 If (Test-Path -LiteralPath $path -PathType Leaf) {
117     $inherit = "None"
118 }
119 ElseIf ($null -eq $inherit) {
120     $inherit = "ContainerInherit, ObjectInherit"
121 }
122 
123 # Bug in Set-Acl, Get-Acl where -LiteralPath only works for the Registry provider if the location is in that root
124 # qualifier. We also don't have a qualifier for a network path so only change if not null
125 if ($null -ne $path_qualifier) {
126     Push-Location -LiteralPath $path_qualifier
127 }
128 
129 Try {
130     SetPrivilegeTokens
131     $path_item = Get-Item -LiteralPath $path -Force
132     If ($path_item.PSProvider.Name -eq "Registry") {
133         $colRights = [System.Security.AccessControl.RegistryRights]$rights
134     }
135     Else {
136         $colRights = [System.Security.AccessControl.FileSystemRights]$rights
137     }
138 
139     $InheritanceFlag = [System.Security.AccessControl.InheritanceFlags]$inherit
140     $PropagationFlag = [System.Security.AccessControl.PropagationFlags]$propagation
141 
142     If ($type -eq "allow") {
143         $objType =[System.Security.AccessControl.AccessControlType]::Allow
144     }
145     Else {
146         $objType =[System.Security.AccessControl.AccessControlType]::Deny
147     }
148 
149     $objUser = New-Object System.Security.Principal.SecurityIdentifier($sid)
150     If ($path_item.PSProvider.Name -eq "Registry") {
151         $objACE = New-Object System.Security.AccessControl.RegistryAccessRule ($objUser, $colRights, $InheritanceFlag, $PropagationFlag, $objType)
152     }
153     Else {
154         $objACE = New-Object System.Security.AccessControl.FileSystemAccessRule ($objUser, $colRights, $InheritanceFlag, $PropagationFlag, $objType)
155     }
156     $objACL = Get-ACL -LiteralPath $path
157 
158     # Check if the ACE exists already in the objects ACL list
159     $match = $false
160 
161     ForEach($rule in $objACL.GetAccessRules($true, $true, [System.Security.Principal.SecurityIdentifier])){
162 
163         If ($path_item.PSProvider.Name -eq "Registry") {
164             If (($rule.RegistryRights -eq $objACE.RegistryRights) -And ($rule.AccessControlType -eq $objACE.AccessControlType) -And ($rule.IdentityReference -eq $objACE.IdentityReference) -And ($rule.IsInherited -eq $objACE.IsInherited) -And ($rule.InheritanceFlags -eq $objACE.InheritanceFlags) -And ($rule.PropagationFlags -eq $objACE.PropagationFlags)) {
165                 $match = $true
166                 Break
167             }
168         } else {
169             If (($rule.FileSystemRights -eq $objACE.FileSystemRights) -And ($rule.AccessControlType -eq $objACE.AccessControlType) -And ($rule.IdentityReference -eq $objACE.IdentityReference) -And ($rule.IsInherited -eq $objACE.IsInherited) -And ($rule.InheritanceFlags -eq $objACE.InheritanceFlags) -And ($rule.PropagationFlags -eq $objACE.PropagationFlags)) {
170                 $match = $true
171                 Break
172             }
173         }
174     }
175 
176     If ($state -eq "present" -And $match -eq $false) {
177         Try {
178             $objACL.AddAccessRule($objACE)
179             If ($path_item.PSProvider.Name -eq "Registry") {
180                 Set-ACL -LiteralPath $path -AclObject $objACL
181             } else {
182                 (Get-Item -LiteralPath $path).SetAccessControl($objACL)
183             }
184             $result.changed = $true
185         }
186         Catch {
187             Fail-Json -obj $result -message "an exception occurred when adding the specified rule - $($_.Exception.Message)"
188         }
189     }
190     ElseIf ($state -eq "absent" -And $match -eq $true) {
191         Try {
192             $objACL.RemoveAccessRule($objACE)
193             If ($path_item.PSProvider.Name -eq "Registry") {
194                 Set-ACL -LiteralPath $path -AclObject $objACL
195             } else {
196                 (Get-Item -LiteralPath $path).SetAccessControl($objACL)
197             }
198             $result.changed = $true
199         }
200         Catch {
201             Fail-Json -obj $result -message "an exception occurred when removing the specified rule - $($_.Exception.Message)"
202         }
203     }
204     Else {
205         # A rule was attempting to be added but already exists
206         If ($match -eq $true) {
207             Exit-Json -obj $result -message "the specified rule already exists"
208         }
209         # A rule didn't exist that was trying to be removed
210         Else {
211             Exit-Json -obj $result -message "the specified rule does not exist"
212         }
213     }
214 }
215 Catch {
216     Fail-Json -obj $result -message "an error occurred when attempting to $state $rights permission(s) on $path for $user - $($_.Exception.Message)"
217 }
218 Finally {
219     # Make sure we revert the location stack to the original path just for cleanups sake
220     if ($null -ne $path_qualifier) {
221         Pop-Location
222     }
223 }
224 
225 Exit-Json -obj $result
226