[NETAPI] Implement NetGroupEnum
authorEric Kohl <eric.kohl@reactos.org>
Sat, 8 Sep 2018 18:55:13 +0000 (20:55 +0200)
committerEric Kohl <eric.kohl@reactos.org>
Sat, 8 Sep 2018 18:55:13 +0000 (20:55 +0200)
dll/win32/netapi32/CMakeLists.txt
dll/win32/netapi32/group.c
dll/win32/netapi32/group_new.c [new file with mode: 0644]

index c5066c0..d714bb2 100644 (file)
@@ -21,6 +21,7 @@ list(APPEND SOURCE
     display.c
     dssetup.c
     group.c
+    group_new.c
     local_group.c
     misc.c
     nbcmdqueue.c
index 92627ec..cac6be7 100644 (file)
@@ -86,26 +86,6 @@ NetGroupDelUser(IN LPCWSTR servername,
 }
 
 
-/************************************************************
- * NetGroupEnum  (NETAPI32.@)
- *
- */
-NET_API_STATUS
-WINAPI
-NetGroupEnum(IN LPCWSTR servername,
-             IN DWORD level,
-             OUT LPBYTE *bufptr,
-             IN DWORD prefmaxlen,
-             OUT LPDWORD entriesread,
-             OUT LPDWORD totalentries,
-             IN OUT PDWORD_PTR resume_handle)
-{
-    FIXME("(%s, %d, %p, %d, %p, %p, %p) stub!\n", debugstr_w(servername),
-          level, bufptr, prefmaxlen, entriesread, totalentries, resume_handle);
-    return ERROR_ACCESS_DENIED;
-}
-
-
 /************************************************************
  * NetGroupGetInfo  (NETAPI32.@)
  *
diff --git a/dll/win32/netapi32/group_new.c b/dll/win32/netapi32/group_new.c
new file mode 100644 (file)
index 0000000..5fa3831
--- /dev/null
@@ -0,0 +1,457 @@
+/*
+ * PROJECT:     ReactOS NetAPI DLL
+ * LICENSE:     GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
+ * PURPOSE:     SAM service group interface code
+ * COPYRIGHT:   Copyright 2018 Eric Kohl (eric.kohl@reactos.org)
+ */
+
+/* INCLUDES ******************************************************************/
+
+#include "netapi32.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(netapi32);
+
+typedef enum _ENUM_PHASE
+{
+    BuiltinPhase,
+    AccountPhase,
+    DonePhase
+} ENUM_PHASE;
+
+typedef struct _ENUM_CONTEXT
+{
+    SAM_HANDLE ServerHandle;
+    SAM_HANDLE DomainHandle;
+    SAM_HANDLE BuiltinDomainHandle;
+    SAM_HANDLE AccountDomainHandle;
+
+    SAM_ENUMERATE_HANDLE EnumerationContext;
+    PSAM_RID_ENUMERATION Buffer;
+    ULONG Returned;
+    ULONG Index;
+    ENUM_PHASE Phase;
+} ENUM_CONTEXT, *PENUM_CONTEXT;
+
+
+/* FUNCTIONS *****************************************************************/
+
+static
+NET_API_STATUS
+BuildGroupInfoBuffer(
+    _In_ PGROUP_GENERAL_INFORMATION GroupInfo,
+    _In_ DWORD Level,
+    _In_ DWORD GroupId,
+    _Out_ LPVOID *Buffer)
+{
+    PVOID GroupBuffer = NULL;
+    PGROUP_INFO_0 GroupInfo0;
+    PGROUP_INFO_1 GroupInfo1;
+    PGROUP_INFO_2 GroupInfo2;
+    PGROUP_INFO_3 GroupInfo3;
+    PWSTR Ptr;
+    ULONG Size = 0;
+    NET_API_STATUS ApiStatus = NERR_Success;
+
+    *Buffer = NULL;
+
+    switch (Level)
+    {
+        case 0:
+            Size = sizeof(GROUP_INFO_0) +
+                   GroupInfo->Name.Length + sizeof(WCHAR);
+            break;
+
+        case 1:
+            Size = sizeof(GROUP_INFO_1) +
+                   GroupInfo->Name.Length + sizeof(WCHAR) +
+                   GroupInfo->AdminComment.Length + sizeof(WCHAR);
+            break;
+
+        case 2:
+            Size = sizeof(GROUP_INFO_2) +
+                   GroupInfo->Name.Length + sizeof(WCHAR) +
+                   GroupInfo->AdminComment.Length + sizeof(WCHAR);
+            break;
+
+        case 3:
+            Size = sizeof(GROUP_INFO_3) +
+                   GroupInfo->Name.Length + sizeof(WCHAR) +
+                   GroupInfo->AdminComment.Length + sizeof(WCHAR);
+                   /* FIXME: Sid size */
+            break;
+
+        default:
+            ApiStatus = ERROR_INVALID_LEVEL;
+            goto done;
+    }
+
+    ApiStatus = NetApiBufferAllocate(Size, &GroupBuffer);
+    if (ApiStatus != NERR_Success)
+        goto done;
+
+    ZeroMemory(GroupBuffer, Size);
+
+    switch (Level)
+    {
+        case 0:
+            GroupInfo0 = (PGROUP_INFO_0)GroupBuffer;
+
+            Ptr = (PWSTR)((ULONG_PTR)GroupInfo0 + sizeof(LOCALGROUP_INFO_0));
+            GroupInfo0->grpi0_name = Ptr;
+
+            memcpy(GroupInfo0->grpi0_name,
+                   GroupInfo->Name.Buffer,
+                   GroupInfo->Name.Length);
+            GroupInfo0->grpi0_name[GroupInfo->Name.Length / sizeof(WCHAR)] = UNICODE_NULL;
+            break;
+
+        case 1:
+            GroupInfo1 = (PGROUP_INFO_1)GroupBuffer;
+
+            Ptr = (PWSTR)((ULONG_PTR)GroupInfo1 + sizeof(GROUP_INFO_1));
+            GroupInfo1->grpi1_name = Ptr;
+
+            memcpy(GroupInfo1->grpi1_name,
+                   GroupInfo->Name.Buffer,
+                   GroupInfo->Name.Length);
+            GroupInfo1->grpi1_name[GroupInfo->Name.Length / sizeof(WCHAR)] = UNICODE_NULL;
+
+            Ptr = (PWSTR)((ULONG_PTR)Ptr + GroupInfo->Name.Length + sizeof(WCHAR));
+            GroupInfo1->grpi1_comment = Ptr;
+
+            memcpy(GroupInfo1->grpi1_comment,
+                   GroupInfo->AdminComment.Buffer,
+                   GroupInfo->AdminComment.Length);
+            GroupInfo1->grpi1_comment[GroupInfo->AdminComment.Length / sizeof(WCHAR)] = UNICODE_NULL;
+            break;
+
+        case 2:
+            GroupInfo2 = (PGROUP_INFO_2)GroupBuffer;
+
+            Ptr = (PWSTR)((ULONG_PTR)GroupInfo2 + sizeof(GROUP_INFO_2));
+            GroupInfo2->grpi2_name = Ptr;
+
+            memcpy(GroupInfo2->grpi2_name,
+                   GroupInfo->Name.Buffer,
+                   GroupInfo->Name.Length);
+            GroupInfo2->grpi2_name[GroupInfo->Name.Length / sizeof(WCHAR)] = UNICODE_NULL;
+
+            Ptr = (PWSTR)((ULONG_PTR)Ptr + GroupInfo->Name.Length + sizeof(WCHAR));
+            GroupInfo2->grpi2_comment = Ptr;
+
+            memcpy(GroupInfo2->grpi2_comment,
+                   GroupInfo->AdminComment.Buffer,
+                   GroupInfo->AdminComment.Length);
+            GroupInfo2->grpi2_comment[GroupInfo->AdminComment.Length / sizeof(WCHAR)] = UNICODE_NULL;
+
+            GroupInfo2->grpi2_group_id = GroupId;
+
+            GroupInfo2->grpi2_attributes= GroupInfo->Attributes;
+            break;
+
+        case 3:
+            GroupInfo3 = (PGROUP_INFO_3)GroupBuffer;
+
+            Ptr = (PWSTR)((ULONG_PTR)GroupInfo3 + sizeof(GROUP_INFO_3));
+            GroupInfo3->grpi3_name = Ptr;
+
+            memcpy(GroupInfo3->grpi3_name,
+                   GroupInfo->Name.Buffer,
+                   GroupInfo->Name.Length);
+            GroupInfo3->grpi3_name[GroupInfo->Name.Length / sizeof(WCHAR)] = UNICODE_NULL;
+
+            Ptr = (PWSTR)((ULONG_PTR)Ptr + GroupInfo->Name.Length + sizeof(WCHAR));
+            GroupInfo3->grpi3_comment = Ptr;
+
+            memcpy(GroupInfo3->grpi3_comment,
+                   GroupInfo->AdminComment.Buffer,
+                   GroupInfo->AdminComment.Length);
+            GroupInfo3->grpi3_comment[GroupInfo->AdminComment.Length / sizeof(WCHAR)] = UNICODE_NULL;
+
+            GroupInfo3->grpi3_group_sid = NULL; /* FIXME */
+
+            GroupInfo3->grpi3_attributes= GroupInfo->Attributes;
+            break;
+    }
+
+done:
+    if (ApiStatus == NERR_Success)
+    {
+        *Buffer = GroupBuffer;
+    }
+    else
+    {
+        if (GroupBuffer != NULL)
+            NetApiBufferFree(GroupBuffer);
+    }
+
+    return ApiStatus;
+}
+
+
+static
+VOID
+FreeGroupInfo(
+    PGROUP_GENERAL_INFORMATION GroupInfo)
+{
+    if (GroupInfo->Name.Buffer != NULL)
+        SamFreeMemory(GroupInfo->Name.Buffer);
+
+    if (GroupInfo->AdminComment.Buffer != NULL)
+        SamFreeMemory(GroupInfo->AdminComment.Buffer);
+
+    SamFreeMemory(GroupInfo);
+}
+
+
+/* PUBLIC FUNCTIONS **********************************************************/
+
+NET_API_STATUS
+WINAPI
+NetGroupEnum(
+    _In_opt_ LPCWSTR servername,
+    _In_ DWORD level,
+    _Out_ LPBYTE *bufptr,
+    _In_ DWORD prefmaxlen,
+    _Out_ LPDWORD entriesread,
+    _Out_ LPDWORD totalentries,
+    _Inout_opt_ PDWORD_PTR resume_handle)
+{
+    UNICODE_STRING ServerName;
+    PSAM_RID_ENUMERATION CurrentGroup;
+    PENUM_CONTEXT EnumContext = NULL;
+    ULONG i;
+    SAM_HANDLE GroupHandle = NULL;
+    PGROUP_GENERAL_INFORMATION GroupInfo = NULL;
+    PVOID Buffer = NULL;
+    NET_API_STATUS ApiStatus = NERR_Success;
+    NTSTATUS Status = STATUS_SUCCESS;
+
+    TRACE("NetGroupEnum(%s, %d, %p, %d, %p, %p, %p)\n", debugstr_w(servername),
+          level, bufptr, prefmaxlen, entriesread, totalentries, resume_handle);
+
+    *entriesread = 0;
+    *totalentries = 0;
+    *bufptr = NULL;
+
+    if (servername != NULL)
+        RtlInitUnicodeString(&ServerName, servername);
+
+    if (resume_handle != NULL && *resume_handle != 0)
+    {
+        EnumContext = (PENUM_CONTEXT)*resume_handle;
+    }
+    else
+    {
+        ApiStatus = NetApiBufferAllocate(sizeof(ENUM_CONTEXT), (PVOID*)&EnumContext);
+        if (ApiStatus != NERR_Success)
+            goto done;
+
+        EnumContext->EnumerationContext = 0;
+        EnumContext->Buffer = NULL;
+        EnumContext->Returned = 0;
+        EnumContext->Index = 0;
+
+        Status = SamConnect((servername != NULL) ? &ServerName : NULL,
+                            &EnumContext->ServerHandle,
+                            SAM_SERVER_CONNECT | SAM_SERVER_LOOKUP_DOMAIN,
+                            NULL);
+        if (!NT_SUCCESS(Status))
+        {
+            ERR("SamConnect failed (Status %08lx)\n", Status);
+            ApiStatus = NetpNtStatusToApiStatus(Status);
+            goto done;
+        }
+
+        Status = OpenAccountDomain(EnumContext->ServerHandle,
+                                   (servername != NULL) ? &ServerName : NULL,
+                                   DOMAIN_LIST_ACCOUNTS | DOMAIN_LOOKUP,
+                                   &EnumContext->AccountDomainHandle);
+        if (!NT_SUCCESS(Status))
+        {
+            ERR("OpenAccountDomain failed (Status %08lx)\n", Status);
+            ApiStatus = NetpNtStatusToApiStatus(Status);
+            goto done;
+        }
+
+        Status = OpenBuiltinDomain(EnumContext->ServerHandle,
+                                   DOMAIN_LIST_ACCOUNTS | DOMAIN_LOOKUP,
+                                   &EnumContext->BuiltinDomainHandle);
+        if (!NT_SUCCESS(Status))
+        {
+            ERR("OpenBuiltinDomain failed (Status %08lx)\n", Status);
+            ApiStatus = NetpNtStatusToApiStatus(Status);
+            goto done;
+        }
+
+        EnumContext->Phase = AccountPhase; //BuiltinPhase;
+        EnumContext->DomainHandle = EnumContext->AccountDomainHandle; //BuiltinDomainHandle;
+    }
+
+
+//    while (TRUE)
+//    {
+        TRACE("EnumContext->Index: %lu\n", EnumContext->Index);
+        TRACE("EnumContext->Returned: %lu\n", EnumContext->Returned);
+
+        if (EnumContext->Index >= EnumContext->Returned)
+        {
+            TRACE("Calling SamEnumerateGroupsInDomain\n");
+
+            Status = SamEnumerateGroupsInDomain(EnumContext->DomainHandle,
+                                                &EnumContext->EnumerationContext,
+                                                (PVOID *)&EnumContext->Buffer,
+                                                prefmaxlen,
+                                                &EnumContext->Returned);
+
+            TRACE("SamEnumerateGroupsInDomain returned (Status %08lx)\n", Status);
+            if (!NT_SUCCESS(Status))
+            {
+                ERR("SamEnumerateAliasesInDomain failed (Status %08lx)\n", Status);
+                ApiStatus = NetpNtStatusToApiStatus(Status);
+                goto done;
+            }
+
+            if (Status == STATUS_MORE_ENTRIES)
+            {
+                ApiStatus = NERR_BufTooSmall;
+                goto done;
+            }
+        }
+
+        TRACE("EnumContext: %lu\n", EnumContext);
+        TRACE("EnumContext->Returned: %lu\n", EnumContext->Returned);
+        TRACE("EnumContext->Buffer: %p\n", EnumContext->Buffer);
+
+        /* Get a pointer to the current group */
+        CurrentGroup = &EnumContext->Buffer[EnumContext->Index];
+
+        TRACE("RID: %lu\n", CurrentGroup->RelativeId);
+
+        Status = SamOpenGroup(EnumContext->DomainHandle,
+                              GROUP_READ_INFORMATION,
+                              CurrentGroup->RelativeId,
+                              &GroupHandle);
+        if (!NT_SUCCESS(Status))
+        {
+            ERR("SamOpenGroup failed (Status %08lx)\n", Status);
+            ApiStatus = NetpNtStatusToApiStatus(Status);
+            goto done;
+        }
+
+        Status = SamQueryInformationGroup(GroupHandle,
+                                          GroupGeneralInformation,
+                                          (PVOID *)&GroupInfo);
+        if (!NT_SUCCESS(Status))
+        {
+            ERR("SamQueryInformationGroup failed (Status %08lx)\n", Status);
+            ApiStatus = NetpNtStatusToApiStatus(Status);
+            goto done;
+        }
+
+        SamCloseHandle(GroupHandle);
+        GroupHandle = NULL;
+
+        TRACE("Name: %S\n", GroupInfo->Name.Buffer);
+        TRACE("Comment: %S\n", GroupInfo->AdminComment.Buffer);
+
+        ApiStatus = BuildGroupInfoBuffer(GroupInfo,
+                                         level,
+                                         CurrentGroup->RelativeId,
+                                         &Buffer);
+        if (ApiStatus != NERR_Success)
+            goto done;
+
+        if (GroupInfo != NULL)
+        {
+            FreeGroupInfo(GroupInfo);
+            GroupInfo = NULL;
+        }
+
+        EnumContext->Index++;
+
+        (*entriesread)++;
+
+        if (EnumContext->Index == EnumContext->Returned)
+        {
+            switch (EnumContext->Phase)
+            {
+                case BuiltinPhase:
+                    EnumContext->Phase = AccountPhase;
+                    EnumContext->DomainHandle = EnumContext->AccountDomainHandle;
+                    EnumContext->EnumerationContext = 0;
+                    EnumContext->Index = 0;
+                    EnumContext->Returned = 0;
+
+                    if (EnumContext->Buffer != NULL)
+                    {
+                        for (i = 0; i < EnumContext->Returned; i++)
+                        {
+                            SamFreeMemory(EnumContext->Buffer[i].Name.Buffer);
+                        }
+
+                        SamFreeMemory(EnumContext->Buffer);
+                        EnumContext->Buffer = NULL;
+                    }
+                    break;
+
+                case AccountPhase:
+                case DonePhase:
+                    EnumContext->Phase = DonePhase;
+                    break;
+            }
+        }
+//    }
+
+done:
+    if (ApiStatus == NERR_Success && EnumContext != NULL && EnumContext->Phase != DonePhase)
+        ApiStatus = ERROR_MORE_DATA;
+
+    if (EnumContext != NULL)
+        *totalentries = EnumContext->Returned;
+
+    if (resume_handle == NULL || ApiStatus != ERROR_MORE_DATA)
+    {
+        if (EnumContext != NULL)
+        {
+            if (EnumContext->BuiltinDomainHandle != NULL)
+                SamCloseHandle(EnumContext->BuiltinDomainHandle);
+
+            if (EnumContext->AccountDomainHandle != NULL)
+                SamCloseHandle(EnumContext->AccountDomainHandle);
+
+            if (EnumContext->ServerHandle != NULL)
+                SamCloseHandle(EnumContext->ServerHandle);
+
+            if (EnumContext->Buffer != NULL)
+            {
+                for (i = 0; i < EnumContext->Returned; i++)
+                {
+                    SamFreeMemory(EnumContext->Buffer[i].Name.Buffer);
+                }
+
+                SamFreeMemory(EnumContext->Buffer);
+            }
+
+            NetApiBufferFree(EnumContext);
+            EnumContext = NULL;
+        }
+    }
+
+    if (GroupHandle != NULL)
+        SamCloseHandle(GroupHandle);
+
+    if (GroupInfo != NULL)
+        FreeGroupInfo(GroupInfo);
+
+    if (resume_handle != NULL)
+        *resume_handle = (DWORD_PTR)EnumContext;
+
+    *bufptr = (LPBYTE)Buffer;
+
+    TRACE("return %lu\n", ApiStatus);
+
+    return ApiStatus;
+}
+
+/* EOF */