/hubblestack_nova/win_auditpol.py

https://github.com/HubbleStack/Nova
Python | 177 lines | 128 code | 20 blank | 29 comment | 47 complexity | 515ccc1d7ed44105154586ac6841719a MD5 | raw file
  1. # -*- encoding: utf-8 -*-
  2. '''
  3. :maintainer: HubbleStack / madchills
  4. :maturity: 2016.7.0
  5. :platform: Windows
  6. :requires: SaltStack
  7. '''
  8. from __future__ import absolute_import
  9. import copy
  10. import csv
  11. import fnmatch
  12. import logging
  13. import salt.utils
  14. log = logging.getLogger(__name__)
  15. __virtualname__ = 'win_auditpol'
  16. def __virtual__():
  17. if not salt.utils.is_windows():
  18. return False, 'This audit module only runs on windows'
  19. return True
  20. def audit(data_list, tags, debug=False):
  21. '''
  22. Runs auditpol on the local machine and audits the return data
  23. with the CIS yaml processed by __virtual__
  24. '''
  25. __data__ = {}
  26. __auditdata__ = _auditpol_import()
  27. for profile, data in data_list:
  28. _merge_yaml(__data__, data, profile)
  29. __tags__ = _get_tags(__data__)
  30. if debug:
  31. log.debug('auditpol audit __data__:')
  32. log.debug(__data__)
  33. log.debug('auditpol audit __tags__:')
  34. log.debug(__tags__)
  35. ret = {'Success': [], 'Failure': [], 'Controlled': []}
  36. for tag in __tags__:
  37. if fnmatch.fnmatch(tag, tags):
  38. for tag_data in __tags__[tag]:
  39. if 'control' in tag_data:
  40. ret['Controlled'].append(tag_data)
  41. continue
  42. name = tag_data['name']
  43. audit_type = tag_data['type']
  44. match_output = tag_data['match_output'].lower()
  45. # Blacklisted audit (do not include)
  46. if 'blacklist' in audit_type:
  47. if name not in __auditdata__:
  48. ret['Success'].append(tag_data)
  49. else:
  50. ret['Failure'].append(tag_data)
  51. # Whitelisted audit (must include)
  52. if 'whitelist' in audit_type:
  53. if name in __auditdata__:
  54. audit_value = __auditdata__[name].lower()
  55. secret = _translate_value_type(audit_value, tag_data['value_type'], match_output)
  56. if secret:
  57. ret['Success'].append(tag_data)
  58. else:
  59. ret['Failure'].append(tag_data)
  60. else:
  61. log.debug('When trying to audit the advanced auditpol section,'
  62. ' the yaml contained incorrect data for the key')
  63. return ret
  64. def _merge_yaml(ret, data, profile=None):
  65. '''
  66. Merge two yaml dicts together at the secedit:blacklist and
  67. secedit:whitelist level
  68. '''
  69. if __virtualname__ not in ret:
  70. ret[__virtualname__] = {}
  71. for topkey in ('blacklist', 'whitelist'):
  72. if topkey in data.get(__virtualname__, {}):
  73. if topkey not in ret[__virtualname__]:
  74. ret[__virtualname__][topkey] = []
  75. for key, val in data[__virtualname__][topkey].iteritems():
  76. if profile and isinstance(val, dict):
  77. val['nova_profile'] = profile
  78. ret[__virtualname__][topkey].append({key: val})
  79. return ret
  80. def _get_tags(data):
  81. '''
  82. Retrieve all the tags for this distro from the yaml
  83. '''
  84. ret = {}
  85. distro = __grains__.get('osfullname')
  86. for toplist, toplevel in data.get(__virtualname__, {}).iteritems():
  87. # secedit:whitelist
  88. for audit_dict in toplevel:
  89. for audit_id, audit_data in audit_dict.iteritems():
  90. # secedit:whitelist:PasswordComplexity
  91. tags_dict = audit_data.get('data', {})
  92. # secedit:whitelist:PasswordComplexity:data
  93. tags = None
  94. for osfinger in tags_dict:
  95. if osfinger == '*':
  96. continue
  97. osfinger_list = [finger.strip() for finger in osfinger.split(',')]
  98. for osfinger_glob in osfinger_list:
  99. if fnmatch.fnmatch(distro, osfinger_glob):
  100. tags = tags_dict.get(osfinger)
  101. break
  102. if tags is not None:
  103. break
  104. # If we didn't find a match, check for a '*'
  105. if tags is None:
  106. tags = tags_dict.get('*', [])
  107. # secedit:whitelist:PasswordComplexity:data:Windows 2012
  108. if isinstance(tags, dict):
  109. # malformed yaml, convert to list of dicts
  110. tmp = []
  111. for name, tag in tags.iteritems():
  112. tmp.append({name: tag})
  113. tags = tmp
  114. for item in tags:
  115. for name, tag in item.iteritems():
  116. tag_data = {}
  117. # Whitelist could have a dictionary, not a string
  118. if isinstance(tag, dict):
  119. tag_data = copy.deepcopy(tag)
  120. tag = tag_data.pop('tag')
  121. if tag not in ret:
  122. ret[tag] = []
  123. formatted_data = {'name': name,
  124. 'tag': tag,
  125. 'module': 'win_auditpol',
  126. 'type': toplist}
  127. formatted_data.update(tag_data)
  128. formatted_data.update(audit_data)
  129. formatted_data.pop('data')
  130. ret[tag].append(formatted_data)
  131. return ret
  132. def _auditpol_export():
  133. try:
  134. dump = __salt__['cmd.run']('auditpol /get /category:* /r')
  135. if dump:
  136. dump = dump.split('\n')
  137. return dump
  138. else:
  139. log.error('Nothing was returned from the auditpol command.')
  140. except StandardError:
  141. log.error('An error occurred running the auditpol command.')
  142. def _auditpol_import():
  143. dict_return = {}
  144. export = _auditpol_export()
  145. auditpol_csv = csv.DictReader(export)
  146. for row in auditpol_csv:
  147. if row:
  148. dict_return[row['Subcategory']] = row['Inclusion Setting']
  149. return dict_return
  150. def _translate_value_type(current, value, evaluator):
  151. if 'equal' in value:
  152. if current == evaluator:
  153. return True
  154. else:
  155. return False