Skip to content

Support additional cred attributes #108

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 33 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
18739d2
Factor out constants for pywin32 submodule
itziakos Feb 13, 2022
9da3b18
Move constants to a top level module
itziakos Feb 27, 2022
23b02ba
Remove constrains to AttributeCount and Attribute
itziakos Feb 27, 2022
4b6ac7e
Add test module for the core authentication module
itziakos Feb 27, 2022
98ffabe
Fix flake8 errors
itziakos May 14, 2022
ded9e96
Fix import
itziakos May 14, 2022
c8333b1
Fix import to constants
itziakos May 14, 2022
f21fbfa
Fix errors from rebase
itziakos Apr 26, 2023
68b9fba
Another draft attempt to support CREDENTIAL_ATTRIBUTES with ctypes
itziakos Mar 9, 2024
b39904d
Add more credential related constants
itziakos Mar 9, 2024
0765ae2
re-enable tests
itziakos Mar 9, 2024
e580c33
Fix flake8 errors
itziakos Mar 9, 2024
25c72ee
restrict writing one credential attribute for ctypes
itziakos Mar 9, 2024
2ee9f38
Test reading mulitple credential attributes
itziakos Mar 9, 2024
b8bf0e7
Add note about the current limitations of the implementation
itziakos Mar 9, 2024
15e5bfc
Fix flake8 errors
itziakos Mar 9, 2024
3b9cf57
Support credential atrributes in cffi backend
itziakos Mar 23, 2024
c1b9ad5
Update tests for the authentication module
itziakos Mar 23, 2024
d1f3534
minor cleanup
itziakos Mar 23, 2024
4f4e243
Fix linting errors
itziakos Mar 23, 2024
4a2b628
Fix credential attribute convetion for ctypes
itziakos Mar 23, 2024
0f0c741
Augment testing
itziakos Apr 6, 2024
591cf80
Update code to avoid memory access issues
itziakos Apr 17, 2024
02446e3
fix flake8 errors
itziakos Apr 17, 2024
88e3312
Move GetTickCount function to the system_information module
itziakos May 4, 2024
9a91ec0
Add module for the winbase components in the cffi backend
itziakos May 4, 2024
5362fc9
Move gettickcount to _system_information in all backends
itziakos Sep 14, 2024
4c75711
Update .gitignore
itziakos Sep 14, 2024
47ccd69
Add a winbase module in core
itziakos Sep 14, 2024
9377d30
We do not need systemtime
itziakos Sep 14, 2024
11588db
Consolidate C definitions
itziakos Sep 14, 2024
e108c0b
fix flake8 errors
itziakos Sep 14, 2024
252ee24
Fix tests
itziakos Sep 14, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ build/
dist/
/docs/build
/docs/source/api
win32ctypes/version.py
1 change: 1 addition & 0 deletions test-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ coverage
haas
--only-binary pywin32
pywin32
parameterized
23 changes: 23 additions & 0 deletions win32ctypes/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#
# (C) Copyright 2014 Enthought, Inc., Austin, TX
# All right reserved.
#
# This file is open source software distributed according to the terms in
# LICENSE.txt
#

# Credential related constants
CRED_TYPE_GENERIC = 0x1
CRED_PERSIST_SESSION = 0x1
CRED_PERSIST_LOCAL_MACHINE = 0x2
CRED_PERSIST_ENTERPRISE = 0x3
CRED_ENUMERATE_ALL_CREDENTIALS = 0x1
CRED_PRESERVE_CREDENTIAL_BLOB = 0x1
CRED_MAX_ATTRIBUTES = 64
CRED_MAX_STRING_LENGTH = 256
CRED_MAX_USERNAME_LENGTH = 513
CRED_MAX_VALUE_SIZE = 256

# Library related constants
LOAD_LIBRARY_AS_DATAFILE = 0x2
LANG_NEUTRAL = 0x00
2 changes: 1 addition & 1 deletion win32ctypes/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,6 @@ def find_spec(self, fullname, path, target=None):


sys.meta_path.append(BackendFinder([
'_dll', '_authentication', '_time',
'_dll', '_authentication', '_winbase',
'_common', '_resource', '_nl_support',
'_system_information']))
2 changes: 2 additions & 0 deletions win32ctypes/core/cffi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
# LICENSE.txt
#
import logging
# Initialize the ffi cdef
from . import _cdefinitions # noqa

logger = logging.getLogger(__name__)
logger.debug('Loaded cffi backend')
204 changes: 109 additions & 95 deletions win32ctypes/core/cffi/_authentication.py
Original file line number Diff line number Diff line change
@@ -1,65 +1,16 @@
#
# (C) Copyright 2015 Enthought, Inc., Austin, TX
# (C) Copyright 2015-2024 Enthought, Inc., Austin, TX
# All right reserved.
#
# This file is open source software distributed according to the terms in
# LICENSE.txt
#
from weakref import WeakKeyDictionary

