diff --git a/salt/modules/aptpkg.py b/salt/modules/aptpkg.py index be296f0decd5..d37115ad1c28 100644 --- a/salt/modules/aptpkg.py +++ b/salt/modules/aptpkg.py @@ -18,6 +18,9 @@ import re import logging import time +import fnmatch +import datetime + # Import third party libs # pylint: disable=no-name-in-module,import-error,redefined-builtin @@ -384,6 +387,7 @@ def install(name=None, pkgs=None, sources=None, reinstall=False, + downloadonly=False, ignore_epoch=False, **kwargs): ''' @@ -730,6 +734,9 @@ def install(name=None, cmd.extend(downgrade) cmds.append(cmd) + if downloadonly: + cmd.append("--download-only") + if to_reinstall: all_pkgs.extend(to_reinstall) cmd = copy.deepcopy(cmd_prefix) @@ -2846,3 +2853,37 @@ def _get_http_proxy_url(): ) return http_proxy_url + + +def list_downloaded(root=None, **kwargs): + ''' + .. versionadded:: 3000? + + List prefetched packages downloaded by apt in the local disk. + + root + operate on a different root directory. + + CLI example: + + .. code-block:: bash + + salt '*' pkg.list_downloaded + ''' + CACHE_DIR = '/var/cache/apt' + if root: + CACHE_DIR = os.path.join(root, os.path.relpath(CACHE_DIR, os.path.sep)) + + ret = {} + for root, dirnames, filenames in salt.utils.path.os_walk(CACHE_DIR): + for filename in fnmatch.filter(filenames, '*.deb'): + package_path = os.path.join(root, filename) + pkg_info = __salt__['lowpkg.bin_pkg_info'](package_path) + pkg_timestamp = int(os.path.getctime(package_path)) + ret.setdefault(pkg_info['name'], {})[pkg_info['version']] = { + 'path': package_path, + 'size': os.path.getsize(package_path), + 'creation_date_time_t': pkg_timestamp, + 'creation_date_time': datetime.datetime.utcfromtimestamp(pkg_timestamp).isoformat(), + } + return ret diff --git a/salt/modules/win_lgpo.py b/salt/modules/win_lgpo.py index d39813780a80..5881987b03da 100644 --- a/salt/modules/win_lgpo.py +++ b/salt/modules/win_lgpo.py @@ -40,6 +40,8 @@ # Import Python libs from __future__ import absolute_import, unicode_literals, print_function import csv +import glob +import zlib import io import os import logging @@ -71,7 +73,6 @@ UUID = uuid.uuid4().hex adm_policy_name_map = {True: {}, False: {}} -adm_policy_key_map = {} HAS_WINDOWS_MODULES = False # define some global XPATH variables that we'll set assuming all our imports are # good @@ -86,7 +87,6 @@ TRUE_LIST_XPATH = None FALSE_LIST_XPATH = None REGKEY_XPATH = None -REGKEY_XPATH_MAPPED = None POLICY_ANCESTOR_XPATH = None ALL_CLASS_POLICY_XPATH = None ADML_DISPLAY_NAME_XPATH = None @@ -116,8 +116,7 @@ VALUE_XPATH = etree.XPath('.//*[local-name() = "value"]') TRUE_LIST_XPATH = etree.XPath('.//*[local-name() = "trueList"]') FALSE_LIST_XPATH = etree.XPath('.//*[local-name() = "falseList"]') - REGKEY_XPATH = etree.XPath('//*[translate(@*[local-name() = "key"], "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz") = $keyvalue]') - REGKEY_XPATH_MAPPED = etree.XPath('//*[@key = $keyvalue]') + REGKEY_XPATH = etree.XPath('//*[@key = $keyvalue]') POLICY_ANCESTOR_XPATH = etree.XPath('ancestor::*[local-name() = "policy"]') ALL_CLASS_POLICY_XPATH = etree.XPath('//*[local-name() = "policy" and (@*[local-name() = "class"] = "Both" or @*[local-name() = "class"] = $registry_class)]') ADML_DISPLAY_NAME_XPATH = etree.XPath('//*[local-name() = $displayNameType and @*[local-name() = "id"] = $displayNameId]') @@ -493,7 +492,7 @@ def __init__(self): self.smb_server_name_hardening_levels = { 0: 'Off', 1: 'Accept if provided by client', - 2: 'Require from client', + 2: 'Required from client', None: 'Not Defined', '(value not set)': 'Not Defined', } @@ -552,7 +551,7 @@ def __init__(self): 0: 'No minimum', 1: 'DES_CBC_CRC', 2: 'DES_CBD_MD5', - 4: 'RC4_MHAC_MD5', + 4: 'RC4_HMAC_MD5', 8: 'AES128_HMAC_SHA1', 16: 'AES256_HMAC_SHA1', 2147483616: 'Future Encryption Types', @@ -842,7 +841,8 @@ def __init__(self): 'Type': 'REG_MULTI_SZ' }, 'Transform': { - 'Put': '_multi_string_put_transform' + 'Put': '_multi_string_put_transform', + 'Get': '_multi_string_get_transform' } }, 'RemoteRegistryExactPaths': { @@ -858,7 +858,8 @@ def __init__(self): 'Type': 'REG_MULTI_SZ' }, 'Transform': { - 'Put': '_multi_string_put_transform' + 'Put': '_multi_string_put_transform', + 'Get': '_multi_string_get_transform' } }, 'RemoteRegistryPaths': { @@ -873,7 +874,8 @@ def __init__(self): 'Type': 'REG_MULTI_SZ' }, 'Transform': { - 'Put': '_multi_string_put_transform' + 'Put': '_multi_string_put_transform', + 'Get': '_multi_string_get_transform' } }, 'RestrictNullSessAccess': { @@ -902,7 +904,8 @@ def __init__(self): 'Type': 'REG_MULTI_SZ' }, 'Transform': { - 'Put': '_multi_string_put_transform' + 'Put': '_multi_string_put_transform', + 'Get': '_multi_string_get_transform' } }, 'ForceGuest': { @@ -1886,7 +1889,7 @@ def __init__(self): 'Path': 'Software\\Microsoft\\Windows NT\\' 'CurrentVersion\\Winlogon', 'Value': 'ScRemoveOption', - 'Type': 'REG_DWORD', + 'Type': 'REG_SZ', }, 'Transform': { 'Get': '_dict_lookup', @@ -3298,6 +3301,7 @@ def __init__(self): 'Policy': 'Access Credential Manager as a trusted ' 'caller', 'lgpo_section': self.user_rights_assignment_gpedit_path, + 'rights_assignment': True, 'Settings': None, 'LsaRights': { 'Option': 'SeTrustedCredManAccessPrivilege' @@ -3310,6 +3314,7 @@ def __init__(self): 'SeNetworkLogonRight': { 'Policy': 'Access this computer from the network', 'lgpo_section': self.user_rights_assignment_gpedit_path, + 'rights_assignment': True, 'Settings': None, 'LsaRights': { 'Option': 'SeNetworkLogonRight' @@ -3322,6 +3327,7 @@ def __init__(self): 'SeTcbPrivilege': { 'Policy': 'Act as part of the operating system', 'lgpo_section': self.user_rights_assignment_gpedit_path, + 'rights_assignment': True, 'Settings': None, 'LsaRights': { 'Option': 'SeTcbPrivilege' @@ -3334,6 +3340,7 @@ def __init__(self): 'SeMachineAccountPrivilege': { 'Policy': 'Add workstations to domain', 'lgpo_section': self.user_rights_assignment_gpedit_path, + 'rights_assignment': True, 'Settings': None, 'LsaRights': { 'Option': 'SeMachineAccountPrivilege' @@ -3346,6 +3353,7 @@ def __init__(self): 'SeIncreaseQuotaPrivilege': { 'Policy': 'Adjust memory quotas for a process', 'lgpo_section': self.user_rights_assignment_gpedit_path, + 'rights_assignment': True, 'Settings': None, 'LsaRights': { 'Option': 'SeIncreaseQuotaPrivilege' @@ -3358,6 +3366,7 @@ def __init__(self): 'SeInteractiveLogonRight': { 'Policy': 'Allow log on locally', 'lgpo_section': self.user_rights_assignment_gpedit_path, + 'rights_assignment': True, 'Settings': None, 'LsaRights': { 'Option': 'SeInteractiveLogonRight' @@ -3370,6 +3379,7 @@ def __init__(self): 'SeRemoteInteractiveLogonRight': { 'Policy': 'Allow log on through Remote Desktop Services', 'lgpo_section': self.user_rights_assignment_gpedit_path, + 'rights_assignment': True, 'Settings': None, 'LsaRights': { 'Option': 'SeRemoteInteractiveLogonRight' @@ -3382,6 +3392,7 @@ def __init__(self): 'SeBackupPrivilege': { 'Policy': 'Backup files and directories', 'lgpo_section': self.user_rights_assignment_gpedit_path, + 'rights_assignment': True, 'Settings': None, 'LsaRights': { 'Option': 'SeBackupPrivilege' @@ -3394,6 +3405,7 @@ def __init__(self): 'SeChangeNotifyPrivilege': { 'Policy': 'Bypass traverse checking', 'lgpo_section': self.user_rights_assignment_gpedit_path, + 'rights_assignment': True, 'Settings': None, 'LsaRights': { 'Option': 'SeChangeNotifyPrivilege' @@ -3406,6 +3418,7 @@ def __init__(self): 'SeSystemtimePrivilege': { 'Policy': 'Change the system time', 'lgpo_section': self.user_rights_assignment_gpedit_path, + 'rights_assignment': True, 'Settings': None, 'LsaRights': { 'Option': 'SeSystemtimePrivilege' @@ -3418,6 +3431,7 @@ def __init__(self): 'SeTimeZonePrivilege': { 'Policy': 'Change the time zone', 'lgpo_section': self.user_rights_assignment_gpedit_path, + 'rights_assignment': True, 'Settings': None, 'LsaRights': { 'Option': 'SeTimeZonePrivilege' @@ -3430,6 +3444,7 @@ def __init__(self): 'SeCreatePagefilePrivilege': { 'Policy': 'Create a pagefile', 'lgpo_section': self.user_rights_assignment_gpedit_path, + 'rights_assignment': True, 'Settings': None, 'LsaRights': { 'Option': 'SeCreatePagefilePrivilege' @@ -3442,6 +3457,7 @@ def __init__(self): 'SeCreateTokenPrivilege': { 'Policy': 'Create a token object', 'lgpo_section': self.user_rights_assignment_gpedit_path, + 'rights_assignment': True, 'Settings': None, 'LsaRights': { 'Option': 'SeCreateTokenPrivilege' @@ -3454,6 +3470,7 @@ def __init__(self): 'SeCreateGlobalPrivilege': { 'Policy': 'Create global objects', 'lgpo_section': self.user_rights_assignment_gpedit_path, + 'rights_assignment': True, 'Settings': None, 'LsaRights': { 'Option': 'SeCreateGlobalPrivilege' @@ -3466,6 +3483,7 @@ def __init__(self): 'SeCreatePermanentPrivilege': { 'Policy': 'Create permanent shared objects', 'lgpo_section': self.user_rights_assignment_gpedit_path, + 'rights_assignment': True, 'Settings': None, 'LsaRights': { 'Option': 'SeCreatePermanentPrivilege' @@ -3478,6 +3496,7 @@ def __init__(self): 'SeCreateSymbolicLinkPrivilege': { 'Policy': 'Create symbolic links', 'lgpo_section': self.user_rights_assignment_gpedit_path, + 'rights_assignment': True, 'Settings': None, 'LsaRights': { 'Option': 'SeCreateSymbolicLinkPrivilege' @@ -3490,6 +3509,7 @@ def __init__(self): 'SeDebugPrivilege': { 'Policy': 'Debug programs', 'lgpo_section': self.user_rights_assignment_gpedit_path, + 'rights_assignment': True, 'Settings': None, 'LsaRights': { 'Option': 'SeDebugPrivilege' @@ -3503,6 +3523,7 @@ def __init__(self): 'Policy': 'Deny access to this computer from the ' 'network', 'lgpo_section': self.user_rights_assignment_gpedit_path, + 'rights_assignment': True, 'Settings': None, 'LsaRights': { 'Option': 'SeDenyNetworkLogonRight' @@ -3515,6 +3536,7 @@ def __init__(self): 'SeDenyBatchLogonRight': { 'Policy': 'Deny log on as a batch job', 'lgpo_section': self.user_rights_assignment_gpedit_path, + 'rights_assignment': True, 'Settings': None, 'LsaRights': { 'Option': 'SeDenyBatchLogonRight' @@ -3527,6 +3549,7 @@ def __init__(self): 'SeDenyServiceLogonRight': { 'Policy': 'Deny log on as a service', 'lgpo_section': self.user_rights_assignment_gpedit_path, + 'rights_assignment': True, 'Settings': None, 'LsaRights': { 'Option': 'SeDenyServiceLogonRight' @@ -3539,6 +3562,7 @@ def __init__(self): 'SeDenyInteractiveLogonRight': { 'Policy': 'Deny log on locally', 'lgpo_section': self.user_rights_assignment_gpedit_path, + 'rights_assignment': True, 'Settings': None, 'LsaRights': { 'Option': 'SeDenyInteractiveLogonRight' @@ -3551,6 +3575,7 @@ def __init__(self): 'SeDenyRemoteInteractiveLogonRight': { 'Policy': 'Deny log on through Remote Desktop Services', 'lgpo_section': self.user_rights_assignment_gpedit_path, + 'rights_assignment': True, 'Settings': None, 'LsaRights': { 'Option': 'SeDenyRemoteInteractiveLogonRight' @@ -3564,6 +3589,7 @@ def __init__(self): 'Policy': 'Enable computer and user accounts to be ' 'trusted for delegation', 'lgpo_section': self.user_rights_assignment_gpedit_path, + 'rights_assignment': True, 'Settings': None, 'LsaRights': { 'Option': 'SeEnableDelegationPrivilege' @@ -3576,6 +3602,7 @@ def __init__(self): 'SeRemoteShutdownPrivilege': { 'Policy': 'Force shutdown from a remote system', 'lgpo_section': self.user_rights_assignment_gpedit_path, + 'rights_assignment': True, 'Settings': None, 'LsaRights': { 'Option': 'SeRemoteShutdownPrivilege' @@ -3588,6 +3615,7 @@ def __init__(self): 'SeAuditPrivilege': { 'Policy': 'Generate security audits', 'lgpo_section': self.user_rights_assignment_gpedit_path, + 'rights_assignment': True, 'Settings': None, 'LsaRights': { 'Option': 'SeAuditPrivilege' @@ -3600,6 +3628,7 @@ def __init__(self): 'SeImpersonatePrivilege': { 'Policy': 'Impersonate a client after authentication', 'lgpo_section': self.user_rights_assignment_gpedit_path, + 'rights_assignment': True, 'Settings': None, 'LsaRights': { 'Option': 'SeImpersonatePrivilege' @@ -3612,6 +3641,7 @@ def __init__(self): 'SeIncreaseWorkingSetPrivilege': { 'Policy': 'Increase a process working set', 'lgpo_section': self.user_rights_assignment_gpedit_path, + 'rights_assignment': True, 'Settings': None, 'LsaRights': { 'Option': 'SeIncreaseWorkingSetPrivilege' @@ -3623,6 +3653,7 @@ def __init__(self): }, 'SeIncreaseBasePriorityPrivilege': { 'Policy': 'Increase scheduling priority', + 'rights_assignment': True, 'lgpo_section': self.user_rights_assignment_gpedit_path, 'Settings': None, 'LsaRights': { @@ -3636,6 +3667,7 @@ def __init__(self): 'SeLoadDriverPrivilege': { 'Policy': 'Load and unload device drivers', 'lgpo_section': self.user_rights_assignment_gpedit_path, + 'rights_assignment': True, 'Settings': None, 'LsaRights': { 'Option': 'SeLoadDriverPrivilege' @@ -3648,6 +3680,7 @@ def __init__(self): 'SeLockMemoryPrivilege': { 'Policy': 'Lock pages in memory', 'lgpo_section': self.user_rights_assignment_gpedit_path, + 'rights_assignment': True, 'Settings': None, 'LsaRights': { 'Option': 'SeLockMemoryPrivilege' @@ -3660,6 +3693,7 @@ def __init__(self): 'SeBatchLogonRight': { 'Policy': 'Log on as a batch job', 'lgpo_section': self.user_rights_assignment_gpedit_path, + 'rights_assignment': True, 'Settings': None, 'LsaRights': { 'Option': 'SeBatchLogonRight' @@ -3672,6 +3706,7 @@ def __init__(self): 'SeServiceLogonRight': { 'Policy': 'Log on as a service', 'lgpo_section': self.user_rights_assignment_gpedit_path, + 'rights_assignment': True, 'Settings': None, 'LsaRights': { 'Option': 'SeServiceLogonRight' @@ -3684,6 +3719,7 @@ def __init__(self): 'SeSecurityPrivilege': { 'Policy': 'Manage auditing and security log', 'lgpo_section': self.user_rights_assignment_gpedit_path, + 'rights_assignment': True, 'Settings': None, 'LsaRights': { 'Option': 'SeSecurityPrivilege' @@ -3696,6 +3732,7 @@ def __init__(self): 'SeRelabelPrivilege': { 'Policy': 'Modify an object label', 'lgpo_section': self.user_rights_assignment_gpedit_path, + 'rights_assignment': True, 'Settings': None, 'LsaRights': { 'Option': 'SeRelabelPrivilege' @@ -3708,6 +3745,7 @@ def __init__(self): 'SeSystemEnvironmentPrivilege': { 'Policy': 'Modify firmware environment values', 'lgpo_section': self.user_rights_assignment_gpedit_path, + 'rights_assignment': True, 'Settings': None, 'LsaRights': { 'Option': 'SeSystemEnvironmentPrivilege' @@ -3720,6 +3758,7 @@ def __init__(self): 'SeManageVolumePrivilege': { 'Policy': 'Perform volume maintenance tasks', 'lgpo_section': self.user_rights_assignment_gpedit_path, + 'rights_assignment': True, 'Settings': None, 'LsaRights': { 'Option': 'SeManageVolumePrivilege' @@ -3732,6 +3771,7 @@ def __init__(self): 'SeProfileSingleProcessPrivilege': { 'Policy': 'Profile single process', 'lgpo_section': self.user_rights_assignment_gpedit_path, + 'rights_assignment': True, 'Settings': None, 'LsaRights': { 'Option': 'SeProfileSingleProcessPrivilege' @@ -3744,6 +3784,7 @@ def __init__(self): 'SeSystemProfilePrivilege': { 'Policy': 'Profile system performance', 'lgpo_section': self.user_rights_assignment_gpedit_path, + 'rights_assignment': True, 'Settings': None, 'LsaRights': { 'Option': 'SeSystemProfilePrivilege' @@ -3756,6 +3797,7 @@ def __init__(self): 'SeUndockPrivilege': { 'Policy': 'Remove computer from docking station', 'lgpo_section': self.user_rights_assignment_gpedit_path, + 'rights_assignment': True, 'Settings': None, 'LsaRights': { 'Option': 'SeUndockPrivilege' @@ -3768,6 +3810,7 @@ def __init__(self): 'SeAssignPrimaryTokenPrivilege': { 'Policy': 'Replace a process level token', 'lgpo_section': self.user_rights_assignment_gpedit_path, + 'rights_assignment': True, 'Settings': None, 'LsaRights': { 'Option': 'SeAssignPrimaryTokenPrivilege' @@ -3780,6 +3823,7 @@ def __init__(self): 'SeRestorePrivilege': { 'Policy': 'Restore files and directories', 'lgpo_section': self.user_rights_assignment_gpedit_path, + 'rights_assignment': True, 'Settings': None, 'LsaRights': { 'Option': 'SeRestorePrivilege' @@ -3792,6 +3836,7 @@ def __init__(self): 'SeShutdownPrivilege': { 'Policy': 'Shut down the system', 'lgpo_section': self.user_rights_assignment_gpedit_path, + 'rights_assignment': True, 'Settings': None, 'LsaRights': { 'Option': 'SeShutdownPrivilege' @@ -3804,6 +3849,7 @@ def __init__(self): 'SeSyncAgentPrivilege': { 'Policy': 'Synchronize directory service data', 'lgpo_section': self.user_rights_assignment_gpedit_path, + 'rights_assignment': True, 'Settings': None, 'LsaRights': { 'Option': 'SeSyncAgentPrivilege' @@ -3816,6 +3862,7 @@ def __init__(self): 'SeTakeOwnershipPrivilege': { 'Policy': 'Take ownership of files or other objects', 'lgpo_section': self.user_rights_assignment_gpedit_path, + 'rights_assignment': True, 'Settings': None, 'LsaRights': { 'Option': 'SeTakeOwnershipPrivilege' @@ -4111,7 +4158,8 @@ def __init__(self): 'Type': 'REG_MULTI_SZ' }, 'Transform': { - 'Put': '_multi_string_put_transform' + 'Put': '_multi_string_put_transform', + 'Get': '_multi_string_get_transform' } }, 'DCAllowedNTLMServers': { @@ -4125,7 +4173,8 @@ def __init__(self): 'Type': 'REG_MULTI_SZ' }, 'Transform': { - 'Put': '_multi_string_put_transform' + 'Put': '_multi_string_put_transform', + 'Get': '_multi_string_get_transform' } }, 'AuditReceivingNTLMTraffic': { @@ -4312,7 +4361,8 @@ def __init__(self): 'Type': 'REG_MULTI_SZ' }, 'Transform': { - 'Put': '_multi_string_put_transform' + 'Put': '_multi_string_put_transform', + 'Get': '_multi_string_get_transform' } }, 'AuthenticodeEnabled': { @@ -4533,7 +4583,7 @@ def _driver_signing_reg_conversion(cls, val, **kwargs): converts the binary value in the registry for driver signing into the correct string representation ''' - log.debug('we have %s for the driver signing value', val) + log.trace('we have %s for the driver signing value', val) if val is not None: # since this is from secedit, it should be 3, _val = val.split(',') @@ -4622,7 +4672,7 @@ def _powershell_script_order_conversion(cls, val, **kwargs): converts true/false/None to the GUI representation of the powershell startup/shutdown script order ''' - log.debug('script order value = %s', val) + log.trace('script order value = %s', val) if val is None or val == 'None': return 'Not Configured' elif val == 'true': @@ -4655,17 +4705,17 @@ def _dict_lookup(cls, item, **kwargs): kwarg value_lookup bool to determine if item should be compared to keys or values ''' - log.debug('item == %s', item) + log.trace('item == %s', item) value_lookup = kwargs.get('value_lookup', False) if 'lookup' in kwargs: for k, v in six.iteritems(kwargs['lookup']): if value_lookup: if six.text_type(v).lower() == six.text_type(item).lower(): - log.debug('returning key %s', k) + log.trace('returning key %s', k) return k else: if six.text_type(k).lower() == six.text_type(item).lower(): - log.debug('returning value %s', v) + log.trace('returning value %s', v) return v return 'Invalid Value' @@ -4716,7 +4766,7 @@ def _dict_lookup_bitwise_add(cls, item, **kwargs): @classmethod def _multi_string_put_transform(cls, item, **kwargs): ''' - transform for a REG_MULTI_SZ to properly handle "Not Defined" + transform for setting REG_MULTI_SZ to properly handle "Not Defined" ''' if isinstance(item, list): return item @@ -4728,10 +4778,22 @@ def _multi_string_put_transform(cls, item, **kwargs): else: return 'Invalid Value' + @classmethod + def _multi_string_get_transform(cls, item, **kwargs): + ''' + transform for getting REG_MULTI_SZ to properly handle `None` + ''' + if isinstance(item, list): + return item + elif item is None: + return 'Not Defined' + else: + return 'Invalid Value' + @classmethod def _string_put_transform(cls, item, **kwargs): ''' - transfrom for a REG_SZ to properly handle "Not Defined" + transform for a REG_SZ to properly handle "Not Defined" ''' if isinstance(item, six.string_types): if item.lower() == 'not defined': @@ -4791,13 +4853,106 @@ def _remove_unicode_encoding(xml_file): ''' attempts to remove the "encoding='unicode'" from an xml file as lxml does not support that on a windows node currently - see issue #38100 + + see issue #38100 (Search.adml) + + For some reason this file is encoded 'utf-16' ''' with salt.utils.files.fopen(xml_file, 'rb') as f: xml_content = f.read() - modified_xml = re.sub(r' encoding=[\'"]+unicode[\'"]+', '', xml_content.decode('utf-16'), count=1) - xmltree = lxml.etree.parse(six.StringIO(modified_xml)) - return xmltree + modified_xml = re.sub(r' encoding=[\'"]+unicode[\'"]+', '', + xml_content.decode('utf-16'), count=1) + xml_tree = lxml.etree.parse(six.StringIO(modified_xml)) + return xml_tree + + +def _remove_invalid_xmlns(xml_file): + ''' + Attempts to remove an invalid xmlns entry in newer versions of + WindowsDefender.adml + + xmlns="http://schemas.microsoft.com/GroupPolicy/2006/07/PolicyDefinitions" + + For some reason this file is encoded 'utf-8' + ''' + with salt.utils.files.fopen(xml_file, 'rb') as f: + xml_content = f.read() + modified_xml = re.sub(r' xmlns=[\'"]+.*[\'"]+', '', + xml_content.decode('utf-8'), count=1) + xml_tree = lxml.etree.parse(six.StringIO(modified_xml)) + return xml_tree + + +def _parse_xml(adm_file): + ''' + Parse the admx/adml file. There are 3 scenarios (so far) that we'll likely + encounter: + + 1. Valid File + 2. invalid encoding (encoding="unicode") which the lxml library doesn't + recognize + 3. invalid xmlns entry in the xml header, which the lxml library doesn't + recognize + ''' + parser = lxml.etree.XMLParser(remove_comments=True) + + modified_xml = '' + with salt.utils.files.fopen(adm_file, 'rb') as rfh: + file_hash = '{0:X}'.format(zlib.crc32(rfh.read()) & 0xffffffff) + + name, ext = os.path.splitext(os.path.basename(adm_file)) + hashed_filename = '{0}-{1}{2}'.format(name, file_hash, ext) + + cache_dir = os.path.join(__opts__['cachedir'], 'lgpo', 'policy_defs') + if not os.path.exists(cache_dir): + os.makedirs(cache_dir) + + out_file = os.path.join(cache_dir, hashed_filename) + + if not os.path.isfile(out_file): + log.debug('LGPO: Generating policy template cache for %s%s', name, ext) + + # Remove old files, keep the cache clean + file_list = glob.glob( + os.path.join(cache_dir, '{0}*{1}'.format(name, ext))) + for file_path in file_list: + os.remove(file_path) + + # Load the file + with salt.utils.files.fopen(adm_file, 'rb') as rfh: + + encoding = 'utf-8' + raw = rfh.read() + try: + raw = raw.decode(encoding) + except UnicodeDecodeError: + log.trace('LGPO: Detecting encoding') + encoding = 'utf-16' + raw = raw.decode(encoding) + for line in raw.split('\r\n'): + if 'key="' in line: + start = line.index('key="') + q1 = line[start:].index('"') + start + q2 = line[q1 + 1:].index('"') + q1 + 1 + line = line.replace(line[start:q2], line[start:q2].lower()) + found_key = True + modified_xml += line + '\r\n' + + with salt.utils.files.fopen(out_file, 'wb') as wfh: + wfh.write(modified_xml.encode(encoding)) + + try: + # First we'll try to parse valid xml + xml_tree = lxml.etree.parse(out_file, parser=parser) + except lxml.etree.XMLSyntaxError: + try: + # Next we'll try invalid encoding (see issue #38100) + xml_tree = _remove_unicode_encoding(out_file) + except lxml.etree.XMLSyntaxError: + # Finally we'll try invalid xmlns entry, if this fails, we just want + # to raise the error + xml_tree = _remove_invalid_xmlns(out_file) + return xml_tree def _load_policy_definitions(path='c:\\Windows\\PolicyDefinitions', @@ -4823,51 +4978,45 @@ def _load_policy_definitions(path='c:\\Windows\\PolicyDefinitions', policydef_resources_xpath = etree.XPath('/policyDefinitionResources') for root, dirs, files in salt.utils.path.os_walk(path): if root == path: - for t_admfile in files: - admfile = os.path.join(root, t_admfile) - parser = lxml.etree.XMLParser(remove_comments=True) - # see issue #38100 + for t_admx_file in files: + admx_file = os.path.join(root, t_admx_file) + # Parse xml for the ADMX file try: - xmltree = lxml.etree.parse(admfile, parser=parser) + xml_tree = _parse_xml(admx_file) except lxml.etree.XMLSyntaxError: - try: - xmltree = _remove_unicode_encoding(admfile) - # TODO: This needs to be more specific - except Exception: # pylint: disable=broad-except - log.exception('Handle this explicitly') - log.error('A error was found while processing admx ' - 'file %s, all policies from this file will ' - 'be unavailable via this module', admfile) - continue - namespaces = xmltree.getroot().nsmap + log.error('An error was found while processing admx ' + 'file %s, all policies from this file will ' + 'be unavailable via this module', admx_file) + continue + namespaces = xml_tree.getroot().nsmap namespace_string = '' if None in namespaces: namespaces['None'] = namespaces[None] namespaces.pop(None) namespace_string = 'None:' - this_prefix = xmltree.xpath( + this_prefix = xml_tree.xpath( '/{0}policyDefinitions/{0}policyNamespaces/{0}target/@prefix'.format(namespace_string), namespaces=namespaces)[0] - this_namespace = xmltree.xpath( + this_namespace = xml_tree.xpath( '/{0}policyDefinitions/{0}policyNamespaces/{0}target/@namespace'.format(namespace_string), namespaces=namespaces)[0] - categories = xmltree.xpath( + categories = xml_tree.xpath( '/{0}policyDefinitions/{0}categories/{0}category'.format(namespace_string), namespaces=namespaces) for category in categories: temp_cat = category temp_cat = _updateNamespace(temp_cat, this_namespace) policydefs_categories_xpath(t_policy_definitions)[0].append(temp_cat) - policies = xmltree.xpath('/{0}policyDefinitions/{0}policies/{0}policy'.format(namespace_string), + policies = xml_tree.xpath('/{0}policyDefinitions/{0}policies/{0}policy'.format(namespace_string), namespaces=namespaces) for policy in policies: temp_pol = policy temp_pol = _updateNamespace(temp_pol, this_namespace) if 'key' in temp_pol.attrib: temp_pol = _updatePolicyElements(temp_pol, temp_pol.attrib['key']) - adm_policy_key_map[temp_pol.attrib['key'].lower()] = temp_pol.attrib['key'] + policydefs_policies_xpath(t_policy_definitions)[0].append(temp_pol) - policy_namespaces = xmltree.xpath( + policy_namespaces = xml_tree.xpath( '/{0}policyDefinitions/{0}policyNamespaces/{0}*'.format(namespace_string), namespaces=namespaces) for policy_ns in policy_namespaces: @@ -4882,65 +5031,58 @@ def _load_policy_definitions(path='c:\\Windows\\PolicyDefinitions', adml_file = os.path.join( root, language, - os.path.splitext(t_admfile)[0] + '.adml') + os.path.splitext(t_admx_file)[0] + '.adml') if not __salt__['file.file_exists'](adml_file): log.info('An ADML file in the specified ADML language ' '"%s" does not exist for the ADMX "%s", the ' 'the abbreviated language code will be tried.', - language, t_admfile) + language, t_admx_file) adml_file = os.path.join( root, language.split('-')[0], - os.path.splitext(t_admfile)[0] + '.adml') + os.path.splitext(t_admx_file)[0] + '.adml') if not __salt__['file.file_exists'](adml_file): log.info('An ADML file in the specified ADML language ' 'code %s does not exist for the ADMX "%s", ' 'the fallback language will be tried.', - language[:2], t_admfile) + language[:2], t_admx_file) adml_file = os.path.join( root, display_language_fallback, - os.path.splitext(t_admfile)[0] + '.adml') + os.path.splitext(t_admx_file)[0] + '.adml') if not __salt__['file.file_exists'](adml_file): log.info('An ADML file in the specified ADML ' 'fallback language "%s" ' 'does not exist for the ADMX "%s" ' 'the abbreviated fallback language code ' 'will be tried.', - display_language_fallback, t_admfile) + display_language_fallback, t_admx_file) adml_file = os.path.join( root, display_language_fallback.split('-')[0], - os.path.splitext(t_admfile)[0] + '.adml') + os.path.splitext(t_admx_file)[0] + '.adml') if not __salt__['file.file_exists'](adml_file): msg = ('An ADML file in the specified ADML language ' '"{0}" and the fallback language "{1}" do not ' 'exist for the ADMX "{2}".') raise SaltInvocationError(msg.format(language, display_language_fallback, - t_admfile)) + t_admx_file)) + # Parse xml for the ADML file try: - xmltree = lxml.etree.parse(adml_file) + xml_tree = _parse_xml(adml_file) except lxml.etree.XMLSyntaxError: - # see issue #38100 - try: - xmltree = _remove_unicode_encoding(adml_file) - # TODO: This needs to be more specific - except Exception: # pylint: disable=broad-except - log.exception('Handle this explicitly') - log.error('An error was found while processing ' - 'adml file %s, all policy ' - 'language data from this file will be ' - 'unavailable via this module', - adml_file) - continue + log.error('An error was found while processing adml ' + 'file %s, all policies from this file will ' + 'be unavailable via this module', adml_file) + continue if None in namespaces: namespaces['None'] = namespaces[None] namespaces.pop(None) - policydefs_resources = policydefs_resources_localname_xpath(xmltree) + policydefs_resources = policydefs_resources_localname_xpath(xml_tree) for policydefs_resource in policydefs_resources: t_poldef = policydefs_resource t_poldef = _updateNamespace(t_poldef, this_namespace) @@ -5157,13 +5299,13 @@ def _set_audit_file_data(option, value): # The value is not None, make the change row['Inclusion Setting'] = auditpol_values[value] row['Setting Value'] = value - log.debug('LGPO: Setting {0} to {1}' + log.trace('LGPO: Setting {0} to {1}' ''.format(option, value)) writer.writerow(row) else: # value is None, remove it by not writing it to the # temp file - log.debug('LGPO: Removing {0}'.format(option)) + log.trace('LGPO: Removing {0}'.format(option)) value_written = True # If it's not the value we're setting, just write it else: @@ -5175,7 +5317,7 @@ def _set_audit_file_data(option, value): if not value_written: if not value == 'None': # value is not None, write the new value - log.debug('LGPO: Setting {0} to {1}' + log.trace('LGPO: Setting {0} to {1}' ''.format(option, value)) defaults = _get_advaudit_defaults(option) writer.writerow({ @@ -5252,7 +5394,8 @@ def _set_advaudit_value(option, value): if not _set_advaudit_pol_data(option=option, value=value): # Only log this error, it will be in effect the next time the machine # updates its policy - log.debug('Failed to apply audit setting: {0}'.format(option)) + log.error('Failed to apply audit setting: {0}\n' + 'Policy will take effect on next GPO update'.format(option)) # Update __context__ if value is None: @@ -5275,7 +5418,7 @@ def _get_netsh_value(profile, option): settings = salt.utils.win_lgpo_netsh.get_all_settings(profile=profile, store='lgpo') __context__['lgpo.netsh_data'].update({profile: settings}) - log.debug('LGPO: netsh returning value: {0}' + log.trace('LGPO: netsh returning value: {0}' ''.format(__context__['lgpo.netsh_data'][profile][option])) return __context__['lgpo.netsh_data'][profile][option] @@ -5283,7 +5426,7 @@ def _get_netsh_value(profile, option): def _set_netsh_value(profile, section, option, value): if section not in ('firewallpolicy', 'settings', 'logging', 'state'): raise ValueError('LGPO: Invalid section: {0}'.format(section)) - log.debug('LGPO: Setting the following\n' + log.trace('LGPO: Setting the following\n' 'Profile: {0}\n' 'Section: {1}\n' 'Option: {2}\n' @@ -5300,20 +5443,17 @@ def _set_netsh_value(profile, section, option, value): if section == 'state': salt.utils.win_lgpo_netsh.set_state( profile=profile, state=value, store='lgpo') - option = 'State' if section == 'logging': if option in ('FileName', 'MaxFileSize'): if value == 'Not configured': value = 'notconfigured' # Trim log for the two logging options - orig_option = option if option.startswith('Log'): option = option[3:] salt.utils.win_lgpo_netsh.set_logging_settings( profile=profile, setting=option, value=value, store='lgpo') - option = orig_option - log.debug('LGPO: Setting {0} for {1} profile'.format(option, profile)) - __context__['lgpo.netsh_data'][profile][option] = value + log.trace('LGPO: Clearing netsh data for {0} profile'.format(profile)) + __context__['lgpo.netsh_data'].pop(profile) return True @@ -5347,13 +5487,7 @@ def _get_secedit_data(): ''' if 'lgpo.secedit_data' not in __context__: log.debug('LGPO: Loading secedit data') - data = _load_secedit_data() - secedit_data = {} - for line in data: - if '=' in line: - key, value = line.split('=') - secedit_data[key.strip()] = value.strip() - __context__['lgpo.secedit_data'] = secedit_data + __context__['lgpo.secedit_data'] = _load_secedit_data() return __context__['lgpo.secedit_data'] @@ -5361,10 +5495,14 @@ def _get_secedit_value(option): ''' Helper function that looks for the passed option in the secedit data ''' - return _get_secedit_data().get(option, 'Not Defined') + secedit_data = _get_secedit_data() + for _line in secedit_data: + if _line.startswith(option): + return _line.split('=')[1].strip() + return 'Not Defined' -def _write_secedit_data(secedit_data): +def _write_secedit_data(inf_data): ''' Helper function to write secedit data to the database ''' @@ -5372,30 +5510,6 @@ def _write_secedit_data(secedit_data): f_sdb = os.path.join(__opts__['cachedir'], 'secedit-{0}.sdb'.format(UUID)) f_inf = os.path.join(__opts__['cachedir'], 'secedit-{0}.inf'.format(UUID)) - # Generate inf data in this format: - # [Unicode] - # Unicode = yes - # [System Access] <==== Section - # EnableGuestAccount = 0 <==== value to set - # [Version] - # signature = "$CHICAGO$" - # Revision = 1 - - log.debug(secedit_data) - ini_data = ['[Unicode]', 'Unicode=yes'] - sections = ['System Access', - 'Event Audit', - 'Registry Values', - 'Privilege Rights'] - for section in sections: - if section in secedit_data: - ini_data.append('[{0}]'.format(section)) - ini_data.append(secedit_data[section][0]) - option, value = secedit_data[section][0].split('=') - ini_data.extend(['[Version]', 'signature="$CHICAGO$"', 'Revision=1']) - inf_data = os.linesep.join(ini_data) - log.debug('inf_data == %s', inf_data) - try: # Write the changes to the inf file __salt__['file.write'](f_inf, inf_data) @@ -5404,8 +5518,8 @@ def _write_secedit_data(secedit_data): retcode = __salt__['cmd.retcode'](cmd) # Success if retcode == 0: - # Update __context__['lgpo.secedit_data'] - __context__['lgpo.secedit_data'][option.strip()] = value.strip() + # Pop secedit data so it will always be current + __context__.pop('lgpo.secedit_data') return True # Failure return False @@ -5641,7 +5755,11 @@ def _getDataFromRegPolData(search_string, policy_data, return_value_name=False): if len(pol_entry) >= 2: valueName = pol_entry[1].decode('utf-16-le').rstrip(chr(0)) if len(pol_entry) >= 5: - value = pol_entry[4] + # Sometimes a semicolon-separated value gets split into + # additional elements in the Registry.pol file. For example, + # a value of test1;test2;test3 will be 'test1', 'test2', and + # 'test3' at the end of the Registry.pol file entry + value = encoded_semicolon.join(pol_entry[4:]) if vtype == 'REG_DWORD' or vtype == 'REG_QWORD': if value: if vtype == 'REG_DWORD': @@ -5657,10 +5775,10 @@ def _getDataFromRegPolData(search_string, policy_data, return_value_name=False): else: value = value.decode('utf-16-le').rstrip(chr(0)) if return_value_name: - log.debug('we want value names and the value') + log.trace('we want value names and the value') values[valueName] = value elif len(matches) > 1: - log.debug('we have multiple matches, we will return a list') + log.trace('we have multiple matches, we will return a list') values.append(value) if values: value = values @@ -5712,7 +5830,7 @@ def _checkListItem(policy_element, policy_name, policy_key, xpath_object, policy if test_items: if _regexSearchRegPolData(re.escape(search_string), policy_file_data): configured_items = configured_items + 1 - log.debug('found the search string in the pol file,' + log.trace('found the search string in the pol file,' '%s of %s items for policy %s are ' 'configured in registry.pol', configured_items, required_items, @@ -5721,7 +5839,7 @@ def _checkListItem(policy_element, policy_name, policy_key, xpath_object, policy expected_strings.append(search_string) if test_items: if required_items > 0 and required_items == configured_items: - log.debug('%s all items are set', policy_name) + log.trace('%s all items are set', policy_name) return True if test_items: return False @@ -5756,7 +5874,7 @@ def _checkValueItemParent(policy_element, policy_name, policy_key, if not test_item: return search_string if _regexSearchRegPolData(re.escape(search_string), policy_file_data): - log.debug('found the search string in the pol file, ' + log.trace('found the search string in the pol file, ' '%s is configured', policy_name) return True return False @@ -5766,12 +5884,11 @@ def _encode_string(value): encoded_null = chr(0).encode('utf-16-le') if value is None: return encoded_null - else: + elif not isinstance(value, six.string_types): # Should we raise an error here, or attempt to cast to a string - if not isinstance(value, six.string_types): - raise TypeError('Value {0} is not a string type\n' - 'Type: {1}'.format(repr(value), type(value))) - return b''.join([value.encode('utf-16-le'), encoded_null]) + raise TypeError('Value {0} is not a string type\n' + 'Type: {1}'.format(repr(value), type(value))) + return b''.join([value.encode('utf-16-le'), encoded_null]) def _buildKnownDataSearchString(reg_key, reg_valueName, reg_vtype, reg_data, @@ -5933,7 +6050,8 @@ def _processValueItem(element, reg_key, reg_valuename, policy, parent_element, if 'expandable' in element.attrib: if element.attrib['expandable'].lower() == 'true': this_vtype = 'REG_EXPAND_SZ' - this_element_value = _encode_string(this_element_value) + if this_element_value is not None: + this_element_value = _encode_string(this_element_value) elif etree.QName(element).localname == 'multiText': this_vtype = 'REG_MULTI_SZ' if not check_deleted else 'REG_SZ' if this_element_value is not None: @@ -5983,10 +6101,10 @@ def _processValueItem(element, reg_key, reg_valuename, policy, parent_element, element_valuenames = [str(z) for z in element_values] if not check_deleted: if this_element_value is not None: - log.debug('_processValueItem has an explicit ' + log.trace('_processValueItem has an explicit ' 'element_value of %s', this_element_value) expected_string = del_keys - log.debug('element_valuenames == %s and element_values ' + log.trace('element_valuenames == %s and element_values ' '== %s', element_valuenames, element_values) for i, item in enumerate(element_valuenames): expected_string = expected_string + b''.join(['['.encode('utf-16-le'), @@ -6099,7 +6217,7 @@ def _checkAllAdmxPolicies(policy_class, policy and look in the registry.pol file to determine if it is enabled/disabled/not configured ''' - log.debug('POLICY CLASS == %s', policy_class) + log.trace('POLICY CLASS == %s', policy_class) module_policy_data = _policy_info() policy_file_data = _read_regpol_file(module_policy_data.admx_registry_classes[policy_class]['policy_path']) admx_policies = [] @@ -6109,7 +6227,7 @@ def _checkAllAdmxPolicies(policy_class, admx_policy_definitions = _get_policy_definitions(language=adml_language) adml_policy_resources = _get_policy_resources(language=adml_language) if policy_file_data: - log.debug('POLICY CLASS {0} has file data'.format(policy_class)) + log.trace('POLICY CLASS {0} has file data'.format(policy_class)) policy_filedata_split = re.sub( salt.utils.stringutils.to_bytes(r'\]{0}$'.format(chr(0))), b'', @@ -6118,26 +6236,16 @@ def _checkAllAdmxPolicies(policy_class, re.sub(re.escape(module_policy_data.reg_pol_header.encode('utf-16-le')), b'', policy_file_data))).split(']['.encode('utf-16-le')) - log.debug('Parsing %s policies...', len(policy_filedata_split)) + log.trace('Searching %s policies...', len(policy_filedata_split)) start_time = time.time() # Get the policy for each item defined in Registry.pol for policy_item in policy_filedata_split: policy_item_key = policy_item.split('{0};'.format(chr(0)).encode('utf-16-le'))[0].decode('utf-16-le').lower() if policy_item_key: # Find the policy definitions with this key - if policy_item_key in adm_policy_key_map: - # Use the faster method if possible - admx_items = REGKEY_XPATH_MAPPED(admx_policy_definitions, - keyvalue=adm_policy_key_map[policy_item_key]) - log.debug('Found %s policies using the mapped method', - len(admx_items)) - else: - # Fall back to this when the faster method is not feasible - admx_items = REGKEY_XPATH(admx_policy_definitions, - keyvalue=policy_item_key) - log.warning('%s not mapped', policy_item_key) - log.warning('Found %s policies using the original method', - len(admx_items)) + admx_items = REGKEY_XPATH(admx_policy_definitions, + keyvalue=policy_item_key) + log.trace('Found %s policies for %s', len(admx_items), policy_item_key) for admx_item in admx_items: # If this is a policy, append it to admx_policies if etree.QName(admx_item).localname == 'policy': @@ -6148,10 +6256,10 @@ def _checkAllAdmxPolicies(policy_class, for policy_item in POLICY_ANCESTOR_XPATH(admx_item): if policy_item not in admx_policies: admx_policies.append(policy_item) - log.debug('Parsing complete: %s seconds', time.time() - start_time) + log.trace('Search complete: %s seconds', time.time() - start_time) if return_not_configured: - log.debug('Gathering non configured policies') + log.trace('Gathering non configured policies') start_time = time.time() not_configured_policies = ALL_CLASS_POLICY_XPATH(admx_policy_definitions, registry_class=policy_class) for policy_item in admx_policies: @@ -6171,7 +6279,7 @@ def _checkAllAdmxPolicies(policy_class, policy_name=not_configured_policy.attrib['name'], return_full_policy_names=return_full_policy_names, adml_language=adml_language) - log.debug('building hierarchy for non-configured item %s', + log.trace('building hierarchy for non-configured item %s', not_configured_policy.attrib['name']) if not_configured_policy_namespace not in hierarchy: hierarchy[not_configured_policy_namespace] = {} @@ -6179,9 +6287,9 @@ def _checkAllAdmxPolicies(policy_class, policy_definition=not_configured_policy, return_full_policy_names=return_full_policy_names, adml_language=adml_language) - log.debug('Gathering complete: %s seconds', time.time() - start_time) + log.trace('Gathering complete: %s seconds', time.time() - start_time) - log.debug('Examining %s policies...', len(admx_policies)) + log.trace('Examining %s policies...', len(admx_policies)) start_time = time.time() for admx_policy in admx_policies: this_valuename = None @@ -6217,7 +6325,7 @@ def _checkAllAdmxPolicies(policy_class, ENABLED_VALUE_XPATH, policy_file_data): this_policy_setting = 'Enabled' - log.debug('%s is enabled by detected ENABLED_VALUE_XPATH', this_policyname) + log.trace('%s is enabled by detected ENABLED_VALUE_XPATH', this_policyname) if this_policynamespace not in policy_vals: policy_vals[this_policynamespace] = {} policy_vals[this_policynamespace][this_policyname] = this_policy_setting @@ -6234,7 +6342,7 @@ def _checkAllAdmxPolicies(policy_class, DISABLED_VALUE_XPATH, policy_file_data): this_policy_setting = 'Disabled' - log.debug('%s is disabled by detected DISABLED_VALUE_XPATH', this_policyname) + log.trace('%s is disabled by detected DISABLED_VALUE_XPATH', this_policyname) if this_policynamespace not in policy_vals: policy_vals[this_policynamespace] = {} policy_vals[this_policynamespace][this_policyname] = this_policy_setting @@ -6244,7 +6352,7 @@ def _checkAllAdmxPolicies(policy_class, explicit_enable_disable_value_setting = True if _checkListItem(admx_policy, this_policyname, this_key, ENABLED_LIST_XPATH, policy_file_data): this_policy_setting = 'Enabled' - log.debug('%s is enabled by detected ENABLED_LIST_XPATH', this_policyname) + log.trace('%s is enabled by detected ENABLED_LIST_XPATH', this_policyname) if this_policynamespace not in policy_vals: policy_vals[this_policynamespace] = {} policy_vals[this_policynamespace][this_policyname] = this_policy_setting @@ -6254,7 +6362,7 @@ def _checkAllAdmxPolicies(policy_class, explicit_enable_disable_value_setting = True if _checkListItem(admx_policy, this_policyname, this_key, DISABLED_LIST_XPATH, policy_file_data): this_policy_setting = 'Disabled' - log.debug('%s is disabled by detected DISABLED_LIST_XPATH', this_policyname) + log.trace('%s is disabled by detected DISABLED_LIST_XPATH', this_policyname) if this_policynamespace not in policy_vals: policy_vals[this_policynamespace] = {} policy_vals[this_policynamespace][this_policyname] = this_policy_setting @@ -6269,7 +6377,7 @@ def _checkAllAdmxPolicies(policy_class, '1')), policy_file_data): this_policy_setting = 'Enabled' - log.debug('%s is enabled by no explicit enable/disable list or value', this_policyname) + log.trace('%s is enabled by no explicit enable/disable list or value', this_policyname) if this_policynamespace not in policy_vals: policy_vals[this_policynamespace] = {} policy_vals[this_policynamespace][this_policyname] = this_policy_setting @@ -6280,7 +6388,7 @@ def _checkAllAdmxPolicies(policy_class, check_deleted=True)), policy_file_data): this_policy_setting = 'Disabled' - log.debug('%s is disabled by no explicit enable/disable list or value', this_policyname) + log.trace('%s is disabled by no explicit enable/disable list or value', this_policyname) if this_policynamespace not in policy_vals: policy_vals[this_policynamespace] = {} policy_vals[this_policynamespace][this_policyname] = this_policy_setting @@ -6299,12 +6407,9 @@ def _checkAllAdmxPolicies(policy_class, return_full_policy_names=return_full_policy_names, adml_language=adml_language) required_elements[this_element_name] = None - child_key = this_key - child_valuename = this_valuename - if 'key' in child_item.attrib: - child_key = child_item.attrib['key'] - if 'valueName' in child_item.attrib: - child_valuename = child_item.attrib['valueName'] + child_key = child_item.attrib.get('key', this_key) + child_valuename = child_item.attrib.get( + 'valueName', this_valuename) if etree.QName(child_item).localname == 'boolean': # https://msdn.microsoft.com/en-us/library/dn605978(v=vs.85).aspx @@ -6317,7 +6422,7 @@ def _checkAllAdmxPolicies(policy_class, TRUE_VALUE_XPATH, policy_file_data): configured_elements[this_element_name] = True - log.debug('element %s is configured true', + log.trace('element %s is configured true', child_item.attrib['id']) if FALSE_VALUE_XPATH(child_item) and this_element_name not in configured_elements: if _checkValueItemParent(child_item, @@ -6328,22 +6433,22 @@ def _checkAllAdmxPolicies(policy_class, policy_file_data): configured_elements[this_element_name] = False policy_disabled_elements = policy_disabled_elements + 1 - log.debug('element %s is configured false', + log.trace('element %s is configured false', child_item.attrib['id']) # WARNING - no standard ADMX files use true/falseList # so this hasn't actually been tested if TRUE_LIST_XPATH(child_item) and this_element_name not in configured_elements: - log.debug('checking trueList') + log.trace('checking trueList') if _checkListItem(child_item, this_policyname, this_key, TRUE_LIST_XPATH, policy_file_data): configured_elements[this_element_name] = True - log.debug('element %s is configured true', + log.trace('element %s is configured true', child_item.attrib['id']) if FALSE_LIST_XPATH(child_item) and this_element_name not in configured_elements: - log.debug('checking falseList') + log.trace('checking falseList') if _checkListItem(child_item, this_policyname, this_key, @@ -6351,7 +6456,7 @@ def _checkAllAdmxPolicies(policy_class, policy_file_data): configured_elements[this_element_name] = False policy_disabled_elements = policy_disabled_elements + 1 - log.debug('element %s is configured false', + log.trace('element %s is configured false', child_item.attrib['id']) else: if _regexSearchRegPolData(re.escape(_processValueItem(child_item, @@ -6363,7 +6468,7 @@ def _checkAllAdmxPolicies(policy_class, policy_file_data): configured_elements[this_element_name] = False policy_disabled_elements = policy_disabled_elements + 1 - log.debug('element %s is configured false', child_item.attrib['id']) + log.trace('element %s is configured false', child_item.attrib['id']) elif _regexSearchRegPolData(re.escape(_processValueItem(child_item, child_key, child_valuename, @@ -6372,7 +6477,7 @@ def _checkAllAdmxPolicies(policy_class, check_deleted=False)), policy_file_data): configured_elements[this_element_name] = True - log.debug('element %s is configured true', + log.trace('element %s is configured true', child_item.attrib['id']) elif etree.QName(child_item).localname == 'decimal' \ or etree.QName(child_item).localname == 'text' \ @@ -6388,7 +6493,7 @@ def _checkAllAdmxPolicies(policy_class, policy_file_data): configured_elements[this_element_name] = 'Disabled' policy_disabled_elements = policy_disabled_elements + 1 - log.debug('element %s is disabled', + log.trace('element %s is disabled', child_item.attrib['id']) elif _regexSearchRegPolData(re.escape(_processValueItem(child_item, child_key, @@ -6405,7 +6510,7 @@ def _checkAllAdmxPolicies(policy_class, check_deleted=False), policy_file_data) configured_elements[this_element_name] = configured_value - log.debug('element %s is enabled, value == %s', + log.trace('element %s is enabled, value == %s', child_item.attrib['id'], configured_value) elif etree.QName(child_item).localname == 'enum': @@ -6416,7 +6521,7 @@ def _checkAllAdmxPolicies(policy_class, elements_item, check_deleted=True)), policy_file_data): - log.debug('enum element %s is disabled', + log.trace('enum element %s is disabled', child_item.attrib['id']) configured_elements[this_element_name] = 'Disabled' policy_disabled_elements = policy_disabled_elements + 1 @@ -6429,13 +6534,13 @@ def _checkAllAdmxPolicies(policy_class, VALUE_XPATH, policy_file_data): if VALUE_LIST_XPATH(enum_item): - log.debug('enum item has a valueList') + log.trace('enum item has a valueList') if _checkListItem(enum_item, this_policyname, child_key, VALUE_LIST_XPATH, policy_file_data): - log.debug('all valueList items exist in file') + log.trace('all valueList items exist in file') configured_elements[this_element_name] = _getAdmlDisplayName( adml_policy_resources, enum_item.attrib['displayName']) @@ -6449,7 +6554,7 @@ def _checkAllAdmxPolicies(policy_class, return_value_name = False if 'explicitValue' in child_item.attrib \ and child_item.attrib['explicitValue'].lower() == 'true': - log.debug('explicitValue list, we will return value names') + log.trace('explicitValue list, we will return value names') return_value_name = True if _regexSearchRegPolData(re.escape(_processValueItem(child_item, child_key, @@ -6468,7 +6573,7 @@ def _checkAllAdmxPolicies(policy_class, policy_file_data, return_value_name=return_value_name) configured_elements[this_element_name] = configured_value - log.debug('element %s is enabled values: %s', + log.trace('element %s is enabled values: %s', child_item.attrib['id'], configured_value) elif _regexSearchRegPolData(re.escape(_processValueItem(child_item, @@ -6480,12 +6585,12 @@ def _checkAllAdmxPolicies(policy_class, policy_file_data): configured_elements[this_element_name] = "Disabled" policy_disabled_elements = policy_disabled_elements + 1 - log.debug('element {0} is disabled'.format(child_item.attrib['id'])) + log.trace('element {0} is disabled'.format(child_item.attrib['id'])) if element_only_enabled_disabled: if len(required_elements.keys()) > 0 \ and len(configured_elements.keys()) == len(required_elements.keys()): if policy_disabled_elements == len(required_elements.keys()): - log.debug('{0} is disabled by all enum elements'.format(this_policyname)) + log.trace('{0} is disabled by all enum elements'.format(this_policyname)) if this_policynamespace not in policy_vals: policy_vals[this_policynamespace] = {} policy_vals[this_policynamespace][this_policyname] = 'Disabled' @@ -6493,7 +6598,7 @@ def _checkAllAdmxPolicies(policy_class, if this_policynamespace not in policy_vals: policy_vals[this_policynamespace] = {} policy_vals[this_policynamespace][this_policyname] = configured_elements - log.debug('{0} is enabled by enum elements'.format(this_policyname)) + log.trace('{0} is enabled by enum elements'.format(this_policyname)) else: if this_policy_setting == 'Enabled': if this_policynamespace not in policy_vals: @@ -6513,9 +6618,9 @@ def _checkAllAdmxPolicies(policy_class, # When the state does it's first `lgpo.get` it would return `AllowTelemetry` # On the second run, it would return `Allow Telemetry` # This makes sure we're always returning the full_name when required - if admx_policy.attrib['name'] in policy_vals[this_policynamespace][this_policyname]: + if this_policyname in policy_vals[this_policynamespace][this_policyname]: full_name = full_names[this_policynamespace][this_policyname] - setting = policy_vals[this_policynamespace][this_policyname].pop(admx_policy.attrib['name']) + setting = policy_vals[this_policynamespace][this_policyname].pop(this_policyname) policy_vals[this_policynamespace][this_policyname][full_name] = setting if this_policynamespace in policy_vals and this_policyname in policy_vals[this_policynamespace]: if this_policynamespace not in hierarchy: @@ -6524,7 +6629,7 @@ def _checkAllAdmxPolicies(policy_class, policy_definition=admx_policy, return_full_policy_names=return_full_policy_names, adml_language=adml_language) - log.debug('Examination complete: %s seconds', time.time() - start_time) + log.trace('Examination complete: %s seconds', time.time() - start_time) if policy_vals and return_full_policy_names and not hierarchical_return: log.debug('Compiling non hierarchical return...') start_time = time.time() @@ -6550,9 +6655,9 @@ def _checkAllAdmxPolicies(policy_class, full_path_list = hierarchy[policy_namespace][unpathed_dict[policy_namespace][path_needed]] full_path_list.reverse() full_path_list.append(path_needed) - log.debug('full_path_list == %s', full_path_list) + log.trace('full_path_list == %s', full_path_list) policy_vals['\\'.join(full_path_list)] = policy_vals[policy_namespace].pop(path_needed) - log.debug('Compilation complete: %s seconds', time.time() - start_time) + log.trace('Compilation complete: %s seconds', time.time() - start_time) for policy_namespace in list(policy_vals): if policy_vals[policy_namespace] == {}: policy_vals.pop(policy_namespace) @@ -6580,7 +6685,7 @@ def _checkAllAdmxPolicies(policy_class, policy_vals = dictupdate.update(policy_vals, tdict) if policy_namespace in policy_vals and policy_vals[policy_namespace] == {}: policy_vals.pop(policy_namespace) - log.debug('Compilation complete: %s seconds', time.time() - start_time) + log.trace('Compilation complete: %s seconds', time.time() - start_time) policy_vals = { module_policy_data.admx_registry_classes[policy_class]['lgpo_section']: { 'Administrative Templates': policy_vals}} @@ -6713,90 +6818,96 @@ def _write_regpol_data(data_to_write, gpt_extension: gpt extension list name from _policy_info class for this registry class gpt_extension_location gpt_extension_guid: admx registry extension guid for the class ''' + # Write Registry.pol file + if not os.path.exists(policy_file_path): + __salt__['file.makedirs'](policy_file_path) try: - reg_pol_header = u'\u5250\u6765\x01\x00' - if not os.path.exists(policy_file_path): - __salt__['file.makedirs'](policy_file_path) with salt.utils.files.fopen(policy_file_path, 'wb') as pol_file: - if not data_to_write.startswith(reg_pol_header.encode('utf-16-le')): - pol_file.write(reg_pol_header.encode('utf-16-le')) + reg_pol_header = '\u5250\u6765\x01\x00'.encode('utf-16-le') + if not data_to_write.startswith(reg_pol_header): + pol_file.write(reg_pol_header) pol_file.write(data_to_write) + # TODO: This needs to be more specific + except Exception as e: # pylint: disable=broad-except + msg = 'An error occurred attempting to write to {0}, the exception ' \ + 'was: {1}'.format(policy_file_path, e) + log.exception(msg) + raise CommandExecutionError(msg) + + # Write the gpt.ini file + gpt_ini_data = '' + if os.path.exists(gpt_ini_path): + with salt.utils.files.fopen(gpt_ini_path, 'r') as gpt_file: + gpt_ini_data = gpt_file.read() + if not _regexSearchRegPolData(r'\[General\]\r\n', gpt_ini_data): + gpt_ini_data = '[General]\r\n' + gpt_ini_data + if _regexSearchRegPolData( + r'{0}='.format(re.escape(gpt_extension)), gpt_ini_data): + # ensure the line contains the ADM guid + gpt_ext_loc = re.search( + r'^{0}=.*\r\n'.format(re.escape(gpt_extension)), + gpt_ini_data, + re.IGNORECASE | re.MULTILINE) + gpt_ext_str = gpt_ini_data[gpt_ext_loc.start():gpt_ext_loc.end()] + if not _regexSearchRegPolData( + r'{0}'.format(re.escape(gpt_extension_guid)), gpt_ext_str): + gpt_ext_str = gpt_ext_str.split('=') + gpt_ext_str[1] = gpt_extension_guid + gpt_ext_str[1] + gpt_ext_str = '='.join(gpt_ext_str) + gpt_ini_data = gpt_ini_data[0:gpt_ext_loc.start()] + \ + gpt_ext_str + gpt_ini_data[gpt_ext_loc.end():] + else: + general_location = re.search( + r'^\[General\]\r\n', gpt_ini_data, re.IGNORECASE | re.MULTILINE) + gpt_ini_data = '{0}{1}={2}\r\n{3}'.format( + gpt_ini_data[general_location.start():general_location.end()], + gpt_extension, + gpt_extension_guid, + gpt_ini_data[general_location.end():]) + # https://technet.microsoft.com/en-us/library/cc978247.aspx + if _regexSearchRegPolData(r'Version=', gpt_ini_data): + version_loc = re.search( + r'^Version=.*\r\n', gpt_ini_data, re.IGNORECASE | re.MULTILINE) + version_str = gpt_ini_data[version_loc.start():version_loc.end()] + version_str = version_str.split('=') + version_nums = struct.unpack( + b'>2H', struct.pack(b'>I', int(version_str[1]))) + if gpt_extension.lower() == 'gPCMachineExtensionNames'.lower(): + version_nums = (version_nums[0], version_nums[1] + 1) + elif gpt_extension.lower() == 'gPCUserExtensionNames'.lower(): + version_nums = (version_nums[0] + 1, version_nums[1]) + version_num = struct.unpack( + b'>I', struct.pack(b'>2H', *version_nums))[0] + gpt_ini_data = '{0}{1}={2}\r\n{3}'.format( + gpt_ini_data[0:version_loc.start()], + 'Version', + version_num, + gpt_ini_data[version_loc.end():]) + else: + general_location = re.search( + r'^\[General\]\r\n', gpt_ini_data, re.IGNORECASE | re.MULTILINE) + if gpt_extension.lower() == 'gPCMachineExtensionNames'.lower(): + version_nums = (0, 1) + elif gpt_extension.lower() == 'gPCUserExtensionNames'.lower(): + version_nums = (1, 0) + gpt_ini_data = '{0}{1}={2}\r\n{3}'.format( + gpt_ini_data[general_location.start():general_location.end()], + 'Version', + int("{0}{1}".format(six.text_type(version_nums[0]).zfill(4), + six.text_type(version_nums[1]).zfill(4)), + 16), + gpt_ini_data[general_location.end():]) + if gpt_ini_data: try: - gpt_ini_data = '' - if os.path.exists(gpt_ini_path): - with salt.utils.files.fopen(gpt_ini_path, 'r') as gpt_file: - gpt_ini_data = gpt_file.read() - if not _regexSearchRegPolData(r'\[General\]\r\n', gpt_ini_data): - gpt_ini_data = '[General]\r\n' + gpt_ini_data - if _regexSearchRegPolData(r'{0}='.format(re.escape(gpt_extension)), - gpt_ini_data): - # ensure the line contains the ADM guid - gpt_ext_loc = re.search(r'^{0}=.*\r\n'.format(re.escape(gpt_extension)), - gpt_ini_data, - re.IGNORECASE | re.MULTILINE) - gpt_ext_str = gpt_ini_data[gpt_ext_loc.start():gpt_ext_loc.end()] - if not _regexSearchRegPolData(r'{0}'.format(re.escape(gpt_extension_guid)), - gpt_ext_str): - gpt_ext_str = gpt_ext_str.split('=') - gpt_ext_str[1] = gpt_extension_guid + gpt_ext_str[1] - gpt_ext_str = '='.join(gpt_ext_str) - gpt_ini_data = gpt_ini_data[0:gpt_ext_loc.start()] + gpt_ext_str + gpt_ini_data[gpt_ext_loc.end():] - else: - general_location = re.search(r'^\[General\]\r\n', - gpt_ini_data, - re.IGNORECASE | re.MULTILINE) - gpt_ini_data = '{0}{1}={2}\r\n{3}'.format( - gpt_ini_data[general_location.start():general_location.end()], - gpt_extension, - gpt_extension_guid, - gpt_ini_data[general_location.end():]) - # https://technet.microsoft.com/en-us/library/cc978247.aspx - if _regexSearchRegPolData(r'Version=', gpt_ini_data): - version_loc = re.search(r'^Version=.*\r\n', - gpt_ini_data, - re.IGNORECASE | re.MULTILINE) - version_str = gpt_ini_data[version_loc.start():version_loc.end()] - version_str = version_str.split('=') - version_nums = struct.unpack(b'>2H', struct.pack(b'>I', int(version_str[1]))) - if gpt_extension.lower() == 'gPCMachineExtensionNames'.lower(): - version_nums = (version_nums[0], version_nums[1] + 1) - elif gpt_extension.lower() == 'gPCUserExtensionNames'.lower(): - version_nums = (version_nums[0] + 1, version_nums[1]) - version_num = struct.unpack(b'>I', struct.pack(b'>2H', *version_nums))[0] - gpt_ini_data = '{0}{1}={2}\r\n{3}'.format( - gpt_ini_data[0:version_loc.start()], - 'Version', - version_num, - gpt_ini_data[version_loc.end():]) - else: - general_location = re.search(r'^\[General\]\r\n', - gpt_ini_data, - re.IGNORECASE | re.MULTILINE) - if gpt_extension.lower() == 'gPCMachineExtensionNames'.lower(): - version_nums = (0, 1) - elif gpt_extension.lower() == 'gPCUserExtensionNames'.lower(): - version_nums = (1, 0) - gpt_ini_data = '{0}{1}={2}\r\n{3}'.format( - gpt_ini_data[general_location.start():general_location.end()], - 'Version', - int("{0}{1}".format(six.text_type(version_nums[0]).zfill(4), - six.text_type(version_nums[1]).zfill(4)), - 16), - gpt_ini_data[general_location.end():]) - if gpt_ini_data: - with salt.utils.files.fopen(gpt_ini_path, 'w') as gpt_file: - gpt_file.write(gpt_ini_data) + with salt.utils.files.fopen(gpt_ini_path, 'w') as gpt_file: + gpt_file.write(gpt_ini_data) # TODO: This needs to be more specific except Exception as e: # pylint: disable=broad-except - msg = 'An error occurred attempting to write to {0}, the exception was {1}'.format( - gpt_ini_path, e) + msg = 'An error occurred attempting to write the gpg.ini file.\n' \ + 'path: {0}\n' \ + 'exception: {1}'.format(gpt_ini_path, e) log.exception(msg) raise CommandExecutionError(msg) - # TODO: This needs to be more specific - except Exception as e: # pylint: disable=broad-except - msg = 'An error occurred attempting to write to {0}, the exception was {1}'.format(policy_file_path, e) - log.exception(msg) - raise CommandExecutionError(msg) def _policyFileReplaceOrAppendList(string_list, policy_data): @@ -6814,15 +6925,15 @@ def _policyFileReplaceOrAppendList(string_list, policy_data): b'', this_string.split(b'\00;')[1], flags=re.IGNORECASE) - log.debug('item value name is %s', list_item_value_name) + log.trace('item value name is %s', list_item_value_name) data_to_replace = _regexSearchKeyValueCombo(policy_data, list_item_key, list_item_value_name) if data_to_replace: - log.debug('replacing %s with %s', data_to_replace, this_string) + log.trace('replacing %s with %s', data_to_replace, this_string) policy_data = policy_data.replace(data_to_replace, this_string) else: - log.debug('appending %s', this_string) + log.trace('appending %s', this_string) policy_data = b''.join([policy_data, this_string]) return policy_data @@ -6845,13 +6956,13 @@ def _policyFileReplaceOrAppend(this_string, policy_data, append_only=False): b'', this_string.split(b'\00;')[1], flags=re.IGNORECASE) - log.debug('item value name is %s', item_value_name) + log.trace('item value name is %s', item_value_name) data_to_replace = _regexSearchKeyValueCombo(policy_data, item_key, item_value_name) if data_to_replace: - log.debug('replacing %s with %s', data_to_replace, this_string) + log.trace('replacing %s with %s', data_to_replace, this_string) policy_data = policy_data.replace(data_to_replace, this_string) else: - log.debug('appending %s', this_string) + log.trace('appending %s', this_string) policy_data = b''.join([policy_data, this_string]) return policy_data @@ -6875,321 +6986,322 @@ def _writeAdminTemplateRegPolFile(admtemplate_data, policySearchXpath = '//ns1:*[@id = "{0}" or @name = "{0}"]' admx_policy_definitions = _get_policy_definitions(language=adml_language) adml_policy_resources = _get_policy_resources(language=adml_language) - try: - base_policy_settings = _checkAllAdmxPolicies( - policy_class=registry_class, - adml_language=adml_language, - return_full_policy_names=False, - hierarchical_return=False, - return_not_configured=False) - for adm_namespace in admtemplate_data: - for adm_policy in admtemplate_data[adm_namespace]: - if six.text_type(admtemplate_data[adm_namespace][adm_policy]).lower() == 'not configured': - if base_policy_settings.get(adm_namespace, {}).pop(adm_policy, None) is not None: - log.debug('Policy "%s" removed', adm_policy) - else: - log.debug('adding %s to base_policy_settings', adm_policy) - if adm_namespace not in base_policy_settings: - base_policy_settings[adm_namespace] = {} - base_policy_settings[adm_namespace][adm_policy] = admtemplate_data[adm_namespace][adm_policy] - for adm_namespace in base_policy_settings: - for admPolicy in base_policy_settings[adm_namespace]: - log.debug('working on admPolicy %s', admPolicy) - explicit_enable_disable_value_setting = False - this_key = None - this_valuename = None - if six.text_type(base_policy_settings[adm_namespace][admPolicy]).lower() == 'disabled': - log.debug('time to disable %s', admPolicy) - this_policy = admx_policy_definitions.xpath(policySearchXpath.format(admPolicy), namespaces={'ns1': adm_namespace}) - if this_policy: - this_policy = this_policy[0] - if 'class' in this_policy.attrib: - if this_policy.attrib['class'] == registry_class or this_policy.attrib['class'] == 'Both': - if 'key' in this_policy.attrib: - this_key = this_policy.attrib['key'] - else: - log.error('policy item %s does not have ' - 'the required "key" attribute', - this_policy.attrib) - break - if 'valueName' in this_policy.attrib: - this_valuename = this_policy.attrib['valueName'] - if DISABLED_VALUE_XPATH(this_policy): - # set the disabled value in the registry.pol file - explicit_enable_disable_value_setting = True - disabled_value_string = _checkValueItemParent(this_policy, + base_policy_settings = _checkAllAdmxPolicies( + policy_class=registry_class, + adml_language=adml_language, + return_full_policy_names=False, + hierarchical_return=False, + return_not_configured=False) + for adm_namespace in admtemplate_data: + for adm_policy in admtemplate_data[adm_namespace]: + if six.text_type(admtemplate_data[adm_namespace][adm_policy]).lower() == 'not configured': + if base_policy_settings.get(adm_namespace, {}).pop(adm_policy, None) is not None: + log.trace('Policy "%s" removed', adm_policy) + else: + log.trace('adding %s to base_policy_settings', adm_policy) + if adm_namespace not in base_policy_settings: + base_policy_settings[adm_namespace] = {} + base_policy_settings[adm_namespace][adm_policy] = admtemplate_data[adm_namespace][adm_policy] + for adm_namespace in base_policy_settings: + for admPolicy in base_policy_settings[adm_namespace]: + log.trace('working on admPolicy %s', admPolicy) + explicit_enable_disable_value_setting = False + this_key = None + this_valuename = None + if six.text_type(base_policy_settings[adm_namespace][admPolicy]).lower() == 'disabled': + log.trace('time to disable %s', admPolicy) + this_policy = admx_policy_definitions.xpath(policySearchXpath.format(admPolicy), namespaces={'ns1': adm_namespace}) + if this_policy: + this_policy = this_policy[0] + if 'class' in this_policy.attrib: + if this_policy.attrib['class'] == registry_class or this_policy.attrib['class'] == 'Both': + if 'key' in this_policy.attrib: + this_key = this_policy.attrib['key'] + else: + log.error('policy item %s does not have ' + 'the required "key" attribute', + this_policy.attrib) + break + if 'valueName' in this_policy.attrib: + this_valuename = this_policy.attrib['valueName'] + if DISABLED_VALUE_XPATH(this_policy): + # set the disabled value in the registry.pol file + explicit_enable_disable_value_setting = True + disabled_value_string = _checkValueItemParent(this_policy, + admPolicy, + this_key, + this_valuename, + DISABLED_VALUE_XPATH, + None, + check_deleted=False, + test_item=False) + existing_data = _policyFileReplaceOrAppend(disabled_value_string, + existing_data) + if DISABLED_LIST_XPATH(this_policy): + explicit_enable_disable_value_setting = True + disabled_list_strings = _checkListItem(this_policy, + admPolicy, + this_key, + DISABLED_LIST_XPATH, + None, + test_items=False) + log.trace('working with disabledList ' + 'portion of %s', admPolicy) + existing_data = _policyFileReplaceOrAppendList(disabled_list_strings, + existing_data) + if not explicit_enable_disable_value_setting and this_valuename: + disabled_value_string = _buildKnownDataSearchString(this_key, + this_valuename, + 'REG_DWORD', + None, + check_deleted=True) + existing_data = _policyFileReplaceOrAppend(disabled_value_string, + existing_data) + if ELEMENTS_XPATH(this_policy): + log.trace('checking elements of %s', + admPolicy) + for elements_item in ELEMENTS_XPATH(this_policy): + for child_item in elements_item.getchildren(): + child_key = this_key + child_valuename = this_valuename + if 'key' in child_item.attrib: + child_key = child_item.attrib['key'] + if 'valueName' in child_item.attrib: + child_valuename = child_item.attrib['valueName'] + if etree.QName(child_item).localname == 'boolean' \ + and (TRUE_LIST_XPATH(child_item) or FALSE_LIST_XPATH(child_item)): + # WARNING: no OOB adm files use true/falseList items + # this has not been fully vetted + temp_dict = {'trueList': TRUE_LIST_XPATH, 'falseList': FALSE_LIST_XPATH} + for this_list in temp_dict: + disabled_list_strings = _checkListItem( + child_item, + admPolicy, + child_key, + temp_dict[this_list], + None, + test_items=False) + log.trace('working with %s portion of %s', + admPolicy, + this_list) + existing_data = _policyFileReplaceOrAppendList( + disabled_list_strings, + existing_data) + elif etree.QName(child_item).localname == 'boolean' \ + or etree.QName(child_item).localname == 'decimal' \ + or etree.QName(child_item).localname == 'text' \ + or etree.QName(child_item).localname == 'longDecimal' \ + or etree.QName(child_item).localname == 'multiText' \ + or etree.QName(child_item).localname == 'enum': + disabled_value_string = _processValueItem(child_item, + child_key, + child_valuename, + this_policy, + elements_item, + check_deleted=True) + log.trace('I have disabled value string of %s', + disabled_value_string) + existing_data = _policyFileReplaceOrAppend( + disabled_value_string, + existing_data) + elif etree.QName(child_item).localname == 'list': + disabled_value_string = _processValueItem(child_item, + child_key, + child_valuename, + this_policy, + elements_item, + check_deleted=True) + log.trace('I have disabled value string of %s', + disabled_value_string) + existing_data = _policyFileReplaceOrAppend( + disabled_value_string, + existing_data) + else: + log.error('policy %s was found but it does not appear to be valid for the class %s', + admPolicy, registry_class) + else: + log.error('policy item %s does not have the requried "class" attribute', + this_policy.attrib) + else: + log.trace('time to enable and set the policy "%s"', + admPolicy) + this_policy = admx_policy_definitions.xpath(policySearchXpath.format(admPolicy), namespaces={'ns1': adm_namespace}) + log.trace('found this_policy == %s', this_policy) + if this_policy: + this_policy = this_policy[0] + if 'class' in this_policy.attrib: + if this_policy.attrib['class'] == registry_class or this_policy.attrib['class'] == 'Both': + if 'key' in this_policy.attrib: + this_key = this_policy.attrib['key'] + else: + log.error('policy item %s does not have the required "key" attribute', + this_policy.attrib) + break + if 'valueName' in this_policy.attrib: + this_valuename = this_policy.attrib['valueName'] + + if ENABLED_VALUE_XPATH(this_policy): + explicit_enable_disable_value_setting = True + enabled_value_string = _checkValueItemParent(this_policy, + admPolicy, + this_key, + this_valuename, + ENABLED_VALUE_XPATH, + None, + check_deleted=False, + test_item=False) + existing_data = _policyFileReplaceOrAppend( + enabled_value_string, + existing_data) + if ENABLED_LIST_XPATH(this_policy): + explicit_enable_disable_value_setting = True + enabled_list_strings = _checkListItem(this_policy, + admPolicy, + this_key, + ENABLED_LIST_XPATH, + None, + test_items=False) + log.trace('working with enabledList portion of %s', admPolicy) + existing_data = _policyFileReplaceOrAppendList( + enabled_list_strings, + existing_data) + if not explicit_enable_disable_value_setting and this_valuename: + enabled_value_string = _buildKnownDataSearchString(this_key, + this_valuename, + 'REG_DWORD', + '1', + check_deleted=False) + existing_data = _policyFileReplaceOrAppend( + enabled_value_string, + existing_data) + if ELEMENTS_XPATH(this_policy): + for elements_item in ELEMENTS_XPATH(this_policy): + for child_item in elements_item.getchildren(): + child_key = this_key + child_valuename = this_valuename + if 'key' in child_item.attrib: + child_key = child_item.attrib['key'] + if 'valueName' in child_item.attrib: + child_valuename = child_item.attrib['valueName'] + if child_item.attrib['id'] in base_policy_settings[adm_namespace][admPolicy]: + if etree.QName(child_item).localname == 'boolean' and ( + TRUE_LIST_XPATH(child_item) or FALSE_LIST_XPATH(child_item)): + list_strings = [] + if base_policy_settings[adm_namespace][admPolicy][child_item.attrib['id']]: + list_strings = _checkListItem(child_item, admPolicy, - this_key, - this_valuename, - DISABLED_VALUE_XPATH, + child_key, + TRUE_LIST_XPATH, None, - check_deleted=False, - test_item=False) - existing_data = _policyFileReplaceOrAppend(disabled_value_string, - existing_data) - if DISABLED_LIST_XPATH(this_policy): - explicit_enable_disable_value_setting = True - disabled_list_strings = _checkListItem(this_policy, - admPolicy, - this_key, - DISABLED_LIST_XPATH, - None, - test_items=False) - log.debug('working with disabledList ' - 'portion of %s', admPolicy) - existing_data = _policyFileReplaceOrAppendList(disabled_list_strings, - existing_data) - if not explicit_enable_disable_value_setting and this_valuename: - disabled_value_string = _buildKnownDataSearchString(this_key, - this_valuename, - 'REG_DWORD', - None, - check_deleted=True) - existing_data = _policyFileReplaceOrAppend(disabled_value_string, - existing_data) - if ELEMENTS_XPATH(this_policy): - log.debug('checking elements of %s', - admPolicy) - for elements_item in ELEMENTS_XPATH(this_policy): - for child_item in elements_item.getchildren(): - child_key = this_key - child_valuename = this_valuename - if 'key' in child_item.attrib: - child_key = child_item.attrib['key'] - if 'valueName' in child_item.attrib: - child_valuename = child_item.attrib['valueName'] - if etree.QName(child_item).localname == 'boolean' \ - and (TRUE_LIST_XPATH(child_item) or FALSE_LIST_XPATH(child_item)): - # WARNING: no OOB adm files use true/falseList items - # this has not been fully vetted - temp_dict = {'trueList': TRUE_LIST_XPATH, 'falseList': FALSE_LIST_XPATH} - for this_list in temp_dict: - disabled_list_strings = _checkListItem( - child_item, - admPolicy, - child_key, - temp_dict[this_list], - None, - test_items=False) - log.debug('working with %s portion of %s', - admPolicy, - this_list) - existing_data = _policyFileReplaceOrAppendList( - disabled_list_strings, - existing_data) + test_items=False) + log.trace('working with trueList portion of {0}'.format(admPolicy)) + else: + list_strings = _checkListItem(child_item, + admPolicy, + child_key, + FALSE_LIST_XPATH, + None, + test_items=False) + existing_data = _policyFileReplaceOrAppendList( + list_strings, + existing_data) + elif etree.QName(child_item).localname == 'boolean' and ( + TRUE_VALUE_XPATH(child_item) or FALSE_VALUE_XPATH(child_item)): + value_string = '' + if base_policy_settings[adm_namespace][admPolicy][child_item.attrib['id']]: + value_string = _checkValueItemParent(child_item, + admPolicy, + child_key, + child_valuename, + TRUE_VALUE_XPATH, + None, + check_deleted=False, + test_item=False) + else: + value_string = _checkValueItemParent(child_item, + admPolicy, + child_key, + child_valuename, + FALSE_VALUE_XPATH, + None, + check_deleted=False, + test_item=False) + existing_data = _policyFileReplaceOrAppend( + value_string, + existing_data) elif etree.QName(child_item).localname == 'boolean' \ or etree.QName(child_item).localname == 'decimal' \ or etree.QName(child_item).localname == 'text' \ or etree.QName(child_item).localname == 'longDecimal' \ - or etree.QName(child_item).localname == 'multiText' \ - or etree.QName(child_item).localname == 'enum': - disabled_value_string = _processValueItem(child_item, - child_key, - child_valuename, - this_policy, - elements_item, - check_deleted=True) - log.debug('I have disabled value string of %s', - disabled_value_string) + or etree.QName(child_item).localname == 'multiText': + enabled_value_string = _processValueItem( + child_item, + child_key, + child_valuename, + this_policy, + elements_item, + check_deleted=False, + this_element_value=base_policy_settings[adm_namespace][admPolicy][child_item.attrib['id']]) + log.trace('I have enabled value string of %s', enabled_value_string) existing_data = _policyFileReplaceOrAppend( - disabled_value_string, + enabled_value_string, existing_data) + elif etree.QName(child_item).localname == 'enum': + for enum_item in child_item.getchildren(): + if base_policy_settings[adm_namespace][admPolicy][child_item.attrib['id']] == \ + _getAdmlDisplayName(adml_policy_resources, + enum_item.attrib['displayName'] + ).strip(): + enabled_value_string = _checkValueItemParent( + enum_item, + child_item.attrib['id'], + child_key, + child_valuename, + VALUE_XPATH, + None, + check_deleted=False, + test_item=False) + existing_data = _policyFileReplaceOrAppend( + enabled_value_string, + existing_data) + if VALUE_LIST_XPATH(enum_item): + enabled_list_strings = _checkListItem(enum_item, + admPolicy, + child_key, + VALUE_LIST_XPATH, + None, + test_items=False) + log.trace('working with valueList portion of %s', + child_item.attrib['id']) + existing_data = _policyFileReplaceOrAppendList( + enabled_list_strings, + existing_data) + break elif etree.QName(child_item).localname == 'list': - disabled_value_string = _processValueItem(child_item, - child_key, - child_valuename, - this_policy, - elements_item, - check_deleted=True) - log.debug('I have disabled value string of %s', - disabled_value_string) + enabled_value_string = _processValueItem( + child_item, + child_key, + child_valuename, + this_policy, + elements_item, + check_deleted=False, + this_element_value=base_policy_settings[adm_namespace][admPolicy][child_item.attrib['id']]) + log.trace('I have enabled value string of %s', + enabled_value_string) existing_data = _policyFileReplaceOrAppend( - disabled_value_string, - existing_data) - else: - log.error('policy %s was found but it does not appear to be valid for the class %s', - admPolicy, registry_class) - else: - log.error('policy item %s does not have the requried "class" attribute', - this_policy.attrib) - else: - log.debug('time to enable and set the policy "%s"', - admPolicy) - this_policy = admx_policy_definitions.xpath(policySearchXpath.format(admPolicy), namespaces={'ns1': adm_namespace}) - log.debug('found this_policy == %s', this_policy) - if this_policy: - this_policy = this_policy[0] - if 'class' in this_policy.attrib: - if this_policy.attrib['class'] == registry_class or this_policy.attrib['class'] == 'Both': - if 'key' in this_policy.attrib: - this_key = this_policy.attrib['key'] - else: - log.error('policy item %s does not have the required "key" attribute', - this_policy.attrib) - break - if 'valueName' in this_policy.attrib: - this_valuename = this_policy.attrib['valueName'] - - if ENABLED_VALUE_XPATH(this_policy): - explicit_enable_disable_value_setting = True - enabled_value_string = _checkValueItemParent(this_policy, - admPolicy, - this_key, - this_valuename, - ENABLED_VALUE_XPATH, - None, - check_deleted=False, - test_item=False) - existing_data = _policyFileReplaceOrAppend( - enabled_value_string, - existing_data) - if ENABLED_LIST_XPATH(this_policy): - explicit_enable_disable_value_setting = True - enabled_list_strings = _checkListItem(this_policy, - admPolicy, - this_key, - ENABLED_LIST_XPATH, - None, - test_items=False) - log.debug('working with enabledList portion of %s', admPolicy) - existing_data = _policyFileReplaceOrAppendList( - enabled_list_strings, - existing_data) - if not explicit_enable_disable_value_setting and this_valuename: - enabled_value_string = _buildKnownDataSearchString(this_key, - this_valuename, - 'REG_DWORD', - '1', - check_deleted=False) - existing_data = _policyFileReplaceOrAppend( - enabled_value_string, - existing_data) - if ELEMENTS_XPATH(this_policy): - for elements_item in ELEMENTS_XPATH(this_policy): - for child_item in elements_item.getchildren(): - child_key = this_key - child_valuename = this_valuename - if 'key' in child_item.attrib: - child_key = child_item.attrib['key'] - if 'valueName' in child_item.attrib: - child_valuename = child_item.attrib['valueName'] - if child_item.attrib['id'] in base_policy_settings[adm_namespace][admPolicy]: - if etree.QName(child_item).localname == 'boolean' and ( - TRUE_LIST_XPATH(child_item) or FALSE_LIST_XPATH(child_item)): - list_strings = [] - if base_policy_settings[adm_namespace][admPolicy][child_item.attrib['id']]: - list_strings = _checkListItem(child_item, - admPolicy, - child_key, - TRUE_LIST_XPATH, - None, - test_items=False) - log.debug('working with trueList portion of {0}'.format(admPolicy)) - else: - list_strings = _checkListItem(child_item, - admPolicy, - child_key, - FALSE_LIST_XPATH, - None, - test_items=False) - existing_data = _policyFileReplaceOrAppendList( - list_strings, - existing_data) - elif etree.QName(child_item).localname == 'boolean' and ( - TRUE_VALUE_XPATH(child_item) or FALSE_VALUE_XPATH(child_item)): - value_string = '' - if base_policy_settings[adm_namespace][admPolicy][child_item.attrib['id']]: - value_string = _checkValueItemParent(child_item, - admPolicy, - child_key, - child_valuename, - TRUE_VALUE_XPATH, - None, - check_deleted=False, - test_item=False) - else: - value_string = _checkValueItemParent(child_item, - admPolicy, - child_key, - child_valuename, - FALSE_VALUE_XPATH, - None, - check_deleted=False, - test_item=False) - existing_data = _policyFileReplaceOrAppend( - value_string, - existing_data) - elif etree.QName(child_item).localname == 'boolean' \ - or etree.QName(child_item).localname == 'decimal' \ - or etree.QName(child_item).localname == 'text' \ - or etree.QName(child_item).localname == 'longDecimal' \ - or etree.QName(child_item).localname == 'multiText': - enabled_value_string = _processValueItem( - child_item, - child_key, - child_valuename, - this_policy, - elements_item, - check_deleted=False, - this_element_value=base_policy_settings[adm_namespace][admPolicy][child_item.attrib['id']]) - log.debug('I have enabled value string of %s', enabled_value_string) - existing_data = _policyFileReplaceOrAppend( - enabled_value_string, - existing_data) - elif etree.QName(child_item).localname == 'enum': - for enum_item in child_item.getchildren(): - if base_policy_settings[adm_namespace][admPolicy][child_item.attrib['id']] == \ - _getAdmlDisplayName(adml_policy_resources, - enum_item.attrib['displayName'] - ).strip(): - enabled_value_string = _checkValueItemParent( - enum_item, - child_item.attrib['id'], - child_key, - child_valuename, - VALUE_XPATH, - None, - check_deleted=False, - test_item=False) - existing_data = _policyFileReplaceOrAppend( - enabled_value_string, - existing_data) - if VALUE_LIST_XPATH(enum_item): - enabled_list_strings = _checkListItem(enum_item, - admPolicy, - child_key, - VALUE_LIST_XPATH, - None, - test_items=False) - log.debug('working with valueList portion of %s', - child_item.attrib['id']) - existing_data = _policyFileReplaceOrAppendList( - enabled_list_strings, - existing_data) - break - elif etree.QName(child_item).localname == 'list': - enabled_value_string = _processValueItem( - child_item, - child_key, - child_valuename, - this_policy, - elements_item, - check_deleted=False, - this_element_value=base_policy_settings[adm_namespace][admPolicy][child_item.attrib['id']]) - log.debug('I have enabled value string of %s', - enabled_value_string) - existing_data = _policyFileReplaceOrAppend( - enabled_value_string, - existing_data, - append_only=True) + enabled_value_string, + existing_data, + append_only=True) + try: _write_regpol_data(existing_data, policy_data.admx_registry_classes[registry_class]['policy_path'], policy_data.gpt_ini_path, policy_data.admx_registry_classes[registry_class]['gpt_extension_location'], policy_data.admx_registry_classes[registry_class]['gpt_extension_guid']) # TODO: This needs to be more specific or removed - except Exception as e: # pylint: disable=broad-except - log.exception('Unhandled exception %s occurred while attempting to write Adm Template Policy File', e) + except CommandExecutionError as exc: # pylint: disable=broad-except + log.exception('Unhandled exception occurred while attempting to ' + 'write Adm Template Policy File.\nException: %s', exc) return False return True @@ -7209,13 +7321,13 @@ def _getScriptSettingsFromIniFile(policy_info): if _existingData: try: _existingData = deserialize(_existingData.decode('utf-16-le').lstrip('\ufeff')) - log.debug('Have deserialized data %s', _existingData) + log.trace('Have deserialized data %s', _existingData) except Exception as error: # pylint: disable=broad-except log.exception('An error occurred attempting to deserialize data for %s', policy_info['Policy']) raise CommandExecutionError(error) if 'Section' in policy_info['ScriptIni'] and policy_info['ScriptIni']['Section'].lower() in [z.lower() for z in _existingData.keys()]: if 'SettingName' in policy_info['ScriptIni']: - log.debug('Need to look for %s', policy_info['ScriptIni']['SettingName']) + log.trace('Need to look for %s', policy_info['ScriptIni']['SettingName']) if policy_info['ScriptIni']['SettingName'].lower() in [z.lower() for z in _existingData[policy_info['ScriptIni']['Section']].keys()]: return _existingData[policy_info['ScriptIni']['Section']][policy_info['ScriptIni']['SettingName'].lower()] else: @@ -7321,6 +7433,7 @@ def _lookup_admin_template(policy_name, hierarchy = [] hierarchy_policy_name = policy_name if not adml_search_results: + log.warning('Trying another: %s', policy_name) if '\\' in policy_name: hierarchy = policy_name.split('\\') policy_name = hierarchy.pop() @@ -7331,14 +7444,14 @@ def _lookup_admin_template(policy_name, suggested_policies = '' adml_to_remove = [] if len(adml_search_results) > 1: - log.debug('multiple ADML entries found matching the policy name %s', policy_name) + log.trace('multiple ADML entries found matching the policy name %s', policy_name) multiple_adml_entries = True for adml_search_result in adml_search_results: if not getattr(adml_search_result, 'text', '').strip() == policy_name: adml_to_remove.append(adml_search_result) else: if hierarchy: - log.debug('we have hierarchy of %s', hierarchy) + log.trace('we have hierarchy of %s', hierarchy) display_name_searchval = '$({0}.{1})'.format( adml_search_result.tag.split('}')[1], adml_search_result.attrib['id']) @@ -7350,10 +7463,10 @@ def _lookup_admin_template(policy_name, admx_results = [] these_admx_search_results = admx_policy_definitions.xpath(policy_search_string, namespaces=adml_search_result.nsmap) if not these_admx_search_results: - log.debug('No admx was found for the adml entry %s, it will be removed', display_name_searchval) + log.trace('No admx was found for the adml entry %s, it will be removed', display_name_searchval) adml_to_remove.append(adml_search_result) for search_result in these_admx_search_results: - log.debug('policy_name == %s', policy_name) + log.trace('policy_name == %s', policy_name) this_hierarchy = _build_parent_list( policy_definition=search_result, return_full_policy_names=True, @@ -7361,13 +7474,13 @@ def _lookup_admin_template(policy_name, this_hierarchy.reverse() if hierarchy != this_hierarchy: msg = 'hierarchy %s does not match this item\'s hierarchy of %s' - log.debug(msg, hierarchy, this_hierarchy) + log.trace(msg, hierarchy, this_hierarchy) if len(these_admx_search_results) == 1: - log.debug('only 1 admx was found and it does not match this adml, it is safe to remove from the list') + log.trace('only 1 admx was found and it does not match this adml, it is safe to remove from the list') adml_to_remove.append(adml_search_result) else: - log.debug('hierarchy %s matches item\'s hierarchy of %s', hierarchy, this_hierarchy) - log.debug('search_result %s added to results', search_result) + log.trace('hierarchy %s matches item\'s hierarchy of %s', hierarchy, this_hierarchy) + log.trace('search_result %s added to results', search_result) admx_results.append(search_result) if len(admx_results) == 1: admx_search_results.append(admx_results[0]) @@ -7389,22 +7502,22 @@ def _lookup_admin_template(policy_name, if len(adml_search_results) == 1 and multiple_adml_entries: multiple_adml_entries = False for adml_search_result in adml_search_results: - log.debug('found an ADML entry matching the string! %s -- %s', + log.trace('found an ADML entry matching the string! %s -- %s', adml_search_result.tag, adml_search_result.attrib) display_name_searchval = '$({0}.{1})'.format( adml_search_result.tag.split('}')[1], adml_search_result.attrib['id']) - log.debug('searching for displayName == %s', display_name_searchval) + log.trace('searching for displayName == %s', display_name_searchval) if not admx_search_results: - log.debug('search for an admx entry matching display_name %s and registry_class %s', display_name_searchval, policy_class) + log.trace('search for an admx entry matching display_name %s and registry_class %s', display_name_searchval, policy_class) admx_search_results = ADMX_DISPLAYNAME_SEARCH_XPATH( admx_policy_definitions, display_name=display_name_searchval, registry_class=policy_class) if admx_search_results: - log.debug('processing admx_search_results of {0}'.format(admx_search_results)) - log.debug('multiple_adml_entries is {0}'.format(multiple_adml_entries)) + log.trace('processing admx_search_results of {0}'.format(admx_search_results)) + log.trace('multiple_adml_entries is {0}'.format(multiple_adml_entries)) if (len(admx_search_results) == 1 or hierarchy) and not multiple_adml_entries: found = False for search_result in admx_search_results: @@ -7415,13 +7528,13 @@ def _lookup_admin_template(policy_name, return_full_policy_names=True, adml_language=adml_language) this_hierarchy.reverse() - log.debug('testing %s == %s', hierarchy, this_hierarchy) + log.trace('testing %s == %s', hierarchy, this_hierarchy) if hierarchy == this_hierarchy: found = True else: found = True if found: - log.debug('found the ADMX policy matching ' + log.trace('found the ADMX policy matching ' 'the display name %s -- %s', search_result, policy_name) if 'name' in search_result.attrib: @@ -7442,7 +7555,7 @@ def _lookup_admin_template(policy_name, return True, search_result, policy_aliases, None else: msg = ('ADMX policy with the display name {0} does not' - 'have the required name attribtue') + 'have the required name attribute') msg = msg.format(policy_name) return False, None, [], msg if not found: @@ -7715,7 +7828,6 @@ def get(policy_class=None, return_full_policy_names=True, ''' vals = {} - modal_returns = {} _policydata = _policy_info() if policy_class is None or policy_class.lower() == 'both': @@ -7743,46 +7855,7 @@ def get(policy_class=None, return_full_policy_names=True, policy_name = policy if _pol: vals_key_name = policy_name - if 'Registry' in _pol: - # get value from registry - class_vals[policy_name] = __salt__['reg.read_value']( - _pol['Registry']['Hive'], - _pol['Registry']['Path'], - _pol['Registry']['Value'])['vdata'] - log.debug( - 'Value %r found for reg policy %s', - class_vals[policy_name], policy_name - ) - elif 'Secedit' in _pol: - # get value from secedit - _val = _get_secedit_value(option=_pol['Secedit']['Option']) - class_vals[policy_name] = _val - elif 'NetSH' in _pol: - # get value from netsh - class_vals[policy_name] = _get_netsh_value( - profile=_pol['NetSH']['Profile'], - option=_pol['NetSH']['Option']) - elif 'AdvAudit' in _pol: - # get value from auditpol - class_vals[policy_name] = _get_advaudit_value( - option=_pol['AdvAudit']['Option']) - elif 'NetUserModal' in _pol: - # get value from UserNetMod - if _pol['NetUserModal']['Modal'] not in modal_returns: - modal_returns[_pol['NetUserModal']['Modal']] = win32net.NetUserModalsGet( - None, - _pol['NetUserModal']['Modal']) - class_vals[policy_name] = modal_returns[_pol['NetUserModal']['Modal']][_pol['NetUserModal']['Option']] - elif 'LsaRights' in _pol: - class_vals[policy_name] = _getRightsAssignments(_pol['LsaRights']['Option']) - elif 'ScriptIni' in _pol: - log.debug('Working with ScriptIni setting %s', policy_name) - class_vals[policy_name] = _getScriptSettingsFromIniFile(_pol) - if policy_name in class_vals: - class_vals[policy_name] = _transform_value( - value=class_vals[policy_name], - policy=_policydata.policies[p_class]['policies'][policy_name], - transform_type='Get') + class_vals[policy_name] = _get_policy_info_setting(_pol) if return_full_policy_names: class_vals[_pol['Policy']] = class_vals.pop(policy_name) vals_key_name = _pol['Policy'] @@ -7803,7 +7876,7 @@ def get(policy_class=None, return_full_policy_names=True, else: msg = 'The specified policy {0} is not currently available ' \ 'to be configured via this module' - raise SaltInvocationError(msg.format(policy_name)) + raise CommandExecutionError(msg.format(policy_name)) class_vals = dictupdate.update( class_vals, _checkAllAdmxPolicies(policy_class=p_class, @@ -7820,6 +7893,690 @@ def get(policy_class=None, return_full_policy_names=True, return vals +def _get_policy_info_setting(policy_definition): + ''' + Some policies are defined in this module and others by the ADMX/ADML files + on the machine. This function loads the current values for policies defined + in this module. + + Args: + policy_definition (dict): + A sub-dict of Policies property of the _policy_info() class. + Basically a dictionary that defines the policy + + Returns: + The transformed value. The transform is defined in the policy + definition. It can be a list, a string, a dictionary, depending on how + it's defined + + Usage: + policy_data = _policy_info() + policy_name = 'RemoteRegistryExactPaths' + policy_definition = policy_data.policies['Machine']['policies'][policy_name] + policy_value = _get_policy_info_setting(policy_definition) + ''' + if 'Registry' in policy_definition: + # Get value using the Registry mechanism + value = __utils__['reg.read_value']( + policy_definition['Registry']['Hive'], + policy_definition['Registry']['Path'], + policy_definition['Registry']['Value'])['vdata'] + log.trace('Value %r found for Regisry policy %s', + value, policy_definition['Policy']) + elif 'Secedit' in policy_definition: + # Get value using the Secedit mechanism + value = _get_secedit_value( + option=policy_definition['Secedit']['Option']) + log.trace('Value %r found for Secedit policy %s', + value, policy_definition['Policy']) + elif 'NetSH' in policy_definition: + # Get value using the NetSH mechanism + value = _get_netsh_value( + profile=policy_definition['NetSH']['Profile'], + option=policy_definition['NetSH']['Option']) + log.trace('Value %r found for NetSH policy %s', + value, policy_definition['Policy']) + elif 'AdvAudit' in policy_definition: + # Get value using the AuditPol mechanism + value = _get_advaudit_value( + option=policy_definition['AdvAudit']['Option']) + log.trace('Value %r found for AuditPol policy %s', + value, policy_definition['Policy']) + elif 'NetUserModal' in policy_definition: + # Get value using the NetUserModal mechanism + modal_return = win32net.NetUserModalsGet( + None, policy_definition['NetUserModal']['Modal']) + value = modal_return[ + policy_definition['NetUserModal']['Option']] + log.trace('Value %r found for NetUserModal policy %s', + value, policy_definition['Policy']) + elif 'LsaRights' in policy_definition: + # Get value using the LSARights mechanism + value = _getRightsAssignments(policy_definition['LsaRights']['Option']) + log.trace('Value %r found for LSARights policy %s', + value, policy_definition['Policy']) + elif 'ScriptIni' in policy_definition: + value = _getScriptSettingsFromIniFile(policy_definition) + log.trace('Value %r found for ScriptIni policy %s', + value, policy_definition['Policy']) + else: + message = 'Unknown or missing mechanism in policy_definition\n' \ + '{0}'.format(policy_definition) + raise CommandExecutionError(message) + value = _transform_value(value=value, + policy=policy_definition, + transform_type='Get') + return value + + +def _get_policy_adm_setting(admx_policy, + policy_class, + adml_language='en-US', + return_full_policy_names=False, + hierarchical_return=False): + ''' + Get the current setting for polices set via the policy templates (ADMX/ADML) + files + + Args: + admx_policy (obj): + The XPath object as returned by the ``_lookup_admin_template`` + function + + policy_class (str): + The policy class. Must be one of ``machine`` or ``user`` + + adml_language (str): + The language code for the adml file to use for localization. The + default is ``en-US`` + + return_full_policy_names (bool): + Returns the full policy name regardless of what was passed in + ``policy_name`` + + hierarchical_return (bool): + Returns a hierarchical view of the policy showing its parents + + Returns: + dict: A dictionary containing the policy settings + + Usage: + policy_name = 'AutoUpdateCfg' + policy_class = 'machine' + adml_language = 'en-US' + success, policy_obj, _, _ = _lookup_admin_template( + policy_name=policy_name, + policy_class=policy_class, + adml_language=adml_language) + if success: + setting = _get_policy_adm_setting( + admx_policy=policy_obj, + policy_class=policy_class, + adml_language=adml_language, + return_full_policy_names=return_full_policy_names, + hierarchical_return=hierarchical_return + ) + ''' + # TODO: Need to figure out how to get the lgpo.get function to use this code + # TODO: as it is very similar + # Validate policy Key and Name attributes + this_key = admx_policy.attrib.get('key', None) + this_policy_name = admx_policy.attrib.get('name', None) + if this_key is None or this_policy_name is None: + msg = 'Policy is missing the required "key" or "name" attribute:\n' \ + '{0}'.format(admx_policy.attrib) + raise CommandExecutionError(msg) + + # Get additional settings + this_value_name = admx_policy.attrib.get('valueName', None) + this_policy_setting = 'Not Configured' + this_policy_namespace = admx_policy.nsmap[admx_policy.prefix] + + # Set some default values, these will get flipped below + element_only_enabled_disabled = True + explicit_enable_disable_value_setting = False + + # Load additional data + policy_data = _policy_info() + policy_file_data = _read_regpol_file( + policy_data.admx_registry_classes[policy_class]['policy_path']) + adml_policy_resources = _get_policy_resources(language=adml_language) + + policy_vals = {} + + if ENABLED_VALUE_XPATH(admx_policy) and this_policy_setting == 'Not Configured': + # some policies have a disabled list but not an enabled list + # added this to address those issues + if DISABLED_LIST_XPATH(admx_policy) or DISABLED_VALUE_XPATH(admx_policy): + element_only_enabled_disabled = False + explicit_enable_disable_value_setting = True + if _checkValueItemParent(policy_element=admx_policy, + policy_name=this_policy_name, + policy_key=this_key, + policy_valueName=this_value_name, + xpath_object=ENABLED_VALUE_XPATH, + policy_file_data=policy_file_data): + log.trace('%s is enabled by detected ENABLED_VALUE_XPATH', + this_policy_name) + this_policy_setting = 'Enabled' + policy_vals.setdefault(this_policy_namespace, {})[this_policy_name] = this_policy_setting + if DISABLED_VALUE_XPATH(admx_policy) and this_policy_setting == 'Not Configured': + # some policies have a disabled list but not an enabled list + # added this to address those issues + if ENABLED_LIST_XPATH(admx_policy) or ENABLED_VALUE_XPATH(admx_policy): + element_only_enabled_disabled = False + explicit_enable_disable_value_setting = True + if _checkValueItemParent(policy_element=admx_policy, + policy_name=this_policy_name, + policy_key=this_key, + policy_valueName=this_value_name, + xpath_object=DISABLED_VALUE_XPATH, + policy_file_data=policy_file_data): + log.trace('%s is disabled by detected DISABLED_VALUE_XPATH', + this_policy_name) + this_policy_setting = 'Disabled' + policy_vals.setdefault(this_policy_namespace, {})[this_policy_name] = this_policy_setting + if ENABLED_LIST_XPATH(admx_policy): + if DISABLED_LIST_XPATH(admx_policy) or DISABLED_VALUE_XPATH(admx_policy): + element_only_enabled_disabled = False + explicit_enable_disable_value_setting = True + if _checkListItem(policy_element=admx_policy, + policy_name=this_policy_name, + policy_key=this_key, + xpath_object=ENABLED_LIST_XPATH, + policy_file_data=policy_file_data): + log.trace('%s is enabled by detected ENABLED_LIST_XPATH', + this_policy_name) + this_policy_setting = 'Enabled' + policy_vals.setdefault(this_policy_namespace, {})[this_policy_name] = this_policy_setting + if DISABLED_LIST_XPATH(admx_policy): + if ENABLED_LIST_XPATH(admx_policy) or ENABLED_VALUE_XPATH(admx_policy): + element_only_enabled_disabled = False + explicit_enable_disable_value_setting = True + if _checkListItem(policy_element=admx_policy, + policy_name=this_policy_name, + policy_key=this_key, + xpath_object=DISABLED_LIST_XPATH, + policy_file_data=policy_file_data): + log.trace('%s is disabled by detected DISABLED_LIST_XPATH', + this_policy_name) + this_policy_setting = 'Disabled' + policy_vals.setdefault(this_policy_namespace, {})[this_policy_name] = this_policy_setting + + if not explicit_enable_disable_value_setting and this_value_name: + # the policy has a key/valuename but no explicit Enabled/Disabled + # Value or List + # these seem to default to a REG_DWORD 1 = "Enabled" **del. = "Disabled" + if _regexSearchRegPolData(re.escape(_buildKnownDataSearchString( + reg_key=this_key, + reg_valueName=this_value_name, + reg_vtype='REG_DWORD', + reg_data='1')), + policy_file_data): + log.trace('%s is enabled by no explicit enable/disable list or ' + 'value', this_policy_name) + this_policy_setting = 'Enabled' + policy_vals.setdefault(this_policy_namespace, {})[this_policy_name] = this_policy_setting + elif _regexSearchRegPolData(re.escape(_buildKnownDataSearchString( + reg_key=this_key, + reg_valueName=this_value_name, + reg_vtype='REG_DWORD', + reg_data=None, + check_deleted=True)), + policy_file_data): + log.trace('%s is disabled by no explicit enable/disable list or ' + 'value', this_policy_name) + this_policy_setting = 'Disabled' + policy_vals.setdefault(this_policy_namespace, {})[this_policy_name] = this_policy_setting + + full_names = {} + hierarchy = {} + if ELEMENTS_XPATH(admx_policy): + if element_only_enabled_disabled or this_policy_setting == 'Enabled': + # TODO does this need to be modified based on the 'required' attribute? + required_elements = {} + configured_elements = {} + policy_disabled_elements = 0 + for elements_item in ELEMENTS_XPATH(admx_policy): + for child_item in elements_item.getchildren(): + this_element_name = _getFullPolicyName( + policy_item=child_item, + policy_name=child_item.attrib['id'], + return_full_policy_names=return_full_policy_names, + adml_language=adml_language) + required_elements[this_element_name] = None + child_key = child_item.attrib.get('key', this_key) + child_value_name = child_item.attrib.get('valueName', + this_value_name) + if etree.QName(child_item).localname == 'boolean': + # https://msdn.microsoft.com/en-us/library/dn605978(v=vs.85).aspx + if child_item.getchildren(): + if TRUE_VALUE_XPATH(child_item) and this_element_name not in configured_elements: + if _checkValueItemParent( + policy_element=child_item, + policy_name=this_policy_name, + policy_key=child_key, + policy_valueName=child_value_name, + xpath_object=TRUE_VALUE_XPATH, + policy_file_data=policy_file_data): + configured_elements[this_element_name] = True + log.trace('element %s is configured true', + child_item.attrib['id']) + if FALSE_VALUE_XPATH(child_item) and this_element_name not in configured_elements: + if _checkValueItemParent( + policy_element=child_item, + policy_name=this_policy_name, + policy_key=child_key, + policy_valueName=child_value_name, + xpath_object=FALSE_VALUE_XPATH, + policy_file_data=policy_file_data): + configured_elements[this_element_name] = False + policy_disabled_elements = policy_disabled_elements + 1 + log.trace('element %s is configured false', + child_item.attrib['id']) + # WARNING - no standard ADMX files use true/falseList + # so this hasn't actually been tested + if TRUE_LIST_XPATH(child_item) and this_element_name not in configured_elements: + log.trace('checking trueList') + if _checkListItem( + policy_element=child_item, + policy_name=this_policy_name, + policy_key=this_key, + xpath_object=TRUE_LIST_XPATH, + policy_file_data=policy_file_data): + configured_elements[this_element_name] = True + log.trace('element %s is configured true', + child_item.attrib['id']) + if FALSE_LIST_XPATH(child_item) and this_element_name not in configured_elements: + log.trace('checking falseList') + if _checkListItem( + policy_element=child_item, + policy_name=this_policy_name, + policy_key=this_key, + xpath_object=FALSE_LIST_XPATH, + policy_file_data=policy_file_data): + configured_elements[this_element_name] = False + policy_disabled_elements = policy_disabled_elements + 1 + log.trace('element %s is configured false', + child_item.attrib['id']) + else: + if _regexSearchRegPolData( + re.escape(_processValueItem( + element=child_item, + reg_key=child_key, + reg_valuename=child_value_name, + policy=admx_policy, + parent_element=elements_item, + check_deleted=True)), + policy_file_data): + configured_elements[this_element_name] = False + policy_disabled_elements = policy_disabled_elements + 1 + log.trace('element %s is configured false', child_item.attrib['id']) + elif _regexSearchRegPolData( + re.escape(_processValueItem( + element=child_item, + reg_key=child_key, + reg_valuename=child_value_name, + policy=admx_policy, + parent_element=elements_item, + check_deleted=False)), + policy_file_data): + configured_elements[this_element_name] = True + log.trace('element %s is configured true', + child_item.attrib['id']) + elif etree.QName(child_item).localname == 'decimal' \ + or etree.QName(child_item).localname == 'text' \ + or etree.QName(child_item).localname == 'longDecimal' \ + or etree.QName(child_item).localname == 'multiText': + # https://msdn.microsoft.com/en-us/library/dn605987(v=vs.85).aspx + if _regexSearchRegPolData( + re.escape(_processValueItem( + element=child_item, + reg_key=child_key, + reg_valuename=child_value_name, + policy=admx_policy, + parent_element=elements_item, + check_deleted=True)), + policy_file_data): + configured_elements[this_element_name] = 'Disabled' + policy_disabled_elements = policy_disabled_elements + 1 + log.trace('element %s is disabled', + child_item.attrib['id']) + elif _regexSearchRegPolData( + re.escape(_processValueItem( + element=child_item, + reg_key=child_key, + reg_valuename=child_value_name, + policy=admx_policy, + parent_element=elements_item, + check_deleted=False)), + policy_data=policy_file_data): + configured_value = _getDataFromRegPolData( + _processValueItem( + element=child_item, + reg_key=child_key, + reg_valuename=child_value_name, + policy=admx_policy, + parent_element=elements_item, + check_deleted=False), + policy_data=policy_file_data) + configured_elements[this_element_name] = configured_value + log.trace('element %s is enabled, value == %s', + child_item.attrib['id'], + configured_value) + elif etree.QName(child_item).localname == 'enum': + if _regexSearchRegPolData( + re.escape(_processValueItem( + element=child_item, + reg_key=child_key, + reg_valuename=child_value_name, + policy=admx_policy, + parent_element=elements_item, + check_deleted=True)), + policy_file_data): + log.trace('enum element %s is disabled', + child_item.attrib['id']) + configured_elements[this_element_name] = 'Disabled' + policy_disabled_elements = policy_disabled_elements + 1 + else: + for enum_item in child_item.getchildren(): + if _checkValueItemParent( + policy_element=enum_item, + policy_name=child_item.attrib['id'], + policy_key=child_key, + policy_valueName=child_value_name, + xpath_object=VALUE_XPATH, + policy_file_data=policy_file_data): + if VALUE_LIST_XPATH(enum_item): + log.trace('enum item has a valueList') + if _checkListItem( + policy_element=enum_item, + policy_name=this_policy_name, + policy_key=child_key, + xpath_object=VALUE_LIST_XPATH, + policy_file_data=policy_file_data): + log.trace('all valueList items exist in file') + configured_elements[this_element_name] = _getAdmlDisplayName( + adml_xml_data=adml_policy_resources, + display_name=enum_item.attrib['displayName']) + else: + configured_elements[this_element_name] = _getAdmlDisplayName( + adml_xml_data=adml_policy_resources, + display_name=enum_item.attrib['displayName']) + elif etree.QName(child_item).localname == 'list': + return_value_name = False + if 'explicitValue' in child_item.attrib \ + and child_item.attrib['explicitValue'].lower() == 'true': + log.trace('explicitValue list, we will return value names') + return_value_name = True + if _regexSearchRegPolData( + re.escape(_processValueItem( + element=child_item, + reg_key=child_key, + reg_valuename=child_value_name, + policy=admx_policy, + parent_element=elements_item, + check_deleted=False)) + salt.utils.stringutils.to_bytes(r'(?!\*\*delvals\.)'), + policy_data=policy_file_data): + configured_value = _getDataFromRegPolData( + _processValueItem( + element=child_item, + reg_key=child_key, + reg_valuename=child_value_name, + policy=admx_policy, + parent_element=elements_item, + check_deleted=False), + policy_data=policy_file_data, + return_value_name=return_value_name) + configured_elements[this_element_name] = configured_value + log.trace('element %s is enabled values: %s', + child_item.attrib['id'], + configured_value) + elif _regexSearchRegPolData( + re.escape(_processValueItem( + element=child_item, + reg_key=child_key, + reg_valuename=child_value_name, + policy=admx_policy, + parent_element=elements_item, + check_deleted=True)), + policy_file_data): + configured_elements[this_element_name] = "Disabled" + policy_disabled_elements = policy_disabled_elements + 1 + log.trace('element {0} is disabled'.format(child_item.attrib['id'])) + if element_only_enabled_disabled: + if len(required_elements.keys()) > 0 and len(configured_elements.keys()) == len(required_elements.keys()): + if policy_disabled_elements == len(required_elements.keys()): + log.trace('{0} is disabled by all enum elements'.format(this_policy_name)) + policy_vals.setdefault(this_policy_namespace, {})[this_policy_name] = 'Disabled' + else: + log.trace('{0} is enabled by enum elements'.format(this_policy_name)) + policy_vals.setdefault(this_policy_namespace, {})[this_policy_name] = configured_elements + else: + policy_vals.setdefault(this_policy_namespace, {})[this_policy_name] = this_policy_setting + else: + if this_policy_setting == 'Enabled': + policy_vals.setdefault(this_policy_namespace, {})[this_policy_name] = configured_elements + else: + policy_vals.setdefault(this_policy_namespace, {})[this_policy_name] = this_policy_setting + else: + policy_vals.setdefault(this_policy_namespace, {})[this_policy_name] = this_policy_setting + + if return_full_policy_names and \ + this_policy_namespace in policy_vals and \ + this_policy_name in policy_vals[this_policy_namespace]: + full_names.setdefault(this_policy_namespace, {}) + full_names[this_policy_namespace][this_policy_name] = _getFullPolicyName( + policy_item=admx_policy, + policy_name=admx_policy.attrib['name'], + return_full_policy_names=return_full_policy_names, + adml_language=adml_language) + # Make sure the we're passing the full policy name + # This issue was found when setting the `Allow Telemetry` setting + # All following states would show a change in this setting + # When the state does it's first `lgpo.get` it would return `AllowTelemetry` + # On the second run, it would return `Allow Telemetry` + # This makes sure we're always returning the full_name when required + if this_policy_name in policy_vals[this_policy_namespace][this_policy_name]: + full_name = full_names[this_policy_namespace][this_policy_name] + setting = policy_vals[this_policy_namespace][this_policy_name].pop(this_policy_name) + policy_vals[this_policy_namespace][this_policy_name][full_name] = setting + + if this_policy_namespace in policy_vals and \ + this_policy_name in policy_vals[this_policy_namespace]: + hierarchy.setdefault(this_policy_namespace, {})[this_policy_name] = _build_parent_list( + policy_definition=admx_policy, + return_full_policy_names=return_full_policy_names, + adml_language=adml_language) + + if policy_vals and return_full_policy_names and not hierarchical_return: + log.debug('Compiling non hierarchical return...') + unpathed_dict = {} + pathed_dict = {} + for policy_namespace in list(policy_vals): + for policy_item in list(policy_vals[policy_namespace]): + full_name = full_names[policy_namespace][policy_item] + if full_name in policy_vals[policy_namespace]: + # add this item with the path'd full name + full_path_list = hierarchy[policy_namespace][policy_item] + full_path_list.reverse() + full_path_list.append(full_names[policy_namespace][policy_item]) + policy_vals['\\'.join(full_path_list)] = policy_vals[policy_namespace].pop(policy_item) + pathed_dict[full_name] = True + else: + policy_vals[policy_namespace][full_name] = policy_vals[policy_namespace].pop(policy_item) + unpathed_dict.setdefault(policy_namespace, {})[full_name] = policy_item + # go back and remove any "unpathed" policies that need a full path + for path_needed in unpathed_dict[policy_namespace]: + # remove the item with the same full name and re-add it w/a path'd version + full_path_list = hierarchy[policy_namespace][unpathed_dict[policy_namespace][path_needed]] + full_path_list.reverse() + full_path_list.append(path_needed) + log.trace('full_path_list == %s', full_path_list) + policy_vals['\\'.join(full_path_list)] = policy_vals[policy_namespace].pop(path_needed) + + for policy_namespace in list(policy_vals): + # Remove empty entries + if policy_vals[policy_namespace] == {}: + policy_vals.pop(policy_namespace) + # Remove namespace and keep the values + elif isinstance(policy_vals[policy_namespace], dict): + if this_policy_namespace == policy_namespace and \ + not hierarchical_return: + policy_vals.update(policy_vals[policy_namespace]) + policy_vals.pop(policy_namespace) + + if policy_vals and hierarchical_return: + if hierarchy: + log.debug('Compiling hierarchical return...') + for policy_namespace in hierarchy: + for hierarchy_item in hierarchy[policy_namespace]: + if hierarchy_item in policy_vals[policy_namespace]: + t_dict = {} + first_item = True + for item in hierarchy[policy_namespace][hierarchy_item]: + new_dict = {} + if first_item: + h_policy_name = hierarchy_item + if return_full_policy_names: + h_policy_name = full_names[policy_namespace][hierarchy_item] + new_dict[item] = {h_policy_name: policy_vals[policy_namespace].pop(hierarchy_item)} + first_item = False + else: + new_dict[item] = t_dict + t_dict = new_dict + if t_dict: + policy_vals = dictupdate.update(policy_vals, t_dict) + if policy_namespace in policy_vals and policy_vals[policy_namespace] == {}: + policy_vals.pop(policy_namespace) + policy_vals = { + policy_data.admx_registry_classes[policy_class]['lgpo_section']: { + 'Administrative Templates': policy_vals}} + + return policy_vals + + +def get_policy(policy_name, + policy_class, + adml_language='en-US', + return_value_only=True, + return_full_policy_names=True, + hierarchical_return=False): + r''' + Get the current settings for a single policy on the machine + + Args: + policy_name (str): + The name of the policy to retrieve. Can be the any of the names + or alieses returned by ``lgpo.get_policy_info`` + + policy_class (str): + The policy class. Must be one of ``machine`` or ``user`` + + adml_language (str): + The language code for the adml file to use for localization. The + default is ``en-US`` + + return_value_only (bool): + ``True`` will return only the value for the policy, without the + name of the policy. ``return_full_policy_names`` and + ``hierarchical_return`` will be ignored. Default is ``True`` + + return_full_policy_names (bool): + Returns the full policy name regardless of what was passed in + ``policy_name`` + + .. note:: + This setting applies to sub-elements of the policy if they + exist. The value passed in ``policy_name`` will always be used + as the policy name when this setting is ``False`` + + hierarchical_return (bool): + Returns a hierarchical view of the policy showing its parents + + Returns: + dict: A dictionary containing the policy settings + + CLI Example: + + .. code-block:: bash + + # Using the policy id + salt * lgpo.get_policy LockoutDuration machine + salt * lgpo.get_policy AutoUpdateCfg machine + + # Using the full name + salt * lgpo.get_policy "Account lockout duration" machine + salt * lgpo.get_policy "Configure Automatic Updates" machine + + # Using full path and name + salt * lgpo.get_policy "Windows Components\Windows Update\Configure Automatic Updates" machine + ''' + if not policy_name: + raise SaltInvocationError('policy_name must be defined') + if not policy_class: + raise SaltInvocationError('policy_class must be defined') + policy_class = policy_class.title() + policy_data = _policy_info() + if policy_class not in policy_data.policies.keys(): + policy_classes = ', '.join(policy_data.policies.keys()) + message = 'The requested policy class "{0}" is invalid, policy_class ' \ + 'should be one of: {1}'.format(policy_class, policy_classes) + raise CommandExecutionError(message) + + # Look in the _policy_data object first + policy_definition = None + if policy_name in policy_data.policies[policy_class]['policies']: + policy_definition = policy_data.policies[policy_class]['policies'][policy_name] + else: + for pol in policy_data.policies[policy_class]['policies']: + if policy_data.policies[policy_class]['policies'][pol]['Policy'].lower() == policy_name.lower(): + policy_definition = policy_data.policies[policy_class]['policies'][pol] + break + if policy_definition: + if return_value_only: + return _get_policy_info_setting(policy_definition) + if return_full_policy_names: + key_name = policy_definition['Policy'] + else: + key_name = policy_name + setting = {key_name: _get_policy_info_setting(policy_definition)} + if hierarchical_return: + if 'lgpo_section' in policy_definition: + first_item = True + t_dict = {} + for level in reversed(policy_definition['lgpo_section']): + new_dict = {} + if first_item: + new_dict[level] = { + key_name: setting.pop(key_name)} + first_item = False + else: + new_dict[level] = t_dict + t_dict = new_dict + if t_dict: + setting = t_dict + + return setting + + success, policy_obj, _, _ = _lookup_admin_template( + policy_name=policy_name, + policy_class=policy_class, + adml_language=adml_language) + if success: + setting = _get_policy_adm_setting( + admx_policy=policy_obj, + policy_class=policy_class, + adml_language=adml_language, + return_full_policy_names=return_full_policy_names, + hierarchical_return=hierarchical_return + ) + if return_value_only: + for key in setting: + return setting[key] + return setting + + def set_computer_policy(name, setting, cumulative_rights_assignments=True, @@ -8023,11 +8780,11 @@ class in this module. raise SaltInvocationError(msg.format(policies[p_class][policy_name], policy_name)) if 'Registry' in _pol: # set value in registry - log.debug('%s is a registry policy', policy_name) + log.trace('%s is a registry policy', policy_name) _regedits[policy_name] = {'policy': _pol, 'value': _value} elif 'Secedit' in _pol: # set value with secedit - log.debug('%s is a Secedit policy', policy_name) + log.trace('%s is a Secedit policy', policy_name) if _pol['Secedit']['Section'] not in _secedits: _secedits[_pol['Secedit']['Section']] = [] _secedits[_pol['Secedit']['Section']].append( @@ -8035,7 +8792,7 @@ class in this module. '=', six.text_type(_value)])) elif 'NetSH' in _pol: # set value with netsh - log.debug('%s is a NetSH policy', policy_name) + log.trace('%s is a NetSH policy', policy_name) _netshs.setdefault(policy_name, { 'profile': _pol['NetSH']['Profile'], 'section': _pol['NetSH']['Section'], @@ -8050,16 +8807,16 @@ class in this module. }) elif 'NetUserModal' in _pol: # set value via NetUserModal - log.debug('%s is a NetUserModal policy', policy_name) + log.trace('%s is a NetUserModal policy', policy_name) if _pol['NetUserModal']['Modal'] not in _modal_sets: _modal_sets[_pol['NetUserModal']['Modal']] = {} _modal_sets[_pol['NetUserModal']['Modal']][_pol['NetUserModal']['Option']] = _value elif 'LsaRights' in _pol: - log.debug('%s is a LsaRights policy', policy_name) + log.trace('%s is a LsaRights policy', policy_name) _lsarights[policy_name] = {'policy': _pol, 'value': _value} else: _value = policies[p_class][policy_name] - log.debug('searching for "%s" in admx data', policy_name) + log.trace('searching for "%s" in admx data', policy_name) success, the_policy, policy_name_list, msg = _lookup_admin_template( policy_name=policy_name, policy_class=p_class, @@ -8073,8 +8830,8 @@ class in this module. else: raise SaltInvocationError(msg) if policy_namespace and policy_name in _admTemplateData[policy_namespace] and the_policy is not None: - log.debug('setting == %s', six.text_type(_admTemplateData[policy_namespace][policy_name]).lower()) - log.debug(six.text_type(_admTemplateData[policy_namespace][policy_name]).lower()) + log.trace('setting == %s', six.text_type(_admTemplateData[policy_namespace][policy_name]).lower()) + log.trace(six.text_type(_admTemplateData[policy_namespace][policy_name]).lower()) if six.text_type(_admTemplateData[policy_namespace][policy_name]).lower() != 'disabled' \ and six.text_type(_admTemplateData[policy_namespace][policy_name]).lower() != 'not configured': if ELEMENTS_XPATH(the_policy): @@ -8082,14 +8839,14 @@ class in this module. for elements_item in ELEMENTS_XPATH(the_policy): for child_item in elements_item.getchildren(): # check each element - log.debug('checking element %s', child_item.attrib['id']) + log.trace('checking element %s', child_item.attrib['id']) temp_element_name = None this_element_name = _getFullPolicyName( policy_item=child_item, policy_name=child_item.attrib['id'], return_full_policy_names=True, adml_language=adml_language) - log.debug('id attribute == "%s" this_element_name == "%s"', child_item.attrib['id'], this_element_name) + log.trace('id attribute == "%s" this_element_name == "%s"', child_item.attrib['id'], this_element_name) if this_element_name in _admTemplateData[policy_namespace][policy_name]: temp_element_name = this_element_name elif child_item.attrib['id'] in _admTemplateData[policy_namespace][policy_name]: @@ -8176,7 +8933,7 @@ class in this module. raise SaltInvocationError(msg) if _regedits: for regedit in _regedits: - log.debug('%s is a Registry policy', regedit) + log.trace('%s is a Registry policy', regedit) # if the value setting is None or "(value not set)", we will delete the value from the registry if _regedits[regedit]['value'] is not None and _regedits[regedit]['value'] != '(value not set)': _ret = __salt__['reg.set_value']( @@ -8222,7 +8979,18 @@ class in this module. raise SaltInvocationError(msg.format(lsaright)) if _secedits: # we've got secedits to make - if not _write_secedit_data(_secedits): + log.trace(_secedits) + ini_data = '\r\n'.join(['[Unicode]', 'Unicode=yes']) + _seceditSections = ['System Access', 'Event Audit', 'Registry Values', 'Privilege Rights'] + for _seceditSection in _seceditSections: + if _seceditSection in _secedits: + ini_data = '\r\n'.join([ini_data, ''.join(['[', _seceditSection, ']']), + '\r\n'.join(_secedits[_seceditSection])]) + ini_data = '\r\n'.join([ini_data, '[Version]', + 'signature="$CHICAGO$"', + 'Revision=1']) + log.trace('ini_data == %s', ini_data) + if not _write_secedit_data(ini_data): msg = ('Error while attempting to set policies via ' 'secedit. Some changes may not be applied as ' 'expected') @@ -8230,34 +8998,36 @@ class in this module. if _netshs: # we've got netsh settings to make for setting in _netshs: - log.debug('Setting firewall policy: {0}'.format(setting)) - log.debug(_netshs[setting]) + log.trace('Setting firewall policy: {0}'.format(setting)) + log.trace(_netshs[setting]) _set_netsh_value(**_netshs[setting]) if _advaudits: # We've got AdvAudit settings to make for setting in _advaudits: - log.debug('Setting Advanced Audit policy: {0}'.format(setting)) - log.debug(_advaudits[setting]) + log.trace('Setting Advanced Audit policy: {0}'.format(setting)) + log.trace(_advaudits[setting]) _set_advaudit_value(**_advaudits[setting]) if _modal_sets: # we've got modalsets to make - log.debug(_modal_sets) + log.trace(_modal_sets) for _modal_set in _modal_sets: try: _existingModalData = win32net.NetUserModalsGet(None, _modal_set) _newModalSetData = dictupdate.update(_existingModalData, _modal_sets[_modal_set]) - log.debug('NEW MODAL SET = %s', _newModalSetData) + log.trace('NEW MODAL SET = %s', _newModalSetData) _ret = win32net.NetUserModalsSet(None, _modal_set, _newModalSetData) # TODO: This needs to be more specific - except Exception: # pylint: disable=broad-except - msg = 'An unhandled exception occurred while attempting to set policy via NetUserModalSet' + except Exception as exc: # pylint: disable=broad-except + msg = 'An unhandled exception occurred while ' \ + 'attempting to set policy via ' \ + 'NetUserModalSet\n{0}'.format(exc) log.exception(msg) raise CommandExecutionError(msg) if _admTemplateData: _ret = False - log.debug('going to write some adm template data :: %s', _admTemplateData) + log.trace('going to write some adm template data :: %s', _admTemplateData) _ret = _writeAdminTemplateRegPolFile(_admTemplateData, adml_language=adml_language, registry_class=p_class) diff --git a/salt/states/loop.py b/salt/states/loop.py index c6370cb867da..524fa56c1a0c 100644 --- a/salt/states/loop.py +++ b/salt/states/loop.py @@ -200,7 +200,7 @@ def until_no_eval( current_attempt += 1 try: res = __salt__[name](*args, **kwargs) - except Exception: + except Exception: # pylint: disable=broad-except (exc_type, exc_value, _) = sys.exc_info() ret['comment'] = 'Exception occurred while executing {}: {}:{}'.format(name, exc_type, exc_value) break diff --git a/salt/states/pkg.py b/salt/states/pkg.py index 7388024421e5..a13d4184002f 100644 --- a/salt/states/pkg.py +++ b/salt/states/pkg.py @@ -2035,7 +2035,7 @@ def downloaded(name, (if specified). Currently supported for the following pkg providers: - :mod:`yumpkg ` and :mod:`zypper ` + :mod:`yumpkg `, :mod:`zypper ` and :mod:`zypper ` :param str name: The name of the package to be downloaded. This parameter is ignored if @@ -2174,7 +2174,7 @@ def downloaded(name, if not ret['changes'] and not ret['comment']: ret['result'] = True - ret['comment'] = 'Packages are already downloaded: ' \ + ret['comment'] = 'Packages downloaded: ' \ '{0}'.format(', '.join(targets)) return ret diff --git a/salt/states/win_lgpo.py b/salt/states/win_lgpo.py index 8645f4420dcf..3c72d42e23a1 100644 --- a/salt/states/win_lgpo.py +++ b/salt/states/win_lgpo.py @@ -7,9 +7,11 @@ This state allows configuring local Windows Group Policy -The state can be used to ensure the setting of a single policy or multiple policies in one pass. +The state can be used to ensure the setting of a single policy or multiple +policies in one pass. -Single policies must specify the policy name, the setting, and the policy class (Machine/User/Both) +Single policies must specify the policy name, the setting, and the policy class +(Machine/User/Both) Example single policy configuration @@ -24,7 +26,7 @@ .. code-block:: yaml Account lockout duration: - gpo.set: + lgpo.set: - setting: 120 - policy_class: Machine @@ -35,10 +37,11 @@ Company Local Group Policy: lgpo.set: - computer_policy: - Deny logon locally: Guest + Deny log on locally: + - Guest Account lockout duration: 120 Account lockout threshold: 10 - Reset account lockout counter after: 1440 + Reset account lockout counter after: 120 Enforce password history: 24 Maximum password age: 60 Minimum password age: 1 @@ -63,9 +66,9 @@ Maximum password age: 60 Minimum password age: 1 Minimum password length: 14 - Account lockout duration: 1440 + Account lockout duration: 120 Account lockout threshold: 10 - Reset account lockout counter after: 1440 + Reset account lockout counter after: 120 Manage auditing and security log: - "BUILTIN\\Administrators" Replace a process level token: @@ -100,9 +103,7 @@ "Set the intranet update service for detecting updates": http://mywsus "Set the intranet statistics server": http://mywsus - cumulative_rights_assignments: True - ''' - # Import python libs from __future__ import absolute_import, unicode_literals, print_function import logging @@ -111,6 +112,7 @@ import salt.utils.data import salt.utils.dictdiffer import salt.utils.json +import salt.utils.win_functions # Import 3rd party libs from salt.ext import six @@ -133,7 +135,7 @@ def _compare_policies(new_policy, current_policy): otherwise ``False`` ''' # Compared dicts, lists, and strings - if isinstance(new_policy, six.string_types): + if isinstance(new_policy, (six.string_types, six.integer_types)): return new_policy == current_policy elif isinstance(new_policy, list): if isinstance(current_policy, list): @@ -157,54 +159,69 @@ def set_(name, cumulative_rights_assignments=True, adml_language='en-US'): ''' - Ensure the specified policy is set - - name - the name of a single policy to configure - - setting - the configuration setting for the single named policy - if this argument is used the computer_policy/user_policy arguments will be ignored - - policy_class - the policy class of the single named policy to configure - this can "machine", "user", or "both" - - computer_policy - a dict of policyname: value pairs of a set of computer policies to configure - if this argument is used, the name/setting/policy_class arguments will be ignored - - user_policy - a dict of policyname: value pairs of a set of user policies to configure - if this argument is used, the name/setting/policy_class arguments will be ignored - - cumulative_rights_assignments - determine if any user right assignment policies specified will be cumulative - or explicit - - adml_language - the adml language to use for AMDX policy data/display conversions + Ensure the specified policy is set. + + .. warning:: + The ``setting`` argument cannot be used in conjunction with the + ``computer_policy`` or ``user_policy`` arguments + + Args: + name (str): The name of a single policy to configure + + setting (str, dict, list): + The configuration setting for the single named policy. If this + argument is used the ``computer_policy`` / ``user_policy`` arguments + will be ignored + + policy_class (str): + The policy class of the single named policy to configure. This can + ``machine``, ``user``, or ``both`` + + computer_policy (dict): + A dictionary of containing the policy name and key/value pairs of a + set of computer policies to configure. If this argument is used, the + ``name`` / ``policy_class`` arguments will be ignored + + user_policy (dict): + A dictionary of containing the policy name and key/value pairs of a + set of user policies to configure. If this argument is used, the + ``name`` / ``policy_class`` arguments will be ignored + + cumulative_rights_assignments (bool): + If user rights assignments are being configured, determines if any + user right assignment policies specified will be cumulative or + explicit + + adml_language (str): + The adml language to use for AMDX policy data/display conversions. + Default is ``en-US`` ''' ret = {'name': name, 'result': True, 'changes': {}, 'comment': ''} policy_classes = ['machine', 'computer', 'user', 'both'] + class_map = { + 'computer': 'Computer Configuration', + 'machine': 'Computer Configuration', + 'user': 'User Configuration' + } if not setting and not computer_policy and not user_policy: - msg = 'At least one of the parameters setting, computer_policy, or user_policy' - msg = msg + ' must be specified.' + msg = 'At least one of the parameters setting, computer_policy, or ' \ + 'user_policy must be specified.' ret['result'] = False ret['comment'] = msg return ret if setting and not policy_class: - msg = 'A single policy setting was specified but the policy_class was not specified.' + msg = 'A single policy setting was specified but the policy_class ' \ + 'was not specified.' ret['result'] = False ret['comment'] = msg return ret if setting and (computer_policy or user_policy): - msg = 'The setting and computer_policy/user_policy parameters are mutually exclusive. Please' - msg = msg + ' specify either a policy name and setting or a computer_policy and/or user_policy' - msg = msg + ' dict' + msg = 'The setting and computer_policy/user_policy parameters are ' \ + 'mutually exclusive. Please specify either a policy name and ' \ + 'setting or a computer_policy and/or user_policy dict' ret['result'] = False ret['comment'] = msg return ret @@ -238,68 +255,62 @@ def set_(name, computer_policy[name] = setting elif policy_class.lower() == 'user': user_policy[name] = setting - elif policy_class.lower() == 'machine' or policy_class.lower() == 'computer': + elif policy_class.lower() in ['machine', 'computer']: computer_policy[name] = setting - pol_data = {} - pol_data['user'] = {'output_section': 'User Configuration', - 'requested_policy': user_policy, - 'policy_lookup': {}} - pol_data['machine'] = {'output_section': 'Computer Configuration', - 'requested_policy': computer_policy, - 'policy_lookup': {}} - + pol_data = { + 'user': { + 'requested_policy': user_policy, + 'policy_lookup': {}}, + 'machine': { + 'requested_policy': computer_policy, + 'policy_lookup': {}}} + + current_policy = {} for p_class, p_data in six.iteritems(pol_data): if p_data['requested_policy']: - for policy_name, policy_setting in six.iteritems(p_data['requested_policy']): - lookup = __salt__['lgpo.get_policy_info'](policy_name, - p_class, - adml_language=adml_language) + for p_name, _ in six.iteritems(p_data['requested_policy']): + lookup = __salt__['lgpo.get_policy_info']( + policy_name=p_name, + policy_class=p_class, + adml_language=adml_language) if lookup['policy_found']: - pol_data[p_class]['policy_lookup'][policy_name] = lookup + pol_data[p_class]['policy_lookup'][p_name] = lookup + # Since we found the policy, let's get the current setting + # as well + current_policy.setdefault(class_map[p_class], {}) + current_policy[class_map[p_class]][p_name] = __salt__['lgpo.get_policy']( + policy_name=p_name, + policy_class=p_class, + adml_language=adml_language, + return_value_only=True) else: ret['comment'] = ' '.join([ret['comment'], lookup['message']]) ret['result'] = False if not ret['result']: return ret - current_policy = __salt__['lgpo.get'](policy_class=policy_class, - adml_language=adml_language, - hierarchical_return=False) + log.debug('pol_data == %s', pol_data) log.debug('current policy == %s', current_policy) # compare policies policy_changes = [] - for policy_section, policy_data in six.iteritems(pol_data): - pol_id = None - if policy_data and policy_data['output_section'] in current_policy: - for policy_name, policy_setting in six.iteritems(policy_data['requested_policy']): - currently_set = False - # Check Case sensitive first (faster) - if policy_name in current_policy[policy_data['output_section']]: + for p_class, p_data in six.iteritems(pol_data): + requested_policy = p_data.get('requested_policy') + if requested_policy: + for p_name, p_setting in six.iteritems(requested_policy): + if p_name in current_policy[class_map[p_class]]: currently_set = True - pol_id = policy_name - # Check case insensitive - elif policy_name.lower() in (k.lower() for k in current_policy[policy_data['output_section']]): - for p_name in current_policy[policy_data['output_section']]: - if policy_name.lower() == p_name.lower(): - currently_set = True - pol_id = p_name - break - # Check aliases - else: - for alias in policy_data['policy_lookup'][policy_name]['policy_aliases']: - log.debug('checking alias %s', alias) - if alias in current_policy[policy_data['output_section']]: - currently_set = True - pol_id = alias - break if currently_set: # compare - log.debug('need to compare %s from ' - 'current/requested policy', policy_name) + log.debug('need to compare %s from current/requested ' + 'policy', p_name) changes = False - requested_policy_json = salt.utils.json.dumps(policy_data['requested_policy'][policy_name], sort_keys=True).lower() - current_policy_json = salt.utils.json.dumps(current_policy[policy_data['output_section']][pol_id], sort_keys=True).lower() + requested_policy_json = salt.utils.json.dumps( + p_data['requested_policy'][p_name], + sort_keys=True).lower() + current_policy_json = salt.utils.json.dumps( + current_policy[class_map[p_class]][p_name], + sort_keys=True).lower() requested_policy_check = salt.utils.json.loads(requested_policy_json) current_policy_check = salt.utils.json.loads(current_policy_json) @@ -310,35 +321,39 @@ def set_(name, if not policies_are_equal: additional_policy_comments = [] - if policy_data['policy_lookup'][policy_name]['rights_assignment'] and cumulative_rights_assignments: - for user in policy_data['requested_policy'][policy_name]: - if user not in current_policy[policy_data['output_section']][pol_id]: - changes = True + if p_data['policy_lookup'][p_name]['rights_assignment'] and cumulative_rights_assignments: + for user in p_data['requested_policy'][p_name]: + if user not in current_policy[class_map[p_class]][p_name]: + user = salt.utils.win_functions.get_sam_name(user) + if user not in current_policy[class_map[p_class]][p_name]: + changes = True + else: + additional_policy_comments.append('"{0}" is already granted the right'.format(user)) else: additional_policy_comments.append('"{0}" is already granted the right'.format(user)) else: changes = True if changes: log.debug('%s current policy != requested policy', - policy_name) + p_name) log.debug( 'we compared %s to %s', requested_policy_json, current_policy_json ) - policy_changes.append(policy_name) + policy_changes.append(p_name) else: if additional_policy_comments: - ret['comment'] = '"{0}" is already set ({1})\n'.format(policy_name, ', '.join(additional_policy_comments)) + ret['comment'] = '"{0}" is already set ({1})\n'.format(p_name, ', '.join(additional_policy_comments)) else: - ret['comment'] = '"{0}" is already set\n'.format(policy_name) + ret['comment'] + ret['comment'] = '"{0}" is already set\n'.format(p_name) + ret['comment'] else: log.debug('%s current setting matches ' - 'the requested setting', policy_name) - ret['comment'] = '"{0}" is already set\n'.format(policy_name) + ret['comment'] + 'the requested setting', p_name) + ret['comment'] = '"{0}" is already set\n'.format(p_name) + ret['comment'] else: - policy_changes.append(policy_name) + policy_changes.append(p_name) log.debug('policy %s is not set, we will configure it', - policy_name) + p_name) if __opts__['test']: if policy_changes: ret['result'] = None @@ -348,17 +363,26 @@ def set_(name, ret['comment'] = 'All specified policies are properly configured' else: if policy_changes: - _ret = __salt__['lgpo.set'](computer_policy=computer_policy, - user_policy=user_policy, - cumulative_rights_assignments=cumulative_rights_assignments, - adml_language=adml_language) + _ret = __salt__['lgpo.set']( + computer_policy=computer_policy, + user_policy=user_policy, + cumulative_rights_assignments=cumulative_rights_assignments, + adml_language=adml_language) if _ret: ret['result'] = _ret + new_policy = {} + for p_class, p_data in six.iteritems(pol_data): + if p_data['requested_policy']: + for p_name, p_setting in six.iteritems( + p_data['requested_policy']): + new_policy.setdefault(class_map[p_class], {}) + new_policy[class_map[p_class]][p_name] = __salt__['lgpo.get_policy']( + policy_name=p_name, + policy_class=p_class, + adml_language=adml_language, + return_value_only=True) ret['changes'] = salt.utils.dictdiffer.deep_diff( - current_policy, - __salt__['lgpo.get'](policy_class=policy_class, - adml_language=adml_language, - hierarchical_return=False)) + old=current_policy, new=new_policy) if ret['changes']: ret['comment'] = 'The following policies changed:\n{0}' \ ''.format('\n'.join(policy_changes)) diff --git a/salt/utils/win_reg.py b/salt/utils/win_reg.py index a7c07f1d082a..40c45c4d77aa 100644 --- a/salt/utils/win_reg.py +++ b/salt/utils/win_reg.py @@ -89,6 +89,8 @@ def _to_unicode(vdata): # None does not convert to Unicode if vdata is None: return None + if isinstance(vdata, int): + vdata = str(vdata) return salt.utils.stringutils.to_unicode(vdata, 'utf-8') @@ -522,7 +524,7 @@ def read_value(hive, key, vname=None, use_32bit_registry=False): try: # RegQueryValueEx returns and accepts unicode data vdata, vtype = win32api.RegQueryValueEx(handle, local_vname) - if vdata or vdata in [0, '']: + if vdata or vdata in [0, '', []]: # Only convert text types to unicode ret['vtype'] = registry.vtype_reverse[vtype] if vtype == win32con.REG_MULTI_SZ: diff --git a/tests/integration/modules/test_win_lgpo.py b/tests/integration/modules/test_win_lgpo.py index 70b31835379e..f4ec94651322 100644 --- a/tests/integration/modules/test_win_lgpo.py +++ b/tests/integration/modules/test_win_lgpo.py @@ -56,15 +56,16 @@ def _testRegistryPolicy(self, (policy_name, policy_config)) self.assertTrue(ret) val = reg.read_value( - registry_value_hive, - registry_value_path, - registry_value_vname) + hive=registry_value_hive, + key=registry_value_path, + vname=registry_value_vname) self.assertTrue(val['success'], msg='Failed to obtain the registry data for policy {0}'.format(policy_name)) if val['success']: - self.assertEqual(val['vdata'], expected_value_data, 'The registry value data {0} does not match the expected value {1} for policy {2}'.format( - val['vdata'], - expected_value_data, - policy_name)) + self.assertEqual( + val['vdata'], + expected_value_data, + 'The registry value data {0} does not match the expected value {1} for policy {2}'.format( + val['vdata'], expected_value_data, policy_name)) def _testSeceditPolicy(self, policy_name, @@ -180,12 +181,13 @@ class setup function, only runs once log.debug('Unable to get osrelease grain') if not os.path.exists(r'c:\windows\system32\lgpo.exe'): log.debug('lgpo.exe does not exist, attempting to download/extract') - ret = cls().run_function('state.single', - ('archive.extracted', r'c:\windows\system32'), - source='https://download.microsoft.com/download/8/5/C/85C25433-A1B0-4FFA-9429-7E023E7DA8D8/LGPO.zip', - archive_format='zip', - source_hash='sha256=6ffb6416366652993c992280e29faea3507b5b5aa661c33ba1af31f48acea9c4', - enforce_toplevel=False) + ret = cls().run_function( + 'state.single', + ('archive.extracted', r'c:\windows\system32'), + source='https://download.microsoft.com/download/8/5/C/85C25433-A1B0-4FFA-9429-7E023E7DA8D8/LGPO.zip', + archive_format='zip', + source_hash='sha256=6ffb6416366652993c992280e29faea3507b5b5aa661c33ba1af31f48acea9c4', + enforce_toplevel=False) log.debug('ret from archive.unzip == %s', ret) @destructiveTest @@ -208,13 +210,13 @@ def test_set_user_policy_point_and_print_restrictions(self): policy_class='User') # Enable Point and Print Restrictions self._testAdmxPolicy( - r'Control Panel\Printers\Point and Print Restrictions', + r'Point and Print Restrictions', { 'Users can only point and print to these servers': True, 'Enter fully qualified server names separated by semicolons': 'fakeserver1;fakeserver2', 'Users can only point and print to machines in their forest': True, 'Security Prompts: When installing drivers for a new connection': 'Show warning and elevation prompt', - 'When updating drivers for an existing connection': 'Do not show warning or elevation prompt', + 'When updating drivers for an existing connection': 'Show warning only', }, [ r'User[\s]*Software\\Policies\\Microsoft\\Windows NT\\Printers\\PointAndPrint[\s]*Restricted[\s]*DWORD:1', @@ -222,15 +224,14 @@ def test_set_user_policy_point_and_print_restrictions(self): r'User[\s]*Software\\Policies\\Microsoft\\Windows NT\\Printers\\PointAndPrint[\s]*ServerList[\s]*SZ:fakeserver1;fakeserver2', r'User[\s]*Software\\Policies\\Microsoft\\Windows NT\\Printers\\PointAndPrint[\s]*InForest[\s]*DWORD:1', r'User[\s]*Software\\Policies\\Microsoft\\Windows NT\\Printers\\PointAndPrint[\s]*NoWarningNoElevationOnInstall[\s]*DWORD:0', - r'User[\s]*Software\\Policies\\Microsoft\\Windows NT\\Printers\\PointAndPrint[\s]*UpdatePromptSettings[\s]*DWORD:2', + r'User[\s]*Software\\Policies\\Microsoft\\Windows NT\\Printers\\PointAndPrint[\s]*UpdatePromptSettings[\s]*DWORD:1', ], policy_class='User') # set Point and Print Restrictions to 'Not Configured' self._testAdmxPolicy( r'Control Panel\Printers\Point and Print Restrictions', 'Not Configured', - [ - r'; Source file: c:\\windows\\system32\\grouppolicy\\user\\registry.pol[\s]*; PARSING COMPLETED.'], + [r'; Source file: c:\\windows\\system32\\grouppolicy\\user\\registry.pol[\s]*; PARSING COMPLETED.'], policy_class='User') @destructiveTest @@ -239,41 +240,44 @@ def test_set_computer_policy_NTP_Client(self): Test setting/unsetting/changing NTP Client policies ''' # Disable Configure NTP Client - self._testAdmxPolicy(r'System\Windows Time Service\Time Providers\Configure Windows NTP Client', - 'Disabled', - [ - r'Computer[\s]*Software\\Policies\\Microsoft\\W32time\\Parameters[\s]*NtpServer[\s]*DELETE', - r'Computer[\s]*Software\\Policies\\Microsoft\\W32time\\Parameters[\s]*Type[\s]*DELETE', - r'Computer[\s]*Software\\Policies\\Microsoft\\W32time\\TimeProviders\\NtpClient[\s]*CrossSiteSyncFlags[\s]*DELETE', - r'Computer[\s]*Software\\Policies\\Microsoft\\W32time\\TimeProviders\\NtpClient[\s]*ResolvePeerBackoffMinutes[\s]*DELETE', - r'Computer[\s]*Software\\Policies\\Microsoft\\W32time\\TimeProviders\\NtpClient[\s]*ResolvePeerBackoffMaxTimes[\s]*DELETE', - r'Computer[\s]*Software\\Policies\\Microsoft\\W32time\\TimeProviders\\NtpClient[\s]*SpecialPollInterval[\s]*DELETE', - r'Computer[\s]*Software\\Policies\\Microsoft\\W32time\\TimeProviders\\NtpClient[\s]*EventLogFlags[\s]*DELETE' - ]) + self._testAdmxPolicy( + r'System\Windows Time Service\Time Providers\Configure Windows NTP Client', + 'Disabled', + [ + r'Computer[\s]*Software\\Policies\\Microsoft\\W32time\\Parameters[\s]*NtpServer[\s]*DELETE', + r'Computer[\s]*Software\\Policies\\Microsoft\\W32time\\Parameters[\s]*Type[\s]*DELETE', + r'Computer[\s]*Software\\Policies\\Microsoft\\W32time\\TimeProviders\\NtpClient[\s]*CrossSiteSyncFlags[\s]*DELETE', + r'Computer[\s]*Software\\Policies\\Microsoft\\W32time\\TimeProviders\\NtpClient[\s]*ResolvePeerBackoffMinutes[\s]*DELETE', + r'Computer[\s]*Software\\Policies\\Microsoft\\W32time\\TimeProviders\\NtpClient[\s]*ResolvePeerBackoffMaxTimes[\s]*DELETE', + r'Computer[\s]*Software\\Policies\\Microsoft\\W32time\\TimeProviders\\NtpClient[\s]*SpecialPollInterval[\s]*DELETE', + r'Computer[\s]*Software\\Policies\\Microsoft\\W32time\\TimeProviders\\NtpClient[\s]*EventLogFlags[\s]*DELETE' + ]) # Enable Configure NTP Client - self._testAdmxPolicy(r'System\Windows Time Service\Time Providers\Configure Windows NTP Client', - { - 'NtpServer': 'time.windows.com,0x9', - 'Type': 'NT5DS', - 'CrossSiteSyncFlags': 2, - 'ResolvePeerBackoffMinutes': 15, - 'ResolvePeerBackoffMaxTimes': 7, - 'W32TIME_SpecialPollInterval': 3600, - 'W32TIME_NtpClientEventLogFlags': 0 - }, - [ - r'Computer[\s]*Software\\Policies\\Microsoft\\W32time\\Parameters[\s]*NtpServer[\s]*SZ:time.windows.com,0x9', - r'Computer[\s]*Software\\Policies\\Microsoft\\W32time\\Parameters[\s]*Type[\s]*SZ:NT5DS', - r'Computer[\s]*Software\\Policies\\Microsoft\\W32time\\TimeProviders\\NtpClient[\s]*CrossSiteSyncFlags[\s]*DWORD:2', - r'Computer[\s]*Software\\Policies\\Microsoft\\W32time\\TimeProviders\\NtpClient[\s]*ResolvePeerBackoffMinutes[\s]*DWORD:15', - r'Computer[\s]*Software\\Policies\\Microsoft\\W32time\\TimeProviders\\NtpClient[\s]*ResolvePeerBackoffMaxTimes[\s]*DWORD:7', - r'Computer[\s]*Software\\Policies\\Microsoft\\W32time\\TimeProviders\\NtpClient[\s]*SpecialPollInterval[\s]*DWORD:3600', - r'Computer[\s]*Software\\Policies\\Microsoft\\W32time\\TimeProviders\\NtpClient[\s]*EventLogFlags[\s]*DWORD:0', - ]) + self._testAdmxPolicy( + r'System\Windows Time Service\Time Providers\Configure Windows NTP Client', + { + 'NtpServer': 'time.windows.com,0x9', + 'Type': 'NT5DS', + 'CrossSiteSyncFlags': 2, + 'ResolvePeerBackoffMinutes': 15, + 'ResolvePeerBackoffMaxTimes': 7, + 'W32TIME_SpecialPollInterval': 3600, + 'W32TIME_NtpClientEventLogFlags': 0 + }, + [ + r'Computer[\s]*Software\\Policies\\Microsoft\\W32time\\Parameters[\s]*NtpServer[\s]*SZ:time.windows.com,0x9', + r'Computer[\s]*Software\\Policies\\Microsoft\\W32time\\Parameters[\s]*Type[\s]*SZ:NT5DS', + r'Computer[\s]*Software\\Policies\\Microsoft\\W32time\\TimeProviders\\NtpClient[\s]*CrossSiteSyncFlags[\s]*DWORD:2', + r'Computer[\s]*Software\\Policies\\Microsoft\\W32time\\TimeProviders\\NtpClient[\s]*ResolvePeerBackoffMinutes[\s]*DWORD:15', + r'Computer[\s]*Software\\Policies\\Microsoft\\W32time\\TimeProviders\\NtpClient[\s]*ResolvePeerBackoffMaxTimes[\s]*DWORD:7', + r'Computer[\s]*Software\\Policies\\Microsoft\\W32time\\TimeProviders\\NtpClient[\s]*SpecialPollInterval[\s]*DWORD:3600', + r'Computer[\s]*Software\\Policies\\Microsoft\\W32time\\TimeProviders\\NtpClient[\s]*EventLogFlags[\s]*DWORD:0', + ]) # set Configure NTP Client to 'Not Configured' - self._testAdmxPolicy(r'System\Windows Time Service\Time Providers\Configure Windows NTP Client', - 'Not Configured', - [r'; Source file: c:\\windows\\system32\\grouppolicy\\machine\\registry.pol[\s]*; PARSING COMPLETED.']) + self._testAdmxPolicy( + r'System\Windows Time Service\Time Providers\Configure Windows NTP Client', + 'Not Configured', + [r'; Source file: c:\\windows\\system32\\grouppolicy\\machine\\registry.pol[\s]*; PARSING COMPLETED.']) @destructiveTest def test_set_computer_policy_RA_Unsolicit(self): @@ -283,32 +287,35 @@ def test_set_computer_policy_RA_Unsolicit(self): # Disable RA_Unsolicit log.debug('Attempting to disable RA_Unsolicit') - self._testAdmxPolicy('RA_Unsolicit', - 'Disabled', - [ - r'Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fAllowUnsolicited[\s]*DWORD:0', - r'Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fAllowUnsolicitedFullControl[\s]*DELETE', - r'Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services\\RAUnsolicit[\s]*\*[\s]*DELETEALLVALUES', - ]) + self._testAdmxPolicy( + 'RA_Unsolicit', + 'Disabled', + [ + r'Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fAllowUnsolicited[\s]*DWORD:0', + r'Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fAllowUnsolicitedFullControl[\s]*DELETE', + r'Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services\\RAUnsolicit[\s]*\*[\s]*DELETEALLVALUES', + ]) # configure RA_Unsolicit log.debug('Attempting to configure RA_Unsolicit') - self._testAdmxPolicy('RA_Unsolicit', - { - 'Configure Offer Remote Access': 'Enabled', - 'Permit remote control of this computer': 'Allow helpers to remotely control the computer', - 'Helpers': ['administrators', 'user1'] - }, - [ - r'Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services\\RAUnsolicit[\s]*user1[\s]*SZ:user1[\s]*', - r'Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services\\RAUnsolicit[\s]*administrators[\s]*SZ:administrators[\s]*', - r'Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fAllowUnsolicited[\s]*DWORD:1', - r'Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fAllowUnsolicitedFullControl[\s]*DWORD:1', - ]) + self._testAdmxPolicy( + 'RA_Unsolicit', + { + 'Configure Offer Remote Access': 'Enabled', + 'Permit remote control of this computer': 'Allow helpers to remotely control the computer', + 'Helpers': ['administrators', 'user1'] + }, + [ + r'Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services\\RAUnsolicit[\s]*user1[\s]*SZ:user1[\s]*', + r'Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services\\RAUnsolicit[\s]*administrators[\s]*SZ:administrators[\s]*', + r'Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fAllowUnsolicited[\s]*DWORD:1', + r'Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fAllowUnsolicitedFullControl[\s]*DWORD:1', + ]) # Not Configure RA_Unsolicit log.debug('Attempting to set RA_Unsolicit to Not Configured') - self._testAdmxPolicy('RA_Unsolicit', - 'Not Configured', - [r'; Source file: c:\\windows\\system32\\grouppolicy\\machine\\registry.pol[\s]*; PARSING COMPLETED.']) + self._testAdmxPolicy( + 'RA_Unsolicit', + 'Not Configured', + [r'; Source file: c:\\windows\\system32\\grouppolicy\\machine\\registry.pol[\s]*; PARSING COMPLETED.']) @destructiveTest def test_set_computer_policy_Pol_HardenedPaths(self): @@ -344,78 +351,141 @@ def test_set_computer_policy_WindowsUpdate(self): ''' Test setting/unsetting/changing WindowsUpdate policy ''' - the_policy = { - 'Configure automatic updating': '4 - Auto download and schedule the install', - 'Install during automatic maintenance': False, - 'Scheduled install day': '7 - Every Saturday', - 'Scheduled install time': '17:00', - 'Install updates for other Microsoft products': True - } - the_policy_check = [ + # Configure Automatic Updates has different options in different builds + # and releases of Windows, so we'll get the elements and add them if + # they are present. Newer elements will need to be added manually as + # they are released by Microsoft + result = self.run_function( + 'lgpo.get_policy_info', + ['Configure Automatic Updates'], + policy_class='machine' + ) + the_policy = {} + the_policy_check_enabled = [ r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*NoAutoUpdate[\s]*DWORD:0', - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*AUOptions[\s]*DWORD:4', - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*AutomaticMaintenanceEnabled[\s]*DELETE', - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallDay[\s]*DWORD:7', - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallTime[\s]*DWORD:17', - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*AllowMUUpdateService[\s]*DWORD:1\s*' ] - - # Configure Automatic Updates has different options in 2016 than in 2012 - # and has only one boolean item, so we'll test it "False" in this block - # and then "True" in next block - if self.osrelease in ['2012Server', '2012ServerR2']: - the_policy = { - 'Configure automatic updating': '4 - Auto download and schedule the install', - 'Install during automatic maintenance': False, - 'Schedule install day': '7 - Every Saturday', - 'Schedule install time': '17:00', - } - the_policy_check = [ - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*NoAutoUpdate[\s]*DWORD:0', - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*AUOptions[\s]*DWORD:4', - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*AutomaticMaintenanceEnabled[\s]*DELETE', - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallDay[\s]*DWORD:7', - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallTime[\s]*DWORD:17', - ] - # test as False - self._testAdmxPolicy(r'Windows Components\Windows Update\Configure Automatic Updates', - the_policy, - the_policy_check) - # configure as True for "enable Automatic Updates" test below - the_policy = { - 'Configure automatic updating': '4 - Auto download and schedule the install', - 'Install during automatic maintenance': True, - 'Schedule install day': '7 - Every Saturday', - 'Schedule install time': '17:00', - } - the_policy_check = [ - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*NoAutoUpdate[\s]*DWORD:0', - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*AUOptions[\s]*DWORD:4', - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*AutomaticMaintenanceEnabled[\s]*DWORD:1\s*', - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallDay[\s]*DWORD:7', - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallTime[\s]*DWORD:17', - ] + the_policy_check_disabled = [ + r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*NoAutoUpdate[\s]*DWORD:1', + ] + for item in result['policy_elements']: + if 'Configure automatic updating' in item['element_aliases']: + the_policy.update({ + 'Configure automatic updating': '4 - Auto download and schedule the install', + }) + the_policy_check_enabled.append( + r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*AUOptions[\s]*DWORD:4', + ) + the_policy_check_disabled.append( + r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*AUOptions[\s]*DELETE', + ) + elif 'Install during automatic maintenance' in item['element_aliases']: + the_policy.update({ + 'Install during automatic maintenance': True, + }) + the_policy_check_enabled.append( + r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*AutomaticMaintenanceEnabled[\s]*DWORD:1\s*', + ) + the_policy_check_disabled.append( + r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*AutomaticMaintenanceEnabled[\s]*DELETE', + ) + elif 'Scheduled install day' in item['element_aliases']: + the_policy.update({ + 'Scheduled install day': '7 - Every Saturday', + }) + the_policy_check_enabled.append( + r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallDay[\s]*DWORD:7', + ) + the_policy_check_disabled.append( + r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallDay[\s]*DELETE', + ) + elif 'Scheduled install time' in item['element_aliases']: + the_policy.update({ + 'Scheduled install time': '17:00', + }) + the_policy_check_enabled.append( + r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallTime[\s]*DWORD:17', + ) + the_policy_check_disabled.append( + r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallTime[\s]*DELETE', + ) + elif 'Install updates for other Microsoft products' in item['element_aliases']: + the_policy.update({ + 'Install updates for other Microsoft products': True + }) + the_policy_check_enabled.append( + r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*AllowMUUpdateService[\s]*DWORD:1\s*' + ) + the_policy_check_disabled.append( + r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*AllowMUUpdateService[\s]*DELETE' + ) + elif 'AutoUpdateSchEveryWeek' in item['element_aliases']: + the_policy.update({ + 'AutoUpdateSchEveryWeek': True + }) + the_policy_check_enabled.append( + r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallEveryWeek[\s]*DWORD:1\s*' + ) + the_policy_check_disabled.append( + r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallEveryWeek[\s]*DELETE' + ) + elif 'First week of the month' in item['element_aliases']: + the_policy.update({ + 'First week of the month': True + }) + the_policy_check_enabled.append( + r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallFirstWeek[\s]*DWORD:1\s*' + ) + the_policy_check_disabled.append( + r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallFirstWeek[\s]*DELETE' + ) + elif 'Second week of the month' in item['element_aliases']: + the_policy.update({ + 'Second week of the month': True + }) + the_policy_check_enabled.append( + r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallSecondWeek[\s]*DWORD:1\s*' + ) + the_policy_check_disabled.append( + r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallSecondWeek[\s]*DELETE' + ) + elif 'Third week of the month' in item['element_aliases']: + the_policy.update({ + 'Third week of the month': True + }) + the_policy_check_enabled.append( + r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallThirdWeek[\s]*DWORD:1\s*' + ) + the_policy_check_disabled.append( + r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallThirdWeek[\s]*DELETE' + ) + elif 'Fourth week of the month' in item['element_aliases']: + the_policy.update({ + 'Fourth week of the month': True + }) + the_policy_check_enabled.append( + r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallFourthWeek[\s]*DWORD:1\s*' + ) + the_policy_check_disabled.append( + r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallFourthWeek[\s]*DELETE' + ) # enable Automatic Updates - self._testAdmxPolicy(r'Windows Components\Windows Update\Configure Automatic Updates', - the_policy, - the_policy_check) + self._testAdmxPolicy( + r'Windows Components\Windows Update\Configure Automatic Updates', + the_policy, + the_policy_check_enabled) # disable Configure Automatic Updates - self._testAdmxPolicy(r'Windows Components\Windows Update\Configure Automatic Updates', - 'Disabled', - [ - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*NoAutoUpdate[\s]*DWORD:1', - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*AUOptions[\s]*DELETE', - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*AutomaticMaintenanceEnabled[\s]*DELETE', - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallDay[\s]*DELETE', - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallTime[\s]*DELETE', - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*AllowMUUpdateService[\s]*DELETE' - ]) + self._testAdmxPolicy( + r'Windows Components\Windows Update\Configure Automatic Updates', + 'Disabled', + the_policy_check_disabled) + # set Configure Automatic Updates to 'Not Configured' - self._testAdmxPolicy(r'Windows Components\Windows Update\Configure Automatic Updates', - 'Not Configured', - [r'; Source file: c:\\windows\\system32\\grouppolicy\\machine\\registry.pol[\s]*; PARSING COMPLETED.']) + self._testAdmxPolicy( + r'Windows Components\Windows Update\Configure Automatic Updates', + 'Not Configured', + [r'; Source file: c:\\windows\\system32\\grouppolicy\\machine\\registry.pol[\s]*; PARSING COMPLETED.']) @destructiveTest def test_set_computer_policy_ClipboardRedirection(self): @@ -423,15 +493,18 @@ def test_set_computer_policy_ClipboardRedirection(self): Test setting/unsetting/changing ClipboardRedirection policy ''' # Enable/Disable/Not Configured "Do not allow Clipboard redirection" - self._testAdmxPolicy(r'Windows Components\Remote Desktop Services\Remote Desktop Session Host\Device and Resource Redirection\Do not allow Clipboard redirection', - 'Enabled', - [r'Computer[\s]*Software\\Policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fDisableClip[\s]*DWORD:1']) - self._testAdmxPolicy(r'Windows Components\Remote Desktop Services\Remote Desktop Session Host\Device and Resource Redirection\Do not allow Clipboard redirection', - 'Disabled', - [r'Computer[\s]*Software\\Policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fDisableClip[\s]*DWORD:0']) - self._testAdmxPolicy(r'Windows Components\Remote Desktop Services\Remote Desktop Session Host\Device and Resource Redirection\Do not allow Clipboard redirection', - 'Not Configured', - [r'; Source file: c:\\windows\\system32\\grouppolicy\\machine\\registry.pol[\s]*; PARSING COMPLETED.']) + self._testAdmxPolicy( + r'Windows Components\Remote Desktop Services\Remote Desktop Session Host\Device and Resource Redirection\Do not allow Clipboard redirection', + 'Enabled', + [r'Computer[\s]*Software\\Policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fDisableClip[\s]*DWORD:1']) + self._testAdmxPolicy( + r'Windows Components\Remote Desktop Services\Remote Desktop Session Host\Device and Resource Redirection\Do not allow Clipboard redirection', + 'Disabled', + [r'Computer[\s]*Software\\Policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fDisableClip[\s]*DWORD:0']) + self._testAdmxPolicy( + r'Windows Components\Remote Desktop Services\Remote Desktop Session Host\Device and Resource Redirection\Do not allow Clipboard redirection', + 'Not Configured', + [r'; Source file: c:\\windows\\system32\\grouppolicy\\machine\\registry.pol[\s]*; PARSING COMPLETED.']) @destructiveTest def test_set_computer_policy_PasswordComplexity(self): @@ -439,13 +512,15 @@ def test_set_computer_policy_PasswordComplexity(self): Test setting/unsetting/changing PasswordComplexity ''' # disable PasswordComplexity - self._testSeceditPolicy('Password must meet complexity requirements', - 'Disabled', - [r'^PasswordComplexity = 0']) + self._testSeceditPolicy( + 'Password must meet complexity requirements', + 'Disabled', + [r'^PasswordComplexity = 0']) # enable PasswordComplexity - self._testSeceditPolicy('PasswordComplexity', - 'Enabled', - [r'^PasswordComplexity = 1']) + self._testSeceditPolicy( + 'PasswordComplexity', + 'Enabled', + [r'^PasswordComplexity = 1']) @destructiveTest def test_set_computer_policy_PasswordLen(self): @@ -453,13 +528,15 @@ def test_set_computer_policy_PasswordLen(self): Test setting/unsetting/changing PasswordLength ''' # set Minimum password length - self._testSeceditPolicy('Minimum password length', - 10, - [r'^MinimumPasswordLength = 10']) + self._testSeceditPolicy( + 'Minimum password length', + 10, + [r'^MinimumPasswordLength = 10']) # set MinimumPasswordLength = 0 - self._testSeceditPolicy('MinPasswordLen', - 0, - [r'^MinimumPasswordLength = 0']) + self._testSeceditPolicy( + 'MinPasswordLen', + 0, + [r'^MinimumPasswordLength = 0']) @destructiveTest def test_set_computer_policy_SeNetworkLogonRight(self): @@ -467,14 +544,16 @@ def test_set_computer_policy_SeNetworkLogonRight(self): Test setting/unsetting/changing PasswordLength ''' # set SeNetworkLogonRight to only Administrators - self._testSeceditPolicy('Access this computer from the network', - ['Administrators'], - [r'^SeNetworkLogonRight = \*S-1-5-32-544'], - cumulative_rights_assignments=False) + self._testSeceditPolicy( + 'Access this computer from the network', + ['Administrators'], + [r'^SeNetworkLogonRight = \*S-1-5-32-544'], + cumulative_rights_assignments=False) # set SeNetworkLogonRight back to the default - self._testSeceditPolicy('SeNetworkLogonRight', - ['Everyone', 'Administrators', 'Users', 'Backup Operators'], - [r'^SeNetworkLogonRight = \*S-1-1-0,\*S-1-5-32-544,\*S-1-5-32-545,\*S-1-5-32-551']) + self._testSeceditPolicy( + 'SeNetworkLogonRight', + ['Everyone', 'Administrators', 'Users', 'Backup Operators'], + [r'^SeNetworkLogonRight = \*S-1-1-0,\*S-1-5-32-544,\*S-1-5-32-545,\*S-1-5-32-551']) @destructiveTest def test_set_computer_policy_multipleAdmxPolicies(self): @@ -482,78 +561,83 @@ def test_set_computer_policy_multipleAdmxPolicies(self): Tests setting several ADMX policies in succession and validating the configuration w/lgop ''' # set one policy - self._testAdmxPolicy(r'Windows Components\Remote Desktop Services\Remote Desktop Session Host\Device and Resource Redirection\Do not allow Clipboard redirection', - 'Disabled', - [r'Computer[\s]*Software\\Policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fDisableClip[\s]*DWORD:0']) + self._testAdmxPolicy( + r'Windows Components\Remote Desktop Services\Remote Desktop Session Host\Device and Resource Redirection\Do not allow Clipboard redirection', + 'Disabled', + [r'Computer[\s]*Software\\Policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fDisableClip[\s]*DWORD:0']) # set another policy and make sure both this policy and the previous are okay - self._testAdmxPolicy('RA_Unsolicit', - { - 'Configure Offer Remote Access': 'Enabled', - 'Permit remote control of this computer': 'Allow helpers to remotely control the computer', - 'Helpers': ['administrators', 'user1'] - }, - [ - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fDisableClip[\s]*DWORD:0', - r'Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services\\RAUnsolicit[\s]*user1[\s]*SZ:user1[\s]*', - r'Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services\\RAUnsolicit[\s]*administrators[\s]*SZ:administrators[\s]*', - r'Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fAllowUnsolicited[\s]*DWORD:1', - r'Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fAllowUnsolicitedFullControl[\s]*DWORD:1', - ]) + self._testAdmxPolicy( + 'RA_Unsolicit', + { + 'Configure Offer Remote Access': 'Enabled', + 'Permit remote control of this computer': 'Allow helpers to remotely control the computer', + 'Helpers': ['administrators', 'user1'] + }, + [ + r'Computer[\s]*Software\\Policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fDisableClip[\s]*DWORD:0', + r'Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services\\RAUnsolicit[\s]*user1[\s]*SZ:user1[\s]*', + r'Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services\\RAUnsolicit[\s]*administrators[\s]*SZ:administrators[\s]*', + r'Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fAllowUnsolicited[\s]*DWORD:1', + r'Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fAllowUnsolicitedFullControl[\s]*DWORD:1', + ]) # Configure Automatic Updates and validate everything is still okay - self._testAdmxPolicy(r'Windows Components\Windows Update\Configure Automatic Updates', - 'Disabled', - [ - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fDisableClip[\s]*DWORD:0', - r'Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services\\RAUnsolicit[\s]*user1[\s]*SZ:user1[\s]*', - r'Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services\\RAUnsolicit[\s]*administrators[\s]*SZ:administrators[\s]*', - r'Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fAllowUnsolicited[\s]*DWORD:1', - r'Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fAllowUnsolicitedFullControl[\s]*DWORD:1', - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*NoAutoUpdate[\s]*DWORD:1', - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*AUOptions[\s]*DELETE', - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*AutomaticMaintenanceEnabled[\s]*DELETE', - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallDay[\s]*DELETE', - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallTime[\s]*DELETE', - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*AllowMUUpdateService[\s]*DELETE' - ]) + self._testAdmxPolicy( + r'Windows Components\Windows Update\Configure Automatic Updates', + 'Disabled', + [ + r'Computer[\s]*Software\\Policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fDisableClip[\s]*DWORD:0', + r'Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services\\RAUnsolicit[\s]*user1[\s]*SZ:user1[\s]*', + r'Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services\\RAUnsolicit[\s]*administrators[\s]*SZ:administrators[\s]*', + r'Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fAllowUnsolicited[\s]*DWORD:1', + r'Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fAllowUnsolicitedFullControl[\s]*DWORD:1', + r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*NoAutoUpdate[\s]*DWORD:1', + r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*AUOptions[\s]*DELETE', + r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*AutomaticMaintenanceEnabled[\s]*DELETE', + r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallDay[\s]*DELETE', + r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallTime[\s]*DELETE', + r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*AllowMUUpdateService[\s]*DELETE' + ]) @destructiveTest def test_set_computer_policy_DisableDomainCreds(self): ''' Tests Enable/Disable of DisableDomainCreds policy ''' - self._testRegistryPolicy('DisableDomainCreds', - 'Enabled', - 'HKEY_LOCAL_MACHINE', - 'SYSTEM\\CurrentControlSet\\Control\\Lsa', - 'DisableDomainCreds', - 1) self._testRegistryPolicy( - 'Network access: Do not allow storage of passwords and credentials for network authentication', - 'Disabled', - 'HKEY_LOCAL_MACHINE', - 'SYSTEM\\CurrentControlSet\\Control\\Lsa', - 'DisableDomainCreds', - 0) + policy_name='DisableDomainCreds', + policy_config='Enabled', + registry_value_hive='HKEY_LOCAL_MACHINE', + registry_value_path='SYSTEM\\CurrentControlSet\\Control\\Lsa', + registry_value_vname='DisableDomainCreds', + expected_value_data=1) + self._testRegistryPolicy( + policy_name='Network access: Do not allow storage of passwords and credentials for network authentication', + policy_config='Disabled', + registry_value_hive='HKEY_LOCAL_MACHINE', + registry_value_path='SYSTEM\\CurrentControlSet\\Control\\Lsa', + registry_value_vname='DisableDomainCreds', + expected_value_data=0) @destructiveTest def test_set_computer_policy_ForceGuest(self): ''' Tests changing ForceGuest policy ''' - self._testRegistryPolicy('ForceGuest', - 'Guest only - local users authenticate as Guest', - 'HKEY_LOCAL_MACHINE', - 'SYSTEM\\CurrentControlSet\\Control\\Lsa', - 'ForceGuest', - 1) self._testRegistryPolicy( - 'Network access: Sharing and security model for local accounts', - 'Classic - local users authenticate as themselves', - 'HKEY_LOCAL_MACHINE', - 'SYSTEM\\CurrentControlSet\\Control\\Lsa', - 'ForceGuest', - 0) + policy_name='ForceGuest', + policy_config='Guest only - local users authenticate as Guest', + registry_value_hive='HKEY_LOCAL_MACHINE', + registry_value_path='SYSTEM\\CurrentControlSet\\Control\\Lsa', + registry_value_vname='ForceGuest', + expected_value_data=1) + self._testRegistryPolicy( + policy_name='Network access: Sharing and security model for local accounts', + policy_config='Classic - local users authenticate as themselves', + registry_value_hive='HKEY_LOCAL_MACHINE', + registry_value_path='SYSTEM\\CurrentControlSet\\Control\\Lsa', + registry_value_vname='ForceGuest', + expected_value_data=0) @destructiveTest def test_set_computer_policy_DisableUXWUAccess(self): @@ -566,34 +650,41 @@ def test_set_computer_policy_DisableUXWUAccess(self): if self.osrelease not in valid_osreleases: self.skipTest('DisableUXWUAccess policy is only applicable if the osrelease grain is {0}'.format(' or '.join(valid_osreleases))) else: - self._testAdmxPolicy(r'DisableUXWUAccess', - 'Enabled', - [r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate[\s]*SetDisableUXWUAccess[\s]*DWORD:1']) - self._testAdmxPolicy(r'Remove access to use all Windows Update features', - 'Disabled', - [r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate[\s]*SetDisableUXWUAccess[\s]*DWORD:0']) - self._testAdmxPolicy(r'Windows Components\Windows Update\Remove access to use all Windows Update features', - 'Not Configured', - [r'; Source file: c:\\windows\\system32\\grouppolicy\\machine\\registry.pol[\s]*; PARSING COMPLETED.']) + self._testAdmxPolicy( + r'DisableUXWUAccess', + 'Enabled', + [r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate[\s]*SetDisableUXWUAccess[\s]*DWORD:1']) + self._testAdmxPolicy( + r'Remove access to use all Windows Update features', + 'Disabled', + [r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate[\s]*SetDisableUXWUAccess[\s]*DWORD:0']) + self._testAdmxPolicy( + r'Windows Components\Windows Update\Remove access to use all Windows Update features', + 'Not Configured', + [r'; Source file: c:\\windows\\system32\\grouppolicy\\machine\\registry.pol[\s]*; PARSING COMPLETED.']) @destructiveTest def test_set_computer_policy_Access_data_sources_across_domains(self): ''' Tests that a policy that has multiple names ''' - self._testAdmxPolicy(r'Access data sources across domains', - 'Enabled', - [], - assert_true=False) - self._testAdmxPolicy(r'Windows Components\Internet Explorer\Internet Control Panel\Security Page\Internet Zone\Access data sources across domains', - {'Access data sources across domains': 'Prompt'}, - [r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Zones\\3[\s]*1406[\s]*DWORD:1']) - self._testAdmxPolicy(r'Windows Components\Internet Explorer\Internet Control Panel\Security Page\Internet Zone\Access data sources across domains', - {'Access data sources across domains': 'Enable'}, - [r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Zones\\3[\s]*1406[\s]*DWORD:0']) - self._testAdmxPolicy(r'Windows Components\Internet Explorer\Internet Control Panel\Security Page\Internet Zone\Access data sources across domains', - 'Disabled', - [r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Zones\\3[\s]*1406[\s]*DELETE']) + self._testAdmxPolicy( + r'Access data sources across domains', + 'Enabled', + [], + assert_true=False) + self._testAdmxPolicy( + r'Windows Components\Internet Explorer\Internet Control Panel\Security Page\Internet Zone\Access data sources across domains', + {'Access data sources across domains': 'Prompt'}, + [r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Zones\\3[\s]*1406[\s]*DWORD:1']) + self._testAdmxPolicy( + r'Windows Components\Internet Explorer\Internet Control Panel\Security Page\Internet Zone\Access data sources across domains', + {'Access data sources across domains': 'Enable'}, + [r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Zones\\3[\s]*1406[\s]*DWORD:0']) + self._testAdmxPolicy( + r'Windows Components\Internet Explorer\Internet Control Panel\Security Page\Internet Zone\Access data sources across domains', + 'Disabled', + [r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Zones\\3[\s]*1406[\s]*DELETE']) @destructiveTest def test_set_computer_policy_ActiveHours(self): @@ -612,30 +703,34 @@ def test_set_computer_policy_ActiveHours(self): if self.osrelease not in valid_osreleases: self.skipTest('ActiveHours policy is only applicable if the osrelease grain is {0}'.format(' or '.join(valid_osreleases))) else: - self._testAdmxPolicy(r'ActiveHours', - {'ActiveHoursStartTime': '8 AM', 'ActiveHoursEndTime': '7 PM'}, - [ - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate[\s]*SetActiveHours[\s]*DWORD:1', - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate[\s]*ActiveHoursStart[\s]*DWORD:8', - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate[\s]*ActiveHoursEnd[\s]*DWORD:19' - ]) - self._testAdmxPolicy(r'ActiveHours', - {'ActiveHoursStartTime': '5 AM', 'ActiveHoursEndTime': '10 PM'}, - [ - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate[\s]*SetActiveHours[\s]*DWORD:1', - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate[\s]*ActiveHoursStart[\s]*DWORD:5', - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate[\s]*ActiveHoursEnd[\s]*DWORD:22' - ]) - self._testAdmxPolicy('Turn off auto-restart for updates during active hours', - 'Disabled', - [ - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate[\s]*SetActiveHours[\s]*DWORD:0', - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate[\s]*ActiveHoursStart[\s]*DELETE', - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate[\s]*ActiveHoursEnd[\s]*DELETE' - ]) - self._testAdmxPolicy(r'Windows Components\Windows Update\Turn off auto-restart for updates during active hours', - 'Not Configured', - [r'; Source file: c:\\windows\\system32\\grouppolicy\\machine\\registry.pol[\s]*; PARSING COMPLETED.']) + self._testAdmxPolicy( + r'ActiveHours', + {'ActiveHoursStartTime': '8 AM', 'ActiveHoursEndTime': '7 PM'}, + [ + r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate[\s]*SetActiveHours[\s]*DWORD:1', + r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate[\s]*ActiveHoursStart[\s]*DWORD:8', + r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate[\s]*ActiveHoursEnd[\s]*DWORD:19' + ]) + self._testAdmxPolicy( + r'ActiveHours', + {'ActiveHoursStartTime': '5 AM', 'ActiveHoursEndTime': '10 PM'}, + [ + r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate[\s]*SetActiveHours[\s]*DWORD:1', + r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate[\s]*ActiveHoursStart[\s]*DWORD:5', + r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate[\s]*ActiveHoursEnd[\s]*DWORD:22' + ]) + self._testAdmxPolicy( + 'Turn off auto-restart for updates during active hours', + 'Disabled', + [ + r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate[\s]*SetActiveHours[\s]*DWORD:0', + r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate[\s]*ActiveHoursStart[\s]*DELETE', + r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate[\s]*ActiveHoursEnd[\s]*DELETE' + ]) + self._testAdmxPolicy( + r'Windows Components\Windows Update\Turn off auto-restart for updates during active hours', + 'Not Configured', + [r'; Source file: c:\\windows\\system32\\grouppolicy\\machine\\registry.pol[\s]*; PARSING COMPLETED.']) @destructiveTest def test_set_computer_policy_AllowTelemetry(self): @@ -653,21 +748,53 @@ def test_set_computer_policy_AllowTelemetry(self): {'AllowTelemetry': '1 - Basic'}, [r'Software\\Policies\\Microsoft\\Windows\\DataCollection[\s]*AllowTelemetry[\s]*DWORD:1'], assert_true=True) + # This policy does not exist on newer Windows builds result = self.run_function( - 'state.single', - ['lgpo.set'], - name='state', - computer_policy={ - 'Disable pre-release features or settings': 'Disabled' - } - ) - name = 'lgpo_|-state_|-state_|-set' - expected = { - 'new': { - 'Computer Configuration': { - 'Windows Components\\Data Collection and Preview Builds\\Disable pre-release features or settings': 'Disabled'}}, - 'old': {'Computer Configuration': {}}} - self.assertDictEqual(result[name]['changes'], expected) + 'lgpo.get_policy_info', + ['Disable pre-release features or settings'], + policy_class='machine') + if result['policy_found']: + result = self.run_function( + 'state.single', + ['lgpo.set'], + name='state', + computer_policy={ + 'Disable pre-release features or settings': 'Disabled' + } + ) + name = 'lgpo_|-state_|-state_|-set' + expected = { + 'new': { + 'Computer Configuration': { + 'Disable pre-release features or settings': 'Disabled'}}, + 'old': { + 'Computer Configuration': { + 'Disable pre-release features or settings': 'Not Configured'}}} + self.assertDictEqual(result[name]['changes'], expected) + else: + result = self.run_function( + 'lgpo.get_policy_info', + ['Manage preview builds'], + policy_class='machine' + ) + if result['policy_found']: + result = self.run_function( + 'state.single', + ['lgpo.set'], + name='state', + computer_policy={ + 'Manage preview builds': 'Disabled' + } + ) + name = 'lgpo_|-state_|-state_|-set' + expected = { + 'new': { + 'Computer Configuration': { + 'Manage preview builds': 'Disabled'}}, + 'old': { + 'Computer Configuration': { + 'Manage preview builds': 'Not Configured'}}} + self.assertDictEqual(result[name]['changes'], expected) def tearDown(self): ''' diff --git a/tests/unit/modules/test_aptpkg.py b/tests/unit/modules/test_aptpkg.py index c5e5310357ae..1ef1a37fd2f7 100644 --- a/tests/unit/modules/test_aptpkg.py +++ b/tests/unit/modules/test_aptpkg.py @@ -523,6 +523,32 @@ def test_show(self): self.assert_called_once(refresh_mock) refresh_mock.reset_mock() + @patch('salt.utils.path.os_walk', MagicMock(return_value=[('test', 'test', 'test')])) + @patch('os.path.getsize', MagicMock(return_value=123456)) + @patch('os.path.getctime', MagicMock(return_value=1234567890.123456)) + @patch('fnmatch.filter', MagicMock(return_value=['/var/cache/apt/archive/test_package.rpm'])) + def test_list_downloaded(self): + ''' + Test downloaded packages listing. + :return: + ''' + DOWNLOADED_RET = { + 'test-package': { + '1.0': { + 'path': '/var/cache/apt/archive/test_package.rpm', + 'size': 123456, + 'creation_date_time_t': 1234567890, + 'creation_date_time': '2009-02-13T23:31:30', + } + } + } + + with patch.dict(aptpkg.__salt__, {'lowpkg.bin_pkg_info': MagicMock(return_value={'name': 'test-package', + 'version': '1.0'})}): + list_downloaded = aptpkg.list_downloaded() + self.assertEqual(len(list_downloaded), 1) + self.assertDictEqual(list_downloaded, DOWNLOADED_RET) + @skipIf(pytest is None, 'PyTest is missing') class AptUtilsTestCase(TestCase, LoaderModuleMockMixin): diff --git a/tests/unit/modules/test_win_lgpo.py b/tests/unit/modules/test_win_lgpo.py index 7ed71a3ffdc6..719280254b54 100644 --- a/tests/unit/modules/test_win_lgpo.py +++ b/tests/unit/modules/test_win_lgpo.py @@ -5,18 +5,43 @@ # Import Python Libs from __future__ import absolute_import, unicode_literals, print_function +import os # Import Salt Testing Libs +from tests.support.helpers import destructiveTest from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import TestCase, skipIf -from tests.support.mock import MagicMock, patch # Import Salt Libs +import salt.config +import salt.modules.cmdmod +import salt.modules.file +import salt.modules.win_file as win_file import salt.modules.win_lgpo as win_lgpo import salt.utils.platform +import salt.utils.win_dacl +import salt.utils.win_lgpo_auditpol +import salt.utils.win_reg +LOADER_DICTS = { + win_lgpo: { + '__salt__': { + 'file.file_exists': salt.modules.file.file_exists, + 'file.makedirs': win_file.makedirs_, + 'file.write': salt.modules.file.write, + 'file.remove': win_file.remove, + 'cmd.run': salt.modules.cmdmod.run}, + '__opts__': salt.config.DEFAULT_MINION_OPTS.copy(), + '__utils__': { + 'reg.read_value': salt.utils.win_reg.read_value, + 'auditpol.get_auditpol_dump': + salt.utils.win_lgpo_auditpol.get_auditpol_dump}}, + win_file: { + '__utils__': { + 'dacl.set_perms': salt.utils.win_dacl.set_perms}}} -class WinSystemTestCase(TestCase): + +class WinLGPOTestCase(TestCase): ''' Test cases for salt.modules.win_lgpo ''' @@ -57,206 +82,615 @@ def test__encode_string_none(self): value = win_lgpo._encode_string(None) self.assertEqual(value, self.encoded_null) + def test__multi_string_get_transform_list(self): + ''' + ``_multi_string_get_transform`` should return the list when a list is + passed + ''' + test_value = ['Spongebob', 'Squarepants'] + value = win_lgpo._policy_info._multi_string_get_transform(item=test_value) + self.assertEqual(value, test_value) + + def test__multi_string_get_transform_none(self): + ''' + ``_multi_string_get_transform`` should return "Not Defined" when + ``None`` is passed + ''' + test_value = None + value = win_lgpo._policy_info._multi_string_get_transform(item=test_value) + self.assertEqual(value, 'Not Defined') + + def test__multi_string_get_transform_invalid(self): + ''' + ``_multi_string_get_transform`` should return "Not Defined" when + ``None`` is passed + ''' + test_value = 'Some String' + value = win_lgpo._policy_info._multi_string_get_transform(item=test_value) + self.assertEqual(value, 'Invalid Value') -@skipIf(not salt.utils.platform.is_windows(), 'Not a Windows system') -class WinLgpoNetShTestCase(TestCase, LoaderModuleMockMixin): + def test__multi_string_put_transform_list(self): + ''' + ``_multi_string_put_transform`` should return the list when a list is + passed + ''' + test_value = ['Spongebob', 'Squarepants'] + value = win_lgpo._policy_info._multi_string_put_transform(item=test_value) + self.assertEqual(value, test_value) + + def test__multi_string_put_transform_none(self): + ''' + ``_multi_string_put_transform`` should return ``None`` when + "Not Defined" is passed + ''' + test_value = "Not Defined" + value = win_lgpo._policy_info._multi_string_put_transform(item=test_value) + self.assertEqual(value, None) + + def test__multi_string_put_transform_list_from_string(self): + ''' + ``_multi_string_put_transform`` should return a list when a comma + delimited string is passed + ''' + test_value = "Spongebob,Squarepants" + value = win_lgpo._policy_info._multi_string_put_transform(item=test_value) + self.assertEqual(value, ['Spongebob', 'Squarepants']) + + def test__multi_string_put_transform_invalid(self): + ''' + ``_multi_string_put_transform`` should return "Invalid" value if neither + string nor list is passed + ''' + test_value = None + value = win_lgpo._policy_info._multi_string_put_transform(item=test_value) + self.assertEqual(value, "Invalid Value") + + +@skipIf(not salt.utils.platform.is_windows(), 'System is not Windows') +class WinLGPOGetPolicyADMXTestCase(TestCase, LoaderModuleMockMixin): ''' - NetSH test cases + Test functions related to the ``get_policy`` function using policy templates + (admx/adml) ''' + def setup_loader_modules(self): + return LOADER_DICTS + + def test_get_policy_name(self): + result = win_lgpo.get_policy(policy_name='Allow Telemetry', + policy_class='machine', + return_value_only=True, + return_full_policy_names=True, + hierarchical_return=False) + expected = 'Not Configured' + self.assertEqual(result, expected) + + def test_get_policy_id(self): + result = win_lgpo.get_policy(policy_name='AllowTelemetry', + policy_class='machine', + return_value_only=True, + return_full_policy_names=True, + hierarchical_return=False) + expected = 'Not Configured' + self.assertEqual(result, expected) + + def test_get_policy_name_full_return_full_names(self): + result = win_lgpo.get_policy(policy_name='Allow Telemetry', + policy_class='machine', + return_value_only=False, + return_full_policy_names=True, + hierarchical_return=False) + expected = { + 'Windows Components\\Data Collection and Preview Builds\\' + 'Allow Telemetry': 'Not Configured'} + self.assertDictEqual(result, expected) + def test_get_policy_id_full_return_full_names(self): + result = win_lgpo.get_policy(policy_name='AllowTelemetry', + policy_class='machine', + return_value_only=False, + return_full_policy_names=True, + hierarchical_return=False) + expected = { + 'Windows Components\\Data Collection and Preview Builds\\' + 'Allow Telemetry': 'Not Configured'} + self.assertDictEqual(result, expected) + + def test_get_policy_name_full_return_ids(self): + result = win_lgpo.get_policy(policy_name='Allow Telemetry', + policy_class='machine', + return_value_only=False, + return_full_policy_names=False, + hierarchical_return=False) + expected = {'AllowTelemetry': 'Not Configured'} + self.assertDictEqual(result, expected) + + def test_get_policy_id_full_return_ids(self): + result = win_lgpo.get_policy(policy_name='AllowTelemetry', + policy_class='machine', + return_value_only=False, + return_full_policy_names=False, + hierarchical_return=False) + expected = {'AllowTelemetry': 'Not Configured'} + self.assertDictEqual(result, expected) + + def test_get_policy_id_full_return_ids_hierarchical(self): + result = win_lgpo.get_policy(policy_name='AllowTelemetry', + policy_class='machine', + return_value_only=False, + return_full_policy_names=False, + hierarchical_return=True) + expected = { + 'Computer Configuration': { + 'Administrative Templates': { + 'WindowsComponents': { + 'DataCollectionAndPreviewBuilds': { + 'AllowTelemetry': 'Not Configured'}}}}} + self.assertDictEqual(result, expected) + + def test_get_policy_name_return_full_names_hierarchical(self): + result = win_lgpo.get_policy(policy_name='Allow Telemetry', + policy_class='machine', + return_value_only=False, + return_full_policy_names=True, + hierarchical_return=True) + expected = { + 'Computer Configuration': { + 'Administrative Templates': { + 'Windows Components': { + 'Data Collection and Preview Builds': { + 'Allow Telemetry': 'Not Configured'}}}}} + self.assertDictEqual(result, expected) + + +@skipIf(not salt.utils.platform.is_windows(), 'System is not Windows') +class WinLGPOGetPolicyFromPolicyInfoTestCase(TestCase, LoaderModuleMockMixin): + ''' + Test functions related to the ``get_policy`` function using _policy_info + object + ''' def setup_loader_modules(self): - return {win_lgpo: { - '__context__': {} - }} + return LOADER_DICTS - def test__set_netsh_value_firewall(self): - ''' - Test setting the firewall inbound policy - ''' - context = { - 'lgpo.netsh_data': { - 'Private': { - 'Inbound': 'Block'}}} + def test_get_policy_name(self): + result = win_lgpo.get_policy( + policy_name='Network firewall: Public: Settings: Display a ' + 'notification', + policy_class='machine', + return_value_only=True, + return_full_policy_names=True, + hierarchical_return=False) + expected = 'Not configured' + self.assertEqual(result, expected) + + def test_get_policy_id(self): + result = win_lgpo.get_policy( + policy_name='WfwPublicSettingsNotification', + policy_class='machine', + return_value_only=True, + return_full_policy_names=True, + hierarchical_return=False) + expected = 'Not configured' + self.assertEqual(result, expected) + + def test_get_policy_name_full_return(self): + result = win_lgpo.get_policy( + policy_name='Network firewall: Public: Settings: Display a ' + 'notification', + policy_class='machine', + return_value_only=False, + return_full_policy_names=True, + hierarchical_return=False) expected = { - 'lgpo.netsh_data': { - 'Private': { - 'Inbound': 'Allow'}}} - with patch('salt.utils.win_lgpo_netsh.set_firewall_settings', - MagicMock(return_value=True)),\ - patch.dict(win_lgpo.__context__, context): - result = win_lgpo._set_netsh_value(profile='Private', - section='firewallpolicy', - option='Inbound', - value='Allow') - self.assertTrue(result) - self.assertEqual(win_lgpo.__context__, expected) - - def test__set_netsh_value_settings(self): - ''' - Test setting firewall settings - ''' - context = { - 'lgpo.netsh_data': { - 'private': { - 'localfirewallrules': 'disable'}}} + 'Network firewall: Public: Settings: Display a notification': + 'Not configured'} + self.assertDictEqual(result, expected) + + def test_get_policy_id_full_return(self): + result = win_lgpo.get_policy( + policy_name='WfwPublicSettingsNotification', + policy_class='machine', + return_value_only=False, + return_full_policy_names=True, + hierarchical_return=False) expected = { - 'lgpo.netsh_data': { - 'private': { - 'localfirewallrules': 'enable'}}} - with patch('salt.utils.win_lgpo_netsh.set_settings', - MagicMock(return_value=True)), \ - patch.dict(win_lgpo.__context__, context): - result = win_lgpo._set_netsh_value(profile='private', - section='settings', - option='localfirewallrules', - value='enable') - self.assertTrue(result) - self.assertEqual(win_lgpo.__context__, expected) - - def test__set_netsh_value_state(self): - ''' - Test setting the firewall state - ''' - context = { - 'lgpo.netsh_data': { - 'private': { - 'State': 'notconfigured'}}} + 'Network firewall: Public: Settings: Display a notification': + 'Not configured'} + self.assertDictEqual(result, expected) + + def test_get_policy_name_full_return_ids(self): + result = win_lgpo.get_policy( + policy_name='Network firewall: Public: Settings: Display a ' + 'notification', + policy_class='machine', + return_value_only=False, + return_full_policy_names=False, + hierarchical_return=False) expected = { - 'lgpo.netsh_data': { - 'private': { - 'State': 'on'}}} - with patch('salt.utils.win_lgpo_netsh.set_state', - MagicMock(return_value=True)), \ - patch.dict(win_lgpo.__context__, context): - result = win_lgpo._set_netsh_value(profile='private', - section='state', - option='unused', - value='on') - self.assertTrue(result) - self.assertEqual(win_lgpo.__context__, expected) - - def test__set_netsh_value_logging(self): - ''' - Test setting firewall logging - ''' - context = { - 'lgpo.netsh_data': { - 'private': { - 'allowedconnections': 'notconfigured'}}} + 'Network firewall: Public: Settings: Display a notification': + 'Not configured'} + self.assertDictEqual(result, expected) + + def test_get_policy_id_full_return_ids(self): + result = win_lgpo.get_policy( + policy_name='WfwPublicSettingsNotification', + policy_class='machine', + return_value_only=False, + return_full_policy_names=False, + hierarchical_return=False) + expected = {'WfwPublicSettingsNotification': 'Not configured'} + self.assertDictEqual(result, expected) + + def test_get_policy_id_full_return_ids_hierarchical(self): + result = win_lgpo.get_policy( + policy_name='WfwPublicSettingsNotification', + policy_class='machine', + return_value_only=False, + return_full_policy_names=False, + hierarchical_return=True) expected = { - 'lgpo.netsh_data': { - 'private': { - 'allowedconnections': 'enable'}}} - with patch('salt.utils.win_lgpo_netsh.set_logging_settings', - MagicMock(return_value=True)), \ - patch.dict(win_lgpo.__context__, context): - result = win_lgpo._set_netsh_value(profile='private', - section='logging', - option='allowedconnections', - value='enable') - self.assertTrue(result) - self.assertEqual(win_lgpo.__context__, expected) - - -class WinLgpoSeceditTestCase(TestCase, LoaderModuleMockMixin): + 'Computer Configuration': { + 'Windows Settings': { + 'Security Settings': { + 'Windows Firewall with Advanced Security': { + 'Windows Firewall with Advanced Security - Local ' + 'Group Policy Object': { + 'WfwPublicSettingsNotification': + 'Not configured'}}}}}} + self.assertDictEqual(result, expected) + + def test_get_policy_id_full_return_full_names_hierarchical(self): + result = win_lgpo.get_policy( + policy_name='WfwPublicSettingsNotification', + policy_class='machine', + return_value_only=False, + return_full_policy_names=True, + hierarchical_return=True) + expected = { + 'Computer Configuration': { + 'Windows Settings': { + 'Security Settings': { + 'Windows Firewall with Advanced Security': { + 'Windows Firewall with Advanced Security - Local ' + 'Group Policy Object': { + 'Network firewall: Public: Settings: Display a ' + 'notification': + 'Not configured'}}}}}} + self.assertDictEqual(result, expected) + + +@skipIf(not salt.utils.platform.is_windows(), 'System is not Windows') +class WinLGPOPolicyInfoMechanismsTestCase(TestCase, LoaderModuleMockMixin): ''' - Secedit test cases + Test getting local group policy settings defined in the _policy_info object + Go through each mechanism ''' + def setup_loader_modules(self): + return LOADER_DICTS @classmethod def setUpClass(cls): - cls.secedit_data = [ - '[Unicode]', - 'Unicode=yes', - '[System Access]', - 'MinimumPasswordAge = 0', - 'MaximumPasswordAge = 42', - '[Event Audit]', - 'AuditSystemEvents = 0', - 'AuditLogonEvents = 0', - '[Registry Values]', - r'MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Setup\RecoveryConsole\SecurityLevel=4,0', - r'MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Setup\RecoveryConsole\SetCommand=4,0', - '[Privilege Rights]', - 'SeNetworkLogonRight = *S-1-1-0,*S-1-5-32-544,*S-1-5-32-545,*S-1-5-32-551', - 'SeBackupPrivilege = *S-1-5-32-544,*S-1-5-32-551', - '[Version]', - 'signature="$CHICAGO$"', - 'Revision=1'] + cls.policy_data = salt.modules.win_lgpo._policy_info() - @classmethod - def tearDownClass(cls): - del cls.secedit_data + def _test_policy(self, policy_name): + ''' + Helper function to get current setting + ''' + policy_definition = self.policy_data.policies['Machine']['policies'][policy_name] + return salt.modules.win_lgpo._get_policy_info_setting(policy_definition) + + def test_registry_mechanism(self): + ''' + Test getting policy value using the Registry mechanism + ''' + policy_name = 'RemoteRegistryExactPaths' + result = self._test_policy(policy_name=policy_name) + expected = [ + 'System\\CurrentControlSet\\Control\\ProductOptions', + 'System\\CurrentControlSet\\Control\\Server Applications', + 'Software\\Microsoft\\Windows NT\\CurrentVersion' + ] + self.assertListEqual(result, expected) + + def test_secedit_mechanism(self): + ''' + Test getting policy value using the Secedit mechanism + ''' + policy_name = 'LSAAnonymousNameLookup' + result = self._test_policy(policy_name=policy_name) + expected = 'Disabled' + self.assertEqual(result, expected) + + def test_netsh_mechanism(self): + ''' + Test getting the policy value using the NetSH mechanism + ''' + policy_name = 'WfwDomainState' + result = self._test_policy(policy_name=policy_name) + expected = 'Not configured' + self.assertEqual(result, expected) + + @destructiveTest + def test_adv_audit_mechanism(self): + ''' + Test getting the policy value using the AdvAudit mechanism + ''' + system_root = os.environ.get('SystemRoot', 'C:\\Windows') + f_audit = os.path.join(system_root, 'security', 'audit', 'audit.csv') + f_audit_gpo = os.path.join(system_root, 'System32', 'GroupPolicy', + 'Machine', 'Microsoft', 'Windows NT', + 'Audit', 'audit.csv') + if os.path.exists(f_audit): + os.remove(f_audit) + if os.path.exists(f_audit_gpo): + os.remove(f_audit_gpo) + policy_name = 'AuditCredentialValidation' + result = self._test_policy(policy_name=policy_name) + expected = 'Not Configured' + self.assertEqual(result, expected) + + def test_net_user_modal_mechanism(self): + ''' + Test getting the policy value using the NetUserModal mechanism + ''' + policy_name = 'PasswordHistory' + result = self._test_policy(policy_name=policy_name) + expected = 0 + self.assertEqual(result, expected) + + def test_lsa_rights_mechanism(self): + ''' + Test getting the policy value using the LsaRights mechanism + ''' + policy_name = 'SeTrustedCredManAccessPrivilege' + result = self._test_policy(policy_name=policy_name) + expected = [] + self.assertEqual(result, expected) + + def test_script_ini_mechanism(self): + ''' + Test getting the policy value using the ScriptIni value + ''' + policy_name = 'StartupScripts' + result = self._test_policy(policy_name=policy_name) + expected = None + self.assertEqual(result, expected) + + +@destructiveTest +@skipIf(not salt.utils.platform.is_windows(), 'System is not Windows') +class WinLGPOGetPointAndPrintNCTestCase(TestCase, LoaderModuleMockMixin): + ''' + Test variations of the Point and Print Restrictions policy when Not + Configured (NC) + ''' + not_configured = False def setup_loader_modules(self): - return {win_lgpo: { - '__context__': {}, - '__opts__': {'cachedir': 'C:\\cachedir'}, - '__salt__': {} - }} + return LOADER_DICTS - def test__get_secedit_data(self): + def setUp(self): + if not self.not_configured: + computer_policy = {'Point and Print Restrictions': 'Not Configured'} + win_lgpo.set_(computer_policy=computer_policy) + self.not_configured = True + + def _get_policy_adm_setting(self, policy_name, policy_class, + return_full_policy_names, hierarchical_return): + ''' + Helper function to get current setting + ''' + # Get the policy + success, policy_obj, _, _ = salt.modules.win_lgpo._lookup_admin_template( + policy_name=policy_name, + policy_class=policy_class, + adml_language='en-US') + if success: + return salt.modules.win_lgpo._get_policy_adm_setting( + admx_policy=policy_obj, + policy_class=policy_class, + adml_language='en-US', + return_full_policy_names=return_full_policy_names, + hierarchical_return=hierarchical_return + ) + return 'Policy Not Found' + + def test_point_and_print_not_configured(self): + result = self._get_policy_adm_setting( + policy_name='Point and Print Restrictions', + policy_class='Machine', + return_full_policy_names=False, + hierarchical_return=False + ) + expected = { + 'PointAndPrint_Restrictions_Win7': 'Not Configured' + } + self.assertDictEqual(result, expected) + + def test_point_and_print_not_configured_hierarchical(self): + result = self._get_policy_adm_setting( + policy_name='Point and Print Restrictions', + policy_class='Machine', + return_full_policy_names=False, + hierarchical_return=True + ) + expected = { + 'Computer Configuration': { + 'Administrative Templates': { + 'Printers': { + 'PointAndPrint_Restrictions_Win7': + 'Not Configured'}}}} + self.assertDictEqual(result, expected) + + def test_point_and_print_not_configured_full_names(self): + result = self._get_policy_adm_setting( + policy_name='Point and Print Restrictions', + policy_class='Machine', + return_full_policy_names=True, + hierarchical_return=False + ) + expected = { + 'Printers\\Point and Print Restrictions': 'Not Configured' + } + self.assertDictEqual(result, expected) + + def test_point_and_print_not_configured_full_names_hierarchical(self): + result = self._get_policy_adm_setting( + policy_name='Point and Print Restrictions', + policy_class='Machine', + return_full_policy_names=True, + hierarchical_return=True + ) + expected = { + 'Computer Configuration': { + 'Administrative Templates': { + 'Printers': { + 'Point and Print Restrictions': + 'Not Configured'}}}} + self.assertDictEqual(result, expected) + + +@destructiveTest +@skipIf(not salt.utils.platform.is_windows(), 'System is not Windows') +class WinLGPOGetPointAndPrintENTestCase(TestCase, LoaderModuleMockMixin): + ''' + Test variations of the Point and Print Restrictions policy when Enabled (EN) + ''' + configured = False + + def setup_loader_modules(self): + return LOADER_DICTS + + def setUp(self): + if not self.configured: + computer_policy = { + 'Point and Print Restrictions': { + 'Users can only point and print to these servers': + True, + 'Enter fully qualified server names separated by ' + 'semicolons': + 'fakeserver1;fakeserver2', + 'Users can only point and print to machines in their ' + 'forest': + True, + 'Security Prompts: When installing drivers for a new ' + 'connection': + 'Show warning and elevation prompt', + 'When updating drivers for an existing connection': + 'Show warning only', + }, + } + win_lgpo.set_(computer_policy=computer_policy) + self.configured = True + + def _get_policy_adm_setting(self, policy_name, policy_class, + return_full_policy_names, hierarchical_return): ''' - Test getting secedit data and loading it into __context__ + Helper function to get current setting ''' + # Get the policy + success, policy_obj, _, _ = salt.modules.win_lgpo._lookup_admin_template( + policy_name=policy_name, + policy_class=policy_class, + adml_language='en-US') + if success: + return salt.modules.win_lgpo._get_policy_adm_setting( + admx_policy=policy_obj, + policy_class=policy_class, + adml_language='en-US', + return_full_policy_names=return_full_policy_names, + hierarchical_return=hierarchical_return + ) + return 'Policy Not Found' + + def test_point_and_print_enabled(self): + result = self._get_policy_adm_setting( + policy_name='Point and Print Restrictions', + policy_class='Machine', + return_full_policy_names=False, + hierarchical_return=False + ) + expected = { + 'PointAndPrint_Restrictions_Win7': { + 'PointAndPrint_NoWarningNoElevationOnInstall_Enum': + 'Show warning and elevation prompt', + 'PointAndPrint_NoWarningNoElevationOnUpdate_Enum': + 'Show warning only', + 'PointAndPrint_TrustedForest_Chk': + True, + 'PointAndPrint_TrustedServers_Chk': + True, + u'PointAndPrint_TrustedServers_Edit': + 'fakeserver1;fakeserver2'}} + self.assertDictEqual(result, expected) + + def test_point_and_print_enabled_hierarchical(self): + result = self._get_policy_adm_setting( + policy_name='Point and Print Restrictions', + policy_class='Machine', + return_full_policy_names=False, + hierarchical_return=True + ) + expected = { + 'Computer Configuration': { + 'Administrative Templates': { + 'Printers': { + 'PointAndPrint_Restrictions_Win7': { + 'PointAndPrint_NoWarningNoElevationOnInstall_Enum': + 'Show warning and elevation prompt', + 'PointAndPrint_NoWarningNoElevationOnUpdate_Enum': + 'Show warning only', + 'PointAndPrint_TrustedForest_Chk': + True, + 'PointAndPrint_TrustedServers_Chk': + True, + u'PointAndPrint_TrustedServers_Edit': + 'fakeserver1;fakeserver2'}}}}} + self.assertDictEqual(result, expected) + + def test_point_and_print_enabled_full_names(self): + result = self._get_policy_adm_setting( + policy_name='Point and Print Restrictions', + policy_class='Machine', + return_full_policy_names=True, + hierarchical_return=False + ) + expected = { + 'Printers\\Point and Print Restrictions': { + 'Enter fully qualified server names separated by semicolons': + 'fakeserver1;fakeserver2', + 'Security Prompts: When installing drivers for a new ' + 'connection': + 'Show warning and elevation prompt', + 'Users can only point and print to machines in their forest': + True, + u'Users can only point and print to these servers': True, + u'When updating drivers for an existing connection': + 'Show warning only'}} + self.assertDictEqual(result, expected) + + def test_point_and_print_enabled_full_names_hierarchical(self): + result = self._get_policy_adm_setting( + policy_name='Point and Print Restrictions', + policy_class='Machine', + return_full_policy_names=True, + hierarchical_return=True + ) expected = { - 'AuditLogonEvents': '0', - 'AuditSystemEvents': '0', - r'MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Setup\RecoveryConsole\SecurityLevel': '4,0', - r'MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Setup\RecoveryConsole\SetCommand': '4,0', - 'MaximumPasswordAge': '42', - 'MinimumPasswordAge': '0', - 'Revision': '1', - 'SeBackupPrivilege': '*S-1-5-32-544,*S-1-5-32-551', - 'SeNetworkLogonRight': '*S-1-1-0,*S-1-5-32-544,*S-1-5-32-545,*S-1-5-32-551', - 'Unicode': 'yes', - 'signature': '"$CHICAGO$"'} - with patch.object(win_lgpo, '_load_secedit_data', - MagicMock(return_value=self.secedit_data)): - result = win_lgpo._get_secedit_data() - self.assertDictEqual(result, expected) - self.assertDictEqual(win_lgpo.__context__['lgpo.secedit_data'], - expected) - - def test__get_secedit_value(self): - ''' - Test getting a specific secedit value - ''' - with patch.object(win_lgpo, '_load_secedit_data', - MagicMock(return_value=self.secedit_data)): - result = win_lgpo._get_secedit_value('AuditSystemEvents') - self.assertEqual(result, '0') - - def test__get_secedit_value_not_defined(self): - ''' - Test getting a secedit value that is undefined - ''' - with patch.object(win_lgpo, '_load_secedit_data', - MagicMock(return_value=self.secedit_data)): - result = win_lgpo._get_secedit_value('UndefinedKey') - self.assertEqual(result, 'Not Defined') - - def test__write_secedit_data(self): - ''' - Test writing secedit data and updating the __context__ - ''' - mock_true = MagicMock(return_value=True) - mock_false = MagicMock(return_value=False) - mock_retcode = MagicMock(return_value=0) - new_secedit_data = {'System Access': ['MaximumPasswordAge=100']} - with patch.object(win_lgpo, '_load_secedit_data', - MagicMock(return_value=self.secedit_data)),\ - patch.dict(win_lgpo.__salt__, {'file.write': mock_true, - 'file.file_exists': mock_false, - 'cmd.retcode': mock_retcode}): - # Populate __context__['lgpo.secedit_data'] - # It will have been run before this function is called - win_lgpo._get_secedit_data() - self.assertEqual( - win_lgpo.__context__['lgpo.secedit_data']['MaximumPasswordAge'], - '42') - result = win_lgpo._write_secedit_data(new_secedit_data) - self.assertTrue(result) - self.assertEqual( - win_lgpo.__context__['lgpo.secedit_data']['MaximumPasswordAge'], - '100') + 'Computer Configuration': { + 'Administrative Templates': { + 'Printers': { + 'Point and Print Restrictions': { + 'Enter fully qualified server names separated by ' + 'semicolons': + 'fakeserver1;fakeserver2', + 'Security Prompts: When installing drivers for a ' + 'new connection': + 'Show warning and elevation prompt', + 'Users can only point and print to machines in ' + 'their forest': + True, + u'Users can only point and print to these servers': + True, + u'When updating drivers for an existing connection': + 'Show warning only'}}}}} + self.assertDictEqual(result, expected) diff --git a/tests/unit/states/test_win_lgpo.py b/tests/unit/states/test_win_lgpo.py index 3b184401e3e0..ef3f833be341 100644 --- a/tests/unit/states/test_win_lgpo.py +++ b/tests/unit/states/test_win_lgpo.py @@ -98,3 +98,30 @@ def test__compare_policies_dict(self): self.assertFalse( win_lgpo._compare_policies(compare_dict, None) ) + + def test__compare_policies_integer(self): + ''' + ``_compare_policies`` should only return ``True`` when the integer + values are the same. All other scenarios should return ``False`` + ''' + compare_integer = 1 + # Same + self.assertTrue( + win_lgpo._compare_policies(compare_integer, compare_integer) + ) + # Different + self.assertFalse( + win_lgpo._compare_policies(compare_integer, 0) + ) + # List + self.assertFalse( + win_lgpo._compare_policies(compare_integer, ['item1', 'item2']) + ) + # Dict + self.assertFalse( + win_lgpo._compare_policies(compare_integer, {'key': 'value'}) + ) + # None + self.assertFalse( + win_lgpo._compare_policies(compare_integer, None) + ) diff --git a/tests/unit/utils/test_win_reg.py b/tests/unit/utils/test_win_reg.py index 7af97b5f37e7..61c704c56a38 100644 --- a/tests/unit/utils/test_win_reg.py +++ b/tests/unit/utils/test_win_reg.py @@ -2,6 +2,7 @@ # Import Python Libs from __future__ import absolute_import, unicode_literals, print_function +from salt.ext import six # Import Salt Testing Libs from tests.support.helpers import destructiveTest, generate_random_name @@ -176,6 +177,40 @@ def test_read_value_non_existing_key(self): expected ) + @destructiveTest + def test_read_value_multi_sz_empty_list(self): + ''' + An empty REG_MULTI_SZ value should return an empty list, not None + ''' + try: + self.assertTrue( + win_reg.set_value( + hive='HKLM', + key=FAKE_KEY, + vname='empty_list', + vdata=[], + vtype='REG_MULTI_SZ' + ) + ) + expected = { + 'hive': 'HKLM', + 'key': FAKE_KEY, + 'success': True, + 'vdata': [], + 'vname': 'empty_list', + 'vtype': 'REG_MULTI_SZ' + } + self.assertEqual( + win_reg.read_value( + hive='HKLM', + key=FAKE_KEY, + vname='empty_list', + ), + expected + ) + finally: + win_reg.delete_key_recursive(hive='HKLM', key=FAKE_KEY) + @destructiveTest def test_set_value(self): ''' @@ -462,3 +497,13 @@ def test_delete_key_unicode(self): ) finally: win_reg.delete_key_recursive(hive='HKLM', key=FAKE_KEY) + + def test__to_unicode_int(self): + ''' + Test the ``_to_unicode`` function when it receives an integer value. + Should return a unicode value, which is unicode in PY2 and str in PY3. + ''' + if six.PY3: + self.assertTrue(isinstance(win_reg._to_unicode(1), str)) + else: + self.assertTrue(isinstance(win_reg._to_unicode(1), unicode)) # pylint: disable=incompatible-py3-code,undefined-variable