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