[NETAPI32]
[reactos.git] / reactos / dll / win32 / netapi32 / local_group.c
index cff46d1..026beb6 100644 (file)
 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;
 
@@ -33,10 +41,24 @@ typedef struct _ENUM_CONTEXT
     PSAM_RID_ENUMERATION Buffer;
     ULONG Returned;
     ULONG Index;
-    BOOLEAN BuiltinDone;
+    ENUM_PHASE Phase;
 
 } ENUM_CONTEXT, *PENUM_CONTEXT;
 
+typedef struct _MEMBER_ENUM_CONTEXT
+{
+    SAM_HANDLE ServerHandle;
+    SAM_HANDLE DomainHandle;
+    SAM_HANDLE AliasHandle;
+    LSA_HANDLE LsaHandle;
+
+    PSID *Sids;
+    ULONG Count;
+    PLSA_REFERENCED_DOMAIN_LIST Domains;
+    PLSA_TRANSLATED_NAME Names;
+
+} MEMBER_ENUM_CONTEXT, *PMEMBER_ENUM_CONTEXT;
+
 
 static
 NET_API_STATUS
@@ -196,6 +218,115 @@ done:
 }
 
 
+static
+NET_API_STATUS
+BuildSidListFromDomainAndName(IN PUNICODE_STRING ServerName,
+                              IN PLOCALGROUP_MEMBERS_INFO_3 buf,
+                              IN ULONG EntryCount,
+                              OUT PLOCALGROUP_MEMBERS_INFO_0 *MemberList)
+{
+    OBJECT_ATTRIBUTES ObjectAttributes;
+    LSA_HANDLE LsaHandle = NULL;
+    PUNICODE_STRING NamesArray = NULL;
+    ULONG i;
+    PLSA_REFERENCED_DOMAIN_LIST Domains = NULL;
+    PLSA_TRANSLATED_SID Sids = NULL;
+    PLOCALGROUP_MEMBERS_INFO_0 MemberBuffer = NULL;
+    NET_API_STATUS ApiStatus = NERR_Success;
+    NTSTATUS Status = STATUS_SUCCESS;
+
+    ApiStatus = NetApiBufferAllocate(sizeof(UNICODE_STRING) * EntryCount,
+                                     (LPVOID*)&NamesArray);
+    if (ApiStatus != NERR_Success)
+    {
+        goto done;
+    }
+
+    for (i = 0; i < EntryCount; i++)
+    {
+        RtlInitUnicodeString(&NamesArray[i],
+                             buf[i].lgrmi3_domainandname);
+    }
+
+    InitializeObjectAttributes(&ObjectAttributes,
+                               NULL,
+                               0,
+                               0,
+                               NULL);
+
+    Status = LsaOpenPolicy(ServerName,
+                           (PLSA_OBJECT_ATTRIBUTES)&ObjectAttributes,
+                           POLICY_EXECUTE,
+                           &LsaHandle);
+    if (!NT_SUCCESS(Status))
+    {
+        ApiStatus = NetpNtStatusToApiStatus(Status);
+        goto done;
+    }
+
+    Status = LsaLookupNames(LsaHandle,
+                            EntryCount,
+                            NamesArray,
+                            &Domains,
+                            &Sids);
+    if (!NT_SUCCESS(Status))
+    {
+        ApiStatus = NetpNtStatusToApiStatus(Status);
+        goto done;
+    }
+
+    ApiStatus = NetApiBufferAllocate(sizeof(LOCALGROUP_MEMBERS_INFO_0) * EntryCount,
+                                     (LPVOID*)&MemberBuffer);
+    if (ApiStatus != NERR_Success)
+    {
+        goto done;
+    }
+
+    for (i = 0; i < EntryCount; i++)
+    {
+        ApiStatus = BuildSidFromSidAndRid(Domains->Domains[Sids[i].DomainIndex].Sid,
+                                          Sids[i].RelativeId,
+                                          &MemberBuffer[i].lgrmi0_sid);
+        if (ApiStatus != NERR_Success)
+        {
+            goto done;
+        }
+    }
+
+done:
+    if (ApiStatus != NERR_Success)
+    {
+        if (MemberBuffer != NULL)
+        {
+            for (i = 0; i < EntryCount; i++)
+            {
+                if (MemberBuffer[i].lgrmi0_sid != NULL)
+                    NetApiBufferFree(MemberBuffer[i].lgrmi0_sid);
+            }
+
+            NetApiBufferFree(MemberBuffer);
+            MemberBuffer = NULL;
+        }
+    }
+
+    if (Sids != NULL)
+        LsaFreeMemory(Sids);
+
+    if (Domains != NULL)
+        LsaFreeMemory(Domains);
+
+    if (LsaHandle != NULL)
+        LsaClose(LsaHandle);
+
+    if (NamesArray != NULL)
+        NetApiBufferFree(NamesArray);
+
+    *MemberList = MemberBuffer;
+
+    return ApiStatus;
+}
+
+
 /************************************************************
  * NetLocalGroupAdd  (NETAPI32.@)
  */
@@ -352,326 +483,1272 @@ done:
 /************************************************************
  *                NetLocalGroupAddMember  (NETAPI32.@)
  */
