[NETAPI32]
[reactos.git] / reactos / dll / win32 / netapi32 / local_group.c
index ab37ee2..d3606a2 100644 (file)
@@ -49,119 +49,353 @@ typedef struct _ENUM_CONTEXT
     SAM_HANDLE AccountDomainHandle;
 
     SAM_ENUMERATE_HANDLE EnumerationContext;
-    PSAM_RID_ENUMERATION EnumBuffer;
-    ULONG EnumReturned;
+    PSAM_RID_ENUMERATION Buffer;
+    ULONG Returned;
+    ULONG Index;
+    BOOLEAN BuiltinDone;
 
 } ENUM_CONTEXT, *PENUM_CONTEXT;
 
-static SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY};
-
 
 static
-NTSTATUS
-GetAccountDomainSid(PSID *AccountDomainSid)
+NET_API_STATUS
+BuildAliasInfoBuffer(PALIAS_GENERAL_INFORMATION AliasInfo,
+                     DWORD level,
+                     LPVOID *Buffer)
 {
-    PPOLICY_ACCOUNT_DOMAIN_INFO AccountDomainInfo = NULL;
-    LSA_OBJECT_ATTRIBUTES ObjectAttributes;
-    LSA_HANDLE PolicyHandle = NULL;
-    ULONG Length = 0;
-    NTSTATUS Status;
-
-    memset(&ObjectAttributes, 0, sizeof(LSA_OBJECT_ATTRIBUTES));
-
-    Status = LsaOpenPolicy(NULL,
-                           &ObjectAttributes,
-                           POLICY_VIEW_LOCAL_INFORMATION,
-                           &PolicyHandle);
-    if (!NT_SUCCESS(Status))
+    LPVOID LocalBuffer = NULL;
+    PLOCALGROUP_INFO_0 LocalInfo0;
+    PLOCALGROUP_INFO_1 LocalInfo1;
+    LPWSTR Ptr;
+    ULONG Size = 0;
+    NET_API_STATUS ApiStatus = NERR_Success;
+
+    *Buffer = NULL;
+
+    switch (level)
     {
-        ERR("LsaOpenPolicy failed (Status %08lx)\n", Status);
-        return Status;
+        case 0:
+            Size = sizeof(LOCALGROUP_INFO_0) +
+                   AliasInfo->Name.Length + sizeof(WCHAR);
+            break;
+
+        case 1:
+            Size = sizeof(LOCALGROUP_INFO_1) +
+                   AliasInfo->Name.Length + sizeof(WCHAR) +
+                   AliasInfo->AdminComment.Length + sizeof(WCHAR);
+            break;
+
+        default:
+            ApiStatus = ERROR_INVALID_LEVEL;
+            goto done;
     }
 
-    Status = LsaQueryInformationPolicy(PolicyHandle,
-                                       PolicyAccountDomainInformation,
-                                       (PVOID *)&AccountDomainInfo);
-    if (!NT_SUCCESS(Status))
-    {
-        ERR("LsaQueryInformationPolicy failed (Status %08lx)\n", Status);
+    ApiStatus = NetApiBufferAllocate(Size, &LocalBuffer);
+    if (ApiStatus != NERR_Success)
         goto done;
-    }
 
-    Length = RtlLengthSid(AccountDomainInfo->DomainSid);
+    ZeroMemory(LocalBuffer, Size);
 
-    *AccountDomainSid = RtlAllocateHeap(RtlGetProcessHeap(), 0, Length);
-    if (*AccountDomainSid == NULL)
+    switch (level)
     {
-        ERR("Failed to allocate SID\n");
-        Status = STATUS_INSUFFICIENT_RESOURCES;
-        goto done;
-    }
+        case 0:
+            LocalInfo0 = (PLOCALGROUP_INFO_0)LocalBuffer;
 
-    memcpy(*AccountDomainSid, AccountDomainInfo->DomainSid, Length);
+            Ptr = (LPWSTR)LocalInfo0++;
+            LocalInfo0->lgrpi0_name = Ptr;
 
-done:
-    if (AccountDomainInfo != NULL)
-        LsaFreeMemory(AccountDomainInfo);
+            memcpy(LocalInfo0->lgrpi0_name,
+                   AliasInfo->Name.Buffer,
+                   AliasInfo->Name.Length);
+            LocalInfo0->lgrpi0_name[AliasInfo->Name.Length / sizeof(WCHAR)] = UNICODE_NULL;
+            break;
+
+        case 1:
+            LocalInfo1 = (PLOCALGROUP_INFO_1)LocalBuffer;
+
+            Ptr = (LPWSTR)((ULONG_PTR)LocalInfo1 + sizeof(LOCALGROUP_INFO_1));
+            LocalInfo1->lgrpi1_name = Ptr;
 
-    LsaClose(PolicyHandle);
+            memcpy(LocalInfo1->lgrpi1_name,
+                   AliasInfo->Name.Buffer,
+                   AliasInfo->Name.Length);
+            LocalInfo1->lgrpi1_name[AliasInfo->Name.Length / sizeof(WCHAR)] = UNICODE_NULL;
+
+            Ptr = (LPWSTR)((ULONG_PTR)Ptr + AliasInfo->Name.Length + sizeof(WCHAR));
+            LocalInfo1->lgrpi1_comment = Ptr;
+
+            memcpy(LocalInfo1->lgrpi1_comment,
+                   AliasInfo->AdminComment.Buffer,
+                   AliasInfo->AdminComment.Length);
+            LocalInfo1->lgrpi1_comment[AliasInfo->AdminComment.Length / sizeof(WCHAR)] = UNICODE_NULL;
+            break;
+    }
+
+done:
+    if (ApiStatus == NERR_Success)
+    {
+        *Buffer = LocalBuffer;
+    }
+    else
+    {
+        if (LocalBuffer != NULL)
+            NetApiBufferFree(LocalBuffer);
+    }
 