from win32ctypes.core.compat import is_text
from ._util import ffi, check_false, dlls
from ._nl_support import _GetACP
from ._common import _PyBytes_FromStringAndSize


ffi.cdef("""

typedef struct _FILETIME {
DWORD dwLowDateTime;
DWORD dwHighDateTime;
} FILETIME, *PFILETIME;

typedef struct _CREDENTIAL_ATTRIBUTE {
LPWSTR Keyword;
DWORD Flags;
DWORD ValueSize;
LPBYTE Value;
} CREDENTIAL_ATTRIBUTE, *PCREDENTIAL_ATTRIBUTE;

typedef struct _CREDENTIAL {
DWORD Flags;
DWORD Type;
LPWSTR TargetName;
LPWSTR Comment;
FILETIME LastWritten;
DWORD CredentialBlobSize;
LPBYTE CredentialBlob;
DWORD Persist;
DWORD AttributeCount;
PCREDENTIAL_ATTRIBUTE Attributes;
LPWSTR TargetAlias;
LPWSTR UserName;
} CREDENTIAL, *PCREDENTIAL;


BOOL WINAPI CredReadW(
LPCWSTR TargetName, DWORD Type, DWORD Flags, PCREDENTIAL *Credential);
BOOL WINAPI CredWriteW(PCREDENTIAL Credential, DWORD);
VOID WINAPI CredFree(PVOID Buffer);
BOOL WINAPI CredDeleteW(LPCWSTR TargetName, DWORD Type, DWORD Flags);
BOOL WINAPI CredEnumerateW(
LPCWSTR Filter, DWORD Flags, DWORD *Count, PCREDENTIAL **Credential);
""")

_keep_alive = WeakKeyDictionary()


SUPPORTED_CREDKEYS = set((
u'Type', u'TargetName', u'Persist',
u'UserName', u'Comment', u'CredentialBlob'))


