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