1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#
# (C) Copyright 2014-18 Enthought, Inc., Austin, TX
# All right reserved.
#
# This file is open source software distributed according to the terms in
# LICENSE.txt
#
from __future__ import absolute_import
 
import ctypes
from ctypes import POINTER, Structure, c_void_p, c_wchar_p, c_char_p, cast
from ctypes.wintypes import (
    BOOL, DWORD, FILETIME, LPCWSTR)
 
from win32ctypes.core.compat import is_text
from ._common import LPBYTE, _PyBytes_FromStringAndSize
from ._util import function_factory, check_zero_factory, dlls
from ._nl_support import _GetACP
 
 
SUPPORTED_CREDKEYS = set((
    u'Type', u'TargetName', u'Persist',
    u'UserName', u'Comment', u'CredentialBlob'))
 
 
class CREDENTIAL(Structure):
    _fields_ = [
        ("Flags", DWORD),
        ("Type", DWORD),
        ("TargetName", c_wchar_p),
        ("Comment", c_wchar_p),
        ("LastWritten", FILETIME),
        ("CredentialBlobSize", DWORD),
        ("CredentialBlob", LPBYTE),
        ("Persist", DWORD),
        ("_DO_NOT_USE_AttributeCount", DWORD),
        ("__DO_NOT_USE_Attribute", c_void_p),
        ("TargetAlias", c_wchar_p),
        ("UserName", c_wchar_p)]
 
    @classmethod
    def fromdict(cls, credential, flags=0):
        unsupported = set(credential.keys()) - SUPPORTED_CREDKEYS
        if len(unsupported):
            raise ValueError("Unsupported keys: {0}".format(unsupported))
        if flags != 0:
            raise ValueError("flag != 0 not yet supported")
 
        c_creds = cls()
        c_pcreds = PCREDENTIAL(c_creds)
 
        # zero-out memory
        ctypes.memset(c_pcreds, 0, ctypes.sizeof(c_creds))
 
        for key in SUPPORTED_CREDKEYS:
            if key in credential:
                if key != 'CredentialBlob':
                    setattr(c_creds, key, credential[key])
                else:
                    blob = make_unicode(credential['CredentialBlob'])
                    blob_data = ctypes.create_unicode_buffer(blob)
                    # Create_unicode_buffer adds a NULL at the end of the
                    # string we do not want that.
                    c_creds.CredentialBlobSize = \
                        ctypes.sizeof(blob_data) - \
                        ctypes.sizeof(ctypes.c_wchar)
                    c_creds.CredentialBlob = ctypes.cast(blob_data, LPBYTE)
        return c_creds
 
 
PCREDENTIAL = POINTER(CREDENTIAL)
 
 
def make_unicode(password):
    """ Convert the input string to unicode.
 
    """
    if is_text(password):
        return password
    else:
        code_page = _GetACP()
        return password.decode(encoding=str(code_page), errors='strict')
 
 
def credential2dict(creds):
    credential = {}
    for key in SUPPORTED_CREDKEYS:
        if key != u'CredentialBlob':
            credential[key] = getattr(creds, key)
        else:
            blob = _PyBytes_FromStringAndSize(
                cast(creds.CredentialBlob, c_char_p),
                creds.CredentialBlobSize)
            credential[u'CredentialBlob'] = blob
    return credential
 
 
_CredWrite = function_factory(
    dlls.advapi32.CredWriteW,
    [PCREDENTIAL, DWORD],
    BOOL,
    check_zero_factory("CredWrite"))
 
_CredRead = function_factory(
    dlls.advapi32.CredReadW,
    [LPCWSTR, DWORD, DWORD, POINTER(PCREDENTIAL)],
    BOOL,
    check_zero_factory("CredRead"))
 
_CredDelete = function_factory(
    dlls.advapi32.CredDeleteW,
    [LPCWSTR, DWORD, DWORD],
    BOOL,
    check_zero_factory("CredDelete"))
 
_CredFree = function_factory(dlls.advapi32.CredFree, [PCREDENTIAL])