743e685c9419ff945641c97488e4bcfeff743570
[reactos.git] / dll / win32 / netapi32 / group_new.c
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 _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 } ENUM_CONTEXT, *PENUM_CONTEXT;
34
35
36 /* FUNCTIONS *****************************************************************/
37
38 static
39 NET_API_STATUS
40 BuildGroupInfoBuffer(
41 _In_ PGROUP_GENERAL_INFORMATION GroupInfo,
42 _In_ DWORD Level,
43 _In_ DWORD GroupId,
44 _Out_ LPVOID *Buffer)
45 {
46 PVOID GroupBuffer = NULL;
47 PGROUP_INFO_0 GroupInfo0;
48 PGROUP_INFO_1 GroupInfo1;
49 PGROUP_INFO_2 GroupInfo2;
50 PGROUP_INFO_3 GroupInfo3;
51 PWSTR Ptr;
52 ULONG Size = 0;
53 NET_API_STATUS ApiStatus = NERR_Success;
54
55 *Buffer = NULL;
56
57 switch (Level)
58 {
59 case 0:
60 Size = sizeof(GROUP_INFO_0) +
61 GroupInfo->Name.Length + sizeof(WCHAR);
62 break;
63
64 case 1:
65 Size = sizeof(GROUP_INFO_1) +
66 GroupInfo->Name.Length + sizeof(WCHAR) +
67 GroupInfo->AdminComment.Length + sizeof(WCHAR);
68 break;
69
70 case 2:
71 Size = sizeof(GROUP_INFO_2) +
72 GroupInfo->Name.Length + sizeof(WCHAR) +
73 GroupInfo->AdminComment.Length + sizeof(WCHAR);
74 break;
75
76 case 3:
77 Size = sizeof(GROUP_INFO_3) +
78 GroupInfo->Name.Length + sizeof(WCHAR) +
79 GroupInfo->AdminComment.Length + sizeof(WCHAR);
80 /* FIXME: Sid size */
81 break;
82
83 default:
84 ApiStatus = ERROR_INVALID_LEVEL;
85 goto done;
86 }
87
88 ApiStatus = NetApiBufferAllocate(Size, &GroupBuffer);
89 if (ApiStatus != NERR_Success)
90 goto done;
91
92 ZeroMemory(GroupBuffer, Size);
93
94 switch (Level)
95 {
96 case 0:
97 GroupInfo0 = (PGROUP_INFO_0)GroupBuffer;
98
99 Ptr = (PWSTR)((ULONG_PTR)GroupInfo0 + sizeof(LOCALGROUP_INFO_0));
100 GroupInfo0->grpi0_name = Ptr;
101
102 memcpy(GroupInfo0->grpi0_name,
103 GroupInfo->Name.Buffer,
104 GroupInfo->Name.Length);
105 GroupInfo0->grpi0_name[GroupInfo->Name.Length / sizeof(WCHAR)] = UNICODE_NULL;
106 break;
107
108 case 1:
109 GroupInfo1 = (PGROUP_INFO_1)GroupBuffer;
110
111 Ptr = (PWSTR)((ULONG_PTR)GroupInfo1 + sizeof(GROUP_INFO_1));
112 GroupInfo1->grpi1_name = Ptr;
113
114 memcpy(GroupInfo1->grpi1_name,
115 GroupInfo->Name.Buffer,
116 GroupInfo->Name.Length);
117 GroupInfo1->grpi1_name[GroupInfo->Name.Length / sizeof(WCHAR)] = UNICODE_NULL;
118
119 Ptr = (PWSTR)((ULONG_PTR)Ptr + GroupInfo->Name.Length + sizeof(WCHAR));
120 GroupInfo1->grpi1_comment = Ptr;
121
122 memcpy(GroupInfo1->grpi1_comment,
123 GroupInfo->AdminComment.Buffer,
124 GroupInfo->AdminComment.Length);
125 GroupInfo1->grpi1_comment[GroupInfo->AdminComment.Length / sizeof(WCHAR)] = UNICODE_NULL;
126 break;
127
128 case 2:
129 GroupInfo2 = (PGROUP_INFO_2)GroupBuffer;
130
131 Ptr = (PWSTR)((ULONG_PTR)GroupInfo2 + sizeof(GROUP_INFO_2));
132 GroupInfo2->grpi2_name = Ptr;
133
134 memcpy(GroupInfo2->grpi2_name,
135 GroupInfo->Name.Buffer,
136 GroupInfo->Name.Length);
137 GroupInfo2->grpi2_name[GroupInfo->Name.Length / sizeof(WCHAR)] = UNICODE_NULL;
138
139 Ptr = (PWSTR)((ULONG_PTR)Ptr + GroupInfo->Name.Length + sizeof(WCHAR));
140 GroupInfo2->grpi2_comment = Ptr;
141
142 memcpy(GroupInfo2->grpi2_comment,
143 GroupInfo->AdminComment.Buffer,
144 GroupInfo->AdminComment.Length);
145 GroupInfo2->grpi2_comment[GroupInfo->AdminComment.Length / sizeof(WCHAR)] = UNICODE_NULL;
146
147 GroupInfo2->grpi2_group_id = GroupId;
148
149 GroupInfo2->grpi2_attributes= GroupInfo->Attributes;
150 break;
151
152 case 3:
153 GroupInfo3 = (PGROUP_INFO_3)GroupBuffer;
154
155 Ptr = (PWSTR)((ULONG_PTR)GroupInfo3 + sizeof(GROUP_INFO_3));
156 GroupInfo3->grpi3_name = Ptr;
157
158 memcpy(GroupInfo3->grpi3_name,
159 GroupInfo->Name.Buffer,
160 GroupInfo->Name.Length);
161 GroupInfo3->grpi3_name[GroupInfo->Name.Length / sizeof(WCHAR)] = UNICODE_NULL;
162
163 Ptr = (PWSTR)((ULONG_PTR)Ptr + GroupInfo->Name.Length + sizeof(WCHAR));
164 GroupInfo3->grpi3_comment = Ptr;
165
166 memcpy(GroupInfo3->grpi3_comment,
167 GroupInfo->AdminComment.Buffer,
168 GroupInfo->AdminComment.Length);
169 GroupInfo3->grpi3_comment[GroupInfo->AdminComment.Length / sizeof(WCHAR)] = UNICODE_NULL;
170
171 GroupInfo3->grpi3_group_sid = NULL; /* FIXME */
172
173 GroupInfo3->grpi3_attributes= GroupInfo->Attributes;
174 break;
175 }
176
177 done:
178 if (ApiStatus == NERR_Success)
179 {
180 *Buffer = GroupBuffer;
181 }
182 else
183 {
184 if (GroupBuffer != NULL)
185 NetApiBufferFree(GroupBuffer);
186 }
187
188 return ApiStatus;
189 }
190
191
192 static
193 VOID
194 FreeGroupInfo(
195 _In_ PGROUP_GENERAL_INFORMATION GroupInfo)
196 {
197 if (GroupInfo->Name.Buffer != NULL)
198 SamFreeMemory(GroupInfo->Name.Buffer);
199
200 if (GroupInfo->AdminComment.Buffer != NULL)
201 SamFreeMemory(GroupInfo->AdminComment.Buffer);
202
203 SamFreeMemory(GroupInfo);
204 }
205
206
207 static
208 NET_API_STATUS
209 OpenGroupByName(
210 _In_ SAM_HANDLE DomainHandle,
211 _In_ PUNICODE_STRING GroupName,
212 _In_ ULONG DesiredAccess,
213 _Out_ PSAM_HANDLE GroupHandle,
214 _Out_ PULONG RelativeId)
215 {
216 PULONG RelativeIds = NULL;
217 PSID_NAME_USE Use = NULL;
218 NET_API_STATUS ApiStatus = NERR_Success;
219 NTSTATUS Status = STATUS_SUCCESS;
220
221 /* Get the RID for the given user name */
222 Status = SamLookupNamesInDomain(DomainHandle,
223 1,
224 GroupName,
225 &RelativeIds,
226 &Use);
227 if (!NT_SUCCESS(Status))
228 {
229 WARN("SamLookupNamesInDomain failed (Status %08lx)\n", Status);
230 return NetpNtStatusToApiStatus(Status);
231 }
232
233 /* Fail, if it is not an alias account */
234 if (Use[0] != SidTypeGroup)
235 {
236 ERR("Object is not a group!\n");
237 ApiStatus = NERR_GroupNotFound;
238 goto done;
239 }
240
241 /* Open the alias account */
242 Status = SamOpenGroup(DomainHandle,
243 DesiredAccess,
244 RelativeIds[0],
245 GroupHandle);
246 if (!NT_SUCCESS(Status))
247 {
248 ERR("SamOpenGroup failed (Status %08lx)\n", Status);
249 ApiStatus = NetpNtStatusToApiStatus(Status);
250 goto done;
251 }
252
253 if (RelativeId != NULL)
254 *RelativeId = RelativeIds[0];
255
256 done:
257 if (RelativeIds != NULL)
258 SamFreeMemory(RelativeIds);
259
260 if (Use != NULL)
261 SamFreeMemory(Use);
262
263 return ApiStatus;
264 }
265
266
267 /* PUBLIC FUNCTIONS **********************************************************/
268
269 NET_API_STATUS
270 WINAPI
271 NetGroupAdd(
272 _In_opt_ LPCWSTR servername,
273 _In_ DWORD level,
274 _In_ LPBYTE buf,
275 _Out_opt_ LPDWORD parm_err)
276 {
277 GROUP_ADM_COMMENT_INFORMATION AdminComment;
278 GROUP_ATTRIBUTE_INFORMATION AttributeInfo;
279 UNICODE_STRING ServerName;
280 UNICODE_STRING GroupName;
281 SAM_HANDLE ServerHandle = NULL;
282 SAM_HANDLE DomainHandle = NULL;
283 SAM_HANDLE GroupHandle = NULL;
284 PWSTR Name = NULL;
285 PWSTR Comment = NULL;
286 ULONG Attributes = 0;
287 ULONG RelativeId;
288 NET_API_STATUS ApiStatus = NERR_Success;
289 NTSTATUS Status = STATUS_SUCCESS;
290
291 TRACE("NetGroupAdd(%s, %d, %p, %p)\n",
292 debugstr_w(servername), level, buf, parm_err);
293
294 /* Initialize the Server name*/
295 if (servername != NULL)
296 RtlInitUnicodeString(&ServerName, servername);
297
298 /* Initialize the Alias name*/
299 switch (level)
300 {
301 case 0:
302 Name = ((PGROUP_INFO_0)buf)->grpi0_name;
303 Comment = NULL;
304 Attributes = 0;
305 break;
306
307 case 1:
308 Name = ((PGROUP_INFO_1)buf)->grpi1_name;
309 Comment = ((PGROUP_INFO_1)buf)->grpi1_comment;
310 Attributes = 0;
311 break;
312
313 case 2:
314 Name = ((PGROUP_INFO_2)buf)->grpi2_name;
315 Comment = ((PGROUP_INFO_2)buf)->grpi2_comment;
316 Attributes = ((PGROUP_INFO_2)buf)->grpi2_attributes;
317 break;
318
319 case 3:
320 Name = ((PGROUP_INFO_3)buf)->grpi3_name;
321 Comment = ((PGROUP_INFO_3)buf)->grpi3_comment;
322 Attributes = ((PGROUP_INFO_3)buf)->grpi3_attributes;
323 break;
324
325 default:
326 return ERROR_INVALID_LEVEL;
327 }
328
329 RtlInitUnicodeString(&GroupName, Name);
330
331 /* Connect to the SAM Server */
332 Status = SamConnect((servername != NULL) ? &ServerName : NULL,
333 &ServerHandle,
334 SAM_SERVER_CONNECT | SAM_SERVER_LOOKUP_DOMAIN,
335 NULL);
336 if (!NT_SUCCESS(Status))
337 {
338 ERR("SamConnect failed (Status %08lx)\n", Status);
339 ApiStatus = NetpNtStatusToApiStatus(Status);
340 goto done;
341 }
342
343 /* Open the account domain */
344 Status = OpenAccountDomain(ServerHandle,
345 (servername != NULL) ? &ServerName : NULL,
346 DOMAIN_CREATE_GROUP | DOMAIN_LOOKUP,
347 &DomainHandle);
348 if (!NT_SUCCESS(Status))
349 {
350 ERR("SamOpenDomain failed (Status %08lx)\n", Status);
351 ApiStatus = NetpNtStatusToApiStatus(Status);
352 goto done;
353 }
354
355 /* Try to open the group account */
356 ApiStatus = OpenGroupByName(DomainHandle,
357 &GroupName,
358 GROUP_READ_INFORMATION,
359 &GroupHandle,
360 NULL);
361 if (ApiStatus == NERR_Success)
362 {
363 ERR("OpenGroupByName: Group %wZ already exists!\n", &GroupName);
364
365 SamCloseHandle(GroupHandle);
366 ApiStatus = ERROR_GROUP_EXISTS;
367 goto done;
368 }
369
370 ApiStatus = NERR_Success;
371
372 /* Create the group */
373 Status = SamCreateGroupInDomain(DomainHandle,
374 &GroupName,
375 DELETE | GROUP_WRITE_ACCOUNT,
376 &GroupHandle,
377 &RelativeId);
378 if (!NT_SUCCESS(Status))
379 {
380 ERR("SamCreateGroupInDomain failed (Status %08lx)\n", Status);
381 ApiStatus = NetpNtStatusToApiStatus(Status);
382 goto done;
383 }
384
385 TRACE("Created group \"%wZ\" (RID: %lu)\n", &GroupName, RelativeId);
386
387 /* Set the admin comment */
388 if (level == 1 || level == 2 || level == 3)
389 {
390 RtlInitUnicodeString(&AdminComment.AdminComment, Comment);
391
392 Status = SamSetInformationGroup(GroupHandle,
393 GroupAdminCommentInformation,
394 &AdminComment);
395 if (!NT_SUCCESS(Status))
396 {
397 ERR("SamSetInformationAlias failed (Status %08lx)\n", Status);
398 ApiStatus = NetpNtStatusToApiStatus(Status);
399
400 /* Delete the Alias if the Comment could not be set */
401 SamDeleteGroup(GroupHandle);
402
403 goto done;
404 }
405 }
406
407 /* Set the attributes */
408 if (level == 2 || level == 3)
409 {
410 AttributeInfo.Attributes = Attributes;
411
412 Status = SamSetInformationGroup(GroupHandle,
413 GroupAttributeInformation,
414 &AttributeInfo);
415 if (!NT_SUCCESS(Status))
416 {
417 ERR("SamSetInformationAlias failed (Status %08lx)\n", Status);
418 ApiStatus = NetpNtStatusToApiStatus(Status);
419
420 /* Delete the Alias if the Attributes could not be set */
421 SamDeleteGroup(GroupHandle);
422
423 goto done;
424 }
425 }
426
427 done:
428 if (GroupHandle != NULL)
429 {
430 if (ApiStatus != NERR_Success)
431 SamDeleteGroup(GroupHandle);
432 else
433 SamCloseHandle(GroupHandle);
434 }
435
436 if (DomainHandle != NULL)
437 SamCloseHandle(DomainHandle);
438
439 if (ServerHandle != NULL)
440 SamCloseHandle(ServerHandle);
441
442 return ApiStatus;
443 }
444
445
446 NET_API_STATUS
447 WINAPI
448 NetGroupAddUser(
449 _In_opt_ LPCWSTR servername,
450 _In_ LPCWSTR groupname,
451 _In_ LPCWSTR username)
452 {
453 UNICODE_STRING ServerName;
454 UNICODE_STRING GroupName;
455 UNICODE_STRING UserName;
456 SAM_HANDLE ServerHandle = NULL;
457 SAM_HANDLE DomainHandle = NULL;
458 SAM_HANDLE GroupHandle = NULL;
459 PULONG RelativeIds = NULL;
460 PSID_NAME_USE Use = NULL;
461 NET_API_STATUS ApiStatus = NERR_Success;
462 NTSTATUS Status = STATUS_SUCCESS;
463
464 TRACE("NetGroupAddUser(%s, %s, %s)\n",
465 debugstr_w(servername), debugstr_w(groupname), debugstr_w(username));
466
467 if (servername != NULL)
468 RtlInitUnicodeString(&ServerName, servername);
469
470 RtlInitUnicodeString(&GroupName, groupname);
471
472 RtlInitUnicodeString(&UserName, username);
473
474 /* Connect to the SAM Server */
475 Status = SamConnect((servername != NULL) ? &ServerName : NULL,
476 &ServerHandle,
477 SAM_SERVER_CONNECT | SAM_SERVER_LOOKUP_DOMAIN,
478 NULL);
479 if (!NT_SUCCESS(Status))
480 {
481 ERR("SamConnect failed (Status %08lx)\n", Status);
482 ApiStatus = NetpNtStatusToApiStatus(Status);
483 goto done;
484 }
485
486 /* Open the Acount Domain */
487 Status = OpenAccountDomain(ServerHandle,
488 (servername != NULL) ? &ServerName : NULL,
489 DOMAIN_LOOKUP,
490 &DomainHandle);
491 if (!NT_SUCCESS(Status))
492 {
493 ERR("OpenAccountDomain failed (Status %08lx)\n", Status);
494 ApiStatus = NetpNtStatusToApiStatus(Status);
495 goto done;
496 }
497
498 /* Open the group account */
499 ApiStatus = OpenGroupByName(DomainHandle,
500 &GroupName,
501 GROUP_ADD_MEMBER,
502 &GroupHandle,
503 NULL);
504 if (ApiStatus != NERR_Success)
505 {
506 ERR("OpenGroupByName failed (ApiStatus %lu)\n", ApiStatus);
507 if (ApiStatus == ERROR_NONE_MAPPED)
508 ApiStatus = NERR_GroupNotFound;
509 goto done;
510 }
511
512 Status = SamLookupNamesInDomain(DomainHandle,
513 1,
514 &UserName,
515 &RelativeIds,
516 &Use);
517 if (!NT_SUCCESS(Status))
518 {
519 ERR("SamLookupNamesInDomain failed (Status %08lx)\n", Status);
520 ApiStatus = NetpNtStatusToApiStatus(Status);
521 goto done;
522 }
523
524 /* Fail, if it is not a user account */
525 if (Use[0] != SidTypeUser)
526 {
527 ERR("Object is not a user!\n");
528 ApiStatus = NERR_GroupNotFound;
529 goto done;
530 }
531
532 Status = SamAddMemberToGroup(GroupHandle,
533 RelativeIds[0],
534 0);
535 if (!NT_SUCCESS(Status))
536 {
537 ERR("SamAddMemberToGroup failed (Status %lu)\n", Status);
538 ApiStatus = NetpNtStatusToApiStatus(Status);
539 goto done;
540 }
541
542 done:
543 if (RelativeIds != NULL)
544 SamFreeMemory(RelativeIds);
545
546 if (Use != NULL)
547 SamFreeMemory(Use);
548
549 if (GroupHandle != NULL)
550 SamCloseHandle(GroupHandle);
551
552 if (DomainHandle != NULL)
553 SamCloseHandle(DomainHandle);
554
555 if (ServerHandle != NULL)
556 SamCloseHandle(ServerHandle);
557
558 return ApiStatus;
559 }
560
561
562 NET_API_STATUS
563 WINAPI
564 NetGroupDel(
565 _In_opt_ LPCWSTR servername,
566 _In_ IN LPCWSTR groupname)
567 {
568 UNICODE_STRING ServerName;
569 UNICODE_STRING GroupName;
570 SAM_HANDLE ServerHandle = NULL;
571 SAM_HANDLE DomainHandle = NULL;
572 SAM_HANDLE GroupHandle = NULL;
573 NET_API_STATUS ApiStatus = NERR_Success;
574 NTSTATUS Status = STATUS_SUCCESS;
575
576 TRACE("NetGroupDel(%s, %s)\n",
577 debugstr_w(servername), debugstr_w(groupname));
578
579 if (servername != NULL)
580 RtlInitUnicodeString(&ServerName, servername);
581
582 RtlInitUnicodeString(&GroupName, groupname);
583
584 /* Connect to the SAM Server */
585 Status = SamConnect((servername != NULL) ? &ServerName : NULL,
586 &ServerHandle,
587 SAM_SERVER_CONNECT | SAM_SERVER_LOOKUP_DOMAIN,
588 NULL);
589 if (!NT_SUCCESS(Status))
590 {
591 ERR("SamConnect failed (Status %08lx)\n", Status);
592 ApiStatus = NetpNtStatusToApiStatus(Status);
593 goto done;
594 }
595
596 /* Open the Acount Domain */
597 Status = OpenAccountDomain(ServerHandle,
598 (servername != NULL) ? &ServerName : NULL,
599 DOMAIN_LOOKUP,
600 &DomainHandle);
601 if (!NT_SUCCESS(Status))
602 {
603 ERR("OpenAccountDomain failed (Status %08lx)\n", Status);
604 ApiStatus = NetpNtStatusToApiStatus(Status);
605 goto done;
606 }
607
608 /* Open the group */
609 ApiStatus = OpenGroupByName(DomainHandle,
610 &GroupName,
611 DELETE,
612 &GroupHandle,
613 NULL);
614 if (ApiStatus != NERR_Success)
615 {
616 ERR("OpenGroupByName failed (ApiStatus %lu)\n", ApiStatus);
617 if (ApiStatus == ERROR_NONE_MAPPED)
618 ApiStatus = NERR_GroupNotFound;
619 goto done;
620 }
621
622 /* Delete the group */
623 Status = SamDeleteGroup(GroupHandle);
624 if (!NT_SUCCESS(Status))
625 {
626 ERR("SamDeleteGroup failed (Status %08lx)\n", Status);
627 ApiStatus = NetpNtStatusToApiStatus(Status);
628 goto done;
629 }
630
631 done:
632 if (GroupHandle != NULL)
633 SamCloseHandle(GroupHandle);
634
635 if (DomainHandle != NULL)
636 SamCloseHandle(DomainHandle);
637
638 if (ServerHandle != NULL)
639 SamCloseHandle(ServerHandle);
640
641 return ApiStatus;
642 }
643
644
645 NET_API_STATUS
646 WINAPI
647 NetGroupDelUser(
648 _In_opt_ LPCWSTR servername,
649 _In_ LPCWSTR groupname,
650 _In_ LPCWSTR username)
651 {
652 UNICODE_STRING ServerName;
653 UNICODE_STRING GroupName;
654 UNICODE_STRING UserName;
655 SAM_HANDLE ServerHandle = NULL;
656 SAM_HANDLE DomainHandle = NULL;
657 SAM_HANDLE GroupHandle = NULL;
658 PULONG RelativeIds = NULL;
659 PSID_NAME_USE Use = NULL;
660 NET_API_STATUS ApiStatus = NERR_Success;
661 NTSTATUS Status = STATUS_SUCCESS;
662
663 TRACE("NetGroupDelUser(%s, %s, %s)\n",
664 debugstr_w(servername), debugstr_w(groupname), debugstr_w(username));
665
666 if (servername != NULL)
667 RtlInitUnicodeString(&ServerName, servername);
668
669 RtlInitUnicodeString(&GroupName, groupname);
670
671 RtlInitUnicodeString(&UserName, username);
672
673 /* Connect to the SAM Server */
674 Status = SamConnect((servername != NULL) ? &ServerName : NULL,
675 &ServerHandle,
676 SAM_SERVER_CONNECT | SAM_SERVER_LOOKUP_DOMAIN,
677 NULL);
678 if (!NT_SUCCESS(Status))
679 {
680 ERR("SamConnect failed (Status %08lx)\n", Status);
681 ApiStatus = NetpNtStatusToApiStatus(Status);
682 goto done;
683 }
684
685 /* Open the Acount Domain */
686 Status = OpenAccountDomain(ServerHandle,
687 (servername != NULL) ? &ServerName : NULL,
688 DOMAIN_LOOKUP,
689 &DomainHandle);
690 if (!NT_SUCCESS(Status))
691 {
692 ERR("OpenAccountDomain failed (Status %08lx)\n", Status);
693 ApiStatus = NetpNtStatusToApiStatus(Status);
694 goto done;
695 }
696
697 /* Open the group account */
698 ApiStatus = OpenGroupByName(DomainHandle,
699 &GroupName,
700 GROUP_REMOVE_MEMBER,
701 &GroupHandle,
702 NULL);
703 if (ApiStatus != NERR_Success)
704 {
705 ERR("OpenGroupByName failed (ApiStatus %lu)\n", ApiStatus);
706 if (ApiStatus == ERROR_NONE_MAPPED)
707 ApiStatus = NERR_GroupNotFound;
708 goto done;
709 }
710
711 Status = SamLookupNamesInDomain(DomainHandle,
712 1,
713 &UserName,
714 &RelativeIds,
715 &Use);
716 if (!NT_SUCCESS(Status))
717 {
718 ERR("SamLookupNamesInDomain failed (Status %08lx)\n", Status);
719 ApiStatus = NetpNtStatusToApiStatus(Status);
720 goto done;
721 }
722
723 /* Fail, if it is not a user account */
724 if (Use[0] != SidTypeUser)
725 {
726 ERR("Object is not a user!\n");
727 ApiStatus = NERR_GroupNotFound;
728 goto done;
729 }
730
731 Status = SamRemoveMemberFromGroup(GroupHandle,
732 RelativeIds[0]);
733 if (!NT_SUCCESS(Status))
734 {
735 ERR("SamRemoveMemberFromGroup failed (Status %lu)\n", Status);
736 ApiStatus = NetpNtStatusToApiStatus(Status);
737 goto done;
738 }
739
740 done:
741 if (RelativeIds != NULL)
742 SamFreeMemory(RelativeIds);
743
744 if (Use != NULL)
745 SamFreeMemory(Use);
746
747 if (GroupHandle != NULL)
748 SamCloseHandle(GroupHandle);
749
750 if (DomainHandle != NULL)
751 SamCloseHandle(DomainHandle);
752
753 if (ServerHandle != NULL)
754 SamCloseHandle(ServerHandle);
755
756 return ApiStatus;
757 }
758
759
760 NET_API_STATUS
761 WINAPI
762 NetGroupEnum(
763 _In_opt_ LPCWSTR servername,
764 _In_ DWORD level,
765 _Out_ LPBYTE *bufptr,
766 _In_ DWORD prefmaxlen,
767 _Out_ LPDWORD entriesread,
768 _Out_ LPDWORD totalentries,
769 _Inout_opt_ PDWORD_PTR resume_handle)
770 {
771 UNICODE_STRING ServerName;
772 PSAM_RID_ENUMERATION CurrentGroup;
773 PENUM_CONTEXT EnumContext = NULL;
774 ULONG i;
775 SAM_HANDLE GroupHandle = NULL;
776 PGROUP_GENERAL_INFORMATION GroupInfo = NULL;
777 PVOID Buffer = NULL;
778 NET_API_STATUS ApiStatus = NERR_Success;
779 NTSTATUS Status = STATUS_SUCCESS;
780
781 TRACE("NetGroupEnum(%s, %d, %p, %d, %p, %p, %p)\n", debugstr_w(servername),
782 level, bufptr, prefmaxlen, entriesread, totalentries, resume_handle);
783
784 *entriesread = 0;
785 *totalentries = 0;
786 *bufptr = NULL;
787
788 if (servername != NULL)
789 RtlInitUnicodeString(&ServerName, servername);
790
791 if (resume_handle != NULL && *resume_handle != 0)
792 {
793 EnumContext = (PENUM_CONTEXT)*resume_handle;
794 }
795 else
796 {
797 ApiStatus = NetApiBufferAllocate(sizeof(ENUM_CONTEXT), (PVOID*)&EnumContext);
798 if (ApiStatus != NERR_Success)
799 goto done;
800
801 EnumContext->EnumerationContext = 0;
802 EnumContext->Buffer = NULL;
803 EnumContext->Returned = 0;
804 EnumContext->Index = 0;
805
806 Status = SamConnect((servername != NULL) ? &ServerName : NULL,
807 &EnumContext->ServerHandle,
808 SAM_SERVER_CONNECT | SAM_SERVER_LOOKUP_DOMAIN,
809 NULL);
810 if (!NT_SUCCESS(Status))
811 {
812 ERR("SamConnect failed (Status %08lx)\n", Status);
813 ApiStatus = NetpNtStatusToApiStatus(Status);
814 goto done;
815 }
816
817 Status = OpenAccountDomain(EnumContext->ServerHandle,
818 (servername != NULL) ? &ServerName : NULL,
819 DOMAIN_LIST_ACCOUNTS | DOMAIN_LOOKUP,
820 &EnumContext->AccountDomainHandle);
821 if (!NT_SUCCESS(Status))
822 {
823 ERR("OpenAccountDomain failed (Status %08lx)\n", Status);
824 ApiStatus = NetpNtStatusToApiStatus(Status);
825 goto done;
826 }
827
828 Status = OpenBuiltinDomain(EnumContext->ServerHandle,
829 DOMAIN_LIST_ACCOUNTS | DOMAIN_LOOKUP,
830 &EnumContext->BuiltinDomainHandle);
831 if (!NT_SUCCESS(Status))
832 {
833 ERR("OpenBuiltinDomain failed (Status %08lx)\n", Status);
834 ApiStatus = NetpNtStatusToApiStatus(Status);
835 goto done;
836 }
837
838 EnumContext->Phase = AccountPhase; //BuiltinPhase;
839 EnumContext->DomainHandle = EnumContext->AccountDomainHandle; //BuiltinDomainHandle;
840 }
841
842
843 // while (TRUE)
844 // {
845 TRACE("EnumContext->Index: %lu\n", EnumContext->Index);
846 TRACE("EnumContext->Returned: %lu\n", EnumContext->Returned);
847
848 if (EnumContext->Index >= EnumContext->Returned)
849 {
850 TRACE("Calling SamEnumerateGroupsInDomain\n");
851
852 Status = SamEnumerateGroupsInDomain(EnumContext->DomainHandle,
853 &EnumContext->EnumerationContext,
854 (PVOID *)&EnumContext->Buffer,
855 prefmaxlen,
856 &EnumContext->Returned);
857
858 TRACE("SamEnumerateGroupsInDomain returned (Status %08lx)\n", Status);
859 if (!NT_SUCCESS(Status))
860 {
861 ERR("SamEnumerateAliasesInDomain failed (Status %08lx)\n", Status);
862 ApiStatus = NetpNtStatusToApiStatus(Status);
863 goto done;
864 }
865
866 if (Status == STATUS_MORE_ENTRIES)
867 {
868 ApiStatus = NERR_BufTooSmall;
869 goto done;
870 }
871 }
872
873 TRACE("EnumContext: %lu\n", EnumContext);
874 TRACE("EnumContext->Returned: %lu\n", EnumContext->Returned);
875 TRACE("EnumContext->Buffer: %p\n", EnumContext->Buffer);
876
877 /* Get a pointer to the current group */
878 CurrentGroup = &EnumContext->Buffer[EnumContext->Index];
879
880 TRACE("RID: %lu\n", CurrentGroup->RelativeId);
881
882 Status = SamOpenGroup(EnumContext->DomainHandle,
883 GROUP_READ_INFORMATION,
884 CurrentGroup->RelativeId,
885 &GroupHandle);
886 if (!NT_SUCCESS(Status))
887 {
888 ERR("SamOpenGroup failed (Status %08lx)\n", Status);
889 ApiStatus = NetpNtStatusToApiStatus(Status);
890 goto done;
891 }
892
893 Status = SamQueryInformationGroup(GroupHandle,
894 GroupGeneralInformation,
895 (PVOID *)&GroupInfo);
896 if (!NT_SUCCESS(Status))
897 {
898 ERR("SamQueryInformationGroup failed (Status %08lx)\n", Status);
899 ApiStatus = NetpNtStatusToApiStatus(Status);
900 goto done;
901 }
902
903 SamCloseHandle(GroupHandle);
904 GroupHandle = NULL;
905
906 TRACE("Name: %S\n", GroupInfo->Name.Buffer);
907 TRACE("Comment: %S\n", GroupInfo->AdminComment.Buffer);
908
909 ApiStatus = BuildGroupInfoBuffer(GroupInfo,
910 level,
911 CurrentGroup->RelativeId,
912 &Buffer);
913 if (ApiStatus != NERR_Success)
914 goto done;
915
916 if (GroupInfo != NULL)
917 {
918 FreeGroupInfo(GroupInfo);
919 GroupInfo = NULL;
920 }
921
922 EnumContext->Index++;
923
924 (*entriesread)++;
925
926 if (EnumContext->Index == EnumContext->Returned)
927 {
928 switch (EnumContext->Phase)
929 {
930 case BuiltinPhase:
931 EnumContext->Phase = AccountPhase;
932 EnumContext->DomainHandle = EnumContext->AccountDomainHandle;
933 EnumContext->EnumerationContext = 0;
934 EnumContext->Index = 0;
935 EnumContext->Returned = 0;
936
937 if (EnumContext->Buffer != NULL)
938 {
939 for (i = 0; i < EnumContext->Returned; i++)
940 {
941 SamFreeMemory(EnumContext->Buffer[i].Name.Buffer);
942 }
943
944 SamFreeMemory(EnumContext->Buffer);
945 EnumContext->Buffer = NULL;
946 }
947 break;
948
949 case AccountPhase:
950 case DonePhase:
951 EnumContext->Phase = DonePhase;
952 break;
953 }
954 }
955 // }
956
957 done:
958 if (ApiStatus == NERR_Success && EnumContext != NULL && EnumContext->Phase != DonePhase)
959 ApiStatus = ERROR_MORE_DATA;
960
961 if (EnumContext != NULL)
962 *totalentries = EnumContext->Returned;
963
964 if (resume_handle == NULL || ApiStatus != ERROR_MORE_DATA)
965 {
966 if (EnumContext != NULL)
967 {
968 if (EnumContext->BuiltinDomainHandle != NULL)
969 SamCloseHandle(EnumContext->BuiltinDomainHandle);
970
971 if (EnumContext->AccountDomainHandle != NULL)
972 SamCloseHandle(EnumContext->AccountDomainHandle);
973
974 if (EnumContext->ServerHandle != NULL)
975 SamCloseHandle(EnumContext->ServerHandle);
976
977 if (EnumContext->Buffer != NULL)
978 {
979 for (i = 0; i < EnumContext->Returned; i++)
980 {
981 SamFreeMemory(EnumContext->Buffer[i].Name.Buffer);
982 }
983
984 SamFreeMemory(EnumContext->Buffer);
985 }
986
987 NetApiBufferFree(EnumContext);
988 EnumContext = NULL;
989 }
990 }
991
992 if (GroupHandle != NULL)
993 SamCloseHandle(GroupHandle);
994
995 if (GroupInfo != NULL)
996 FreeGroupInfo(GroupInfo);
997
998 if (resume_handle != NULL)
999 *resume_handle = (DWORD_PTR)EnumContext;
1000
1001 *bufptr = (LPBYTE)Buffer;
1002
1003 TRACE("return %lu\n", ApiStatus);
1004
1005 return ApiStatus;
1006 }
1007
1008
1009 NET_API_STATUS
1010 WINAPI
1011 NetGroupGetInfo(
1012 _In_opt_ LPCWSTR servername,
1013 _In_ LPCWSTR groupname,
1014 _In_ DWORD level,
1015 _Out_ LPBYTE *bufptr)
1016 {
1017 UNICODE_STRING ServerName;
1018 UNICODE_STRING GroupName;
1019 SAM_HANDLE ServerHandle = NULL;
1020 SAM_HANDLE DomainHandle = NULL;
1021 SAM_HANDLE GroupHandle = NULL;
1022 PGROUP_GENERAL_INFORMATION GroupInfo = NULL;
1023 PVOID Buffer = NULL;
1024 ULONG RelativeId;
1025 NET_API_STATUS ApiStatus = NERR_Success;
1026 NTSTATUS Status = STATUS_SUCCESS;
1027
1028 TRACE("NetGroupGetInfo(%s, %s, %d, %p)\n",
1029 debugstr_w(servername), debugstr_w(groupname), level, bufptr);
1030
1031 if (servername != NULL)
1032 RtlInitUnicodeString(&ServerName, servername);
1033
1034 RtlInitUnicodeString(&GroupName, groupname);
1035
1036 /* Connect to the SAM Server */
1037 Status = SamConnect((servername != NULL) ? &ServerName : NULL,
1038 &ServerHandle,
1039 SAM_SERVER_CONNECT | SAM_SERVER_LOOKUP_DOMAIN,
1040 NULL);
1041 if (!NT_SUCCESS(Status))
1042 {
1043 ERR("SamConnect failed (Status %08lx)\n", Status);
1044 ApiStatus = NetpNtStatusToApiStatus(Status);
1045 goto done;
1046 }
1047
1048 /* Open the Acount Domain */
1049 Status = OpenAccountDomain(ServerHandle,
1050 (servername != NULL) ? &ServerName : NULL,
1051 DOMAIN_LOOKUP,
1052 &DomainHandle);
1053 if (!NT_SUCCESS(Status))
1054 {
1055 ERR("OpenAccountDomain failed (Status %08lx)\n", Status);
1056 ApiStatus = NetpNtStatusToApiStatus(Status);
1057 goto done;
1058 }
1059
1060 /* Open the group account in the account domain */
1061 ApiStatus = OpenGroupByName(DomainHandle,
1062 &GroupName,
1063 GROUP_READ_INFORMATION,
1064 &GroupHandle,
1065 &RelativeId);
1066 if (ApiStatus != NERR_Success)
1067 {
1068 ERR("OpenGroupByName failed (ApiStatus %lu)\n", ApiStatus);
1069 if (ApiStatus == ERROR_NONE_MAPPED)
1070 ApiStatus = NERR_GroupNotFound;
1071 goto done;
1072 }
1073
1074 Status = SamQueryInformationGroup(GroupHandle,
1075 GroupGeneralInformation,
1076 (PVOID *)&GroupInfo);
1077 if (!NT_SUCCESS(Status))
1078 {
1079 ERR("SamQueryInformationGroup failed (Status %08lx)\n", Status);
1080 ApiStatus = NetpNtStatusToApiStatus(Status);
1081 goto done;
1082 }
1083
1084 ApiStatus = BuildGroupInfoBuffer(GroupInfo,
1085 level,
1086 RelativeId,
1087 &Buffer);
1088 if (ApiStatus != NERR_Success)
1089 goto done;
1090
1091 done:
1092 if (GroupInfo != NULL)
1093 FreeGroupInfo(GroupInfo);
1094
1095 if (GroupHandle != NULL)
1096 SamCloseHandle(GroupHandle);
1097
1098 if (DomainHandle != NULL)
1099 SamCloseHandle(DomainHandle);
1100
1101 if (ServerHandle != NULL)
1102 SamCloseHandle(ServerHandle);
1103
1104 *bufptr = (LPBYTE)Buffer;
1105
1106 return ApiStatus;
1107 }
1108
1109
1110 NET_API_STATUS
1111 WINAPI
1112 NetGroupSetInfo(
1113 _In_opt_ LPCWSTR servername,
1114 _In_ LPCWSTR groupname,
1115 _In_ DWORD level,
1116 _In_ LPBYTE buf,
1117 _Out_opt_ LPDWORD parm_err)
1118 {
1119 UNICODE_STRING ServerName;
1120 UNICODE_STRING GroupName;
1121 SAM_HANDLE ServerHandle = NULL;
1122 SAM_HANDLE DomainHandle = NULL;
1123 SAM_HANDLE GroupHandle = NULL;
1124 GROUP_NAME_INFORMATION GroupNameInfo;
1125 GROUP_ADM_COMMENT_INFORMATION AdminCommentInfo;
1126 GROUP_ATTRIBUTE_INFORMATION AttributeInfo;
1127 NET_API_STATUS ApiStatus = NERR_Success;
1128 NTSTATUS Status = STATUS_SUCCESS;
1129
1130 TRACE("NetGroupSetInfo(%s, %s, %d, %p, %p)\n",
1131 debugstr_w(servername), debugstr_w(groupname), level, buf, parm_err);
1132
1133 if (parm_err != NULL)
1134 *parm_err = PARM_ERROR_NONE;
1135
1136 if (servername != NULL)
1137 RtlInitUnicodeString(&ServerName, servername);
1138
1139 RtlInitUnicodeString(&GroupName, groupname);
1140
1141 /* Connect to the SAM Server */
1142 Status = SamConnect((servername != NULL) ? &ServerName : NULL,
1143 &ServerHandle,
1144 SAM_SERVER_CONNECT | SAM_SERVER_LOOKUP_DOMAIN,
1145 NULL);
1146 if (!NT_SUCCESS(Status))
1147 {
1148 ERR("SamConnect failed (Status %08lx)\n", Status);
1149 ApiStatus = NetpNtStatusToApiStatus(Status);
1150 goto done;
1151 }
1152
1153 /* Open the Acount Domain */
1154 Status = OpenAccountDomain(ServerHandle,
1155 (servername != NULL) ? &ServerName : NULL,
1156 DOMAIN_LOOKUP,
1157 &DomainHandle);
1158 if (!NT_SUCCESS(Status))
1159 {
1160 ERR("OpenAccountDomain failed (Status %08lx)\n", Status);
1161 ApiStatus = NetpNtStatusToApiStatus(Status);
1162 goto done;
1163 }
1164
1165 /* Open the group */
1166 ApiStatus = OpenGroupByName(DomainHandle,
1167 &GroupName,
1168 GROUP_WRITE_ACCOUNT,
1169 &GroupHandle,
1170 NULL);
1171 if (ApiStatus != NERR_Success)
1172 {
1173 WARN("OpenGroupByName failed (ApiStatus %lu)\n", ApiStatus);
1174 if (ApiStatus == ERROR_NONE_MAPPED)
1175 ApiStatus = NERR_GroupNotFound;
1176 goto done;
1177 }
1178
1179 switch (level)
1180 {
1181 case 0:
1182 /* Set the group name */
1183 RtlInitUnicodeString(&GroupNameInfo.Name,
1184 ((PGROUP_INFO_0)buf)->grpi0_name);
1185
1186 Status = SamSetInformationGroup(GroupHandle,
1187 GroupNameInformation,
1188 &GroupNameInfo);
1189 if (!NT_SUCCESS(Status))
1190 {
1191 ERR("SamSetInformationGroup failed (ApiStatus %lu)\n", ApiStatus);
1192 ApiStatus = NetpNtStatusToApiStatus(Status);
1193 goto done;
1194 }
1195 break;
1196
1197 case 1:
1198 /* Set the group name */
1199 RtlInitUnicodeString(&GroupNameInfo.Name,
1200 ((PGROUP_INFO_1)buf)->grpi1_name);
1201
1202 Status = SamSetInformationGroup(GroupHandle,
1203 GroupNameInformation,
1204 &GroupNameInfo);
1205 if (!NT_SUCCESS(Status))
1206 {
1207 ERR("SamSetInformationGroup failed (ApiStatus %lu)\n", ApiStatus);
1208 ApiStatus = NetpNtStatusToApiStatus(Status);
1209 goto done;
1210 }
1211
1212 /* Set the admin comment */
1213 RtlInitUnicodeString(&AdminCommentInfo.AdminComment,
1214 ((PGROUP_INFO_1)buf)->grpi1_comment);
1215
1216 Status = SamSetInformationGroup(GroupHandle,
1217 GroupAdminCommentInformation,
1218 &AdminCommentInfo);
1219 if (!NT_SUCCESS(Status))
1220 {
1221 ERR("SamSetInformationGroup failed (ApiStatus %lu)\n", ApiStatus);
1222 ApiStatus = NetpNtStatusToApiStatus(Status);
1223 goto done;
1224 }
1225 break;
1226
1227 case 2:
1228 /* Set the group name */
1229 RtlInitUnicodeString(&GroupNameInfo.Name,
1230 ((PGROUP_INFO_2)buf)->grpi2_name);
1231
1232 Status = SamSetInformationGroup(GroupHandle,
1233 GroupNameInformation,
1234 &GroupNameInfo);
1235 if (!NT_SUCCESS(Status))
1236 {
1237 ERR("SamSetInformationGroup failed (ApiStatus %lu)\n", ApiStatus);
1238 ApiStatus = NetpNtStatusToApiStatus(Status);
1239 goto done;
1240 }
1241
1242 /* Set the admin comment */
1243 RtlInitUnicodeString(&AdminCommentInfo.AdminComment,
1244 ((PGROUP_INFO_2)buf)->grpi2_comment);
1245
1246 Status = SamSetInformationGroup(GroupHandle,
1247 GroupAdminCommentInformation,
1248 &AdminCommentInfo);
1249 if (!NT_SUCCESS(Status))
1250 {
1251 ERR("SamSetInformationGroup failed (ApiStatus %lu)\n", ApiStatus);
1252 ApiStatus = NetpNtStatusToApiStatus(Status);
1253 goto done;
1254 }
1255
1256 /* Set the attributes */
1257 AttributeInfo.Attributes = ((PGROUP_INFO_2)buf)->grpi2_attributes;
1258
1259 Status = SamSetInformationGroup(GroupHandle,
1260 GroupAttributeInformation,
1261 &AttributeInfo);
1262 if (!NT_SUCCESS(Status))
1263 {
1264 ERR("SamSetInformationGroup failed (ApiStatus %lu)\n", ApiStatus);
1265 ApiStatus = NetpNtStatusToApiStatus(Status);
1266 goto done;
1267 }
1268 break;
1269
1270 case 3:
1271 /* Set the group name */
1272 RtlInitUnicodeString(&GroupNameInfo.Name,
1273 ((PGROUP_INFO_3)buf)->grpi3_name);
1274
1275 Status = SamSetInformationGroup(GroupHandle,
1276 GroupNameInformation,
1277 &GroupNameInfo);
1278 if (!NT_SUCCESS(Status))
1279 {
1280 ERR("SamSetInformationGroup failed (ApiStatus %lu)\n", ApiStatus);
1281 ApiStatus = NetpNtStatusToApiStatus(Status);
1282 goto done;
1283 }
1284
1285 /* Set the admin comment */
1286 RtlInitUnicodeString(&AdminCommentInfo.AdminComment,
1287 ((PGROUP_INFO_3)buf)->grpi3_comment);
1288
1289 Status = SamSetInformationGroup(GroupHandle,
1290 GroupAdminCommentInformation,
1291 &AdminCommentInfo);
1292 if (!NT_SUCCESS(Status))
1293 {
1294 ERR("SamSetInformationGroup failed (ApiStatus %lu)\n", ApiStatus);
1295 ApiStatus = NetpNtStatusToApiStatus(Status);
1296 goto done;
1297 }
1298
1299 /* Set the attributes */
1300 AttributeInfo.Attributes = ((PGROUP_INFO_3)buf)->grpi3_attributes;
1301
1302 Status = SamSetInformationGroup(GroupHandle,
1303 GroupAttributeInformation,
1304 &AttributeInfo);
1305 if (!NT_SUCCESS(Status))
1306 {
1307 ERR("SamSetInformationGroup failed (ApiStatus %lu)\n", ApiStatus);
1308 ApiStatus = NetpNtStatusToApiStatus(Status);
1309 goto done;
1310 }
1311 break;
1312
1313 case 1002:
1314 /* Set the admin comment */
1315 RtlInitUnicodeString(&AdminCommentInfo.AdminComment,
1316 ((PGROUP_INFO_1002)buf)->grpi1002_comment);
1317
1318 Status = SamSetInformationGroup(GroupHandle,
1319 GroupAdminCommentInformation,
1320 &AdminCommentInfo);
1321 if (!NT_SUCCESS(Status))
1322 {
1323 ERR("SamSetInformationGroup failed (ApiStatus %lu)\n", ApiStatus);
1324 ApiStatus = NetpNtStatusToApiStatus(Status);
1325 goto done;
1326 }
1327 break;
1328
1329 case 1005:
1330 /* Set the attributes */
1331 AttributeInfo.Attributes = ((PGROUP_INFO_1005)buf)->grpi1005_attributes;
1332
1333 Status = SamSetInformationGroup(GroupHandle,
1334 GroupAttributeInformation,
1335 &AttributeInfo);
1336 if (!NT_SUCCESS(Status))
1337 {
1338 ERR("SamSetInformationGroup failed (ApiStatus %lu)\n", ApiStatus);
1339 ApiStatus = NetpNtStatusToApiStatus(Status);
1340 goto done;
1341 }
1342 break;
1343
1344 default:
1345 ApiStatus = ERROR_INVALID_LEVEL;
1346 goto done;
1347 }
1348
1349 done:
1350 if (GroupHandle != NULL)
1351 SamCloseHandle(GroupHandle);
1352
1353 if (DomainHandle != NULL)
1354 SamCloseHandle(DomainHandle);
1355
1356 if (ServerHandle != NULL)
1357 SamCloseHandle(ServerHandle);
1358
1359 return ApiStatus;
1360 }
1361
1362 /* EOF */