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