-    return Status;
+    return ApiStatus;
 }
 
 
 static
-NTSTATUS
-GetBuiltinDomainSid(PSID *BuiltinDomainSid)
+VOID
+FreeAliasInfo(PALIAS_GENERAL_INFORMATION AliasInfo)
 {
-    PSID Sid = NULL;
-    PULONG Ptr;
-    NTSTATUS Status = STATUS_SUCCESS;
+    if (AliasInfo->Name.Buffer != NULL)
+        SamFreeMemory(AliasInfo->Name.Buffer);
 
-    *BuiltinDomainSid = NULL;
+    if (AliasInfo->AdminComment.Buffer != NULL)
+        SamFreeMemory(AliasInfo->AdminComment.Buffer);
 
-    Sid = RtlAllocateHeap(RtlGetProcessHeap(),
-                          0,
-                          RtlLengthRequiredSid(1));
-    if (Sid == NULL)
-        return STATUS_INSUFFICIENT_RESOURCES;
+    SamFreeMemory(AliasInfo);
+}
 
-    Status = RtlInitializeSid(Sid,
-                              &NtAuthority,
-                              1);
-    if (!NT_SUCCESS(Status))
-        goto done;
 
-    Ptr = RtlSubAuthoritySid(Sid, 0);
-    *Ptr = SECURITY_BUILTIN_DOMAIN_RID;
+static
+NET_API_STATUS
+OpenAliasByName(SAM_HANDLE DomainHandle,
+                PUNICODE_STRING AliasName,
+                ULONG DesiredAccess,
+                PSAM_HANDLE AliasHandle)
+{
+    PULONG RelativeIds = NULL;
+    PSID_NAME_USE Use = NULL;
+    NET_API_STATUS ApiStatus = NERR_Success;
+    NTSTATUS Status = STATUS_SUCCESS;
 
-    *BuiltinDomainSid = Sid;
+    /* Get the RID for the given user name */
+    Status = SamLookupNamesInDomain(DomainHandle,
+                                    1,
+                                    AliasName,
+                                    &RelativeIds,
+                                    &Use);
+    if (!NT_SUCCESS(Status))
+    {
+        ERR("SamLookupNamesInDomain failed (Status %08lx)\n", Status);
+        return NetpNtStatusToApiStatus(Status);
+    }
 
-done:
+    /* Fail, if it is not an alias account */
+    if (Use[0] != SidTypeAlias)
+    {
+        ERR("Object is not an Alias!\n");
+        ApiStatus = NERR_GroupNotFound;
+        goto done;
+    }
+
+    /* Open the alias account */
+    Status = SamOpenAlias(DomainHandle,
+                          DesiredAccess,
+                          RelativeIds[0],
+                          AliasHandle);
     if (!NT_SUCCESS(Status))
     {
-        if (Sid != NULL)
-            RtlFreeHeap(RtlGetProcessHeap(), 0, Sid);
+        ERR("SamOpenDomain failed (Status %08lx)\n", Status);
+        ApiStatus = NetpNtStatusToApiStatus(Status);
+        goto done;
     }
 
-    return Status;
+done:
+    if (RelativeIds != NULL)
+        SamFreeMemory(RelativeIds);
+
+    if (Use != NULL)
+        SamFreeMemory(Use);
+
+    return ApiStatus;
 }
 
 
 /************************************************************
- *                NetLocalGroupAdd  (NETAPI32.@)
+ * NetLocalGroupAdd  (NETAPI32.@)
  */
