Skip to content

Commit

Permalink
Enhance CredDelete to work with dictionaries (#2198)
Browse files Browse the repository at this point in the history
  • Loading branch information
CristiFati authored Mar 12, 2024
1 parent b622db4 commit 1d29e4a
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 10 deletions.
60 changes: 50 additions & 10 deletions win32/src/win32credmodule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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;
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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 <o PyUnicode>|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 <o dict>|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))
Expand Down Expand Up @@ -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;
}
73 changes: 73 additions & 0 deletions win32/test/test_win32cred.py
Original file line number Diff line number Diff line change
@@ -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())

0 comments on commit 1d29e4a

Please sign in to comment.