bpo-39007: Add auditing events to functions in winreg (GH-17541)

Also allows winreg.CloseKey() to accept same types as other functions.
This commit is contained in:
Steve Dower 2019-12-09 11:18:12 -08:00 committed by GitHub
parent b8cbe74c34
commit ee17e37356
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 217 additions and 26 deletions

View File

@ -53,6 +53,8 @@ This module offers the following functions:
The return value is the handle of the opened key. If the function fails, an
:exc:`OSError` exception is raised.
.. audit-event:: winreg.ConnectRegistry computer_name,key winreg.ConnectRegistry
.. versionchanged:: 3.3
See :ref:`above <exception-changed>`.
@ -75,6 +77,10 @@ This module offers the following functions:
The return value is the handle of the opened key. If the function fails, an
:exc:`OSError` exception is raised.
.. audit-event:: winreg.CreateKey key,sub_key,access winreg.CreateKey
.. audit-event:: winreg.OpenKey/result key winreg.CreateKey
.. versionchanged:: 3.3
See :ref:`above <exception-changed>`.
@ -103,6 +109,10 @@ This module offers the following functions:
The return value is the handle of the opened key. If the function fails, an
:exc:`OSError` exception is raised.
.. audit-event:: winreg.CreateKey key,sub_key,access winreg.CreateKeyEx
.. audit-event:: winreg.OpenKey/result key winreg.CreateKeyEx
.. versionadded:: 3.2
.. versionchanged:: 3.3
@ -124,6 +134,8 @@ This module offers the following functions:
If the method succeeds, the entire key, including all of its values, is removed.
If the method fails, an :exc:`OSError` exception is raised.
.. audit-event:: winreg.DeleteKey key,sub_key,access winreg.DeleteKey
.. versionchanged:: 3.3
See :ref:`above <exception-changed>`.
@ -158,6 +170,8 @@ This module offers the following functions:
On unsupported Windows versions, :exc:`NotImplementedError` is raised.
.. audit-event:: winreg.DeleteKey key,sub_key,access winreg.DeleteKeyEx
.. versionadded:: 3.2
.. versionchanged:: 3.3
@ -173,6 +187,8 @@ This module offers the following functions:
*value* is a string that identifies the value to remove.
.. audit-event:: winreg.DeleteValue key,value winreg.DeleteValue
.. function:: EnumKey(key, index)
@ -187,6 +203,8 @@ This module offers the following functions:
typically called repeatedly until an :exc:`OSError` exception is
raised, indicating, no more values are available.
.. audit-event:: winreg.EnumKey key,index winreg.EnumKey
.. versionchanged:: 3.3
See :ref:`above <exception-changed>`.
@ -220,6 +238,8 @@ This module offers the following functions:
| | :meth:`SetValueEx`) |
+-------+--------------------------------------------+
.. audit-event:: winreg.EnumValue key,index winreg.EnumValue
.. versionchanged:: 3.3
See :ref:`above <exception-changed>`.
@ -235,6 +255,8 @@ This module offers the following functions:
>>> ExpandEnvironmentStrings('%windir%')
'C:\\Windows'
.. audit-event:: winreg.ExpandEnvironmentStrings str winreg.ExpandEnvironmentStrings
.. function:: FlushKey(key)
@ -279,6 +301,8 @@ This module offers the following functions:
If *key* is a handle returned by :func:`ConnectRegistry`, then the path
specified in *file_name* is relative to the remote computer.
.. audit-event:: winreg.LoadKey key,sub_key,file_name winreg.LoadKey
.. function:: OpenKey(key, sub_key, reserved=0, access=KEY_READ)
OpenKeyEx(key, sub_key, reserved=0, access=KEY_READ)
@ -300,6 +324,10 @@ This module offers the following functions:
If the function fails, :exc:`OSError` is raised.
.. audit-event:: winreg.OpenKey key,sub_key,access winreg.OpenKey
.. audit-event:: winreg.OpenKey/result key winreg.OpenKey
.. versionchanged:: 3.2
Allow the use of named arguments.
@ -330,6 +358,8 @@ This module offers the following functions:
| | nanoseconds since Jan 1, 1601. |
+-------+---------------------------------------------+
.. audit-event:: winreg.QueryInfoKey key winreg.QueryInfoKey
.. function:: QueryValue(key, sub_key)
@ -347,6 +377,8 @@ This module offers the following functions:
underlying API call doesn't return the type, so always use
:func:`QueryValueEx` if possible.
.. audit-event:: winreg.QueryValue key,sub_key,value_name winreg.QueryValue
.. function:: QueryValueEx(key, value_name)
@ -370,6 +402,8 @@ This module offers the following functions:
| | :meth:`SetValueEx`) |
+-------+-----------------------------------------+
.. audit-event:: winreg.QueryValue key,sub_key,value_name winreg.QueryValueEx
.. function:: SaveKey(key, file_name)
@ -393,6 +427,8 @@ This module offers the following functions:
This function passes ``NULL`` for *security_attributes* to the API.
.. audit-event:: winreg.SaveKey key,file_name winreg.SaveKey
.. function:: SetValue(key, sub_key, type, value)
@ -419,6 +455,8 @@ This module offers the following functions:
The key identified by the *key* parameter must have been opened with
:const:`KEY_SET_VALUE` access.
.. audit-event:: winreg.SetValue key,sub_key,type,value winreg.SetValue
.. function:: SetValueEx(key, value_name, reserved, type, value)
@ -447,6 +485,8 @@ This module offers the following functions:
bytes) should be stored as files with the filenames stored in the configuration
registry. This helps the registry perform efficiently.
.. audit-event:: winreg.SetValue key,sub_key,type,value winreg.SetValueEx
.. function:: DisableReflectionKey(key)
@ -463,6 +503,8 @@ This module offers the following functions:
effect. Disabling reflection for a key does not affect reflection of any
subkeys.
.. audit-event:: winreg.DisableReflectionKey key winreg.DisableReflectionKey
.. function:: EnableReflectionKey(key)
@ -476,6 +518,8 @@ This module offers the following functions:
Restoring reflection for a key does not affect reflection of any subkeys.
.. audit-event:: winreg.EnableReflectionKey key winreg.EnableReflectionKey
.. function:: QueryReflectionKey(key)
@ -489,6 +533,8 @@ This module offers the following functions:
Will generally raise :exc:`NotImplementedError` if executed on a 32-bit
operating system.
.. audit-event:: winreg.QueryReflectionKey key winreg.QueryReflectionKey
.. _constants:
@ -741,6 +787,9 @@ integer handle, and also disconnect the Windows handle from the handle object.
handle is not closed. You would call this function when you need the
underlying Win32 handle to exist beyond the lifetime of the handle object.
.. audit-event:: winreg.PyHKEY.Detach key winreg.PyHKEY.Detach
.. method:: PyHKEY.__enter__()
PyHKEY.__exit__(\*exc_info)

