Merge pull request #54072 from twangboy/fix_53955_develop

Check for key before trying to list it (master)
This commit is contained in:
Daniel Wozniak 2020-01-14 14:20:05 -07:00 committed by GitHub
commit 648b90b292
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 777 additions and 282 deletions

View file

@ -478,7 +478,7 @@ def _get_reg_software(include_components=True,
# https://github.com/aws/amazon-ssm-agent/blob/master/agent/plugins/inventory/gatherers/application/dataProvider_windows.go
reg_software = {}
def skip_component(hive, key, sub_key, use_32bit):
def skip_component(hive, key, sub_key, use_32bit_registry):
'''
'SystemComponent' must be either absent or present with a value of 0,
because this value is usually set on programs that have been installed
@ -493,16 +493,16 @@ def _get_reg_software(include_components=True,
hive=hive,
key='{0}\\{1}'.format(key, sub_key),
vname='SystemComponent',
use_32bit_registry=use_32bit):
use_32bit_registry=use_32bit_registry):
if __utils__['reg.read_value'](
hive=hive,
key='{0}\\{1}'.format(key, sub_key),
vname='SystemComponent',
use_32bit_registry=use_32bit)['vdata'] > 0:
use_32bit_registry=use_32bit_registry)['vdata'] > 0:
return True
return False
def skip_win_installer(hive, key, sub_key, use_32bit):
def skip_win_installer(hive, key, sub_key, use_32bit_registry):
'''
'WindowsInstaller' must be either absent or present with a value of 0.
If the value is set to 1, then the application is included in the list
@ -517,21 +517,21 @@ def _get_reg_software(include_components=True,
hive=hive,
key='{0}\\{1}'.format(key, sub_key),
vname='WindowsInstaller',
use_32bit_registry=use_32bit):
use_32bit_registry=use_32bit_registry):
if __utils__['reg.read_value'](
hive=hive,
key='{0}\\{1}'.format(key, sub_key),
vname='WindowsInstaller',
use_32bit_registry=use_32bit)['vdata'] > 0:
use_32bit_registry=use_32bit_registry)['vdata'] > 0:
squid = salt.utils.win_functions.guid_to_squid(sub_key)
if not __utils__['reg.key_exists'](
hive='HKLM',
key=products_key.format(squid),
use_32bit_registry=use_32bit):
use_32bit_registry=use_32bit_registry):
return True
return False
def skip_uninstall_string(hive, key, sub_key, use_32bit):
def skip_uninstall_string(hive, key, sub_key, use_32bit_registry):
'''
'UninstallString' must be present, because it stores the command line
that gets executed by Add/Remove programs, when the user tries to
@ -544,11 +544,11 @@ def _get_reg_software(include_components=True,
hive=hive,
key='{0}\\{1}'.format(key, sub_key),
vname='UninstallString',
use_32bit_registry=use_32bit):
use_32bit_registry=use_32bit_registry):
return True
return False
def skip_release_type(hive, key, sub_key, use_32bit):
def skip_release_type(hive, key, sub_key, use_32bit_registry):
'''
'ReleaseType' must either be absent or if present must not have a
value set to 'Security Update', 'Update Rollup', or 'Hotfix', because
@ -566,16 +566,16 @@ def _get_reg_software(include_components=True,
hive=hive,
key='{0}\\{1}'.format(key, sub_key),
vname='ReleaseType',
use_32bit_registry=use_32bit):
use_32bit_registry=use_32bit_registry):
if __utils__['reg.read_value'](
hive=hive,
key='{0}\\{1}'.format(key, sub_key),
vname='ReleaseType',
use_32bit_registry=use_32bit)['vdata'] in skip_types:
use_32bit_registry=use_32bit_registry)['vdata'] in skip_types:
return True
return False
def skip_parent_key(hive, key, sub_key, use_32bit):
def skip_parent_key(hive, key, sub_key, use_32bit_registry):
'''
'ParentKeyName' must NOT be present, because that indicates it's an
update to the parent program.
@ -587,12 +587,12 @@ def _get_reg_software(include_components=True,
hive=hive,
key='{0}\\{1}'.format(key, sub_key),
vname='ParentKeyName',
use_32bit_registry=use_32bit):
use_32bit_registry=use_32bit_registry):
return True
return False
def add_software(hive, key, sub_key, use_32bit):
def add_software(hive, key, sub_key, use_32bit_registry):
'''
'DisplayName' must be present with a valid value, as this is reflected
as the software name returned by pkg.list_pkgs. Also, its value must
@ -603,7 +603,7 @@ def _get_reg_software(include_components=True,
hive=hive,
key='{0}\\{1}'.format(key, sub_key),
vname='DisplayName',
use_32bit_registry=use_32bit)
use_32bit_registry=use_32bit_registry)
if (not d_name_regdata['success'] or
d_name_regdata['vtype'] not in ['REG_SZ', 'REG_EXPAND_SZ'] or
@ -619,7 +619,7 @@ def _get_reg_software(include_components=True,
hive=hive,
key='{0}\\{1}'.format(key, sub_key),
vname='DisplayVersion',
use_32bit_registry=use_32bit)
use_32bit_registry=use_32bit_registry)
d_vers = 'Not Found'
if (d_vers_regdata['success'] and
@ -635,9 +635,8 @@ def _get_reg_software(include_components=True,
# HKLM Uninstall 64 bit
kwargs = {'hive': 'HKLM',
'key': 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall',
'use_32bit': False}
for sub_key in __utils__['reg.list_keys'](hive=kwargs['hive'],
key=kwargs['key']):
'use_32bit_registry': False}
for sub_key in __utils__['reg.list_keys'](**kwargs):
kwargs['sub_key'] = sub_key
if skip_component(**kwargs):
continue
@ -652,10 +651,9 @@ def _get_reg_software(include_components=True,
add_software(**kwargs)
# HKLM Uninstall 32 bit
kwargs['use_32bit'] = True
for sub_key in __utils__['reg.list_keys'](hive=kwargs['hive'],
key=kwargs['key'],
use_32bit_registry=kwargs['use_32bit']):
kwargs['use_32bit_registry'] = True
kwargs.pop('sub_key', False)
for sub_key in __utils__['reg.list_keys'](**kwargs):
kwargs['sub_key'] = sub_key
if skip_component(**kwargs):
continue
@ -672,10 +670,10 @@ def _get_reg_software(include_components=True,
# HKLM Uninstall 64 bit
kwargs = {'hive': 'HKLM',
'key': 'Software\\Classes\\Installer\\Products',
'use_32bit': False}
'use_32bit_registry': False}
userdata_key = 'Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\' \
'UserData\\S-1-5-18\\Products'
for sub_key in __utils__['reg.list_keys'](hive=kwargs['hive'], key=kwargs['key']):
for sub_key in __utils__['reg.list_keys'](**kwargs):
# If the key does not exist in userdata, skip it
if not __utils__['reg.key_exists'](
hive=kwargs['hive'],
@ -699,72 +697,73 @@ def _get_reg_software(include_components=True,
for user_guid in __utils__['reg.list_keys'](hive=hive_hku):
kwargs = {'hive': hive_hku,
'key': uninstall_key.format(user_guid),
'use_32bit': False}
for sub_key in __utils__['reg.list_keys'](hive=kwargs['hive'],
key=kwargs['key']):
kwargs['sub_key'] = sub_key
if skip_component(**kwargs):
continue
if skip_win_installer(**kwargs):
continue
if skip_uninstall_string(**kwargs):
continue
if skip_release_type(**kwargs):
continue
if skip_parent_key(**kwargs):
continue
add_software(**kwargs)
# While we have the user guid, we're gong to check userdata in HKLM
for sub_key in __utils__['reg.list_keys'](hive=hive_hku,
key=product_key.format(user_guid)):
kwargs = {'hive': 'HKLM',
'key': user_data_key.format(user_guid, sub_key),
'sub_key': 'InstallProperties',
'use_32bit': False}
if __utils__['reg.key_exists'](hive=kwargs['hive'],
key=kwargs['key']):
'use_32bit_registry': False}
if __utils__['reg.key_exists'](**kwargs):
for sub_key in __utils__['reg.list_keys'](**kwargs):
kwargs['sub_key'] = sub_key
if skip_component(**kwargs):
continue
if skip_win_installer(**kwargs):
continue
if skip_uninstall_string(**kwargs):
continue
if skip_release_type(**kwargs):
continue
if skip_parent_key(**kwargs):
continue
add_software(**kwargs)
# While we have the user guid, we're gong to check userdata in HKLM
kwargs = {'hive': hive_hku,
'key': product_key.format(user_guid),
'use_32bit_registry': False}
if __utils__['reg.key_exists'](**kwargs):
for sub_key in __utils__['reg.list_keys'](**kwargs):
kwargs = {'hive': 'HKLM',
'key': user_data_key.format(user_guid, sub_key),
'use_32bit_registry': False}
if __utils__['reg.key_exists'](**kwargs):
kwargs['sub_key'] = 'InstallProperties'
if skip_component(**kwargs):
continue
add_software(**kwargs)
# Uninstall for each user on the system (HKU), 32 bit
for user_guid in __utils__['reg.list_keys'](hive=hive_hku,
use_32bit_registry=True):
kwargs = {'hive': hive_hku,
'key': uninstall_key.format(user_guid),
'use_32bit': True}
for sub_key in __utils__['reg.list_keys'](hive=kwargs['hive'],
key=kwargs['key'],
use_32bit_registry=kwargs['use_32bit']):
kwargs['sub_key'] = sub_key
if skip_component(**kwargs):
continue
if skip_win_installer(**kwargs):
continue
if skip_uninstall_string(**kwargs):
continue
if skip_release_type(**kwargs):
continue
if skip_parent_key(**kwargs):
continue
add_software(**kwargs)
# While we have the user guid, we're gong to check userdata in HKLM
for sub_key_2 in __utils__['reg.list_keys'](hive=hive_hku,
key=product_key.format(user_guid),
use_32bit_registry=True):
kwargs = {'hive': 'HKLM',
'key': user_data_key.format(user_guid, sub_key_2),
'sub_key': 'InstallProperties',
'use_32bit': True}
if __utils__['reg.key_exists'](hive=kwargs['hive'],
key=kwargs['key'],
use_32bit_registry=kwargs['use_32bit']):
'use_32bit_registry': True}
if __utils__['reg.key_exists'](**kwargs):
for sub_key in __utils__['reg.list_keys'](**kwargs):
kwargs['sub_key'] = sub_key
if skip_component(**kwargs):
continue
if skip_win_installer(**kwargs):
continue
if skip_uninstall_string(**kwargs):
continue
if skip_release_type(**kwargs):
continue
if skip_parent_key(**kwargs):
continue
add_software(**kwargs)
kwargs = {'hive': hive_hku,
'key': product_key.format(user_guid),
'use_32bit_registry': True}
if __utils__['reg.key_exists'](**kwargs):
# While we have the user guid, we're going to check userdata in HKLM
for sub_key_2 in __utils__['reg.list_keys'](**kwargs):
kwargs = {'hive': 'HKLM',
'key': user_data_key.format(user_guid, sub_key_2),
'use_32bit_registry': True}
if __utils__['reg.key_exists'](**kwargs):
kwargs['sub_key'] = 'InstallProperties'
if skip_component(**kwargs):
continue
add_software(**kwargs)
return reg_software

View file

@ -39,7 +39,6 @@ try:
import win32gui
import win32api
import win32con
import pywintypes
HAS_WINDOWS_MODULES = True
except ImportError:
HAS_WINDOWS_MODULES = False
@ -198,7 +197,7 @@ def key_exists(hive, key, use_32bit_registry=False):
try:
handle = win32api.RegOpenKeyEx(hkey, local_key, 0, access_mask)
return True
except pywintypes.error as exc:
except win32api.error as exc:
if exc.winerror == 2:
return False
raise
@ -231,7 +230,9 @@ def value_exists(hive, key, vname, use_32bit_registry=False):
.. code-block:: python
import salt.utils.win_reg
winreg.key_exists(hive='HKLM', key='SOFTWARE\\Microsoft')
winreg.value_exists(hive='HKLM',
key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion',
vname='CommonFilesDir')
'''
local_hive = _to_unicode(hive)
local_key = _to_unicode(key)
@ -246,7 +247,7 @@ def value_exists(hive, key, vname, use_32bit_registry=False):
try:
handle = win32api.RegOpenKeyEx(hkey, local_key, 0, access_mask)
except pywintypes.error as exc:
except win32api.error as exc:
if exc.winerror == 2:
# The key containing the value/data pair does not exist
return False
@ -257,7 +258,7 @@ def value_exists(hive, key, vname, use_32bit_registry=False):
_, _ = win32api.RegQueryValueEx(handle, local_vname)
# value/data pair exists
return True
except pywintypes.error as exc:
except win32api.error as exc:
if exc.winerror == 2 and vname is None:
# value/data pair exists but is empty
return True
@ -349,9 +350,12 @@ def list_keys(hive, key=None, use_32bit_registry=False):
else:
subkeys.append(subkey)
except Exception: # pylint: disable=broad-except
log.debug(r'Cannot find key: %s\%s', hive, key, exc_info=True)
return False, r'Cannot find key: {0}\{1}'.format(hive, key)
except win32api.error as exc:
if exc.winerror == 2:
log.debug(r'Cannot find key: %s\%s', hive, key, exc_info=True)
return False, r'Cannot find key: {0}\{1}'.format(hive, key)
raise
finally:
if handle:
handle.Close()
@ -359,10 +363,14 @@ def list_keys(hive, key=None, use_32bit_registry=False):
return subkeys
def list_values(hive, key=None, use_32bit_registry=False, include_default=True):
def list_values(hive, key=None, use_32bit_registry=False):
'''
Enumerates the values in a registry key or hive.
.. note::
The ``(Default)`` value will only be returned if it is set, otherwise it
will not be returned in the list of values.
Args:
hive (str):
@ -382,9 +390,6 @@ def list_values(hive, key=None, use_32bit_registry=False, include_default=True):
Accesses the 32bit portion of the registry on 64 bit installations.
On 32bit machines this is ignored.
include_default (bool):
Toggle whether to include the '(Default)' value.
Returns:
list: A list of values under the hive or key.
@ -429,9 +434,13 @@ def list_values(hive, key=None, use_32bit_registry=False, include_default=True):
else:
value['vdata'] = vdata
values.append(value)
except Exception as exc: # pylint: disable=broad-except
log.debug(r'Cannot find key: %s\%s', hive, key, exc_info=True)
return False, r'Cannot find key: {0}\{1}'.format(hive, key)
except win32api.error as exc:
if exc.winerror == 2:
log.debug(r'Cannot find key: %s\%s', hive, key)
return False, r'Cannot find key: {0}\{1}'.format(hive, key)
raise
finally:
if handle:
handle.Close()
@ -535,23 +544,28 @@ def read_value(hive, key, vname=None, use_32bit_registry=False):
ret['vdata'] = vdata
else:
ret['comment'] = 'Empty Value'
except Exception as exc: # pylint: disable=broad-except
except win32api.error as exc:
if exc.winerror == 2 and vname is None:
ret['vdata'] = ('(value not set)')
ret['vtype'] = 'REG_SZ'
else:
elif exc.winerror == 2:
msg = 'Cannot find {0} in {1}\\{2}' \
''.format(local_vname, local_hive, local_key)
log.trace(exc)
log.trace(msg)
ret['comment'] = msg
ret['success'] = False
except Exception as exc: # pylint: disable=broad-except
msg = 'Cannot find key: {0}\\{1}'.format(local_hive, local_key)
log.trace(exc)
log.trace(msg)
ret['comment'] = msg
ret['success'] = False
else:
raise
except win32api.error as exc:
if exc.winerror == 2:
msg = 'Cannot find key: {0}\\{1}'.format(local_hive, local_key)
log.trace(exc)
log.trace(msg)
ret['comment'] = msg
ret['success'] = False
else:
raise
return ret
@ -705,15 +719,30 @@ def set_value(hive,
handle = None
try:
handle, _ = win32api.RegCreateKeyEx(hkey, local_key, access_mask,
Options=create_options)
win32api.RegSetValueEx(handle, local_vname, 0, vtype_value, local_vdata)
win32api.RegFlushKey(handle)
broadcast_change()
return True
except (win32api.error, SystemError, ValueError, TypeError): # pylint: disable=E0602
log.exception('Encountered error setting registry value')
handle, result = win32api.RegCreateKeyEx(hkey, local_key, access_mask,
Options=create_options)
msg = 'Created new key: %s\\%s' if result == 1 else \
'Opened existing key: %s\\%s'
log.debug(msg, local_hive, local_key)
try:
win32api.RegSetValueEx(handle, local_vname, 0, vtype_value, local_vdata)
win32api.RegFlushKey(handle)
broadcast_change()
return True
except TypeError as exc:
log.exception('"vdata" does not match the expected data type.\n%s',
exc)
return False
except (SystemError, ValueError) as exc:
log.exception('Encountered error setting registry value.\n%s', exc)
return False
except win32api.error as exc:
log.exception('Error creating/opening key: %s\\%s\n%s', local_hive,
local_key, exc.winerror)
return False
finally:
if handle:
win32api.RegCloseKey(handle)
@ -771,7 +800,7 @@ def cast_vdata(vdata=None, vtype='REG_SZ'):
return [_to_unicode(i) for i in vdata]
# Make sure REG_QWORD is a 64 bit integer
elif vtype_value == win32con.REG_QWORD:
return vdata if six.PY3 else long(vdata) # pylint: disable=undefined-variable,incompatible-py3-code
return int(vdata) if six.PY3 else long(vdata) # pylint: disable=undefined-variable,incompatible-py3-code
# Everything else is int
else:
return int(vdata)
@ -829,13 +858,12 @@ def delete_key_recursive(hive, key, use_32bit_registry=False):
access_mask = registry.registry_32[use_32bit_registry] | win32con.KEY_ALL_ACCESS
if not key_exists(local_hive, local_key, use_32bit_registry):
log.debug('"%s\\%s" not found', hive, key)
return False
if (len(key) > 1) and (key.count('\\', 1) < registry.subkey_slash_check[hkey]):
log.error(
'Hive:%s Key:%s; key is too close to root, not safe to remove',
hive, key
)
'"%s\\%s" is too close to root, not safe to remove', hive, key)
return False
# Functions for traversing the registry tree
@ -849,7 +877,7 @@ def delete_key_recursive(hive, key, use_32bit_registry=False):
subkey = win32api.RegEnumKey(_key, i)
yield _to_mbcs(subkey)
i += 1
except Exception: # pylint: disable=broad-except
except win32api.error:
break
def _traverse_registry_tree(_hkey, _keypath, _ret, _access_mask):
@ -874,13 +902,21 @@ def delete_key_recursive(hive, key, use_32bit_registry=False):
# Delete all sub_keys
for sub_key_path in key_list:
key_handle = None
try:
key_handle = win32api.RegOpenKeyEx(hkey, sub_key_path, 0, access_mask)
win32api.RegDeleteKey(key_handle, '')
ret['Deleted'].append(r'{0}\{1}'.format(hive, sub_key_path))
except WindowsError as exc: # pylint: disable=E0602
try:
win32api.RegDeleteKey(key_handle, '')
ret['Deleted'].append(r'{0}\{1}'.format(hive, sub_key_path))
except WindowsError as exc: # pylint: disable=undefined-variable
log.error(exc, exc_info=True)
ret['Failed'].append(r'{0}\{1} {2}'.format(hive, sub_key_path, exc))
except win32api.error as exc:
log.error(exc, exc_info=True)
ret['Failed'].append(r'{0}\{1} {2}'.format(hive, sub_key_path, exc))
ret['Failed'].append(r'{0}\{1} {2}'.format(hive, sub_key_path, exc.strerror))
finally:
if key_handle:
win32api.CloseHandle(key_handle)
broadcast_change()
@ -940,16 +976,10 @@ def delete_value(hive, key, vname=None, use_32bit_registry=False):
win32api.RegDeleteValue(handle, local_vname)
broadcast_change()
return True
except Exception as exc: # pylint: disable=broad-except
except win32api.error as exc:
if exc.winerror == 2:
return None
else:
log.error(exc, exc_info=True)
log.error('Hive: %s', local_hive)
log.error('Key: %s', local_key)
log.error('ValueName: %s', local_vname)
log.error('32bit Reg: %s', use_32bit_registry)
return False
raise
finally:
if handle:
win32api.RegCloseKey(handle)

View file

@ -17,6 +17,10 @@ import salt.modules.pkg_resource as pkg_resource
import salt.modules.win_pkg as win_pkg
import salt.utils.data
import salt.utils.platform
import salt.utils.win_reg as win_reg
# Import 3rd Party Libs
from salt.ext import six
@skipIf(not salt.utils.platform.is_windows(), "Must be on Windows!")
@ -57,6 +61,12 @@ class WinPkgInstallTestCase(TestCase, LoaderModuleMockMixin):
'pkg_resource.stringify': pkg_resource.stringify,
'config.valid_fileproto': config.valid_fileproto,
},
'__utils__': {
'reg.key_exists': win_reg.key_exists,
'reg.list_keys': win_reg.list_keys,
'reg.read_value': win_reg.read_value,
'reg.value_exists': win_reg.value_exists,
},
},
pkg_resource: {
'__grains__': {
@ -65,6 +75,16 @@ class WinPkgInstallTestCase(TestCase, LoaderModuleMockMixin):
},
}
def test_pkg__get_reg_software(self):
result = win_pkg._get_reg_software()
self.assertTrue(isinstance(result, dict))
found_python = False
search = 'Python 2' if six.PY2 else 'Python 3'
for key in result:
if search in key:
found_python = True
self.assertTrue(found_python)
def test_pkg_install_not_found(self):
'''
Test pkg.install when the Version is NOT FOUND in the Software

File diff suppressed because it is too large Load diff