advapi32: Enumerate host credentials through mountmgr.

Signed-off-by: Hans Leidekker <hans@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Hans Leidekker 2020-09-04 13:15:47 +02:00 committed by Alexandre Julliard
parent 3885b32bc8
commit 66baf15352
2 changed files with 115 additions and 297 deletions

View file

@ -3,7 +3,6 @@ MODULE = advapi32.dll
IMPORTLIB = advapi32
IMPORTS = kernelbase sechost
DELAYIMPORTS = rpcrt4
EXTRALIBS = $(SECURITY_LIBS)
C_SRCS = \
advapi.c \

View file

@ -22,12 +22,6 @@
#include <time.h>
#include <limits.h>
#ifdef __APPLE__
# include <Security/SecKeychain.h>
# include <Security/SecKeychainItem.h>
# include <Security/SecKeychainSearch.h>
#endif
#include "windef.h"
#include "winbase.h"
#include "winreg.h"
@ -237,189 +231,6 @@ 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)
{
int status;
UInt32 i, cred_blob_len;
void *cred_blob;
WCHAR *user = NULL;
BOOL user_name_present = FALSE;
SecKeychainAttributeInfo info;
SecKeychainAttributeList *attr_list;
UInt32 info_tags[] = { kSecServiceItemAttr, kSecAccountItemAttr,
kSecCommentItemAttr, kSecCreationDateItemAttr };
info.count = ARRAY_SIZE(info_tags);
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 %d\n", status);
return ERROR_NOT_FOUND;
}
for (i = 0; i < attr_list->count; i++)
if (attr_list->attr[i].tag == kSecAccountItemAttr && attr_list->attr[i].data)
{
user_name_present = TRUE;
break;
}
if (!user_name_present)
{
WARN("no kSecAccountItemAttr for item\n");
SecKeychainItemFreeAttributesAndData(attr_list, cred_blob);
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 kSecServiceItemAttr:
TRACE("kSecServiceItemAttr: %.*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 = heap_alloc((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 kSecCreationDateItemAttr:
TRACE("kSecCreationDateItemAttr: %.*s\n", (int)attr_list->attr[i].length,
(char *)attr_list->attr[i].data);
if (!attr_list->attr[i].data) continue;
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;
default:
FIXME("unhandled attribute %u\n", (unsigned)attr_list->attr[i].tag);
break;
}
}
if (user)
{
INT str_len;
if (buffer)
credential->UserName = (LPWSTR)buffer;
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));
}
}
heap_free(user);
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);
*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)
@ -736,98 +547,6 @@ static DWORD registry_enumerate_credentials(HKEY hkeyMgr, LPCWSTR filter,
return ret;
}
#ifdef __APPLE__
static BOOL mac_credential_matches_filter(void *data, UInt32 data_len, const WCHAR *filter)
{
int len;
WCHAR *target_name;
const WCHAR *p;
BOOL ret;
if (!filter) return TRUE;
len = MultiByteToWideChar(CP_UTF8, 0, data, data_len, NULL, 0);
if (!(target_name = heap_alloc((len + 1) * sizeof(WCHAR)))) return FALSE;
MultiByteToWideChar(CP_UTF8, 0, data, data_len, target_name, len);
target_name[len] = 0;
TRACE("comparing filter %s to target name %s\n", debugstr_w(filter), debugstr_w(target_name));
p = strchrW(filter, '*');
ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, filter,
(p && !p[1] ? p - filter : -1), target_name,
(p && !p[1] ? p - filter : -1)) == CSTR_EQUAL;
heap_free(target_name);
return ret;
}
static DWORD mac_enumerate_credentials(LPCWSTR filter, PCREDENTIALW *credentials,
char *buffer, DWORD *len, DWORD *count)
{
SecKeychainSearchRef search;
SecKeychainItemRef item;
int status;
Boolean saved_user_interaction_allowed;
DWORD ret;
SecKeychainGetUserInteractionAllowed(&saved_user_interaction_allowed);
SecKeychainSetUserInteractionAllowed(false);
status = SecKeychainSearchCreateFromAttributes(NULL, kSecGenericPasswordItemClass, NULL, &search);
if (status == noErr)
{
while (SecKeychainSearchCopyNext(search, &item) == noErr)
{
SecKeychainAttributeInfo info;
SecKeychainAttributeList *attr_list;
UInt32 info_tags[] = { kSecServiceItemAttr };
BOOL match;
info.count = ARRAY_SIZE(info_tags);
info.tag = info_tags;
info.format = NULL;
status = SecKeychainItemCopyAttributesAndData(item, &info, NULL, &attr_list, NULL, NULL);
if (status != noErr)
{
WARN("SecKeychainItemCopyAttributesAndData returned status %d\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 != kSecServiceItemAttr)
{
SecKeychainItemFreeAttributesAndData(attr_list, NULL);
continue;
}
TRACE("service item: %.*s\n", (int)attr_list->attr[0].length, (char *)attr_list->attr[0].data);
match = mac_credential_matches_filter(attr_list->attr[0].data, attr_list->attr[0].length, filter);
SecKeychainItemFreeAttributesAndData(attr_list, NULL);
if (!match) continue;
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 %d\n", status);
SecKeychainSetUserInteractionAllowed(saved_user_interaction_allowed);
return ERROR_SUCCESS;
}
#endif
/******************************************************************************
* convert_PCREDENTIALW_to_PCREDENTIALA [internal]
*
@ -1194,11 +913,113 @@ BOOL WINAPI CredEnumerateA(LPCSTR Filter, DWORD Flags, DWORD *Count,
return TRUE;
}
#define CRED_LIST_COUNT 16
#define CRED_DATA_SIZE 2048
static DWORD host_enumerate_credentials( const WCHAR *filter, CREDENTIALW **credentials, char *buf, DWORD *len, DWORD *count )
{
static const WCHAR emptyW[] = {0};
struct mountmgr_credential_list *list, *tmp;
DWORD i, j, ret, size, filter_size, offset = 0;
HANDLE mgr;
WCHAR *ptr;
if (filter) filter_size = (strlenW( filter ) + 1) * sizeof(WCHAR);
else
{
filter = emptyW;
filter_size = sizeof(emptyW);
}
mgr = CreateFileW( MOUNTMGR_DOS_DEVICE_NAME, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0 );
if (mgr == INVALID_HANDLE_VALUE) return GetLastError();
size = FIELD_OFFSET( struct mountmgr_credential_list, creds[CRED_LIST_COUNT] ) + filter_size + CRED_DATA_SIZE;
if (!(list = heap_alloc( size )))
{
CloseHandle( mgr );
return ERROR_OUTOFMEMORY;
}
for (;;)
{
list->filter_offset = sizeof(*list);
list->filter_size = filter_size;
ptr = (WCHAR *)((char *)list + list->filter_offset);
strcpyW( ptr, filter );
if (DeviceIoControl( mgr, IOCTL_MOUNTMGR_ENUMERATE_CREDENTIALS, list, size, list, size, NULL, NULL )) break;
if ((ret = GetLastError()) != ERROR_MORE_DATA) goto done;
size = list->size + filter_size;
if (!(tmp = heap_realloc( list, size )))
{
ret = ERROR_OUTOFMEMORY;
goto done;
}
list = tmp;
}
for (i = 0, j = *count; i < list->count; i++)
{
CREDENTIALW *cred = (CREDENTIALW *)(buf + offset);
offset += sizeof(*cred) + list->creds[i].targetname_size + list->creds[i].comment_size + list->creds[i].blob_size +
list->creds[i].username_size;
if (!buf) continue;
ptr = (WCHAR *)(cred + 1);
cred->Flags = 0;
cred->Type = CRED_TYPE_DOMAIN_PASSWORD;
cred->TargetName = ptr;
memcpy( cred->TargetName, (char *)&list->creds[i] + list->creds[i].targetname_offset, list->creds[i].targetname_size );
ptr += list->creds[i].targetname_size / sizeof(WCHAR);
if (list->creds[i].comment_size)
{
cred->Comment = ptr;
memcpy( cred->Comment, (char *)&list->creds[i] + list->creds[i].comment_offset, list->creds[i].comment_size );
ptr += list->creds[i].comment_size / sizeof(WCHAR);
}
else cred->Comment = NULL;
cred->LastWritten = list->creds[i].last_written;
if (list->creds[i].blob_size)
{
cred->CredentialBlobSize = list->creds[i].blob_size;
cred->CredentialBlob = (BYTE *)ptr;
memcpy( cred->CredentialBlob, (char *)&list->creds[i] + list->creds[i].blob_offset, list->creds[i].blob_size );
ptr += list->creds[i].blob_size / sizeof(WCHAR);
}
else
{
cred->CredentialBlobSize = 0;
cred->CredentialBlob = NULL;
}
cred->Persist = CRED_PERSIST_LOCAL_MACHINE;
cred->AttributeCount = 0;
cred->Attributes = NULL;
cred->TargetAlias = NULL;
if (list->creds[i].username_size)
{
cred->UserName = ptr;
memcpy( cred->UserName, (char *)&list->creds[i] + list->creds[i].username_offset, list->creds[i].username_size );
}
else cred->UserName = NULL;
if (credentials) credentials[j++] = cred;
}
*len += offset;
*count += list->count;
ret = ERROR_SUCCESS;
done:
heap_free( list );
CloseHandle( mgr );
return ret;
}
/******************************************************************************
* CredEnumerateW [ADVAPI32.@]
*/
BOOL WINAPI CredEnumerateW(LPCWSTR Filter, DWORD Flags, DWORD *Count,
PCREDENTIALW **Credentials)
BOOL WINAPI CredEnumerateW(LPCWSTR Filter, DWORD Flags, DWORD *Count, PCREDENTIALW **Credentials)
{
HKEY hkeyMgr;
DWORD ret;
@ -1252,10 +1073,11 @@ 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
{
ret = host_enumerate_credentials(Filter, NULL, NULL, &len, Count);
if (ret == ERROR_NOT_SUPPORTED) ret = ERROR_SUCCESS;
}
if (ret == ERROR_SUCCESS && *Count == 0)
ret = ERROR_NOT_FOUND;
if (ret != ERROR_SUCCESS)
@ -1275,18 +1097,15 @@ BOOL WINAPI CredEnumerateW(LPCWSTR Filter, DWORD Flags, DWORD *Count,
{
buffer += *Count * sizeof(PCREDENTIALW);
*Count = 0;
ret = registry_enumerate_credentials(hkeyMgr, Filter, target_name,
target_name_len, key_data,
*Credentials, &buffer, &len,
Count);
#ifdef __APPLE__
ret = registry_enumerate_credentials(hkeyMgr, Filter, target_name, target_name_len, key_data,
*Credentials, &buffer, &len, Count);
if (ret == ERROR_SUCCESS)
ret = mac_enumerate_credentials(Filter, *Credentials,
buffer, &len, Count);
#endif
{
ret = host_enumerate_credentials(Filter, *Credentials, buffer, &len, Count);
if (ret == ERROR_NOT_SUPPORTED) ret = ERROR_SUCCESS;
}
}
else
ret = ERROR_OUTOFMEMORY;
else ret = ERROR_OUTOFMEMORY;
}
heap_free(target_name);