xref: /reactos/dll/win32/netapi32/local_group.c (revision 40462c92)
1 /*
2  * Copyright 2006 Robert Reif
3  *
4  * netapi32 local group functions
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 #include "netapi32.h"
22 
23 WINE_DEFAULT_DEBUG_CHANNEL(netapi32);
24 
25 typedef enum _ENUM_PHASE
26 {
27     BuiltinPhase,
28     AccountPhase,
29     DonePhase
30 } ENUM_PHASE;
31 
32 typedef struct _ENUM_CONTEXT
33 {
34     SAM_HANDLE ServerHandle;
35     SAM_HANDLE DomainHandle;
36     SAM_HANDLE BuiltinDomainHandle;
37     SAM_HANDLE AccountDomainHandle;
38 
39     SAM_ENUMERATE_HANDLE EnumerationContext;
40     PSAM_RID_ENUMERATION Buffer;
41     ULONG Returned;
42     ULONG Index;
43     ENUM_PHASE Phase;
44 
45 } ENUM_CONTEXT, *PENUM_CONTEXT;
46 
47 typedef struct _MEMBER_ENUM_CONTEXT
48 {
49     SAM_HANDLE ServerHandle;
50     SAM_HANDLE DomainHandle;
51     SAM_HANDLE AliasHandle;
52     LSA_HANDLE LsaHandle;
53 
54     PSID *Sids;
55     ULONG Count;
56     PLSA_REFERENCED_DOMAIN_LIST Domains;
57     PLSA_TRANSLATED_NAME Names;
58 
59 } MEMBER_ENUM_CONTEXT, *PMEMBER_ENUM_CONTEXT;
60 
61 
62 static
63 NET_API_STATUS
64 BuildAliasInfoBuffer(PALIAS_GENERAL_INFORMATION AliasInfo,
65                      DWORD level,
66                      LPVOID *Buffer)
67 {
68     LPVOID LocalBuffer = NULL;
69     PLOCALGROUP_INFO_0 LocalInfo0;
70     PLOCALGROUP_INFO_1 LocalInfo1;
71     LPWSTR Ptr;
72     ULONG Size = 0;
73     NET_API_STATUS ApiStatus = NERR_Success;
74 
75     *Buffer = NULL;
76 
77     switch (level)
78     {
79         case 0:
80             Size = sizeof(LOCALGROUP_INFO_0) +
81                    AliasInfo->Name.Length + sizeof(WCHAR);
82             break;
83 
84         case 1:
85             Size = sizeof(LOCALGROUP_INFO_1) +
86                    AliasInfo->Name.Length + sizeof(WCHAR) +
87                    AliasInfo->AdminComment.Length + sizeof(WCHAR);
88             break;
89 
90         default:
91             ApiStatus = ERROR_INVALID_LEVEL;
92             goto done;
93     }
94 
95     ApiStatus = NetApiBufferAllocate(Size, &LocalBuffer);
96     if (ApiStatus != NERR_Success)
97         goto done;
98 
99     ZeroMemory(LocalBuffer, Size);
100 
101     switch (level)
102     {
103         case 0:
104             LocalInfo0 = (PLOCALGROUP_INFO_0)LocalBuffer;
105 
106             Ptr = (LPWSTR)((ULONG_PTR)LocalInfo0 + sizeof(LOCALGROUP_INFO_0));
107             LocalInfo0->lgrpi0_name = Ptr;
108 
109             memcpy(LocalInfo0->lgrpi0_name,
110                    AliasInfo->Name.Buffer,
111                    AliasInfo->Name.Length);
112             LocalInfo0->lgrpi0_name[AliasInfo->Name.Length / sizeof(WCHAR)] = UNICODE_NULL;
113             break;
114 
115         case 1:
116             LocalInfo1 = (PLOCALGROUP_INFO_1)LocalBuffer;
117 
118             Ptr = (LPWSTR)((ULONG_PTR)LocalInfo1 + sizeof(LOCALGROUP_INFO_1));
119             LocalInfo1->lgrpi1_name = Ptr;
120 
121             memcpy(LocalInfo1->lgrpi1_name,
122                    AliasInfo->Name.Buffer,
123                    AliasInfo->Name.Length);
124             LocalInfo1->lgrpi1_name[AliasInfo->Name.Length / sizeof(WCHAR)] = UNICODE_NULL;
125 
126             Ptr = (LPWSTR)((ULONG_PTR)Ptr + AliasInfo->Name.Length + sizeof(WCHAR));
127             LocalInfo1->lgrpi1_comment = Ptr;
128 
129             memcpy(LocalInfo1->lgrpi1_comment,
130                    AliasInfo->AdminComment.Buffer,
131                    AliasInfo->AdminComment.Length);
132             LocalInfo1->lgrpi1_comment[AliasInfo->AdminComment.Length / sizeof(WCHAR)] = UNICODE_NULL;
133             break;
134     }
135 
136 done:
137     if (ApiStatus == NERR_Success)
138     {
139         *Buffer = LocalBuffer;
140     }
141     else
142     {
143         if (LocalBuffer != NULL)
144             NetApiBufferFree(LocalBuffer);
145     }
146 
147     return ApiStatus;
148 }
149 
150 
151 static
152 VOID
153 FreeAliasInfo(PALIAS_GENERAL_INFORMATION AliasInfo)
154 {
155     if (AliasInfo->Name.Buffer != NULL)
156         SamFreeMemory(AliasInfo->Name.Buffer);
157 
158     if (AliasInfo->AdminComment.Buffer != NULL)
159         SamFreeMemory(AliasInfo->AdminComment.Buffer);
160 
161     SamFreeMemory(AliasInfo);
162 }
163 
164 
165 static
166 NET_API_STATUS
167 OpenAliasByName(SAM_HANDLE DomainHandle,
168                 PUNICODE_STRING AliasName,
169                 ULONG DesiredAccess,
170                 PSAM_HANDLE AliasHandle)
171 {
172     PULONG RelativeIds = NULL;
173     PSID_NAME_USE Use = NULL;
174     NET_API_STATUS ApiStatus = NERR_Success;
175     NTSTATUS Status = STATUS_SUCCESS;
176 
177     /* Get the RID for the given user name */
178     Status = SamLookupNamesInDomain(DomainHandle,
179                                     1,
180                                     AliasName,
181                                     &RelativeIds,
182                                     &Use);
183     if (!NT_SUCCESS(Status))
184     {
185         ERR("SamLookupNamesInDomain(%wZ) failed (Status %08lx)\n", AliasName, Status);
186         return NetpNtStatusToApiStatus(Status);
187     }
188 
189     /* Fail, if it is not an alias account */
190     if (Use[0] != SidTypeAlias)
191     {
192         ERR("Object is not an Alias!\n");
193         ApiStatus = NERR_GroupNotFound;
194         goto done;
195     }
196 
197     /* Open the alias account */
198     Status = SamOpenAlias(DomainHandle,
199                           DesiredAccess,
200                           RelativeIds[0],
201                           AliasHandle);
202     if (!NT_SUCCESS(Status))
203     {
204         ERR("SamOpenDomain failed (Status %08lx)\n", Status);
205         ApiStatus = NetpNtStatusToApiStatus(Status);
206         goto done;
207     }
208 
209 done:
210     if (RelativeIds != NULL)
211         SamFreeMemory(RelativeIds);
212 
213     if (Use != NULL)
214         SamFreeMemory(Use);
215 
216     return ApiStatus;
217 }
218 
219 
220 static
221 NET_API_STATUS
222 BuildSidListFromDomainAndName(IN PUNICODE_STRING ServerName,
223                               IN PLOCALGROUP_MEMBERS_INFO_3 buf,
224                               IN ULONG EntryCount,
225                               OUT PLOCALGROUP_MEMBERS_INFO_0 *MemberList)
226 {
227     OBJECT_ATTRIBUTES ObjectAttributes;
228     LSA_HANDLE LsaHandle = NULL;
229     PUNICODE_STRING NamesArray = NULL;
230     ULONG i;
231     PLSA_REFERENCED_DOMAIN_LIST Domains = NULL;
232     PLSA_TRANSLATED_SID Sids = NULL;
233     PLOCALGROUP_MEMBERS_INFO_0 MemberBuffer = NULL;
234     NET_API_STATUS ApiStatus = NERR_Success;
235     NTSTATUS Status = STATUS_SUCCESS;
236 
237     ApiStatus = NetApiBufferAllocate(sizeof(UNICODE_STRING) * EntryCount,
238                                      (LPVOID*)&NamesArray);
239     if (ApiStatus != NERR_Success)
240     {
241         goto done;
242     }
243 
244     for (i = 0; i < EntryCount; i++)
245     {
246         RtlInitUnicodeString(&NamesArray[i],
247                              buf[i].lgrmi3_domainandname);
248     }
249 
250     InitializeObjectAttributes(&ObjectAttributes,
251                                NULL,
252                                0,
253                                0,
254                                NULL);
255 
256     Status = LsaOpenPolicy(ServerName,
257                            (PLSA_OBJECT_ATTRIBUTES)&ObjectAttributes,
258                            POLICY_EXECUTE,
259                            &LsaHandle);
260     if (!NT_SUCCESS(Status))
261     {
262         ApiStatus = NetpNtStatusToApiStatus(Status);
263         goto done;
264     }
265 
266     Status = LsaLookupNames(LsaHandle,
267                             EntryCount,
268                             NamesArray,
269                             &Domains,
270                             &Sids);
271     if (!NT_SUCCESS(Status))
272     {
273         ApiStatus = NetpNtStatusToApiStatus(Status);
274         goto done;
275     }
276 
277     ApiStatus = NetApiBufferAllocate(sizeof(LOCALGROUP_MEMBERS_INFO_0) * EntryCount,
278                                      (LPVOID*)&MemberBuffer);
279     if (ApiStatus != NERR_Success)
280     {
281         goto done;
282     }
283 
284     for (i = 0; i < EntryCount; i++)
285     {
286         ApiStatus = BuildSidFromSidAndRid(Domains->Domains[Sids[i].DomainIndex].Sid,
287                                           Sids[i].RelativeId,
288                                           &MemberBuffer[i].lgrmi0_sid);
289         if (ApiStatus != NERR_Success)
290         {
291             goto done;
292         }
293     }
294 
295 done:
296     if (ApiStatus != NERR_Success)
297     {
298         if (MemberBuffer != NULL)
299         {
300             for (i = 0; i < EntryCount; i++)
301             {
302                 if (MemberBuffer[i].lgrmi0_sid != NULL)
303                     NetApiBufferFree(MemberBuffer[i].lgrmi0_sid);
304             }
305 
306             NetApiBufferFree(MemberBuffer);
307             MemberBuffer = NULL;
308         }
309     }
310 
311     if (Sids != NULL)
312         LsaFreeMemory(Sids);
313 
314     if (Domains != NULL)
315         LsaFreeMemory(Domains);
316 
317     if (LsaHandle != NULL)
318         LsaClose(LsaHandle);
319 
320     if (NamesArray != NULL)
321         NetApiBufferFree(NamesArray);
322 
323     *MemberList = MemberBuffer;
324 
325     return ApiStatus;
326 }
327 
328 
329 /************************************************************
330  * NetLocalGroupAdd  (NETAPI32.@)
331  */
332 NET_API_STATUS
333 WINAPI
334 NetLocalGroupAdd(
335     LPCWSTR servername,
336     DWORD level,
337     LPBYTE buf,
338     LPDWORD parm_err)
339 {
340     ALIAS_ADM_COMMENT_INFORMATION AdminComment;
341     UNICODE_STRING ServerName;
342     UNICODE_STRING AliasName;
343     SAM_HANDLE ServerHandle = NULL;
344     SAM_HANDLE DomainHandle = NULL;
345     SAM_HANDLE AliasHandle = NULL;
346     LPWSTR aliasname = NULL;
347     LPWSTR aliascomment = NULL;
348     ULONG RelativeId;
349     NET_API_STATUS ApiStatus = NERR_Success;
350     NTSTATUS Status = STATUS_SUCCESS;
351 
352     TRACE("(%s %d %p %p) stub!\n", debugstr_w(servername), level, buf,
353           parm_err);
354 
355     if (parm_err != NULL)
356         *parm_err = PARM_ERROR_NONE;
357 
358     /* Initialize the Server name*/
359     if (servername != NULL)
360         RtlInitUnicodeString(&ServerName, servername);
361 
362     /* Initialize the Alias name*/
363     switch (level)
364     {
365         case 0:
366             aliasname = ((PLOCALGROUP_INFO_0)buf)->lgrpi0_name;
367             aliascomment = NULL;
368             break;
369 
370         case 1:
371             aliasname = ((PLOCALGROUP_INFO_1)buf)->lgrpi1_name;
372             aliascomment = ((PLOCALGROUP_INFO_1)buf)->lgrpi1_comment;
373             break;
374 
375         default:
376             return ERROR_INVALID_LEVEL;
377     }
378 
379     RtlInitUnicodeString(&AliasName, aliasname);
380 
381     /* Connect to the SAM Server */
382     Status = SamConnect((servername != NULL) ? &ServerName : NULL,
383                         &ServerHandle,
384                         SAM_SERVER_CONNECT | SAM_SERVER_LOOKUP_DOMAIN,
385                         NULL);
386     if (!NT_SUCCESS(Status))
387     {
388         ERR("SamConnect failed (Status %08lx)\n", Status);
389         ApiStatus = NetpNtStatusToApiStatus(Status);
390         goto done;
391     }
392 
393     /* Open the Builtin Domain */
394     Status = OpenBuiltinDomain(ServerHandle,
395                                DOMAIN_LOOKUP,
396                                &DomainHandle);
397     if (!NT_SUCCESS(Status))
398     {
399         ERR("OpenBuiltinDomain failed (Status %08lx)\n", Status);
400         ApiStatus = NetpNtStatusToApiStatus(Status);
401         goto done;
402     }
403 
404     /* Try to open the Alias Account in the Builtin Domain */
405     ApiStatus = OpenAliasByName(DomainHandle,
406                                 &AliasName,
407                                 ALIAS_READ_INFORMATION,
408                                 &AliasHandle);
409     if (ApiStatus == NERR_Success)
410     {
411         ERR("OpenAliasByName: alias %wZ already exists!\n", &AliasName);
412 
413         SamCloseHandle(AliasHandle);
414         ApiStatus = ERROR_ALIAS_EXISTS;
415         goto done;
416     }
417 
418     ApiStatus = NERR_Success;
419 
420     /* Close the Builtin Domain */
421     SamCloseHandle(DomainHandle);
422     DomainHandle = NULL;
423 
424     /* Open the account domain */
425     Status = OpenAccountDomain(ServerHandle,
426                                (servername != NULL) ? &ServerName : NULL,
427                                DOMAIN_CREATE_ALIAS | DOMAIN_LOOKUP,
428                                &DomainHandle);
429     if (!NT_SUCCESS(Status))
430     {
431         ERR("SamOpenDomain failed (Status %08lx)\n", Status);
432         ApiStatus = NetpNtStatusToApiStatus(Status);
433         goto done;
434     }
435 
436     /* Create the alias */
437     Status = SamCreateAliasInDomain(DomainHandle,
438                                     &AliasName,
439                                     DELETE | ALIAS_WRITE_ACCOUNT,
440                                     &AliasHandle,
441                                     &RelativeId);
442     if (!NT_SUCCESS(Status))
443     {
444         ERR("SamCreateAliasInDomain failed (Status %08lx)\n", Status);
445         ApiStatus = NetpNtStatusToApiStatus(Status);
446         goto done;
447     }
448 
449     TRACE("Created alias \"%wZ\" (RID: %lu)\n", &AliasName, RelativeId);
450 
451     /* Set the admin comment */
452     if (level == 1)
453     {
454         RtlInitUnicodeString(&AdminComment.AdminComment, aliascomment);
455 
456         Status = SamSetInformationAlias(AliasHandle,
457                                         AliasAdminCommentInformation,
458                                         &AdminComment);
459         if (!NT_SUCCESS(Status))
460         {
461             ERR("SamSetInformationAlias failed (Status %08lx)\n", Status);
462             ApiStatus = NetpNtStatusToApiStatus(Status);
463 
464             /* Delete the Alias if the Comment could not be set */
465             SamDeleteAlias(AliasHandle);
466 
467             goto done;
468         }
469     }
470 
471 done:
472     if (AliasHandle != NULL)
473     {
474         if (ApiStatus != NERR_Success)
475             SamDeleteAlias(AliasHandle);
476         else
477             SamCloseHandle(AliasHandle);
478     }
479 
480     if (DomainHandle != NULL)
481         SamCloseHandle(DomainHandle);
482 
483     if (ServerHandle != NULL)
484         SamCloseHandle(ServerHandle);
485 
486     return ApiStatus;
487 }
488 
489 
490 /************************************************************
491  *                NetLocalGroupAddMember  (NETAPI32.@)
492  */
493 NET_API_STATUS
494 WINAPI
495 NetLocalGroupAddMember(
496     LPCWSTR servername,
497     LPCWSTR groupname,
498     PSID membersid)
499 {
500     LOCALGROUP_MEMBERS_INFO_0 Member;
501 
502     TRACE("(%s %s %p)\n", debugstr_w(servername),
503           debugstr_w(groupname), membersid);
504 
505     Member.lgrmi0_sid = membersid;
506 
507     return NetLocalGroupAddMembers(servername,
508                                    groupname,
509                                    0,
510                                    (LPBYTE)&Member,
511                                    1);
512 }
513 
514 
515 /************************************************************
516  *                NetLocalGroupAddMembers  (NETAPI32.@)
517  */
518 NET_API_STATUS
519 WINAPI
520 NetLocalGroupAddMembers(
521     LPCWSTR servername,
522     LPCWSTR groupname,
523     DWORD level,
524     LPBYTE buf,
525     DWORD totalentries)
526 {
527     UNICODE_STRING ServerName;
528     UNICODE_STRING AliasName;
529     SAM_HANDLE ServerHandle = NULL;
530     SAM_HANDLE DomainHandle = NULL;
531     SAM_HANDLE AliasHandle = NULL;
532     PLOCALGROUP_MEMBERS_INFO_0 MemberList = NULL;
533     ULONG i;
534     NET_API_STATUS ApiStatus = NERR_Success;
535     NTSTATUS Status = STATUS_SUCCESS;
536 
537     TRACE("(%s %s %d %p %d)\n", debugstr_w(servername),
538           debugstr_w(groupname), level, buf, totalentries);
539 
540     if (servername != NULL)
541         RtlInitUnicodeString(&ServerName, servername);
542 
543     RtlInitUnicodeString(&AliasName, groupname);
544 
545     switch (level)
546     {
547         case 0:
548             MemberList = (PLOCALGROUP_MEMBERS_INFO_0)buf;
549             break;
550 
551         case 3:
552             Status = BuildSidListFromDomainAndName((servername != NULL) ? &ServerName : NULL,
553                                                    (PLOCALGROUP_MEMBERS_INFO_3)buf,
554                                                    totalentries,
555                                                    &MemberList);
556             if (!NT_SUCCESS(Status))
557             {
558                 ERR("BuildSidListFromDomainAndName failed (Status %08lx)\n", Status);
559                 ApiStatus = NetpNtStatusToApiStatus(Status);
560                 goto done;
561             }
562             break;
563 
564         default:
565             ApiStatus = ERROR_INVALID_LEVEL;
566             goto done;
567     }
568 
569     /* Connect to the SAM Server */
570     Status = SamConnect((servername != NULL) ? &ServerName : NULL,
571                         &ServerHandle,
572                         SAM_SERVER_CONNECT | SAM_SERVER_LOOKUP_DOMAIN,
573                         NULL);
574     if (!NT_SUCCESS(Status))
575     {
576         ERR("SamConnect failed (Status %08lx)\n", Status);
577         ApiStatus = NetpNtStatusToApiStatus(Status);
578         goto done;
579     }
580 
581     /* Open the Builtin Domain */
582     Status = OpenBuiltinDomain(ServerHandle,
583                                DOMAIN_LOOKUP,
584                                &DomainHandle);
585     if (!NT_SUCCESS(Status))
586     {
587         ERR("OpenBuiltinDomain failed (Status %08lx)\n", Status);
588         ApiStatus = NetpNtStatusToApiStatus(Status);
589         goto done;
590     }
591 
592     /* Open the alias account in the builtin domain */
593     ApiStatus = OpenAliasByName(DomainHandle,
594                                 &AliasName,
595                                 ALIAS_ADD_MEMBER,
596                                 &AliasHandle);
597     if (ApiStatus != NERR_Success && ApiStatus != ERROR_NONE_MAPPED)
598     {
599         ERR("OpenAliasByName(%wZ) failed (ApiStatus %lu)\n", &AliasName, ApiStatus);
600         goto done;
601     }
602 
603     if (AliasHandle == NULL)
604     {
605         if (DomainHandle != NULL)
606             SamCloseHandle(DomainHandle);
607 
608         /* Open the Acount Domain */
609         Status = OpenAccountDomain(ServerHandle,
610                                    (servername != NULL) ? &ServerName : NULL,
611                                    DOMAIN_LOOKUP,
612                                    &DomainHandle);
613         if (!NT_SUCCESS(Status))
614         {
615             ERR("OpenAccountDomain failed (Status %08lx)\n", Status);
616             ApiStatus = NetpNtStatusToApiStatus(Status);
617             goto done;
618         }
619 
620         /* Open the alias account in the account domain */
621         ApiStatus = OpenAliasByName(DomainHandle,
622                                     &AliasName,
623                                     ALIAS_ADD_MEMBER,
624                                     &AliasHandle);
625         if (ApiStatus != NERR_Success)
626         {
627             ERR("OpenAliasByName(%wZ) failed (ApiStatus %lu)\n", &AliasName, ApiStatus);
628             if (ApiStatus == ERROR_NONE_MAPPED)
629                 ApiStatus = NERR_GroupNotFound;
630             goto done;
631         }
632     }
633 
634     /* Add new members to the alias */
635     for (i = 0; i < totalentries; i++)
636     {
637         Status = SamAddMemberToAlias(AliasHandle,
638                                      MemberList[i].lgrmi0_sid);
639         if (!NT_SUCCESS(Status))
640         {
641             ERR("SamAddMemberToAlias failed (Status %lu)\n", Status);
642             ApiStatus = NetpNtStatusToApiStatus(Status);
643             goto done;
644         }
645     }
646 
647 done:
648     if (level == 3 && MemberList != NULL)
649     {
650         for (i = 0; i < totalentries; i++)
651         {
652             if (MemberList[i].lgrmi0_sid != NULL)
653                 NetApiBufferFree(MemberList[i].lgrmi0_sid);
654         }
655 
656         NetApiBufferFree(MemberList);
657     }
658 
659     if (AliasHandle != NULL)
660         SamCloseHandle(AliasHandle);
661 
662     if (DomainHandle != NULL)
663         SamCloseHandle(DomainHandle);
664 
665     if (ServerHandle != NULL)
666         SamCloseHandle(ServerHandle);
667 
668     return ApiStatus;
669 }
670 
671 
672 /************************************************************
673  *                NetLocalGroupDel  (NETAPI32.@)
674  */
675 NET_API_STATUS
676 WINAPI
677 NetLocalGroupDel(
678     LPCWSTR servername,
679     LPCWSTR groupname)
680 {
681     UNICODE_STRING ServerName;
682     UNICODE_STRING GroupName;
683     SAM_HANDLE ServerHandle = NULL;
684     SAM_HANDLE DomainHandle = NULL;
685     SAM_HANDLE AliasHandle = NULL;
686     NET_API_STATUS ApiStatus = NERR_Success;
687     NTSTATUS Status = STATUS_SUCCESS;
688 
689     TRACE("(%s %s)\n", debugstr_w(servername), debugstr_w(groupname));
690 
691     if (servername != NULL)
692         RtlInitUnicodeString(&ServerName, servername);
693 
694     RtlInitUnicodeString(&GroupName, groupname);
695 
696     /* Connect to the SAM Server */
697     Status = SamConnect((servername != NULL) ? &ServerName : NULL,
698                         &ServerHandle,
699                         SAM_SERVER_CONNECT | SAM_SERVER_LOOKUP_DOMAIN,
700                         NULL);
701     if (!NT_SUCCESS(Status))
702     {
703         ERR("SamConnect failed (Status %08lx)\n", Status);
704         ApiStatus = NetpNtStatusToApiStatus(Status);
705         goto done;
706     }
707 
708     /* Open the Builtin Domain */
709     Status = OpenBuiltinDomain(ServerHandle,
710                                DOMAIN_LOOKUP,
711                                &DomainHandle);
712     if (!NT_SUCCESS(Status))
713     {
714         ERR("OpenBuiltinDomain failed (Status %08lx)\n", Status);
715         ApiStatus = NetpNtStatusToApiStatus(Status);
716         goto done;
717     }
718 
719     /* Open the alias account in the builtin domain */
720     ApiStatus = OpenAliasByName(DomainHandle,
721                                 &GroupName,
722                                 DELETE,
723                                 &AliasHandle);
724     if (ApiStatus != NERR_Success && ApiStatus != ERROR_NONE_MAPPED)
725     {
726         TRACE("OpenAliasByName(%wZ) failed (ApiStatus %lu)\n", &GroupName, ApiStatus);
727         goto done;
728     }
729 
730     if (AliasHandle == NULL)
731     {
732         if (DomainHandle != NULL)
733         {
734             SamCloseHandle(DomainHandle);
735             DomainHandle = NULL;
736         }
737 
738         /* Open the Acount Domain */
739         Status = OpenAccountDomain(ServerHandle,
740                                    (servername != NULL) ? &ServerName : NULL,
741                                    DOMAIN_LOOKUP,
742                                    &DomainHandle);
743         if (!NT_SUCCESS(Status))
744         {
745             ERR("OpenAccountDomain failed (Status %08lx)\n", Status);
746             ApiStatus = NetpNtStatusToApiStatus(Status);
747             goto done;
748         }
749 
750         /* Open the alias account in the account domain */
751         ApiStatus = OpenAliasByName(DomainHandle,
752                                     &GroupName,
753                                     DELETE,
754                                     &AliasHandle);
755         if (ApiStatus != NERR_Success)
756         {
757             ERR("OpenAliasByName(%wZ) failed (ApiStatus %lu)\n", &GroupName, ApiStatus);
758             if (ApiStatus == ERROR_NONE_MAPPED)
759                 ApiStatus = NERR_GroupNotFound;
760             goto done;
761         }
762     }
763 
764     /* Delete the alias */
765     Status = SamDeleteAlias(AliasHandle);
766     if (!NT_SUCCESS(Status))
767     {
768         ERR("SamDeleteAlias failed (Status %08lx)\n", Status);
769         ApiStatus = NetpNtStatusToApiStatus(Status);
770         goto done;
771     }
772 
773 done:
774     if (AliasHandle != NULL)
775         SamCloseHandle(AliasHandle);
776 
777     if (DomainHandle != NULL)
778         SamCloseHandle(DomainHandle);
779 
780     if (ServerHandle != NULL)
781         SamCloseHandle(ServerHandle);
782 
783     return ApiStatus;
784 }
785 
786 
787 /************************************************************
788  *                NetLocalGroupDelMember  (NETAPI32.@)
789  */
790 NET_API_STATUS
791 WINAPI
792 NetLocalGroupDelMember(
793     LPCWSTR servername,
794     LPCWSTR groupname,
795     PSID membersid)
796 {
797     LOCALGROUP_MEMBERS_INFO_0 Member;
798 
799     TRACE("(%s %s %p)\n", debugstr_w(servername),
800           debugstr_w(groupname), membersid);
801 
802     Member.lgrmi0_sid = membersid;
803 
804     return NetLocalGroupDelMembers(servername,
805                                    groupname,
806                                    0,
807                                    (LPBYTE)&Member,
808                                    1);
809 }
810 
811 
812 /************************************************************
813  *                NetLocalGroupDelMembers  (NETAPI32.@)
814  */
815 NET_API_STATUS
816 WINAPI
817 NetLocalGroupDelMembers(
818     LPCWSTR servername,
819     LPCWSTR groupname,
820     DWORD level,
821     LPBYTE buf,
822     DWORD totalentries)
823 {
824     UNICODE_STRING ServerName;
825     UNICODE_STRING AliasName;
826     SAM_HANDLE ServerHandle = NULL;
827     SAM_HANDLE DomainHandle = NULL;
828     SAM_HANDLE AliasHandle = NULL;
829     PLOCALGROUP_MEMBERS_INFO_0 MemberList = NULL;
830     ULONG i;
831     NET_API_STATUS ApiStatus = NERR_Success;
832     NTSTATUS Status = STATUS_SUCCESS;
833 
834     TRACE("(%s %s %d %p %d)\n", debugstr_w(servername),
835           debugstr_w(groupname), level, buf, totalentries);
836 
837     if (servername != NULL)
838         RtlInitUnicodeString(&ServerName, servername);
839 
840     RtlInitUnicodeString(&AliasName, groupname);
841 
842     switch (level)
843     {
844         case 0:
845             MemberList = (PLOCALGROUP_MEMBERS_INFO_0)buf;
846             break;
847 
848         case 3:
849             Status = BuildSidListFromDomainAndName((servername != NULL) ? &ServerName : NULL,
850                                                    (PLOCALGROUP_MEMBERS_INFO_3)buf,
851                                                    totalentries,
852                                                    &MemberList);
853             if (!NT_SUCCESS(Status))
854             {
855                 ERR("BuildSidListFromDomainAndName failed (Status %08lx)\n", Status);
856                 ApiStatus = NetpNtStatusToApiStatus(Status);
857                 goto done;
858             }
859             break;
860 
861         default:
862             ApiStatus = ERROR_INVALID_LEVEL;
863             goto done;
864     }
865 
866     /* Connect to the SAM Server */
867     Status = SamConnect((servername != NULL) ? &ServerName : NULL,
868                         &ServerHandle,
869                         SAM_SERVER_CONNECT | SAM_SERVER_LOOKUP_DOMAIN,
870                         NULL);
871     if (!NT_SUCCESS(Status))
872     {
873         ERR("SamConnect failed (Status %08lx)\n", Status);
874         ApiStatus = NetpNtStatusToApiStatus(Status);
875         goto done;
876     }
877 
878     /* Open the Builtin Domain */
879     Status = OpenBuiltinDomain(ServerHandle,
880                                DOMAIN_LOOKUP,
881                                &DomainHandle);
882     if (!NT_SUCCESS(Status))
883     {
884         ERR("OpenBuiltinDomain failed (Status %08lx)\n", Status);
885         ApiStatus = NetpNtStatusToApiStatus(Status);
886         goto done;
887     }
888 
889     /* Open the alias account in the builtin domain */
890     ApiStatus = OpenAliasByName(DomainHandle,
891                                 &AliasName,
892                                 ALIAS_REMOVE_MEMBER,
893                                 &AliasHandle);
894     if (ApiStatus != NERR_Success && ApiStatus != ERROR_NONE_MAPPED)
895     {
896         ERR("OpenAliasByName(%wZ) failed (ApiStatus %lu)\n", &AliasName, ApiStatus);
897         goto done;
898     }
899 
900     if (AliasHandle == NULL)
901     {
902         if (DomainHandle != NULL)
903             SamCloseHandle(DomainHandle);
904 
905         /* Open the Acount Domain */
906         Status = OpenAccountDomain(ServerHandle,
907                                    (servername != NULL) ? &ServerName : NULL,
908                                    DOMAIN_LOOKUP,
909                                    &DomainHandle);
910         if (!NT_SUCCESS(Status))
911         {
912             ERR("OpenAccountDomain failed (Status %08lx)\n", Status);
913             ApiStatus = NetpNtStatusToApiStatus(Status);
914             goto done;
915         }
916 
917         /* Open the alias account in the account domain */
918         ApiStatus = OpenAliasByName(DomainHandle,
919                                     &AliasName,
920                                     ALIAS_REMOVE_MEMBER,
921                                     &AliasHandle);
922         if (ApiStatus != NERR_Success)
923         {
924             ERR("OpenAliasByName(%wZ) failed (ApiStatus %lu)\n", &AliasName, ApiStatus);
925             if (ApiStatus == ERROR_NONE_MAPPED)
926                 ApiStatus = NERR_GroupNotFound;
927             goto done;
928         }
929     }
930 
931     /* Remove members from the alias */
932     for (i = 0; i < totalentries; i++)
933     {
934         Status = SamRemoveMemberFromAlias(AliasHandle,
935                                           MemberList[i].lgrmi0_sid);
936         if (!NT_SUCCESS(Status))
937         {
938             ERR("SamAddMemberToAlias failed (Status %lu)\n", Status);
939             ApiStatus = NetpNtStatusToApiStatus(Status);
940             goto done;
941         }
942     }
943 
944 done:
945     if (level == 3 && MemberList != NULL)
946     {
947         for (i = 0; i < totalentries; i++)
948         {
949             if (MemberList[i].lgrmi0_sid != NULL)
950                 NetApiBufferFree(MemberList[i].lgrmi0_sid);
951         }
952 
953         NetApiBufferFree(MemberList);
954     }
955 
956     if (AliasHandle != NULL)
957         SamCloseHandle(AliasHandle);
958 
959     if (DomainHandle != NULL)
960         SamCloseHandle(DomainHandle);
961 
962     if (ServerHandle != NULL)
963         SamCloseHandle(ServerHandle);
964 
965     return ApiStatus;
966 }
967 
968 
969 /************************************************************
970  *                NetLocalGroupEnum  (NETAPI32.@)
971  */
972 NET_API_STATUS
973 WINAPI
974 NetLocalGroupEnum(
975     LPCWSTR servername,
976     DWORD level,
977     LPBYTE* bufptr,
978     DWORD prefmaxlen,
979     LPDWORD entriesread,
980     LPDWORD totalentries,
981     PDWORD_PTR resumehandle)
982 {
983     UNICODE_STRING ServerName;
984     PSAM_RID_ENUMERATION CurrentAlias;
985     PENUM_CONTEXT EnumContext = NULL;
986     ULONG i;
987     SAM_HANDLE AliasHandle = NULL;
988     PALIAS_GENERAL_INFORMATION AliasInfo = NULL;
989     LPVOID Buffer = NULL;
990     NET_API_STATUS ApiStatus = NERR_Success;
991     NTSTATUS Status = STATUS_SUCCESS;
992 
993     TRACE("(%s %d %p %d %p %p %p) stub!\n", debugstr_w(servername),
994           level, bufptr, prefmaxlen, entriesread, totalentries, resumehandle);
995 
996     *entriesread = 0;
997     *totalentries = 0;
998     *bufptr = NULL;
999 
1000     if (servername != NULL)
1001         RtlInitUnicodeString(&ServerName, servername);
1002 
1003     if (resumehandle != NULL && *resumehandle != 0)
1004     {
1005         EnumContext = (PENUM_CONTEXT)*resumehandle;
1006     }
1007     else
1008     {
1009         ApiStatus = NetApiBufferAllocate(sizeof(ENUM_CONTEXT), (PVOID*)&EnumContext);
1010         if (ApiStatus != NERR_Success)
1011             goto done;
1012 
1013         EnumContext->EnumerationContext = 0;
1014         EnumContext->Buffer = NULL;
1015         EnumContext->Returned = 0;
1016         EnumContext->Index = 0;
1017 
1018         Status = SamConnect((servername != NULL) ? &ServerName : NULL,
1019                             &EnumContext->ServerHandle,
1020                             SAM_SERVER_CONNECT | SAM_SERVER_LOOKUP_DOMAIN,
1021                             NULL);
1022         if (!NT_SUCCESS(Status))
1023         {
1024             ERR("SamConnect failed (Status %08lx)\n", Status);
1025             ApiStatus = NetpNtStatusToApiStatus(Status);
1026             goto done;
1027         }
1028 
1029         Status = OpenAccountDomain(EnumContext->ServerHandle,
1030                                    (servername != NULL) ? &ServerName : NULL,
1031                                    DOMAIN_LIST_ACCOUNTS | DOMAIN_LOOKUP,
1032                                    &EnumContext->AccountDomainHandle);
1033         if (!NT_SUCCESS(Status))
1034         {
1035             ERR("OpenAccountDomain failed (Status %08lx)\n", Status);
1036             ApiStatus = NetpNtStatusToApiStatus(Status);
1037             goto done;
1038         }
1039 
1040         Status = OpenBuiltinDomain(EnumContext->ServerHandle,
1041                                    DOMAIN_LIST_ACCOUNTS | DOMAIN_LOOKUP,
1042                                    &EnumContext->BuiltinDomainHandle);
1043         if (!NT_SUCCESS(Status))
1044         {
1045             ERR("OpenBuiltinDomain failed (Status %08lx)\n", Status);
1046             ApiStatus = NetpNtStatusToApiStatus(Status);
1047             goto done;
1048         }
1049 
1050         EnumContext->Phase = BuiltinPhase;
1051         EnumContext->DomainHandle = EnumContext->BuiltinDomainHandle;
1052     }
1053 
1054 
1055 //    while (TRUE)
1056 //    {
1057         TRACE("EnumContext->Index: %lu\n", EnumContext->Index);
1058         TRACE("EnumContext->Returned: %lu\n", EnumContext->Returned);
1059 
1060         if (EnumContext->Index >= EnumContext->Returned)
1061         {
1062             TRACE("Calling SamEnumerateAliasesInDomain\n");
1063 
1064             Status = SamEnumerateAliasesInDomain(EnumContext->DomainHandle,
1065                                                  &EnumContext->EnumerationContext,
1066                                                  (PVOID *)&EnumContext->Buffer,
1067                                                  prefmaxlen,
1068                                                  &EnumContext->Returned);
1069 
1070             TRACE("SamEnumerateAliasesInDomain returned (Status %08lx)\n", Status);
1071             if (!NT_SUCCESS(Status))
1072             {
1073                 ERR("SamEnumerateAliasesInDomain failed (Status %08lx)\n", Status);
1074                 ApiStatus = NetpNtStatusToApiStatus(Status);
1075                 goto done;
1076             }
1077 
1078             if (Status == STATUS_MORE_ENTRIES)
1079             {
1080                 ApiStatus = NERR_BufTooSmall;
1081                 goto done;
1082             }
1083         }
1084 
1085         TRACE("EnumContext: %lu\n", EnumContext);
1086         TRACE("EnumContext->Returned: %lu\n", EnumContext->Returned);
1087         TRACE("EnumContext->Buffer: %p\n", EnumContext->Buffer);
1088 
1089         /* Get a pointer to the current alias */
1090         CurrentAlias = &EnumContext->Buffer[EnumContext->Index];
1091 
1092         TRACE("RID: %lu\n", CurrentAlias->RelativeId);
1093 
1094         Status = SamOpenAlias(EnumContext->DomainHandle,
1095                               ALIAS_READ_INFORMATION,
1096                               CurrentAlias->RelativeId,
1097                               &AliasHandle);
1098         if (!NT_SUCCESS(Status))
1099         {
1100             ERR("SamOpenAlias failed (Status %08lx)\n", Status);
1101             ApiStatus = NetpNtStatusToApiStatus(Status);
1102             goto done;
1103         }
1104 
1105         Status = SamQueryInformationAlias(AliasHandle,
1106                                           AliasGeneralInformation,
1107                                           (PVOID *)&AliasInfo);
1108         if (!NT_SUCCESS(Status))
1109         {
1110             ERR("SamQueryInformationAlias failed (Status %08lx)\n", Status);
1111             ApiStatus = NetpNtStatusToApiStatus(Status);
1112             goto done;
1113         }
1114 
1115         SamCloseHandle(AliasHandle);
1116         AliasHandle = NULL;
1117 
1118         TRACE("Name: %S\n", AliasInfo->Name.Buffer);
1119         TRACE("Comment: %S\n", AliasInfo->AdminComment.Buffer);
1120 
1121         ApiStatus = BuildAliasInfoBuffer(AliasInfo,
1122                                          level,
1123                                          &Buffer);
1124         if (ApiStatus != NERR_Success)
1125             goto done;
1126 
1127         if (AliasInfo != NULL)
1128         {
1129             FreeAliasInfo(AliasInfo);
1130             AliasInfo = NULL;
1131         }
1132 
1133         EnumContext->Index++;
1134 
1135         (*entriesread)++;
1136 
1137         if (EnumContext->Index == EnumContext->Returned)
1138         {
1139             switch (EnumContext->Phase)
1140             {
1141                 case BuiltinPhase:
1142                     EnumContext->Phase = AccountPhase;
1143                     EnumContext->DomainHandle = EnumContext->AccountDomainHandle;
1144                     EnumContext->EnumerationContext = 0;
1145                     EnumContext->Index = 0;
1146                     EnumContext->Returned = 0;
1147 
1148                     if (EnumContext->Buffer != NULL)
1149                     {
1150                         for (i = 0; i < EnumContext->Returned; i++)
1151                         {
1152                             SamFreeMemory(EnumContext->Buffer[i].Name.Buffer);
1153                         }
1154 
1155                         SamFreeMemory(EnumContext->Buffer);
1156                         EnumContext->Buffer = NULL;
1157                     }
1158                     break;
1159 
1160                 case AccountPhase:
1161                 case DonePhase:
1162                     EnumContext->Phase = DonePhase;
1163                     break;
1164             }
1165         }
1166 //    }
1167 
1168 done:
1169     if (ApiStatus == NERR_Success && EnumContext != NULL && EnumContext->Phase != DonePhase)
1170         ApiStatus = ERROR_MORE_DATA;
1171 
1172     if (EnumContext != NULL)
1173         *totalentries = EnumContext->Returned;
1174 
1175     if (resumehandle == NULL || ApiStatus != ERROR_MORE_DATA)
1176     {
1177         if (EnumContext != NULL)
1178         {
1179             if (EnumContext->BuiltinDomainHandle != NULL)
1180                 SamCloseHandle(EnumContext->BuiltinDomainHandle);
1181 
1182             if (EnumContext->AccountDomainHandle != NULL)
1183                 SamCloseHandle(EnumContext->AccountDomainHandle);
1184 
1185             if (EnumContext->ServerHandle != NULL)
1186                 SamCloseHandle(EnumContext->ServerHandle);
1187 
1188             if (EnumContext->Buffer != NULL)
1189             {
1190                 for (i = 0; i < EnumContext->Returned; i++)
1191                 {
1192                     SamFreeMemory(EnumContext->Buffer[i].Name.Buffer);
1193                 }
1194 
1195                 SamFreeMemory(EnumContext->Buffer);
1196             }
1197 
1198             NetApiBufferFree(EnumContext);
1199             EnumContext = NULL;
1200         }
1201     }
1202 
1203     if (AliasHandle != NULL)
1204         SamCloseHandle(AliasHandle);
1205 
1206     if (AliasInfo != NULL)
1207         FreeAliasInfo(AliasInfo);
1208 
1209     if (resumehandle != NULL)
1210         *resumehandle = (DWORD_PTR)EnumContext;
1211 
1212     *bufptr = (LPBYTE)Buffer;
1213 
1214     TRACE ("return %lu\n", ApiStatus);
1215 
1216     return ApiStatus;
1217 }
1218 
1219 
1220 /************************************************************
1221  * NetLocalGroupGetInfo  (NETAPI32.@)
1222  */
1223 NET_API_STATUS
1224 WINAPI
1225 NetLocalGroupGetInfo(
1226     LPCWSTR servername,
1227     LPCWSTR groupname,
1228     DWORD level,
1229     LPBYTE* bufptr)
1230 {
1231     UNICODE_STRING ServerName;
1232     UNICODE_STRING GroupName;
1233     SAM_HANDLE ServerHandle = NULL;
1234     SAM_HANDLE DomainHandle = NULL;
1235     SAM_HANDLE AliasHandle = NULL;
1236     PALIAS_GENERAL_INFORMATION AliasInfo = NULL;
1237     LPVOID Buffer = NULL;
1238     NET_API_STATUS ApiStatus = NERR_Success;
1239     NTSTATUS Status = STATUS_SUCCESS;
1240 
1241     TRACE("(%s %s %d %p) stub!\n", debugstr_w(servername),
1242           debugstr_w(groupname), level, bufptr);
1243 
1244     if (servername != NULL)
1245         RtlInitUnicodeString(&ServerName, servername);
1246 
1247     RtlInitUnicodeString(&GroupName, groupname);
1248 
1249     /* Connect to the SAM Server */
1250     Status = SamConnect((servername != NULL) ? &ServerName : NULL,
1251                         &ServerHandle,
1252                         SAM_SERVER_CONNECT | SAM_SERVER_LOOKUP_DOMAIN,
1253                         NULL);
1254     if (!NT_SUCCESS(Status))
1255     {
1256         ERR("SamConnect failed (Status %08lx)\n", Status);
1257         ApiStatus = NetpNtStatusToApiStatus(Status);
1258         goto done;
1259     }
1260 
1261     /* Open the Builtin Domain */
1262     Status = OpenBuiltinDomain(ServerHandle,
1263                                DOMAIN_LOOKUP,
1264                                &DomainHandle);
1265     if (!NT_SUCCESS(Status))
1266     {
1267         ERR("OpenBuiltinDomain failed (Status %08lx)\n", Status);
1268         ApiStatus = NetpNtStatusToApiStatus(Status);
1269         goto done;
1270     }
1271 
1272     /* Open the alias account in the builtin domain */
1273     ApiStatus = OpenAliasByName(DomainHandle,
1274                                 &GroupName,
1275                                 ALIAS_READ_INFORMATION,
1276                                 &AliasHandle);
1277     if (ApiStatus != NERR_Success && ApiStatus != ERROR_NONE_MAPPED)
1278     {
1279         ERR("OpenAliasByName(%wZ) failed (ApiStatus %lu)\n", &GroupName, ApiStatus);
1280         goto done;
1281     }
1282 
1283     if (AliasHandle == NULL)
1284     {
1285         if (DomainHandle != NULL)
1286             SamCloseHandle(DomainHandle);
1287 
1288         /* Open the Acount Domain */
1289         Status = OpenAccountDomain(ServerHandle,
1290                                    (servername != NULL) ? &ServerName : NULL,
1291                                    DOMAIN_LOOKUP,
1292                                    &DomainHandle);
1293         if (!NT_SUCCESS(Status))
1294         {
1295             ERR("OpenAccountDomain failed (Status %08lx)\n", Status);
1296             ApiStatus = NetpNtStatusToApiStatus(Status);
1297             goto done;
1298         }
1299 
1300         /* Open the alias account in the account domain */
1301         ApiStatus = OpenAliasByName(DomainHandle,
1302                                     &GroupName,
1303                                     ALIAS_READ_INFORMATION,
1304                                     &AliasHandle);
1305         if (ApiStatus != NERR_Success)
1306         {
1307             ERR("OpenAliasByName(%wZ) failed (ApiStatus %lu)\n", &GroupName, ApiStatus);
1308             if (ApiStatus == ERROR_NONE_MAPPED)
1309                 ApiStatus = NERR_GroupNotFound;
1310             goto done;
1311         }
1312     }
1313 
1314     Status = SamQueryInformationAlias(AliasHandle,
1315                                       AliasGeneralInformation,
1316                                       (PVOID *)&AliasInfo);
1317     if (!NT_SUCCESS(Status))
1318     {
1319         ERR("SamQueryInformationAlias failed (Status %08lx)\n", Status);
1320         ApiStatus = NetpNtStatusToApiStatus(Status);
1321         goto done;
1322     }
1323 
1324     ApiStatus = BuildAliasInfoBuffer(AliasInfo,
1325                                      level,
1326                                      &Buffer);
1327     if (ApiStatus != NERR_Success)
1328         goto done;
1329 
1330 done:
1331     if (AliasInfo != NULL)
1332         FreeAliasInfo(AliasInfo);
1333 
1334     if (AliasHandle != NULL)
1335         SamCloseHandle(AliasHandle);
1336 
1337     if (DomainHandle != NULL)
1338         SamCloseHandle(DomainHandle);
1339 
1340     if (ServerHandle != NULL)
1341         SamCloseHandle(ServerHandle);
1342 
1343     *bufptr = (LPBYTE)Buffer;
1344 
1345     return ApiStatus;
1346 }
1347 
1348 
1349 /************************************************************
1350  *                NetLocalGroupGetMembers  (NETAPI32.@)
1351  */
1352 NET_API_STATUS
1353 WINAPI
1354 NetLocalGroupGetMembers(
1355     LPCWSTR servername,
1356     LPCWSTR localgroupname,
1357     DWORD level,
1358     LPBYTE* bufptr,
1359     DWORD prefmaxlen,
1360     LPDWORD entriesread,
1361     LPDWORD totalentries,
1362     PDWORD_PTR resumehandle)
1363 {
1364     OBJECT_ATTRIBUTES ObjectAttributes;
1365     UNICODE_STRING ServerName;
1366     UNICODE_STRING AliasName;
1367     PMEMBER_ENUM_CONTEXT EnumContext = NULL;
1368     LPVOID Buffer = NULL;
1369     PLOCALGROUP_MEMBERS_INFO_0 MembersInfo0;
1370     PLOCALGROUP_MEMBERS_INFO_1 MembersInfo1;
1371     PLOCALGROUP_MEMBERS_INFO_2 MembersInfo2;
1372     PLOCALGROUP_MEMBERS_INFO_3 MembersInfo3;
1373     LPWSTR Ptr;
1374     ULONG Size = 0;
1375     ULONG SidLength;
1376     ULONG i;
1377     NET_API_STATUS ApiStatus = NERR_Success;
1378     NTSTATUS Status = STATUS_SUCCESS;
1379 
1380     TRACE("(%s %s %d %p %d %p %p %p)\n", debugstr_w(servername),
1381           debugstr_w(localgroupname), level, bufptr, prefmaxlen, entriesread,
1382           totalentries, resumehandle);
1383 
1384     *entriesread = 0;
1385     *totalentries = 0;
1386     *bufptr = NULL;
1387 
1388     if (servername != NULL)
1389         RtlInitUnicodeString(&ServerName, servername);
1390 
1391     RtlInitUnicodeString(&AliasName, localgroupname);
1392 
1393     if (resumehandle != NULL && *resumehandle != 0)
1394     {
1395         EnumContext = (PMEMBER_ENUM_CONTEXT)*resumehandle;
1396     }
1397     else
1398     {
1399         /* Allocate the enumeration context */
1400         ApiStatus = NetApiBufferAllocate(sizeof(MEMBER_ENUM_CONTEXT), (PVOID*)&EnumContext);
1401         if (ApiStatus != NERR_Success)
1402             goto done;
1403 
1404         /* Connect to the SAM Server */
1405         Status = SamConnect((servername != NULL) ? &ServerName : NULL,
1406                             &EnumContext->ServerHandle,
1407                             SAM_SERVER_CONNECT | SAM_SERVER_LOOKUP_DOMAIN,
1408                             NULL);
1409         if (!NT_SUCCESS(Status))
1410         {
1411             ERR("SamConnect failed (Status %08lx)\n", Status);
1412             ApiStatus = NetpNtStatusToApiStatus(Status);
1413             goto done;
1414         }
1415 
1416         /* Open the Builtin Domain */
1417         Status = OpenBuiltinDomain(EnumContext->ServerHandle,
1418                                    DOMAIN_LOOKUP,
1419                                    &EnumContext->DomainHandle);
1420         if (!NT_SUCCESS(Status))
1421         {
1422             ERR("OpenBuiltinDomain failed (Status %08lx)\n", Status);
1423             ApiStatus = NetpNtStatusToApiStatus(Status);
1424             goto done;
1425         }
1426 
1427         /* Open the alias account in the builtin domain */
1428         ApiStatus = OpenAliasByName(EnumContext->DomainHandle,
1429                                     &AliasName,
1430                                     ALIAS_LIST_MEMBERS,
1431                                     &EnumContext->AliasHandle);
1432         if (ApiStatus != NERR_Success && ApiStatus != ERROR_NONE_MAPPED)
1433         {
1434             ERR("OpenAliasByName(%wZ) failed (ApiStatus %lu)\n", &AliasName, ApiStatus);
1435             goto done;
1436         }
1437 
1438         if (EnumContext->AliasHandle == NULL)
1439         {
1440             if (EnumContext->DomainHandle != NULL)
1441                 SamCloseHandle(EnumContext->DomainHandle);
1442 
1443             /* Open the Acount Domain */
1444             Status = OpenAccountDomain(EnumContext->ServerHandle,
1445                                        (servername != NULL) ? &ServerName : NULL,
1446                                        DOMAIN_LOOKUP,
1447                                        &EnumContext->DomainHandle);
1448             if (!NT_SUCCESS(Status))
1449             {
1450                 ERR("OpenAccountDomain failed (Status %08lx)\n", Status);
1451                 ApiStatus = NetpNtStatusToApiStatus(Status);
1452                 goto done;
1453             }
1454 
1455             /* Open the alias account in the account domain */
1456             ApiStatus = OpenAliasByName(EnumContext->DomainHandle,
1457                                         &AliasName,
1458                                         ALIAS_LIST_MEMBERS,
1459                                         &EnumContext->AliasHandle);
1460             if (ApiStatus != NERR_Success)
1461             {
1462                 ERR("OpenAliasByName(%wZ) failed (ApiStatus %lu)\n", &AliasName, ApiStatus);
1463                 if (ApiStatus == ERROR_NONE_MAPPED)
1464                     ApiStatus = NERR_GroupNotFound;
1465                 goto done;
1466             }
1467         }
1468 
1469         /* Get the member list */
1470         Status = SamGetMembersInAlias(EnumContext->AliasHandle,
1471                                       &EnumContext->Sids,
1472                                       &EnumContext->Count);
1473         if (!NT_SUCCESS(Status))
1474         {
1475             ERR("SamGetMemberInAlias failed (Status %08lx)\n", Status);
1476             ApiStatus = NetpNtStatusToApiStatus(Status);
1477             goto done;
1478         }
1479 
1480         if (EnumContext->Count == 0)
1481         {
1482             TRACE("No member found. We're done.\n");
1483             ApiStatus = NERR_Success;
1484             goto done;
1485         }
1486 
1487         /* Get name and domain information for all members */
1488         if (level != 0)
1489         {
1490             InitializeObjectAttributes(&ObjectAttributes,
1491                                        NULL,
1492                                        0,
1493                                        0,
1494                                        NULL);
1495 
1496             Status = LsaOpenPolicy((servername != NULL) ? &ServerName : NULL,
1497                                    (PLSA_OBJECT_ATTRIBUTES)&ObjectAttributes,
1498                                    POLICY_EXECUTE,
1499                                    &EnumContext->LsaHandle);
1500             if (!NT_SUCCESS(Status))
1501             {
1502                 ApiStatus = NetpNtStatusToApiStatus(Status);
1503                 goto done;
1504             }
1505 
1506             Status = LsaLookupSids(EnumContext->LsaHandle,
1507                                    EnumContext->Count,
1508                                    EnumContext->Sids,
1509                                    &EnumContext->Domains,
1510                                    &EnumContext->Names);
1511             if (!NT_SUCCESS(Status))
1512             {
1513                 ApiStatus = NetpNtStatusToApiStatus(Status);
1514                 goto done;
1515             }
1516         }
1517     }
1518 
1519     /* Calculate the required buffer size */
1520     for (i = 0; i < EnumContext->Count; i++)
1521     {
1522         switch (level)
1523         {
1524             case 0:
1525                 Size += sizeof(LOCALGROUP_MEMBERS_INFO_0) +
1526                         RtlLengthSid(EnumContext->Sids[i]);
1527                 break;
1528 
1529             case 1:
1530                 Size += sizeof(LOCALGROUP_MEMBERS_INFO_1) +
1531                         RtlLengthSid(EnumContext->Sids[i]) +
1532                         EnumContext->Names[i].Name.Length + sizeof(WCHAR);
1533                 break;
1534 
1535             case 2:
1536                 Size += sizeof(LOCALGROUP_MEMBERS_INFO_2) +
1537                         RtlLengthSid(EnumContext->Sids[i]) +
1538                         EnumContext->Names[i].Name.Length + sizeof(WCHAR);
1539                 if (EnumContext->Names[i].DomainIndex >= 0)
1540                     Size += EnumContext->Domains->Domains[EnumContext->Names[i].DomainIndex].Name.Length + sizeof(WCHAR);
1541                 break;
1542 
1543             case 3:
1544                 Size += sizeof(LOCALGROUP_MEMBERS_INFO_3) +
1545                         EnumContext->Names[i].Name.Length + sizeof(WCHAR);
1546                 if (EnumContext->Names[i].DomainIndex >= 0)
1547                     Size += EnumContext->Domains->Domains[EnumContext->Names[i].DomainIndex].Name.Length + sizeof(WCHAR);
1548                 break;
1549 
1550             default:
1551                 ApiStatus = ERROR_INVALID_LEVEL;
1552                 goto done;
1553         }
1554     }
1555 
1556     /* Allocate the member buffer */
1557     ApiStatus = NetApiBufferAllocate(Size, &Buffer);
1558     if (ApiStatus != NERR_Success)
1559         goto done;
1560 
1561     ZeroMemory(Buffer, Size);
1562 
1563     /* Fill the member buffer */
1564     switch (level)
1565     {
1566         case 0:
1567             MembersInfo0 = (PLOCALGROUP_MEMBERS_INFO_0)Buffer;
1568             Ptr = (PVOID)((ULONG_PTR)Buffer + sizeof(LOCALGROUP_MEMBERS_INFO_0) * EnumContext->Count);
1569             break;
1570 
1571         case 1:
1572             MembersInfo1 = (PLOCALGROUP_MEMBERS_INFO_1)Buffer;
1573             Ptr = (PVOID)((ULONG_PTR)Buffer + sizeof(LOCALGROUP_MEMBERS_INFO_1) * EnumContext->Count);
1574             break;
1575 
1576         case 2:
1577             MembersInfo2 = (PLOCALGROUP_MEMBERS_INFO_2)Buffer;
1578             Ptr = (PVOID)((ULONG_PTR)Buffer + sizeof(LOCALGROUP_MEMBERS_INFO_2) * EnumContext->Count);
1579             break;
1580 
1581         case 3:
1582             MembersInfo3 = (PLOCALGROUP_MEMBERS_INFO_3)Buffer;
1583             Ptr = (PVOID)((ULONG_PTR)Buffer + sizeof(LOCALGROUP_MEMBERS_INFO_3) * EnumContext->Count);
1584             break;
1585     }
1586 
1587     for (i = 0; i < EnumContext->Count; i++)
1588     {
1589         switch (level)
1590         {
1591             case 0:
1592                 MembersInfo0->lgrmi0_sid = (PSID)Ptr;
1593 
1594                 SidLength = RtlLengthSid(EnumContext->Sids[i]);
1595                 memcpy(MembersInfo0->lgrmi0_sid,
1596                        EnumContext->Sids[i],
1597                        SidLength);
1598                 Ptr = (PVOID)((ULONG_PTR)Ptr + SidLength);
1599                 MembersInfo0++;
1600                 break;
1601 
1602             case 1:
1603                 MembersInfo1->lgrmi1_sid = (PSID)Ptr;
1604 
1605                 SidLength = RtlLengthSid(EnumContext->Sids[i]);
1606                 memcpy(MembersInfo1->lgrmi1_sid,
1607                        EnumContext->Sids[i],
1608                        SidLength);
1609 
1610                 Ptr = (PVOID)((ULONG_PTR)Ptr + SidLength);
1611 
1612                 MembersInfo1->lgrmi1_sidusage = EnumContext->Names[i].Use;
1613 
1614                 TRACE("Name: %S\n", EnumContext->Names[i].Name.Buffer);
1615 
1616                 MembersInfo1->lgrmi1_name = Ptr;
1617 
1618                 memcpy(MembersInfo1->lgrmi1_name,
1619                        EnumContext->Names[i].Name.Buffer,
1620                        EnumContext->Names[i].Name.Length);
1621                 Ptr = (PVOID)((ULONG_PTR)Ptr + EnumContext->Names[i].Name.Length + sizeof(WCHAR));
1622                 MembersInfo1++;
1623                 break;
1624 
1625             case 2:
1626                 MembersInfo2->lgrmi2_sid = (PSID)Ptr;
1627 
1628                 SidLength = RtlLengthSid(EnumContext->Sids[i]);
1629                 memcpy(MembersInfo2->lgrmi2_sid,
1630                        EnumContext->Sids[i],
1631                        SidLength);
1632 
1633                 Ptr = (PVOID)((ULONG_PTR)Ptr + SidLength);
1634 
1635                 MembersInfo2->lgrmi2_sidusage = EnumContext->Names[i].Use;
1636 
1637                 MembersInfo2->lgrmi2_domainandname = Ptr;
1638 
1639                 if (EnumContext->Names[i].DomainIndex >= 0)
1640                 {
1641                     memcpy(MembersInfo2->lgrmi2_domainandname,
1642                            EnumContext->Domains->Domains[EnumContext->Names[i].DomainIndex].Name.Buffer,
1643                            EnumContext->Domains->Domains[EnumContext->Names[i].DomainIndex].Name.Length);
1644 
1645                     Ptr = (PVOID)((ULONG_PTR)Ptr + EnumContext->Domains->Domains[EnumContext->Names[i].DomainIndex].Name.Length);
1646 
1647                     *((LPWSTR)Ptr) = L'\\';
1648 
1649                     Ptr = (PVOID)((ULONG_PTR)Ptr + sizeof(WCHAR));
1650                 }
1651 
1652                 memcpy(Ptr,
1653                        EnumContext->Names[i].Name.Buffer,
1654                        EnumContext->Names[i].Name.Length);
1655                 Ptr = (PVOID)((ULONG_PTR)Ptr + EnumContext->Names[i].Name.Length + sizeof(WCHAR));
1656                 MembersInfo2++;
1657                 break;
1658 
1659             case 3:
1660                 MembersInfo3->lgrmi3_domainandname = Ptr;
1661 
1662                 if (EnumContext->Names[i].DomainIndex >= 0)
1663                 {
1664                     memcpy(MembersInfo3->lgrmi3_domainandname,
1665                            EnumContext->Domains->Domains[EnumContext->Names[i].DomainIndex].Name.Buffer,
1666                            EnumContext->Domains->Domains[EnumContext->Names[i].DomainIndex].Name.Length);
1667 
1668                     Ptr = (PVOID)((ULONG_PTR)Ptr + EnumContext->Domains->Domains[EnumContext->Names[i].DomainIndex].Name.Length);
1669 
1670                     *((LPWSTR)Ptr) = L'\\';
1671 
1672                     Ptr = (PVOID)((ULONG_PTR)Ptr + sizeof(WCHAR));
1673                 }
1674 
1675                 memcpy(Ptr,
1676                        EnumContext->Names[i].Name.Buffer,
1677                        EnumContext->Names[i].Name.Length);
1678                 Ptr = (PVOID)((ULONG_PTR)Ptr + EnumContext->Names[i].Name.Length + sizeof(WCHAR));
1679                 MembersInfo3++;
1680                 break;
1681         }
1682     }
1683 
1684     *entriesread = EnumContext->Count;
1685 
1686     *bufptr = (LPBYTE)Buffer;
1687 
1688 done:
1689     if (EnumContext != NULL)
1690         *totalentries = EnumContext->Count;
1691 
1692     if (resumehandle == NULL || ApiStatus != ERROR_MORE_DATA)
1693     {
1694         /* Release the enumeration context */
1695         if (EnumContext != NULL)
1696         {
1697             if (EnumContext->LsaHandle != NULL)
1698                 LsaClose(EnumContext->LsaHandle);
1699 
1700             if (EnumContext->AliasHandle != NULL)
1701                 SamCloseHandle(EnumContext->AliasHandle);
1702 
1703             if (EnumContext->DomainHandle != NULL)
1704                 SamCloseHandle(EnumContext->DomainHandle);
1705 
1706             if (EnumContext->ServerHandle != NULL)
1707                 SamCloseHandle(EnumContext->ServerHandle);
1708 
1709             if (EnumContext->Sids != NULL)
1710                 SamFreeMemory(EnumContext->Sids);
1711 
1712             if (EnumContext->Domains != NULL)
1713                 LsaFreeMemory(EnumContext->Domains);
1714 
1715             if (EnumContext->Names != NULL)
1716                 LsaFreeMemory(EnumContext->Names);
1717 
1718             NetApiBufferFree(EnumContext);
1719             EnumContext = NULL;
1720         }
1721     }
1722 
1723     return ApiStatus;
1724 }
1725 
1726 
1727 /************************************************************
1728  *                NetLocalGroupSetInfo  (NETAPI32.@)
1729  */
1730 NET_API_STATUS
1731 WINAPI
1732 NetLocalGroupSetInfo(
1733     LPCWSTR servername,
1734     LPCWSTR groupname,
1735     DWORD level,
1736     LPBYTE buf,
1737     LPDWORD parm_err)
1738 {
1739     UNICODE_STRING ServerName;
1740     UNICODE_STRING AliasName;
1741     SAM_HANDLE ServerHandle = NULL;
1742     SAM_HANDLE DomainHandle = NULL;
1743     SAM_HANDLE AliasHandle = NULL;
1744     ALIAS_NAME_INFORMATION AliasNameInfo;
1745     ALIAS_ADM_COMMENT_INFORMATION AdminCommentInfo;
1746     NET_API_STATUS ApiStatus = NERR_Success;
1747     NTSTATUS Status = STATUS_SUCCESS;
1748 
1749     TRACE("(%s %s %d %p %p)\n", debugstr_w(servername),
1750           debugstr_w(groupname), level, buf, parm_err);
1751 
1752     if (parm_err != NULL)
1753         *parm_err = PARM_ERROR_NONE;
1754 
1755     if (servername != NULL)
1756         RtlInitUnicodeString(&ServerName, servername);
1757 
1758     RtlInitUnicodeString(&AliasName, groupname);
1759 
1760     /* Connect to the SAM Server */
1761     Status = SamConnect((servername != NULL) ? &ServerName : NULL,
1762                         &ServerHandle,
1763                         SAM_SERVER_CONNECT | SAM_SERVER_LOOKUP_DOMAIN,
1764                         NULL);
1765     if (!NT_SUCCESS(Status))
1766     {
1767         ERR("SamConnect failed (Status %08lx)\n", Status);
1768         ApiStatus = NetpNtStatusToApiStatus(Status);
1769         goto done;
1770     }
1771 
1772     /* Open the Builtin Domain */
1773     Status = OpenBuiltinDomain(ServerHandle,
1774                                DOMAIN_LOOKUP,
1775                                &DomainHandle);
1776     if (!NT_SUCCESS(Status))
1777     {
1778         ERR("OpenBuiltinDomain failed (Status %08lx)\n", Status);
1779         ApiStatus = NetpNtStatusToApiStatus(Status);
1780         goto done;
1781     }
1782 
1783     /* Open the alias account in the builtin domain */
1784     ApiStatus = OpenAliasByName(DomainHandle,
1785                                 &AliasName,
1786                                 ALIAS_WRITE_ACCOUNT,
1787                                 &AliasHandle);
1788     if (ApiStatus != NERR_Success && ApiStatus != ERROR_NONE_MAPPED)
1789     {
1790         ERR("OpenAliasByName(%wZ) failed (ApiStatus %lu)\n", &AliasName, ApiStatus);
1791         goto done;
1792     }
1793 
1794     if (AliasHandle == NULL)
1795     {
1796         if (DomainHandle != NULL)
1797             SamCloseHandle(DomainHandle);
1798 
1799         /* Open the Acount Domain */
1800         Status = OpenAccountDomain(ServerHandle,
1801                                    (servername != NULL) ? &ServerName : NULL,
1802                                    DOMAIN_LOOKUP,
1803                                    &DomainHandle);
1804         if (!NT_SUCCESS(Status))
1805         {
1806             ERR("OpenAccountDomain failed (Status %08lx)\n", Status);
1807             ApiStatus = NetpNtStatusToApiStatus(Status);
1808             goto done;
1809         }
1810 
1811         /* Open the alias account in the account domain */
1812         ApiStatus = OpenAliasByName(DomainHandle,
1813                                     &AliasName,
1814                                     ALIAS_WRITE_ACCOUNT,
1815                                     &AliasHandle);
1816         if (ApiStatus != NERR_Success)
1817         {
1818             ERR("OpenAliasByName(%wZ) failed (ApiStatus %lu)\n", &AliasName, ApiStatus);
1819             if (ApiStatus == ERROR_NONE_MAPPED)
1820                 ApiStatus = NERR_GroupNotFound;
1821             goto done;
1822         }
1823     }
1824 
1825     switch (level)
1826     {
1827         case 0:
1828             /* Set the alias name */
1829             RtlInitUnicodeString(&AliasNameInfo.Name,
1830                                  ((PLOCALGROUP_INFO_0)buf)->lgrpi0_name);
1831 
1832             Status = SamSetInformationAlias(AliasHandle,
1833                                             AliasNameInformation,
1834                                             &AliasNameInfo);
1835             if (!NT_SUCCESS(Status))
1836             {
1837                 TRACE("SamSetInformationAlias failed (ApiStatus %lu)\n", ApiStatus);
1838                 ApiStatus = NetpNtStatusToApiStatus(Status);
1839                 goto done;
1840             }
1841             break;
1842 
1843         case 1:
1844         case 1002:
1845             /* Set the alias admin comment */
1846             if (level == 1)
1847                 RtlInitUnicodeString(&AdminCommentInfo.AdminComment,
1848                                      ((PLOCALGROUP_INFO_1)buf)->lgrpi1_comment);
1849             else
1850                 RtlInitUnicodeString(&AdminCommentInfo.AdminComment,
1851                                      ((PLOCALGROUP_INFO_1002)buf)->lgrpi1002_comment);
1852 
1853             Status = SamSetInformationAlias(AliasHandle,
1854                                             AliasAdminCommentInformation,
1855                                             &AdminCommentInfo);
1856             if (!NT_SUCCESS(Status))
1857             {
1858                 TRACE("SamSetInformationAlias failed (ApiStatus %lu)\n", ApiStatus);
1859                 ApiStatus = NetpNtStatusToApiStatus(Status);
1860                 goto done;
1861             }
1862             break;
1863 
1864         default:
1865             ApiStatus = ERROR_INVALID_LEVEL;
1866             goto done;
1867     }
1868 
1869 done:
1870     if (AliasHandle != NULL)
1871         SamCloseHandle(AliasHandle);
1872 
1873     if (DomainHandle != NULL)
1874         SamCloseHandle(DomainHandle);
1875 
1876     if (ServerHandle != NULL)
1877         SamCloseHandle(ServerHandle);
1878 
1879     return ApiStatus;
1880 }
1881 
1882 
1883 /************************************************************
1884  *                NetLocalGroupSetMember (NETAPI32.@)
1885  */
1886 NET_API_STATUS
1887 WINAPI
1888 NetLocalGroupSetMembers(
1889     LPCWSTR servername,
1890     LPCWSTR groupname,
1891     DWORD level,
1892     LPBYTE buf,
1893     DWORD totalentries)
1894 {
1895     FIXME("(%s %s %d %p %d) stub!\n", debugstr_w(servername),
1896             debugstr_w(groupname), level, buf, totalentries);
1897     return NERR_Success;
1898 }
1899 
1900 /* EOF */
1901