diff --git a/win32/src/win32credmodule.cpp b/win32/src/win32credmodule.cpp index 7fd9f947fc..4856010e15 100644 --- a/win32/src/win32credmodule.cpp +++ b/win32/src/win32credmodule.cpp @@ -62,14 +62,14 @@ BOOL PyWinObject_AsCREDENTIAL_ATTRIBUTE(PyObject *obattr, PCREDENTIAL_ATTRIBUTE if (!PyWinObject_AsWCHAR(obKeyword, &attr->Keyword, FALSE)) { goto done; } - // Handle `Value`: the docs https://docs.microsoft.com/en-us/windows/win32/api/wincred/ns-wincred-credential_attributew - // say it's an LPBYTE Value (meaning it's just bytes) but then the description says "Data - // associated with the attribute. By convention, if Value is a text string, then Value should - // not include the trailing zero character and should be in UNICODE." + // Handle `Value`: the docs + // https://docs.microsoft.com/en-us/windows/win32/api/wincred/ns-wincred-credential_attributew say it's an LPBYTE + // Value (meaning it's just bytes) but then the description says "Data associated with the attribute. By convention, + // if Value is a text string, then Value should not include the trailing zero character and should be in UNICODE." if (PyUnicode_Check(obValue)) { Py_ssize_t nchars = PyUnicode_GetLength(obValue); Py_ssize_t nbytes = nchars * sizeof(wchar_t); - attr->ValueSize = nbytes; + attr->ValueSize = nbytes; if (attr->ValueSize == -1) { goto done; } @@ -82,7 +82,8 @@ BOOL PyWinObject_AsCREDENTIAL_ATTRIBUTE(PyObject *obattr, PCREDENTIAL_ATTRIBUTE if (PyUnicode_AsWideChar(obValue, (wchar_t *)attr->Value, nchars) == -1) { goto done; } - } else { + } + else { // Use the buffer API to get bytes if possible. if (!pybuf.init(obValue)) { goto done; @@ -472,7 +473,7 @@ PyObject *PyCredUnmarshalCredential(PyObject *self, PyObject *args, PyObject *kw case CertCredential: ret = Py_BuildValue("kN", credtype, PyBytes_FromStringAndSize((char *)&((PCERT_CREDENTIAL_INFO)credential)->rgbHashOfCert, - CERT_HASH_LENGTH)); + CERT_HASH_LENGTH)); break; // @flag UsernameTargetCredential|Unicode string containing username case UsernameTargetCredential: @@ -622,16 +623,53 @@ PyObject *PyCredReadDomainCredentials(PyObject *self, PyObject *args, PyObject * PyObject *PyCredDelete(PyObject *self, PyObject *args, PyObject *kwargs) { static char *keywords[] = {"TargetName", "Type", "Flags", NULL}; - PyObject *obtargetname, *ret = NULL; + PyObject *obtargetname, *ret = NULL, *obtarget; WCHAR *targetname; DWORD cred_type, flags = 0; - if (!PyArg_ParseTupleAndKeywords( args, kwargs, "Ok|k:CredDelete", keywords, &obtargetname, // @pyparm |TargetName||Target of credential to be deleted &cred_type, // @pyparm int|Type||One of the CRED_TYPE_* values &flags)) // @pyparm int|Flags|0|Reserved, use only 0 - return NULL; + { + char *kwds[] = {"Target", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O:CredDelete", kwds, + &obtarget // @pyparm |Target||Credential to be deleted + )) + return NULL; + PyErr_Clear(); + if (!PyDict_Check(obtarget)) { + PyErr_SetString(PyExc_TypeError, + "First argument must be either a dictionary (no other arguments allowed) or a string " + "(other arguments required)"); + return NULL; + } + obtargetname = PyDict_GetItemString(obtarget, keywords[0]); + if (!obtargetname) { + PyErr_SetString(PyExc_KeyError, keywords[0]); + return NULL; + } + PyObject *val = PyDict_GetItemString(obtarget, keywords[1]); + if (!val) { + PyErr_SetString(PyExc_KeyError, keywords[1]); + return NULL; + } + if (!PyLong_Check(val)) { + PyErr_SetString(PyExc_TypeError, "Argument should be int"); + return NULL; + } + cred_type = PyLong_AsUnsignedLong(val); + val = PyDict_GetItemString(obtarget, keywords[2]); + if (!val) { + PyErr_SetString(PyExc_KeyError, keywords[2]); + return NULL; + } + if (!PyLong_Check(val)) { + PyErr_SetString(PyExc_TypeError, "Argument should be int"); + return NULL; + } + flags = PyLong_AsUnsignedLong(val); + } if (!PyWinObject_AsWCHAR(obtargetname, &targetname, FALSE)) return NULL; if (!CredDelete(targetname, cred_type, flags)) @@ -1169,5 +1207,7 @@ PYWIN_MODULE_INIT_FUNC(win32cred) PyModule_AddIntConstant(module, "CREDUI_MAX_USERNAME_LENGTH", CREDUI_MAX_USERNAME_LENGTH); PyModule_AddIntConstant(module, "CREDUI_MAX_PASSWORD_LENGTH", CREDUI_MAX_PASSWORD_LENGTH); + PyModule_AddIntConstant(module, "CRED_ENUMERATE_ALL_CREDENTIALS", CRED_ENUMERATE_ALL_CREDENTIALS); + PYWIN_MODULE_INIT_RETURN_SUCCESS; } diff --git a/win32/test/test_win32cred.py b/win32/test/test_win32cred.py new file mode 100644 index 0000000000..e4af181353 --- /dev/null +++ b/win32/test/test_win32cred.py @@ -0,0 +1,73 @@ +import copy +import unittest + +import win32cred + + +class TestCredFunctions(unittest.TestCase): + + def setUp(self): + self.flags = 0 + self.dummy_cred = { + "TargetName": "DumyyUser", + "Type": win32cred.CRED_TYPE_GENERIC, + "Flags": self.flags, + } + + def create_dummy_cred(self): + cred = copy.deepcopy(self.dummy_cred) + cred.update( + { + "Persist": win32cred.CRED_PERSIST_SESSION, + } + ) + try: + win32cred.CredWrite(cred, self.flags) + except Exception as e: + print(e) + + def is_dummy_cred(self): + return ( + len( + [ + e + for e in win32cred.CredEnumerate() + if e["TargetName"] == self.dummy_cred["TargetName"] + ] + ) + == 1 + ) + + def test_creddelete(self): + self.create_dummy_cred() + self.assertTrue(self.is_dummy_cred()) + self.assertIsNone(win32cred.CredDelete(**self.dummy_cred)) + self.assertFalse(self.is_dummy_cred()) + self.create_dummy_cred() + self.assertTrue(self.is_dummy_cred()) + self.assertIsNone(win32cred.CredDelete(*self.dummy_cred.values())) + self.assertFalse(self.is_dummy_cred()) + self.create_dummy_cred() + self.assertTrue(self.is_dummy_cred()) + self.assertIsNone( + win32cred.CredDelete(self.dummy_cred["TargetName"], self.dummy_cred["Type"]) + ) + self.assertFalse(self.is_dummy_cred()) + self.create_dummy_cred() + self.assertTrue(self.is_dummy_cred()) + self.assertIsNone(win32cred.CredDelete(self.dummy_cred)) + self.assertFalse(self.is_dummy_cred()) + self.create_dummy_cred() + self.assertTrue(self.is_dummy_cred()) + self.assertIsNone(win32cred.CredDelete(Target=self.dummy_cred)) + self.assertFalse(self.is_dummy_cred()) + self.create_dummy_cred() + self.assertTrue(self.is_dummy_cred()) + self.assertRaises(TypeError, win32cred.CredDelete, "") + self.assertRaises(KeyError, win32cred.CredDelete, {}) + self.assertRaises(KeyError, win32cred.CredDelete, {"TargetName": ""}) + self.assertRaises( + TypeError, win32cred.CredDelete, {"TargetName": "", "Type": 3.141593} + ) + self.assertIsNone(win32cred.CredDelete(self.dummy_cred)) + self.assertFalse(self.is_dummy_cred())