View File

@ -304,6 +304,29 @@ def test_unraisablehook():
write_unraisable_exc(RuntimeError("nonfatal-error"), "for audit hook test", None)
def test_winreg():
from winreg import OpenKey, EnumKey, CloseKey, HKEY_LOCAL_MACHINE
def hook(event, args):
if not event.startswith("winreg."):
return
print(event, *args)
sys.addaudithook(hook)
k = OpenKey(HKEY_LOCAL_MACHINE, "Software")
EnumKey(k, 0)
try:
EnumKey(k, 10000)
except OSError:
pass
else:
raise RuntimeError("Expected EnumKey(HKLM, 10000) to fail")
kv = k.Detach()
CloseKey(kv)
if __name__ == "__main__":
from test.libregrtest.setup import suppress_msvcrt_asserts

View File

@ -104,6 +104,20 @@ class AuditTest(unittest.TestCase):
"RuntimeError('nonfatal-error') Exception ignored for audit hook test",
)
def test_winreg(self):
support.import_module("winreg")
returncode, events, stderr = self.run_python("test_winreg")
if returncode:
self.fail(stderr)
self.assertEqual(events[0][0], "winreg.OpenKey")
self.assertEqual(events[1][0], "winreg.OpenKey/result")
expected = events[1][2]
self.assertTrue(expected)
self.assertSequenceEqual(["winreg.EnumKey", " ", f"{expected} 0"], events[2])
self.assertSequenceEqual(["winreg.EnumKey", " ", f"{expected} 10000"], events[3])
self.assertSequenceEqual(["winreg.PyHKEY.Detach", " ", expected], events[4])
if __name__ == "__main__":
unittest.main()

View File

@ -0,0 +1 @@
Add auditing events to functions in :mod:`winreg`.

View File

