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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#
# (C) Copyright 2015 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
 
from weakref import WeakKeyDictionary
 
from win32ctypes.core.compat import is_text
from ._util import ffi, check_zero, 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);
 
""")
 
_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.
 
    """
    if is_text(password):
        return password
    else:
        code_page = _GetACP()
        return password.decode(encoding=str(code_page), errors='strict')
 
 
class _CREDENTIAL(object):
 
    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")
 
        factory = cls()
        c_creds = factory()
        # values to ref and make sure that they will not go away
        values = []
        for key in SUPPORTED_CREDKEYS:
            if key in credential:
                if key == u'CredentialBlob':
                    blob = make_unicode(credential['CredentialBlob'])
                    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, credential[key])
                else:
                    blob = make_unicode(credential[key])
                    value = ffi.new('wchar_t[]', blob)
                    values.append(value)
                    setattr(c_creds, key, ffi.cast('LPTSTR', value))
        # keep values alive until c_creds goes away.
        _keep_alive[c_creds] = tuple(values)
        return c_creds
 
 
CREDENTIAL = _CREDENTIAL()
 
 
def PCREDENTIAL(value=None):
    return ffi.new("PCREDENTIAL", ffi.NULL if value is None else value)
 
 
def PPCREDENTIAL(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':
            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)
            if string_pointer == ffi.NULL:
                data = u''
            else:
                data = ffi.string(string_pointer)
        credentials[key] = data
    return credentials
 
 
def _CredRead(TargetName, Type, Flags, ppCredential):
    target = make_unicode(TargetName)
    value = check_zero(
        dlls.advapi32.CredReadW(target, Type, Flags, ppCredential),
        u'CredRead')
    return value
 
 
def _CredWrite(Credential, Flags):
    return check_zero(
        dlls.advapi32.CredWriteW(Credential, Flags), u'CredWrite')
 
 
def _CredDelete(TargetName, Type, Flags):
    return check_zero(
        dlls.advapi32.CredDeleteW(
            make_unicode(TargetName), Type, Flags), u'CredDelete')
 
 
_CredFree = dlls.advapi32.CredFree