mirror of
https://github.com/valitydev/salt.git
synced 2024-11-08 17:33:54 +00:00
commit
76bd9b536d
749
salt/modules/win_dacl.py
Normal file
749
salt/modules/win_dacl.py
Normal file
@ -0,0 +1,749 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
Manage DACLs on Windows
|
||||
|
||||
:depends: - winreg Python module
|
||||
'''
|
||||
|
||||
# TODO: Figure out the exceptions that could be raised and properly catch
|
||||
# them instead of a bare except that catches any exception at all
|
||||
# may also need to add the ability to take ownership of an object to set
|
||||
# permissions if the minion is running as a user and not LOCALSYSTEM
|
||||
|
||||
from salt.ext.six import string_types
|
||||
|
||||
# Import third party libs
|
||||
try:
|
||||
import _winreg
|
||||
import win32security
|
||||
import ntsecuritycon
|
||||
import os
|
||||
HAS_WINDOWS_MODULES = True
|
||||
except ImportError:
|
||||
try:
|
||||
import winreg as _winreg
|
||||
HAS_WINDOWS_MODULES = True
|
||||
except ImportError:
|
||||
HAS_WINDOWS_MODULES = False
|
||||
|
||||
# Import python libs
|
||||
import logging
|
||||
|
||||
# Import salt libs
|
||||
import salt.utils
|
||||
from salt.exceptions import CommandExecutionError
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
# Define the module's virtual name
|
||||
__virtualname__ = 'win_dacl'
|
||||
|
||||
|
||||
class daclConstants(object):
|
||||
'''
|
||||
dacl constants used throughout the module
|
||||
'''
|
||||
def __init__(self):
|
||||
self.hkeys_security = {
|
||||
'HKEY_LOCAL_MACHINE': 'MACHINE',
|
||||
'HKEY_USERS': 'USERS',
|
||||
'HKEY_CURRENT_USER': 'CURRENT_USER',
|
||||
'HKEY_CLASSES_ROOT': 'CLASSES_ROOT',
|
||||
'MACHINE': 'MACHINE',
|
||||
'USERS': 'USERS',
|
||||
'CURRENT_USER': 'CURRENT_USER',
|
||||
'CLASSES_ROOT': 'CLASSES_ROOT',
|
||||
'HKLM': 'MACHINE',
|
||||
'HKU': 'USERS',
|
||||
'HKCU': 'CURRENT_USER',
|
||||
'HKCR': 'CLASSES_ROOT',
|
||||
}
|
||||
self.rights = {
|
||||
win32security.SE_REGISTRY_KEY: {
|
||||
'READ': {
|
||||
'BITS': _winreg.KEY_READ,
|
||||
'TEXT': 'read'},
|
||||
'FULLCONTROL': {
|
||||
'BITS': _winreg.KEY_ALL_ACCESS,
|
||||
'TEXT': 'full control'}
|
||||
},
|
||||
win32security.SE_FILE_OBJECT: {
|
||||
'READ': {
|
||||
'BITS': ntsecuritycon.FILE_GENERIC_READ,
|
||||
'TEXT': 'read'},
|
||||
'WRITE': {
|
||||
'BITS': ntsecuritycon.FILE_GENERIC_WRITE,
|
||||
'TEXT': 'write'},
|
||||
'READ&EXECUTE': {
|
||||
'BITS': ntsecuritycon.FILE_GENERIC_EXECUTE |
|
||||
ntsecuritycon.FILE_GENERIC_READ,
|
||||
'TEXT': 'read and execute'},
|
||||
'MODIFY': {
|
||||
'BITS': ntsecuritycon.FILE_GENERIC_WRITE |
|
||||
ntsecuritycon.FILE_GENERIC_READ |
|
||||
ntsecuritycon.FILE_GENERIC_EXECUTE |
|
||||
ntsecuritycon.DELETE,
|
||||
'TEXT': 'modify'},
|
||||
'FULLCONTROL': {
|
||||
'BITS': ntsecuritycon.FILE_ALL_ACCESS,
|
||||
'TEXT': 'full control'}
|
||||
}
|
||||
}
|
||||
self.validAceTypes = {
|
||||
'ALLOW': {'TEXT': 'allowed', 'BITS': 0},
|
||||
'DENY': {'TEXT': 'denied', 'BITS': 1}}
|
||||
self.validPropagations = {
|
||||
win32security.SE_REGISTRY_KEY: {
|
||||
'KEY': {
|
||||
'TEXT': 'this key only',
|
||||
'BITS': win32security.NO_INHERITANCE},
|
||||
'KEY&SUBKEYS': {
|
||||
'TEXT': 'this key and subkeys',
|
||||
'BITS': win32security.CONTAINER_INHERIT_ACE},
|
||||
'SUBKEYS': {
|
||||
'TEXT': 'subkeys only',
|
||||
'BITS': win32security.INHERIT_ONLY_ACE |
|
||||
win32security.CONTAINER_INHERIT_ACE},
|
||||
'THIS KEY ONLY': {
|
||||
'TEXT': 'this key only',
|
||||
'BITS': win32security.NO_INHERITANCE},
|
||||
'THIS KEY AND SUBKEYS': {
|
||||
'TEXT': 'this key and subkeys',
|
||||
'BITS': win32security.CONTAINER_INHERIT_ACE},
|
||||
'SUBKEYS ONLY': {
|
||||
'TEXT': 'subkeys only',
|
||||
'BITS': win32security.INHERIT_ONLY_ACE |
|
||||
win32security.CONTAINER_INHERIT_ACE}
|
||||
},
|
||||
win32security.SE_FILE_OBJECT: {
|
||||
'FILE': {
|
||||
'TEXT': 'this file/folder only',
|
||||
'BITS': win32security.NO_INHERITANCE},
|
||||
'FOLDER': {
|
||||
'TEXT': 'this file/folder only',
|
||||
'BITS': win32security.NO_INHERITANCE},
|
||||
'FOLDER&SUBFOLDERS&FILES': {
|
||||
'TEXT': 'this folder, subfolders, and files',
|
||||
'BITS': win32security.CONTAINER_INHERIT_ACE |
|
||||
win32security.OBJECT_INHERIT_ACE},
|
||||
'FOLDER&SUBFOLDERS': {
|
||||
'TEXT': 'this folder and subfolders',
|
||||
'BITS': win32security.CONTAINER_INHERIT_ACE},
|
||||
'FOLDER&FILES': {
|
||||
'TEXT': 'this folder and files',
|
||||
'BITS': win32security.OBJECT_INHERIT_ACE},
|
||||
'SUBFOLDERS&FILES': {
|
||||
'TEXT': 'subfolders and files',
|
||||
'BITS': win32security.INHERIT_ONLY_ACE |
|
||||
win32security.CONTAINER_INHERIT_ACE |
|
||||
win32security.OBJECT_INHERIT_ACE},
|
||||
'SUBFOLDERS': {
|
||||
'TEXT': 'subfolders only',
|
||||
'BITS': win32security.INHERIT_ONLY_ACE |
|
||||
win32security.CONTAINER_INHERIT_ACE},
|
||||
'FILES': {
|
||||
'TEXT': 'files only',
|
||||
'BITS': win32security.INHERIT_ONLY_ACE |
|
||||
win32security.OBJECT_INHERIT_ACE},
|
||||
'THIS FILE ONLY': {
|
||||
'TEXT': 'this file/folder only',
|
||||
'BITS': win32security.NO_INHERITANCE},
|
||||
'THIS FOLDER ONLY': {
|
||||
'TEXT': 'this file/folder only',
|
||||
'BITS': win32security.NO_INHERITANCE},
|
||||
'THIS FOLDER, SUBFOLDERS, and FILES': {
|
||||
'TEXT': 'this folder, subfolders, and files',
|
||||
'BITS': win32security.CONTAINER_INHERIT_ACE |
|
||||
win32security.OBJECT_INHERIT_ACE},
|
||||
'THIS FOLDER AND SUBFOLDERS': {
|
||||
'TEXT': 'this folder and subfolders',
|
||||
'BITS': win32security.CONTAINER_INHERIT_ACE},
|
||||
'THIS FOLDER AND FILES': {
|
||||
'TEXT': 'this folder and files',
|
||||
'BITS': win32security.OBJECT_INHERIT_ACE},
|
||||
'SUBFOLDERS AND FILES': {
|
||||
'TEXT': 'subfolders and files',
|
||||
'BITS': win32security.INHERIT_ONLY_ACE |
|
||||
win32security.CONTAINER_INHERIT_ACE |
|
||||
win32security.OBJECT_INHERIT_ACE},
|
||||
'SUBFOLDERS ONLY': {
|
||||
'TEXT': 'subfolders only',
|
||||
'BITS': win32security.INHERIT_ONLY_ACE |
|
||||
win32security.CONTAINER_INHERIT_ACE},
|
||||
'FILES ONLY': {
|
||||
'TEXT': 'files only',
|
||||
'BITS': win32security.INHERIT_ONLY_ACE |
|
||||
win32security.OBJECT_INHERIT_ACE}
|
||||
}
|
||||
}
|
||||
self.reflection_mask = {
|
||||
True: _winreg.KEY_ALL_ACCESS,
|
||||
False: _winreg.KEY_ALL_ACCESS | _winreg.KEY_WOW64_64KEY,
|
||||
}
|
||||
self.objectType = {
|
||||
'FILE': win32security.SE_FILE_OBJECT,
|
||||
'DIRECTORY': win32security.SE_FILE_OBJECT,
|
||||
'REGISTRY': win32security.SE_REGISTRY_KEY}
|
||||
|
||||
def getObjectTypeBit(self, t):
|
||||
'''
|
||||
returns the bit value of the string object type
|
||||
'''
|
||||
if isinstance(t, string_types):
|
||||
t = t.upper()
|
||||
try:
|
||||
return self.objectType[t]
|
||||
except KeyError:
|
||||
raise CommandExecutionError((
|
||||
'Invalid object type "{0}". It should be one of the following: {1}'
|
||||
).format(t, ', '.join(self.objectType)))
|
||||
else:
|
||||
return t
|
||||
|
||||
def getSecurityHkey(self, s):
|
||||
'''
|
||||
returns the necessary string value for an HKEY for the win32security module
|
||||
'''
|
||||
try:
|
||||
return self.hkeys_security[s]
|
||||
except KeyError:
|
||||
raise CommandExecutionError((
|
||||
'No HKEY named "{0}". It should be one of the following: {1}'
|
||||
).format(s, ', '.join(self.hkeys_security)))
|
||||
|
||||
def getPermissionBit(self, t, m):
|
||||
'''
|
||||
returns a permission bit of the string permission value for the specified object type
|
||||
'''
|
||||
try:
|
||||
if isinstance(m, string_types):
|
||||
return self.rights[t][m]['BITS']
|
||||
else:
|
||||
return m
|
||||
except KeyError:
|
||||
raise CommandExecutionError((
|
||||
'No right "{0}". It should be one of the following: {1}')
|
||||
.format(m, ', '.join(self.rights[t])))
|
||||
|
||||
def getPermissionText(self, t, m):
|
||||
'''
|
||||
returns the permission textual representation of a specified permission bit/object type
|
||||
'''
|
||||
try:
|
||||
return self.rights[t][m]['TEXT']
|
||||
except KeyError:
|
||||
raise CommandExecutionError((
|
||||
'No right "{0}". It should be one of the following: {1}')
|
||||
.format(m, ', '.join(self.rights[t])))
|
||||
|
||||
def getAceTypeBit(self, t):
|
||||
'''
|
||||
returns the acetype bit of a text value
|
||||
'''
|
||||
try:
|
||||
return self.validAceTypes[t]['BITS']
|
||||
except KeyError:
|
||||
raise CommandExecutionError((
|
||||
'No ACE type "{0}". It should be one of the following: {1}'
|
||||
).format(t, ', '.join(self.validAceTypes)))
|
||||
|
||||
def getAceTypeText(self, t):
|
||||
'''
|
||||
returns the textual representation of a acetype bit
|
||||
'''
|
||||
try:
|
||||
return self.validAceTypes[t]['TEXT']
|
||||
except KeyError:
|
||||
raise CommandExecutionError((
|
||||
'No ACE type "{0}". It should be one of the following: {1}'
|
||||
).format(t, ', '.join(self.validAceTypes)))
|
||||
|
||||
def getPropagationBit(self, t, p):
|
||||
'''
|
||||
returns the propagation bit of a text value
|
||||
'''
|
||||
try:
|
||||
return self.validPropagations[t][p]['BITS']
|
||||
except KeyError:
|
||||
raise CommandExecutionError((
|
||||
'No propagation type of "{0}". It should be one of the following: {1}'
|
||||
).format(p, ', '.join(self.validPropagations[t])))
|
||||
|
||||
def getPropagationText(self, t, p):
|
||||
'''
|
||||
returns the textual representation of a propagation bit
|
||||
'''
|
||||
try:
|
||||
return self.validPropagations[t][p]['TEXT']
|
||||
except KeyError:
|
||||
raise CommandExecutionError((
|
||||
'No propagation type of "{0}". It should be one of the following: {1}'
|
||||
).format(p, ', '.join(self.validPropagations[t])))
|
||||
|
||||
def processPath(self, path, objectType):
|
||||
'''
|
||||
processes a path/object type combo and returns:
|
||||
registry types with the correct HKEY text representation
|
||||
files/directories with environment variables expanded
|
||||
'''
|
||||
if objectType == win32security.SE_REGISTRY_KEY:
|
||||
splt = path.split("\\")
|
||||
hive = self.getSecurityHkey(splt.pop(0).upper())
|
||||
splt.insert(0, hive)
|
||||
path = r'\\'.join(splt)
|
||||
else:
|
||||
path = os.path.expandvars(path)
|
||||
return path
|
||||
|
||||
|
||||
class User(object):
|
||||
'''
|
||||
class object that returns a users SID
|
||||
'''
|
||||
def __getattr__(self, u):
|
||||
try:
|
||||
sid = win32security.LookupAccountName('', u)[0]
|
||||
return sid
|
||||
except Exception as e:
|
||||
raise CommandExecutionError((
|
||||
'There was an error obtaining the SID of user "{0}". Error returned: {1}'
|
||||
).format(u, e))
|
||||
|
||||
|
||||
def __virtual__():
|
||||
'''
|
||||
Only works on Windows systems
|
||||
'''
|
||||
if salt.utils.is_windows() and HAS_WINDOWS_MODULES:
|
||||
return __virtualname__
|
||||
return False
|
||||
|
||||
|
||||
def _get_dacl(path, objectType):
|
||||
'''
|
||||
gets the dacl of a path
|
||||
'''
|
||||
try:
|
||||
dacl = win32security.GetNamedSecurityInfo(
|
||||
path, objectType, win32security.DACL_SECURITY_INFORMATION
|
||||
).GetSecurityDescriptorDacl()
|
||||
except Exception:
|
||||
dacl = None
|
||||
return dacl
|
||||
|
||||
|
||||
def get(path, objectType):
|
||||
'''
|
||||
get the acl of an object
|
||||
'''
|
||||
ret = {'Path': path,
|
||||
'ACLs': []}
|
||||
|
||||
if path and objectType:
|
||||
dc = daclConstants()
|
||||
objectTypeBit = dc.getObjectTypeBit(objectType)
|
||||
path = dc.processPath(path, objectTypeBit)
|
||||
tdacl = _get_dacl(path, objectTypeBit)
|
||||
if tdacl:
|
||||
for counter in range(0, tdacl.GetAceCount()):
|
||||
tAce = tdacl.GetAce(counter)
|
||||
ret['ACLs'].append(_ace_to_text(tAce, objectTypeBit))
|
||||
return ret
|
||||
|
||||
|
||||
def add_ace(path, objectType, user, permission, acetype, propagation):
|
||||
'''
|
||||
add an ace to an object
|
||||
|
||||
path: path to the object (i.e. c:\\temp\\file, HKEY_LOCAL_MACHINE\\SOFTWARE\\KEY, etc)
|
||||
user: user to add
|
||||
permission: permissions for the user
|
||||
acetypes: either allow/deny for each user/permission (ALLOW, DENY)
|
||||
propagation: how the ACE applies to children for Registry Keys and Directories(KEY, KEY&SUBKEYS, SUBKEYS)
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
allow domain\fakeuser full control on HKLM\\SOFTWARE\\somekey, propagate to this key and subkeys
|
||||
salt 'myminion' win_dacl.add_ace 'HKEY_LOCAL_MACHINE\\SOFTWARE\\somekey' 'Registry' 'domain\fakeuser' 'FULLCONTROL' 'ALLOW' 'KEY&SUBKEYS'
|
||||
'''
|
||||
ret = {'result': None,
|
||||
'changes': {},
|
||||
'comment': []}
|
||||
|
||||
if (path and user and
|
||||
permission and acetype
|
||||
and propagation):
|
||||
if objectType.upper() == "FILE":
|
||||
propagation = "FILE"
|
||||
dc = daclConstants()
|
||||
objectTypeBit = dc.getObjectTypeBit(objectType)
|
||||
path = dc.processPath(path, objectTypeBit)
|
||||
u = User()
|
||||
user = user.strip()
|
||||
permission = permission.strip().upper()
|
||||
acetype = acetype.strip().upper()
|
||||
propagation = propagation.strip().upper()
|
||||
|
||||
thisSid = getattr(u, user)
|
||||
permissionbit = dc.getPermissionBit(objectTypeBit, permission)
|
||||
acetypebit = dc.getAceTypeBit(acetype)
|
||||
propagationbit = dc.getPropagationBit(objectTypeBit, propagation)
|
||||
dacl = _get_dacl(path, objectTypeBit)
|
||||
|
||||
if dacl:
|
||||
acesAdded = []
|
||||
try:
|
||||
if acetypebit == 0:
|
||||
dacl.AddAccessAllowedAceEx(win32security.ACL_REVISION, propagationbit, permissionbit, thisSid)
|
||||
elif acetypebit == 1:
|
||||
dacl.AddAccessDeniedAceEx(win32security.ACL_REVISION, propagationbit, permissionbit, thisSid)
|
||||
win32security.SetNamedSecurityInfo(
|
||||
path, objectTypeBit, win32security.DACL_SECURITY_INFORMATION,
|
||||
None, None, dacl, None)
|
||||
acesAdded.append((
|
||||
'{0} {1} {2} on {3}'
|
||||
).format(
|
||||
user, dc.getAceTypeText(acetype), dc.getPermissionText(objectTypeBit, permission),
|
||||
dc.getPropagationText(objectTypeBit, propagation)))
|
||||
ret['result'] = True
|
||||
except Exception as e:
|
||||
ret['comment'].append((
|
||||
'An error occurred attempting to add the ace. The error was {0}'
|
||||
).format(e))
|
||||
ret['result'] = False
|
||||
return ret
|
||||
if acesAdded:
|
||||
ret['changes']['Added ACEs'] = acesAdded
|
||||
else:
|
||||
ret['comment'].append((
|
||||
'Unable to obtain the DACL of {0}'
|
||||
).format(path))
|
||||
else:
|
||||
ret['comment'].append('An empty value was specified for a required item.')
|
||||
ret['result'] = False
|
||||
return ret
|
||||
|
||||
|
||||
def rm_ace(path, objectType, user, permission, acetype, propagation):
|
||||
'''
|
||||
remove an ace to an object
|
||||
|
||||
path: path to the object (i.e. c:\\temp\\file, HKEY_LOCAL_MACHINE\\SOFTWARE\\KEY, etc)
|
||||
user: user to remove
|
||||
permission: permissions for the user
|
||||
acetypes: either allow/deny for each user/permission (ALLOW, DENY)
|
||||
propagation: how the ACE applies to children for Registry Keys and Directories(KEY, KEY&SUBKEYS, SUBKEYS)
|
||||
|
||||
***The entire ACE must match to be removed***
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
remove allow domain\fakeuser full control on HKLM\\SOFTWARE\\somekey propagated to this key and subkeys
|
||||
salt 'myminion' win_dacl.rm_ace 'Registry' 'HKEY_LOCAL_MACHINE\\SOFTWARE\\somekey' 'domain\fakeuser' 'FULLCONTROL' 'ALLOW' 'KEY&SUBKEYS'
|
||||
'''
|
||||
ret = {'result': None,
|
||||
'changes': {},
|
||||
'comment': []}
|
||||
|
||||
if (path and user and
|
||||
permission and acetype
|
||||
and propagation):
|
||||
dc = daclConstants()
|
||||
if objectType.upper() == "FILE":
|
||||
propagation = "FILE"
|
||||
objectTypeBit = dc.getObjectTypeBit(objectType)
|
||||
path = dc.processPath(path, objectTypeBit)
|
||||
|
||||
u = User()
|
||||
user = user.strip()
|
||||
permission = permission.strip().upper()
|
||||
acetype = acetype.strip().upper()
|
||||
propagation = propagation.strip().upper()
|
||||
|
||||
if check_ace(path, objectType, user, permission, acetype, propagation, True)['Exists']:
|
||||
thisSid = getattr(u, user)
|
||||
permissionbit = dc.getPermissionBit(objectTypeBit, permission)
|
||||
acetypebit = dc.getAceTypeBit(acetype)
|
||||
propagationbit = dc.getPropagationBit(objectTypeBit, propagation)
|
||||
dacl = _get_dacl(path, objectTypeBit)
|
||||
counter = 0
|
||||
acesRemoved = []
|
||||
if objectTypeBit == win32security.SE_FILE_OBJECT:
|
||||
if check_inheritance(path, objectType)['Inheritance']:
|
||||
if permission == 'FULLCONTROL':
|
||||
# if inhertiance is enabled on an SE_FILE_OBJECT, then the SI_NO_ACL_PROTECT
|
||||
# gets unset on FullControl which greys out the include inheritable permission
|
||||
# checkbox on the advanced security settings gui page
|
||||
permissionbit = permissionbit ^ ntsecuritycon.SI_NO_ACL_PROTECT
|
||||
while counter < dacl.GetAceCount():
|
||||
tAce = dacl.GetAce(counter)
|
||||
if (tAce[0][1] & win32security.INHERITED_ACE) != win32security.INHERITED_ACE:
|
||||
if tAce[2] == thisSid:
|
||||
if tAce[0][0] == acetypebit:
|
||||
if (tAce[0][1] & propagationbit) == propagationbit:
|
||||
if tAce[1] == permissionbit:
|
||||
dacl.DeleteAce(counter)
|
||||
counter = counter - 1
|
||||
acesRemoved.append((
|
||||
'{0} {1} {2} on {3}'
|
||||
).format(user, dc.getAceTypeText(acetype),
|
||||
dc.getPermissionText(objectTypeBit, permission),
|
||||
dc.getPropagationText(objectTypeBit, propagation)))
|
||||
|
||||
counter = counter + 1
|
||||
if acesRemoved:
|
||||
try:
|
||||
win32security.SetNamedSecurityInfo(
|
||||
path, objectTypeBit, win32security.DACL_SECURITY_INFORMATION,
|
||||
None, None, dacl, None)
|
||||
ret['changes']['Removed ACEs'] = acesRemoved
|
||||
ret['result'] = True
|
||||
except Exception as e:
|
||||
ret['result'] = False
|
||||
ret['comment'].append((
|
||||
'Error removing ACE. The error was {0}'
|
||||
).format(e))
|
||||
return ret
|
||||
else:
|
||||
ret['comment'].append((
|
||||
'The specified ACE was not found on the path'))
|
||||
return ret
|
||||
|
||||
|
||||
def _ace_to_text(ace, objectType):
|
||||
'''
|
||||
helper function to convert an ace to a textual representation
|
||||
'''
|
||||
dc = daclConstants()
|
||||
objectType = dc.getObjectTypeBit(objectType)
|
||||
try:
|
||||
userSid = win32security.LookupAccountSid('', ace[2])
|
||||
if userSid[1]:
|
||||
userSid = '{1}\\{0}'.format(userSid[0], userSid[1])
|
||||
else:
|
||||
userSid = '{0}'.format(userSid[0])
|
||||
except Exception:
|
||||
userSid = win32security.ConvertSidToStringSid(ace[2])
|
||||
tPerm = ace[1]
|
||||
tAceType = ace[0][0]
|
||||
tProps = ace[0][1]
|
||||
tInherited = ''
|
||||
for x in dc.validAceTypes:
|
||||
if dc.validAceTypes[x]['BITS'] == tAceType:
|
||||
tAceType = dc.validAceTypes[x]['TEXT']
|
||||
break
|
||||
for x in dc.rights[objectType]:
|
||||
if dc.rights[objectType][x]['BITS'] == tPerm:
|
||||
tPerm = dc.rights[objectType][x]['TEXT']
|
||||
break
|
||||
else:
|
||||
if objectType == win32security.SE_FILE_OBJECT:
|
||||
if (tPerm ^ ntsecuritycon.FILE_ALL_ACCESS) == ntsecuritycon.SI_NO_ACL_PROTECT:
|
||||
tPerm = 'full control'
|
||||
break
|
||||
if (tProps & win32security.INHERITED_ACE) == win32security.INHERITED_ACE:
|
||||
tInherited = '[Inherited]'
|
||||
tProps = (tProps ^ win32security.INHERITED_ACE)
|
||||
for x in dc.validPropagations[objectType]:
|
||||
if dc.validPropagations[objectType][x]['BITS'] == tProps:
|
||||
tProps = dc.validPropagations[objectType][x]['TEXT']
|
||||
break
|
||||
return ((
|
||||
'{0} {1} {2} on {3} {4}'
|
||||
).format(userSid, tAceType, tPerm, tProps, tInherited))
|
||||
|
||||
|
||||
def _set_dacl_inheritance(path, objectType, inheritance=True, copy=True, clear=False):
|
||||
'''
|
||||
helper function to set the inheritance
|
||||
'''
|
||||
ret = {'result': False,
|
||||
'comment': '',
|
||||
'changes': {}}
|
||||
|
||||
if path:
|
||||
try:
|
||||
sd = win32security.GetNamedSecurityInfo(path, objectType, win32security.DACL_SECURITY_INFORMATION)
|
||||
tdacl = sd.GetSecurityDescriptorDacl()
|
||||
if inheritance:
|
||||
if clear:
|
||||
counter = 0
|
||||
removedAces = []
|
||||
while counter < tdacl.GetAceCount():
|
||||
tAce = tdacl.GetAce(counter)
|
||||
if (tAce[0][1] & win32security.INHERITED_ACE) != win32security.INHERITED_ACE:
|
||||
tdacl.DeleteAce(counter)
|
||||
removedAces.append(_ace_to_text(tAce, objectType))
|
||||
else:
|
||||
counter = counter + 1
|
||||
if removedAces:
|
||||
ret['changes']['Removed ACEs'] = removedAces
|
||||
else:
|
||||
ret['changes']['Non-Inherited ACEs'] = 'Left in the DACL'
|
||||
win32security.SetNamedSecurityInfo(
|
||||
path, objectType,
|
||||
win32security.DACL_SECURITY_INFORMATION | win32security.UNPROTECTED_DACL_SECURITY_INFORMATION,
|
||||
None, None, tdacl, None)
|
||||
ret['changes']['Inheritance'] = 'Enabled'
|
||||
else:
|
||||
if not copy:
|
||||
counter = 0
|
||||
inheritedAcesRemoved = []
|
||||
while counter < tdacl.GetAceCount():
|
||||
tAce = tdacl.GetAce(counter)
|
||||
if (tAce[0][1] & win32security.INHERITED_ACE) == win32security.INHERITED_ACE:
|
||||
tdacl.DeleteAce(counter)
|
||||
inheritedAcesRemoved.append(_ace_to_text(tAce, objectType))
|
||||
else:
|
||||
counter = counter + 1
|
||||
if inheritedAcesRemoved:
|
||||
ret['changes']['Removed ACEs'] = inheritedAcesRemoved
|
||||
else:
|
||||
ret['changes']['Previously Inherited ACEs'] = 'Copied to the DACL'
|
||||
win32security.SetNamedSecurityInfo(
|
||||
path, objectType,
|
||||
win32security.DACL_SECURITY_INFORMATION | win32security.PROTECTED_DACL_SECURITY_INFORMATION,
|
||||
None, None, tdacl, None)
|
||||
ret['changes']['Inheritance'] = 'Disabled'
|
||||
ret['result'] = True
|
||||
except Exception as e:
|
||||
ret['result'] = False
|
||||
ret['comment'] = (
|
||||
'Error attempting to set the inheritance. The error was {0}'
|
||||
).format(e)
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def enable_inheritance(path, objectType, clear=False):
|
||||
'''
|
||||
enable/disable inheritance on an object
|
||||
|
||||
clear = True will remove non-Inherited ACEs from the ACL
|
||||
'''
|
||||
dc = daclConstants()
|
||||
objectType = dc.getObjectTypeBit(objectType)
|
||||
path = dc.processPath(path, objectType)
|
||||
|
||||
return _set_dacl_inheritance(path, objectType, True, None, clear)
|
||||
|
||||
|
||||
def disable_inheritance(path, objectType, copy=True):
|
||||
'''
|
||||
disable inheritance on an object
|
||||
|
||||
copy = True will copy the Inerhited ACEs to the DACL before disabling inheritance
|
||||
'''
|
||||
dc = daclConstants()
|
||||
objectType = dc.getObjectTypeBit(objectType)
|
||||
path = dc.processPath(path, objectType)
|
||||
|
||||
return _set_dacl_inheritance(path, objectType, False, copy, None)
|
||||
|
||||
|
||||
def check_inheritance(path, objectType):
|
||||
'''
|
||||
check a specified path to verify if inheritance is enabled
|
||||
returns 'Inheritance' of True/False
|
||||
|
||||
hkey: HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER, etc
|
||||
path: path of the registry key to check
|
||||
'''
|
||||
|
||||
ret = {'result': False,
|
||||
'Inheritance': False,
|
||||
'comment': []}
|
||||
dc = daclConstants()
|
||||
objectType = dc.getObjectTypeBit(objectType)
|
||||
path = dc.processPath(path, objectType)
|
||||
|
||||
try:
|
||||
sd = win32security.GetNamedSecurityInfo(path, objectType, win32security.DACL_SECURITY_INFORMATION)
|
||||
dacls = sd.GetSecurityDescriptorDacl()
|
||||
except Exception as e:
|
||||
ret['result'] = False
|
||||
ret['comment'].append((
|
||||
'Error obtaining the Security Descriptor or DACL of the path: {0}'
|
||||
).format(e))
|
||||
return ret
|
||||
|
||||
for counter in range(0, dacls.GetAceCount()):
|
||||
ace = dacls.GetAce(counter)
|
||||
if (ace[0][1] & win32security.INHERITED_ACE) == win32security.INHERITED_ACE:
|
||||
ret['Inheritance'] = True
|
||||
ret['result'] = True
|
||||
return ret
|
||||
|
||||
|
||||
def check_ace(path, objectType, user=None, permission=None, acetype=None, propagation=None, exactPermissionMatch=False):
|
||||
'''
|
||||
checks a path to verify the ACE (access control entry) specified exists
|
||||
returns 'Exists' true if the ACE exists, false if it does not
|
||||
|
||||
path: path to the file/reg key
|
||||
user: user that the ACL is for
|
||||
permission: permission to test for (READ, FULLCONTROl, etc)
|
||||
acetype: the type of ACE (ALLOW or DENY)
|
||||
propagation: the propagation type of the ACE (FILES, FOLDERS, KEY, KEY&SUBKEYS, SUBKEYS, etc)
|
||||
exactPermissionMatch: the ACL must match exactly, IE if READ is specified, the user must have READ exactly and not FULLCONTROL (which also has the READ permission obviously)
|
||||
|
||||
'''
|
||||
ret = {'result': False,
|
||||
'Exists': False,
|
||||
'comment': []}
|
||||
|
||||
dc = daclConstants()
|
||||
objectTypeBit = dc.getObjectTypeBit(objectType)
|
||||
path = dc.processPath(path, objectTypeBit)
|
||||
|
||||
permission = permission.upper()
|
||||
acetype = acetype.upper()
|
||||
propagation = propagation.upper()
|
||||
|
||||
permissionbit = dc.getPermissionBit(objectTypeBit, permission)
|
||||
acetypebit = dc.getAceTypeBit(acetype)
|
||||
propagationbit = dc.getPropagationBit(objectTypeBit, propagation)
|
||||
|
||||
try:
|
||||
userSid = win32security.LookupAccountName('', user)[0]
|
||||
except Exception as e:
|
||||
ret['result'] = False
|
||||
ret['comment'].append((
|
||||
'Unable to obtain the security identifier for {0}. The exception was {1}'
|
||||
).format(user, e))
|
||||
return ret
|
||||
|
||||
dacls = _get_dacl(path, objectTypeBit)
|
||||
if dacls:
|
||||
if objectTypeBit == win32security.SE_FILE_OBJECT:
|
||||
if check_inheritance(path, objectType)['Inheritance']:
|
||||
if permission == 'FULLCONTROL':
|
||||
# if inhertiance is enabled on an SE_FILE_OBJECT, then the SI_NO_ACL_PROTECT
|
||||
# gets unset on FullControl which greys out the include inheritable permission
|
||||
# checkbox on the advanced security settings gui page
|
||||
permissionbit = permissionbit ^ ntsecuritycon.SI_NO_ACL_PROTECT
|
||||
for counter in range(0, dacls.GetAceCount()):
|
||||
ace = dacls.GetAce(counter)
|
||||
if ace[2] == userSid:
|
||||
if ace[0][0] == acetypebit:
|
||||
if (ace[0][1] & propagationbit) == propagationbit:
|
||||
if exactPermissionMatch:
|
||||
if ace[1] == permissionbit:
|
||||
ret['result'] = True
|
||||
ret['Exists'] = True
|
||||
return ret
|
||||
else:
|
||||
if (ace[1] & permissionbit) == permissionbit:
|
||||
ret['result'] = True
|
||||
ret['Exists'] = True
|
||||
return ret
|
||||
else:
|
||||
ret['result'] = False
|
||||
ret['comment'].append(
|
||||
'Error obtaining DACL for object')
|
||||
ret['result'] = True
|
||||
return ret
|
234
salt/states/win_dacl.py
Normal file
234
salt/states/win_dacl.py
Normal file
@ -0,0 +1,234 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
Windows Object Access Control Lists
|
||||
|
||||
Ensure an ACL is present
|
||||
parameters:
|
||||
name - the path of the object
|
||||
objectType - Registry/File/Directory
|
||||
user - user account for the ace
|
||||
permission - permission for the ace (see module win_acl for available permissions for each objectType)
|
||||
acetype - Allow/Deny
|
||||
propagation - how the ACL should apply to child objects (see module win_acl for available propagation types)
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
addAcl:
|
||||
win_dacl.present:
|
||||
- name: HKEY_LOCAL_MACHINE\\SOFTWARE\\mykey
|
||||
- objectType: Registry
|
||||
- user: FakeUser
|
||||
- permission: FulLControl
|
||||
- acetype: ALLOW
|
||||
- propagation: KEY&SUBKEYS
|
||||
|
||||
Ensure an ACL does not exist
|
||||
parameters:
|
||||
name - the path of the object
|
||||
objectType - Registry/File/Directory
|
||||
user - user account for the ace
|
||||
permission - permission for the ace (see module win_acl for available permissions for each objectType)
|
||||
acetype - Allow/Deny
|
||||
propagation - how the ACL should apply to child objects (see module win_acl for available propagation types)
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
removeAcl:
|
||||
win_dacl.absent:
|
||||
- name: HKEY_LOCAL_MACHINE\\SOFTWARE\\mykey
|
||||
- objectType: Registry
|
||||
- user: FakeUser
|
||||
- permission: FulLControl
|
||||
- acetype: ALLOW
|
||||
- propagation: KEY&SUBKEYS
|
||||
|
||||
Ensure an object is inheriting permissions
|
||||
parameters:
|
||||
name - the path of the object
|
||||
objectType - Registry/File/Directory
|
||||
clear_existing_acl - True/False - when inheritance is enabled, should the existing ACL be kept or cleared out
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
eInherit:
|
||||
win_dacl.enableinheritance:
|
||||
- name: HKEY_LOCAL_MACHINE\\SOFTWARE\\mykey
|
||||
- objectType: Registry
|
||||
- clear_existing_acl: True
|
||||
|
||||
Ensure an object is not inheriting permissions
|
||||
parameters:
|
||||
name - the path of the object
|
||||
objectType - Registry/File/Directory
|
||||
copy_inherited_acl - True/False - if inheritance is enabled, should the inherited permissions be copied to the ACL when inheritance is disabled
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
dInherit:
|
||||
win_dacl.disableinheritance:
|
||||
- name: HKEY_LOCAL_MACHINE\\SOFTWARE\\mykey
|
||||
- objectType: Registry
|
||||
- copy_inherited_acl: False
|
||||
'''
|
||||
|
||||
|
||||
__virtualname__ = 'win_dacl'
|
||||
|
||||
|
||||
def __virtual__():
|
||||
'''
|
||||
Load this state if the win_acl module exists
|
||||
'''
|
||||
return 'win_dacl' if 'win_dacl.add_ace' in __salt__ else False
|
||||
|
||||
|
||||
def present(name, objectType, user, permission, acetype, propagation):
|
||||
'''
|
||||
Ensure an ACE is present
|
||||
'''
|
||||
ret = {'name': name,
|
||||
'result': True,
|
||||
'changes': {},
|
||||
'comment': []}
|
||||
tRet = __salt__['win_dacl.check_ace'](name, objectType, user, permission, acetype, propagation, True)
|
||||
if tRet['result']:
|
||||
if not tRet['Exists']:
|
||||
if __opts__['test']:
|
||||
ret['result'] = None
|
||||
ret['comment'].append(
|
||||
'The ACE is set to be added')
|
||||
ret['changes']['Added ACEs'] = ((
|
||||
'{0} {1} {2} on {3}'
|
||||
).format(user, acetype, permission, propagation))
|
||||
return ret
|
||||
addRet = __salt__['win_dacl.add_ace'](name, objectType, user, permission, acetype, propagation)
|
||||
if addRet['result']:
|
||||
ret['result'] = True
|
||||
ret['changes'] = dict(ret['changes'], **addRet['changes'])
|
||||
else:
|
||||
ret['result'] = False
|
||||
ret['comment'] = ret['comment'] + addRet['comment']
|
||||
else:
|
||||
if __opts__['test']:
|
||||
ret['result'] = None
|
||||
ret['comment'].append(
|
||||
'The ACE is present')
|
||||
else:
|
||||
ret['result'] = False
|
||||
ret['comment'] = tRet['comment']
|
||||
return ret
|
||||
return ret
|
||||
|
||||
|
||||
def absent(name, objectType, user, permission, acetype, propagation):
|
||||
'''
|
||||
Ensure a Linux ACL does not exist
|
||||
'''
|
||||
ret = {'name': name,
|
||||
'result': True,
|
||||
'changes': {},
|
||||
'comment': []}
|
||||
tRet = __salt__['win_dacl.check_ace'](name, objectType, user, permission, acetype, propagation, True)
|
||||
if tRet['result']:
|
||||
if tRet['Exists']:
|
||||
if __opts__['test']:
|
||||
ret['result'] = None
|
||||
ret['comment'].append(
|
||||
'The ACE is set to be removed')
|
||||
ret['changes']['Removed ACEs'] = ((
|
||||
'{0} {1} {2} on {3}'
|
||||
).format(user, acetype, permission, propagation))
|
||||
return ret
|
||||
addRet = __salt__['win_dacl.rm_ace'](name, objectType, user, permission, acetype, propagation)
|
||||
if addRet['result']:
|
||||
ret['result'] = True
|
||||
ret['changes'] = dict(ret['changes'], **addRet['changes'])
|
||||
else:
|
||||
ret['result'] = False
|
||||
ret['comment'] = ret['comment'] + addRet['comment']
|
||||
else:
|
||||
if __opts__['test']:
|
||||
ret['result'] = None
|
||||
ret['comment'].append(
|
||||
'The ACE is not present')
|
||||
else:
|
||||
ret['result'] = False
|
||||
ret['comment'] = tRet['comment']
|
||||
return ret
|
||||
return ret
|
||||
|
||||
|
||||
def inherit(name, objectType, clear_existing_acl=False):
|
||||
'''
|
||||
Ensure an object is inheriting ACLs from its parent
|
||||
'''
|
||||
ret = {'name': name,
|
||||
'result': True,
|
||||
'changes': {},
|
||||
'comment': []}
|
||||
tRet = __salt__['win_dacl.check_inheritance'](name, objectType)
|
||||
if tRet['result']:
|
||||
if not tRet['Inheritance']:
|
||||
if __opts__['test']:
|
||||
ret['result'] = None
|
||||
ret['changes']['Inheritance'] = "Enabled"
|
||||
ret['comment'].append(
|
||||
'Inheritance is set to be enabled')
|
||||
ret['changes']['Existing ACLs'] = (
|
||||
'Are set to be removed' if clear_existing_acl else 'Are set to be kept')
|
||||
return ret
|
||||
eRet = __salt__['win_dacl.enable_inheritance'](name, objectType, clear_existing_acl)
|
||||
if eRet['result']:
|
||||
ret['result'] = True
|
||||
ret['changes'] = dict(ret['changes'], **eRet['changes'])
|
||||
else:
|
||||
ret['result'] = False
|
||||
ret['comment'] = ret['comment'] + eRet['comment']
|
||||
else:
|
||||
if __opts__['test']:
|
||||
ret['result'] = None
|
||||
ret['comment'].append(
|
||||
'Inheritance is enabled')
|
||||
else:
|
||||
ret['result'] = False
|
||||
ret['comment'] = tRet['comment']
|
||||
return ret
|
||||
return ret
|
||||
|
||||
|
||||
def disinherit(name, objectType, copy_inherited_acl=True):
|
||||
'''
|
||||
Ensure an object is not inheriting ACLs from its parent
|
||||
'''
|
||||
ret = {'name': name,
|
||||
'result': True,
|
||||
'changes': {},
|
||||
'comment': []}
|
||||
tRet = __salt__['win_dacl.check_inheritance'](name, objectType)
|
||||
if tRet['result']:
|
||||
if tRet['Inheritance']:
|
||||
if __opts__['test']:
|
||||
ret['result'] = None
|
||||
ret['changes']['Inheritance'] = "Disabled"
|
||||
ret['comment'].append(
|
||||
'Inheritance is set to be disabled')
|
||||
ret['changes']['Inherited ACLs'] = (
|
||||
'Are set to be kept' if copy_inherited_acl else 'Are set to be removed')
|
||||
return ret
|
||||
eRet = __salt__['win_dacl.disable_inheritance'](name, objectType, copy_inherited_acl)
|
||||
if eRet['result']:
|
||||
ret['result'] = True
|
||||
ret['changes'] = dict(ret['changes'], **eRet['changes'])
|
||||
else:
|
||||
ret['result'] = False
|
||||
ret['comment'] = ret['comment'] + eRet['comment']
|
||||
else:
|
||||
if __opts__['test']:
|
||||
ret['result'] = None
|
||||
ret['comment'].append(
|
||||
'Inheritance is disabled')
|
||||
else:
|
||||
ret['result'] = False
|
||||
ret['comment'] = tRet['comment']
|
||||
return ret
|
||||
return ret
|
Loading…
Reference in New Issue
Block a user