Skip to content

Commit bd3429a

Browse files
authored
Add support for CredEnumerate (#111)
* Add code for CredEnumerate in the cffi backend (based on Add CredEnumerate function #109) * Add code for CredEnumerate in ctypes * Cleanup code a little bit * Add tests for CredEnumerate fixes #110
1 parent 80b0661 commit bd3429a

File tree

7 files changed

+195
-88
lines changed

7 files changed

+195
-88
lines changed

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
filename = os.path.join(HERE, 'win32ctypes', 'version.py')
1010
with open(filename, 'w') as handle:
11-
handle.write('__version__="%s"\n' % version)
11+
handle.write(f'__version__ = "{version}"\n')
1212

1313
setup(
1414
name='pywin32-ctypes',

win32ctypes/core/cffi/_authentication.py

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from weakref import WeakKeyDictionary
99

1010
from win32ctypes.core.compat import is_text
11-
from ._util import ffi, check_zero, dlls
11+
from ._util import ffi, check_false, dlls
1212
from ._nl_support import _GetACP
1313
from ._common import _PyBytes_FromStringAndSize
1414

@@ -48,7 +48,8 @@
4848
BOOL WINAPI CredWriteW(PCREDENTIAL Credential, DWORD);
4949
VOID WINAPI CredFree(PVOID Buffer);
5050
BOOL WINAPI CredDeleteW(LPCWSTR TargetName, DWORD Type, DWORD Flags);
51-
51+
BOOL WINAPI CredEnumerateW(
52+
LPCWSTR Filter, DWORD Flags, DWORD *Count, PCREDENTIAL **Credential);
5253
""")
5354

5455
_keep_alive = WeakKeyDictionary()
@@ -120,6 +121,10 @@ def PPCREDENTIAL(value=None):
120121
return ffi.new("PCREDENTIAL*", ffi.NULL if value is None else value)
121122

122123

124+
def PPPCREDENTIAL(value=None):
125+
return ffi.new("PCREDENTIAL**", ffi.NULL if value is None else value)
126+
127+
123128
def credential2dict(pc_creds):
124129
credentials = {}
125130
for key in SUPPORTED_CREDKEYS:
@@ -140,21 +145,27 @@ def credential2dict(pc_creds):
140145

141146
def _CredRead(TargetName, Type, Flags, ppCredential):
142147
target = make_unicode(TargetName)
143-
value = check_zero(
148+
return check_false(
144149
dlls.advapi32.CredReadW(target, Type, Flags, ppCredential),
145150
u'CredRead')
146-
return value
147151

148152

149153
def _CredWrite(Credential, Flags):
150-
return check_zero(
154+
return check_false(
151155
dlls.advapi32.CredWriteW(Credential, Flags), u'CredWrite')
152156

153157

154158
def _CredDelete(TargetName, Type, Flags):
155-
return check_zero(
159+
return check_false(
156160
dlls.advapi32.CredDeleteW(
157161
make_unicode(TargetName), Type, Flags), u'CredDelete')
158162

159163

164+
def _CredEnumerate(Filter, Flags, Count, pppCredential):
165+
filter = make_unicode(Filter) if Filter is not None else ffi.NULL
166+
return check_false(
167+
dlls.advapi32.CredEnumerateW(filter, Flags, Count, pppCredential),
168+
u'CredEnumerate')
169+
170+
160171
_CredFree = dlls.advapi32.CredFree

win32ctypes/core/cffi/_common.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,7 @@ def byreference(x):
2323

2424
def dereference(x):
2525
return x[0]
26+
27+
28+
def PDWORD(value=0):
29+
return ffi.new("DWORD *", value)

win32ctypes/core/ctypes/_authentication.py

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
BOOL, DWORD, FILETIME, LPCWSTR)
1212

1313
from win32ctypes.core.compat import is_text
14-
from ._common import LPBYTE, _PyBytes_FromStringAndSize
15-
from ._util import function_factory, check_zero_factory, dlls
14+
from ._common import LPBYTE, _PyBytes_FromStringAndSize, PDWORD
15+
from ._util import function_factory, check_false_factory, dlls
1616
from ._nl_support import _GetACP
1717

1818

@@ -67,17 +67,19 @@ def fromdict(cls, credential, flags=0):
6767

6868

6969
PCREDENTIAL = POINTER(CREDENTIAL)
70+
PPCREDENTIAL = POINTER(PCREDENTIAL)
71+
PPPCREDENTIAL = POINTER(PPCREDENTIAL)
7072

7173

72-
def make_unicode(password):
74+
def make_unicode(text):
7375
""" Convert the input string to unicode.
7476
7577
"""
76-
if is_text(password):
77-
return password
78+
if is_text(text):
79+
return text
7880
else:
7981
code_page = _GetACP()
80-
return password.decode(encoding=str(code_page), errors='strict')
82+
return text.decode(encoding=str(code_page), errors='strict')
8183

8284

8385
def credential2dict(creds):
@@ -93,22 +95,33 @@ def credential2dict(creds):
9395
return credential
9496

9597

98+
def _CredEnumerate(Filter, Flags, Count, pppCredential):
99+
filter_ = LPCWSTR(Filter)
100+
_BaseCredEnumerate(filter_, Flags, Count, pppCredential)
101+
102+
96103
_CredWrite = function_factory(
97104
dlls.advapi32.CredWriteW,
98105
[PCREDENTIAL, DWORD],
99106
BOOL,
100-
check_zero_factory("CredWrite"))
107+
check_false_factory("CredWrite"))
101108

102109
_CredRead = function_factory(
103110
dlls.advapi32.CredReadW,
104-
[LPCWSTR, DWORD, DWORD, POINTER(PCREDENTIAL)],
111+
[LPCWSTR, DWORD, DWORD, PPCREDENTIAL],
105112
BOOL,
106-
check_zero_factory("CredRead"))
113+
check_false_factory("CredRead"))
107114

108115
_CredDelete = function_factory(
109116
dlls.advapi32.CredDeleteW,
110117
[LPCWSTR, DWORD, DWORD],
111118
BOOL,
112-
check_zero_factory("CredDelete"))
119+
check_false_factory("CredDelete"))
120+
121+
_BaseCredEnumerate = function_factory(
122+
dlls.advapi32.CredEnumerateW,
123+
[LPCWSTR, DWORD, PDWORD, PPPCREDENTIAL],
124+
BOOL,
125+
check_false_factory("CredEnumerate"))
113126

114127
_CredFree = function_factory(dlls.advapi32.CredFree, [PCREDENTIAL])

win32ctypes/pywin32/pywintypes.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,6 @@ def pywin32error():
3232
try:
3333
yield
3434
except WindowsError as exception:
35+
if not hasattr(exception, 'function'):
36+
exception.function = 'unknown'
3537
raise error(exception.winerror, exception.function, exception.strerror)

win32ctypes/pywin32/win32cred.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
CRED_PERSIST_LOCAL_MACHINE = 0x2
1515
CRED_PERSIST_ENTERPRISE = 0x3
1616
CRED_PRESERVE_CREDENTIAL_BLOB = 0
17+
CRED_ENUMERATE_ALL_CREDENTIALS = 0x1
1718

1819

1920
def CredWrite(Credential, Flags=CRED_PRESERVE_CREDENTIAL_BLOB):
@@ -89,3 +90,49 @@ def CredDelete(TargetName, Type, Flags=0):
8990
raise ValueError("Type != CRED_TYPE_GENERIC not yet supported.")
9091
with _pywin32error():
9192
_authentication._CredDelete(TargetName, Type, 0)
93+
94+
95+
def CredEnumerate(Filter=None, Flags=0):
96+
""" Remove the given target name from the stored credentials.
97+
98+
Parameters
99+
----------
100+
Filter : unicode
101+
Matches credentials' target names by prefix, can be None.
102+
Flags : int
103+
When set to CRED_ENUMERATE_ALL_CREDENTIALS enumerates all of
104+
the credentials in the user's credential set but in that
105+
case the Filter parameter should be NULL, an error is
106+
raised otherwise
107+
108+
Returns
109+
-------
110+
credentials : list
111+
Returns a sequence of CREDENTIAL dictionaries.
112+
113+
"""
114+
with _pywin32error():
115+
if _backend == 'cffi':
116+
pcount = _common.PDWORD()
117+
pppcreds = _authentication.PPPCREDENTIAL()
118+
_authentication._CredEnumerate(Filter, Flags, pcount, pppcreds)
119+
count = pcount[0]
120+
pcreds = _common.dereference(
121+
_common.ffi.cast(f"PCREDENTIAL*[{count}]", pppcreds))
122+
else:
123+
import ctypes
124+
count = ctypes.DWORD()
125+
# Create a mutable pointer variable
126+
mem = ctypes.create_string_buffer(1)
127+
pppcreds = _common.cast(
128+
mem, _authentication.PPPCREDENTIAL)
129+
_authentication._CredEnumerate(
130+
Filter, Flags, _common.byreference(count), pppcreds)
131+
count = count.value
132+
pcreds = _common.dereference(_common.dereference(pppcreds))
133+
try:
134+
return [
135+
_authentication.credential2dict(pcreds[i])
136+
for i in range(count)]
137+
finally:
138+
_authentication._CredFree(pcreds)

0 commit comments

Comments
 (0)