-NET_API_STATUS WINAPI NetLocalGroupAddMember(
+NET_API_STATUS
+WINAPI
+NetLocalGroupAddMember(
     LPCWSTR servername,
     LPCWSTR groupname,
     PSID membersid)
 {
-    FIXME("(%s %s %p) stub!\n", debugstr_w(servername),
+    LOCALGROUP_MEMBERS_INFO_0 Member;
+
+    TRACE("(%s %s %p)\n", debugstr_w(servername),
           debugstr_w(groupname), membersid);
-    return NERR_Success;
-}
 
-/************************************************************
- *                NetLocalGroupAddMembers  (NETAPI32.@)
- */
-NET_API_STATUS WINAPI NetLocalGroupAddMembers(
-    LPCWSTR servername,
-    LPCWSTR groupname,
-    DWORD level,
-    LPBYTE buf,
-    DWORD totalentries)
-{
-    FIXME("(%s %s %d %p %d) stub!\n", debugstr_w(servername),
-          debugstr_w(groupname), level, buf, totalentries);
-    return NERR_Success;
-}
+    Member.lgrmi0_sid = membersid;
 
-/************************************************************
- *                NetLocalGroupDel  (NETAPI32.@)
- */
-NET_API_STATUS WINAPI NetLocalGroupDel(
-    LPCWSTR servername,
-    LPCWSTR groupname)
-{
-    FIXME("(%s %s) stub!\n", debugstr_w(servername), debugstr_w(groupname));
-    return NERR_Success;
+    return NetLocalGroupAddMembers(servername,
+                                   groupname,
+                                   0,
+                                   (LPBYTE)&Member,
+                                   1);
 }
 
-/************************************************************
- *                NetLocalGroupDelMember  (NETAPI32.@)
- */
-NET_API_STATUS WINAPI NetLocalGroupDelMember(
-    LPCWSTR servername,
-    LPCWSTR groupname,
-    PSID membersid)
-{
-    FIXME("(%s %s %p) stub!\n", debugstr_w(servername),
-          debugstr_w(groupname), membersid);
-    return NERR_Success;
-}
 
 /************************************************************
- *                NetLocalGroupDelMembers  (NETAPI32.@)
+ *                NetLocalGroupAddMembers  (NETAPI32.@)
  */
-NET_API_STATUS WINAPI NetLocalGroupDelMembers(
+NET_API_STATUS
+WINAPI
+NetLocalGroupAddMembers(
     LPCWSTR servername,
     LPCWSTR groupname,
     DWORD level,
     LPBYTE buf,
     DWORD totalentries)
-{
-    FIXME("(%s %s %d %p %d) stub!\n", debugstr_w(servername),
-          debugstr_w(groupname), level, buf, totalentries);
-    return NERR_Success;
-}
-
-/************************************************************
- *                NetLocalGroupEnum  (NETAPI32.@)
- */
-NET_API_STATUS WINAPI NetLocalGroupEnum(
-    LPCWSTR servername,
-    DWORD level,
-    LPBYTE* bufptr,
-    DWORD prefmaxlen,
-    LPDWORD entriesread,
-    LPDWORD totalentries,
-    PDWORD_PTR resumehandle)
 {
     UNICODE_STRING ServerName;
-    PSAM_RID_ENUMERATION CurrentAlias;
-    PENUM_CONTEXT EnumContext = NULL;
-    ULONG i;
+    UNICODE_STRING AliasName;
+    SAM_HANDLE ServerHandle = NULL;
+    SAM_HANDLE DomainHandle = NULL;
     SAM_HANDLE AliasHandle = NULL;
-    PALIAS_GENERAL_INFORMATION AliasInfo = NULL;
-    LPVOID Buffer = NULL;
+    PLOCALGROUP_MEMBERS_INFO_0 MemberList = NULL;
+    ULONG i;
     NET_API_STATUS ApiStatus = NERR_Success;
     NTSTATUS Status = STATUS_SUCCESS;
 
-    TRACE("(%s %d %p %d %p %p %p) stub!\n", debugstr_w(servername),
-          level, bufptr, prefmaxlen, entriesread, totalentries, resumehandle);
-
-    *entriesread = 0;
-    *totalentries = 0;
-    *bufptr = NULL;
+    TRACE("(%s %s %d %p %d)\n", debugstr_w(servername),
+          debugstr_w(groupname), level, buf, totalentries);
 
     if (servername != NULL)
         RtlInitUnicodeString(&ServerName, servername);
 
-    if (resumehandle != NULL && *resumehandle != 0)
+    RtlInitUnicodeString(&AliasName, groupname);
+
+    switch (level)
     {
-        EnumContext = (PENUM_CONTEXT)*resumehandle;
+        case 0:
+            MemberList = (PLOCALGROUP_MEMBERS_INFO_0)buf;
+            break;
+
+        case 3:
+            Status = BuildSidListFromDomainAndName((servername != NULL) ? &ServerName : NULL,
+                                                   (PLOCALGROUP_MEMBERS_INFO_3)buf,
+                                                   totalentries,
+                                                   &MemberList);
+            if (!NT_SUCCESS(Status))
+            {
+                ERR("BuildSidListFromDomainAndName failed (Status %08lx)\n", Status);
+                ApiStatus = NetpNtStatusToApiStatus(Status);
+                goto done;
+            }
+            break;
+
+        default:
+            ApiStatus = ERROR_INVALID_LEVEL;
+            goto done;
     }
-    else
+
+    /* Connect to the SAM Server */
+    Status = SamConnect((servername != NULL) ? &ServerName : NULL,
+                        &ServerHandle,
+                        SAM_SERVER_CONNECT | SAM_SERVER_LOOKUP_DOMAIN,
+                        NULL);
+    if (!NT_SUCCESS(Status))
     {
-        ApiStatus = NetApiBufferAllocate(sizeof(ENUM_CONTEXT), (PVOID*)&EnumContext);
-        if (ApiStatus != NERR_Success)
-            goto done;
+        ERR("SamConnect failed (Status %08lx)\n", Status);
+        ApiStatus = NetpNtStatusToApiStatus(Status);
+        goto done;
+    }
 
-        EnumContext->EnumerationContext = 0;
-        EnumContext->Buffer = NULL;
-        EnumContext->Returned = 0;
-        EnumContext->Index = 0;
-        EnumContext->BuiltinDone = FALSE;
+    /* Open the Builtin Domain */
+    Status = OpenBuiltinDomain(ServerHandle,
+                               DOMAIN_LOOKUP,
+                               &DomainHandle);
+    if (!NT_SUCCESS(Status))
+    {
+        ERR("OpenBuiltinDomain failed (Status %08lx)\n", Status);
+        ApiStatus = NetpNtStatusToApiStatus(Status);
+        goto done;
+    }
 
-        Status = SamConnect((servername != NULL) ? &ServerName : NULL,
-                            &EnumContext->ServerHandle,
-                            SAM_SERVER_CONNECT | SAM_SERVER_LOOKUP_DOMAIN,
-                            NULL);
+    /* Open the alias account in the builtin domain */
+    ApiStatus = OpenAliasByName(DomainHandle,
+                                &AliasName,
+                                ALIAS_ADD_MEMBER,
+                                &AliasHandle);
+    if (ApiStatus != NERR_Success && ApiStatus != ERROR_NONE_MAPPED)
+    {
+        ERR("OpenAliasByName failed (ApiStatus %lu)\n", ApiStatus);
+        goto done;
+    }
+
+    if (AliasHandle == NULL)
+    {
+        if (DomainHandle != NULL)
+            SamCloseHandle(DomainHandle);
+
+        /* Open the Acount Domain */
+        Status = OpenAccountDomain(ServerHandle,
+                                   (servername != NULL) ? &ServerName : NULL,
+                                   DOMAIN_LOOKUP,
+                                   &DomainHandle);
         if (!NT_SUCCESS(Status))
         {
-            ERR("SamConnect failed (Status %08lx)\n", Status);
+            ERR("OpenAccountDomain 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))
+        /* Open the alias account in the account domain */
+        ApiStatus = OpenAliasByName(DomainHandle,
+                                    &AliasName,
+                                    ALIAS_ADD_MEMBER,
+                                    &AliasHandle);
+        if (ApiStatus != NERR_Success)
         {
-            ERR("OpenAccountDomain failed (Status %08lx)\n", Status);
-            ApiStatus = NetpNtStatusToApiStatus(Status);
+            ERR("OpenAliasByName failed (ApiStatus %lu)\n", ApiStatus);
+            if (ApiStatus == ERROR_NONE_MAPPED)
+                ApiStatus = NERR_GroupNotFound;
             goto done;
         }
+    }
 
-        Status = OpenBuiltinDomain(EnumContext->ServerHandle,
-                                   DOMAIN_LIST_ACCOUNTS | DOMAIN_LOOKUP,
-                                   &EnumContext->BuiltinDomainHandle);
+    /* Add new members to the alias */
+    for (i = 0; i < totalentries; i++)
+    {
+        Status = SamAddMemberToAlias(AliasHandle,
+                                     MemberList[i].lgrmi0_sid);
         if (!NT_SUCCESS(Status))
         {
-            ERR("OpenBuiltinDomain failed (Status %08lx)\n", Status);
+            ERR("SamAddMemberToAlias failed (Status %lu)\n", Status);
             ApiStatus = NetpNtStatusToApiStatus(Status);
             goto done;
         }
     }
 
-
-//    while (TRUE)
-//    {
-        TRACE("EnumContext->Index: %lu\n", EnumContext->Index);
-        TRACE("EnumContext->Returned: %lu\n", EnumContext->Returned);
-
-        if (EnumContext->Index >= EnumContext->Returned)
+done:
+    if (level == 3 && MemberList != NULL)
+    {
+        for (i = 0; i < totalentries; i++)
         {
-//            if (EnumContext->BuiltinDone == TRUE)
-//            {
-//                ApiStatus = NERR_Success;
-//                goto done;
-//            }
+            if (MemberList[i].lgrmi0_sid != NULL)
+                NetApiBufferFree(MemberList[i].lgrmi0_sid);
+        }
 
-            TRACE("Calling SamEnumerateAliasesInDomain\n");
+        NetApiBufferFree(MemberList);
+    }
 
-            Status = SamEnumerateAliasesInDomain(EnumContext->BuiltinDomainHandle,
-                                                 &EnumContext->EnumerationContext,
-                                                 (PVOID *)&EnumContext->Buffer,
-                                                 prefmaxlen,
-                                                 &EnumContext->Returned);
+    if (AliasHandle != NULL)
+        SamCloseHandle(AliasHandle);
 
-            TRACE("SamEnumerateAliasesInDomain returned (Status %08lx)\n", Status);
-            if (!NT_SUCCESS(Status))
-            {
-                ERR("SamEnumerateAliasesInDomain failed (Status %08lx)\n", Status);
-                ApiStatus = NetpNtStatusToApiStatus(Status);
-                goto done;
-            }
+    if (DomainHandle != NULL)
+        SamCloseHandle(DomainHandle);
 
-            if (Status == STATUS_MORE_ENTRIES)
-            {
+    if (ServerHandle != NULL)
+        SamCloseHandle(ServerHandle);
+
+    return ApiStatus;
+}
+
+
+/************************************************************
+ *                NetLocalGroupDel  (NETAPI32.@)
+ */
+NET_API_STATUS
+WINAPI
+NetLocalGroupDel(
+    LPCWSTR servername,
+    LPCWSTR groupname)
+{
+    UNICODE_STRING ServerName;
+    UNICODE_STRING GroupName;
+    SAM_HANDLE ServerHandle = NULL;
+    SAM_HANDLE DomainHandle = NULL;
+    SAM_HANDLE AliasHandle = NULL;
+    NET_API_STATUS ApiStatus = NERR_Success;
+    NTSTATUS Status = STATUS_SUCCESS;
+
+    TRACE("(%s %s)\n", debugstr_w(servername), debugstr_w(groupname));
+
+    if (servername != NULL)
+        RtlInitUnicodeString(&ServerName, servername);
+
+    RtlInitUnicodeString(&GroupName, groupname);
+
+    /* Connect to the SAM Server */
+    Status = SamConnect((servername != NULL) ? &ServerName : NULL,
+                        &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;
+    }
+
+    /* Open the Builtin Domain */
+    Status = OpenBuiltinDomain(ServerHandle,
+                               DOMAIN_LOOKUP,
+                               &DomainHandle);
+    if (!NT_SUCCESS(Status))
+    {
+        ERR("OpenBuiltinDomain failed (Status %08lx)\n", Status);
+        ApiStatus = NetpNtStatusToApiStatus(Status);
+        goto done;
+    }
+
+    /* Open the alias account in the builtin domain */
+    ApiStatus = OpenAliasByName(DomainHandle,
+                                &GroupName,
+                                DELETE,
+                                &AliasHandle);
+    if (ApiStatus != NERR_Success && ApiStatus != ERROR_NONE_MAPPED)
+    {
+        TRACE("OpenAliasByName failed (ApiStatus %lu)\n", ApiStatus);
+        goto done;
+    }
+
+    if (AliasHandle == NULL)
+    {
+        if (DomainHandle != NULL)
+        {
+            SamCloseHandle(DomainHandle);
+            DomainHandle = NULL;
+        }
+
+        /* Open the Acount Domain */
+        Status = OpenAccountDomain(ServerHandle,
+                                   (servername != NULL) ? &ServerName : NULL,
+                                   DOMAIN_LOOKUP,
+                                   &DomainHandle);
+        if (!NT_SUCCESS(Status))
+        {
+            ERR("OpenAccountDomain failed (Status %08lx)\n", Status);
+            ApiStatus = NetpNtStatusToApiStatus(Status);
+            goto done;
+        }
+
+        /* Open the alias account in the account domain */
+        ApiStatus = OpenAliasByName(DomainHandle,
+                                    &GroupName,
+                                    DELETE,
+                                    &AliasHandle);
+        if (ApiStatus != NERR_Success)
+        {
+            ERR("OpenAliasByName failed (ApiStatus %lu)\n", ApiStatus);
+            if (ApiStatus == ERROR_NONE_MAPPED)
+                ApiStatus = NERR_GroupNotFound;
+            goto done;
+        }
+    }
+
+    /* Delete the alias */
+    Status = SamDeleteAlias(AliasHandle);
+    if (!NT_SUCCESS(Status))
+    {
+        ERR("SamDeleteAlias failed (Status %08lx)\n", Status);
+        ApiStatus = NetpNtStatusToApiStatus(Status);
+        goto done;
+    }
+
+done:
+    if (AliasHandle != NULL)
+        SamCloseHandle(AliasHandle);
+
+    if (DomainHandle != NULL)
+        SamCloseHandle(DomainHandle);
+
+    if (ServerHandle != NULL)
+        SamCloseHandle(ServerHandle);
+
+    return ApiStatus;
+}
+
+
+/************************************************************
+ *                NetLocalGroupDelMember  (NETAPI32.@)
+ */
+NET_API_STATUS
+WINAPI
+NetLocalGroupDelMember(
+    LPCWSTR servername,
+    LPCWSTR groupname,
+    PSID membersid)
+{
+    LOCALGROUP_MEMBERS_INFO_0 Member;
+
+    TRACE("(%s %s %p)\n", debugstr_w(servername),
+          debugstr_w(groupname), membersid);
+
+    Member.lgrmi0_sid = membersid;
+
+    return NetLocalGroupDelMembers(servername,
+                                   groupname,
+                                   0,
+                                   (LPBYTE)&Member,
+                                   1);
+}
+
+
+/************************************************************
+ *                NetLocalGroupDelMembers  (NETAPI32.@)
+ */
+NET_API_STATUS
+WINAPI
+NetLocalGroupDelMembers(
+    LPCWSTR servername,
+    LPCWSTR groupname,
+    DWORD level,
+    LPBYTE buf,
+    DWORD totalentries)
+{
+    UNICODE_STRING ServerName;
+    UNICODE_STRING AliasName;
+    SAM_HANDLE ServerHandle = NULL;
+    SAM_HANDLE DomainHandle = NULL;
+    SAM_HANDLE AliasHandle = NULL;
+    PLOCALGROUP_MEMBERS_INFO_0 MemberList = NULL;
+    ULONG i;
+    NET_API_STATUS ApiStatus = NERR_Success;
+    NTSTATUS Status = STATUS_SUCCESS;
+
+    TRACE("(%s %s %d %p %d)\n", debugstr_w(servername),
+          debugstr_w(groupname), level, buf, totalentries);
+
+    if (servername != NULL)
+        RtlInitUnicodeString(&ServerName, servername);
+
+    RtlInitUnicodeString(&AliasName, groupname);
+
+    switch (level)
+    {
+        case 0:
+            MemberList = (PLOCALGROUP_MEMBERS_INFO_0)buf;
+            break;
+
+        case 3:
+            Status = BuildSidListFromDomainAndName((servername != NULL) ? &ServerName : NULL,
+                                                   (PLOCALGROUP_MEMBERS_INFO_3)buf,
+                                                   totalentries,
+                                                   &MemberList);
+            if (!NT_SUCCESS(Status))
+            {
+                ERR("BuildSidListFromDomainAndName failed (Status %08lx)\n", Status);
+                ApiStatus = NetpNtStatusToApiStatus(Status);
+                goto done;
+            }
+            break;
+
+        default:
+            ApiStatus = ERROR_INVALID_LEVEL;
+            goto done;
+    }
+
+    /* Connect to the SAM Server */
+    Status = SamConnect((servername != NULL) ? &ServerName : NULL,
+                        &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;
+    }
+
+    /* Open the Builtin Domain */
+    Status = OpenBuiltinDomain(ServerHandle,
+                               DOMAIN_LOOKUP,
+                               &DomainHandle);
+    if (!NT_SUCCESS(Status))
+    {
+        ERR("OpenBuiltinDomain failed (Status %08lx)\n", Status);
+        ApiStatus = NetpNtStatusToApiStatus(Status);
+        goto done;
+    }
+
+    /* Open the alias account in the builtin domain */
+    ApiStatus = OpenAliasByName(DomainHandle,
+                                &AliasName,
+                                ALIAS_REMOVE_MEMBER,
+                                &AliasHandle);
+    if (ApiStatus != NERR_Success && ApiStatus != ERROR_NONE_MAPPED)
+    {
+        ERR("OpenAliasByName failed (ApiStatus %lu)\n", ApiStatus);
+        goto done;
+    }
+
+    if (AliasHandle == NULL)
+    {
+        if (DomainHandle != NULL)
+            SamCloseHandle(DomainHandle);
+
+        /* Open the Acount Domain */
+        Status = OpenAccountDomain(ServerHandle,
+                                   (servername != NULL) ? &ServerName : NULL,
+                                   DOMAIN_LOOKUP,
+                                   &DomainHandle);
+        if (!NT_SUCCESS(Status))
+        {
+            ERR("OpenAccountDomain failed (Status %08lx)\n", Status);
+            ApiStatus = NetpNtStatusToApiStatus(Status);
+            goto done;
+        }
+
+        /* Open the alias account in the account domain */
+        ApiStatus = OpenAliasByName(DomainHandle,
+                                    &AliasName,
+                                    ALIAS_REMOVE_MEMBER,
+                                    &AliasHandle);
+        if (ApiStatus != NERR_Success)
+        {
+            ERR("OpenAliasByName failed (ApiStatus %lu)\n", ApiStatus);
+            if (ApiStatus == ERROR_NONE_MAPPED)
+                ApiStatus = NERR_GroupNotFound;
+            goto done;
+        }
+    }
+
+    /* Remove members from the alias */
+    for (i = 0; i < totalentries; i++)
+    {
+        Status = SamRemoveMemberFromAlias(AliasHandle,
+                                          MemberList[i].lgrmi0_sid);
+        if (!NT_SUCCESS(Status))
+        {
+            ERR("SamAddMemberToAlias failed (Status %lu)\n", Status);
+            ApiStatus = NetpNtStatusToApiStatus(Status);
+            goto done;
+        }
+    }
+
+done:
+    if (level == 3 && MemberList != NULL)
+    {
+        for (i = 0; i < totalentries; i++)
+        {
+            if (MemberList[i].lgrmi0_sid != NULL)
+                NetApiBufferFree(MemberList[i].lgrmi0_sid);
+        }
+
+        NetApiBufferFree(MemberList);
+    }
+
+    if (AliasHandle != NULL)
+        SamCloseHandle(AliasHandle);
+
+    if (DomainHandle != NULL)
+        SamCloseHandle(DomainHandle);
+
+    if (ServerHandle != NULL)
+        SamCloseHandle(ServerHandle);
+
+    return ApiStatus;
+}
+
+
+/************************************************************
+ *                NetLocalGroupEnum  (NETAPI32.@)
+ */
+NET_API_STATUS
+WINAPI
+NetLocalGroupEnum(
+    LPCWSTR servername,
+    DWORD level,
+    LPBYTE* bufptr,
+    DWORD prefmaxlen,
+    LPDWORD entriesread,
+    LPDWORD totalentries,
+    PDWORD_PTR resumehandle)
+{
+    UNICODE_STRING ServerName;
+    PSAM_RID_ENUMERATION CurrentAlias;
+    PENUM_CONTEXT EnumContext = NULL;
+    ULONG i;
+    SAM_HANDLE AliasHandle = NULL;
+    PALIAS_GENERAL_INFORMATION AliasInfo = NULL;
+    LPVOID Buffer = NULL;
+    NET_API_STATUS ApiStatus = NERR_Success;
+    NTSTATUS Status = STATUS_SUCCESS;
+
+    TRACE("(%s %d %p %d %p %p %p) stub!\n", debugstr_w(servername),
+          level, bufptr, prefmaxlen, entriesread, totalentries, resumehandle);
+
+    *entriesread = 0;
+    *totalentries = 0;
+    *bufptr = NULL;
+
+    if (servername != NULL)
+        RtlInitUnicodeString(&ServerName, servername);
+
+    if (resumehandle != NULL && *resumehandle != 0)
+    {
+        EnumContext = (PENUM_CONTEXT)*resumehandle;
+    }
+    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 = BuiltinPhase;
+        EnumContext->DomainHandle = EnumContext->BuiltinDomainHandle;
+    }
+
+
+//    while (TRUE)
+//    {
+        TRACE("EnumContext->Index: %lu\n", EnumContext->Index);
+        TRACE("EnumContext->Returned: %lu\n", EnumContext->Returned);
+
+        if (EnumContext->Index >= EnumContext->Returned)
+        {
+            TRACE("Calling SamEnumerateAliasesInDomain\n");
+
+            Status = SamEnumerateAliasesInDomain(EnumContext->DomainHandle,
+                                                 &EnumContext->EnumerationContext,
+                                                 (PVOID *)&EnumContext->Buffer,
+                                                 prefmaxlen,
+                                                 &EnumContext->Returned);
+
+            TRACE("SamEnumerateAliasesInDomain 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;
             }
-            else
+        }
+
+        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 alias */
+        CurrentAlias = &EnumContext->Buffer[EnumContext->Index];
+
+        TRACE("RID: %lu\n", CurrentAlias->RelativeId);
+
+        Status = SamOpenAlias(EnumContext->DomainHandle,
+                              ALIAS_READ_INFORMATION,
+                              CurrentAlias->RelativeId,
+                              &AliasHandle);
+        if (!NT_SUCCESS(Status))
+        {
+            ERR("SamOpenAlias failed (Status %08lx)\n", Status);
+            ApiStatus = NetpNtStatusToApiStatus(Status);
+            goto done;
+        }
+
+        Status = SamQueryInformationAlias(AliasHandle,
+                                          AliasGeneralInformation,
+                                          (PVOID *)&AliasInfo);
+        if (!NT_SUCCESS(Status))
+        {
+            ERR("SamQueryInformationAlias failed (Status %08lx)\n", Status);
+            ApiStatus = NetpNtStatusToApiStatus(Status);
+            goto done;
+        }
+
+        SamCloseHandle(AliasHandle);
+        AliasHandle = NULL;
+
+        TRACE("Name: %S\n", AliasInfo->Name.Buffer);
+        TRACE("Comment: %S\n", AliasInfo->AdminComment.Buffer);
+
+        ApiStatus = BuildAliasInfoBuffer(AliasInfo,
+                                         level,
+                                         &Buffer);
+        if (ApiStatus != NERR_Success)
+            goto done;
+
+        if (AliasInfo != NULL)
+        {
+            FreeAliasInfo(AliasInfo);
+            AliasInfo = 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->Phase != DonePhase)
+        ApiStatus = ERROR_MORE_DATA;
+
+    if (EnumContext != NULL)
+        *totalentries = EnumContext->Returned;
+
+    if (resumehandle == 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 (AliasHandle != NULL)
+        SamCloseHandle(AliasHandle);
+
+    if (AliasInfo != NULL)
+        FreeAliasInfo(AliasInfo);
+
+    if (resumehandle != NULL)
+        *resumehandle = (DWORD_PTR)EnumContext;
+
+    *bufptr = (LPBYTE)Buffer;
+
+    TRACE ("return %lu\n", ApiStatus);
+
+    return ApiStatus;
+}
+
+
+/************************************************************
+ * NetLocalGroupGetInfo  (NETAPI32.@)
+ */
+NET_API_STATUS
+WINAPI
+NetLocalGroupGetInfo(
+    LPCWSTR servername,
+    LPCWSTR groupname,
+    DWORD level,
+    LPBYTE* bufptr)
+{
+    UNICODE_STRING ServerName;
+    UNICODE_STRING GroupName;
+    SAM_HANDLE ServerHandle = NULL;
+    SAM_HANDLE DomainHandle = NULL;
+    SAM_HANDLE AliasHandle = NULL;
+    PALIAS_GENERAL_INFORMATION AliasInfo = NULL;
+    LPVOID Buffer = NULL;
+    NET_API_STATUS ApiStatus = NERR_Success;
+    NTSTATUS Status = STATUS_SUCCESS;
+
+    TRACE("(%s %s %d %p) stub!\n", debugstr_w(servername),
+          debugstr_w(groupname), level, bufptr);
+
+    if (servername != NULL)
+        RtlInitUnicodeString(&ServerName, servername);
+
+    RtlInitUnicodeString(&GroupName, groupname);
+
+    /* Connect to the SAM Server */
+    Status = SamConnect((servername != NULL) ? &ServerName : NULL,
+                        &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;
+    }
+
+    /* Open the Builtin Domain */
+    Status = OpenBuiltinDomain(ServerHandle,
+                               DOMAIN_LOOKUP,
+                               &DomainHandle);
+    if (!NT_SUCCESS(Status))
+    {
+        ERR("OpenBuiltinDomain failed (Status %08lx)\n", Status);
+        ApiStatus = NetpNtStatusToApiStatus(Status);
+        goto done;
+    }
+
+    /* Open the alias account in the builtin domain */
+    ApiStatus = OpenAliasByName(DomainHandle,
+                                &GroupName,
+                                ALIAS_READ_INFORMATION,
+                                &AliasHandle);
+    if (ApiStatus != NERR_Success && ApiStatus != ERROR_NONE_MAPPED)
+    {
+        ERR("OpenAliasByName failed (ApiStatus %lu)\n", ApiStatus);
+        goto done;
+    }
+
+    if (AliasHandle == NULL)
+    {
+        if (DomainHandle != NULL)
+            SamCloseHandle(DomainHandle);
+
+        /* Open the Acount Domain */
+        Status = OpenAccountDomain(ServerHandle,
+                                   (servername != NULL) ? &ServerName : NULL,
+                                   DOMAIN_LOOKUP,
+                                   &DomainHandle);
+        if (!NT_SUCCESS(Status))
+        {
+            ERR("OpenAccountDomain failed (Status %08lx)\n", Status);
+            ApiStatus = NetpNtStatusToApiStatus(Status);
+            goto done;
+        }
+
+        /* Open the alias account in the account domain */
+        ApiStatus = OpenAliasByName(DomainHandle,
+                                    &GroupName,
+                                    ALIAS_READ_INFORMATION,
+                                    &AliasHandle);
+        if (ApiStatus != NERR_Success)
+        {
+            ERR("OpenAliasByName failed (ApiStatus %lu)\n", ApiStatus);
+            if (ApiStatus == ERROR_NONE_MAPPED)
+                ApiStatus = NERR_GroupNotFound;
+            goto done;
+        }
+    }
+
+    Status = SamQueryInformationAlias(AliasHandle,
+                                      AliasGeneralInformation,
+                                      (PVOID *)&AliasInfo);
+    if (!NT_SUCCESS(Status))
+    {
+        ERR("SamQueryInformationAlias failed (Status %08lx)\n", Status);
+        ApiStatus = NetpNtStatusToApiStatus(Status);
+        goto done;
+    }
+
+    ApiStatus = BuildAliasInfoBuffer(AliasInfo,
+                                     level,
+                                     &Buffer);
+    if (ApiStatus != NERR_Success)
+        goto done;
+
+done:
+    if (AliasInfo != NULL)
+        FreeAliasInfo(AliasInfo);
+
+    if (AliasHandle != NULL)
+        SamCloseHandle(AliasHandle);
+
+    if (DomainHandle != NULL)
+        SamCloseHandle(DomainHandle);
+
+    if (ServerHandle != NULL)
+        SamCloseHandle(ServerHandle);
+
+    *bufptr = (LPBYTE)Buffer;
+
+    return ApiStatus;
+}
+
+
+/************************************************************
+ *                NetLocalGroupGetMembers  (NETAPI32.@)
+ */
+NET_API_STATUS
+WINAPI
+NetLocalGroupGetMembers(
+    LPCWSTR servername,
+    LPCWSTR localgroupname,
+    DWORD level,
+    LPBYTE* bufptr,
+    DWORD prefmaxlen,
+    LPDWORD entriesread,
+    LPDWORD totalentries,
+    PDWORD_PTR resumehandle)
+{
+    OBJECT_ATTRIBUTES ObjectAttributes;
+    UNICODE_STRING ServerName;
+    UNICODE_STRING AliasName;
+    PMEMBER_ENUM_CONTEXT EnumContext = NULL;
+    LPVOID Buffer = NULL;
+    PLOCALGROUP_MEMBERS_INFO_0 MembersInfo0;
+    PLOCALGROUP_MEMBERS_INFO_1 MembersInfo1;
+    PLOCALGROUP_MEMBERS_INFO_2 MembersInfo2;
+    PLOCALGROUP_MEMBERS_INFO_3 MembersInfo3;
+    LPWSTR Ptr;
+    ULONG Size = 0;
+    ULONG SidLength;
+    ULONG i;
+    NET_API_STATUS ApiStatus = NERR_Success;
+    NTSTATUS Status = STATUS_SUCCESS;
+
+    TRACE("(%s %s %d %p %d, %p %p %p)\n", debugstr_w(servername),
+          debugstr_w(localgroupname), level, bufptr, prefmaxlen, entriesread,
+          totalentries, resumehandle);
+
+    *entriesread = 0;
+    *totalentries = 0;
+    *bufptr = NULL;
+
+    if (servername != NULL)
+        RtlInitUnicodeString(&ServerName, servername);
+
+    RtlInitUnicodeString(&AliasName, localgroupname);
+
+    if (resumehandle != NULL && *resumehandle != 0)
+    {
+        EnumContext = (PMEMBER_ENUM_CONTEXT)*resumehandle;
+    }
+    else
+    {
+        /* Allocate the enumeration context */
+        ApiStatus = NetApiBufferAllocate(sizeof(MEMBER_ENUM_CONTEXT), (PVOID*)&EnumContext);
+        if (ApiStatus != NERR_Success)
+            goto done;
+
+        /* Connect to the SAM Server */
+        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;
+        }
+
+        /* Open the Builtin Domain */
+        Status = OpenBuiltinDomain(EnumContext->ServerHandle,
+                                   DOMAIN_LOOKUP,
+                                   &EnumContext->DomainHandle);
+        if (!NT_SUCCESS(Status))
+        {
+            ERR("OpenBuiltinDomain failed (Status %08lx)\n", Status);
+            ApiStatus = NetpNtStatusToApiStatus(Status);
+            goto done;
+        }
+
+        /* Open the alias account in the builtin domain */
+        ApiStatus = OpenAliasByName(EnumContext->DomainHandle,
+                                    &AliasName,
+                                    ALIAS_LIST_MEMBERS,
+                                    &EnumContext->AliasHandle);
+        if (ApiStatus != NERR_Success && ApiStatus != ERROR_NONE_MAPPED)
+        {
+            ERR("OpenAliasByName failed (ApiStatus %lu)\n", ApiStatus);
+            goto done;
+        }
+
+        if (EnumContext->AliasHandle == NULL)
+        {
+            if (EnumContext->DomainHandle != NULL)
+                SamCloseHandle(EnumContext->DomainHandle);
+
+            /* Open the Acount Domain */
+            Status = OpenAccountDomain(EnumContext->ServerHandle,
+                                       (servername != NULL) ? &ServerName : NULL,
+                                       DOMAIN_LOOKUP,
+                                       &EnumContext->DomainHandle);
+            if (!NT_SUCCESS(Status))
+            {
+                ERR("OpenAccountDomain failed (Status %08lx)\n", Status);
+                ApiStatus = NetpNtStatusToApiStatus(Status);
+                goto done;
+            }
+
+            /* Open the alias account in the account domain */
+            ApiStatus = OpenAliasByName(EnumContext->DomainHandle,
+                                        &AliasName,
+                                        ALIAS_LIST_MEMBERS,
+                                        &EnumContext->AliasHandle);
+            if (ApiStatus != NERR_Success)
             {
-                EnumContext->BuiltinDone = TRUE;
+                ERR("OpenAliasByName failed (ApiStatus %lu)\n", ApiStatus);
+                if (ApiStatus == ERROR_NONE_MAPPED)
+                    ApiStatus = NERR_GroupNotFound;
+                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 alias */
-        CurrentAlias = &EnumContext->Buffer[EnumContext->Index];
-
-        TRACE("RID: %lu\n", CurrentAlias->RelativeId);
-
-        Status = SamOpenAlias(EnumContext->BuiltinDomainHandle,
-                              ALIAS_READ_INFORMATION,
-                              CurrentAlias->RelativeId,
-                              &AliasHandle);
+        /* Get the member list */
+        Status = SamGetMembersInAlias(EnumContext->AliasHandle,
+                                      &EnumContext->Sids,
+                                      &EnumContext->Count);
         if (!NT_SUCCESS(Status))
         {
-            ERR("SamOpenAlias failed (Status %08lx)\n", Status);
+            ERR("SamGetMemberInAlias failed (Status %08lx)\n", Status);
             ApiStatus = NetpNtStatusToApiStatus(Status);
             goto done;
         }
 
-        Status = SamQueryInformationAlias(AliasHandle,
-                                          AliasGeneralInformation,
-                                          (PVOID *)&AliasInfo);
-        if (!NT_SUCCESS(Status))
+        if (EnumContext->Count == 0)
         {
-            ERR("SamQueryInformationAlias failed (Status %08lx)\n", Status);
-            ApiStatus = NetpNtStatusToApiStatus(Status);
+            TRACE("No member found. We're done.\n");
+            ApiStatus = NERR_Success;
             goto done;
         }
 
-        SamCloseHandle(AliasHandle);
-        AliasHandle = NULL;
-
-        TRACE("Name: %S\n", AliasInfo->Name.Buffer);
-        TRACE("Comment: %S\n", AliasInfo->AdminComment.Buffer);
+        /* Get name and domain information for all members */
+        if (level != 0)
+        {
+            InitializeObjectAttributes(&ObjectAttributes,
+                                       NULL,
+                                       0,
+                                       0,
+                                       NULL);
+
+            Status = LsaOpenPolicy((servername != NULL) ? &ServerName : NULL,
+                                   (PLSA_OBJECT_ATTRIBUTES)&ObjectAttributes,
+                                   POLICY_EXECUTE,
+                                   &EnumContext->LsaHandle);
+            if (!NT_SUCCESS(Status))
+            {
+                ApiStatus = NetpNtStatusToApiStatus(Status);
+                goto done;
+            }
 
-        ApiStatus = BuildAliasInfoBuffer(AliasInfo,
-                                         level,
-                                         &Buffer);
-        if (ApiStatus != NERR_Success)
-            goto done;
+            Status = LsaLookupSids(EnumContext->LsaHandle,
+                                   EnumContext->Count,
+                                   EnumContext->Sids,
+                                   &EnumContext->Domains,
+                                   &EnumContext->Names);
+            if (!NT_SUCCESS(Status))
+            {
+                ApiStatus = NetpNtStatusToApiStatus(Status);
+                goto done;
+            }
+        }
+    }
 
-        if (AliasInfo != NULL)
+    /* Calculate the required buffer size */
+    for (i = 0; i < EnumContext->Count; i++)
+    {
+        switch (level)
         {
-            FreeAliasInfo(AliasInfo);
-            AliasInfo = NULL;
+            case 0:
+                Size += sizeof(LOCALGROUP_MEMBERS_INFO_0) +
+                        RtlLengthSid(EnumContext->Sids[i]);
+                break;
+
+            case 1:
+                Size += sizeof(LOCALGROUP_MEMBERS_INFO_1) +
+                        RtlLengthSid(EnumContext->Sids[i]) +
+                        EnumContext->Names[i].Name.Length + sizeof(WCHAR);
+                break;
+
+            case 2:
+                Size += sizeof(LOCALGROUP_MEMBERS_INFO_2) +
+                        RtlLengthSid(EnumContext->Sids[i]) +
+                        EnumContext->Names[i].Name.Length + sizeof(WCHAR);
+                if (EnumContext->Names[i].DomainIndex >= 0)
+                    Size += EnumContext->Domains->Domains[EnumContext->Names[i].DomainIndex].Name.Length + sizeof(WCHAR);
+                break;
+
+            case 3:
+                Size += sizeof(LOCALGROUP_MEMBERS_INFO_3) +
+                        EnumContext->Names[i].Name.Length + sizeof(WCHAR);
+                if (EnumContext->Names[i].DomainIndex >= 0)
+                    Size += EnumContext->Domains->Domains[EnumContext->Names[i].DomainIndex].Name.Length + sizeof(WCHAR);
+                break;
+
+            default:
+                ApiStatus = ERROR_INVALID_LEVEL;
+                goto done;
         }
+    }
 
-        EnumContext->Index++;
+    /* Allocate the member buffer */
+    ApiStatus = NetApiBufferAllocate(Size, &Buffer);
+    if (ApiStatus != NERR_Success)
+        goto done;
 
-        (*entriesread)++;
+    ZeroMemory(Buffer, Size);
 
-//    }
+    /* Fill the member buffer */
+    switch (level)
+    {
+        case 0:
+            MembersInfo0 = (PLOCALGROUP_MEMBERS_INFO_0)Buffer;
+            Ptr = (PVOID)((ULONG_PTR)Buffer + sizeof(LOCALGROUP_MEMBERS_INFO_0) * EnumContext->Count);
+            break;
 
-done:
-    if (ApiStatus == NERR_Success && EnumContext->Index < EnumContext->Returned)
-        ApiStatus = ERROR_MORE_DATA;
+        case 1:
+            MembersInfo1 = (PLOCALGROUP_MEMBERS_INFO_1)Buffer;
+            Ptr = (PVOID)((ULONG_PTR)Buffer + sizeof(LOCALGROUP_MEMBERS_INFO_1) * EnumContext->Count);
+            break;
 
-    if (EnumContext != NULL)
-        *totalentries = EnumContext->Returned;
+        case 2:
+            MembersInfo2 = (PLOCALGROUP_MEMBERS_INFO_2)Buffer;
+            Ptr = (PVOID)((ULONG_PTR)Buffer + sizeof(LOCALGROUP_MEMBERS_INFO_2) * EnumContext->Count);
+            break;
 
-    if (resumehandle == NULL || ApiStatus != ERROR_MORE_DATA)
+        case 3:
+            MembersInfo3 = (PLOCALGROUP_MEMBERS_INFO_3)Buffer;
+            Ptr = (PVOID)((ULONG_PTR)Buffer + sizeof(LOCALGROUP_MEMBERS_INFO_3) * EnumContext->Count);
+            break;
+    }
+
+    for (i = 0; i < EnumContext->Count; i++)
     {
-        if (EnumContext != NULL)
+        switch (level)
         {
-            if (EnumContext->BuiltinDomainHandle != NULL)
-                SamCloseHandle(EnumContext->BuiltinDomainHandle);
+            case 0:
+                MembersInfo0->lgrmi0_sid = (PSID)Ptr;
 
-            if (EnumContext->AccountDomainHandle != NULL)
-                SamCloseHandle(EnumContext->AccountDomainHandle);
+                SidLength = RtlLengthSid(EnumContext->Sids[i]);
+                memcpy(MembersInfo0->lgrmi0_sid,
+                       EnumContext->Sids[i],
+                       SidLength);
+                Ptr = (PVOID)((ULONG_PTR)Ptr + SidLength);
+                MembersInfo0++;
+                break;
 
-            if (EnumContext->ServerHandle != NULL)
-                SamCloseHandle(EnumContext->ServerHandle);
+            case 1:
+                MembersInfo1->lgrmi1_sid = (PSID)Ptr;
 
-            if (EnumContext->Buffer != NULL)
-            {
-                for (i = 0; i < EnumContext->Returned; i++)
+                SidLength = RtlLengthSid(EnumContext->Sids[i]);
+                memcpy(MembersInfo1->lgrmi1_sid,
+                       EnumContext->Sids[i],
+                       SidLength);
+
+                Ptr = (PVOID)((ULONG_PTR)Ptr + SidLength);
+
+                MembersInfo1->lgrmi1_sidusage = EnumContext->Names[i].Use;
+
+                TRACE("Name: %S\n", EnumContext->Names[i].Name.Buffer);
+
+                MembersInfo1->lgrmi1_name = (LPWSTR)Ptr;
+
+                memcpy(MembersInfo1->lgrmi1_name,
+                       EnumContext->Names[i].Name.Buffer,
+                       EnumContext->Names[i].Name.Length);
+                Ptr = (PVOID)((ULONG_PTR)Ptr + EnumContext->Names[i].Name.Length + sizeof(WCHAR));
+                MembersInfo1++;
+                break;
+
+            case 2:
+                MembersInfo2->lgrmi2_sid = (PSID)Ptr;
+
+                SidLength = RtlLengthSid(EnumContext->Sids[i]);
+                memcpy(MembersInfo2->lgrmi2_sid,
+                       EnumContext->Sids[i],
+                       SidLength);
+
+                Ptr = (PVOID)((ULONG_PTR)Ptr + SidLength);
+
+                MembersInfo2->lgrmi2_sidusage = EnumContext->Names[i].Use;
+
+                MembersInfo2->lgrmi2_domainandname = (LPWSTR)Ptr;
+
+                if (EnumContext->Names[i].DomainIndex >= 0)
                 {
-                    SamFreeMemory(EnumContext->Buffer[i].Name.Buffer);
+                    memcpy(MembersInfo2->lgrmi2_domainandname,
+                           EnumContext->Domains->Domains[EnumContext->Names[i].DomainIndex].Name.Buffer,
+                           EnumContext->Domains->Domains[EnumContext->Names[i].DomainIndex].Name.Length);
+
+                    Ptr = (PVOID)((ULONG_PTR)Ptr + EnumContext->Domains->Domains[EnumContext->Names[i].DomainIndex].Name.Length);
+
+                    *((LPWSTR)Ptr) = L'\\';
+
+                    Ptr = (PVOID)((ULONG_PTR)Ptr + sizeof(WCHAR));
                 }
 
-                SamFreeMemory(EnumContext->Buffer);
-            }
+                memcpy(Ptr,
+                       EnumContext->Names[i].Name.Buffer,
+                       EnumContext->Names[i].Name.Length);
+                Ptr = (PVOID)((ULONG_PTR)Ptr + EnumContext->Names[i].Name.Length + sizeof(WCHAR));
+                MembersInfo2++;
+                break;
 
-            NetApiBufferFree(EnumContext);
-            EnumContext = NULL;
+            case 3:
+                MembersInfo3->lgrmi3_domainandname = (PSID)Ptr;
+
+                if (EnumContext->Names[i].DomainIndex >= 0)
+                {
+                    memcpy(MembersInfo2->lgrmi2_domainandname,
+                           EnumContext->Domains->Domains[EnumContext->Names[i].DomainIndex].Name.Buffer,
+                           EnumContext->Domains->Domains[EnumContext->Names[i].DomainIndex].Name.Length);
+
+                    Ptr = (PVOID)((ULONG_PTR)Ptr + EnumContext->Domains->Domains[EnumContext->Names[i].DomainIndex].Name.Length);
+
+                    *((LPWSTR)Ptr) = L'\\';
+
+                    Ptr = (PVOID)((ULONG_PTR)Ptr + sizeof(WCHAR));
+                }
+
+                memcpy(Ptr,
+                       EnumContext->Names[i].Name.Buffer,
+                       EnumContext->Names[i].Name.Length);
+                Ptr = (PVOID)((ULONG_PTR)Ptr + EnumContext->Names[i].Name.Length + sizeof(WCHAR));
+                MembersInfo3++;
+                break;
         }
     }
 
-    if (AliasHandle != NULL)
-        SamCloseHandle(AliasHandle);
+    *entriesread = EnumContext->Count;
 
-    if (AliasInfo != NULL)
-        FreeAliasInfo(AliasInfo);
+    *bufptr = (LPBYTE)Buffer;
 
-    if (resumehandle != NULL)
-        *resumehandle = (DWORD_PTR)EnumContext;
+done:
+    if (EnumContext != NULL)
+        *totalentries = EnumContext->Count;
 
-    *bufptr = (LPBYTE)Buffer;
+    if (resumehandle == NULL || ApiStatus != ERROR_MORE_DATA)
+    {
+        /* Release the enumeration context */
+        if (EnumContext != NULL)
+        {
+            if (EnumContext->LsaHandle != NULL)
+                LsaClose(EnumContext->LsaHandle);
 
-    TRACE ("return %lu\n", ApiStatus);
+            if (EnumContext->AliasHandle != NULL)
+                SamCloseHandle(EnumContext->AliasHandle);
+
+            if (EnumContext->DomainHandle != NULL)
+                SamCloseHandle(EnumContext->DomainHandle);
+
+            if (EnumContext->ServerHandle != NULL)
+                SamCloseHandle(EnumContext->ServerHandle);
+
+            if (EnumContext->Sids != NULL)
+                SamFreeMemory(EnumContext->Sids);
+
+            if (EnumContext->Domains != NULL)
+                LsaFreeMemory(EnumContext->Domains);
+
+            if (EnumContext->Names != NULL)
+                LsaFreeMemory(EnumContext->Names);
+
+            NetApiBufferFree(EnumContext);
+            EnumContext = NULL;
+        }
+    }
 
     return ApiStatus;
 }
 
 
 /************************************************************
- * NetLocalGroupGetInfo  (NETAPI32.@)
+ *                NetLocalGroupSetInfo  (NETAPI32.@)
  */
 NET_API_STATUS
 WINAPI
-NetLocalGroupGetInfo(
+NetLocalGroupSetInfo(
     LPCWSTR servername,
     LPCWSTR groupname,
     DWORD level,
-    LPBYTE* bufptr)
+    LPBYTE buf,
+    LPDWORD parm_err)
 {
     UNICODE_STRING ServerName;
-    UNICODE_STRING GroupName;
+    UNICODE_STRING AliasName;
     SAM_HANDLE ServerHandle = NULL;
     SAM_HANDLE DomainHandle = NULL;
     SAM_HANDLE AliasHandle = NULL;
-    PALIAS_GENERAL_INFORMATION AliasInfo = NULL;
-    LPVOID Buffer = NULL;
+    ALIAS_NAME_INFORMATION AliasNameInfo;
+    ALIAS_ADM_COMMENT_INFORMATION AdminCommentInfo;
     NET_API_STATUS ApiStatus = NERR_Success;
     NTSTATUS Status = STATUS_SUCCESS;
 
-    TRACE("(%s %s %d %p) stub!\n", debugstr_w(servername),
-          debugstr_w(groupname), level, bufptr);
+    TRACE("(%s %s %d %p %p)\n", debugstr_w(servername),
+          debugstr_w(groupname), level, buf, parm_err);
+
+    if (parm_err != NULL)
+        *parm_err = PARM_ERROR_NONE;
 
     if (servername != NULL)
         RtlInitUnicodeString(&ServerName, servername);
 
-    RtlInitUnicodeString(&GroupName, groupname);
+    RtlInitUnicodeString(&AliasName, groupname);
 
     /* Connect to the SAM Server */
     Status = SamConnect((servername != NULL) ? &ServerName : NULL,
@@ -698,10 +1775,10 @@ NetLocalGroupGetInfo(
 
     /* Open the alias account in the builtin domain */
     ApiStatus = OpenAliasByName(DomainHandle,
-                                &GroupName,
-                                ALIAS_READ_INFORMATION,
+                                &AliasName,
+                                ALIAS_WRITE_ACCOUNT,
                                 &AliasHandle);
-    if (ApiStatus != NERR_Success)
+    if (ApiStatus != NERR_Success && ApiStatus != ERROR_NONE_MAPPED)
     {
         ERR("OpenAliasByName failed (ApiStatus %lu)\n", ApiStatus);
         goto done;
@@ -726,36 +1803,63 @@ NetLocalGroupGetInfo(
 
         /* Open the alias account in the account domain */
         ApiStatus = OpenAliasByName(DomainHandle,
-                                    &GroupName,
-                                    ALIAS_READ_INFORMATION,
+                                    &AliasName,
+                                    ALIAS_WRITE_ACCOUNT,
                                     &AliasHandle);
         if (ApiStatus != NERR_Success)
         {
             ERR("OpenAliasByName failed (ApiStatus %lu)\n", ApiStatus);
+            if (ApiStatus == ERROR_NONE_MAPPED)
+                ApiStatus = NERR_GroupNotFound;
             goto done;
         }
     }
 
-    Status = SamQueryInformationAlias(AliasHandle,
-                                      AliasGeneralInformation,
-                                      (PVOID *)&AliasInfo);
-    if (!NT_SUCCESS(Status))
+    switch (level)
     {
-        ERR("SamQueryInformationAlias failed (Status %08lx)\n", Status);
-        ApiStatus = NetpNtStatusToApiStatus(Status);
-        goto done;
-    }
+        case 0:
+            /* Set the alias name */
+            RtlInitUnicodeString(&AliasNameInfo.Name,
+                                 ((PLOCALGROUP_INFO_0)buf)->lgrpi0_name);
 
-    ApiStatus = BuildAliasInfoBuffer(AliasInfo,
-                                     level,
-                                     &Buffer);
-    if (ApiStatus != NERR_Success)
-        goto done;
+            Status = SamSetInformationAlias(AliasHandle,
+                                            AliasNameInformation,
+                                            &AliasNameInfo);
+            if (!NT_SUCCESS(Status))
+            {
+                TRACE("SamSetInformationAlias failed (ApiStatus %lu)\n", ApiStatus);
+                ApiStatus = NetpNtStatusToApiStatus(Status);
+                goto done;
+            }
+            break;
 
-done:
-    if (AliasInfo != NULL)
-        FreeAliasInfo(AliasInfo);
+        case 1:
+        case 1002:
+            /* Set the alias admin comment */
+            if (level == 1)
+                RtlInitUnicodeString(&AdminCommentInfo.AdminComment,
+                                     ((PLOCALGROUP_INFO_1)buf)->lgrpi1_comment);
+            else
+                RtlInitUnicodeString(&AdminCommentInfo.AdminComment,
+                                     ((PLOCALGROUP_INFO_1002)buf)->lgrpi1002_comment);
+
+            Status = SamSetInformationAlias(AliasHandle,
+                                            AliasAdminCommentInformation,
+                                            &AdminCommentInfo);
+            if (!NT_SUCCESS(Status))
+            {
+                TRACE("SamSetInformationAlias failed (ApiStatus %lu)\n", ApiStatus);
+                ApiStatus = NetpNtStatusToApiStatus(Status);
+                goto done;
+            }
+            break;
+
+        default:
+            ApiStatus = ERROR_INVALID_LEVEL;
+            goto done;
+    }
 
+done:
     if (AliasHandle != NULL)
         SamCloseHandle(AliasHandle);
 
@@ -765,79 +1869,10 @@ done:
     if (ServerHandle != NULL)
         SamCloseHandle(ServerHandle);
 
-    *bufptr = (LPBYTE)Buffer;
-
     return ApiStatus;
 }
 
 
-/************************************************************
- *                NetLocalGroupGetMembers  (NETAPI32.@)
- */
-NET_API_STATUS WINAPI NetLocalGroupGetMembers(
-    LPCWSTR servername,
-    LPCWSTR localgroupname,
-    DWORD level,
-    LPBYTE* bufptr,
-    DWORD prefmaxlen,
-    LPDWORD entriesread,
-    LPDWORD totalentries,
-    PDWORD_PTR resumehandle)
-{
-    FIXME("(%s %s %d %p %d, %p %p %p) stub!\n", debugstr_w(servername),
-          debugstr_w(localgroupname), level, bufptr, prefmaxlen, entriesread,
-          totalentries, resumehandle);
-
-    if (level == 3)
-    {
-        WCHAR userName[MAX_COMPUTERNAME_LENGTH + 1];
-        DWORD userNameLen;
-        DWORD len,needlen;
-        PLOCALGROUP_MEMBERS_INFO_3 ptr;
-
-        /* still a stub,  current user is belonging to all groups */
-
-        *totalentries = 1;
-        *entriesread = 0;
-
-        userNameLen = MAX_COMPUTERNAME_LENGTH + 1;
-        GetUserNameW(userName,&userNameLen);
-        needlen = sizeof(LOCALGROUP_MEMBERS_INFO_3) +
-             (userNameLen+2) * sizeof(WCHAR);
-        if (prefmaxlen != MAX_PREFERRED_LENGTH)
-            len = min(prefmaxlen,needlen);
-        else
-            len = needlen;
-
-        NetApiBufferAllocate(len, (LPVOID *) bufptr);
-        if (len < needlen)
-            return ERROR_MORE_DATA;
-
-        ptr = (PLOCALGROUP_MEMBERS_INFO_3)*bufptr;
-        ptr->lgrmi3_domainandname = (LPWSTR)(*bufptr+sizeof(LOCALGROUP_MEMBERS_INFO_3));
-        lstrcpyW(ptr->lgrmi3_domainandname,userName);
-
-        *entriesread = 1;
-    }
-
-    return NERR_Success;
-}
-
-/************************************************************
- *                NetLocalGroupSetInfo  (NETAPI32.@)
- */
-NET_API_STATUS WINAPI NetLocalGroupSetInfo(
-    LPCWSTR servername,
-    LPCWSTR groupname,
-    DWORD level,
-    LPBYTE buf,
-    LPDWORD parm_err)
-{
-    FIXME("(%s %s %d %p %p) stub!\n", debugstr_w(servername),
-          debugstr_w(groupname), level, buf, parm_err);
-    return NERR_Success;
-}
-
 /************************************************************
  *                NetLocalGroupSetMember (NETAPI32.@)
  */
@@ -852,3 +1887,5 @@ NET_API_STATUS WINAPI NetLocalGroupSetMembers(
             debugstr_w(groupname), level, buf, totalentries);
     return NERR_Success;
 }
+
+/* EOF */