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