-NET_API_STATUS WINAPI NetLocalGroupAdd(
+NET_API_STATUS
+WINAPI
+NetLocalGroupAdd(
     LPCWSTR servername,
     DWORD level,
     LPBYTE buf,
     LPDWORD parm_err)
 {
-    FIXME("(%s %d %p %p) stub!\n", debugstr_w(servername), level, buf,
+    ALIAS_ADM_COMMENT_INFORMATION AdminComment;
+    UNICODE_STRING ServerName;
+    UNICODE_STRING AliasName;
+    SAM_HANDLE ServerHandle = NULL;
+    SAM_HANDLE DomainHandle = NULL;
+    SAM_HANDLE AliasHandle = NULL;
+    PSID DomainSid = NULL;
+    LPWSTR aliasname = NULL;
+    LPWSTR aliascomment = NULL;
+    ULONG RelativeId;
+    NET_API_STATUS ApiStatus = NERR_Success;
+    NTSTATUS Status = STATUS_SUCCESS;
+
+    TRACE("(%s %d %p %p) stub!\n", debugstr_w(servername), level, buf,
           parm_err);
-    return NERR_Success;
+
+    /* Initialize the Server name*/
+    if (servername != NULL)
+        RtlInitUnicodeString(&ServerName, servername);
+
+    /* Initialize the Alias name*/
+    switch (level)
+    {
+        case 0:
+            aliasname = ((PLOCALGROUP_INFO_0)buf)->lgrpi0_name;
+            aliascomment = NULL;
+            break;
+
+        case 1:
+            aliasname = ((PLOCALGROUP_INFO_1)buf)->lgrpi1_name;
+            aliascomment = ((PLOCALGROUP_INFO_1)buf)->lgrpi1_comment;
+            break;
+
+        default:
+            return ERROR_INVALID_LEVEL;
+    }
+
+    RtlInitUnicodeString(&AliasName, aliasname);
+
+    /* 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;
+    }
+
+    /* Get the Builtin Domain SID */
+    Status = GetBuiltinDomainSid(&DomainSid);
+    if (!NT_SUCCESS(Status))
+    {
+        ERR("GetBuiltinDomainSid failed (Status %08lx)\n", Status);
+        ApiStatus = NetpNtStatusToApiStatus(Status);
+        goto done;
+    }
+
+    /* Open the Builtin Domain */
+    Status = SamOpenDomain(ServerHandle,
+                           DOMAIN_LOOKUP,
+                           DomainSid,
+                           &DomainHandle);
+    if (!NT_SUCCESS(Status))
+    {
+        ERR("SamOpenDomain failed (Status %08lx)\n", Status);
+        ApiStatus = NetpNtStatusToApiStatus(Status);
+        goto done;
+    }
+
+    /* Try to open the Alias Account in the Builtin Domain */
+    ApiStatus = OpenAliasByName(DomainHandle,
+                                &AliasName,
+                                ALIAS_READ_INFORMATION,
+                                &AliasHandle);
+    if (ApiStatus == NERR_Success)
+    {
+        ERR("OpenAliasByName: alias %wZ already exists!\n", &AliasName);
+
+        SamCloseHandle(AliasHandle);
+        ApiStatus = ERROR_ALIAS_EXISTS;
+        goto done;
+    }
+
+    ApiStatus = NERR_Success;
+
+    /* Free the Builtin Domain SID */
+    RtlFreeHeap(RtlGetProcessHeap(), 0, DomainSid);
+    DomainSid = NULL;
+
+    /* Close the Builtin Domain */
+    SamCloseHandle(DomainHandle);
+    DomainHandle = NULL;
+
+    /* Get the account domain SID */
+    Status = GetAccountDomainSid((servername != NULL) ? &ServerName : NULL,
+                                 &DomainSid);
+    if (!NT_SUCCESS(Status))
+    {
+        ERR("GetAccountDomainSid failed (Status %08lx)\n", Status);
+        ApiStatus = NetpNtStatusToApiStatus(Status);
+        goto done;
+    }
+
+    /* Open the account domain */
+    Status = SamOpenDomain(ServerHandle,
+                           DOMAIN_CREATE_ALIAS | DOMAIN_LOOKUP,
+                           DomainSid,
+                           &DomainHandle);
+    if (!NT_SUCCESS(Status))
+    {
+        ERR("SamOpenDomain failed (Status %08lx)\n", Status);
+        ApiStatus = NetpNtStatusToApiStatus(Status);
+        goto done;
+    }
+
+    /* Create the alias */
+    Status = SamCreateAliasInDomain(DomainHandle,
+                                    &AliasName,
+                                    DELETE | ALIAS_WRITE_ACCOUNT,
+                                    &AliasHandle,
+                                    &RelativeId);
+    if (!NT_SUCCESS(Status))
+    {
+        ERR("SamCreateAliasInDomain failed (Status %08lx)\n", Status);
+        ApiStatus = NetpNtStatusToApiStatus(Status);
+        goto done;
+    }
+
+    TRACE("Created alias \"%wZ\" (RID: %lu)\n", &AliasName, RelativeId);
+
+    /* Set the admin comment */
+    if (level == 1)
+    {
+        RtlInitUnicodeString(&AdminComment.AdminComment, aliascomment);
+
+        Status = SamSetInformationAlias(AliasHandle,
+                                        AliasAdminCommentInformation,
+                                        &AdminComment);
+        if (!NT_SUCCESS(Status))
+        {
+            ERR("SamSetInformationAlias failed (Status %08lx)\n", Status);
+            ApiStatus = NetpNtStatusToApiStatus(Status);
+
+            /* Delete the Alias if the Comment could not be set */
+            SamDeleteAlias(AliasHandle);
+
+            goto done;
+        }
+    }
+
+done:
+    if (AliasHandle != NULL)
+        SamCloseHandle(AliasHandle);
+
+    if (DomainSid != NULL)
+        RtlFreeHeap(RtlGetProcessHeap(), 0, DomainSid);
+
+    if (DomainHandle != NULL)
+        SamCloseHandle(DomainHandle);
+
+    if (ServerHandle != NULL)
+        SamCloseHandle(ServerHandle);
+
+    return ApiStatus;
 }
 
+
 /************************************************************
  *                NetLocalGroupAddMember  (NETAPI32.@)
  */
@@ -241,24 +475,30 @@ NET_API_STATUS WINAPI NetLocalGroupEnum(
     LPDWORD totalentries,
     PDWORD_PTR resumehandle)
 {
+    UNICODE_STRING ServerName;
+    PSAM_RID_ENUMERATION CurrentAlias;
     PENUM_CONTEXT EnumContext = NULL;
     PSID DomainSid = 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;
 
-
-    FIXME("(%s %d %p %d %p %p %p) stub!\n", debugstr_w(servername),
+    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;
+        EnumContext = (PENUM_CONTEXT)*resumehandle;
     }
     else
     {
@@ -267,10 +507,12 @@ NET_API_STATUS WINAPI NetLocalGroupEnum(
             goto done;
 
         EnumContext->EnumerationContext = 0;
-        EnumContext->EnumBuffer = NULL;
-        EnumContext->EnumReturned = 0;
+        EnumContext->Buffer = NULL;
+        EnumContext->Returned = 0;
+        EnumContext->Index = 0;
+        EnumContext->BuiltinDone = FALSE;
 
-        Status = SamConnect(NULL,
+        Status = SamConnect((servername != NULL) ? &ServerName : NULL,
                             &EnumContext->ServerHandle,
                             SAM_SERVER_CONNECT | SAM_SERVER_LOOKUP_DOMAIN,
                             NULL);
@@ -281,7 +523,8 @@ NET_API_STATUS WINAPI NetLocalGroupEnum(
             goto done;
         }
 
-        Status = GetAccountDomainSid(&DomainSid);
+        Status = GetAccountDomainSid((servername != NULL) ? &ServerName : NULL,
+                                     &DomainSid);
         if (!NT_SUCCESS(Status))
         {
             ERR("GetAccountDomainSid failed (Status %08lx)\n", Status);
@@ -290,7 +533,7 @@ NET_API_STATUS WINAPI NetLocalGroupEnum(
         }
 
         Status = SamOpenDomain(EnumContext->ServerHandle,
-                               DOMAIN_LIST_ACCOUNTS,
+                               DOMAIN_LIST_ACCOUNTS | DOMAIN_LOOKUP,
                                DomainSid,
                                &EnumContext->AccountDomainHandle);
 
@@ -312,7 +555,7 @@ NET_API_STATUS WINAPI NetLocalGroupEnum(
         }
 
         Status = SamOpenDomain(EnumContext->ServerHandle,
-                               DOMAIN_LIST_ACCOUNTS,
+                               DOMAIN_LIST_ACCOUNTS | DOMAIN_LOOKUP,
                                DomainSid,
                                &EnumContext->BuiltinDomainHandle);
 
@@ -326,44 +569,108 @@ NET_API_STATUS WINAPI NetLocalGroupEnum(
         }
     }
 
-    while (TRUE)
-    {
-        Status = SamEnumerateAliasesInDomain(EnumContext->BuiltinDomainHandle,
-                                             &EnumContext->EnumerationContext,
-                                             (PVOID *)&EnumContext->EnumBuffer,
-                                             prefmaxlen,
-                                             &EnumContext->EnumReturned);
 
-        TRACE("SamEnumerateAliasesInDomain returned (Status %08lx)\n", Status);
+//    while (TRUE)
+//    {
+        TRACE("EnumContext->Index: %lu\n", EnumContext->Index);
+        TRACE("EnumContext->Returned: %lu\n", EnumContext->Returned);
 
+        if (EnumContext->Index >= EnumContext->Returned)
+        {
+//            if (EnumContext->BuiltinDone == TRUE)
+//            {
+//                ApiStatus = NERR_Success;
+//                goto done;
+//            }
+
+            TRACE("Calling SamEnumerateAliasesInDomain\n");
+
+            Status = SamEnumerateAliasesInDomain(EnumContext->BuiltinDomainHandle,
+                                                 &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
+            {
+                EnumContext->BuiltinDone = TRUE;
+            }
+        }
+
+        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);
         if (!NT_SUCCESS(Status))
         {
-            ERR("SamEnumerateAliasesInDomain failed (Status %08lx)\n", Status);
+            ERR("SamOpenAlias failed (Status %08lx)\n", Status);
             ApiStatus = NetpNtStatusToApiStatus(Status);
             goto done;
         }
 
-        TRACE("EnumContext: %lu\n", EnumContext);
-        TRACE("EnumReturned: %lu\n", EnumContext->EnumReturned);
-        TRACE("EnumBuffer: %p\n", EnumContext->EnumBuffer);
+        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;
 
-        for (i = 0; i < EnumContext->EnumReturned; i++)
+        if (AliasInfo != NULL)
         {
-            TRACE("RID: %lu\n", EnumContext->EnumBuffer[i].RelativeId);
-            TRACE("Name: %p\n", EnumContext->EnumBuffer[i].Name.Buffer);
-            TRACE("Name: %S\n", EnumContext->EnumBuffer[i].Name.Buffer);
-            if (EnumContext->EnumBuffer[i].Name.Buffer != NULL)
-            {
-                TRACE("Name: %hx\n", EnumContext->EnumBuffer[i].Name.Buffer[0]);
-            }
+            FreeAliasInfo(AliasInfo);
+            AliasInfo = NULL;
         }
 
-        if (Status != STATUS_MORE_ENTRIES)
-            break;
-    }
+        EnumContext->Index++;
 
+        (*entriesread)++;
+
+//    }
 
 done:
+    if (ApiStatus == NERR_Success && EnumContext->Index < EnumContext->Returned)
+        ApiStatus = ERROR_MORE_DATA;
+
+    if (EnumContext != NULL)
+        *totalentries = EnumContext->Returned;
+
     if (resumehandle == NULL || ApiStatus != ERROR_MORE_DATA)
     {
         if (EnumContext != NULL)
@@ -377,14 +684,14 @@ done:
             if (EnumContext->ServerHandle != NULL)
                 SamCloseHandle(EnumContext->ServerHandle);
 
-            if (EnumContext->EnumBuffer != NULL)
+            if (EnumContext->Buffer != NULL)
             {
-                for (i = 0; i < EnumContext->EnumReturned; i++)
+                for (i = 0; i < EnumContext->Returned; i++)
                 {
-                    SamFreeMemory(EnumContext->EnumBuffer[i].Name.Buffer);
+                    SamFreeMemory(EnumContext->Buffer[i].Name.Buffer);
                 }
 
-                SamFreeMemory(EnumContext->EnumBuffer);
+                SamFreeMemory(EnumContext->Buffer);
             }
 
             NetApiBufferFree(EnumContext);
@@ -392,27 +699,177 @@ done:
         }
     }
 
+    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.@)
+ * NetLocalGroupGetInfo  (NETAPI32.@)
  */
-NET_API_STATUS WINAPI NetLocalGroupGetInfo(
+NET_API_STATUS
+WINAPI
+NetLocalGroupGetInfo(
     LPCWSTR servername,
     LPCWSTR groupname,
     DWORD level,
     LPBYTE* bufptr)
 {
-    FIXME("(%s %s %d %p) stub!\n", debugstr_w(servername),
+    UNICODE_STRING ServerName;
+    UNICODE_STRING GroupName;
+    SAM_HANDLE ServerHandle = NULL;
+    SAM_HANDLE DomainHandle = NULL;
+    SAM_HANDLE AliasHandle = NULL;
+    PSID DomainSid = 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);
-    return NERR_Success;
+
+    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;
+    }
+
+    /* Get the Builtin Domain SID */
+    Status = GetBuiltinDomainSid(&DomainSid);
+    if (!NT_SUCCESS(Status))
+    {
+        ERR("GetBuiltinDomainSid failed (Status %08lx)\n", Status);
+        ApiStatus = NetpNtStatusToApiStatus(Status);
+        goto done;
+    }
+
+    /* Open the Builtin Domain */
+    Status = SamOpenDomain(ServerHandle,
+                           DOMAIN_LOOKUP,
+                           DomainSid,
+                           &DomainHandle);
+    if (!NT_SUCCESS(Status))
+    {
+        ERR("SamOpenDomain 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)
+    {
+        ERR("OpenAliasByName failed (ApiStatus %lu)\n", ApiStatus);
+        goto done;
+    }
+
+    if (AliasHandle == NULL)
+    {
+        if (DomainSid != NULL)
+            RtlFreeHeap(RtlGetProcessHeap(), 0, DomainSid);
+
+        if (DomainHandle != NULL)
+            SamCloseHandle(DomainHandle);
+
+        /* Get the Account Domain SID */
+        Status = GetAccountDomainSid((servername != NULL) ? &ServerName : NULL,
+                                     &DomainSid);
+        if (!NT_SUCCESS(Status))
+        {
+            ERR("GetAccountDomainSid failed (Status %08lx)\n", Status);
+            ApiStatus = NetpNtStatusToApiStatus(Status);
+            goto done;
+        }
+
+        /* Open the Acount Domain */
+        Status = SamOpenDomain(ServerHandle,
+                               DOMAIN_LOOKUP,
+                               DomainSid,
+                               &DomainHandle);
+        if (!NT_SUCCESS(Status))
+        {
+            ERR("SamOpenDomain 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);
+            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 (DomainSid != NULL)
+        RtlFreeHeap(RtlGetProcessHeap(), 0, DomainSid);
+
+    if (DomainHandle != NULL)
+        SamCloseHandle(DomainHandle);
+
+    if (ServerHandle != NULL)
+        SamCloseHandle(ServerHandle);
+
+    *bufptr = (LPBYTE)Buffer;
+
+    return ApiStatus;
 }
 
+
 /************************************************************
  *                NetLocalGroupGetMembers  (NETAPI32.@)
  */