@ -293,6 +293,9 @@ winreg_HKEYType_Detach_impl(PyHKEYObject *self)
/*[clinic end generated code: output=dda5a9e1a01ae78f input=dd2cc09e6c6ba833]*/
{
void* ret;
if (PySys_Audit("winreg.PyHKEY.Detach", "n", (Py_ssize_t)self->hkey) < 0) {
return NULL;
}
ret = (void*)self->hkey;
self->hkey = 0;
return PyLong_FromVoidPtr(ret);
@ -397,15 +400,15 @@ BOOL
PyHKEY_Close(PyObject *ob_handle)
{
LONG rc;
PyHKEYObject *key;
HKEY key;
if (!PyHKEY_Check(ob_handle)) {
PyErr_SetString(PyExc_TypeError, "bad operand type");
if (!PyHKEY_AsHKEY(ob_handle, &key, TRUE)) {
return FALSE;
}
key = (PyHKEYObject *)ob_handle;
rc = key->hkey ? RegCloseKey((HKEY)key->hkey) : ERROR_SUCCESS;
key->hkey = 0;
if (PyHKEY_Check(ob_handle)) {
((PyHKEYObject*)ob_handle)->hkey = 0;
}
rc = key ? RegCloseKey(key) : ERROR_SUCCESS;
if (rc != ERROR_SUCCESS)
PyErr_SetFromWindowsErrWithFunction(rc, "RegCloseKey");
return rc == ERROR_SUCCESS;
@ -841,6 +844,10 @@ winreg_ConnectRegistry_impl(PyObject *module,
{
HKEY retKey;
long rc;
if (PySys_Audit("winreg.ConnectRegistry", "un",
computer_name, (Py_ssize_t)key) < 0) {
return NULL;
}
Py_BEGIN_ALLOW_THREADS
rc = RegConnectRegistryW(computer_name, key, &retKey);
Py_END_ALLOW_THREADS
@ -878,11 +885,20 @@ winreg_CreateKey_impl(PyObject *module, HKEY key, const Py_UNICODE *sub_key)
HKEY retKey;
long rc;
if (PySys_Audit("winreg.CreateKey", "nun",
(Py_ssize_t)key, sub_key,
(Py_ssize_t)KEY_WRITE) < 0) {
return NULL;
}
rc = RegCreateKeyW(key, sub_key, &retKey);
if (rc != ERROR_SUCCESS) {
PyErr_SetFromWindowsErrWithFunction(rc, "CreateKey");
return NULL;
}
if (PySys_Audit("winreg.OpenKey/result", "n",
(Py_ssize_t)retKey) < 0) {
return NULL;
}
return retKey;
}
@ -919,12 +935,21 @@ winreg_CreateKeyEx_impl(PyObject *module, HKEY key,
HKEY retKey;
long rc;
if (PySys_Audit("winreg.CreateKey", "nun",
(Py_ssize_t)key, sub_key,
(Py_ssize_t)access) < 0) {
return NULL;
}
rc = RegCreateKeyExW(key, sub_key, reserved, NULL, 0,
access, NULL, &retKey, NULL);
if (rc != ERROR_SUCCESS) {
PyErr_SetFromWindowsErrWithFunction(rc, "CreateKeyEx");
return NULL;
}
if (PySys_Audit("winreg.OpenKey/result", "n",
(Py_ssize_t)retKey) < 0) {
return NULL;
}
return retKey;
}
@ -951,6 +976,11 @@ winreg_DeleteKey_impl(PyObject *module, HKEY key, const Py_UNICODE *sub_key)
/*[clinic end generated code: output=d2652a84f70e0862 input=b31d225b935e4211]*/
{
long rc;
if (PySys_Audit("winreg.DeleteKey", "nun",
(Py_ssize_t)key, sub_key,
(Py_ssize_t)0) < 0) {
return NULL;
}
rc = RegDeleteKeyW(key, sub_key );
if (rc != ERROR_SUCCESS)
return PyErr_SetFromWindowsErrWithFunction(rc, "RegDeleteKey");
@ -992,13 +1022,17 @@ winreg_DeleteKeyEx_impl(PyObject *module, HKEY key,
RDKEFunc pfn = NULL;
long rc;
if (PySys_Audit("winreg.DeleteKey", "nun",
(Py_ssize_t)key, sub_key,
(Py_ssize_t)access) < 0) {
return NULL;
}
/* Only available on 64bit platforms, so we must load it
dynamically. */
Py_BEGIN_ALLOW_THREADS
hMod = GetModuleHandleW(L"advapi32.dll");
if (hMod)
pfn = (RDKEFunc)GetProcAddress(hMod,
"RegDeleteKeyExW");
pfn = (RDKEFunc)GetProcAddress(hMod, "RegDeleteKeyExW");
Py_END_ALLOW_THREADS
if (!pfn) {
PyErr_SetString(PyExc_NotImplementedError,
@ -1031,6 +1065,10 @@ winreg_DeleteValue_impl(PyObject *module, HKEY key, const Py_UNICODE *value)
/*[clinic end generated code: output=56fa9d21f3a54371 input=a78d3407a4197b21]*/
{
long rc;
if (PySys_Audit("winreg.DeleteValue", "nu",
(Py_ssize_t)key, value) < 0) {
return NULL;
}
Py_BEGIN_ALLOW_THREADS
rc = RegDeleteValueW(key, value);
Py_END_ALLOW_THREADS
@ -1063,6 +1101,10 @@ winreg_EnumKey_impl(PyObject *module, HKEY key, int index)
long rc;
PyObject *retStr;
if (PySys_Audit("winreg.EnumKey", "ni",
(Py_ssize_t)key, index) < 0) {
return NULL;
}
/* The Windows docs claim that the max key name length is 255
* characters, plus a terminating nul character. However,
* empirical testing demonstrates that it is possible to
@ -1121,6 +1163,10 @@ winreg_EnumValue_impl(PyObject *module, HKEY key, int index)
PyObject *obData;
PyObject *retVal;
if (PySys_Audit("winreg.EnumValue", "ni",
(Py_ssize_t)key, index) < 0) {
return NULL;
}
if ((rc = RegQueryInfoKeyW(key, NULL, NULL, NULL, NULL, NULL, NULL,
NULL,
&retValueSize, &retDataSize, NULL, NULL))
@ -1204,6 +1250,11 @@ winreg_ExpandEnvironmentStrings_impl(PyObject *module,
DWORD rc;
PyObject *o;
if (PySys_Audit("winreg.ExpandEnvironmentStrings", "u",
string) < 0) {
return NULL;
}
retValueSize = ExpandEnvironmentStringsW(string, retValue, 0);
if (retValueSize == 0) {
return PyErr_SetFromWindowsErrWithFunction(retValueSize,
@ -1295,6 +1346,10 @@ winreg_LoadKey_impl(PyObject *module, HKEY key, const Py_UNICODE *sub_key,
{
long rc;
if (PySys_Audit("winreg.LoadKey", "nuu",
(Py_ssize_t)key, sub_key, file_name) < 0) {
return NULL;
}
Py_BEGIN_ALLOW_THREADS
rc = RegLoadKeyW(key, sub_key, file_name );
Py_END_ALLOW_THREADS
@ -1330,6 +1385,11 @@ winreg_OpenKey_impl(PyObject *module, HKEY key, const Py_UNICODE *sub_key,
HKEY retKey;
long rc;
if (PySys_Audit("winreg.OpenKey", "nun",
(Py_ssize_t)key, sub_key,
(Py_ssize_t)access) < 0) {
return NULL;
}
Py_BEGIN_ALLOW_THREADS
rc = RegOpenKeyExW(key, sub_key, reserved, access, &retKey);
Py_END_ALLOW_THREADS
@ -1337,6 +1397,10 @@ winreg_OpenKey_impl(PyObject *module, HKEY key, const Py_UNICODE *sub_key,
PyErr_SetFromWindowsErrWithFunction(rc, "RegOpenKeyEx");
return NULL;
}
if (PySys_Audit("winreg.OpenKey/result", "n",
(Py_ssize_t)retKey) < 0) {
return NULL;
}
return retKey;
}
@ -1384,15 +1448,20 @@ winreg_QueryInfoKey_impl(PyObject *module, HKEY key)
PyObject *l;
PyObject *ret;
if (PySys_Audit("winreg.QueryInfoKey", "n", (Py_ssize_t)key) < 0) {
return NULL;
}
if ((rc = RegQueryInfoKey(key, NULL, NULL, 0, &nSubKeys, NULL, NULL,
&nValues, NULL, NULL, NULL, &ft))
!= ERROR_SUCCESS)
!= ERROR_SUCCESS) {
return PyErr_SetFromWindowsErrWithFunction(rc, "RegQueryInfoKey");
}
li.LowPart = ft.dwLowDateTime;
li.HighPart = ft.dwHighDateTime;
l = PyLong_FromLongLong(li.QuadPart);
if (l == NULL)
if (l == NULL) {
return NULL;
}
ret = Py_BuildValue("iiO", nSubKeys, nValues, l);
Py_DECREF(l);
return ret;
@ -1430,6 +1499,10 @@ winreg_QueryValue_impl(PyObject *module, HKEY key, const Py_UNICODE *sub_key)
DWORD retSize = 0;
wchar_t *tmp;
if (PySys_Audit("winreg.QueryValue", "nuu",
(Py_ssize_t)key, sub_key, NULL) < 0) {
return NULL;
}
rc = RegQueryValueW(key, sub_key, NULL, &retSize);
if (rc == ERROR_MORE_DATA)
retSize = 256;
@ -1497,6 +1570,10 @@ winreg_QueryValueEx_impl(PyObject *module, HKEY key, const Py_UNICODE *name)
PyObject *obData;
PyObject *result;
if (PySys_Audit("winreg.QueryValue", "nuu",
(Py_ssize_t)key, NULL, name) < 0) {
return NULL;
}
rc = RegQueryValueExW(key, name, NULL, NULL, NULL, &bufSize);
if (rc == ERROR_MORE_DATA)
bufSize = 256;
@ -1570,6 +1647,10 @@ winreg_SaveKey_impl(PyObject *module, HKEY key, const Py_UNICODE *file_name)
if (!PyWinObject_AsSECURITY_ATTRIBUTES(obSA, &pSA, TRUE))
return NULL;
*/
if (PySys_Audit("winreg.SaveKey", "nu",
(Py_ssize_t)key, file_name) < 0) {
return NULL;
}
Py_BEGIN_ALLOW_THREADS
rc = RegSaveKeyW(key, file_name, pSA );
Py_END_ALLOW_THREADS
@ -1622,6 +1703,12 @@ winreg_SetValue_impl(PyObject *module, HKEY key, const Py_UNICODE *sub_key,
return NULL;
}
if (PySys_Audit("winreg.SetValue", "nunu#",
(Py_ssize_t)key, sub_key, (Py_ssize_t)type,
value, value_length) < 0) {
return NULL;
}
Py_BEGIN_ALLOW_THREADS
rc = RegSetValueW(key, sub_key, REG_SZ, value, (DWORD)(value_length + 1));
Py_END_ALLOW_THREADS
@ -1692,6 +1779,11 @@ winreg_SetValueEx_impl(PyObject *module, HKEY key,
"Could not convert the data to the specified type.");
return NULL;
}
if (PySys_Audit("winreg.SetValue", "nunO",
(Py_ssize_t)key, value_name, (Py_ssize_t)type,
value) < 0) {
return NULL;
}
Py_BEGIN_ALLOW_THREADS
rc = RegSetValueExW(key, value_name, 0, type, data, len);
Py_END_ALLOW_THREADS
@ -1727,6 +1819,10 @@ winreg_DisableReflectionKey_impl(PyObject *module, HKEY key)
RDRKFunc pfn = NULL;
LONG rc;
if (PySys_Audit("winreg.DisableReflectionKey", "n", (Py_ssize_t)key) < 0) {
return NULL;
}
/* Only available on 64bit platforms, so we must load it
dynamically.*/
Py_BEGIN_ALLOW_THREADS
@ -1772,6 +1868,10 @@ winreg_EnableReflectionKey_impl(PyObject *module, HKEY key)
RERKFunc pfn = NULL;
LONG rc;
if (PySys_Audit("winreg.EnableReflectionKey", "n", (Py_ssize_t)key) < 0) {
return NULL;
}
/* Only available on 64bit platforms, so we must load it
dynamically.*/
Py_BEGIN_ALLOW_THREADS
@ -1816,6 +1916,10 @@ winreg_QueryReflectionKey_impl(PyObject *module, HKEY key)
BOOL result;
LONG rc;
if (PySys_Audit("winreg.QueryReflectionKey", "n", (Py_ssize_t)key) < 0) {
return NULL;
}
/* Only available on 64bit platforms, so we must load it
dynamically.*/
Py_BEGIN_ALLOW_THREADS