Skip to content

Commit 3e3551f

Browse files
committed
Support credential atrributes in cffi backend
1 parent 76b0d8f commit 3e3551f

File tree

1 file changed

+132
-52
lines changed

1 file changed

+132
-52
lines changed

win32ctypes/core/cffi/_authentication.py

Lines changed: 132 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
from ._nl_support import _GetACP
1313
from ._common import _PyBytes_FromStringAndSize
1414

15+
# values to ref and make sure that they will not go away
16+
_keep_alive = WeakKeyDictionary()
1517

1618
ffi.cdef("""
1719
@@ -52,13 +54,6 @@
5254
LPCWSTR Filter, DWORD Flags, DWORD *Count, PCREDENTIAL **Credential);
5355
""")
5456

55-
_keep_alive = WeakKeyDictionary()
56-
57-
58-
SUPPORTED_CREDKEYS = set((
59-
u'Type', u'TargetName', u'Persist',
60-
u'UserName', u'Comment', u'CredentialBlob'))
61-
6257

6358
def make_unicode(password):
6459
""" Convert the input string to unicode.
@@ -70,48 +65,101 @@ def make_unicode(password):
7065
code_page = _GetACP()
7166
return password.decode(encoding=str(code_page), errors='strict')
7267

68+
class _FILETIME(object):
69+
70+
def __call__(self):
71+
return ffi.new("PFILETIME")[0]
72+
73+
@classmethod
74+
def fromdict(cls, filetime):
75+
factory = cls()
76+
c_filetime = factory()
77+
c_filetime.dwLowDateTime = filetime['dwLowDateTime']
78+
c_filetime.dwHighDateTime = filetime['dwHighDateTime']
79+
return c_filetime
80+
7381

7482
class _CREDENTIAL(object):
7583

7684
def __call__(self):
7785
return ffi.new("PCREDENTIAL")[0]
7886

7987
@classmethod
80-
def fromdict(cls, credential, flag=0):
81-
unsupported = set(credential.keys()) - SUPPORTED_CREDKEYS
82-
if len(unsupported):
83-
raise ValueError("Unsupported keys: {0}".format(unsupported))
84-
if flag != 0:
85-
raise ValueError("flag != 0 not yet supported")
86-
88+
def fromdict(cls, credential, flags=0):
8789
factory = cls()
88-
c_creds = factory()
89-
# values to ref and make sure that they will not go away
90-
values = []
90+
c_credential = factory()
91+
values = [] # values to ref and make sure that they will not go away
9192
for key, value in credential.items():
92-
if key == u'CredentialBlob':
93-
blob = make_unicode(value)
94-
blob_data = ffi.new('wchar_t[]', blob)
95-
# new adds a NULL at the end that we do not want.
96-
c_creds.CredentialBlobSize = \
97-
ffi.sizeof(blob_data) - ffi.sizeof('wchar_t')
98-
c_creds.CredentialBlob = ffi.cast('LPBYTE', blob_data)
99-
values.append(blob_data)
100-
elif key in (u'Type', u'Persist'):
101-
setattr(c_creds, key, value)
102-
elif value is None:
103-
setattr(c_creds, key, ffi.NULL)
104-
else:
105-
blob = make_unicode(value)
106-
pblob = ffi.new('wchar_t[]', blob)
107-
values.append(pblob)
108-
setattr(c_creds, key, ffi.cast('LPTSTR', pblob))
109-
# keep values alive until c_creds goes away.
110-
_keep_alive[c_creds] = tuple(values)
111-
return c_creds
93+
if key == 'CredentialBlob':
94+
blob = ffi.new('wchar_t[]', value)
95+
c_credential.CredentialBlob = ffi.cast('LPBYTE', blob)
96+
c_credential.CredentialBlobSize = ffi.sizeof(blob) - ffi.sizeof('wchar_t') # noqa
97+
values.append(blob)
98+
elif key == 'Attributes':
99+
count = len(value)
100+
if count == 0:
101+
continue
102+
elif count > 1:
103+
raise ValueError('Multiple attributes are not supported')
104+
c_attribute = CREDENTIAL_ATTRIBUTE.fromdict(value[0])
105+
c_credential.Attributes = PCREDENTIAL_ATTRIBUTE(c_attribute)
106+
c_credential.AttributeCount = count
107+
values.append(c_attribute)
108+
elif key in ('Type', 'Persist', 'Flags'):
109+
setattr(c_credential, key, value)
110+
elif key in ('TargetName', 'Comment', 'TargetAlias', 'UserName'):
111+
if value is None:
112+
setattr(c_credential, key, ffi.NULL)
113+
else:
114+
blob_pointer = ffi.new('wchar_t[]', value)
115+
setattr(c_credential, key, ffi.cast('LPWSTR', blob_pointer))
116+
values.append(blob_pointer)
117+
118+
# keep values alive until c_credential goes away.
119+
_keep_alive[c_credential] = tuple(values)
120+
return c_credential
121+
122+
123+
class _CREDENTIAL_ATTRIBUTE(object):
124+
125+
def __call__(self):
126+
return ffi.new("PCREDENTIAL_ATTRIBUTE")[0]
127+
128+
@classmethod
129+
def fromdict(cls, attribute, flags=0):
130+
factory = cls()
131+
c_attribute = factory()
132+
c_attribute.Flags = attribute.get('Flags', flags)
133+
keyword = attribute.get('Keyword', None)
134+
if keyword is None:
135+
c_attribute.Keyword = ffi.NULL
136+
else:
137+
blob_pointer = ffi.new('wchar_t[]', keyword)
138+
c_attribute.Keyword = ffi.cast('LPWSTR', blob_pointer)
139+
value = attribute['Value']
140+
if len(value) == 0:
141+
data, size = ffi.NULL, 0
142+
elif is_text(value):
143+
blob = ffi.new('wchar_t[]', value)
144+
_keep_alive[c_attribute] = blob
145+
data = ffi.cast('LPBYTE', blob)
146+
size = ffi.sizeof(blob) - ffi.sizeof('wchar_t') # noqa
147+
else:
148+
data = ffi.new('BYTE[]', value)
149+
size = ffi.sizeof(blob_pointer) - ffi.sizeof('BYTE')
150+
_keep_alive[c_attribute] = data
151+
c_attribute.Value = data
152+
c_attribute.ValueSize = size
153+
return c_attribute
112154

113155

114156
CREDENTIAL = _CREDENTIAL()
157+
CREDENTIAL_ATTRIBUTE = _CREDENTIAL_ATTRIBUTE()
158+
FILETIME = _FILETIME()
159+
160+
161+
def PFILETIME(value=None):
162+
return ffi.new("PFILETIME", ffi.NULL if value is None else value)
115163

116164

117165
def PCREDENTIAL(value=None):
@@ -126,47 +174,79 @@ def PPPCREDENTIAL(value=None):
126174
return ffi.new("PCREDENTIAL**", ffi.NULL if value is None else value)
127175

128176

129-
def credential2dict(pc_creds):
130-
credentials = {}
131-
for key in SUPPORTED_CREDKEYS:
132-
if key == u'CredentialBlob':
177+
def PCREDENTIAL_ATTRIBUTE(value=None):
178+
return ffi.new(
179+
"PCREDENTIAL_ATTRIBUTE", ffi.NULL if value is None else value)
180+
181+
182+
def credential_attribute2dict(c_attribute):
183+
attribute = {}
184+
keyword = c_attribute.Keyword
185+
if keyword == ffi.NULL:
186+
attribute['Keyword'] = None
187+
else:
188+
attribute['Keyword'] = ffi.string(keyword)
189+
attribute['Flags'] = c_attribute.Flags
190+
size = c_attribute.ValueSize
191+
if size > 0:
192+
value = _PyBytes_FromStringAndSize(c_attribute.Value, size)
193+
attribute['Value'] = value
194+
return attribute
195+
196+
197+
def credential2dict(c_credential):
198+
credential = {}
199+
for key in dir(c_credential):
200+
if key == 'CredentialBlob':
133201
data = _PyBytes_FromStringAndSize(
134-
pc_creds.CredentialBlob, pc_creds.CredentialBlobSize)
135-
elif key in (u'Type', u'Persist'):
136-
data = int(getattr(pc_creds, key))
137-
else:
138-
string_pointer = getattr(pc_creds, key)
202+
c_credential.CredentialBlob, c_credential.CredentialBlobSize)
203+
elif key == 'Attributes':
204+
attributes = []
205+
count = c_credential.AttributeCount
206+
c_attributes = c_credential.Attributes
207+
for index in range(count):
208+
attribute = credential_attribute2dict(c_attributes[index])
209+
attributes.append(attribute)
210+
data = tuple(attributes)
211+
elif key == 'LastWritten':
212+
data = c_credential.LastWritten
213+
elif key in ('Type', 'Persist', 'Flags'):
214+
data = int(getattr(c_credential, key))
215+
elif key in ('TargetName', 'Comment', 'TargetAlias', 'UserName'):
216+
string_pointer = getattr(c_credential, key)
139217
if string_pointer == ffi.NULL:
140218
data = None
141219
else:
142220
data = ffi.string(string_pointer)
143-
credentials[key] = data
144-
return credentials
221+
else:
222+
continue
223+
credential[key] = data
224+
return credential
145225

146226

147227
def _CredRead(TargetName, Type, Flags, ppCredential):
148228
target = make_unicode(TargetName)
149229
return check_false(
150230
dlls.advapi32.CredReadW(target, Type, Flags, ppCredential),
151-
u'CredRead')
231+
'CredRead')
152232

153233

154234
def _CredWrite(Credential, Flags):
155235
return check_false(
156-
dlls.advapi32.CredWriteW(Credential, Flags), u'CredWrite')
236+
dlls.advapi32.CredWriteW(Credential, Flags), 'CredWrite')
157237

158238

159239
def _CredDelete(TargetName, Type, Flags):
160240
return check_false(
161241
dlls.advapi32.CredDeleteW(
162-
make_unicode(TargetName), Type, Flags), u'CredDelete')
242+
make_unicode(TargetName), Type, Flags), 'CredDelete')
163243

164244

165245
def _CredEnumerate(Filter, Flags, Count, pppCredential):
166246
filter = make_unicode(Filter) if Filter is not None else ffi.NULL
167247
return check_false(
168248
dlls.advapi32.CredEnumerateW(filter, Flags, Count, pppCredential),
169-
u'CredEnumerate')
249+
'CredEnumerate')
170250

171251

172252
_CredFree = dlls.advapi32.CredFree

0 commit comments

Comments
 (0)