d3d10core: Implement d3d10_device_SOSetTargets().
[wine] / dlls / advapi32 / cred.c
1 /*
2  * Credential Management APIs
3  *
4  * Copyright 2007 Robert Shearman for CodeWeavers
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include <stdarg.h>
22 #include <time.h>
23 #include <limits.h>
24
25 #ifdef __APPLE__
26 # include <Security/SecKeychain.h>
27 # include <Security/SecKeychainItem.h>
28 # include <Security/SecKeychainSearch.h>
29 #endif
30
31 #include "windef.h"
32 #include "winbase.h"
33 #include "winreg.h"
34 #include "wincred.h"
35 #include "winternl.h"
36
37 #include "crypt.h"
38
39 #include "wine/unicode.h"
40 #include "wine/debug.h"
41
42 WINE_DEFAULT_DEBUG_CHANNEL(cred);
43
44 /* the size of the ARC4 key used to encrypt the password data */
45 #define KEY_SIZE 8
46
47 static const WCHAR wszCredentialManagerKey[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\',
48     'C','r','e','d','e','n','t','i','a','l',' ','M','a','n','a','g','e','r',0};
49 static const WCHAR wszEncryptionKeyValue[] = {'E','n','c','r','y','p','t','i','o','n','K','e','y',0};
50
51 static const WCHAR wszFlagsValue[] = {'F','l','a','g','s',0};
52 static const WCHAR wszTypeValue[] = {'T','y','p','e',0};
53 static const WCHAR wszCommentValue[] = {'C','o','m','m','e','n','t',0};
54 static const WCHAR wszLastWrittenValue[] = {'L','a','s','t','W','r','i','t','t','e','n',0};
55 static const WCHAR wszPersistValue[] = {'P','e','r','s','i','s','t',0};
56 static const WCHAR wszTargetAliasValue[] = {'T','a','r','g','e','t','A','l','i','a','s',0};
57 static const WCHAR wszUserNameValue[] = {'U','s','e','r','N','a','m','e',0};
58 static const WCHAR wszPasswordValue[] = {'P','a','s','s','w','o','r','d',0};
59
60 static DWORD read_credential_blob(HKEY hkey, const BYTE key_data[KEY_SIZE],
61                                   LPBYTE credential_blob,
62                                   DWORD *credential_blob_size)
63 {
64     DWORD ret;
65     DWORD type;
66
67     *credential_blob_size = 0;
68     ret = RegQueryValueExW(hkey, wszPasswordValue, 0, &type, NULL, credential_blob_size);
69     if (ret != ERROR_SUCCESS)
70         return ret;
71     else if (type != REG_BINARY)
72         return ERROR_REGISTRY_CORRUPT;
73     if (credential_blob)
74     {
75         struct ustring data;
76         struct ustring key;
77
78         ret = RegQueryValueExW(hkey, wszPasswordValue, 0, &type, credential_blob,
79                                credential_blob_size);
80         if (ret != ERROR_SUCCESS)
81             return ret;
82         else if (type != REG_BINARY)
83             return ERROR_REGISTRY_CORRUPT;
84
85         key.Length = key.MaximumLength = KEY_SIZE;
86         key.Buffer = (unsigned char *)key_data;
87
88         data.Length = data.MaximumLength = *credential_blob_size;
89         data.Buffer = credential_blob;
90         SystemFunction032(&data, &key);
91     }
92     return ERROR_SUCCESS;
93 }
94
95 static DWORD registry_read_credential(HKEY hkey, PCREDENTIALW credential,
96                                       const BYTE key_data[KEY_SIZE],
97                                       char *buffer, DWORD *len)
98 {
99     DWORD type;
100     DWORD ret;
101     DWORD count;
102
103     ret = RegQueryValueExW(hkey, NULL, 0, &type, NULL, &count);
104     if (ret != ERROR_SUCCESS)
105         return ret;
106     else if (type != REG_SZ)
107         return ERROR_REGISTRY_CORRUPT;
108     *len += count;
109     if (credential)
110     {
111         credential->TargetName = (LPWSTR)buffer;
112         ret = RegQueryValueExW(hkey, NULL, 0, &type, (LPVOID)credential->TargetName,
113                                &count);
114         if (ret != ERROR_SUCCESS || type != REG_SZ) return ret;
115         buffer += count;
116     }
117
118     ret = RegQueryValueExW(hkey, wszCommentValue, 0, &type, NULL, &count);
119     if (ret != ERROR_FILE_NOT_FOUND && ret != ERROR_SUCCESS)
120         return ret;
121     else if (type != REG_SZ)
122         return ERROR_REGISTRY_CORRUPT;
123     *len += count;
124     if (credential)
125     {
126         credential->Comment = (LPWSTR)buffer;
127         ret = RegQueryValueExW(hkey, wszCommentValue, 0, &type, (LPVOID)credential->Comment,
128                                &count);
129         if (ret == ERROR_FILE_NOT_FOUND)
130             credential->Comment = NULL;
131         else if (ret != ERROR_SUCCESS)
132             return ret;
133         else if (type != REG_SZ)
134             return ERROR_REGISTRY_CORRUPT;
135         else
136             buffer += count;
137     }
138
139     ret = RegQueryValueExW(hkey, wszTargetAliasValue, 0, &type, NULL, &count);
140     if (ret != ERROR_FILE_NOT_FOUND && ret != ERROR_SUCCESS)
141         return ret;
142     else if (type != REG_SZ)
143         return ERROR_REGISTRY_CORRUPT;
144     *len += count;
145     if (credential)
146     {
147         credential->TargetAlias = (LPWSTR)buffer;
148         ret = RegQueryValueExW(hkey, wszTargetAliasValue, 0, &type, (LPVOID)credential->TargetAlias,
149                                &count);
150         if (ret == ERROR_FILE_NOT_FOUND)
151             credential->TargetAlias = NULL;
152         else if (ret != ERROR_SUCCESS)
153             return ret;
154         else if (type != REG_SZ)
155             return ERROR_REGISTRY_CORRUPT;
156         else
157             buffer += count;
158     }
159
160     ret = RegQueryValueExW(hkey, wszUserNameValue, 0, &type, NULL, &count);
161     if (ret != ERROR_FILE_NOT_FOUND && ret != ERROR_SUCCESS)
162         return ret;
163     else if (type != REG_SZ)
164         return ERROR_REGISTRY_CORRUPT;
165     *len += count;
166     if (credential)
167     {
168         credential->UserName = (LPWSTR)buffer;
169         ret = RegQueryValueExW(hkey, wszUserNameValue, 0, &type, (LPVOID)credential->UserName,
170                                &count);
171         if (ret == ERROR_FILE_NOT_FOUND)
172             credential->UserName = NULL;
173         else if (ret != ERROR_SUCCESS)
174             return ret;
175         else if (type != REG_SZ)
176             return ERROR_REGISTRY_CORRUPT;
177         else
178             buffer += count;
179     }
180
181     ret = read_credential_blob(hkey, key_data, NULL, &count);
182     if (ret != ERROR_FILE_NOT_FOUND && ret != ERROR_SUCCESS)
183         return ret;
184     *len += count;
185     if (credential)
186     {
187         credential->CredentialBlob = (LPBYTE)buffer;
188         ret = read_credential_blob(hkey, key_data, credential->CredentialBlob, &count);
189         if (ret == ERROR_FILE_NOT_FOUND)
190             credential->CredentialBlob = NULL;
191         else if (ret != ERROR_SUCCESS)
192             return ret;
193         credential->CredentialBlobSize = count;
194     }
195
196     /* FIXME: Attributes */
197     if (credential)
198     {
199         credential->AttributeCount = 0;
200         credential->Attributes = NULL;
201     }
202
203     if (!credential) return ERROR_SUCCESS;
204
205     count = sizeof(credential->Flags);
206     ret = RegQueryValueExW(hkey, wszFlagsValue, NULL, &type, (LPVOID)&credential->Flags,
207                            &count);
208     if (ret != ERROR_SUCCESS)
209         return ret;
210     else if (type != REG_DWORD)
211         return ERROR_REGISTRY_CORRUPT;
212     count = sizeof(credential->Type);
213     ret = RegQueryValueExW(hkey, wszTypeValue, NULL, &type, (LPVOID)&credential->Type,
214                            &count);
215     if (ret != ERROR_SUCCESS)
216         return ret;
217     else if (type != REG_DWORD)
218         return ERROR_REGISTRY_CORRUPT;
219
220     count = sizeof(credential->LastWritten);
221     ret = RegQueryValueExW(hkey, wszLastWrittenValue, NULL, &type, (LPVOID)&credential->LastWritten,
222                            &count);
223     if (ret != ERROR_SUCCESS)
224         return ret;
225     else if (type != REG_BINARY)
226         return ERROR_REGISTRY_CORRUPT;
227     count = sizeof(credential->Persist);
228     ret = RegQueryValueExW(hkey, wszPersistValue, NULL, &type, (LPVOID)&credential->Persist,
229                            &count);
230     if (ret == ERROR_SUCCESS && type != REG_DWORD)
231         return ERROR_REGISTRY_CORRUPT;
232     return ret;
233 }
234
235 #ifdef __APPLE__
236 static DWORD mac_read_credential_from_item(SecKeychainItemRef item, BOOL require_password,
237                                            PCREDENTIALW credential, char *buffer,
238                                            DWORD *len)
239 {
240     OSStatus status;
241     UInt32 i;
242     UInt32 cred_blob_len;
243     void *cred_blob;
244     LPWSTR domain = NULL;
245     LPWSTR user = NULL;
246     BOOL user_name_present = FALSE;
247     SecKeychainAttributeInfo info;
248     SecKeychainAttributeList *attr_list;
249     UInt32 info_tags[] = { kSecServerItemAttr, kSecSecurityDomainItemAttr, kSecAccountItemAttr,
250                            kSecCommentItemAttr, kSecCreationDateItemAttr };
251     info.count = sizeof(info_tags)/sizeof(info_tags[0]);
252     info.tag = info_tags;
253     info.format = NULL;
254     status = SecKeychainItemCopyAttributesAndData(item, &info, NULL, &attr_list, &cred_blob_len, &cred_blob);
255     if (status == errSecAuthFailed && !require_password)
256     {
257         cred_blob_len = 0;
258         cred_blob = NULL;
259         status = SecKeychainItemCopyAttributesAndData(item, &info, NULL, &attr_list, &cred_blob_len, NULL);
260     }
261     if (status != noErr)
262     {
263         WARN("SecKeychainItemCopyAttributesAndData returned status %ld\n", status);
264         return ERROR_NOT_FOUND;
265     }
266
267     for (i = 0; i < attr_list->count; i++)
268         if (attr_list->attr[i].tag == kSecAccountItemAttr && attr_list->attr[i].data)
269         {
270             user_name_present = TRUE;
271             break;
272         }
273     if (!user_name_present)
274     {
275         WARN("no kSecAccountItemAttr for item\n");
276         SecKeychainItemFreeAttributesAndData(attr_list, cred_blob);
277         return ERROR_NOT_FOUND;
278     }
279
280     if (buffer)
281     {
282         credential->Flags = 0;
283         credential->Type = CRED_TYPE_DOMAIN_PASSWORD;
284         credential->TargetName = NULL;
285         credential->Comment = NULL;
286         memset(&credential->LastWritten, 0, sizeof(credential->LastWritten));
287         credential->CredentialBlobSize = 0;
288         credential->CredentialBlob = NULL;
289         credential->Persist = CRED_PERSIST_LOCAL_MACHINE;
290         credential->AttributeCount = 0;
291         credential->Attributes = NULL;
292         credential->TargetAlias = NULL;
293         credential->UserName = NULL;
294     }
295     for (i = 0; i < attr_list->count; i++)
296     {
297         switch (attr_list->attr[i].tag)
298         {
299             case kSecServerItemAttr:
300                 TRACE("kSecServerItemAttr: %.*s\n", (int)attr_list->attr[i].length,
301                       (char *)attr_list->attr[i].data);
302                 if (!attr_list->attr[i].data) continue;
303                 if (buffer)
304                 {
305                     INT str_len;
306                     credential->TargetName = (LPWSTR)buffer;
307                     str_len = MultiByteToWideChar(CP_UTF8, 0, attr_list->attr[i].data,
308                                                   attr_list->attr[i].length, (LPWSTR)buffer, 0xffff);
309                     credential->TargetName[str_len] = '\0';
310                     buffer += (str_len + 1) * sizeof(WCHAR);
311                     *len += (str_len + 1) * sizeof(WCHAR);
312                 }
313                 else
314                 {
315                     INT str_len;
316                     str_len = MultiByteToWideChar(CP_UTF8, 0, attr_list->attr[i].data,
317                                                   attr_list->attr[i].length, NULL, 0);
318                     *len += (str_len + 1) * sizeof(WCHAR);
319                 }
320                 break;
321             case kSecAccountItemAttr:
322             {
323                 INT str_len;
324                 TRACE("kSecAccountItemAttr: %.*s\n", (int)attr_list->attr[i].length,
325                       (char *)attr_list->attr[i].data);
326                 if (!attr_list->attr[i].data) continue;
327                 str_len = MultiByteToWideChar(CP_UTF8, 0, attr_list->attr[i].data,
328                                               attr_list->attr[i].length, NULL, 0);
329                 user = HeapAlloc(GetProcessHeap(), 0, (str_len + 1) * sizeof(WCHAR));
330                 MultiByteToWideChar(CP_UTF8, 0, attr_list->attr[i].data,
331                                     attr_list->attr[i].length, user, str_len);
332                 user[str_len] = '\0';
333                 break;
334             }
335             case kSecCommentItemAttr:
336                 TRACE("kSecCommentItemAttr: %.*s\n", (int)attr_list->attr[i].length,
337                       (char *)attr_list->attr[i].data);
338                 if (!attr_list->attr[i].data) continue;
339                 if (buffer)
340                 {
341                     INT str_len;
342                     credential->Comment = (LPWSTR)buffer;
343                     str_len = MultiByteToWideChar(CP_UTF8, 0, attr_list->attr[i].data,
344                                                   attr_list->attr[i].length, (LPWSTR)buffer, 0xffff);
345                     credential->Comment[str_len] = '\0';
346                     buffer += (str_len + 1) * sizeof(WCHAR);
347                     *len += (str_len + 1) * sizeof(WCHAR);
348                 }
349                 else
350                 {
351                     INT str_len;
352                     str_len = MultiByteToWideChar(CP_UTF8, 0, attr_list->attr[i].data,
353                                                   attr_list->attr[i].length, NULL, 0);
354                     *len += (str_len + 1) * sizeof(WCHAR);
355                 }
356                 break;
357             case kSecSecurityDomainItemAttr:
358             {
359                 INT str_len;
360                 TRACE("kSecSecurityDomainItemAttr: %.*s\n", (int)attr_list->attr[i].length,
361                       (char *)attr_list->attr[i].data);
362                 if (!attr_list->attr[i].data) continue;
363                 str_len = MultiByteToWideChar(CP_UTF8, 0, attr_list->attr[i].data,
364                                               attr_list->attr[i].length, NULL, 0);
365                 domain = HeapAlloc(GetProcessHeap(), 0, (str_len + 1) * sizeof(WCHAR));
366                 MultiByteToWideChar(CP_UTF8, 0, attr_list->attr[i].data,
367                                     attr_list->attr[i].length, domain, str_len);
368                 domain[str_len] = '\0';
369                 break;
370             }
371             case kSecCreationDateItemAttr:
372                 TRACE("kSecCreationDateItemAttr: %.*s\n", (int)attr_list->attr[i].length,
373                       (char *)attr_list->attr[i].data);
374                 if (buffer)
375                 {
376                     LARGE_INTEGER win_time;
377                     struct tm tm;
378                     time_t time;
379                     memset(&tm, 0, sizeof(tm));
380                     strptime(attr_list->attr[i].data, "%Y%m%d%H%M%SZ", &tm);
381                     time = mktime(&tm);
382                     RtlSecondsSince1970ToTime(time, &win_time);
383                     credential->LastWritten.dwLowDateTime = win_time.u.LowPart;
384                     credential->LastWritten.dwHighDateTime = win_time.u.HighPart;
385                 }
386                 break;
387         }
388     }
389
390     if (user)
391     {
392         INT str_len;
393         if (buffer)
394             credential->UserName = (LPWSTR)buffer;
395         if (domain)
396         {
397             str_len = strlenW(domain);
398             *len += (str_len + 1) * sizeof(WCHAR);
399             if (buffer)
400             {
401                 memcpy(credential->UserName, domain, str_len * sizeof(WCHAR));
402                 /* FIXME: figure out when to use an '@' */
403                 credential->UserName[str_len] = '\\';
404                 buffer += (str_len + 1) * sizeof(WCHAR);
405             }
406         }
407         str_len = strlenW(user);
408         *len += (str_len + 1) * sizeof(WCHAR);
409         if (buffer)
410         {
411             memcpy(buffer, user, (str_len + 1) * sizeof(WCHAR));
412             buffer += (str_len + 1) * sizeof(WCHAR);
413             TRACE("UserName = %s\n", debugstr_w(credential->UserName));
414         }
415     }
416     HeapFree(GetProcessHeap(), 0, user);
417     HeapFree(GetProcessHeap(), 0, domain);
418
419     if (cred_blob)
420     {
421         if (buffer)
422         {
423             INT str_len;
424             credential->CredentialBlob = (BYTE *)buffer;
425             str_len = MultiByteToWideChar(CP_UTF8, 0, cred_blob, cred_blob_len,
426                                           (LPWSTR)buffer, 0xffff);
427             credential->CredentialBlobSize = str_len * sizeof(WCHAR);
428             *len += str_len * sizeof(WCHAR);
429         }
430         else
431         {
432             INT str_len;
433             str_len = MultiByteToWideChar(CP_UTF8, 0, cred_blob, cred_blob_len,
434                                           NULL, 0);
435             *len += str_len * sizeof(WCHAR);
436         }
437     }
438     SecKeychainItemFreeAttributesAndData(attr_list, cred_blob);
439     return ERROR_SUCCESS;
440 }
441 #endif
442
443 static DWORD write_credential_blob(HKEY hkey, LPCWSTR target_name, DWORD type,
444                                    const BYTE key_data[KEY_SIZE],
445                                    const BYTE *credential_blob, DWORD credential_blob_size)
446 {
447     LPBYTE encrypted_credential_blob;
448     struct ustring data;
449     struct ustring key;
450     DWORD ret;
451
452     key.Length = key.MaximumLength = KEY_SIZE;
453     key.Buffer = (unsigned char *)key_data;
454
455     encrypted_credential_blob = HeapAlloc(GetProcessHeap(), 0, credential_blob_size);
456     if (!encrypted_credential_blob) return ERROR_OUTOFMEMORY;
457
458     memcpy(encrypted_credential_blob, credential_blob, credential_blob_size);
459     data.Length = data.MaximumLength = credential_blob_size;
460     data.Buffer = encrypted_credential_blob;
461     SystemFunction032(&data, &key);
462
463     ret = RegSetValueExW(hkey, wszPasswordValue, 0, REG_BINARY, encrypted_credential_blob, credential_blob_size);
464     HeapFree(GetProcessHeap(), 0, encrypted_credential_blob);
465
466     return ret;
467 }
468
469 static DWORD registry_write_credential(HKEY hkey, const CREDENTIALW *credential,
470                                        const BYTE key_data[KEY_SIZE], BOOL preserve_blob)
471 {
472     DWORD ret;
473     FILETIME LastWritten;
474
475     GetSystemTimeAsFileTime(&LastWritten);
476
477     ret = RegSetValueExW(hkey, wszFlagsValue, 0, REG_DWORD, (const BYTE*)&credential->Flags,
478                          sizeof(credential->Flags));
479     if (ret != ERROR_SUCCESS) return ret;
480     ret = RegSetValueExW(hkey, wszTypeValue, 0, REG_DWORD, (const BYTE*)&credential->Type,
481                          sizeof(credential->Type));
482     if (ret != ERROR_SUCCESS) return ret;
483     ret = RegSetValueExW(hkey, NULL, 0, REG_SZ, (LPVOID)credential->TargetName,
484                          sizeof(WCHAR)*(strlenW(credential->TargetName)+1));
485     if (ret != ERROR_SUCCESS) return ret;
486     if (credential->Comment)
487     {
488         ret = RegSetValueExW(hkey, wszCommentValue, 0, REG_SZ, (LPVOID)credential->Comment,
489                              sizeof(WCHAR)*(strlenW(credential->Comment)+1));
490         if (ret != ERROR_SUCCESS) return ret;
491     }
492     ret = RegSetValueExW(hkey, wszLastWrittenValue, 0, REG_BINARY, (LPVOID)&LastWritten,
493                          sizeof(LastWritten));
494     if (ret != ERROR_SUCCESS) return ret;
495     ret = RegSetValueExW(hkey, wszPersistValue, 0, REG_DWORD, (const BYTE*)&credential->Persist,
496                          sizeof(credential->Persist));
497     if (ret != ERROR_SUCCESS) return ret;
498     /* FIXME: Attributes */
499     if (credential->TargetAlias)
500     {
501         ret = RegSetValueExW(hkey, wszTargetAliasValue, 0, REG_SZ, (LPVOID)credential->TargetAlias,
502                              sizeof(WCHAR)*(strlenW(credential->TargetAlias)+1));
503         if (ret != ERROR_SUCCESS) return ret;
504     }
505     if (credential->UserName)
506     {
507         ret = RegSetValueExW(hkey, wszUserNameValue, 0, REG_SZ, (LPVOID)credential->UserName,
508                              sizeof(WCHAR)*(strlenW(credential->UserName)+1));
509         if (ret != ERROR_SUCCESS) return ret;
510     }
511     if (!preserve_blob)
512     {
513         ret = write_credential_blob(hkey, credential->TargetName, credential->Type,
514                                     key_data, credential->CredentialBlob,
515                                     credential->CredentialBlobSize);
516     }
517     return ret;
518 }
519
520 #ifdef __APPLE__
521 static DWORD mac_write_credential(const CREDENTIALW *credential, BOOL preserve_blob)
522 {
523     OSStatus status;
524     SecKeychainItemRef keychain_item;
525     char *username;
526     char *domain = NULL;
527     char *password;
528     char *servername;
529     UInt32 userlen;
530     UInt32 domainlen = 0;
531     UInt32 pwlen;
532     UInt32 serverlen;
533     LPCWSTR p;
534     SecKeychainAttribute attrs[1];
535     SecKeychainAttributeList attr_list;
536
537     if (credential->Flags)
538         FIXME("Flags 0x%x not written\n", credential->Flags);
539     if (credential->Type != CRED_TYPE_DOMAIN_PASSWORD)
540         FIXME("credential type of %d not supported\n", credential->Type);
541     if (credential->Persist != CRED_PERSIST_LOCAL_MACHINE)
542         FIXME("persist value of %d not supported\n", credential->Persist);
543     if (credential->AttributeCount)
544         FIXME("custom attributes not supported\n");
545
546     p = strchrW(credential->UserName, '\\');
547     if (p)
548     {
549         domainlen = WideCharToMultiByte(CP_UTF8, 0, credential->UserName,
550                                         p - credential->UserName, NULL, 0, NULL, NULL);
551         domain = HeapAlloc(GetProcessHeap(), 0, (domainlen + 1) * sizeof(*domain));
552         WideCharToMultiByte(CP_UTF8, 0, credential->UserName, p - credential->UserName,
553                             domain, domainlen, NULL, NULL);
554         domain[domainlen] = '\0';
555         p++;
556     }
557     else
558         p = credential->UserName;
559     userlen = WideCharToMultiByte(CP_UTF8, 0, p, -1, NULL, 0, NULL, NULL);
560     username = HeapAlloc(GetProcessHeap(), 0, userlen * sizeof(*username));
561     WideCharToMultiByte(CP_UTF8, 0, p, -1, username, userlen, NULL, NULL);
562
563     serverlen = WideCharToMultiByte(CP_UTF8, 0, credential->TargetName, -1, NULL, 0, NULL, NULL);
564     servername = HeapAlloc(GetProcessHeap(), 0, serverlen * sizeof(*servername));
565     WideCharToMultiByte(CP_UTF8, 0, credential->TargetName, -1, servername, serverlen, NULL, NULL);
566     pwlen = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)credential->CredentialBlob,
567                                 credential->CredentialBlobSize / sizeof(WCHAR), NULL, 0, NULL, NULL);
568     password = HeapAlloc(GetProcessHeap(), 0, pwlen * sizeof(*domain));
569     WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)credential->CredentialBlob,
570                         credential->CredentialBlobSize / sizeof(WCHAR), password, pwlen, NULL, NULL);
571
572     TRACE("adding server %s, domain %s, username %s using Keychain\n", servername, domain, username);
573     status = SecKeychainAddInternetPassword(NULL, strlen(servername), servername,
574                                             strlen(domain), domain, strlen(username),
575                                             username, 0, NULL, 0,
576                                             0 /* no protocol */,
577                                             kSecAuthenticationTypeDefault,
578                                             strlen(password), password, &keychain_item);
579     if (status != noErr)
580         ERR("SecKeychainAddInternetPassword returned %ld\n", status);
581     if (status == errSecDuplicateItem)
582     {
583         SecKeychainItemRef keychain_item;
584
585         status = SecKeychainFindInternetPassword(NULL, strlen(servername), servername,
586                                                  strlen(domain), domain,
587                                                  strlen(username), username,
588                                                  0, NULL /* any path */, 0,
589                                                  0 /* any protocol */,
590                                                  0 /* any authentication type */,
591                                                  0, NULL, &keychain_item);
592         if (status != noErr)
593             ERR("SecKeychainFindInternetPassword returned %ld\n", status);
594     }
595     HeapFree(GetProcessHeap(), 0, domain);
596     HeapFree(GetProcessHeap(), 0, username);
597     HeapFree(GetProcessHeap(), 0, servername);
598     if (status != noErr)
599     {
600         HeapFree(GetProcessHeap(), 0, password);
601         return ERROR_GEN_FAILURE;
602     }
603     if (credential->Comment)
604     {
605         attr_list.count = 1;
606         attr_list.attr = attrs;
607         attrs[0].tag = kSecCommentItemAttr;
608         attrs[0].length = WideCharToMultiByte(CP_UTF8, 0, credential->Comment, -1, NULL, 0, NULL, NULL);
609         if (attrs[0].length) attrs[0].length--;
610         attrs[0].data = HeapAlloc(GetProcessHeap(), 0, attrs[0].length);
611         WideCharToMultiByte(CP_UTF8, 0, credential->Comment, -1, attrs[0].data, attrs[0].length, NULL, NULL);
612     }
613     else
614     {
615         attr_list.count = 0;
616         attr_list.attr = NULL;
617     }
618     status = SecKeychainItemModifyAttributesAndData(keychain_item, &attr_list,
619                                                     preserve_blob ? 0 : strlen(password),
620                                                     preserve_blob ? NULL : password);
621     if (credential->Comment)
622         HeapFree(GetProcessHeap(), 0, attrs[0].data);
623     HeapFree(GetProcessHeap(), 0, password);
624     /* FIXME: set TargetAlias attribute */
625     CFRelease(keychain_item);
626     if (status != noErr)
627         return ERROR_GEN_FAILURE;
628     return ERROR_SUCCESS;
629 }
630 #endif
631
632 static DWORD open_cred_mgr_key(HKEY *hkey, BOOL open_for_write)
633 {
634     return RegCreateKeyExW(HKEY_CURRENT_USER, wszCredentialManagerKey, 0,
635                            NULL, REG_OPTION_NON_VOLATILE,
636                            KEY_READ | (open_for_write ? KEY_WRITE : 0), NULL, hkey, NULL);
637 }
638
639 static DWORD get_cred_mgr_encryption_key(HKEY hkeyMgr, BYTE key_data[KEY_SIZE])
640 {
641     static const BYTE my_key_data[KEY_SIZE] = { 0 };
642     DWORD type;
643     DWORD count;
644     FILETIME ft;
645     ULONG seed;
646     ULONG value;
647     DWORD ret;
648
649     memcpy(key_data, my_key_data, KEY_SIZE);
650
651     count = KEY_SIZE;
652     ret = RegQueryValueExW(hkeyMgr, wszEncryptionKeyValue, NULL, &type, key_data,
653                            &count);
654     if (ret == ERROR_SUCCESS)
655     {
656         if (type != REG_BINARY)
657             return ERROR_REGISTRY_CORRUPT;
658         else
659             return ERROR_SUCCESS;
660     }
661     if (ret != ERROR_FILE_NOT_FOUND)
662         return ret;
663
664     GetSystemTimeAsFileTime(&ft);
665     seed = ft.dwLowDateTime;
666     value = RtlUniform(&seed);
667     *(DWORD *)key_data = value;
668     seed = ft.dwHighDateTime;
669     value = RtlUniform(&seed);
670     *(DWORD *)(key_data + 4) = value;
671
672     ret = RegSetValueExW(hkeyMgr, wszEncryptionKeyValue, 0, REG_BINARY,
673                          key_data, KEY_SIZE);
674     if (ret == ERROR_ACCESS_DENIED)
675     {
676         ret = open_cred_mgr_key(&hkeyMgr, TRUE);
677         if (ret == ERROR_SUCCESS)
678         {
679             ret = RegSetValueExW(hkeyMgr, wszEncryptionKeyValue, 0, REG_BINARY,
680                                  key_data, KEY_SIZE);
681             RegCloseKey(hkeyMgr);
682         }
683     }
684     return ret;
685 }
686
687 static LPWSTR get_key_name_for_target(LPCWSTR target_name, DWORD type)
688 {
689     static const WCHAR wszGenericPrefix[] = {'G','e','n','e','r','i','c',':',' ',0};
690     static const WCHAR wszDomPasswdPrefix[] = {'D','o','m','P','a','s','s','w','d',':',' ',0};
691     INT len;
692     LPCWSTR prefix = NULL;
693     LPWSTR key_name, p;
694
695     len = strlenW(target_name);
696     if (type == CRED_TYPE_GENERIC)
697     {
698         prefix = wszGenericPrefix;
699         len += sizeof(wszGenericPrefix)/sizeof(wszGenericPrefix[0]);
700     }
701     else
702     {
703         prefix = wszDomPasswdPrefix;
704         len += sizeof(wszDomPasswdPrefix)/sizeof(wszDomPasswdPrefix[0]);
705     }
706
707     key_name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
708     if (!key_name) return NULL;
709
710     strcpyW(key_name, prefix);
711     strcatW(key_name, target_name);
712
713     for (p = key_name; *p; p++)
714         if (*p == '\\') *p = '_';
715
716     return key_name;
717 }
718
719 static BOOL credential_matches_filter(HKEY hkeyCred, LPCWSTR filter)
720 {
721     LPWSTR target_name;
722     DWORD ret;
723     DWORD type;
724     DWORD count;
725     LPCWSTR p;
726
727     if (!filter) return TRUE;
728
729     ret = RegQueryValueExW(hkeyCred, NULL, 0, &type, NULL, &count);
730     if (ret != ERROR_SUCCESS)
731         return FALSE;
732     else if (type != REG_SZ)
733         return FALSE;
734
735     target_name = HeapAlloc(GetProcessHeap(), 0, count);
736     if (!target_name)
737         return FALSE;
738     ret = RegQueryValueExW(hkeyCred, NULL, 0, &type, (LPVOID)target_name, &count);
739     if (ret != ERROR_SUCCESS || type != REG_SZ)
740     {
741         HeapFree(GetProcessHeap(), 0, target_name);
742         return FALSE;
743     }
744
745     TRACE("comparing filter %s to target name %s\n", debugstr_w(filter),
746           debugstr_w(target_name));
747
748     p = strchrW(filter, '*');
749     ret = CompareStringW(GetThreadLocale(), 0, filter,
750                          (p && !p[1] ? p - filter : -1), target_name,
751                          (p && !p[1] ? p - filter : -1)) == CSTR_EQUAL;
752
753     HeapFree(GetProcessHeap(), 0, target_name);
754     return ret;
755 }
756
757 static DWORD registry_enumerate_credentials(HKEY hkeyMgr, LPCWSTR filter,
758                                             LPWSTR target_name,
759                                             DWORD target_name_len, const BYTE key_data[KEY_SIZE],
760                                             PCREDENTIALW *credentials, char **buffer,
761                                             DWORD *len, DWORD *count)
762 {
763     DWORD i;
764     DWORD ret;
765     for (i = 0;; i++)
766     {
767         HKEY hkeyCred;
768         ret = RegEnumKeyW(hkeyMgr, i, target_name, target_name_len+1);
769         if (ret == ERROR_NO_MORE_ITEMS)
770         {
771             ret = ERROR_SUCCESS;
772             break;
773         }
774         else if (ret != ERROR_SUCCESS)
775             continue;
776         TRACE("target_name = %s\n", debugstr_w(target_name));
777         ret = RegOpenKeyExW(hkeyMgr, target_name, 0, KEY_QUERY_VALUE, &hkeyCred);
778         if (ret != ERROR_SUCCESS)
779             continue;
780         if (!credential_matches_filter(hkeyCred, filter))
781         {
782             RegCloseKey(hkeyCred);
783             continue;
784         }
785         if (buffer)
786         {
787             *len = sizeof(CREDENTIALW);
788             credentials[*count] = (PCREDENTIALW)*buffer;
789         }
790         else
791             *len += sizeof(CREDENTIALW);
792         ret = registry_read_credential(hkeyCred, buffer ? credentials[*count] : NULL,
793                                        key_data, buffer ? *buffer + sizeof(CREDENTIALW) : NULL,
794                                        len);
795         RegCloseKey(hkeyCred);
796         if (ret != ERROR_SUCCESS) break;
797         if (buffer) *buffer += *len;
798         (*count)++;
799     }
800     return ret;
801 }
802
803 #ifdef __APPLE__
804 static DWORD mac_enumerate_credentials(LPCWSTR filter, PCREDENTIALW *credentials,
805                                        char *buffer, DWORD *len, DWORD *count)
806 {
807     SecKeychainSearchRef search;
808     SecKeychainItemRef item;
809     OSStatus status;
810     Boolean saved_user_interaction_allowed;
811     DWORD ret;
812
813     SecKeychainGetUserInteractionAllowed(&saved_user_interaction_allowed);
814     SecKeychainSetUserInteractionAllowed(false);
815
816     status = SecKeychainSearchCreateFromAttributes(NULL, kSecInternetPasswordItemClass, NULL, &search);
817     if (status == noErr)
818     {
819         while (SecKeychainSearchCopyNext(search, &item) == noErr)
820         {
821             SecKeychainAttributeInfo info;
822             SecKeychainAttributeList *attr_list;
823             UInt32 info_tags[] = { kSecServerItemAttr };
824             info.count = sizeof(info_tags)/sizeof(info_tags[0]);
825             info.tag = info_tags;
826             info.format = NULL;
827             status = SecKeychainItemCopyAttributesAndData(item, &info, NULL, &attr_list, NULL, NULL);
828             if (status != noErr)
829             {
830                 WARN("SecKeychainItemCopyAttributesAndData returned status %ld\n", status);
831                 continue;
832             }
833             if (buffer)
834             {
835                 *len = sizeof(CREDENTIALW);
836                 credentials[*count] = (PCREDENTIALW)buffer;
837             }
838             else
839                 *len += sizeof(CREDENTIALW);
840             if (attr_list->count != 1 || attr_list->attr[0].tag != kSecServerItemAttr) continue;
841             TRACE("server item: %.*s\n", (int)attr_list->attr[0].length, (char *)attr_list->attr[0].data);
842             /* FIXME: filter based on attr_list->attr[0].data */
843             SecKeychainItemFreeAttributesAndData(attr_list, NULL);
844             ret = mac_read_credential_from_item(item, FALSE,
845                                                 buffer ? credentials[*count] : NULL,
846                                                 buffer ? buffer + sizeof(CREDENTIALW) : NULL,
847                                                 len);
848             CFRelease(item);
849             if (ret == ERROR_SUCCESS)
850             {
851                 (*count)++;
852                 if (buffer) buffer += *len;
853             }
854         }
855         CFRelease(search);
856     }
857     else
858         ERR("SecKeychainSearchCreateFromAttributes returned status %ld\n", status);
859     SecKeychainSetUserInteractionAllowed(saved_user_interaction_allowed);
860     return ERROR_SUCCESS;
861 }
862
863 static DWORD mac_delete_credential(LPCWSTR TargetName)
864 {
865     OSStatus status;
866     SecKeychainSearchRef search;
867     status = SecKeychainSearchCreateFromAttributes(NULL, kSecInternetPasswordItemClass, NULL, &search);
868     if (status == noErr)
869     {
870         SecKeychainItemRef item;
871         while (SecKeychainSearchCopyNext(search, &item) == noErr)
872         {
873             SecKeychainAttributeInfo info;
874             SecKeychainAttributeList *attr_list;
875             UInt32 info_tags[] = { kSecServerItemAttr };
876             LPWSTR target_name;
877             INT str_len;
878             info.count = sizeof(info_tags)/sizeof(info_tags[0]);
879             info.tag = info_tags;
880             info.format = NULL;
881             status = SecKeychainItemCopyAttributesAndData(item, &info, NULL, &attr_list, NULL, NULL);
882             if (status != noErr)
883             {
884                 WARN("SecKeychainItemCopyAttributesAndData returned status %ld\n", status);
885                 continue;
886             }
887             if (attr_list->count != 1 || attr_list->attr[0].tag != kSecServerItemAttr)
888             {
889                 CFRelease(item);
890                 continue;
891             }
892             str_len = MultiByteToWideChar(CP_UTF8, 0, attr_list->attr[0].data, attr_list->attr[0].length, NULL, 0);
893             target_name = HeapAlloc(GetProcessHeap(), 0, (str_len + 1) * sizeof(WCHAR));
894             MultiByteToWideChar(CP_UTF8, 0, attr_list->attr[0].data, attr_list->attr[0].length, target_name, str_len);
895             /* nul terminate */
896             target_name[str_len] = '\0';
897             if (strcmpiW(TargetName, target_name))
898             {
899                 CFRelease(item);
900                 HeapFree(GetProcessHeap(), 0, target_name);
901                 continue;
902             }
903             HeapFree(GetProcessHeap(), 0, target_name);
904             SecKeychainItemFreeAttributesAndData(attr_list, NULL);
905             SecKeychainItemDelete(item);
906             CFRelease(item);
907             CFRelease(search);
908
909             return ERROR_SUCCESS;
910         }
911         CFRelease(search);
912     }
913     return ERROR_NOT_FOUND;
914 }
915 #endif
916
917 /******************************************************************************
918  * convert_PCREDENTIALW_to_PCREDENTIALA [internal]
919  *
920  * convert a Credential struct from UNICODE to ANSI and return the needed size in Bytes
921  *
922  */
923
924 static INT convert_PCREDENTIALW_to_PCREDENTIALA(const CREDENTIALW *CredentialW, PCREDENTIALA CredentialA, DWORD len)
925 {
926     char *buffer;
927     INT string_len;
928     INT needed = sizeof(CREDENTIALA);
929
930     if (!CredentialA)
931     {
932         if (CredentialW->TargetName)
933             needed += WideCharToMultiByte(CP_ACP, 0, CredentialW->TargetName, -1, NULL, 0, NULL, NULL);
934         if (CredentialW->Comment)
935             needed += WideCharToMultiByte(CP_ACP, 0, CredentialW->Comment, -1, NULL, 0, NULL, NULL);
936         needed += CredentialW->CredentialBlobSize;
937         if (CredentialW->TargetAlias)
938             needed += WideCharToMultiByte(CP_ACP, 0, CredentialW->TargetAlias, -1, NULL, 0, NULL, NULL);
939         if (CredentialW->UserName)
940             needed += WideCharToMultiByte(CP_ACP, 0, CredentialW->UserName, -1, NULL, 0, NULL, NULL);
941
942         return needed;
943     }
944
945
946     buffer = (char *)CredentialA + sizeof(CREDENTIALA);
947     len -= sizeof(CREDENTIALA);
948     CredentialA->Flags = CredentialW->Flags;
949     CredentialA->Type = CredentialW->Type;
950
951     if (CredentialW->TargetName)
952     {
953         CredentialA->TargetName = buffer;
954         string_len = WideCharToMultiByte(CP_ACP, 0, CredentialW->TargetName, -1, buffer, len, NULL, NULL);
955         buffer += string_len;
956         needed += string_len;
957         len -= string_len;
958     }
959     else
960         CredentialA->TargetName = NULL;
961     if (CredentialW->Comment)
962     {
963         CredentialA->Comment = buffer;
964         string_len = WideCharToMultiByte(CP_ACP, 0, CredentialW->Comment, -1, buffer, len, NULL, NULL);
965         buffer += string_len;
966         needed += string_len;
967         len -= string_len;
968     }
969     else
970         CredentialA->Comment = NULL;
971     CredentialA->LastWritten = CredentialW->LastWritten;
972     CredentialA->CredentialBlobSize = CredentialW->CredentialBlobSize;
973     if (CredentialW->CredentialBlobSize && (CredentialW->CredentialBlobSize <= len))
974     {
975         CredentialA->CredentialBlob =(LPBYTE)buffer;
976         memcpy(CredentialA->CredentialBlob, CredentialW->CredentialBlob,
977                CredentialW->CredentialBlobSize);
978         buffer += CredentialW->CredentialBlobSize;
979         needed += CredentialW->CredentialBlobSize;
980         len -= CredentialW->CredentialBlobSize;
981     }
982     else
983         CredentialA->CredentialBlob = NULL;
984     CredentialA->Persist = CredentialW->Persist;
985     CredentialA->AttributeCount = 0;
986     CredentialA->Attributes = NULL; /* FIXME */
987     if (CredentialW->TargetAlias)
988     {
989         CredentialA->TargetAlias = buffer;
990         string_len = WideCharToMultiByte(CP_ACP, 0, CredentialW->TargetAlias, -1, buffer, len, NULL, NULL);
991         buffer += string_len;
992         needed += string_len;
993         len -= string_len;
994     }
995     else
996         CredentialA->TargetAlias = NULL;
997     if (CredentialW->UserName)
998     {
999         CredentialA->UserName = buffer;
1000         string_len = WideCharToMultiByte(CP_ACP, 0, CredentialW->UserName, -1, buffer, len, NULL, NULL);
1001         needed += string_len;
1002     }
1003     else
1004         CredentialA->UserName = NULL;
1005
1006     return needed;
1007 }
1008
1009 /******************************************************************************
1010  * convert_PCREDENTIALA_to_PCREDENTIALW [internal]
1011  *
1012  * convert a Credential struct from ANSI to UNICODE and return the needed size in Bytes
1013  *
1014  */
1015 static INT convert_PCREDENTIALA_to_PCREDENTIALW(const CREDENTIALA *CredentialA, PCREDENTIALW CredentialW, INT len)
1016 {
1017     char *buffer;
1018     INT string_len;
1019     INT needed = sizeof(CREDENTIALW);
1020
1021     if (!CredentialW)
1022     {
1023         if (CredentialA->TargetName)
1024             needed += sizeof(WCHAR) * MultiByteToWideChar(CP_ACP, 0, CredentialA->TargetName, -1, NULL, 0);
1025         if (CredentialA->Comment)
1026             needed += sizeof(WCHAR) * MultiByteToWideChar(CP_ACP, 0, CredentialA->Comment, -1, NULL, 0);
1027         needed += CredentialA->CredentialBlobSize;
1028         if (CredentialA->TargetAlias)
1029             needed += sizeof(WCHAR) * MultiByteToWideChar(CP_ACP, 0, CredentialA->TargetAlias, -1, NULL, 0);
1030         if (CredentialA->UserName)
1031             needed += sizeof(WCHAR) * MultiByteToWideChar(CP_ACP, 0, CredentialA->UserName, -1, NULL, 0);
1032
1033         return needed;
1034     }
1035
1036     buffer = (char *)CredentialW + sizeof(CREDENTIALW);
1037     len -= sizeof(CREDENTIALW);
1038     CredentialW->Flags = CredentialA->Flags;
1039     CredentialW->Type = CredentialA->Type;
1040     if (CredentialA->TargetName)
1041     {
1042         CredentialW->TargetName = (LPWSTR)buffer;
1043         string_len = MultiByteToWideChar(CP_ACP, 0, CredentialA->TargetName, -1, CredentialW->TargetName, len / sizeof(WCHAR));
1044         buffer += sizeof(WCHAR) * string_len;
1045         needed += sizeof(WCHAR) * string_len;
1046         len -= sizeof(WCHAR) * string_len;
1047     }
1048     else
1049         CredentialW->TargetName = NULL;
1050     if (CredentialA->Comment)
1051     {
1052         CredentialW->Comment = (LPWSTR)buffer;
1053         string_len = MultiByteToWideChar(CP_ACP, 0, CredentialA->Comment, -1, CredentialW->Comment, len / sizeof(WCHAR));
1054         buffer += sizeof(WCHAR) * string_len;
1055         needed += sizeof(WCHAR) * string_len;
1056         len -= sizeof(WCHAR) * string_len;
1057     }
1058     else
1059         CredentialW->Comment = NULL;
1060     CredentialW->LastWritten = CredentialA->LastWritten;
1061     CredentialW->CredentialBlobSize = CredentialA->CredentialBlobSize;
1062     if (CredentialA->CredentialBlobSize)
1063     {
1064         CredentialW->CredentialBlob =(LPBYTE)buffer;
1065         memcpy(CredentialW->CredentialBlob, CredentialA->CredentialBlob,
1066                CredentialA->CredentialBlobSize);
1067         buffer += CredentialA->CredentialBlobSize;
1068         needed += CredentialA->CredentialBlobSize;
1069         len -= CredentialA->CredentialBlobSize;
1070     }
1071     else
1072         CredentialW->CredentialBlob = NULL;
1073     CredentialW->Persist = CredentialA->Persist;
1074     CredentialW->AttributeCount = 0;
1075     CredentialW->Attributes = NULL; /* FIXME */
1076     if (CredentialA->TargetAlias)
1077     {
1078         CredentialW->TargetAlias = (LPWSTR)buffer;
1079         string_len = MultiByteToWideChar(CP_ACP, 0, CredentialA->TargetAlias, -1, CredentialW->TargetAlias, len / sizeof(WCHAR));
1080         buffer += sizeof(WCHAR) * string_len;
1081         needed += sizeof(WCHAR) * string_len;
1082         len -= sizeof(WCHAR) * string_len;
1083     }
1084     else
1085         CredentialW->TargetAlias = NULL;
1086     if (CredentialA->UserName)
1087     {
1088         CredentialW->UserName = (LPWSTR)buffer;
1089         string_len = MultiByteToWideChar(CP_ACP, 0, CredentialA->UserName, -1, CredentialW->UserName, len / sizeof(WCHAR));
1090         needed += sizeof(WCHAR) * string_len;
1091     }
1092     else
1093         CredentialW->UserName = NULL;
1094
1095     return needed;
1096 }
1097
1098 /******************************************************************************
1099  * CredDeleteA [ADVAPI32.@]
1100  */
1101 BOOL WINAPI CredDeleteA(LPCSTR TargetName, DWORD Type, DWORD Flags)
1102 {
1103     LPWSTR TargetNameW;
1104     DWORD len;
1105     BOOL ret;
1106
1107     TRACE("(%s, %d, 0x%x)\n", debugstr_a(TargetName), Type, Flags);
1108
1109     if (!TargetName)
1110     {
1111         SetLastError(ERROR_INVALID_PARAMETER);
1112         return FALSE;
1113     }
1114
1115     len = MultiByteToWideChar(CP_ACP, 0, TargetName, -1, NULL, 0);
1116     TargetNameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1117     if (!TargetNameW)
1118     {
1119         SetLastError(ERROR_OUTOFMEMORY);
1120         return FALSE;
1121     }
1122     MultiByteToWideChar(CP_ACP, 0, TargetName, -1, TargetNameW, len);
1123
1124     ret = CredDeleteW(TargetNameW, Type, Flags);
1125
1126     HeapFree(GetProcessHeap(), 0, TargetNameW);
1127
1128     return ret;
1129 }
1130
1131 /******************************************************************************
1132  * CredDeleteW [ADVAPI32.@]
1133  */
1134 BOOL WINAPI CredDeleteW(LPCWSTR TargetName, DWORD Type, DWORD Flags)
1135 {
1136     HKEY hkeyMgr;
1137     DWORD ret;
1138     LPWSTR key_name;
1139
1140     TRACE("(%s, %d, 0x%x)\n", debugstr_w(TargetName), Type, Flags);
1141
1142     if (!TargetName)
1143     {
1144         SetLastError(ERROR_INVALID_PARAMETER);
1145         return FALSE;
1146     }
1147
1148     if (Type != CRED_TYPE_GENERIC && Type != CRED_TYPE_DOMAIN_PASSWORD)
1149     {
1150         FIXME("unhandled type %d\n", Type);
1151         SetLastError(ERROR_INVALID_PARAMETER);
1152         return FALSE;
1153     }
1154
1155     if (Flags)
1156     {
1157         FIXME("unhandled flags 0x%x\n", Flags);
1158         SetLastError(ERROR_INVALID_FLAGS);
1159         return FALSE;
1160     }
1161
1162 #ifdef __APPLE__
1163     if (Type == CRED_TYPE_DOMAIN_PASSWORD)
1164     {
1165         ret = mac_delete_credential(TargetName);
1166         if (ret == ERROR_SUCCESS)
1167             return TRUE;
1168     }
1169 #endif
1170
1171     ret = open_cred_mgr_key(&hkeyMgr, TRUE);
1172     if (ret != ERROR_SUCCESS)
1173     {
1174         WARN("couldn't open/create manager key, error %d\n", ret);
1175         SetLastError(ERROR_NO_SUCH_LOGON_SESSION);
1176         return FALSE;
1177     }
1178
1179     key_name = get_key_name_for_target(TargetName, Type);
1180     ret = RegDeleteKeyW(hkeyMgr, key_name);
1181     HeapFree(GetProcessHeap(), 0, key_name);
1182     RegCloseKey(hkeyMgr);
1183     if (ret != ERROR_SUCCESS)
1184     {
1185         SetLastError(ERROR_NOT_FOUND);
1186         return FALSE;
1187     }
1188
1189     return TRUE;
1190 }
1191
1192 /******************************************************************************
1193  * CredEnumerateA [ADVAPI32.@]
1194  */
1195 BOOL WINAPI CredEnumerateA(LPCSTR Filter, DWORD Flags, DWORD *Count,
1196                            PCREDENTIALA **Credentials)
1197 {
1198     LPWSTR FilterW;
1199     PCREDENTIALW *CredentialsW;
1200     DWORD i;
1201     INT len;
1202     INT needed;
1203     char *buffer;
1204
1205     TRACE("(%s, 0x%x, %p, %p)\n", debugstr_a(Filter), Flags, Count, Credentials);
1206
1207     if (Filter)
1208     {
1209         len = MultiByteToWideChar(CP_ACP, 0, Filter, -1, NULL, 0);
1210         FilterW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1211         if (!FilterW)
1212         {
1213             SetLastError(ERROR_OUTOFMEMORY);
1214             return FALSE;
1215         }
1216         MultiByteToWideChar(CP_ACP, 0, Filter, -1, FilterW, len);
1217     }
1218     else
1219         FilterW = NULL;
1220
1221     if (!CredEnumerateW(FilterW, Flags, Count, &CredentialsW))
1222     {
1223         HeapFree(GetProcessHeap(), 0, FilterW);
1224         return FALSE;
1225     }
1226     HeapFree(GetProcessHeap(), 0, FilterW);
1227
1228     len = *Count * sizeof(PCREDENTIALA);
1229     for (i = 0; i < *Count; i++)
1230         len += convert_PCREDENTIALW_to_PCREDENTIALA(CredentialsW[i], NULL, 0);
1231
1232     *Credentials = HeapAlloc(GetProcessHeap(), 0, len);
1233     if (!*Credentials)
1234     {
1235         CredFree(CredentialsW);
1236         SetLastError(ERROR_OUTOFMEMORY);
1237         return FALSE;
1238     }
1239
1240     buffer = (char *)&(*Credentials)[*Count];
1241     len -= *Count * sizeof(PCREDENTIALA);
1242     for (i = 0; i < *Count; i++)
1243     {
1244         (*Credentials)[i] = (PCREDENTIALA)buffer;
1245         needed = convert_PCREDENTIALW_to_PCREDENTIALA(CredentialsW[i], (*Credentials)[i], len);
1246         buffer += needed;
1247         len -= needed;
1248     }
1249
1250     CredFree(CredentialsW);
1251
1252     return TRUE;
1253 }
1254
1255 /******************************************************************************
1256  * CredEnumerateW [ADVAPI32.@]
1257  */
1258 BOOL WINAPI CredEnumerateW(LPCWSTR Filter, DWORD Flags, DWORD *Count,
1259                            PCREDENTIALW **Credentials)
1260 {
1261     HKEY hkeyMgr;
1262     DWORD ret;
1263     LPWSTR target_name;
1264     DWORD target_name_len;
1265     DWORD len;
1266     char *buffer;
1267     BYTE key_data[KEY_SIZE];
1268
1269     TRACE("(%s, 0x%x, %p, %p)\n", debugstr_w(Filter), Flags, Count, Credentials);
1270
1271     if (Flags)
1272     {
1273         SetLastError(ERROR_INVALID_FLAGS);
1274         return FALSE;
1275     }
1276
1277     ret = open_cred_mgr_key(&hkeyMgr, FALSE);
1278     if (ret != ERROR_SUCCESS)
1279     {
1280         WARN("couldn't open/create manager key, error %d\n", ret);
1281         SetLastError(ERROR_NO_SUCH_LOGON_SESSION);
1282         return FALSE;
1283     }
1284
1285     ret = get_cred_mgr_encryption_key(hkeyMgr, key_data);
1286     if (ret != ERROR_SUCCESS)
1287     {
1288         RegCloseKey(hkeyMgr);
1289         SetLastError(ret);
1290         return FALSE;
1291     }
1292
1293     ret = RegQueryInfoKeyW(hkeyMgr, NULL, NULL, NULL, NULL, &target_name_len, NULL, NULL, NULL, NULL, NULL, NULL);
1294     if (ret != ERROR_SUCCESS)
1295     {
1296         RegCloseKey(hkeyMgr);
1297         SetLastError(ret);
1298         return FALSE;
1299     }
1300
1301     target_name = HeapAlloc(GetProcessHeap(), 0, (target_name_len+1)*sizeof(WCHAR));
1302     if (!target_name)
1303     {
1304         RegCloseKey(hkeyMgr);
1305         SetLastError(ERROR_OUTOFMEMORY);
1306         return FALSE;
1307     }
1308
1309     *Count = 0;
1310     len = 0;
1311     ret = registry_enumerate_credentials(hkeyMgr, Filter, target_name, target_name_len,
1312                                          key_data, NULL, NULL, &len, Count);
1313 #ifdef __APPLE__
1314     if (ret == ERROR_SUCCESS)
1315         ret = mac_enumerate_credentials(Filter, NULL, NULL, &len, Count);
1316 #endif
1317     if (ret == ERROR_SUCCESS && *Count == 0)
1318         ret = ERROR_NOT_FOUND;
1319     if (ret != ERROR_SUCCESS)
1320     {
1321         HeapFree(GetProcessHeap(), 0, target_name);
1322         RegCloseKey(hkeyMgr);
1323         SetLastError(ret);
1324         return FALSE;
1325     }
1326     len += *Count * sizeof(PCREDENTIALW);
1327
1328     if (ret == ERROR_SUCCESS)
1329     {
1330         buffer = HeapAlloc(GetProcessHeap(), 0, len);
1331         *Credentials = (PCREDENTIALW *)buffer;
1332         if (buffer)
1333         {
1334             buffer += *Count * sizeof(PCREDENTIALW);
1335             *Count = 0;
1336             ret = registry_enumerate_credentials(hkeyMgr, Filter, target_name,
1337                                                  target_name_len, key_data,
1338                                                  *Credentials, &buffer, &len,
1339                                                  Count);
1340 #ifdef __APPLE__
1341             if (ret == ERROR_SUCCESS)
1342                 ret = mac_enumerate_credentials(Filter, *Credentials,
1343                                                 buffer, &len, Count);
1344 #endif
1345         }
1346         else
1347             ret = ERROR_OUTOFMEMORY;
1348     }
1349
1350     HeapFree(GetProcessHeap(), 0, target_name);
1351     RegCloseKey(hkeyMgr);
1352
1353     if (ret != ERROR_SUCCESS)
1354     {
1355         SetLastError(ret);
1356         return FALSE;
1357     }
1358     return TRUE;
1359 }
1360
1361 /******************************************************************************
1362  * CredFree [ADVAPI32.@]
1363  */
1364 VOID WINAPI CredFree(PVOID Buffer)
1365 {
1366     HeapFree(GetProcessHeap(), 0, Buffer);
1367 }
1368
1369 /******************************************************************************
1370  * CredReadA [ADVAPI32.@]
1371  */
1372 BOOL WINAPI CredReadA(LPCSTR TargetName, DWORD Type, DWORD Flags, PCREDENTIALA *Credential)
1373 {
1374     LPWSTR TargetNameW;
1375     PCREDENTIALW CredentialW;
1376     INT len;
1377
1378     TRACE("(%s, %d, 0x%x, %p)\n", debugstr_a(TargetName), Type, Flags, Credential);
1379
1380     if (!TargetName)
1381     {
1382         SetLastError(ERROR_INVALID_PARAMETER);
1383         return FALSE;
1384     }
1385
1386     len = MultiByteToWideChar(CP_ACP, 0, TargetName, -1, NULL, 0);
1387     TargetNameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1388     if (!TargetNameW)
1389     {
1390         SetLastError(ERROR_OUTOFMEMORY);
1391         return FALSE;
1392     }
1393     MultiByteToWideChar(CP_ACP, 0, TargetName, -1, TargetNameW, len);
1394
1395     if (!CredReadW(TargetNameW, Type, Flags, &CredentialW))
1396     {
1397         HeapFree(GetProcessHeap(), 0, TargetNameW);
1398         return FALSE;
1399     }
1400     HeapFree(GetProcessHeap(), 0, TargetNameW);
1401
1402     len = convert_PCREDENTIALW_to_PCREDENTIALA(CredentialW, NULL, 0);
1403     *Credential = HeapAlloc(GetProcessHeap(), 0, len);
1404     if (!*Credential)
1405     {
1406         SetLastError(ERROR_OUTOFMEMORY);
1407         return FALSE;
1408     }
1409     convert_PCREDENTIALW_to_PCREDENTIALA(CredentialW, *Credential, len);
1410
1411     CredFree(CredentialW);
1412
1413     return TRUE;
1414 }
1415
1416 /******************************************************************************
1417  * CredReadW [ADVAPI32.@]
1418  */
1419 BOOL WINAPI CredReadW(LPCWSTR TargetName, DWORD Type, DWORD Flags, PCREDENTIALW *Credential)
1420 {
1421     HKEY hkeyMgr;
1422     HKEY hkeyCred;
1423     DWORD ret;
1424     LPWSTR key_name;
1425     DWORD len;
1426     BYTE key_data[KEY_SIZE];
1427
1428     TRACE("(%s, %d, 0x%x, %p)\n", debugstr_w(TargetName), Type, Flags, Credential);
1429
1430     if (!TargetName)
1431     {
1432         SetLastError(ERROR_INVALID_PARAMETER);
1433         return FALSE;
1434     }
1435
1436     if (Type != CRED_TYPE_GENERIC && Type != CRED_TYPE_DOMAIN_PASSWORD)
1437     {
1438         FIXME("unhandled type %d\n", Type);
1439         SetLastError(ERROR_INVALID_PARAMETER);
1440         return FALSE;
1441     }
1442
1443     if (Flags)
1444     {
1445         FIXME("unhandled flags 0x%x\n", Flags);
1446         SetLastError(ERROR_INVALID_FLAGS);
1447         return FALSE;
1448     }
1449
1450 #ifdef __APPLE__
1451     if (Type == CRED_TYPE_DOMAIN_PASSWORD)
1452     {
1453         OSStatus status;
1454         SecKeychainSearchRef search;
1455         status = SecKeychainSearchCreateFromAttributes(NULL, kSecInternetPasswordItemClass, NULL, &search);
1456         if (status == noErr)
1457         {
1458             SecKeychainItemRef item;
1459             while (SecKeychainSearchCopyNext(search, &item) == noErr)
1460             {
1461                 SecKeychainAttributeInfo info;
1462                 SecKeychainAttributeList *attr_list;
1463                 UInt32 info_tags[] = { kSecServerItemAttr };
1464                 LPWSTR target_name;
1465                 INT str_len;
1466                 info.count = sizeof(info_tags)/sizeof(info_tags[0]);
1467                 info.tag = info_tags;
1468                 info.format = NULL;
1469                 status = SecKeychainItemCopyAttributesAndData(item, &info, NULL, &attr_list, NULL, NULL);
1470                 len = sizeof(**Credential);
1471                 if (status != noErr)
1472                 {
1473                     WARN("SecKeychainItemCopyAttributesAndData returned status %ld\n", status);
1474                     continue;
1475                 }
1476                 if (attr_list->count != 1 || attr_list->attr[0].tag != kSecServerItemAttr)
1477                 {
1478                     CFRelease(item);
1479                     continue;
1480                 }
1481                 str_len = MultiByteToWideChar(CP_UTF8, 0, attr_list->attr[0].data, attr_list->attr[0].length, NULL, 0);
1482                 target_name = HeapAlloc(GetProcessHeap(), 0, (str_len + 1) * sizeof(WCHAR));
1483                 MultiByteToWideChar(CP_UTF8, 0, attr_list->attr[0].data, attr_list->attr[0].length, target_name, str_len);
1484                 /* nul terminate */
1485                 target_name[str_len] = '\0';
1486                 if (strcmpiW(TargetName, target_name))
1487                 {
1488                     CFRelease(item);
1489                     HeapFree(GetProcessHeap(), 0, target_name);
1490                     continue;
1491                 }
1492                 HeapFree(GetProcessHeap(), 0, target_name);
1493                 SecKeychainItemFreeAttributesAndData(attr_list, NULL);
1494                 ret = mac_read_credential_from_item(item, TRUE, NULL, NULL, &len);
1495                 if (ret == ERROR_SUCCESS)
1496                 {
1497                     *Credential = HeapAlloc(GetProcessHeap(), 0, len);
1498                     if (*Credential)
1499                     {
1500                         len = sizeof(**Credential);
1501                         ret = mac_read_credential_from_item(item, TRUE, *Credential,
1502                                                             (char *)(*Credential + 1), &len);
1503                     }
1504                     else
1505                         ret = ERROR_OUTOFMEMORY;
1506                     CFRelease(item);
1507                     CFRelease(search);
1508                     if (ret != ERROR_SUCCESS)
1509                     {
1510                         SetLastError(ret);
1511                         return FALSE;
1512                     }
1513                     return TRUE;
1514                 }
1515                 CFRelease(item);
1516             }
1517             CFRelease(search);
1518         }
1519     }
1520 #endif
1521
1522     ret = open_cred_mgr_key(&hkeyMgr, FALSE);
1523     if (ret != ERROR_SUCCESS)
1524     {
1525         WARN("couldn't open/create manager key, error %d\n", ret);
1526         SetLastError(ERROR_NO_SUCH_LOGON_SESSION);
1527         return FALSE;
1528     }
1529
1530     ret = get_cred_mgr_encryption_key(hkeyMgr, key_data);
1531     if (ret != ERROR_SUCCESS)
1532     {
1533         RegCloseKey(hkeyMgr);
1534         SetLastError(ret);
1535         return FALSE;
1536     }
1537
1538     key_name = get_key_name_for_target(TargetName, Type);
1539     ret = RegOpenKeyExW(hkeyMgr, key_name, 0, KEY_QUERY_VALUE, &hkeyCred);
1540     HeapFree(GetProcessHeap(), 0, key_name);
1541     if (ret != ERROR_SUCCESS)
1542     {
1543         TRACE("credentials for target name %s not found\n", debugstr_w(TargetName));
1544         SetLastError(ERROR_NOT_FOUND);
1545         return FALSE;
1546     }
1547
1548     len = sizeof(**Credential);
1549     ret = registry_read_credential(hkeyCred, NULL, key_data, NULL, &len);
1550     if (ret == ERROR_SUCCESS)
1551     {
1552         *Credential = HeapAlloc(GetProcessHeap(), 0, len);
1553         if (*Credential)
1554         {
1555             len = sizeof(**Credential);
1556             ret = registry_read_credential(hkeyCred, *Credential, key_data,
1557                                            (char *)(*Credential + 1), &len);
1558         }
1559         else
1560             ret = ERROR_OUTOFMEMORY;
1561     }
1562
1563     RegCloseKey(hkeyCred);
1564     RegCloseKey(hkeyMgr);
1565
1566     if (ret != ERROR_SUCCESS)
1567     {
1568         SetLastError(ret);
1569         return FALSE;
1570     }
1571     return TRUE;
1572 }
1573
1574 /******************************************************************************
1575  * CredReadDomainCredentialsA [ADVAPI32.@]
1576  */
1577 BOOL WINAPI CredReadDomainCredentialsA(PCREDENTIAL_TARGET_INFORMATIONA TargetInformation,
1578                                        DWORD Flags, DWORD *Size, PCREDENTIALA **Credentials)
1579 {
1580     PCREDENTIAL_TARGET_INFORMATIONW TargetInformationW;
1581     INT len;
1582     DWORD i;
1583     WCHAR *buffer, *end;
1584     BOOL ret;
1585     PCREDENTIALW* CredentialsW;
1586
1587     TRACE("(%p, 0x%x, %p, %p)\n", TargetInformation, Flags, Size, Credentials);
1588
1589     /* follow Windows behavior - do not test for NULL, initialize early */
1590     *Size = 0;
1591     *Credentials = NULL;
1592
1593     if (!TargetInformation)
1594     {
1595         SetLastError(ERROR_INVALID_PARAMETER);
1596         return FALSE;
1597     }
1598
1599     len = sizeof(*TargetInformationW);
1600     if (TargetInformation->TargetName)
1601         len += MultiByteToWideChar(CP_ACP, 0, TargetInformation->TargetName, -1, NULL, 0) * sizeof(WCHAR);
1602     if (TargetInformation->NetbiosServerName)
1603         len += MultiByteToWideChar(CP_ACP, 0, TargetInformation->NetbiosServerName, -1, NULL, 0) * sizeof(WCHAR);
1604     if (TargetInformation->DnsServerName)
1605         len += MultiByteToWideChar(CP_ACP, 0, TargetInformation->DnsServerName, -1, NULL, 0) * sizeof(WCHAR);
1606     if (TargetInformation->NetbiosDomainName)
1607         len += MultiByteToWideChar(CP_ACP, 0, TargetInformation->NetbiosDomainName, -1, NULL, 0) * sizeof(WCHAR);
1608     if (TargetInformation->DnsDomainName)
1609         len += MultiByteToWideChar(CP_ACP, 0, TargetInformation->DnsDomainName, -1, NULL, 0) * sizeof(WCHAR);
1610     if (TargetInformation->DnsTreeName)
1611         len += MultiByteToWideChar(CP_ACP, 0, TargetInformation->DnsTreeName, -1, NULL, 0) * sizeof(WCHAR);
1612     if (TargetInformation->PackageName)
1613         len += MultiByteToWideChar(CP_ACP, 0, TargetInformation->PackageName, -1, NULL, 0) * sizeof(WCHAR);
1614
1615     TargetInformationW = HeapAlloc(GetProcessHeap(), 0, len);
1616     if (!TargetInformationW)
1617     {
1618         SetLastError(ERROR_OUTOFMEMORY);
1619         return FALSE;
1620     }
1621     buffer = (WCHAR*)(TargetInformationW + 1);
1622     end = (WCHAR *)((char *)TargetInformationW + len);
1623
1624     if (TargetInformation->TargetName)
1625     {
1626         TargetInformationW->TargetName = buffer;
1627         buffer += MultiByteToWideChar(CP_ACP, 0, TargetInformation->TargetName, -1,
1628                                       TargetInformationW->TargetName, end - buffer);
1629     } else
1630         TargetInformationW->TargetName = NULL;
1631
1632     if (TargetInformation->NetbiosServerName)
1633     {
1634         TargetInformationW->NetbiosServerName = buffer;
1635         buffer += MultiByteToWideChar(CP_ACP, 0, TargetInformation->NetbiosServerName, -1,
1636                                       TargetInformationW->NetbiosServerName, end - buffer);
1637     } else
1638         TargetInformationW->NetbiosServerName = NULL;
1639
1640     if (TargetInformation->DnsServerName)
1641     {
1642         TargetInformationW->DnsServerName = buffer;
1643         buffer += MultiByteToWideChar(CP_ACP, 0, TargetInformation->DnsServerName, -1,
1644                                       TargetInformationW->DnsServerName, end - buffer);
1645     } else
1646         TargetInformationW->DnsServerName = NULL;
1647
1648     if (TargetInformation->NetbiosDomainName)
1649     {
1650         TargetInformationW->NetbiosDomainName = buffer;
1651         buffer += MultiByteToWideChar(CP_ACP, 0, TargetInformation->NetbiosDomainName, -1,
1652                                       TargetInformationW->NetbiosDomainName, end - buffer);
1653     } else
1654         TargetInformationW->NetbiosDomainName = NULL;
1655
1656     if (TargetInformation->DnsDomainName)
1657     {
1658         TargetInformationW->DnsDomainName = buffer;
1659         buffer += MultiByteToWideChar(CP_ACP, 0, TargetInformation->DnsDomainName, -1,
1660                                       TargetInformationW->DnsDomainName, end - buffer);
1661     } else
1662         TargetInformationW->DnsDomainName = NULL;
1663
1664     if (TargetInformation->DnsTreeName)
1665     {
1666         TargetInformationW->DnsTreeName = buffer;
1667         buffer += MultiByteToWideChar(CP_ACP, 0, TargetInformation->DnsTreeName, -1,
1668                                       TargetInformationW->DnsTreeName, end - buffer);
1669     } else
1670         TargetInformationW->DnsTreeName = NULL;
1671
1672     if (TargetInformation->PackageName)
1673     {
1674         TargetInformationW->PackageName = buffer;
1675         MultiByteToWideChar(CP_ACP, 0, TargetInformation->PackageName, -1,
1676                             TargetInformationW->PackageName, end - buffer);
1677     } else
1678         TargetInformationW->PackageName = NULL;
1679
1680     TargetInformationW->Flags = TargetInformation->Flags;
1681     TargetInformationW->CredTypeCount = TargetInformation->CredTypeCount;
1682     TargetInformationW->CredTypes = TargetInformation->CredTypes;
1683
1684     ret = CredReadDomainCredentialsW(TargetInformationW, Flags, Size, &CredentialsW);
1685
1686     HeapFree(GetProcessHeap(), 0, TargetInformationW);
1687
1688     if (ret)
1689     {
1690         char *buf;
1691         INT needed;
1692
1693         len = *Size * sizeof(PCREDENTIALA);
1694         for (i = 0; i < *Size; i++)
1695             len += convert_PCREDENTIALW_to_PCREDENTIALA(CredentialsW[i], NULL, 0);
1696
1697         *Credentials = HeapAlloc(GetProcessHeap(), 0, len);
1698         if (!*Credentials)
1699         {
1700             CredFree(CredentialsW);
1701             SetLastError(ERROR_OUTOFMEMORY);
1702             return FALSE;
1703         }
1704
1705         buf = (char *)&(*Credentials)[*Size];
1706         len -= *Size * sizeof(PCREDENTIALA);
1707         for (i = 0; i < *Size; i++)
1708         {
1709             (*Credentials)[i] = (PCREDENTIALA)buf;
1710             needed = convert_PCREDENTIALW_to_PCREDENTIALA(CredentialsW[i], (*Credentials)[i], len);
1711             buf += needed;
1712             len -= needed;
1713         }
1714
1715         CredFree(CredentialsW);
1716     }
1717     return ret;
1718 }
1719
1720 /******************************************************************************
1721  * CredReadDomainCredentialsW [ADVAPI32.@]
1722  */
1723 BOOL WINAPI CredReadDomainCredentialsW(PCREDENTIAL_TARGET_INFORMATIONW TargetInformation, DWORD Flags,
1724                                        DWORD *Size, PCREDENTIALW **Credentials)
1725 {
1726     FIXME("(%p, 0x%x, %p, %p) stub\n", TargetInformation, Flags, Size, Credentials);
1727
1728     /* follow Windows behavior - do not test for NULL, initialize early */
1729     *Size = 0;
1730     *Credentials = NULL;
1731     if (!TargetInformation)
1732     {
1733         SetLastError(ERROR_INVALID_PARAMETER);
1734         return FALSE;
1735     }
1736
1737     SetLastError(ERROR_NOT_FOUND);
1738     return FALSE;
1739 }
1740
1741 /******************************************************************************
1742  * CredWriteA [ADVAPI32.@]
1743  */
1744 BOOL WINAPI CredWriteA(PCREDENTIALA Credential, DWORD Flags)
1745 {
1746     BOOL ret;
1747     INT len;
1748     PCREDENTIALW CredentialW;
1749
1750     TRACE("(%p, 0x%x)\n", Credential, Flags);
1751
1752     if (!Credential || !Credential->TargetName)
1753     {
1754         SetLastError(ERROR_INVALID_PARAMETER);
1755         return FALSE;
1756     }
1757
1758     len = convert_PCREDENTIALA_to_PCREDENTIALW(Credential, NULL, 0);
1759     CredentialW = HeapAlloc(GetProcessHeap(), 0, len);
1760     if (!CredentialW)
1761     {
1762         SetLastError(ERROR_OUTOFMEMORY);
1763         return FALSE;
1764     }
1765
1766     convert_PCREDENTIALA_to_PCREDENTIALW(Credential, CredentialW, len);
1767
1768     ret = CredWriteW(CredentialW, Flags);
1769
1770     HeapFree(GetProcessHeap(), 0, CredentialW);
1771
1772     return ret;
1773 }
1774
1775 /******************************************************************************
1776  * CredWriteW [ADVAPI32.@]
1777  */
1778 BOOL WINAPI CredWriteW(PCREDENTIALW Credential, DWORD Flags)
1779 {
1780     HKEY hkeyMgr;
1781     HKEY hkeyCred;
1782     DWORD ret;
1783     LPWSTR key_name;
1784     BYTE key_data[KEY_SIZE];
1785
1786     TRACE("(%p, 0x%x)\n", Credential, Flags);
1787
1788     if (!Credential || !Credential->TargetName)
1789     {
1790         SetLastError(ERROR_INVALID_PARAMETER);
1791         return FALSE;
1792     }
1793
1794     if (Flags & ~CRED_PRESERVE_CREDENTIAL_BLOB)
1795     {
1796         FIXME("unhandled flags 0x%x\n", Flags);
1797         SetLastError(ERROR_INVALID_FLAGS);
1798         return FALSE;
1799     }
1800
1801     if (Credential->Type != CRED_TYPE_GENERIC && Credential->Type != CRED_TYPE_DOMAIN_PASSWORD)
1802     {
1803         FIXME("unhandled type %d\n", Credential->Type);
1804         SetLastError(ERROR_INVALID_PARAMETER);
1805         return FALSE;
1806     }
1807
1808     TRACE("Credential->Flags = 0x%08x\n", Credential->Flags);
1809     TRACE("Credential->Type = %u\n", Credential->Type);
1810     TRACE("Credential->TargetName = %s\n", debugstr_w(Credential->TargetName));
1811     TRACE("Credential->Comment = %s\n", debugstr_w(Credential->Comment));
1812     TRACE("Credential->Persist = %u\n", Credential->Persist);
1813     TRACE("Credential->TargetAlias = %s\n", debugstr_w(Credential->TargetAlias));
1814     TRACE("Credential->UserName = %s\n", debugstr_w(Credential->UserName));
1815
1816     if (Credential->Type == CRED_TYPE_DOMAIN_PASSWORD)
1817     {
1818         if (!Credential->UserName ||
1819             (Credential->Persist == CRED_PERSIST_ENTERPRISE &&
1820             (!strchrW(Credential->UserName, '\\') && !strchrW(Credential->UserName, '@'))))
1821         {
1822             ERR("bad username %s\n", debugstr_w(Credential->UserName));
1823             SetLastError(ERROR_BAD_USERNAME);
1824             return FALSE;
1825         }
1826     }
1827
1828 #ifdef __APPLE__
1829     if (!Credential->AttributeCount &&
1830         Credential->Type == CRED_TYPE_DOMAIN_PASSWORD &&
1831         (Credential->Persist == CRED_PERSIST_LOCAL_MACHINE || Credential->Persist == CRED_PERSIST_ENTERPRISE))
1832     {
1833         ret = mac_write_credential(Credential, Flags & CRED_PRESERVE_CREDENTIAL_BLOB);
1834         if (ret != ERROR_SUCCESS)
1835         {
1836             SetLastError(ret);
1837             return FALSE;
1838         }
1839         return TRUE;
1840     }
1841 #endif
1842
1843     ret = open_cred_mgr_key(&hkeyMgr, FALSE);
1844     if (ret != ERROR_SUCCESS)
1845     {
1846         WARN("couldn't open/create manager key, error %d\n", ret);
1847         SetLastError(ERROR_NO_SUCH_LOGON_SESSION);
1848         return FALSE;
1849     }
1850
1851     ret = get_cred_mgr_encryption_key(hkeyMgr, key_data);
1852     if (ret != ERROR_SUCCESS)
1853     {
1854         RegCloseKey(hkeyMgr);
1855         SetLastError(ret);
1856         return FALSE;
1857     }
1858
1859     key_name = get_key_name_for_target(Credential->TargetName, Credential->Type);
1860     ret = RegCreateKeyExW(hkeyMgr, key_name, 0, NULL,
1861                           Credential->Persist == CRED_PERSIST_SESSION ? REG_OPTION_VOLATILE : REG_OPTION_NON_VOLATILE,
1862                           KEY_READ|KEY_WRITE, NULL, &hkeyCred, NULL);
1863     HeapFree(GetProcessHeap(), 0, key_name);
1864     if (ret != ERROR_SUCCESS)
1865     {
1866         TRACE("credentials for target name %s not found\n",
1867               debugstr_w(Credential->TargetName));
1868         SetLastError(ERROR_NOT_FOUND);
1869         return FALSE;
1870     }
1871
1872     ret = registry_write_credential(hkeyCred, Credential, key_data,
1873                                     Flags & CRED_PRESERVE_CREDENTIAL_BLOB);
1874
1875     RegCloseKey(hkeyCred);
1876     RegCloseKey(hkeyMgr);
1877
1878     if (ret != ERROR_SUCCESS)
1879     {
1880         SetLastError(ret);
1881         return FALSE;
1882     }
1883     return TRUE;
1884 }
1885
1886 /******************************************************************************
1887  * CredGetSessionTypes [ADVAPI32.@]
1888  */
1889 WINADVAPI BOOL WINAPI CredGetSessionTypes(DWORD persistCount, LPDWORD persists)
1890 {
1891     TRACE("(%u, %p)\n", persistCount, persists);
1892
1893     memset(persists, CRED_PERSIST_NONE, persistCount*sizeof(*persists));
1894     if (CRED_TYPE_GENERIC < persistCount)
1895     {
1896         persists[CRED_TYPE_GENERIC] = CRED_PERSIST_ENTERPRISE;
1897
1898         if (CRED_TYPE_DOMAIN_PASSWORD < persistCount)
1899         {
1900             persists[CRED_TYPE_DOMAIN_PASSWORD] = CRED_PERSIST_ENTERPRISE;
1901         }
1902     }
1903     return TRUE;
1904 }
1905
1906 /******************************************************************************
1907  * CredMarshalCredentialA [ADVAPI32.@]
1908  */
1909 BOOL WINAPI CredMarshalCredentialA( CRED_MARSHAL_TYPE type, PVOID cred, LPSTR *out )
1910 {
1911     BOOL ret;
1912     WCHAR *outW;
1913
1914     TRACE("%u, %p, %p\n", type, cred, out);
1915
1916     if ((ret = CredMarshalCredentialW( type, cred, &outW )))
1917     {
1918         int len = WideCharToMultiByte( CP_ACP, 0, outW, -1, NULL, 0, NULL, NULL );
1919         if (!(*out = HeapAlloc( GetProcessHeap(), 0, len )))
1920         {
1921             HeapFree( GetProcessHeap(), 0, outW );
1922             return FALSE;
1923         }
1924         WideCharToMultiByte( CP_ACP, 0, outW, -1, *out, len, NULL, NULL );
1925         HeapFree( GetProcessHeap(), 0, outW );
1926     }
1927     return ret;
1928 }
1929
1930 static UINT cred_encode( const char *bin, unsigned int len, WCHAR *cred )
1931 {
1932     static char enc[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789#-";
1933     UINT n = 0, x;
1934
1935     while (len > 0)
1936     {
1937         cred[n++] = enc[bin[0] & 0x3f];
1938         x = (bin[0] & 0xc0) >> 6;
1939         if (len == 1)
1940         {
1941             cred[n++] = enc[x];
1942             break;
1943         }
1944         cred[n++] = enc[((bin[1] & 0xf) << 2) | x];
1945         x = (bin[1] & 0xf0) >> 4;
1946         if (len == 2)
1947         {
1948             cred[n++] = enc[x];
1949             break;
1950         }
1951         cred[n++] = enc[((bin[2] & 0x3) << 4) | x];
1952         cred[n++] = enc[(bin[2] & 0xfc) >> 2];
1953         bin += 3;
1954         len -= 3;
1955     }
1956     return n;
1957 }
1958
1959 /******************************************************************************
1960  * CredMarshalCredentialW [ADVAPI32.@]
1961  */
1962 BOOL WINAPI CredMarshalCredentialW( CRED_MARSHAL_TYPE type, PVOID cred, LPWSTR *out )
1963 {
1964     CERT_CREDENTIAL_INFO *cert = cred;
1965     USERNAME_TARGET_CREDENTIAL_INFO *target = cred;
1966     DWORD len, size;
1967     WCHAR *p;
1968
1969     TRACE("%u, %p, %p\n", type, cred, out);
1970
1971     if (!cred || (type == CertCredential && cert->cbSize < sizeof(*cert)) ||
1972         (type != CertCredential && type != UsernameTargetCredential && type != BinaryBlobCredential) ||
1973         (type == UsernameTargetCredential && (!target->UserName || !target->UserName[0])))
1974     {
1975         SetLastError( ERROR_INVALID_PARAMETER );
1976         return FALSE;
1977     }
1978     switch (type)
1979     {
1980     case CertCredential:
1981     {
1982         char hash[CERT_HASH_LENGTH + 2];
1983
1984         memcpy( hash, cert->rgbHashOfCert, sizeof(cert->rgbHashOfCert) );
1985         memset( hash + sizeof(cert->rgbHashOfCert), 0, sizeof(hash) - sizeof(cert->rgbHashOfCert) );
1986
1987         size = sizeof(hash) * 4 / 3;
1988         if (!(p = HeapAlloc( GetProcessHeap(), 0, (size + 4) * sizeof(WCHAR) ))) return FALSE;
1989         p[0] = '@';
1990         p[1] = '@';
1991         p[2] = 'A' + type;
1992         len = cred_encode( (const char *)hash, sizeof(hash), p + 3 );
1993         p[len] = 0;
1994         break;
1995     }
1996     case UsernameTargetCredential:
1997     {
1998         len = strlenW( target->UserName );
1999         size = (sizeof(DWORD) + len * sizeof(WCHAR) + 2) * 4 / 3;
2000         if (!(p = HeapAlloc( GetProcessHeap(), 0, (size + 4) * sizeof(WCHAR) ))) return FALSE;
2001         p[0] = '@';
2002         p[1] = '@';
2003         p[2] = 'A' + type;
2004         size = len * sizeof(WCHAR);
2005         len = cred_encode( (const char *)&size, sizeof(DWORD), p + 3 );
2006         len += cred_encode( (const char *)target->UserName, size, p + 3 + len );
2007         p[len + 3] = 0;
2008         break;
2009     }
2010     case BinaryBlobCredential:
2011         FIXME("BinaryBlobCredential not implemented\n");
2012         return FALSE;
2013     default:
2014         return FALSE;
2015     }
2016     *out = p;
2017     return TRUE;
2018 }
2019
2020 /******************************************************************************
2021  * CredUnmarshalCredentialA [ADVAPI32.@]
2022  */
2023 BOOL WINAPI CredUnmarshalCredentialA( LPCSTR cred, PCRED_MARSHAL_TYPE type, PVOID *out )
2024 {
2025     BOOL ret;
2026     WCHAR *credW = NULL;
2027
2028     TRACE("%s, %p, %p\n", debugstr_a(cred), type, out);
2029
2030     if (cred)
2031     {
2032         int len = MultiByteToWideChar( CP_ACP, 0, cred, -1, NULL, 0 );
2033         if (!(credW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) return FALSE;
2034         MultiByteToWideChar( CP_ACP, 0, cred, -1, credW, len );
2035     }
2036     ret = CredUnmarshalCredentialW( credW, type, out );
2037     HeapFree( GetProcessHeap(), 0, credW );
2038     return ret;
2039 }
2040
2041 static inline char char_decode( WCHAR c )
2042 {
2043     if (c >= 'A' && c <= 'Z') return c - 'A';
2044     if (c >= 'a' && c <= 'z') return c - 'a' + 26;
2045     if (c >= '0' && c <= '9') return c - '0' + 52;
2046     if (c == '#') return 62;
2047     if (c == '-') return 63;
2048     return 64;
2049 }
2050
2051 static BOOL cred_decode( const WCHAR *cred, unsigned int len, char *buf )
2052 {
2053     unsigned int i = 0;
2054     char c0, c1, c2, c3;
2055     const WCHAR *p = cred;
2056
2057     while (len >= 4)
2058     {
2059         if ((c0 = char_decode( p[0] )) > 63) return FALSE;
2060         if ((c1 = char_decode( p[1] )) > 63) return FALSE;
2061         if ((c2 = char_decode( p[2] )) > 63) return FALSE;
2062         if ((c3 = char_decode( p[3] )) > 63) return FALSE;
2063
2064         buf[i + 0] = (c1 << 6) | c0;
2065         buf[i + 1] = (c2 << 4) | (c1 >> 2);
2066         buf[i + 2] = (c3 << 2) | (c2 >> 4);
2067         len -= 4;
2068         i += 3;
2069         p += 4;
2070     }
2071     if (len == 3)
2072     {
2073         if ((c0 = char_decode( p[0] )) > 63) return FALSE;
2074         if ((c1 = char_decode( p[1] )) > 63) return FALSE;
2075         if ((c2 = char_decode( p[2] )) > 63) return FALSE;
2076
2077         buf[i + 0] = (c1 << 6) | c0;
2078         buf[i + 1] = (c2 << 4) | (c1 >> 2);
2079         buf[i + 2] = c2 >> 4;
2080     }
2081     else if (len == 2)
2082     {
2083         if ((c0 = char_decode( p[0] )) > 63) return FALSE;
2084         if ((c1 = char_decode( p[1] )) > 63) return FALSE;
2085
2086         buf[i + 0] = (c1 << 6) | c0;
2087         buf[i + 1] = c1 >> 2;
2088         buf[i + 2] = 0;
2089     }
2090     else if (len == 1)
2091     {
2092         if ((c0 = char_decode( p[0] )) > 63) return FALSE;
2093
2094         buf[i + 0] = c0;
2095         buf[i + 1] = 0;
2096         buf[i + 2] = 0;
2097     }
2098     return TRUE;
2099 }
2100
2101 /******************************************************************************
2102  * CredUnmarshalCredentialW [ADVAPI32.@]
2103  */
2104 BOOL WINAPI CredUnmarshalCredentialW( LPCWSTR cred, PCRED_MARSHAL_TYPE type, PVOID *out )
2105 {
2106     unsigned int len, buflen;
2107
2108     TRACE("%s, %p, %p\n", debugstr_w(cred), type, out);
2109
2110     if (!cred || cred[0] != '@' || cred[1] != '@' || !cred[2] || !cred[3])
2111     {
2112         SetLastError( ERROR_INVALID_PARAMETER );
2113         return FALSE;
2114     }
2115     len = strlenW( cred + 3 );
2116     switch (cred[2] - 'A')
2117     {
2118     case CertCredential:
2119     {
2120         char hash[CERT_HASH_LENGTH + 2];
2121         CERT_CREDENTIAL_INFO *cert;
2122
2123         if (len != 27 || !cred_decode( cred + 3, len, hash ))
2124         {
2125             SetLastError( ERROR_INVALID_PARAMETER );
2126             return FALSE;
2127         }
2128         if (!(cert = HeapAlloc( GetProcessHeap(), 0, sizeof(*cert) ))) return FALSE;
2129         memcpy( cert->rgbHashOfCert, hash, sizeof(cert->rgbHashOfCert) );
2130         cert->cbSize = sizeof(*cert);
2131         *type = CertCredential;
2132         *out = cert;
2133         break;
2134     }
2135     case UsernameTargetCredential:
2136     {
2137         USERNAME_TARGET_CREDENTIAL_INFO *target;
2138         ULONGLONG size = 0;
2139
2140         if (len < 9 || !cred_decode( cred + 3, 6, (char *)&size ) ||
2141             !size || size % sizeof(WCHAR) || size > INT_MAX)
2142         {
2143             SetLastError( ERROR_INVALID_PARAMETER );
2144             return FALSE;
2145         }
2146         buflen = sizeof(*target) + size + sizeof(WCHAR);
2147         if (!(target = HeapAlloc( GetProcessHeap(), 0, buflen ))) return FALSE;
2148         if (!cred_decode( cred + 9, len - 6, (char *)(target + 1) ))
2149         {
2150             HeapFree( GetProcessHeap(), 0, target );
2151             return FALSE;
2152         }
2153         target->UserName = (WCHAR *)(target + 1);
2154         target->UserName[size / sizeof(WCHAR)] = 0;
2155         *type = UsernameTargetCredential;
2156         *out = target;
2157         break;
2158     }
2159     case BinaryBlobCredential:
2160         FIXME("BinaryBlobCredential not implemented\n");
2161         return FALSE;
2162     default:
2163         WARN("unhandled type %u\n", cred[2] - 'A');
2164         return FALSE;
2165     }
2166     return TRUE;
2167 }
2168
2169 /******************************************************************************
2170  * CredIsMarshaledCredentialW [ADVAPI32.@]
2171  *
2172  * Check, if the name parameter is a marshaled credential, hash or binary blob
2173  *
2174  * PARAMS
2175  *  name    the name to check
2176  *
2177  * RETURNS
2178  *  TRUE:  the name parameter is a marshaled credential, hash or binary blob
2179  *  FALSE: the name is a plain username
2180  */
2181 BOOL WINAPI CredIsMarshaledCredentialW(LPCWSTR name)
2182 {
2183     TRACE("(%s)\n", debugstr_w(name));
2184
2185     if (name && name[0] == '@' && name[1] == '@' && name[2] > 'A' && name[3])
2186     {
2187         char hash[CERT_HASH_LENGTH + 2];
2188         int len = strlenW(name + 3 );
2189         DWORD size;
2190
2191         if ((name[2] - 'A') == CertCredential && (len == 27) && cred_decode(name + 3, len, hash))
2192             return TRUE;
2193
2194         if (((name[2] - 'A') == UsernameTargetCredential) &&
2195             (len >= 9) && cred_decode(name + 3, 6, (char *)&size) && size)
2196             return TRUE;
2197
2198         if ((name[2] - 'A') == BinaryBlobCredential)
2199             FIXME("BinaryBlobCredential not checked\n");
2200
2201         if ((name[2] - 'A') > BinaryBlobCredential)
2202             TRACE("unknown type: %d\n", (name[2] - 'A'));
2203     }
2204
2205     SetLastError(ERROR_INVALID_PARAMETER);
2206     return FALSE;
2207 }
2208
2209 /******************************************************************************
2210  * CredIsMarshaledCredentialA [ADVAPI32.@]
2211  *
2212  * See CredIsMarshaledCredentialW
2213  *
2214  */
2215 BOOL WINAPI CredIsMarshaledCredentialA(LPCSTR name)
2216 {
2217     LPWSTR nameW = NULL;
2218     BOOL res;
2219     int len;
2220
2221     TRACE("(%s)\n", debugstr_a(name));
2222
2223     if (name)
2224     {
2225         len = MultiByteToWideChar(CP_ACP, 0, name, -1, NULL, 0);
2226         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2227         MultiByteToWideChar(CP_ACP, 0, name, -1, nameW, len);
2228     }
2229
2230     res = CredIsMarshaledCredentialW(nameW);
2231     HeapFree(GetProcessHeap(), 0, nameW);
2232     return res;
2233 }