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