From 312a1db3ec28fe13cacc2719f782161ff6e224c1 Mon Sep 17 00:00:00 2001 From: Rob Shearman <rob@codeweavers.com> Date: Mon, 26 Nov 2007 22:57:35 +0000 Subject: [PATCH] advapi32: Add support for using the Mac Keychain services as a backend for the credential functions instead of the registry. --- configure | 8 +- configure.ac | 1 + dlls/advapi32/Makefile.in | 1 + dlls/advapi32/cred.c | 540 +++++++++++++++++++++++++++++++++++++- 4 files changed, 543 insertions(+), 7 deletions(-) diff --git a/configure b/configure index 63ae5239c69..176b5b6e3ab 100755 --- a/configure +++ b/configure @@ -722,6 +722,7 @@ LDD DLLTOOL DLLWRAP MINGWAR +SECURITYLIB COREFOUNDATIONLIB IOKITLIB LDEXECFLAGS @@ -8093,7 +8094,9 @@ fi LDSHARED="\$(CC) -dynamiclib" STRIP="$STRIP -x" LDRPATH_LOCAL="&& install_name_tool -change @executable_path/\`\$(RELPATH) \$(bindir) \$(libdir)\`/libwine.1.dylib @executable_path/\$(TOPOBJDIR)/libs/wine/libwine.1.dylib \$@ || \$(RM) \$@" - COREFOUNDATIONLIB="-framework CoreFoundation" + SECURITYLIB="-framework Security -framework CoreFoundation" + + COREFOUNDATIONLIB="-framework CoreFoundation" IOKITLIB="-framework IOKit -framework CoreFoundation" @@ -22414,6 +22417,7 @@ LDD!$LDD$ac_delim DLLTOOL!$DLLTOOL$ac_delim DLLWRAP!$DLLWRAP$ac_delim MINGWAR!$MINGWAR$ac_delim +SECURITYLIB!$SECURITYLIB$ac_delim COREFOUNDATIONLIB!$COREFOUNDATIONLIB$ac_delim IOKITLIB!$IOKITLIB$ac_delim LDEXECFLAGS!$LDEXECFLAGS$ac_delim @@ -22462,7 +22466,7 @@ LIBOBJS!$LIBOBJS$ac_delim LTLIBOBJS!$LTLIBOBJS$ac_delim _ACEOF - if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 71; then + if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 72; then break elif $ac_last_try; then { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 diff --git a/configure.ac b/configure.ac index 7aa5d85eca5..3a3b8832a48 100644 --- a/configure.ac +++ b/configure.ac @@ -388,6 +388,7 @@ case $host_os in STRIP="$STRIP -x" LDRPATH_LOCAL="&& install_name_tool -change @executable_path/\`\$(RELPATH) \$(bindir) \$(libdir)\`/libwine.1.dylib @executable_path/\$(TOPOBJDIR)/libs/wine/libwine.1.dylib \$@ || \$(RM) \$@" dnl declare needed frameworks + AC_SUBST(SECURITYLIB,"-framework Security -framework CoreFoundation") AC_SUBST(COREFOUNDATIONLIB,"-framework CoreFoundation") AC_SUBST(IOKITLIB,"-framework IOKit -framework CoreFoundation") AC_SUBST(LDEXECFLAGS,["-image_base 0x7bf00000 -Wl,-segaddr,WINE_DOS,0x00000000,-segaddr,WINE_SHARED_HEAP,0x7f000000"]) diff --git a/dlls/advapi32/Makefile.in b/dlls/advapi32/Makefile.in index 77d53dcca09..2c68895449d 100644 --- a/dlls/advapi32/Makefile.in +++ b/dlls/advapi32/Makefile.in @@ -6,6 +6,7 @@ VPATH = @srcdir@ MODULE = advapi32.dll IMPORTLIB = libadvapi32.$(IMPLIBEXT) IMPORTS = kernel32 ntdll +EXTRALIBS = @SECURITYLIB@ C_SRCS = \ advapi.c \ diff --git a/dlls/advapi32/cred.c b/dlls/advapi32/cred.c index 37123296588..65677c49628 100644 --- a/dlls/advapi32/cred.c +++ b/dlls/advapi32/cred.c @@ -19,6 +19,7 @@ */ #include <stdarg.h> +#include <time.h> #include "windef.h" #include "winbase.h" @@ -26,6 +27,12 @@ #include "wincred.h" #include "winternl.h" +#ifdef __APPLE__ +# include <Security/SecKeychain.h> +# include <Security/SecKeychainItem.h> +# include <Security/SecKeychainSearch.h> +#endif + #include "crypt.h" #include "wine/unicode.h" @@ -232,6 +239,200 @@ static DWORD registry_read_credential(HKEY hkey, PCREDENTIALW credential, return ret; } +#ifdef __APPLE__ +static DWORD mac_read_credential_from_item(SecKeychainItemRef item, BOOL require_password, + PCREDENTIALW credential, char *buffer, + DWORD *len) +{ + OSStatus status; + UInt32 i; + UInt32 cred_blob_len; + void *cred_blob; + LPWSTR domain = NULL; + LPWSTR user = NULL; + SecKeychainAttributeInfo info; + SecKeychainAttributeList *attr_list; + UInt32 info_tags[] = { kSecServerItemAttr, kSecSecurityDomainItemAttr, kSecAccountItemAttr, + kSecCommentItemAttr, kSecCreationDateItemAttr }; + info.count = sizeof(info_tags)/sizeof(info_tags[0]); + info.tag = info_tags; + info.format = NULL; + status = SecKeychainItemCopyAttributesAndData(item, &info, NULL, &attr_list, &cred_blob_len, &cred_blob); + if (status == errSecAuthFailed && !require_password) + { + cred_blob_len = 0; + cred_blob = NULL; + status = SecKeychainItemCopyAttributesAndData(item, &info, NULL, &attr_list, &cred_blob_len, NULL); + } + if (status != noErr) + { + WARN("SecKeychainItemCopyAttributesAndData returned status %ld\n", status); + return ERROR_NOT_FOUND; + } + if (buffer) + { + credential->Flags = 0; + credential->Type = CRED_TYPE_DOMAIN_PASSWORD; + credential->TargetName = NULL; + credential->Comment = NULL; + memset(&credential->LastWritten, 0, sizeof(credential->LastWritten)); + credential->CredentialBlobSize = 0; + credential->CredentialBlob = NULL; + credential->Persist = CRED_PERSIST_LOCAL_MACHINE; + credential->AttributeCount = 0; + credential->Attributes = NULL; + credential->TargetAlias = NULL; + credential->UserName = NULL; + } + for (i = 0; i < attr_list->count; i++) + { + switch (attr_list->attr[i].tag) + { + case kSecServerItemAttr: + TRACE("kSecServerItemAttr: %.*s\n", (int)attr_list->attr[i].length, + (char *)attr_list->attr[i].data); + if (!attr_list->attr[i].data) continue; + if (buffer) + { + INT str_len; + credential->TargetName = (LPWSTR)buffer; + str_len = MultiByteToWideChar(CP_UTF8, 0, attr_list->attr[i].data, + attr_list->attr[i].length, (LPWSTR)buffer, 0xffff); + credential->TargetName[str_len] = '\0'; + buffer += (str_len + 1) * sizeof(WCHAR); + *len += (str_len + 1) * sizeof(WCHAR); + } + else + { + INT str_len; + str_len = MultiByteToWideChar(CP_UTF8, 0, attr_list->attr[i].data, + attr_list->attr[i].length, NULL, 0); + *len += (str_len + 1) * sizeof(WCHAR); + } + break; + case kSecAccountItemAttr: + { + INT str_len; + TRACE("kSecAccountItemAttr: %.*s\n", (int)attr_list->attr[i].length, + (char *)attr_list->attr[i].data); + if (!attr_list->attr[i].data) continue; + str_len = MultiByteToWideChar(CP_UTF8, 0, attr_list->attr[i].data, + attr_list->attr[i].length, NULL, 0); + user = HeapAlloc(GetProcessHeap(), 0, (str_len + 1) * sizeof(WCHAR)); + MultiByteToWideChar(CP_UTF8, 0, attr_list->attr[i].data, + attr_list->attr[i].length, user, str_len); + user[str_len] = '\0'; + break; + } + case kSecCommentItemAttr: + TRACE("kSecCommentItemAttr: %.*s\n", (int)attr_list->attr[i].length, + (char *)attr_list->attr[i].data); + if (!attr_list->attr[i].data) continue; + if (buffer) + { + INT str_len; + credential->Comment = (LPWSTR)buffer; + str_len = MultiByteToWideChar(CP_UTF8, 0, attr_list->attr[i].data, + attr_list->attr[i].length, (LPWSTR)buffer, 0xffff); + credential->Comment[str_len] = '\0'; + buffer += (str_len + 1) * sizeof(WCHAR); + *len += (str_len + 1) * sizeof(WCHAR); + } + else + { + INT str_len; + str_len = MultiByteToWideChar(CP_UTF8, 0, attr_list->attr[i].data, + attr_list->attr[i].length, NULL, 0); + *len += (str_len + 1) * sizeof(WCHAR); + } + break; + case kSecSecurityDomainItemAttr: + { + INT str_len; + TRACE("kSecSecurityDomainItemAttr: %.*s\n", (int)attr_list->attr[i].length, + (char *)attr_list->attr[i].data); + if (!attr_list->attr[i].data) continue; + str_len = MultiByteToWideChar(CP_UTF8, 0, attr_list->attr[i].data, + attr_list->attr[i].length, NULL, 0); + domain = HeapAlloc(GetProcessHeap(), 0, (str_len + 1) * sizeof(WCHAR)); + MultiByteToWideChar(CP_UTF8, 0, attr_list->attr[i].data, + attr_list->attr[i].length, domain, str_len); + domain[str_len] = '\0'; + break; + } + case kSecCreationDateItemAttr: + TRACE("kSecCreationDateItemAttr: %.*s\n", (int)attr_list->attr[i].length, + (char *)attr_list->attr[i].data); + if (buffer) + { + LARGE_INTEGER win_time; + struct tm tm; + time_t time; + memset(&tm, 0, sizeof(tm)); + strptime(attr_list->attr[i].data, "%Y%m%d%H%M%SZ", &tm); + time = mktime(&tm); + RtlSecondsSince1970ToTime(time, &win_time); + credential->LastWritten.dwLowDateTime = win_time.u.LowPart; + credential->LastWritten.dwHighDateTime = win_time.u.HighPart; + } + break; + } + } + + if (user) + { + INT str_len; + if (buffer) + credential->UserName = (LPWSTR)buffer; + if (domain) + { + str_len = strlenW(domain); + *len += (str_len + 1) * sizeof(WCHAR); + if (buffer) + { + memcpy(credential->UserName, domain, str_len * sizeof(WCHAR)); + /* FIXME: figure out when to use an '@' */ + credential->UserName[str_len] = '\\'; + buffer += (str_len + 1) * sizeof(WCHAR); + } + } + str_len = strlenW(user); + *len += (str_len + 1) * sizeof(WCHAR); + if (buffer) + { + memcpy(buffer, user, (str_len + 1) * sizeof(WCHAR)); + buffer += (str_len + 1) * sizeof(WCHAR); + TRACE("UserName = %s\n", debugstr_w(credential->UserName)); + } + } + HeapFree(GetProcessHeap(), 0, user); + HeapFree(GetProcessHeap(), 0, domain); + + if (cred_blob) + { + if (buffer) + { + INT str_len; + credential->CredentialBlob = (BYTE *)buffer; + str_len = MultiByteToWideChar(CP_UTF8, 0, cred_blob, cred_blob_len, + (LPWSTR)buffer, 0xffff); + credential->CredentialBlobSize = str_len * sizeof(WCHAR); + buffer += str_len * sizeof(WCHAR); + *len += str_len * sizeof(WCHAR); + } + else + { + INT str_len; + str_len = MultiByteToWideChar(CP_UTF8, 0, cred_blob, cred_blob_len, + NULL, 0); + *len += str_len * sizeof(WCHAR); + } + } + SecKeychainItemFreeAttributesAndData(attr_list, cred_blob); + return ERROR_SUCCESS; +} +#endif + 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) @@ -309,6 +510,116 @@ static DWORD registry_write_credential(HKEY hkey, const CREDENTIALW *credential, return ret; } +#ifdef __APPLE__ +static DWORD mac_write_credential(const CREDENTIALW *credential, BOOL preserve_blob) +{ + OSStatus status; + SecKeychainItemRef keychain_item; + char *username; + char *domain = NULL; + char *password; + char *servername; + UInt32 userlen; + UInt32 domainlen = 0; + UInt32 pwlen; + UInt32 serverlen; + LPCWSTR p; + SecKeychainAttribute attrs[1]; + SecKeychainAttributeList attr_list; + + if (credential->Flags) + FIXME("Flags 0x%x not written\n", credential->Flags); + if (credential->Type != CRED_TYPE_DOMAIN_PASSWORD) + FIXME("credential type of %d not supported\n", credential->Type); + if (credential->Persist != CRED_PERSIST_LOCAL_MACHINE) + FIXME("persist value of %d not supported\n", credential->Persist); + if (credential->AttributeCount) + FIXME("custom attributes not supported\n"); + + p = strchrW(credential->UserName, '\\'); + if (p) + { + domainlen = WideCharToMultiByte(CP_UTF8, 0, credential->UserName, + p - credential->UserName, NULL, 0, NULL, NULL); + domain = HeapAlloc(GetProcessHeap(), 0, (domainlen + 1) * sizeof(*domain)); + WideCharToMultiByte(CP_UTF8, 0, credential->UserName, p - credential->UserName, + domain, domainlen, NULL, NULL); + domain[domainlen] = '\0'; + p++; + } + else + p = credential->UserName; + userlen = WideCharToMultiByte(CP_UTF8, 0, p, -1, NULL, 0, NULL, NULL); + username = HeapAlloc(GetProcessHeap(), 0, userlen * sizeof(*username)); + WideCharToMultiByte(CP_UTF8, 0, p, -1, username, userlen, NULL, NULL); + + serverlen = WideCharToMultiByte(CP_UTF8, 0, credential->TargetName, -1, NULL, 0, NULL, NULL); + servername = HeapAlloc(GetProcessHeap(), 0, serverlen * sizeof(*servername)); + WideCharToMultiByte(CP_UTF8, 0, credential->TargetName, -1, servername, serverlen, NULL, NULL); + pwlen = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)credential->CredentialBlob, + credential->CredentialBlobSize / sizeof(WCHAR), NULL, 0, NULL, NULL); + password = HeapAlloc(GetProcessHeap(), 0, pwlen * sizeof(*domain)); + WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)credential->CredentialBlob, + credential->CredentialBlobSize / sizeof(WCHAR), password, pwlen, NULL, NULL); + + TRACE("adding server %s, domain %s, username %s using Keychain\n", servername, domain, username); + status = SecKeychainAddInternetPassword(NULL, strlen(servername), servername, + strlen(domain), domain, strlen(username), + username, 0, NULL, 0, + 0 /* no protocol */, + kSecAuthenticationTypeDefault, + strlen(password), password, &keychain_item); + if (status != noErr) + ERR("SecKeychainAddInternetPassword returned %ld\n", status); + if (status == errSecDuplicateItem) + { + SecKeychainItemRef keychain_item; + + status = SecKeychainFindInternetPassword(NULL, strlen(servername), servername, + strlen(domain), domain, + strlen(username), username, + 0, NULL /* any path */, 0, + 0 /* any protocol */, + 0 /* any authentication type */, + 0, NULL, &keychain_item); + if (status != noErr) + ERR("SecKeychainFindInternetPassword returned %ld\n", status); + } + HeapFree(GetProcessHeap(), 0, domain); + HeapFree(GetProcessHeap(), 0, username); + HeapFree(GetProcessHeap(), 0, servername); + if (status != noErr) + { + HeapFree(GetProcessHeap(), 0, password); + return ERROR_GEN_FAILURE; + } + if (credential->Comment) + { + attr_list.count = 1; + attr_list.attr = attrs; + attrs[0].tag = kSecCommentItemAttr; + attrs[0].length = WideCharToMultiByte(CP_UTF8, 0, credential->Comment, -1, NULL, 0, NULL, NULL); + if (attrs[0].length) attrs[0].length--; + attrs[0].data = HeapAlloc(GetProcessHeap(), 0, attrs[0].length); + WideCharToMultiByte(CP_UTF8, 0, credential->Comment, -1, attrs[0].data, attrs[0].length, NULL, NULL); + } + else + { + attr_list.count = 0; + attr_list.attr = NULL; + } + status = SecKeychainItemModifyAttributesAndData(keychain_item, &attr_list, + preserve_blob ? 0 : strlen(password), + preserve_blob ? NULL : password); + if (credential->Comment) + HeapFree(GetProcessHeap(), 0, attrs[0].data); + HeapFree(GetProcessHeap(), 0, password); + /* FIXME: set TargetAlias attribute */ + CFRelease(keychain_item); + return ERROR_SUCCESS; +} +#endif + static DWORD open_cred_mgr_key(HKEY *hkey, BOOL open_for_write) { return RegCreateKeyExW(HKEY_CURRENT_USER, wszCredentialManagerKey, 0, @@ -426,7 +737,7 @@ static BOOL credential_matches_filter(HKEY hkeyCred, LPCWSTR filter) static DWORD registry_enumerate_credentials(HKEY hkeyMgr, LPCWSTR filter, LPWSTR target_name, DWORD target_name_len, BYTE key_data[KEY_SIZE], - PCREDENTIALW *credentials, char *buffer, + PCREDENTIALW *credentials, char **buffer, DWORD *len, DWORD *count) { DWORD i; @@ -460,21 +771,135 @@ static DWORD registry_enumerate_credentials(HKEY hkeyMgr, LPCWSTR filter, if (buffer) { *len = sizeof(CREDENTIALW); - credentials[*count] = (PCREDENTIALW)buffer; + credentials[*count] = (PCREDENTIALW)*buffer; } else *len += sizeof(CREDENTIALW); ret = registry_read_credential(hkeyCred, buffer ? credentials[*count] : NULL, - key_data, buffer ? buffer + sizeof(CREDENTIALW) : NULL, + key_data, buffer ? *buffer + sizeof(CREDENTIALW) : NULL, len); RegCloseKey(hkeyCred); if (ret != ERROR_SUCCESS) break; - if (buffer) buffer += *len; + if (buffer) *buffer += *len; (*count)++; } return ret; } +#ifdef __APPLE__ +static DWORD mac_enumerate_credentials(LPCWSTR filter, PCREDENTIALW *credentials, + char *buffer, DWORD *len, DWORD *count) +{ + SecKeychainSearchRef search; + SecKeychainItemRef item; + OSStatus status; + Boolean saved_user_interaction_allowed; + DWORD ret; + + SecKeychainGetUserInteractionAllowed(&saved_user_interaction_allowed); + SecKeychainSetUserInteractionAllowed(false); + + status = SecKeychainSearchCreateFromAttributes(NULL, kSecInternetPasswordItemClass, NULL, &search); + if (status == noErr) + { + while (SecKeychainSearchCopyNext(search, &item) == noErr) + { + SecKeychainAttributeInfo info; + SecKeychainAttributeList *attr_list; + UInt32 info_tags[] = { kSecServerItemAttr }; + info.count = sizeof(info_tags)/sizeof(info_tags[0]); + info.tag = info_tags; + info.format = NULL; + status = SecKeychainItemCopyAttributesAndData(item, &info, NULL, &attr_list, NULL, NULL); + if (status != noErr) + { + WARN("SecKeychainItemCopyAttributesAndData returned status %ld\n", status); + continue; + } + if (buffer) + { + *len = sizeof(CREDENTIALW); + credentials[*count] = (PCREDENTIALW)buffer; + } + else + *len += sizeof(CREDENTIALW); + if (attr_list->count != 1 || attr_list->attr[0].tag != kSecServerItemAttr) continue; + TRACE("server item: %.*s\n", (int)attr_list->attr[0].length, (char *)attr_list->attr[0].data); + /* FIXME: filter based on attr_list->attr[0].data */ + SecKeychainItemFreeAttributesAndData(attr_list, NULL); + ret = mac_read_credential_from_item(item, FALSE, + buffer ? credentials[*count] : NULL, + buffer ? buffer + sizeof(CREDENTIALW) : NULL, + len); + CFRelease(item); + if (ret == ERROR_SUCCESS) + { + (*count)++; + if (buffer) buffer += *len; + } + } + CFRelease(search); + } + else + ERR("SecKeychainSearchCreateFromAttributes returned status %ld\n", status); + SecKeychainSetUserInteractionAllowed(saved_user_interaction_allowed); + return ERROR_SUCCESS; +} + +static DWORD mac_delete_credential(LPCWSTR TargetName) +{ + OSStatus status; + SecKeychainSearchRef search; + status = SecKeychainSearchCreateFromAttributes(NULL, kSecInternetPasswordItemClass, NULL, &search); + if (status == noErr) + { + SecKeychainItemRef item; + while (SecKeychainSearchCopyNext(search, &item) == noErr) + { + SecKeychainAttributeInfo info; + SecKeychainAttributeList *attr_list; + UInt32 info_tags[] = { kSecServerItemAttr }; + LPWSTR target_name; + INT str_len; + info.count = sizeof(info_tags)/sizeof(info_tags[0]); + info.tag = info_tags; + info.format = NULL; + status = SecKeychainItemCopyAttributesAndData(item, &info, NULL, &attr_list, NULL, NULL); + if (status != noErr) + { + WARN("SecKeychainItemCopyAttributesAndData returned status %ld\n", status); + continue; + } + if (attr_list->count != 1 || attr_list->attr[0].tag != kSecServerItemAttr) + { + CFRelease(item); + continue; + } + str_len = MultiByteToWideChar(CP_UTF8, 0, attr_list->attr[0].data, attr_list->attr[0].length, NULL, 0); + target_name = HeapAlloc(GetProcessHeap(), 0, (str_len + 1) * sizeof(WCHAR)); + MultiByteToWideChar(CP_UTF8, 0, attr_list->attr[0].data, attr_list->attr[0].length, target_name, str_len); + /* nul terminate */ + target_name[str_len] = '\0'; + if (strcmpiW(TargetName, target_name)) + { + CFRelease(item); + HeapFree(GetProcessHeap(), 0, target_name); + continue; + } + HeapFree(GetProcessHeap(), 0, target_name); + SecKeychainItemFreeAttributesAndData(attr_list, NULL); + SecKeychainItemDelete(item); + CFRelease(item); + CFRelease(search); + + return ERROR_SUCCESS; + } + CFRelease(search); + } + return ERROR_NOT_FOUND; +} +#endif + static void convert_PCREDENTIALW_to_PCREDENTIALA(const CREDENTIALW *CredentialW, PCREDENTIALA CredentialA, DWORD *len) { char *buffer = (char *)CredentialA + sizeof(CREDENTIALA); @@ -683,6 +1108,15 @@ BOOL WINAPI CredDeleteW(LPCWSTR TargetName, DWORD Type, DWORD Flags) return FALSE; } +#ifdef __APPLE__ + if (Type == CRED_TYPE_DOMAIN_PASSWORD) + { + ret = mac_delete_credential(TargetName); + if (ret == ERROR_SUCCESS) + return TRUE; + } +#endif + ret = open_cred_mgr_key(&hkeyMgr, TRUE); if (ret != ERROR_SUCCESS) { @@ -823,6 +1257,10 @@ BOOL WINAPI CredEnumerateW(LPCWSTR Filter, DWORD Flags, DWORD *Count, len = 0; ret = registry_enumerate_credentials(hkeyMgr, Filter, target_name, target_name_len, key_data, NULL, NULL, &len, Count); +#ifdef __APPLE__ + if (ret == ERROR_SUCCESS) + ret = mac_enumerate_credentials(Filter, NULL, NULL, &len, Count); +#endif if (ret == ERROR_SUCCESS && *Count == 0) ret = ERROR_NOT_FOUND; if (ret != ERROR_SUCCESS) @@ -844,8 +1282,13 @@ BOOL WINAPI CredEnumerateW(LPCWSTR Filter, DWORD Flags, DWORD *Count, *Count = 0; ret = registry_enumerate_credentials(hkeyMgr, Filter, target_name, target_name_len, key_data, - *Credentials, buffer, &len, + *Credentials, &buffer, &len, Count); +#ifdef __APPLE__ + if (ret == ERROR_SUCCESS) + ret = mac_enumerate_credentials(Filter, *Credentials, + buffer, &len, Count); +#endif } else ret = ERROR_OUTOFMEMORY; @@ -953,6 +1396,78 @@ BOOL WINAPI CredReadW(LPCWSTR TargetName, DWORD Type, DWORD Flags, PCREDENTIALW return FALSE; } +#ifdef __APPLE__ + if (Type == CRED_TYPE_DOMAIN_PASSWORD) + { + OSStatus status; + SecKeychainSearchRef search; + status = SecKeychainSearchCreateFromAttributes(NULL, kSecInternetPasswordItemClass, NULL, &search); + if (status == noErr) + { + SecKeychainItemRef item; + while (SecKeychainSearchCopyNext(search, &item) == noErr) + { + SecKeychainAttributeInfo info; + SecKeychainAttributeList *attr_list; + UInt32 info_tags[] = { kSecServerItemAttr }; + LPWSTR target_name; + INT str_len; + info.count = sizeof(info_tags)/sizeof(info_tags[0]); + info.tag = info_tags; + info.format = NULL; + status = SecKeychainItemCopyAttributesAndData(item, &info, NULL, &attr_list, NULL, NULL); + len = sizeof(**Credential); + if (status != noErr) + { + WARN("SecKeychainItemCopyAttributesAndData returned status %ld\n", status); + continue; + } + if (attr_list->count != 1 || attr_list->attr[0].tag != kSecServerItemAttr) + { + CFRelease(item); + continue; + } + str_len = MultiByteToWideChar(CP_UTF8, 0, attr_list->attr[0].data, attr_list->attr[0].length, NULL, 0); + target_name = HeapAlloc(GetProcessHeap(), 0, (str_len + 1) * sizeof(WCHAR)); + MultiByteToWideChar(CP_UTF8, 0, attr_list->attr[0].data, attr_list->attr[0].length, target_name, str_len); + /* nul terminate */ + target_name[str_len] = '\0'; + if (strcmpiW(TargetName, target_name)) + { + CFRelease(item); + HeapFree(GetProcessHeap(), 0, target_name); + continue; + } + HeapFree(GetProcessHeap(), 0, target_name); + SecKeychainItemFreeAttributesAndData(attr_list, NULL); + ret = mac_read_credential_from_item(item, TRUE, NULL, NULL, &len); + if (ret == ERROR_SUCCESS) + { + *Credential = HeapAlloc(GetProcessHeap(), 0, len); + if (*Credential) + { + len = sizeof(**Credential); + ret = mac_read_credential_from_item(item, TRUE, *Credential, + (char *)(*Credential + 1), &len); + } + else + ret = ERROR_OUTOFMEMORY; + CFRelease(item); + CFRelease(search); + if (ret != ERROR_SUCCESS) + { + SetLastError(ret); + return FALSE; + } + return TRUE; + } + CFRelease(item); + } + CFRelease(search); + } + } +#endif + ret = open_cred_mgr_key(&hkeyMgr, FALSE); if (ret != ERROR_SUCCESS) { @@ -1087,6 +1602,21 @@ BOOL WINAPI CredWriteW(PCREDENTIALW Credential, DWORD Flags) } } +#ifdef __APPLE__ + if (!Credential->AttributeCount && + Credential->Type == CRED_TYPE_DOMAIN_PASSWORD && + (Credential->Persist == CRED_PERSIST_LOCAL_MACHINE || Credential->Persist == CRED_PERSIST_ENTERPRISE)) + { + ret = mac_write_credential(Credential, Flags & CRED_PRESERVE_CREDENTIAL_BLOB); + if (ret != ERROR_SUCCESS) + { + SetLastError(ret); + return FALSE; + } + return TRUE; + } +#endif + ret = open_cred_mgr_key(&hkeyMgr, FALSE); if (ret != ERROR_SUCCESS) { -- GitLab