wininet: Use LIST_FOR_EACH_ENTRY in URLCacheContainers_FindContainerW instead of...
[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 #include "windef.h"
25 #include "winbase.h"
26 #include "winreg.h"
27 #include "wincred.h"
28 #include "winternl.h"
29
30 #ifdef __APPLE__
31 # include <Security/SecKeychain.h>
32 # include <Security/SecKeychainItem.h>
33 # include <Security/SecKeychainSearch.h>
34 #endif
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 wszTargetNameValue[] = {'T','a','r','g','e','t','N','a','m','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, (LPVOID)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 = sizeof(key_data);
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         {
173             credential->UserName = NULL;
174             ret = ERROR_SUCCESS;
175         }
176         else if (ret != ERROR_SUCCESS)
177             return ret;
178         else if (type != REG_SZ)
179             return ERROR_REGISTRY_CORRUPT;
180         else
181             buffer += count;
182     }
183
184     ret = read_credential_blob(hkey, key_data, NULL, &count);
185     if (ret != ERROR_FILE_NOT_FOUND && ret != ERROR_SUCCESS)
186         return ret;
187     *len += count;
188     if (credential)
189     {
190         credential->CredentialBlob = (LPBYTE)buffer;
191         ret = read_credential_blob(hkey, key_data, credential->CredentialBlob, &count);
192         if (ret == ERROR_FILE_NOT_FOUND)
193         {
194             credential->CredentialBlob = NULL;
195             ret = ERROR_SUCCESS;
196         }
197         else if (ret != ERROR_SUCCESS)
198             return ret;
199         credential->CredentialBlobSize = count;
200         buffer += count;
201     }
202
203     /* FIXME: Attributes */
204     if (credential)
205     {
206         credential->AttributeCount = 0;
207         credential->Attributes = NULL;
208     }
209
210     if (!credential) return ERROR_SUCCESS;
211
212     count = sizeof(credential->Flags);
213     ret = RegQueryValueExW(hkey, wszFlagsValue, NULL, &type, (LPVOID)&credential->Flags,
214                            &count);
215     if (ret != ERROR_SUCCESS)
216         return ret;
217     else if (type != REG_DWORD)
218         return ERROR_REGISTRY_CORRUPT;
219     count = sizeof(credential->Type);
220     ret = RegQueryValueExW(hkey, wszTypeValue, NULL, &type, (LPVOID)&credential->Type,
221                            &count);
222     if (ret != ERROR_SUCCESS)
223         return ret;
224     else if (type != REG_DWORD)
225         return ERROR_REGISTRY_CORRUPT;
226
227     count = sizeof(credential->LastWritten);
228     ret = RegQueryValueExW(hkey, wszLastWrittenValue, NULL, &type, (LPVOID)&credential->LastWritten,
229                            &count);
230     if (ret != ERROR_SUCCESS)
231         return ret;
232     else if (type != REG_BINARY)
233         return ERROR_REGISTRY_CORRUPT;
234     count = sizeof(credential->Persist);
235     ret = RegQueryValueExW(hkey, wszPersistValue, NULL, &type, (LPVOID)&credential->Persist,
236                            &count);
237     if (ret == ERROR_SUCCESS && type != REG_DWORD)
238         return ERROR_REGISTRY_CORRUPT;
239     return ret;
240 }
241
242 #ifdef __APPLE__
243 static DWORD mac_read_credential_from_item(SecKeychainItemRef item, BOOL require_password,
244                                            PCREDENTIALW credential, char *buffer,
245                                            DWORD *len)
246 {
247     OSStatus status;
248     UInt32 i;
249     UInt32 cred_blob_len;
250     void *cred_blob;
251     LPWSTR domain = NULL;
252     LPWSTR user = NULL;
253     SecKeychainAttributeInfo info;
254     SecKeychainAttributeList *attr_list;
255     UInt32 info_tags[] = { kSecServerItemAttr, kSecSecurityDomainItemAttr, kSecAccountItemAttr,
256                            kSecCommentItemAttr, kSecCreationDateItemAttr };
257     info.count = sizeof(info_tags)/sizeof(info_tags[0]);
258     info.tag = info_tags;
259     info.format = NULL;
260     status = SecKeychainItemCopyAttributesAndData(item, &info, NULL, &attr_list, &cred_blob_len, &cred_blob);
261     if (status == errSecAuthFailed && !require_password)
262     {
263         cred_blob_len = 0;
264         cred_blob = NULL;
265         status = SecKeychainItemCopyAttributesAndData(item, &info, NULL, &attr_list, &cred_blob_len, NULL);
266     }
267     if (status != noErr)
268     {
269         WARN("SecKeychainItemCopyAttributesAndData returned status %ld\n", status);
270         return ERROR_NOT_FOUND;
271     }
272     if (buffer)
273     {
274         credential->Flags = 0;
275         credential->Type = CRED_TYPE_DOMAIN_PASSWORD;
276         credential->TargetName = NULL;
277         credential->Comment = NULL;
278         memset(&credential->LastWritten, 0, sizeof(credential->LastWritten));
279         credential->CredentialBlobSize = 0;
280         credential->CredentialBlob = NULL;
281         credential->Persist = CRED_PERSIST_LOCAL_MACHINE;
282         credential->AttributeCount = 0;
283         credential->Attributes = NULL;
284         credential->TargetAlias = NULL;
285         credential->UserName = NULL;
286     }
287     for (i = 0; i < attr_list->count; i++)
288     {
289         switch (attr_list->attr[i].tag)
290         {
291             case kSecServerItemAttr:
292                 TRACE("kSecServerItemAttr: %.*s\n", (int)attr_list->attr[i].length,
293                       (char *)attr_list->attr[i].data);
294                 if (!attr_list->attr[i].data) continue;
295                 if (buffer)
296                 {
297                     INT str_len;
298                     credential->TargetName = (LPWSTR)buffer;
299                     str_len = MultiByteToWideChar(CP_UTF8, 0, attr_list->attr[i].data,
300                                                   attr_list->attr[i].length, (LPWSTR)buffer, 0xffff);
301                     credential->TargetName[str_len] = '\0';
302                     buffer += (str_len + 1) * sizeof(WCHAR);
303                     *len += (str_len + 1) * sizeof(WCHAR);
304                 }
305                 else
306                 {
307                     INT str_len;
308                     str_len = MultiByteToWideChar(CP_UTF8, 0, attr_list->attr[i].data,
309                                                   attr_list->attr[i].length, NULL, 0);
310                     *len += (str_len + 1) * sizeof(WCHAR);
311                 }
312                 break;
313             case kSecAccountItemAttr:
314             {
315                 INT str_len;
316                 TRACE("kSecAccountItemAttr: %.*s\n", (int)attr_list->attr[i].length,
317                       (char *)attr_list->attr[i].data);
318                 if (!attr_list->attr[i].data) continue;
319                 str_len = MultiByteToWideChar(CP_UTF8, 0, attr_list->attr[i].data,
320                                               attr_list->attr[i].length, NULL, 0);
321                 user = HeapAlloc(GetProcessHeap(), 0, (str_len + 1) * sizeof(WCHAR));
322                 MultiByteToWideChar(CP_UTF8, 0, attr_list->attr[i].data,
323                                     attr_list->attr[i].length, user, str_len);
324                 user[str_len] = '\0';
325                 break;
326             }
327             case kSecCommentItemAttr:
328                 TRACE("kSecCommentItemAttr: %.*s\n", (int)attr_list->attr[i].length,
329                       (char *)attr_list->attr[i].data);
330                 if (!attr_list->attr[i].data) continue;
331                 if (buffer)
332                 {
333                     INT str_len;
334                     credential->Comment = (LPWSTR)buffer;
335                     str_len = MultiByteToWideChar(CP_UTF8, 0, attr_list->attr[i].data,
336                                                   attr_list->attr[i].length, (LPWSTR)buffer, 0xffff);
337                     credential->Comment[str_len] = '\0';
338                     buffer += (str_len + 1) * sizeof(WCHAR);
339                     *len += (str_len + 1) * sizeof(WCHAR);
340                 }
341                 else
342                 {
343                     INT str_len;
344                     str_len = MultiByteToWideChar(CP_UTF8, 0, attr_list->attr[i].data,
345                                                   attr_list->attr[i].length, NULL, 0);
346                     *len += (str_len + 1) * sizeof(WCHAR);
347                 }
348                 break;
349             case kSecSecurityDomainItemAttr:
350             {
351                 INT str_len;
352                 TRACE("kSecSecurityDomainItemAttr: %.*s\n", (int)attr_list->attr[i].length,
353                       (char *)attr_list->attr[i].data);
354                 if (!attr_list->attr[i].data) continue;
355                 str_len = MultiByteToWideChar(CP_UTF8, 0, attr_list->attr[i].data,
356                                               attr_list->attr[i].length, NULL, 0);
357                 domain = HeapAlloc(GetProcessHeap(), 0, (str_len + 1) * sizeof(WCHAR));
358                 MultiByteToWideChar(CP_UTF8, 0, attr_list->attr[i].data,
359                                     attr_list->attr[i].length, domain, str_len);
360                 domain[str_len] = '\0';
361                 break;
362             }
363             case kSecCreationDateItemAttr:
364                 TRACE("kSecCreationDateItemAttr: %.*s\n", (int)attr_list->attr[i].length,
365                       (char *)attr_list->attr[i].data);
366                 if (buffer)
367                 {
368                     LARGE_INTEGER win_time;
369                     struct tm tm;
370                     time_t time;
371                     memset(&tm, 0, sizeof(tm));
372                     strptime(attr_list->attr[i].data, "%Y%m%d%H%M%SZ", &tm);
373                     time = mktime(&tm);
374                     RtlSecondsSince1970ToTime(time, &win_time);
375                     credential->LastWritten.dwLowDateTime = win_time.u.LowPart;
376                     credential->LastWritten.dwHighDateTime = win_time.u.HighPart;
377                 }
378                 break;
379         }
380     }
381
382     if (user)
383     {
384         INT str_len;
385         if (buffer)
386             credential->UserName = (LPWSTR)buffer;
387         if (domain)
388         {
389             str_len = strlenW(domain);
390             *len += (str_len + 1) * sizeof(WCHAR);
391             if (buffer)
392             {
393                 memcpy(credential->UserName, domain, str_len * sizeof(WCHAR));
394                 /* FIXME: figure out when to use an '@' */
395                 credential->UserName[str_len] = '\\';
396                 buffer += (str_len + 1) * sizeof(WCHAR);
397             }
398         }
399         str_len = strlenW(user);
400         *len += (str_len + 1) * sizeof(WCHAR);
401         if (buffer)
402         {
403             memcpy(buffer, user, (str_len + 1) * sizeof(WCHAR));
404             buffer += (str_len + 1) * sizeof(WCHAR);
405             TRACE("UserName = %s\n", debugstr_w(credential->UserName));
406         }
407     }
408     HeapFree(GetProcessHeap(), 0, user);
409     HeapFree(GetProcessHeap(), 0, domain);
410
411     if (cred_blob)
412     {
413         if (buffer)
414         {
415             INT str_len;
416             credential->CredentialBlob = (BYTE *)buffer;
417             str_len = MultiByteToWideChar(CP_UTF8, 0, cred_blob, cred_blob_len,
418                                           (LPWSTR)buffer, 0xffff);
419             credential->CredentialBlobSize = str_len * sizeof(WCHAR);
420             buffer += str_len * sizeof(WCHAR);
421             *len += str_len * sizeof(WCHAR);
422         }
423         else
424         {
425             INT str_len;
426             str_len = MultiByteToWideChar(CP_UTF8, 0, cred_blob, cred_blob_len,
427                                           NULL, 0);
428             *len += str_len * sizeof(WCHAR);
429         }
430     }
431     SecKeychainItemFreeAttributesAndData(attr_list, cred_blob);
432     return ERROR_SUCCESS;
433 }
434 #endif
435
436 static DWORD write_credential_blob(HKEY hkey, LPCWSTR target_name, DWORD type,
437                                    const BYTE key_data[KEY_SIZE],
438                                    const BYTE *credential_blob, DWORD credential_blob_size)
439 {
440     LPBYTE encrypted_credential_blob;
441     struct ustring data;
442     struct ustring key;
443     DWORD ret;
444
445     key.Length = key.MaximumLength = sizeof(key_data);
446     key.Buffer = (unsigned char *)key_data;
447
448     encrypted_credential_blob = HeapAlloc(GetProcessHeap(), 0, credential_blob_size);
449     if (!encrypted_credential_blob) return ERROR_OUTOFMEMORY;
450
451     memcpy(encrypted_credential_blob, credential_blob, credential_blob_size);
452     data.Length = data.MaximumLength = credential_blob_size;
453     data.Buffer = encrypted_credential_blob;
454     SystemFunction032(&data, &key);
455
456     ret = RegSetValueExW(hkey, wszPasswordValue, 0, REG_BINARY, (LPVOID)encrypted_credential_blob, credential_blob_size);
457     HeapFree(GetProcessHeap(), 0, encrypted_credential_blob);
458
459     return ret;
460 }
461
462 static DWORD registry_write_credential(HKEY hkey, const CREDENTIALW *credential,
463                                        const BYTE key_data[KEY_SIZE], BOOL preserve_blob)
464 {
465     DWORD ret;
466     FILETIME LastWritten;
467
468     GetSystemTimeAsFileTime(&LastWritten);
469
470     ret = RegSetValueExW(hkey, wszFlagsValue, 0, REG_DWORD, (LPVOID)&credential->Flags,
471                          sizeof(credential->Flags));
472     if (ret != ERROR_SUCCESS) return ret;
473     ret = RegSetValueExW(hkey, wszTypeValue, 0, REG_DWORD, (LPVOID)&credential->Type,
474                          sizeof(credential->Type));
475     if (ret != ERROR_SUCCESS) return ret;
476     ret = RegSetValueExW(hkey, NULL, 0, REG_SZ, (LPVOID)credential->TargetName,
477                          sizeof(WCHAR)*(strlenW(credential->TargetName)+1));
478     if (ret != ERROR_SUCCESS) return ret;
479     if (credential->Comment)
480     {
481         ret = RegSetValueExW(hkey, wszCommentValue, 0, REG_SZ, (LPVOID)credential->Comment,
482                              sizeof(WCHAR)*(strlenW(credential->Comment)+1));
483         if (ret != ERROR_SUCCESS) return ret;
484     }
485     ret = RegSetValueExW(hkey, wszLastWrittenValue, 0, REG_BINARY, (LPVOID)&LastWritten,
486                          sizeof(LastWritten));
487     if (ret != ERROR_SUCCESS) return ret;
488     ret = RegSetValueExW(hkey, wszPersistValue, 0, REG_DWORD, (LPVOID)&credential->Persist,
489                          sizeof(credential->Persist));
490     if (ret != ERROR_SUCCESS) return ret;
491     /* FIXME: Attributes */
492     if (credential->TargetAlias)
493     {
494         ret = RegSetValueExW(hkey, wszTargetAliasValue, 0, REG_SZ, (LPVOID)credential->TargetAlias,
495                              sizeof(WCHAR)*(strlenW(credential->TargetAlias)+1));
496         if (ret != ERROR_SUCCESS) return ret;
497     }
498     if (credential->UserName)
499     {
500         ret = RegSetValueExW(hkey, wszUserNameValue, 0, REG_SZ, (LPVOID)credential->UserName,
501                              sizeof(WCHAR)*(strlenW(credential->UserName)+1));
502         if (ret != ERROR_SUCCESS) return ret;
503     }
504     if (!preserve_blob)
505     {
506         ret = write_credential_blob(hkey, credential->TargetName, credential->Type,
507                                     key_data, credential->CredentialBlob,
508                                     credential->CredentialBlobSize);
509     }
510     return ret;
511 }
512
513 #ifdef __APPLE__
514 static DWORD mac_write_credential(const CREDENTIALW *credential, BOOL preserve_blob)
515 {
516     OSStatus status;
517     SecKeychainItemRef keychain_item;
518     char *username;
519     char *domain = NULL;
520     char *password;
521     char *servername;
522     UInt32 userlen;
523     UInt32 domainlen = 0;
524     UInt32 pwlen;
525     UInt32 serverlen;
526     LPCWSTR p;
527     SecKeychainAttribute attrs[1];
528     SecKeychainAttributeList attr_list;
529
530     if (credential->Flags)
531         FIXME("Flags 0x%x not written\n", credential->Flags);
532     if (credential->Type != CRED_TYPE_DOMAIN_PASSWORD)
533         FIXME("credential type of %d not supported\n", credential->Type);
534     if (credential->Persist != CRED_PERSIST_LOCAL_MACHINE)
535         FIXME("persist value of %d not supported\n", credential->Persist);
536     if (credential->AttributeCount)
537         FIXME("custom attributes not supported\n");
538
539     p = strchrW(credential->UserName, '\\');
540     if (p)
541     {
542         domainlen = WideCharToMultiByte(CP_UTF8, 0, credential->UserName,
543                                         p - credential->UserName, NULL, 0, NULL, NULL);
544         domain = HeapAlloc(GetProcessHeap(), 0, (domainlen + 1) * sizeof(*domain));
545         WideCharToMultiByte(CP_UTF8, 0, credential->UserName, p - credential->UserName,
546                             domain, domainlen, NULL, NULL);
547         domain[domainlen] = '\0';
548         p++;
549     }
550     else
551         p = credential->UserName;
552     userlen = WideCharToMultiByte(CP_UTF8, 0, p, -1, NULL, 0, NULL, NULL);
553     username = HeapAlloc(GetProcessHeap(), 0, userlen * sizeof(*username));
554     WideCharToMultiByte(CP_UTF8, 0, p, -1, username, userlen, NULL, NULL);
555
556     serverlen = WideCharToMultiByte(CP_UTF8, 0, credential->TargetName, -1, NULL, 0, NULL, NULL);
557     servername = HeapAlloc(GetProcessHeap(), 0, serverlen * sizeof(*servername));
558     WideCharToMultiByte(CP_UTF8, 0, credential->TargetName, -1, servername, serverlen, NULL, NULL);
559     pwlen = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)credential->CredentialBlob,
560                                 credential->CredentialBlobSize / sizeof(WCHAR), NULL, 0, NULL, NULL);
561     password = HeapAlloc(GetProcessHeap(), 0, pwlen * sizeof(*domain));
562     WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)credential->CredentialBlob,
563                         credential->CredentialBlobSize / sizeof(WCHAR), password, pwlen, NULL, NULL);
564
565     TRACE("adding server %s, domain %s, username %s using Keychain\n", servername, domain, username);
566     status = SecKeychainAddInternetPassword(NULL, strlen(servername), servername,
567                                             strlen(domain), domain, strlen(username),
568                                             username, 0, NULL, 0,
569                                             0 /* no protocol */,
570                                             kSecAuthenticationTypeDefault,
571                                             strlen(password), password, &keychain_item);
572     if (status != noErr)
573         ERR("SecKeychainAddInternetPassword returned %ld\n", status);
574     if (status == errSecDuplicateItem)
575     {
576         SecKeychainItemRef keychain_item;
577
578         status = SecKeychainFindInternetPassword(NULL, strlen(servername), servername,
579                                                  strlen(domain), domain,
580                                                  strlen(username), username,
581                                                  0, NULL /* any path */, 0,
582                                                  0 /* any protocol */,
583                                                  0 /* any authentication type */,
584                                                  0, NULL, &keychain_item);
585         if (status != noErr)
586             ERR("SecKeychainFindInternetPassword returned %ld\n", status);
587     }
588     HeapFree(GetProcessHeap(), 0, domain);
589     HeapFree(GetProcessHeap(), 0, username);
590     HeapFree(GetProcessHeap(), 0, servername);
591     if (status != noErr)
592     {
593         HeapFree(GetProcessHeap(), 0, password);
594         return ERROR_GEN_FAILURE;
595     }
596     if (credential->Comment)
597     {
598         attr_list.count = 1;
599         attr_list.attr = attrs;
600         attrs[0].tag = kSecCommentItemAttr;
601         attrs[0].length = WideCharToMultiByte(CP_UTF8, 0, credential->Comment, -1, NULL, 0, NULL, NULL);
602         if (attrs[0].length) attrs[0].length--;
603         attrs[0].data = HeapAlloc(GetProcessHeap(), 0, attrs[0].length);
604         WideCharToMultiByte(CP_UTF8, 0, credential->Comment, -1, attrs[0].data, attrs[0].length, NULL, NULL);
605     }
606     else
607     {
608         attr_list.count = 0;
609         attr_list.attr = NULL;
610     }
611     status = SecKeychainItemModifyAttributesAndData(keychain_item, &attr_list,
612                                                     preserve_blob ? 0 : strlen(password),
613                                                     preserve_blob ? NULL : password);
614     if (credential->Comment)
615         HeapFree(GetProcessHeap(), 0, attrs[0].data);
616     HeapFree(GetProcessHeap(), 0, password);
617     /* FIXME: set TargetAlias attribute */
618     CFRelease(keychain_item);
619     return ERROR_SUCCESS;
620 }
621 #endif
622
623 static DWORD open_cred_mgr_key(HKEY *hkey, BOOL open_for_write)
624 {
625     return RegCreateKeyExW(HKEY_CURRENT_USER, wszCredentialManagerKey, 0,
626                            NULL, REG_OPTION_NON_VOLATILE,
627                            KEY_READ | (open_for_write ? KEY_WRITE : 0), NULL, hkey, NULL);
628 }
629
630 static DWORD get_cred_mgr_encryption_key(HKEY hkeyMgr, BYTE key_data[KEY_SIZE])
631 {
632     static const BYTE my_key_data[KEY_SIZE] = { 0 };
633     DWORD type;
634     DWORD count;
635     FILETIME ft;
636     ULONG seed;
637     ULONG value;
638     DWORD ret;
639
640     memcpy(key_data, my_key_data, KEY_SIZE);
641
642     count = KEY_SIZE;
643     ret = RegQueryValueExW(hkeyMgr, wszEncryptionKeyValue, NULL, &type, (LPVOID)key_data,
644                            &count);
645     if (ret == ERROR_SUCCESS)
646     {
647         if (type != REG_BINARY)
648             return ERROR_REGISTRY_CORRUPT;
649         else
650             return ERROR_SUCCESS;
651     }
652     if (ret != ERROR_FILE_NOT_FOUND)
653         return ret;
654
655     GetSystemTimeAsFileTime(&ft);
656     seed = ft.dwLowDateTime;
657     value = RtlUniform(&seed);
658     *(DWORD *)key_data = value;
659     seed = ft.dwHighDateTime;
660     value = RtlUniform(&seed);
661     *(DWORD *)(key_data + 4) = value;
662
663     ret = RegSetValueExW(hkeyMgr, wszEncryptionKeyValue, 0, REG_BINARY,
664                          (LPVOID)key_data, KEY_SIZE);
665     if (ret == ERROR_ACCESS_DENIED)
666     {
667         ret = open_cred_mgr_key(&hkeyMgr, TRUE);
668         if (ret == ERROR_SUCCESS)
669         {
670             ret = RegSetValueExW(hkeyMgr, wszEncryptionKeyValue, 0, REG_BINARY,
671                                  (LPVOID)key_data, KEY_SIZE);
672             RegCloseKey(hkeyMgr);
673         }
674     }
675     return ret;
676 }
677
678 static LPWSTR get_key_name_for_target(LPCWSTR target_name, DWORD type)
679 {
680     static const WCHAR wszGenericPrefix[] = {'G','e','n','e','r','i','c',':',' ',0};
681     static const WCHAR wszDomPasswdPrefix[] = {'D','o','m','P','a','s','s','w','d',':',' ',0};
682     INT len;
683     LPCWSTR prefix = NULL;
684     LPWSTR key_name, p;
685
686     len = strlenW(target_name);
687     if (type == CRED_TYPE_GENERIC)
688     {
689         prefix = wszGenericPrefix;
690         len += sizeof(wszGenericPrefix)/sizeof(wszGenericPrefix[0]);
691     }
692     else
693     {
694         prefix = wszDomPasswdPrefix;
695         len += sizeof(wszDomPasswdPrefix)/sizeof(wszDomPasswdPrefix[0]);
696     }
697
698     key_name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
699     if (!key_name) return NULL;
700
701     strcpyW(key_name, prefix);
702     strcatW(key_name, target_name);
703
704     for (p = key_name; *p; p++)
705         if (*p == '\\') *p = '_';
706
707     return key_name;
708 }
709
710 static BOOL credential_matches_filter(HKEY hkeyCred, LPCWSTR filter)
711 {
712     LPWSTR target_name;
713     DWORD ret;
714     DWORD type;
715     DWORD count;
716     LPCWSTR p;
717
718     if (!filter) return TRUE;
719
720     ret = RegQueryValueExW(hkeyCred, NULL, 0, &type, NULL, &count);
721     if (ret != ERROR_SUCCESS)
722         return FALSE;
723     else if (type != REG_SZ)
724         return FALSE;
725
726     target_name = HeapAlloc(GetProcessHeap(), 0, count);
727     if (!target_name)
728         return FALSE;
729     ret = RegQueryValueExW(hkeyCred, NULL, 0, &type, (LPVOID)target_name, &count);
730     if (ret != ERROR_SUCCESS || type != REG_SZ)
731     {
732         HeapFree(GetProcessHeap(), 0, target_name);
733         return FALSE;
734     }
735
736     TRACE("comparing filter %s to target name %s\n", debugstr_w(filter),
737           debugstr_w(target_name));
738
739     p = strchrW(filter, '*');
740     ret = CompareStringW(GetThreadLocale(), 0, filter,
741                          (p && !p[1] ? p - filter : -1), target_name,
742                          (p && !p[1] ? p - filter : -1)) == CSTR_EQUAL;
743
744     HeapFree(GetProcessHeap(), 0, target_name);
745     return ret;
746 }
747
748 static DWORD registry_enumerate_credentials(HKEY hkeyMgr, LPCWSTR filter,
749                                             LPWSTR target_name,
750                                             DWORD target_name_len, BYTE key_data[KEY_SIZE],
751                                             PCREDENTIALW *credentials, char **buffer,
752                                             DWORD *len, DWORD *count)
753 {
754     DWORD i;
755     DWORD ret;
756     for (i = 0;; i++)
757     {
758         HKEY hkeyCred;
759         ret = RegEnumKeyW(hkeyMgr, i, target_name, target_name_len+1);
760         if (ret == ERROR_NO_MORE_ITEMS)
761         {
762             ret = ERROR_SUCCESS;
763             break;
764         }
765         else if (ret != ERROR_SUCCESS)
766         {
767             ret = ERROR_SUCCESS;
768             continue;
769         }
770         TRACE("target_name = %s\n", debugstr_w(target_name));
771         ret = RegOpenKeyExW(hkeyMgr, target_name, 0, KEY_QUERY_VALUE, &hkeyCred);
772         if (ret != ERROR_SUCCESS)
773         {
774             ret = ERROR_SUCCESS;
775             continue;
776         }
777         if (!credential_matches_filter(hkeyCred, filter))
778         {
779             RegCloseKey(hkeyCred);
780             continue;
781         }
782         if (buffer)
783         {
784             *len = sizeof(CREDENTIALW);
785             credentials[*count] = (PCREDENTIALW)*buffer;
786         }
787         else
788             *len += sizeof(CREDENTIALW);
789         ret = registry_read_credential(hkeyCred, buffer ? credentials[*count] : NULL,
790                                        key_data, buffer ? *buffer + sizeof(CREDENTIALW) : NULL,
791                                        len);
792         RegCloseKey(hkeyCred);
793         if (ret != ERROR_SUCCESS) break;
794         if (buffer) *buffer += *len;
795         (*count)++;
796     }
797     return ret;
798 }
799
800 #ifdef __APPLE__
801 static DWORD mac_enumerate_credentials(LPCWSTR filter, PCREDENTIALW *credentials,
802                                        char *buffer, DWORD *len, DWORD *count)
803 {
804     SecKeychainSearchRef search;
805     SecKeychainItemRef item;
806     OSStatus status;
807     Boolean saved_user_interaction_allowed;
808     DWORD ret;
809
810     SecKeychainGetUserInteractionAllowed(&saved_user_interaction_allowed);
811     SecKeychainSetUserInteractionAllowed(false);
812
813     status = SecKeychainSearchCreateFromAttributes(NULL, kSecInternetPasswordItemClass, NULL, &search);
814     if (status == noErr)
815     {
816         while (SecKeychainSearchCopyNext(search, &item) == noErr)
817         {
818             SecKeychainAttributeInfo info;
819             SecKeychainAttributeList *attr_list;
820             UInt32 info_tags[] = { kSecServerItemAttr };
821             info.count = sizeof(info_tags)/sizeof(info_tags[0]);
822             info.tag = info_tags;
823             info.format = NULL;
824             status = SecKeychainItemCopyAttributesAndData(item, &info, NULL, &attr_list, NULL, NULL);
825             if (status != noErr)
826             {
827                 WARN("SecKeychainItemCopyAttributesAndData returned status %ld\n", status);
828                 continue;
829             }
830             if (buffer)
831             {
832                 *len = sizeof(CREDENTIALW);
833                 credentials[*count] = (PCREDENTIALW)buffer;
834             }
835             else
836                 *len += sizeof(CREDENTIALW);
837             if (attr_list->count != 1 || attr_list->attr[0].tag != kSecServerItemAttr) continue;
838             TRACE("server item: %.*s\n", (int)attr_list->attr[0].length, (char *)attr_list->attr[0].data);
839             /* FIXME: filter based on attr_list->attr[0].data */
840             SecKeychainItemFreeAttributesAndData(attr_list, NULL);
841             ret = mac_read_credential_from_item(item, FALSE,
842                                                 buffer ? credentials[*count] : NULL,
843                                                 buffer ? buffer + sizeof(CREDENTIALW) : NULL,
844                                                 len);
845             CFRelease(item);
846             if (ret == ERROR_SUCCESS)
847             {
848                 (*count)++;
849                 if (buffer) buffer += *len;
850             }
851         }
852         CFRelease(search);
853     }
854     else
855         ERR("SecKeychainSearchCreateFromAttributes returned status %ld\n", status);
856     SecKeychainSetUserInteractionAllowed(saved_user_interaction_allowed);
857     return ERROR_SUCCESS;
858 }
859
860 static DWORD mac_delete_credential(LPCWSTR TargetName)
861 {
862     OSStatus status;
863     SecKeychainSearchRef search;
864     status = SecKeychainSearchCreateFromAttributes(NULL, kSecInternetPasswordItemClass, NULL, &search);
865     if (status == noErr)
866     {
867         SecKeychainItemRef item;
868         while (SecKeychainSearchCopyNext(search, &item) == noErr)
869         {
870             SecKeychainAttributeInfo info;
871             SecKeychainAttributeList *attr_list;
872             UInt32 info_tags[] = { kSecServerItemAttr };
873             LPWSTR target_name;
874             INT str_len;
875             info.count = sizeof(info_tags)/sizeof(info_tags[0]);
876             info.tag = info_tags;
877             info.format = NULL;
878             status = SecKeychainItemCopyAttributesAndData(item, &info, NULL, &attr_list, NULL, NULL);
879             if (status != noErr)
880             {
881                 WARN("SecKeychainItemCopyAttributesAndData returned status %ld\n", status);
882                 continue;
883             }
884             if (attr_list->count != 1 || attr_list->attr[0].tag != kSecServerItemAttr)
885             {
886                 CFRelease(item);
887                 continue;
888             }
889             str_len = MultiByteToWideChar(CP_UTF8, 0, attr_list->attr[0].data, attr_list->attr[0].length, NULL, 0);
890             target_name = HeapAlloc(GetProcessHeap(), 0, (str_len + 1) * sizeof(WCHAR));
891             MultiByteToWideChar(CP_UTF8, 0, attr_list->attr[0].data, attr_list->attr[0].length, target_name, str_len);
892             /* nul terminate */
893             target_name[str_len] = '\0';
894             if (strcmpiW(TargetName, target_name))
895             {
896                 CFRelease(item);
897                 HeapFree(GetProcessHeap(), 0, target_name);
898                 continue;
899             }
900             HeapFree(GetProcessHeap(), 0, target_name);
901             SecKeychainItemFreeAttributesAndData(attr_list, NULL);
902             SecKeychainItemDelete(item);
903             CFRelease(item);
904             CFRelease(search);
905
906             return ERROR_SUCCESS;
907         }
908         CFRelease(search);
909     }
910     return ERROR_NOT_FOUND;
911 }
912 #endif
913
914 static void convert_PCREDENTIALW_to_PCREDENTIALA(const CREDENTIALW *CredentialW, PCREDENTIALA CredentialA, DWORD *len)
915 {
916     char *buffer = (char *)CredentialA + sizeof(CREDENTIALA);
917     INT string_len;
918
919     *len += sizeof(CREDENTIALA);
920     if (!CredentialA)
921     {
922         if (CredentialW->TargetName) *len += WideCharToMultiByte(CP_ACP, 0, CredentialW->TargetName, -1, NULL, 0, NULL, NULL);
923         if (CredentialW->Comment) *len += WideCharToMultiByte(CP_ACP, 0, CredentialW->Comment, -1, NULL, 0, NULL, NULL);
924         *len += CredentialW->CredentialBlobSize;
925         if (CredentialW->TargetAlias) *len += WideCharToMultiByte(CP_ACP, 0, CredentialW->TargetAlias, -1, NULL, 0, NULL, NULL);
926         if (CredentialW->UserName) *len += WideCharToMultiByte(CP_ACP, 0, CredentialW->UserName, -1, NULL, 0, NULL, NULL);
927
928         return;
929     }
930
931     CredentialA->Flags = CredentialW->Flags;
932     CredentialA->Type = CredentialW->Type;
933     if (CredentialW->TargetName)
934     {
935         CredentialA->TargetName = buffer;
936         string_len = WideCharToMultiByte(CP_ACP, 0, CredentialW->TargetName, -1, CredentialA->TargetName, -1, NULL, NULL);
937         buffer += string_len;
938         *len += string_len;
939     }
940     else
941         CredentialA->TargetName = NULL;
942     if (CredentialW->Comment)
943     {
944         CredentialA->Comment = buffer;
945         string_len = WideCharToMultiByte(CP_ACP, 0, CredentialW->Comment, -1, CredentialA->Comment, -1, NULL, NULL);
946         buffer += string_len;
947         *len += string_len;
948     }
949     else
950         CredentialA->Comment = NULL;
951     CredentialA->LastWritten = CredentialW->LastWritten;
952     CredentialA->CredentialBlobSize = CredentialW->CredentialBlobSize;
953     if (CredentialW->CredentialBlobSize)
954     {
955         CredentialA->CredentialBlob =(LPBYTE)buffer;
956         memcpy(CredentialA->CredentialBlob, CredentialW->CredentialBlob,
957                CredentialW->CredentialBlobSize);
958         buffer += CredentialW->CredentialBlobSize;
959         *len += CredentialW->CredentialBlobSize;
960     }
961     else
962         CredentialA->CredentialBlob = NULL;
963     CredentialA->Persist = CredentialW->Persist;
964     CredentialA->AttributeCount = 0;
965     CredentialA->Attributes = NULL; /* FIXME */
966     if (CredentialW->TargetAlias)
967     {
968         CredentialA->TargetAlias = buffer;
969         string_len = WideCharToMultiByte(CP_ACP, 0, CredentialW->TargetAlias, -1, CredentialA->TargetAlias, -1, NULL, NULL);
970         buffer += string_len;
971         *len += string_len;
972     }
973     else
974         CredentialA->TargetAlias = NULL;
975     if (CredentialW->UserName)
976     {
977         CredentialA->UserName = buffer;
978         string_len = WideCharToMultiByte(CP_ACP, 0, CredentialW->UserName, -1, CredentialA->UserName, -1, NULL, NULL);
979         buffer += string_len;
980         *len += string_len;
981     }
982     else
983         CredentialA->UserName = NULL;
984 }
985
986 static void convert_PCREDENTIALA_to_PCREDENTIALW(const CREDENTIALA *CredentialA, PCREDENTIALW CredentialW, DWORD *len)
987 {
988     char *buffer = (char *)CredentialW + sizeof(CREDENTIALW);
989     INT string_len;
990
991     *len += sizeof(CREDENTIALW);
992     if (!CredentialW)
993     {
994         if (CredentialA->TargetName) *len += sizeof(WCHAR) * MultiByteToWideChar(CP_ACP, 0, CredentialA->TargetName, -1, NULL, 0);
995         if (CredentialA->Comment) *len += sizeof(WCHAR) * MultiByteToWideChar(CP_ACP, 0, CredentialA->Comment, -1, NULL, 0);
996         *len += CredentialA->CredentialBlobSize;
997         if (CredentialA->TargetAlias) *len += sizeof(WCHAR) * MultiByteToWideChar(CP_ACP, 0, CredentialA->TargetAlias, -1, NULL, 0);
998         if (CredentialA->UserName) *len += sizeof(WCHAR) * MultiByteToWideChar(CP_ACP, 0, CredentialA->UserName, -1, NULL, 0);
999
1000         return;
1001     }
1002
1003     CredentialW->Flags = CredentialA->Flags;
1004     CredentialW->Type = CredentialA->Type;
1005     if (CredentialA->TargetName)
1006     {
1007         CredentialW->TargetName = (LPWSTR)buffer;
1008         string_len = MultiByteToWideChar(CP_ACP, 0, CredentialA->TargetName, -1, CredentialW->TargetName, -1);
1009         buffer += sizeof(WCHAR) * string_len;
1010         *len += sizeof(WCHAR) * string_len;
1011     }
1012     else
1013         CredentialW->TargetName = NULL;
1014     if (CredentialA->Comment)
1015     {
1016         CredentialW->Comment = (LPWSTR)buffer;
1017         string_len = MultiByteToWideChar(CP_ACP, 0, CredentialA->Comment, -1, CredentialW->Comment, -1);
1018         buffer += sizeof(WCHAR) * string_len;
1019         *len += sizeof(WCHAR) * string_len;
1020     }
1021     else
1022         CredentialW->Comment = NULL;
1023     CredentialW->LastWritten = CredentialA->LastWritten;
1024     CredentialW->CredentialBlobSize = CredentialA->CredentialBlobSize;
1025     if (CredentialA->CredentialBlobSize)
1026     {
1027         CredentialW->CredentialBlob =(LPBYTE)buffer;
1028         memcpy(CredentialW->CredentialBlob, CredentialA->CredentialBlob,
1029                CredentialA->CredentialBlobSize);
1030         buffer += CredentialA->CredentialBlobSize;
1031         *len += CredentialA->CredentialBlobSize;
1032     }
1033     else
1034         CredentialW->CredentialBlob = NULL;
1035     CredentialW->Persist = CredentialA->Persist;
1036     CredentialW->AttributeCount = 0;
1037     CredentialW->Attributes = NULL; /* FIXME */
1038     if (CredentialA->TargetAlias)
1039     {
1040         CredentialW->TargetAlias = (LPWSTR)buffer;
1041         string_len = MultiByteToWideChar(CP_ACP, 0, CredentialA->TargetAlias, -1, CredentialW->TargetAlias, -1);
1042         buffer += sizeof(WCHAR) * string_len;
1043         *len += sizeof(WCHAR) * string_len;
1044     }
1045     else
1046         CredentialW->TargetAlias = NULL;
1047     if (CredentialA->UserName)
1048     {
1049         CredentialW->UserName = (LPWSTR)buffer;
1050         string_len = MultiByteToWideChar(CP_ACP, 0, CredentialA->UserName, -1, CredentialW->UserName, -1);
1051         buffer += sizeof(WCHAR) * string_len;
1052         *len += sizeof(WCHAR) * string_len;
1053     }
1054     else
1055         CredentialW->UserName = NULL;
1056 }
1057
1058 /******************************************************************************
1059  * CredDeleteA [ADVAPI32.@]
1060  */
1061 BOOL WINAPI CredDeleteA(LPCSTR TargetName, DWORD Type, DWORD Flags)
1062 {
1063     LPWSTR TargetNameW;
1064     DWORD len;
1065     BOOL ret;
1066
1067     TRACE("(%s, %d, 0x%x)\n", debugstr_a(TargetName), Type, Flags);
1068
1069     if (!TargetName)
1070     {
1071         SetLastError(ERROR_INVALID_PARAMETER);
1072         return FALSE;
1073     }
1074
1075     len = MultiByteToWideChar(CP_ACP, 0, TargetName, -1, NULL, 0);
1076     TargetNameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1077     if (!TargetNameW)
1078     {
1079         SetLastError(ERROR_OUTOFMEMORY);
1080         return FALSE;
1081     }
1082     MultiByteToWideChar(CP_ACP, 0, TargetName, -1, TargetNameW, len);
1083
1084     ret = CredDeleteW(TargetNameW, Type, Flags);
1085
1086     HeapFree(GetProcessHeap(), 0, TargetNameW);
1087
1088     return ret;
1089 }
1090
1091 /******************************************************************************
1092  * CredDeleteW [ADVAPI32.@]
1093  */
1094 BOOL WINAPI CredDeleteW(LPCWSTR TargetName, DWORD Type, DWORD Flags)
1095 {
1096     HKEY hkeyMgr;
1097     DWORD ret;
1098     LPWSTR key_name;
1099
1100     TRACE("(%s, %d, 0x%x)\n", debugstr_w(TargetName), Type, Flags);
1101
1102     if (!TargetName)
1103     {
1104         SetLastError(ERROR_INVALID_PARAMETER);
1105         return FALSE;
1106     }
1107
1108     if (Type != CRED_TYPE_GENERIC && Type != CRED_TYPE_DOMAIN_PASSWORD)
1109     {
1110         FIXME("unhandled type %d\n", Type);
1111         SetLastError(ERROR_INVALID_PARAMETER);
1112         return FALSE;
1113     }
1114
1115     if (Flags)
1116     {
1117         FIXME("unhandled flags 0x%x\n", Flags);
1118         SetLastError(ERROR_INVALID_FLAGS);
1119         return FALSE;
1120     }
1121
1122 #ifdef __APPLE__
1123     if (Type == CRED_TYPE_DOMAIN_PASSWORD)
1124     {
1125         ret = mac_delete_credential(TargetName);
1126         if (ret == ERROR_SUCCESS)
1127             return TRUE;
1128     }
1129 #endif
1130
1131     ret = open_cred_mgr_key(&hkeyMgr, TRUE);
1132     if (ret != ERROR_SUCCESS)
1133     {
1134         WARN("couldn't open/create manager key, error %d\n", ret);
1135         SetLastError(ERROR_NO_SUCH_LOGON_SESSION);
1136         return FALSE;
1137     }
1138
1139     key_name = get_key_name_for_target(TargetName, Type);
1140     ret = RegDeleteKeyW(hkeyMgr, key_name);
1141     HeapFree(GetProcessHeap(), 0, key_name);
1142     RegCloseKey(hkeyMgr);
1143     if (ret != ERROR_SUCCESS)
1144     {
1145         SetLastError(ERROR_NOT_FOUND);
1146         return FALSE;
1147     }
1148
1149     return TRUE;
1150 }
1151
1152 /******************************************************************************
1153  * CredEnumerateA [ADVAPI32.@]
1154  */
1155 BOOL WINAPI CredEnumerateA(LPCSTR Filter, DWORD Flags, DWORD *Count,
1156                            PCREDENTIALA **Credentials)
1157 {
1158     LPWSTR FilterW;
1159     PCREDENTIALW *CredentialsW;
1160     DWORD i;
1161     DWORD len;
1162     char *buffer;
1163
1164     TRACE("(%s, 0x%x, %p, %p)\n", debugstr_a(Filter), Flags, Count, Credentials);
1165
1166     if (Filter)
1167     {
1168         len = MultiByteToWideChar(CP_ACP, 0, Filter, -1, NULL, 0);
1169         FilterW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1170         if (!FilterW)
1171         {
1172             SetLastError(ERROR_OUTOFMEMORY);
1173             return FALSE;
1174         }
1175         MultiByteToWideChar(CP_ACP, 0, Filter, -1, FilterW, len);
1176     }
1177     else
1178         FilterW = NULL;
1179
1180     if (!CredEnumerateW(FilterW, Flags, Count, &CredentialsW))
1181     {
1182         HeapFree(GetProcessHeap(), 0, FilterW);
1183         return FALSE;
1184     }
1185     HeapFree(GetProcessHeap(), 0, FilterW);
1186
1187     len = *Count * sizeof(PCREDENTIALA);
1188     for (i = 0; i < *Count; i++)
1189         convert_PCREDENTIALW_to_PCREDENTIALA(CredentialsW[i], NULL, &len);
1190
1191     *Credentials = HeapAlloc(GetProcessHeap(), 0, len);
1192     if (!*Credentials)
1193     {
1194         CredFree(CredentialsW);
1195         SetLastError(ERROR_OUTOFMEMORY);
1196         return FALSE;
1197     }
1198
1199     buffer = (char *)&(*Credentials)[*Count];
1200     for (i = 0; i < *Count; i++)
1201     {
1202         len = 0;
1203         (*Credentials)[i] = (PCREDENTIALA)buffer;
1204         convert_PCREDENTIALW_to_PCREDENTIALA(CredentialsW[i], (*Credentials)[i], &len);
1205         buffer += len;
1206     }
1207
1208     CredFree(CredentialsW);
1209
1210     return TRUE;
1211 }
1212
1213 /******************************************************************************
1214  * CredEnumerateW [ADVAPI32.@]
1215  */
1216 BOOL WINAPI CredEnumerateW(LPCWSTR Filter, DWORD Flags, DWORD *Count,
1217                            PCREDENTIALW **Credentials)
1218 {
1219     HKEY hkeyMgr;
1220     DWORD ret;
1221     LPWSTR target_name;
1222     DWORD target_name_len;
1223     DWORD len;
1224     char *buffer;
1225     BYTE key_data[KEY_SIZE];
1226
1227     TRACE("(%s, 0x%x, %p, %p)\n", debugstr_w(Filter), Flags, Count, Credentials);
1228
1229     if (Flags)
1230     {
1231         SetLastError(ERROR_INVALID_FLAGS);
1232         return FALSE;
1233     }
1234
1235     ret = open_cred_mgr_key(&hkeyMgr, FALSE);
1236     if (ret != ERROR_SUCCESS)
1237     {
1238         WARN("couldn't open/create manager key, error %d\n", ret);
1239         SetLastError(ERROR_NO_SUCH_LOGON_SESSION);
1240         return FALSE;
1241     }
1242
1243     ret = get_cred_mgr_encryption_key(hkeyMgr, key_data);
1244     if (ret != ERROR_SUCCESS)
1245     {
1246         RegCloseKey(hkeyMgr);
1247         SetLastError(ret);
1248         return FALSE;
1249     }
1250
1251     ret = RegQueryInfoKeyW(hkeyMgr, NULL, NULL, NULL, NULL, &target_name_len, NULL, NULL, NULL, NULL, NULL, NULL);
1252     if (ret != ERROR_SUCCESS)
1253     {
1254         RegCloseKey(hkeyMgr);
1255         SetLastError(ret);
1256         return FALSE;
1257     }
1258
1259     target_name = HeapAlloc(GetProcessHeap(), 0, (target_name_len+1)*sizeof(WCHAR));
1260     if (!target_name)
1261     {
1262         RegCloseKey(hkeyMgr);
1263         SetLastError(ERROR_OUTOFMEMORY);
1264         return FALSE;
1265     }
1266
1267     *Count = 0;
1268     len = 0;
1269     ret = registry_enumerate_credentials(hkeyMgr, Filter, target_name, target_name_len,
1270                                          key_data, NULL, NULL, &len, Count);
1271 #ifdef __APPLE__
1272     if (ret == ERROR_SUCCESS)
1273         ret = mac_enumerate_credentials(Filter, NULL, NULL, &len, Count);
1274 #endif
1275     if (ret == ERROR_SUCCESS && *Count == 0)
1276         ret = ERROR_NOT_FOUND;
1277     if (ret != ERROR_SUCCESS)
1278     {
1279         HeapFree(GetProcessHeap(), 0, target_name);
1280         RegCloseKey(hkeyMgr);
1281         SetLastError(ret);
1282         return FALSE;
1283     }
1284     len += *Count * sizeof(PCREDENTIALW);
1285
1286     if (ret == ERROR_SUCCESS)
1287     {
1288         buffer = HeapAlloc(GetProcessHeap(), 0, len);
1289         *Credentials = (PCREDENTIALW *)buffer;
1290         if (buffer)
1291         {
1292             buffer += *Count * sizeof(PCREDENTIALW);
1293             *Count = 0;
1294             ret = registry_enumerate_credentials(hkeyMgr, Filter, target_name,
1295                                                  target_name_len, key_data,
1296                                                  *Credentials, &buffer, &len,
1297                                                  Count);
1298 #ifdef __APPLE__
1299             if (ret == ERROR_SUCCESS)
1300                 ret = mac_enumerate_credentials(Filter, *Credentials,
1301                                                 buffer, &len, Count);
1302 #endif
1303         }
1304         else
1305             ret = ERROR_OUTOFMEMORY;
1306     }
1307
1308     HeapFree(GetProcessHeap(), 0, target_name);
1309     RegCloseKey(hkeyMgr);
1310
1311     if (ret != ERROR_SUCCESS)
1312     {
1313         SetLastError(ret);
1314         return FALSE;
1315     }
1316     return TRUE;
1317 }
1318
1319 /******************************************************************************
1320  * CredFree [ADVAPI32.@]
1321  */
1322 VOID WINAPI CredFree(PVOID Buffer)
1323 {
1324     HeapFree(GetProcessHeap(), 0, Buffer);
1325 }
1326
1327 /******************************************************************************
1328  * CredReadA [ADVAPI32.@]
1329  */
1330 BOOL WINAPI CredReadA(LPCSTR TargetName, DWORD Type, DWORD Flags, PCREDENTIALA *Credential)
1331 {
1332     LPWSTR TargetNameW;
1333     PCREDENTIALW CredentialW;
1334     DWORD len;
1335
1336     TRACE("(%s, %d, 0x%x, %p)\n", debugstr_a(TargetName), Type, Flags, Credential);
1337
1338     if (!TargetName)
1339     {
1340         SetLastError(ERROR_INVALID_PARAMETER);
1341         return FALSE;
1342     }
1343
1344     len = MultiByteToWideChar(CP_ACP, 0, TargetName, -1, NULL, 0);
1345     TargetNameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1346     if (!TargetNameW)
1347     {
1348         SetLastError(ERROR_OUTOFMEMORY);
1349         return FALSE;
1350     }
1351     MultiByteToWideChar(CP_ACP, 0, TargetName, -1, TargetNameW, len);
1352
1353     if (!CredReadW(TargetNameW, Type, Flags, &CredentialW))
1354     {
1355         HeapFree(GetProcessHeap(), 0, TargetNameW);
1356         return FALSE;
1357     }
1358     HeapFree(GetProcessHeap(), 0, TargetNameW);
1359
1360     len = 0;
1361     convert_PCREDENTIALW_to_PCREDENTIALA(CredentialW, NULL, &len);
1362     *Credential = HeapAlloc(GetProcessHeap(), 0, len);
1363     if (!*Credential)
1364     {
1365         SetLastError(ERROR_OUTOFMEMORY);
1366         return FALSE;
1367     }
1368     len = 0;
1369     convert_PCREDENTIALW_to_PCREDENTIALA(CredentialW, *Credential, &len);
1370
1371     CredFree(CredentialW);
1372
1373     return TRUE;
1374 }
1375
1376 /******************************************************************************
1377  * CredReadW [ADVAPI32.@]
1378  */
1379 BOOL WINAPI CredReadW(LPCWSTR TargetName, DWORD Type, DWORD Flags, PCREDENTIALW *Credential)
1380 {
1381     HKEY hkeyMgr;
1382     HKEY hkeyCred;
1383     DWORD ret;
1384     LPWSTR key_name;
1385     DWORD len;
1386     BYTE key_data[KEY_SIZE];
1387
1388     TRACE("(%s, %d, 0x%x, %p)\n", debugstr_w(TargetName), Type, Flags, Credential);
1389
1390     if (!TargetName)
1391     {
1392         SetLastError(ERROR_INVALID_PARAMETER);
1393         return FALSE;
1394     }
1395
1396     if (Type != CRED_TYPE_GENERIC && Type != CRED_TYPE_DOMAIN_PASSWORD)
1397     {
1398         FIXME("unhandled type %d\n", Type);
1399         SetLastError(ERROR_INVALID_PARAMETER);
1400         return FALSE;
1401     }
1402
1403     if (Flags)
1404     {
1405         FIXME("unhandled flags 0x%x\n", Flags);
1406         SetLastError(ERROR_INVALID_FLAGS);
1407         return FALSE;
1408     }
1409
1410 #ifdef __APPLE__
1411     if (Type == CRED_TYPE_DOMAIN_PASSWORD)
1412     {
1413         OSStatus status;
1414         SecKeychainSearchRef search;
1415         status = SecKeychainSearchCreateFromAttributes(NULL, kSecInternetPasswordItemClass, NULL, &search);
1416         if (status == noErr)
1417         {
1418             SecKeychainItemRef item;
1419             while (SecKeychainSearchCopyNext(search, &item) == noErr)
1420             {
1421                 SecKeychainAttributeInfo info;
1422                 SecKeychainAttributeList *attr_list;
1423                 UInt32 info_tags[] = { kSecServerItemAttr };
1424                 LPWSTR target_name;
1425                 INT str_len;
1426                 info.count = sizeof(info_tags)/sizeof(info_tags[0]);
1427                 info.tag = info_tags;
1428                 info.format = NULL;
1429                 status = SecKeychainItemCopyAttributesAndData(item, &info, NULL, &attr_list, NULL, NULL);
1430                 len = sizeof(**Credential);
1431                 if (status != noErr)
1432                 {
1433                     WARN("SecKeychainItemCopyAttributesAndData returned status %ld\n", status);
1434                     continue;
1435                 }
1436                 if (attr_list->count != 1 || attr_list->attr[0].tag != kSecServerItemAttr)
1437                 {
1438                     CFRelease(item);
1439                     continue;
1440                 }
1441                 str_len = MultiByteToWideChar(CP_UTF8, 0, attr_list->attr[0].data, attr_list->attr[0].length, NULL, 0);
1442                 target_name = HeapAlloc(GetProcessHeap(), 0, (str_len + 1) * sizeof(WCHAR));
1443                 MultiByteToWideChar(CP_UTF8, 0, attr_list->attr[0].data, attr_list->attr[0].length, target_name, str_len);
1444                 /* nul terminate */
1445                 target_name[str_len] = '\0';
1446                 if (strcmpiW(TargetName, target_name))
1447                 {
1448                     CFRelease(item);
1449                     HeapFree(GetProcessHeap(), 0, target_name);
1450                     continue;
1451                 }
1452                 HeapFree(GetProcessHeap(), 0, target_name);
1453                 SecKeychainItemFreeAttributesAndData(attr_list, NULL);
1454                 ret = mac_read_credential_from_item(item, TRUE, NULL, NULL, &len);
1455                 if (ret == ERROR_SUCCESS)
1456                 {
1457                     *Credential = HeapAlloc(GetProcessHeap(), 0, len);
1458                     if (*Credential)
1459                     {
1460                         len = sizeof(**Credential);
1461                         ret = mac_read_credential_from_item(item, TRUE, *Credential,
1462                                                             (char *)(*Credential + 1), &len);
1463                     }
1464                     else
1465                         ret = ERROR_OUTOFMEMORY;
1466                     CFRelease(item);
1467                     CFRelease(search);
1468                     if (ret != ERROR_SUCCESS)
1469                     {
1470                         SetLastError(ret);
1471                         return FALSE;
1472                     }
1473                     return TRUE;
1474                 }
1475                 CFRelease(item);
1476             }
1477             CFRelease(search);
1478         }
1479     }
1480 #endif
1481
1482     ret = open_cred_mgr_key(&hkeyMgr, FALSE);
1483     if (ret != ERROR_SUCCESS)
1484     {
1485         WARN("couldn't open/create manager key, error %d\n", ret);
1486         SetLastError(ERROR_NO_SUCH_LOGON_SESSION);
1487         return FALSE;
1488     }
1489
1490     ret = get_cred_mgr_encryption_key(hkeyMgr, key_data);
1491     if (ret != ERROR_SUCCESS)
1492     {
1493         RegCloseKey(hkeyMgr);
1494         SetLastError(ret);
1495         return FALSE;
1496     }
1497
1498     key_name = get_key_name_for_target(TargetName, Type);
1499     ret = RegOpenKeyExW(hkeyMgr, key_name, 0, KEY_QUERY_VALUE, &hkeyCred);
1500     HeapFree(GetProcessHeap(), 0, key_name);
1501     if (ret != ERROR_SUCCESS)
1502     {
1503         TRACE("credentials for target name %s not found\n", debugstr_w(TargetName));
1504         SetLastError(ERROR_NOT_FOUND);
1505         return FALSE;
1506     }
1507
1508     len = sizeof(**Credential);
1509     ret = registry_read_credential(hkeyCred, NULL, key_data, NULL, &len);
1510     if (ret == ERROR_SUCCESS)
1511     {
1512         *Credential = HeapAlloc(GetProcessHeap(), 0, len);
1513         if (*Credential)
1514         {
1515             len = sizeof(**Credential);
1516             ret = registry_read_credential(hkeyCred, *Credential, key_data,
1517                                            (char *)(*Credential + 1), &len);
1518         }
1519         else
1520             ret = ERROR_OUTOFMEMORY;
1521     }
1522
1523     RegCloseKey(hkeyCred);
1524     RegCloseKey(hkeyMgr);
1525
1526     if (ret != ERROR_SUCCESS)
1527     {
1528         SetLastError(ret);
1529         return FALSE;
1530     }
1531     return TRUE;
1532 }
1533
1534 /******************************************************************************
1535  * CredWriteA [ADVAPI32.@]
1536  */
1537 BOOL WINAPI CredWriteA(PCREDENTIALA Credential, DWORD Flags)
1538 {
1539     BOOL ret;
1540     DWORD len;
1541     PCREDENTIALW CredentialW;
1542
1543     TRACE("(%p, 0x%x)\n", Credential, Flags);
1544
1545     if (!Credential || !Credential->TargetName)
1546     {
1547         SetLastError(ERROR_INVALID_PARAMETER);
1548         return FALSE;
1549     }
1550
1551     len = 0;
1552     convert_PCREDENTIALA_to_PCREDENTIALW(Credential, NULL, &len);
1553     CredentialW = HeapAlloc(GetProcessHeap(), 0, len);
1554     if (!CredentialW)
1555     {
1556         SetLastError(ERROR_OUTOFMEMORY);
1557         return FALSE;
1558     }
1559     len = 0;
1560     convert_PCREDENTIALA_to_PCREDENTIALW(Credential, CredentialW, &len);
1561
1562     ret = CredWriteW(CredentialW, Flags);
1563
1564     HeapFree(GetProcessHeap(), 0, CredentialW);
1565
1566     return ret;
1567 }
1568
1569 /******************************************************************************
1570  * CredWriteW [ADVAPI32.@]
1571  */
1572 BOOL WINAPI CredWriteW(PCREDENTIALW Credential, DWORD Flags)
1573 {
1574     HKEY hkeyMgr;
1575     HKEY hkeyCred;
1576     DWORD ret;
1577     LPWSTR key_name;
1578     BYTE key_data[KEY_SIZE];
1579
1580     TRACE("(%p, 0x%x)\n", Credential, Flags);
1581
1582     if (!Credential || !Credential->TargetName)
1583     {
1584         SetLastError(ERROR_INVALID_PARAMETER);
1585         return FALSE;
1586     }
1587
1588     if (Flags & ~CRED_PRESERVE_CREDENTIAL_BLOB)
1589     {
1590         FIXME("unhandled flags 0x%x\n", Flags);
1591         SetLastError(ERROR_INVALID_FLAGS);
1592         return FALSE;
1593     }
1594
1595     if (Credential->Type != CRED_TYPE_GENERIC && Credential->Type != CRED_TYPE_DOMAIN_PASSWORD)
1596     {
1597         FIXME("unhandled type %d\n", Credential->Type);
1598         SetLastError(ERROR_INVALID_PARAMETER);
1599         return FALSE;
1600     }
1601
1602     TRACE("Credential->TargetName = %s\n", debugstr_w(Credential->TargetName));
1603     TRACE("Credential->UserName = %s\n", debugstr_w(Credential->UserName));
1604
1605     if (Credential->Type == CRED_TYPE_DOMAIN_PASSWORD)
1606     {
1607         if (!Credential->UserName ||
1608             (!strchrW(Credential->UserName, '\\') && !strchrW(Credential->UserName, '@')))
1609         {
1610             ERR("bad username %s\n", debugstr_w(Credential->UserName));
1611             SetLastError(ERROR_BAD_USERNAME);
1612             return FALSE;
1613         }
1614     }
1615
1616 #ifdef __APPLE__
1617     if (!Credential->AttributeCount &&
1618         Credential->Type == CRED_TYPE_DOMAIN_PASSWORD &&
1619         (Credential->Persist == CRED_PERSIST_LOCAL_MACHINE || Credential->Persist == CRED_PERSIST_ENTERPRISE))
1620     {
1621         ret = mac_write_credential(Credential, Flags & CRED_PRESERVE_CREDENTIAL_BLOB);
1622         if (ret != ERROR_SUCCESS)
1623         {
1624             SetLastError(ret);
1625             return FALSE;
1626         }
1627         return TRUE;
1628     }
1629 #endif
1630
1631     ret = open_cred_mgr_key(&hkeyMgr, FALSE);
1632     if (ret != ERROR_SUCCESS)
1633     {
1634         WARN("couldn't open/create manager key, error %d\n", ret);
1635         SetLastError(ERROR_NO_SUCH_LOGON_SESSION);
1636         return FALSE;
1637     }
1638
1639     ret = get_cred_mgr_encryption_key(hkeyMgr, key_data);
1640     if (ret != ERROR_SUCCESS)
1641     {
1642         RegCloseKey(hkeyMgr);
1643         SetLastError(ret);
1644         return FALSE;
1645     }
1646
1647     key_name = get_key_name_for_target(Credential->TargetName, Credential->Type);
1648     ret = RegCreateKeyExW(hkeyMgr, key_name, 0, NULL,
1649                           Credential->Persist == CRED_PERSIST_SESSION ? REG_OPTION_VOLATILE : REG_OPTION_NON_VOLATILE,
1650                           KEY_READ|KEY_WRITE, NULL, &hkeyCred, NULL);
1651     HeapFree(GetProcessHeap(), 0, key_name);
1652     if (ret != ERROR_SUCCESS)
1653     {
1654         TRACE("credentials for target name %s not found\n",
1655               debugstr_w(Credential->TargetName));
1656         SetLastError(ERROR_NOT_FOUND);
1657         return FALSE;
1658     }
1659
1660     ret = registry_write_credential(hkeyCred, Credential, key_data,
1661                                     Flags & CRED_PRESERVE_CREDENTIAL_BLOB);
1662
1663     RegCloseKey(hkeyCred);
1664     RegCloseKey(hkeyMgr);
1665
1666     if (ret != ERROR_SUCCESS)
1667     {
1668         SetLastError(ret);
1669         return FALSE;
1670     }
1671     return TRUE;
1672 }
1673
1674 /******************************************************************************
1675  * CredGetSessionTypes [ADVAPI32.@]
1676  */
1677 WINADVAPI BOOL WINAPI CredGetSessionTypes(DWORD persistCount, LPDWORD persists)
1678 {
1679     TRACE("(%u, %p)\n", persistCount, persists);
1680
1681     memset(persists, CRED_PERSIST_NONE, persistCount*sizeof(*persists));
1682     if (CRED_TYPE_GENERIC < persistCount)
1683     {
1684         persists[CRED_TYPE_GENERIC] = CRED_PERSIST_ENTERPRISE;
1685
1686         if (CRED_TYPE_DOMAIN_PASSWORD < persistCount)
1687         {
1688             persists[CRED_TYPE_DOMAIN_PASSWORD] = CRED_PERSIST_ENTERPRISE;
1689         }
1690     }
1691     return TRUE;
1692 }