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