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