def make_unicode(password):
""" Convert the input string to unicode.

Expand All @@ -77,41 +28,72 @@ def __call__(self):
return ffi.new("PCREDENTIAL")[0]

@classmethod
def fromdict(cls, credential, flag=0):
unsupported = set(credential.keys()) - SUPPORTED_CREDKEYS
if len(unsupported):
raise ValueError("Unsupported keys: {0}".format(unsupported))
if flag != 0:
raise ValueError("flag != 0 not yet supported")

def fromdict(cls, credential, flags=0):
factory = cls()
c_creds = factory()
# values to ref and make sure that they will not go away
values = []
c_credential = factory()
values = [] # keep values in context during processing
for key, value in credential.items():
if key == u'CredentialBlob':
blob = make_unicode(value)
blob_data = ffi.new('wchar_t[]', blob)
# new adds a NULL at the end that we do not want.
c_creds.CredentialBlobSize = \
ffi.sizeof(blob_data) - ffi.sizeof('wchar_t')
c_creds.CredentialBlob = ffi.cast('LPBYTE', blob_data)
values.append(blob_data)
elif key in (u'Type', u'Persist'):
setattr(c_creds, key, value)
elif value is None:
setattr(c_creds, key, ffi.NULL)
else:
blob = make_unicode(value)
pblob = ffi.new('wchar_t[]', blob)
values.append(pblob)
setattr(c_creds, key, ffi.cast('LPTSTR', pblob))
# keep values alive until c_creds goes away.
_keep_alive[c_creds] = tuple(values)
return c_creds
if key == 'CredentialBlob':
blob = ffi.new('wchar_t[]', value)
c_credential.CredentialBlob = ffi.cast('LPBYTE', blob)
c_credential.CredentialBlobSize = ffi.sizeof(blob) - ffi.sizeof('wchar_t') # noqa
values.append(blob)
elif key == 'Attributes':
count = len(value)
if count == 0:
continue
elif count > 1:
raise ValueError('Multiple attributes are not supported')
c_attribute = CREDENTIAL_ATTRIBUTE.fromdict(value[0])
c_credential.Attributes = PCREDENTIAL_ATTRIBUTE(c_attribute)
c_credential.AttributeCount = count
values.append(c_attribute)
elif key in ('Type', 'Persist', 'Flags'):
setattr(c_credential, key, value)
elif key in ('TargetName', 'Comment', 'TargetAlias', 'UserName'):
if value is None:
setattr(c_credential, key, ffi.NULL)
else:
blob_pointer = ffi.new('wchar_t[]', value)
setattr(
c_credential, key, ffi.cast('LPWSTR', blob_pointer))
values.append(blob_pointer)
return c_credential


class _CREDENTIAL_ATTRIBUTE(object):

def __call__(self):
return ffi.new("PCREDENTIAL_ATTRIBUTE")[0]

@classmethod
def fromdict(cls, attribute, flags=0):
factory = cls()
c_attribute = factory()
c_attribute.Flags = attribute.get('Flags', flags)
keyword = attribute.get('Keyword', None)
if keyword is None:
c_attribute.Keyword = ffi.NULL
else:
blob_pointer = ffi.new('wchar_t[]', keyword)
c_attribute.Keyword = ffi.cast('LPWSTR', blob_pointer)
value = attribute['Value']
if len(value) == 0:
data, size = ffi.NULL, 0
elif is_text(value):
blob = ffi.new('wchar_t[]', value)
data = ffi.cast('LPBYTE', blob)
size = ffi.sizeof(blob) - ffi.sizeof('wchar_t') # noqa
else:
data = ffi.new('BYTE[]', value)
size = ffi.sizeof(blob_pointer) - ffi.sizeof('BYTE')
c_attribute.Value = data
c_attribute.ValueSize = size
return c_attribute


CREDENTIAL = _CREDENTIAL()
CREDENTIAL_ATTRIBUTE = _CREDENTIAL_ATTRIBUTE()


def PCREDENTIAL(value=None):
Expand All @@ -126,47 +108,79 @@ def PPPCREDENTIAL(value=None):
return ffi.new("PCREDENTIAL**", ffi.NULL if value is None else value)


def credential2dict(pc_creds):
credentials = {}
for key in SUPPORTED_CREDKEYS:
if key == u'CredentialBlob':
def PCREDENTIAL_ATTRIBUTE(value=None):
return ffi.new(
"PCREDENTIAL_ATTRIBUTE", ffi.NULL if value is None else value)


def credential_attribute2dict(c_attribute):
attribute = {}
keyword = c_attribute.Keyword
if keyword == ffi.NULL:
attribute['Keyword'] = None
else:
attribute['Keyword'] = ffi.string(keyword)
attribute['Flags'] = c_attribute.Flags
size = c_attribute.ValueSize
if size > 0:
value = _PyBytes_FromStringAndSize(c_attribute.Value, size)
attribute['Value'] = value
return attribute


def credential2dict(c_credential):
credential = {}
for key in dir(c_credential):
if key == 'CredentialBlob':
data = _PyBytes_FromStringAndSize(
pc_creds.CredentialBlob, pc_creds.CredentialBlobSize)
elif key in (u'Type', u'Persist'):
data = int(getattr(pc_creds, key))
else:
string_pointer = getattr(pc_creds, key)
c_credential.CredentialBlob, c_credential.CredentialBlobSize)
elif key == 'Attributes':
attributes = []
count = c_credential.AttributeCount
c_attributes = c_credential.Attributes
for index in range(count):
attribute = credential_attribute2dict(c_attributes[index])
attributes.append(attribute)
data = tuple(attributes)
elif key == 'LastWritten':
data = c_credential.LastWritten
elif key in ('Type', 'Persist', 'Flags'):
data = int(getattr(c_credential, key))
elif key in ('TargetName', 'Comment', 'TargetAlias', 'UserName'):
string_pointer = getattr(c_credential, key)
if string_pointer == ffi.NULL:
data = None
else:
data = ffi.string(string_pointer)
credentials[key] = data
return credentials
else:
continue
credential[key] = data
return credential


def _CredRead(TargetName, Type, Flags, ppCredential):
target = make_unicode(TargetName)
return check_false(
dlls.advapi32.CredReadW(target, Type, Flags, ppCredential),
u'CredRead')
'CredRead')


def _CredWrite(Credential, Flags):
return check_false(
dlls.advapi32.CredWriteW(Credential, Flags), u'CredWrite')
dlls.advapi32.CredWriteW(Credential, Flags), 'CredWrite')


def _CredDelete(TargetName, Type, Flags):
return check_false(
dlls.advapi32.CredDeleteW(
make_unicode(TargetName), Type, Flags), u'CredDelete')
make_unicode(TargetName), Type, Flags), 'CredDelete')


def _CredEnumerate(Filter, Flags, Count, pppCredential):
filter = make_unicode(Filter) if Filter is not None else ffi.NULL
return check_false(
dlls.advapi32.CredEnumerateW(filter, Flags, Count, pppCredential),
u'CredEnumerate')
'CredEnumerate')


_CredFree = dlls.advapi32.CredFree
Loading