From be97da34accdf65aafaded60c3be1131a2b3cd97 Mon Sep 17 00:00:00 2001 From: Timo Kreuzer Date: Sun, 10 Mar 2019 22:19:12 +0100 Subject: [PATCH] [NETAPI32] Fix NetUserEnum to work on x64 The previous implementation used the resume_handle parameter to return a pointer to the active enumeration context, but resume_handle is a DWORD. To support 64 bit pointers, the enumeration context is inserted into a global linked list and given a unique 32 bit value as identifier for later lookup. The way the function is implemented, leaking a data structure while the MSDN description does not indicate that, seems a little questionable in general, but that is something that I leave to the original author to investigate. --- dll/win32/netapi32/netapi32.c | 2 + dll/win32/netapi32/netapi32.h | 3 + dll/win32/netapi32/user.c | 100 ++++++++++++++++++++++++++++++---- 3 files changed, 95 insertions(+), 10 deletions(-) diff --git a/dll/win32/netapi32/netapi32.c b/dll/win32/netapi32/netapi32.c index 5775aa46bd8..576da6a37aa 100644 --- a/dll/win32/netapi32/netapi32.c +++ b/dll/win32/netapi32/netapi32.c @@ -26,6 +26,8 @@ BOOL WINAPI DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) switch (fdwReason) { case DLL_PROCESS_ATTACH: + InitializeListHead(&g_EnumContextListHead); + InitializeCriticalSection(&g_EnumContextListLock); DisableThreadLibraryCalls(hinstDLL); NetBIOSInit(); NetBTInit(); diff --git a/dll/win32/netapi32/netapi32.h b/dll/win32/netapi32/netapi32.h index 50b5255d6ad..451865707ff 100644 --- a/dll/win32/netapi32/netapi32.h +++ b/dll/win32/netapi32/netapi32.h @@ -30,6 +30,9 @@ #include "nbnamecache.h" #include "netbios.h" +extern LIST_ENTRY g_EnumContextListHead; +extern CRITICAL_SECTION g_EnumContextListLock; + NET_API_STATUS WINAPI NetpNtStatusToApiStatus(NTSTATUS Status); diff --git a/dll/win32/netapi32/user.c b/dll/win32/netapi32/user.c index a867b786ec4..699ae839b8d 100644 --- a/dll/win32/netapi32/user.c +++ b/dll/win32/netapi32/user.c @@ -36,6 +36,9 @@ WINE_DEFAULT_DEBUG_CHANNEL(netapi32); typedef struct _ENUM_CONTEXT { + LIST_ENTRY ListLink; + ULONG EnumHandle; + SAM_HANDLE ServerHandle; SAM_HANDLE BuiltinDomainHandle; SAM_HANDLE AccountDomainHandle; @@ -50,6 +53,9 @@ typedef struct _ENUM_CONTEXT } ENUM_CONTEXT, *PENUM_CONTEXT; +LIST_ENTRY g_EnumContextListHead; +CRITICAL_SECTION g_EnumContextListLock; +LONG g_EnumContextHandle = 0; static ULONG @@ -2662,6 +2668,86 @@ done: return ApiStatus; } +static +NET_API_STATUS +AllocateEnumContext( + PENUM_CONTEXT *AllocatedEnumContext) +{ + NET_API_STATUS ApiStatus; + PENUM_CONTEXT EnumContext; + + /* Allocate the context structure */ + ApiStatus = NetApiBufferAllocate(sizeof(ENUM_CONTEXT), (PVOID*)&EnumContext); + if (ApiStatus != NERR_Success) + return ApiStatus; + + /* Initialize the fields */ + EnumContext->EnumerationContext = 0; + EnumContext->Buffer = NULL; + EnumContext->Count = 0; + EnumContext->Index = 0; + EnumContext->BuiltinDone = FALSE; + + /* Set a "unique" handle */ + EnumContext->EnumHandle = InterlockedIncrement(&g_EnumContextHandle); + if (EnumContext->EnumHandle == 0) + { + EnumContext->EnumHandle = InterlockedIncrement(&g_EnumContextHandle); + } + + /* Insert the context in the list */ + EnterCriticalSection(&g_EnumContextListLock); + InsertTailList(&g_EnumContextListHead, &EnumContext->ListLink); + LeaveCriticalSection(&g_EnumContextListLock); + + *AllocatedEnumContext = EnumContext; + return NERR_Success; +} + +static +VOID +FreeEnumContext( + PENUM_CONTEXT EnumContext) + +{ + /* Remove the context from the list */ + EnterCriticalSection(&g_EnumContextListLock); + RemoveEntryList(&EnumContext->ListLink); + LeaveCriticalSection(&g_EnumContextListLock); + + /* Free it */ + NetApiBufferFree(EnumContext); +} + +static +PENUM_CONTEXT +LookupEnumContext( + SAM_ENUMERATE_HANDLE EnumerationHandle) +{ + PENUM_CONTEXT FoundEnumContext = NULL; + PLIST_ENTRY ListEntry; + + /* Acquire the list lock */ + EnterCriticalSection(&g_EnumContextListLock); + + /* Search the list for the handle */ + for (ListEntry = g_EnumContextListHead.Flink; + ListEntry != &g_EnumContextListHead; + ListEntry = ListEntry->Flink) + { + PENUM_CONTEXT EnumContext = CONTAINING_RECORD(ListEntry, ENUM_CONTEXT, ListLink); + if (EnumContext->EnumHandle == EnumerationHandle) + { + FoundEnumContext = EnumContext; + break; + } + } + + /* Release the list lock */ + LeaveCriticalSection(&g_EnumContextListLock); + + return FoundEnumContext; +} /************************************************************ * NetUserEnum (NETAPI32.@) @@ -2699,20 +2785,14 @@ NetUserEnum(LPCWSTR servername, if (resume_handle != NULL && *resume_handle != 0) { - EnumContext = (PENUM_CONTEXT)*resume_handle; + EnumContext = LookupEnumContext(*resume_handle); } else { - ApiStatus = NetApiBufferAllocate(sizeof(ENUM_CONTEXT), (PVOID*)&EnumContext); + ApiStatus = AllocateEnumContext(&EnumContext); if (ApiStatus != NERR_Success) goto done; - EnumContext->EnumerationContext = 0; - EnumContext->Buffer = NULL; - EnumContext->Count = 0; - EnumContext->Index = 0; - EnumContext->BuiltinDone = FALSE; - Status = SamConnect((servername != NULL) ? &ServerName : NULL, &EnumContext->ServerHandle, SAM_SERVER_CONNECT | SAM_SERVER_LOOKUP_DOMAIN, @@ -2892,7 +2972,7 @@ done: SamFreeMemory(EnumContext->Buffer); } - NetApiBufferFree(EnumContext); + FreeEnumContext(EnumContext); EnumContext = NULL; } } @@ -2901,7 +2981,7 @@ done: SamCloseHandle(UserHandle); if (resume_handle != NULL) - *resume_handle = (DWORD_PTR)EnumContext; + *resume_handle = EnumContext ? EnumContext->EnumHandle : 0; *bufptr = (LPBYTE)Buffer;