~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Wine Cross Reference
wine/dlls/advapi32/cred.c

Version: ~ [ wine-1.1.0 ] ~ [ wine-1.0 ] ~ [ wine-1.0-rc5 ] ~ [ wine-1.0-rc4 ] ~ [ wine-1.0-rc3 ] ~ [ wine-1.0-rc2 ] ~ [ wine-1.0-rc1 ] ~ [ wine-0.9.61 ] ~ [ wine-0.9.60 ] ~ [ wine-0.9.59 ] ~ [ wine-0.9.58 ] ~ [ wine-0.9.57 ] ~ [ wine-0.9.56 ] ~ [ wine-0.9.55 ] ~ [ wine-0.9.54 ] ~ [ wine-0.9.53 ] ~ [ wine-0.9.52 ] ~ [ wine-0.9.51 ] ~ [ wine-0.9.50 ] ~ [ wine-0.9.49 ] ~ [ wine-0.9.48 ] ~ [ wine-0.9.47 ] ~ [ wine-0.9.46 ] ~ [ wine-0.9.45 ] ~ [ wine-0.9.44 ] ~ [ wine-0.9.43 ] ~ [ wine-0.9.42 ] ~ [ wine-0.9.41 ] ~ [ wine-0.9.40 ] ~ [ wine-0.9.39 ] ~ [ wine-0.9.38 ] ~ [ wine-0.9.37 ] ~ [ wine-0.9.36 ] ~ [ wine-0.9.35 ] ~ [ wine-0.9.34 ] ~ [ wine-0.9.33 ] ~ [ wine-0.9.32 ] ~ [ wine-0.9.31 ] ~ [ wine-0.9.30 ] ~ [ wine-0.9.29 ] ~ [ wine-0.9.28 ] ~ [ wine-0.9.27 ] ~ [ wine-0.9.26 ] ~ [ wine-0.9.25 ] ~ [ wine-0.9.24 ] ~ [ wine-0.9.23 ] ~ [ wine-0.9.22 ] ~ [ wine-0.9.21 ] ~ [ wine-0.9.20 ] ~ [ wine-0.9.19 ] ~ [ wine-0.9.18 ] ~ [ wine-0.9.17 ] ~ [ wine-0.9.16 ] ~ [ wine-0.9.15 ] ~ [ wine-0.9.14 ] ~ [ wine-0.9.13 ] ~ [ wine-0.9.12 ] ~ [ wine-0.9.11 ] ~ [ wine-0.9.10 ] ~ [ wine-0.9.9 ] ~ [ wine-0.9.8 ] ~ [ wine-0.9.7 ] ~ [ wine-0.9.6 ] ~ [ wine-0.9.5 ] ~ [ wine-0.9.4 ] ~ [ wine-0.9.3 ] ~ [ wine-0.9.2 ] ~ [ wine-0.9.1 ] ~ [ wine-0.9 ] ~ [ wine20050930 ] ~ [ wine20050830 ] ~ [ wine20050725 ] ~ [ wine20050628 ] ~ [ wine20050524 ] ~ [ wine20050419 ] ~ [ wine20050310 ] ~ [ wine20050211 ] ~ [ wine20050111 ] ~ [ wine20041201 ] ~ [ wine20041019 ] ~ [ wine20040914 ] ~ [ wine20040813 ] ~ [ wine20040716 ] ~ [ wine20040615 ] ~ [ wine20040505 ] ~ [ wine20040408 ] ~ [ wine20040309 ] ~ [ wine20040213 ] ~ [ wine20040121 ] ~ [ wine20031212 ] ~ [ wine20031118 ] ~ [ wine20031016 ] ~ [ wine20030911 ] ~ [ wine20030813 ] ~ [ wine20030709 ] ~ [ wine20030618 ] ~ [ wine20030508 ] ~ [ wine20030408 ] ~ [ wine20030318 ] ~ [ wine20030219 ] ~ [ wine20030115 ] ~ [ wine20021219 ] ~ [ wine20021125 ] ~ [ wine20021031 ] ~ [ wine20021007 ] ~ [ wine20020904 ] ~ [ wine20020804 ] ~ [ wine20020710 ] ~ [ wine20020605 ] ~ [ wine20020509 ] ~ [ wine20020411 ] ~ [ wine20020310 ] ~ [ wine20020228 ] ~ [ wine20011226 ] ~ [ wine20011108 ] ~ [ wine20011004 ] ~ [ wine20010824 ] ~ [ wine20010731 ] ~ [ wine20010629 ] ~ [ wine20010510 ] ~ [ wine20010418 ] ~ [ wine20010326 ] ~ [ wine20010305 ] ~ [ wine20010216 ] ~ [ wine20010112 ] ~ [ wine20001222 ] ~ [ wine20001202 ] ~ [ wine20001026 ] ~ [ wine20001002 ] ~ [ wine20000909 ] ~ [ wine20000821 ] ~ [ wine20000801 ] ~ [ wine20000716 ] ~ [ wine20000326 ] ~ [ wine20000227 ] ~ [ wine20000130 ] ~ [ wine20000109 ] ~

  1 /*
  2  * Credential Management APIs
  3  *
  4  * Copyright 2007 Robert Shearman for CodeWeavers
  5  *
  6  * This library is free software; you can redistribute it and/or
  7  * modify it under the terms of the GNU Lesser General Public
  8  * License as published by the Free Software Foundation; either
  9  * version 2.1 of the License, or (at your option) any later version.
 10  *
 11  * This library is distributed in the hope that it will be useful,
 12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 14  * Lesser General Public License for more details.
 15  *
 16  * You should have received a copy of the GNU Lesser General Public
 17  * License along with this library; if not, write to the Free Software
 18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 19  */
 20 
 21 #include <stdarg.h>
 22 #include <time.h>
 23 
 24 #include "windef.h"
 25 #include "winbase.h"
 26 #include "winreg.h"
 27 #include "wincred.h"
 28 #include "winternl.h"
 29 
 30 #ifdef __APPLE__
 31 # include <Security/SecKeychain.h>
 32 # include <Security/SecKeychainItem.h>
 33 # include <Security/SecKeychainSearch.h>
 34 #endif
 35 
 36 #include "crypt.h"
 37 
 38 #include "wine/unicode.h"
 39 #include "wine/debug.h"
 40 
 41 WINE_DEFAULT_DEBUG_CHANNEL(cred);
 42 
 43 /* the size of the ARC4 key used to encrypt the password data */
 44 #define KEY_SIZE 8
 45 
 46 static const WCHAR wszCredentialManagerKey[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\',
 47     'C','r','e','d','e','n','t','i','a','l',' ','M','a','n','a','g','e','r',0};
 48 static const WCHAR wszEncryptionKeyValue[] = {'E','n','c','r','y','p','t','i','o','n','K','e','y',0};
 49 
 50 static const WCHAR wszFlagsValue[] = {'F','l','a','g','s',0};
 51 static const WCHAR wszTypeValue[] = {'T','y','p','e',0};
 52 static const WCHAR wszTargetNameValue[] = {'T','a','r','g','e','t','N','a','m','e',0};
 53 static const WCHAR wszCommentValue[] = {'C','o','m','m','e','n','t',0};
 54 static const WCHAR wszLastWrittenValue[] = {'L','a','s','t','W','r','i','t','t','e','n',0};
 55 static const WCHAR wszPersistValue[] = {'P','e','r','s','i','s','t',0};
 56 static const WCHAR wszTargetAliasValue[] = {'T','a','r','g','e','t','A','l','i','a','s',0};
 57 static const WCHAR wszUserNameValue[] = {'U','s','e','r','N','a','m','e',0};
 58 static const WCHAR wszPasswordValue[] = {'P','a','s','s','w','o','r','d',0};
 59 
 60 static DWORD read_credential_blob(HKEY hkey, const BYTE key_data[KEY_SIZE],
 61                                   LPBYTE credential_blob,
 62                                   DWORD *credential_blob_size)
 63 {
 64     DWORD ret;
 65     DWORD type;
 66 
 67     *credential_blob_size = 0;
 68     ret = RegQueryValueExW(hkey, wszPasswordValue, 0, &type, NULL, credential_blob_size);
 69     if (ret != ERROR_SUCCESS)
 70         return ret;
 71     else if (type != REG_BINARY)
 72         return ERROR_REGISTRY_CORRUPT;
 73     if (credential_blob)
 74     {
 75         struct ustring data;
 76         struct ustring key;
 77 
 78         ret = RegQueryValueExW(hkey, wszPasswordValue, 0, &type, (LPVOID)credential_blob,
 79                                credential_blob_size);
 80         if (ret != ERROR_SUCCESS)
 81             return ret;
 82         else if (type != REG_BINARY)
 83             return ERROR_REGISTRY_CORRUPT;
 84 
 85         key.Length = key.MaximumLength = KEY_SIZE;
 86         key.Buffer = (unsigned char *)key_data;
 87 
 88         data.Length = data.MaximumLength = *credential_blob_size;
 89         data.Buffer = credential_blob;
 90         SystemFunction032(&data, &key);
 91     }
 92     return ERROR_SUCCESS;
 93 }
 94 
 95 static DWORD registry_read_credential(HKEY hkey, PCREDENTIALW credential,
 96                                       const BYTE key_data[KEY_SIZE],
 97                                       char *buffer, DWORD *len)
 98 {
 99     DWORD type;
100     DWORD ret;
101     DWORD count;
102 
103     ret = RegQueryValueExW(hkey, NULL, 0, &type, NULL, &count);
104     if (ret != ERROR_SUCCESS)
105         return ret;
106     else if (type != REG_SZ)
107         return ERROR_REGISTRY_CORRUPT;
108     *len += count;
109     if (credential)
110     {
111         credential->TargetName = (LPWSTR)buffer;
112         ret = RegQueryValueExW(hkey, NULL, 0, &type, (LPVOID)credential->TargetName,
113                                &count);
114         if (ret != ERROR_SUCCESS || type != REG_SZ) return ret;
115         buffer += count;
116     }
117 
118     ret = RegQueryValueExW(hkey, wszCommentValue, 0, &type, NULL, &count);
119     if (ret != ERROR_FILE_NOT_FOUND && ret != ERROR_SUCCESS)
120         return ret;
121     else if (type != REG_SZ)
122         return ERROR_REGISTRY_CORRUPT;
123     *len += count;
124     if (credential)
125     {
126         credential->Comment = (LPWSTR)buffer;
127         ret = RegQueryValueExW(hkey, wszCommentValue, 0, &type, (LPVOID)credential->Comment,
128                                &count);
129         if (ret == ERROR_FILE_NOT_FOUND)
130             credential->Comment = NULL;
131         else if (ret != ERROR_SUCCESS)
132             return ret;
133         else if (type != REG_SZ)
134             return ERROR_REGISTRY_CORRUPT;
135         else
136             buffer += count;
137     }
138 
139     ret = RegQueryValueExW(hkey, wszTargetAliasValue, 0, &type, NULL, &count);
140     if (ret != ERROR_FILE_NOT_FOUND && ret != ERROR_SUCCESS)
141         return ret;
142     else if (type != REG_SZ)
143         return ERROR_REGISTRY_CORRUPT;
144     *len += count;
145     if (credential)
146     {
147         credential->TargetAlias = (LPWSTR)buffer;
148         ret = RegQueryValueExW(hkey, wszTargetAliasValue, 0, &type, (LPVOID)credential->TargetAlias,
149                                &count);
150         if (ret == ERROR_FILE_NOT_FOUND)
151             credential->TargetAlias = NULL;
152         else if (ret != ERROR_SUCCESS)
153             return ret;
154         else if (type != REG_SZ)
155             return ERROR_REGISTRY_CORRUPT;
156         else
157             buffer += count;
158     }
159 
160     ret = RegQueryValueExW(hkey, wszUserNameValue, 0, &type, NULL, &count);
161     if (ret != ERROR_FILE_NOT_FOUND && ret != ERROR_SUCCESS)
162         return ret;
163     else if (type != REG_SZ)
164         return ERROR_REGISTRY_CORRUPT;
165     *len += count;
166     if (credential)
167     {
168         credential->UserName = (LPWSTR)buffer;
169         ret = RegQueryValueExW(hkey, wszUserNameValue, 0, &type, (LPVOID)credential->UserName,
170                                &count);
171         if (ret == ERROR_FILE_NOT_FOUND)
172         {
173             credential->UserName = NULL;
174             ret = ERROR_SUCCESS;
175         }
176         else if (ret != ERROR_SUCCESS)
177             return ret;
178         else if (type != REG_SZ)
179             return ERROR_REGISTRY_CORRUPT;
180         else
181             buffer += count;
182     }
183 
184     ret = read_credential_blob(hkey, key_data, NULL, &count);
185     if (ret != ERROR_FILE_NOT_FOUND && ret != ERROR_SUCCESS)
186         return ret;
187     *len += count;
188     if (credential)
189     {
190         credential->CredentialBlob = (LPBYTE)buffer;
191         ret = read_credential_blob(hkey, key_data, credential->CredentialBlob, &count);
192         if (ret == ERROR_FILE_NOT_FOUND)
193         {
194             credential->CredentialBlob = NULL;
195             ret = ERROR_SUCCESS;
196         }
197         else if (ret != ERROR_SUCCESS)
198             return ret;
199         credential->CredentialBlobSize = count;
200         buffer += count;
201     }
202 
203     /* FIXME: Attributes */
204     if (credential)
205     {
206         credential->AttributeCount = 0;
207         credential->Attributes = NULL;
208     }
209 
210     if (!credential) return ERROR_SUCCESS;
211 
212     count = sizeof(credential->Flags);
213     ret = RegQueryValueExW(hkey, wszFlagsValue, NULL, &type, (LPVOID)&credential->Flags,
214                            &count);
215     if (ret != ERROR_SUCCESS)
216         return ret;
217     else if (type != REG_DWORD)
218         return ERROR_REGISTRY_CORRUPT;
219     count = sizeof(credential->Type);
220     ret = RegQueryValueExW(hkey, wszTypeValue, NULL, &type, (LPVOID)&credential->Type,
221                            &count);
222     if (ret != ERROR_SUCCESS)
223         return ret;
224     else if (type != REG_DWORD)
225         return ERROR_REGISTRY_CORRUPT;
226 
227     count = sizeof(credential->LastWritten);
228     ret = RegQueryValueExW(hkey, wszLastWrittenValue, NULL, &type, (LPVOID)&credential->LastWritten,
229                            &count);
230     if (ret != ERROR_SUCCESS)
231         return ret;
232     else if (type != REG_BINARY)
233         return ERROR_REGISTRY_CORRUPT;
234     count = sizeof(credential->Persist);
235     ret = RegQueryValueExW(hkey, wszPersistValue, NULL, &type, (LPVOID)&credential->Persist,
236                            &count);
237     if (ret == ERROR_SUCCESS && type != REG_DWORD)
238         return ERROR_REGISTRY_CORRUPT;
239     return ret;
240 }
241 
242 #ifdef __APPLE__
243 static DWORD mac_read_credential_from_item(SecKeychainItemRef item, BOOL require_password,
244                                            PCREDENTIALW credential, char *buffer,
245                                            DWORD *len)
246 {
247     OSStatus status;
248     UInt32 i;
249     UInt32 cred_blob_len;
250     void *cred_blob;
251     LPWSTR domain = NULL;
252     LPWSTR user = NULL;
253     BOOL user_name_present = FALSE;
254     SecKeychainAttributeInfo info;
255     SecKeychainAttributeList *attr_list;
256     UInt32 info_tags[] = { kSecServerItemAttr, kSecSecurityDomainItemAttr, kSecAccountItemAttr,
257                            kSecCommentItemAttr, kSecCreationDateItemAttr };
258     info.count = sizeof(info_tags)/sizeof(info_tags[0]);
259     info.tag = info_tags;
260     info.format = NULL;
261     status = SecKeychainItemCopyAttributesAndData(item, &info, NULL, &attr_list, &cred_blob_len, &cred_blob);
262     if (status == errSecAuthFailed && !require_password)
263     {
264         cred_blob_len = 0;
265         cred_blob = NULL;
266         status = SecKeychainItemCopyAttributesAndData(item, &info, NULL, &attr_list, &cred_blob_len, NULL);
267     }
268     if (status != noErr)
269     {
270         WARN("SecKeychainItemCopyAttributesAndData returned status %ld\n", status);
271         return ERROR_NOT_FOUND;
272     }
273 
274     for (i = 0; i < attr_list->count; i++)
275         if (attr_list->attr[i].tag == kSecAccountItemAttr && attr_list->attr[i].data)
276         {
277             user_name_present = TRUE;
278             break;
279         }
280     if (!user_name_present)
281     {
282         WARN("no kSecAccountItemAttr for item\n");
283         return ERROR_NOT_FOUND;
284     }
285 
286     if (buffer)
287     {
288         credential->Flags = 0;
289         credential->Type = CRED_TYPE_DOMAIN_PASSWORD;
290         credential->TargetName = NULL;
291         credential->Comment = NULL;
292         memset(&credential->LastWritten, 0, sizeof(credential->LastWritten));
293         credential->CredentialBlobSize = 0;
294         credential->CredentialBlob = NULL;
295         credential->Persist = CRED_PERSIST_LOCAL_MACHINE;
296         credential->AttributeCount = 0;
297         credential->Attributes = NULL;
298         credential->TargetAlias = NULL;
299         credential->UserName = NULL;
300     }
301     for (i = 0; i < attr_list->count; i++)
302     {
303         switch (attr_list->attr[i].tag)
304         {
305             case kSecServerItemAttr:
306                 TRACE("kSecServerItemAttr: %.*s\n", (int)attr_list->attr[i].length,
307                       (char *)attr_list->attr[i].data);
308                 if (!attr_list->attr[i].data) continue;
309                 if (buffer)
310                 {
311                     INT str_len;
312                     credential->TargetName = (LPWSTR)buffer;
313                     str_len = MultiByteToWideChar(CP_UTF8, 0, attr_list->attr[i].data,
314                                                   attr_list->attr[i].length, (LPWSTR)buffer, 0xffff);
315                     credential->TargetName[str_len] = '\0';
316                     buffer += (str_len + 1) * sizeof(WCHAR);
317                     *len += (str_len + 1) * sizeof(WCHAR);
318                 }
319                 else
320                 {
321                     INT str_len;
322                     str_len = MultiByteToWideChar(CP_UTF8, 0, attr_list->attr[i].data,
323                                                   attr_list->attr[i].length, NULL, 0);
324                     *len += (str_len + 1) * sizeof(WCHAR);
325                 }
326                 break;
327             case kSecAccountItemAttr:
328             {
329                 INT str_len;
330                 TRACE("kSecAccountItemAttr: %.*s\n", (int)attr_list->attr[i].length,
331                       (char *)attr_list->attr[i].data);
332                 if (!attr_list->attr[i].data) continue;
333                 str_len = MultiByteToWideChar(CP_UTF8, 0, attr_list->attr[i].data,
334                                               attr_list->attr[i].length, NULL, 0);
335                 user = HeapAlloc(GetProcessHeap(), 0, (str_len + 1) * sizeof(WCHAR));
336                 MultiByteToWideChar(CP_UTF8, 0, attr_list->attr[i].data,
337                                     attr_list->attr[i].length, user, str_len);
338                 user[str_len] = '\0';
339                 break;
340             }
341             case kSecCommentItemAttr:
342                 TRACE("kSecCommentItemAttr: %.*s\n", (int)attr_list->attr[i].length,
343                       (char *)attr_list->attr[i].data);
344                 if (!attr_list->attr[i].data) continue;
345                 if (buffer)
346                 {
347                     INT str_len;
348                     credential->Comment = (LPWSTR)buffer;
349                     str_len = MultiByteToWideChar(CP_UTF8, 0, attr_list->attr[i].data,
350                                                   attr_list->attr[i].length, (LPWSTR)buffer, 0xffff);
351                     credential->Comment[str_len] = '\0';
352                     buffer += (str_len + 1) * sizeof(WCHAR);
353                     *len += (str_len + 1) * sizeof(WCHAR);
354                 }
355                 else
356                 {
357                     INT str_len;
358                     str_len = MultiByteToWideChar(CP_UTF8, 0, attr_list->attr[i].data,
359                                                   attr_list->attr[i].length, NULL, 0);
360                     *len += (str_len + 1) * sizeof(WCHAR);
361                 }
362                 break;
363             case kSecSecurityDomainItemAttr:
364             {
365                 INT str_len;
366                 TRACE("kSecSecurityDomainItemAttr: %.*s\n", (int)attr_list->attr[i].length,
367                       (char *)attr_list->attr[i].data);
368                 if (!attr_list->attr[i].data) continue;
369                 str_len = MultiByteToWideChar(CP_UTF8, 0, attr_list->attr[i].data,
370                                               attr_list->attr[i].length, NULL, 0);
371                 domain = HeapAlloc(GetProcessHeap(), 0, (str_len + 1) * sizeof(WCHAR));
372                 MultiByteToWideChar(CP_UTF8, 0, attr_list->attr[i].data,
373                                     attr_list->attr[i].length, domain, str_len);
374                 domain[str_len] = '\0';
375                 break;
376             }
377             case kSecCreationDateItemAttr:
378                 TRACE("kSecCreationDateItemAttr: %.*s\n", (int)attr_list->attr[i].length,
379                       (char *)attr_list->attr[i].data);
380                 if (buffer)
381                 {
382                     LARGE_INTEGER win_time;
383                     struct tm tm;
384                     time_t time;
385                     memset(&tm, 0, sizeof(tm));
386                     strptime(attr_list->attr[i].data, "%Y%m%d%H%M%SZ", &tm);
387                     time = mktime(&tm);
388                     RtlSecondsSince1970ToTime(time, &win_time);
389                     credential->LastWritten.dwLowDateTime = win_time.u.LowPart;
390                     credential->LastWritten.dwHighDateTime = win_time.u.HighPart;
391                 }
392                 break;
393         }
394     }
395 
396     if (user)
397     {
398         INT str_len;
399         if (buffer)
400             credential->UserName = (LPWSTR)buffer;
401         if (domain)
402         {
403             str_len = strlenW(domain);
404             *len += (str_len + 1) * sizeof(WCHAR);
405             if (buffer)
406             {
407                 memcpy(credential->UserName, domain, str_len * sizeof(WCHAR));
408                 /* FIXME: figure out when to use an '@' */
409                 credential->UserName[str_len] = '\\';
410                 buffer += (str_len + 1) * sizeof(WCHAR);
411             }
412         }
413         str_len = strlenW(user);
414         *len += (str_len + 1) * sizeof(WCHAR);
415         if (buffer)
416         {
417             memcpy(buffer, user, (str_len + 1) * sizeof(WCHAR));
418             buffer += (str_len + 1) * sizeof(WCHAR);
419             TRACE("UserName = %s\n", debugstr_w(credential->UserName));
420         }
421     }
422     HeapFree(GetProcessHeap(), 0, user);
423     HeapFree(GetProcessHeap(), 0, domain);
424 
425     if (cred_blob)
426     {
427         if (buffer)
428         {
429             INT str_len;
430             credential->CredentialBlob = (BYTE *)buffer;
431             str_len = MultiByteToWideChar(CP_UTF8, 0, cred_blob, cred_blob_len,
432                                           (LPWSTR)buffer, 0xffff);
433             credential->CredentialBlobSize = str_len * sizeof(WCHAR);
434             buffer += str_len * sizeof(WCHAR);
435             *len += str_len * sizeof(WCHAR);
436         }
437         else
438         {
439             INT str_len;
440             str_len = MultiByteToWideChar(CP_UTF8, 0, cred_blob, cred_blob_len,
441                                           NULL, 0);
442             *len += str_len * sizeof(WCHAR);
443         }
444     }
445     SecKeychainItemFreeAttributesAndData(attr_list, cred_blob);
446     return ERROR_SUCCESS;
447 }
448 #endif
449 
450 static DWORD write_credential_blob(HKEY hkey, LPCWSTR target_name, DWORD type,
451                                    const BYTE key_data[KEY_SIZE],
452                                    const BYTE *credential_blob, DWORD credential_blob_size)
453 {
454     LPBYTE encrypted_credential_blob;
455     struct ustring data;
456     struct ustring key;
457     DWORD ret;
458 
459     key.Length = key.MaximumLength = KEY_SIZE;
460     key.Buffer = (unsigned char *)key_data;
461 
462     encrypted_credential_blob = HeapAlloc(GetProcessHeap(), 0, credential_blob_size);
463     if (!encrypted_credential_blob) return ERROR_OUTOFMEMORY;
464 
465     memcpy(encrypted_credential_blob, credential_blob, credential_blob_size);
466     data.Length = data.MaximumLength = credential_blob_size;
467     data.Buffer = encrypted_credential_blob;
468     SystemFunction032(&data, &key);
469 
470     ret = RegSetValueExW(hkey, wszPasswordValue, 0, REG_BINARY, (LPVOID)encrypted_credential_blob, credential_blob_size);
471     HeapFree(GetProcessHeap(), 0, encrypted_credential_blob);
472 
473     return ret;
474 }
475 
476 static DWORD registry_write_credential(HKEY hkey, const CREDENTIALW *credential,
477                                        const BYTE key_data[KEY_SIZE], BOOL preserve_blob)
478 {
479     DWORD ret;
480     FILETIME LastWritten;
481 
482     GetSystemTimeAsFileTime(&LastWritten);
483 
484     ret = RegSetValueExW(hkey, wszFlagsValue, 0, REG_DWORD, (LPVOID)&credential->Flags,
485                          sizeof(credential->Flags));
486     if (ret != ERROR_SUCCESS) return ret;
487     ret = RegSetValueExW(hkey, wszTypeValue, 0, REG_DWORD, (LPVOID)&credential->Type,
488                          sizeof(credential->Type));
489     if (ret != ERROR_SUCCESS) return ret;
490     ret = RegSetValueExW(hkey, NULL, 0, REG_SZ, (LPVOID)credential->TargetName,
491                          sizeof(WCHAR)*(strlenW(credential->TargetName)+1));
492     if (ret != ERROR_SUCCESS) return ret;
493     if (credential->Comment)
494     {
495         ret = RegSetValueExW(hkey, wszCommentValue, 0, REG_SZ, (LPVOID)credential->Comment,
496                              sizeof(WCHAR)*(strlenW(credential->Comment)+1));
497         if (ret != ERROR_SUCCESS) return ret;
498     }
499     ret = RegSetValueExW(hkey, wszLastWrittenValue, 0, REG_BINARY, (LPVOID)&LastWritten,
500                          sizeof(LastWritten));
501     if (ret != ERROR_SUCCESS) return ret;
502     ret = RegSetValueExW(hkey, wszPersistValue, 0, REG_DWORD, (LPVOID)&credential->Persist,
503                          sizeof(credential->Persist));
504     if (ret != ERROR_SUCCESS) return ret;
505     /* FIXME: Attributes */
506     if (credential->TargetAlias)
507     {
508         ret = RegSetValueExW(hkey, wszTargetAliasValue, 0, REG_SZ, (LPVOID)credential->TargetAlias,
509                              sizeof(WCHAR)*(strlenW(credential->TargetAlias)+1));
510         if (ret != ERROR_SUCCESS) return ret;
511     }
512     if (credential->UserName)
513     {
514         ret = RegSetValueExW(hkey, wszUserNameValue, 0, REG_SZ, (LPVOID)credential->UserName,
515                              sizeof(WCHAR)*(strlenW(credential->UserName)+1));
516         if (ret != ERROR_SUCCESS) return ret;
517     }
518     if (!preserve_blob)
519     {
520         ret = write_credential_blob(hkey, credential->TargetName, credential->Type,
521                                     key_data, credential->CredentialBlob,
522                                     credential->CredentialBlobSize);
523     }
524     return ret;
525 }
526 
527 #ifdef __APPLE__
528 static DWORD mac_write_credential(const CREDENTIALW *credential, BOOL preserve_blob)
529 {
530     OSStatus status;
531     SecKeychainItemRef keychain_item;
532     char *username;
533     char *domain = NULL;
534     char *password;
535     char *servername;
536     UInt32 userlen;
537     UInt32 domainlen = 0;
538     UInt32 pwlen;
539     UInt32 serverlen;
540     LPCWSTR p;
541     SecKeychainAttribute attrs[1];
542     SecKeychainAttributeList attr_list;
543 
544     if (credential->Flags)
545         FIXME("Flags 0x%x not written\n", credential->Flags);
546     if (credential->Type != CRED_TYPE_DOMAIN_PASSWORD)
547         FIXME("credential type of %d not supported\n", credential->Type);
548     if (credential->Persist != CRED_PERSIST_LOCAL_MACHINE)
549         FIXME("persist value of %d not supported\n", credential->Persist);
550     if (credential->AttributeCount)
551         FIXME("custom attributes not supported\n");
552 
553     p = strchrW(credential->UserName, '\\');
554     if (p)
555     {
556         domainlen = WideCharToMultiByte(CP_UTF8, 0, credential->UserName,
557                                         p - credential->UserName, NULL, 0, NULL, NULL);
558         domain = HeapAlloc(GetProcessHeap(), 0, (domainlen + 1) * sizeof(*domain));
559         WideCharToMultiByte(CP_UTF8, 0, credential->UserName, p - credential->UserName,
560                             domain, domainlen, NULL, NULL);
561         domain[domainlen] = '\0';
562         p++;
563     }
564     else
565         p = credential->UserName;
566     userlen = WideCharToMultiByte(CP_UTF8, 0, p, -1, NULL, 0, NULL, NULL);
567     username = HeapAlloc(GetProcessHeap(), 0, userlen * sizeof(*username));
568     WideCharToMultiByte(CP_UTF8, 0, p, -1, username, userlen, NULL, NULL);
569 
570     serverlen = WideCharToMultiByte(CP_UTF8, 0, credential->TargetName, -1, NULL, 0, NULL, NULL);
571     servername = HeapAlloc(GetProcessHeap(), 0, serverlen * sizeof(*servername));
572     WideCharToMultiByte(CP_UTF8, 0, credential->TargetName, -1, servername, serverlen, NULL, NULL);
573     pwlen = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)credential->CredentialBlob,
574                                 credential->CredentialBlobSize / sizeof(WCHAR), NULL, 0, NULL, NULL);
575     password = HeapAlloc(GetProcessHeap(), 0, pwlen * sizeof(*domain));
576     WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)credential->CredentialBlob,
577                         credential->CredentialBlobSize / sizeof(WCHAR), password, pwlen, NULL, NULL);
578 
579     TRACE("adding server %s, domain %s, username %s using Keychain\n", servername, domain, username);
580     status = SecKeychainAddInternetPassword(NULL, strlen(servername), servername,
581                                             strlen(domain), domain, strlen(username),
582                                             username, 0, NULL, 0,
583                                             0 /* no protocol */,
584                                             kSecAuthenticationTypeDefault,
585                                             strlen(password), password, &keychain_item);
586     if (status != noErr)
587         ERR("SecKeychainAddInternetPassword returned %ld\n", status);
588     if (status == errSecDuplicateItem)
589     {
590         SecKeychainItemRef keychain_item;
591 
592         status = SecKeychainFindInternetPassword(NULL, strlen(servername), servername,
593                                                  strlen(domain), domain,
594                                                  strlen(username), username,
595                                                  0, NULL /* any path */, 0,
596                                                  0 /* any protocol */,
597                                                  0 /* any authentication type */,
598                                                  0, NULL, &keychain_item);
599         if (status != noErr)
600             ERR("SecKeychainFindInternetPassword returned %ld\n", status);
601     }
602     HeapFree(GetProcessHeap(), 0, domain);
603     HeapFree(GetProcessHeap(), 0, username);
604     HeapFree(GetProcessHeap(), 0, servername);
605     if (status != noErr)
606     {
607         HeapFree(GetProcessHeap(), 0, password);
608         return ERROR_GEN_FAILURE;
609     }
610     if (credential->Comment)
611     {
612         attr_list.count = 1;
613         attr_list.attr = attrs;
614         attrs[0].tag = kSecCommentItemAttr;
615         attrs[0].length = WideCharToMultiByte(CP_UTF8, 0, credential->Comment, -1, NULL, 0, NULL, NULL);
616         if (attrs[0].length) attrs[0].length--;
617         attrs[0].data = HeapAlloc(GetProcessHeap(), 0, attrs[0].length);
618         WideCharToMultiByte(CP_UTF8, 0, credential->Comment, -1, attrs[0].data, attrs[0].length, NULL, NULL);
619     }
620     else
621     {
622         attr_list.count = 0;
623         attr_list.attr = NULL;
624     }
625     status = SecKeychainItemModifyAttributesAndData(keychain_item, &attr_list,
626                                                     preserve_blob ? 0 : strlen(password),
627                                                     preserve_blob ? NULL : password);
628     if (credential->Comment)
629         HeapFree(GetProcessHeap(), 0, attrs[0].data);
630     HeapFree(GetProcessHeap(), 0, password);
631     /* FIXME: set TargetAlias attribute */
632     CFRelease(keychain_item);
633     return ERROR_SUCCESS;
634 }
635 #endif
636 
637 static DWORD open_cred_mgr_key(HKEY *hkey, BOOL open_for_write)
638 {
639     return RegCreateKeyExW(HKEY_CURRENT_USER, wszCredentialManagerKey, 0,
640                            NULL, REG_OPTION_NON_VOLATILE,
641                            KEY_READ | (open_for_write ? KEY_WRITE : 0), NULL, hkey, NULL);
642 }
643 
644 static DWORD get_cred_mgr_encryption_key(HKEY hkeyMgr, BYTE key_data[KEY_SIZE])
645 {
646     static const BYTE my_key_data[KEY_SIZE] = { 0 };
647     DWORD type;
648     DWORD count;
649     FILETIME ft;
650     ULONG seed;
651     ULONG value;
652     DWORD ret;
653 
654     memcpy(key_data, my_key_data, KEY_SIZE);
655 
656     count = KEY_SIZE;
657     ret = RegQueryValueExW(hkeyMgr, wszEncryptionKeyValue, NULL, &type, (LPVOID)key_data,
658                            &count);
659     if (ret == ERROR_SUCCESS)
660     {
661         if (type != REG_BINARY)
662             return ERROR_REGISTRY_CORRUPT;
663         else
664             return ERROR_SUCCESS;
665     }
666     if (ret != ERROR_FILE_NOT_FOUND)
667         return ret;
668 
669     GetSystemTimeAsFileTime(&ft);
670     seed = ft.dwLowDateTime;
671     value = RtlUniform(&seed);
672     *(DWORD *)key_data = value;
673     seed = ft.dwHighDateTime;
674     value = RtlUniform(&seed);
675     *(DWORD *)(key_data + 4) = value;
676 
677     ret = RegSetValueExW(hkeyMgr, wszEncryptionKeyValue, 0, REG_BINARY,
678                          (LPVOID)key_data, KEY_SIZE);
679     if (ret == ERROR_ACCESS_DENIED)
680     {
681         ret = open_cred_mgr_key(&hkeyMgr, TRUE);
682         if (ret == ERROR_SUCCESS)
683         {
684             ret = RegSetValueExW(hkeyMgr, wszEncryptionKeyValue, 0, REG_BINARY,
685                                  (LPVOID)key_data, KEY_SIZE);
686             RegCloseKey(hkeyMgr);
687         }
688     }
689     return ret;
690 }
691 
692 static LPWSTR get_key_name_for_target(LPCWSTR target_name, DWORD type)
693 {
694     static const WCHAR wszGenericPrefix[] = {'G','e','n','e','r','i','c',':',' ',0};
695     static const WCHAR wszDomPasswdPrefix[] = {'D','o','m','P','a','s','s','w','d',':',' ',0};
696     INT len;
697     LPCWSTR prefix = NULL;
698     LPWSTR key_name, p;
699 
700     len = strlenW(target_name);
701     if (type == CRED_TYPE_GENERIC)
702     {
703         prefix = wszGenericPrefix;
704         len += sizeof(wszGenericPrefix)/sizeof(wszGenericPrefix[0]);
705     }
706     else
707     {
708         prefix = wszDomPasswdPrefix;
709         len += sizeof(wszDomPasswdPrefix)/sizeof(wszDomPasswdPrefix[0]);
710     }
711 
712     key_name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
713     if (!key_name) return NULL;
714 
715     strcpyW(key_name, prefix);
716     strcatW(key_name, target_name);
717 
718     for (p = key_name; *p; p++)
719         if (*p == '\\') *p = '_';
720 
721     return key_name;
722 }
723 
724 static BOOL credential_matches_filter(HKEY hkeyCred, LPCWSTR filter)
725 {
726     LPWSTR target_name;
727     DWORD ret;
728     DWORD type;
729     DWORD count;
730     LPCWSTR p;
731 
732     if (!filter) return TRUE;
733 
734     ret = RegQueryValueExW(hkeyCred, NULL, 0, &type, NULL, &count);
735     if (ret != ERROR_SUCCESS)
736         return FALSE;
737     else if (type != REG_SZ)
738         return FALSE;
739 
740     target_name = HeapAlloc(GetProcessHeap(), 0, count);
741     if (!target_name)
742         return FALSE;
743     ret = RegQueryValueExW(hkeyCred, NULL, 0, &type, (LPVOID)target_name, &count);
744     if (ret != ERROR_SUCCESS || type != REG_SZ)
745     {
746         HeapFree(GetProcessHeap(), 0, target_name);
747         return FALSE;
748     }
749 
750     TRACE("comparing filter %s to target name %s\n", debugstr_w(filter),
751           debugstr_w(target_name));
752 
753     p = strchrW(filter, '*');
754     ret = CompareStringW(GetThreadLocale(), 0, filter,
755                          (p && !p[1] ? p - filter : -1), target_name,
756                          (p && !p[1] ? p - filter : -1)) == CSTR_EQUAL;
757 
758     HeapFree(GetProcessHeap(), 0, target_name);
759     return ret;
760 }
761 
762 static DWORD registry_enumerate_credentials(HKEY hkeyMgr, LPCWSTR filter,
763                                             LPWSTR target_name,
764                                             DWORD target_name_len, BYTE key_data[KEY_SIZE],
765                                             PCREDENTIALW *credentials, char **buffer,
766                                             DWORD *len, DWORD *count)
767 {
768     DWORD i;
769     DWORD ret;
770     for (i = 0;; i++)
771     {
772         HKEY hkeyCred;
773         ret = RegEnumKeyW(hkeyMgr, i, target_name, target_name_len+1);
774         if (ret == ERROR_NO_MORE_ITEMS)
775         {
776             ret = ERROR_SUCCESS;
777             break;
778         }
779         else if (ret != ERROR_SUCCESS)
780         {
781             ret = ERROR_SUCCESS;
782             continue;
783         }
784         TRACE("target_name = %s\n", debugstr_w(target_name));
785         ret = RegOpenKeyExW(hkeyMgr, target_name, 0, KEY_QUERY_VALUE, &hkeyCred);
786         if (ret != ERROR_SUCCESS)
787         {
788             ret = ERROR_SUCCESS;
789             continue;
790         }
791         if (!credential_matches_filter(hkeyCred, filter))
792         {
793             RegCloseKey(hkeyCred);
794             continue;
795         }
796         if (buffer)
797         {
798             *len = sizeof(CREDENTIALW);
799             credentials[*count] = (PCREDENTIALW)*buffer;
800         }
801         else
802             *len += sizeof(CREDENTIALW);
803         ret = registry_read_credential(hkeyCred, buffer ? credentials[*count] : NULL,
804                                        key_data, buffer ? *buffer + sizeof(CREDENTIALW) : NULL,
805                                        len);
806         RegCloseKey(hkeyCred);
807         if (ret != ERROR_SUCCESS) break;
808         if (buffer) *buffer += *len;
809         (*count)++;
810     }
811     return ret;
812 }
813 
814 #ifdef __APPLE__
815 static DWORD mac_enumerate_credentials(LPCWSTR filter, PCREDENTIALW *credentials,
816                                        char *buffer, DWORD *len, DWORD *count)
817 {
818     SecKeychainSearchRef search;
819     SecKeychainItemRef item;
820     OSStatus status;
821     Boolean saved_user_interaction_allowed;
822     DWORD ret;
823 
824     SecKeychainGetUserInteractionAllowed(&saved_user_interaction_allowed);
825     SecKeychainSetUserInteractionAllowed(false);
826 
827     status = SecKeychainSearchCreateFromAttributes(NULL, kSecInternetPasswordItemClass, NULL, &search);
828     if (status == noErr)
829     {
830         while (SecKeychainSearchCopyNext(search, &item) == noErr)
831         {
832             SecKeychainAttributeInfo info;
833             SecKeychainAttributeList *attr_list;
834             UInt32 info_tags[] = { kSecServerItemAttr };
835             info.count = sizeof(info_tags)/sizeof(info_tags[0]);
836             info.tag = info_tags;
837             info.format = NULL;
838             status = SecKeychainItemCopyAttributesAndData(item, &info, NULL, &attr_list, NULL, NULL);
839             if (status != noErr)
840             {
841                 WARN("SecKeychainItemCopyAttributesAndData returned status %ld\n", status);
842                 continue;