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