[NETAPI] Implement NetGroupGetInfo
[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 ERR("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 NetGroupEnum(
272 _In_opt_ LPCWSTR servername,
273 _In_ DWORD level,
274 _Out_ LPBYTE *bufptr,
275 _In_ DWORD prefmaxlen,
276 _Out_ LPDWORD entriesread,
277 _Out_ LPDWORD totalentries,
278 _Inout_opt_ PDWORD_PTR resume_handle)
279 {
280 UNICODE_STRING ServerName;
281 PSAM_RID_ENUMERATION CurrentGroup;
282 PENUM_CONTEXT EnumContext = NULL;
283 ULONG i;
284 SAM_HANDLE GroupHandle = NULL;
285 PGROUP_GENERAL_INFORMATION GroupInfo = NULL;
286 PVOID Buffer = NULL;
287 NET_API_STATUS ApiStatus = NERR_Success;
288 NTSTATUS Status = STATUS_SUCCESS;
289
290 TRACE("NetGroupEnum(%s, %d, %p, %d, %p, %p, %p)\n", debugstr_w(servername),
291 level, bufptr, prefmaxlen, entriesread, totalentries, resume_handle);
292
293 *entriesread = 0;
294 *totalentries = 0;
295 *bufptr = NULL;
296
297 if (servername != NULL)
298 RtlInitUnicodeString(&ServerName, servername);
299
300 if (resume_handle != NULL && *resume_handle != 0)
301 {
302 EnumContext = (PENUM_CONTEXT)*resume_handle;
303 }
304 else
305 {
306 ApiStatus = NetApiBufferAllocate(sizeof(ENUM_CONTEXT), (PVOID*)&EnumContext);
307 if (ApiStatus != NERR_Success)
308 goto done;
309
310 EnumContext->EnumerationContext = 0;
311 EnumContext->Buffer = NULL;
312 EnumContext->Returned = 0;
313 EnumContext->Index = 0;
314
315 Status = SamConnect((servername != NULL) ? &ServerName : NULL,
316 &EnumContext->ServerHandle,
317 SAM_SERVER_CONNECT | SAM_SERVER_LOOKUP_DOMAIN,
318 NULL);
319 if (!NT_SUCCESS(Status))
320 {
321 ERR("SamConnect failed (Status %08lx)\n", Status);
322 ApiStatus = NetpNtStatusToApiStatus(Status);
323 goto done;
324 }
325
326 Status = OpenAccountDomain(EnumContext->ServerHandle,
327 (servername != NULL) ? &ServerName : NULL,
328 DOMAIN_LIST_ACCOUNTS | DOMAIN_LOOKUP,
329 &EnumContext->AccountDomainHandle);
330 if (!NT_SUCCESS(Status))
331 {
332 ERR("OpenAccountDomain failed (Status %08lx)\n", Status);
333 ApiStatus = NetpNtStatusToApiStatus(Status);
334 goto done;
335 }
336
337 Status = OpenBuiltinDomain(EnumContext->ServerHandle,
338 DOMAIN_LIST_ACCOUNTS | DOMAIN_LOOKUP,
339 &EnumContext->BuiltinDomainHandle);
340 if (!NT_SUCCESS(Status))
341 {
342 ERR("OpenBuiltinDomain failed (Status %08lx)\n", Status);
343 ApiStatus = NetpNtStatusToApiStatus(Status);
344 goto done;
345 }
346
347 EnumContext->Phase = AccountPhase; //BuiltinPhase;
348 EnumContext->DomainHandle = EnumContext->AccountDomainHandle; //BuiltinDomainHandle;
349 }
350
351
352 // while (TRUE)
353 // {
354 TRACE("EnumContext->Index: %lu\n", EnumContext->Index);
355 TRACE("EnumContext->Returned: %lu\n", EnumContext->Returned);
356
357 if (EnumContext->Index >= EnumContext->Returned)
358 {
359 TRACE("Calling SamEnumerateGroupsInDomain\n");
360
361 Status = SamEnumerateGroupsInDomain(EnumContext->DomainHandle,
362 &EnumContext->EnumerationContext,
363 (PVOID *)&EnumContext->Buffer,
364 prefmaxlen,
365 &EnumContext->Returned);
366
367 TRACE("SamEnumerateGroupsInDomain returned (Status %08lx)\n", Status);
368 if (!NT_SUCCESS(Status))
369 {
370 ERR("SamEnumerateAliasesInDomain failed (Status %08lx)\n", Status);
371 ApiStatus = NetpNtStatusToApiStatus(Status);
372 goto done;
373 }
374
375 if (Status == STATUS_MORE_ENTRIES)
376 {
377 ApiStatus = NERR_BufTooSmall;
378 goto done;
379 }
380 }
381
382 TRACE("EnumContext: %lu\n", EnumContext);
383 TRACE("EnumContext->Returned: %lu\n", EnumContext->Returned);
384 TRACE("EnumContext->Buffer: %p\n", EnumContext->Buffer);
385
386 /* Get a pointer to the current group */
387 CurrentGroup = &EnumContext->Buffer[EnumContext->Index];
388
389 TRACE("RID: %lu\n", CurrentGroup->RelativeId);
390
391 Status = SamOpenGroup(EnumContext->DomainHandle,
392 GROUP_READ_INFORMATION,
393 CurrentGroup->RelativeId,
394 &GroupHandle);
395 if (!NT_SUCCESS(Status))
396 {
397 ERR("SamOpenGroup failed (Status %08lx)\n", Status);
398 ApiStatus = NetpNtStatusToApiStatus(Status);
399 goto done;
400 }
401
402 Status = SamQueryInformationGroup(GroupHandle,
403 GroupGeneralInformation,
404 (PVOID *)&GroupInfo);
405 if (!NT_SUCCESS(Status))
406 {
407 ERR("SamQueryInformationGroup failed (Status %08lx)\n", Status);
408 ApiStatus = NetpNtStatusToApiStatus(Status);
409 goto done;
410 }
411
412 SamCloseHandle(GroupHandle);
413 GroupHandle = NULL;
414
415 TRACE("Name: %S\n", GroupInfo->Name.Buffer);
416 TRACE("Comment: %S\n", GroupInfo->AdminComment.Buffer);
417
418 ApiStatus = BuildGroupInfoBuffer(GroupInfo,
419 level,
420 CurrentGroup->RelativeId,
421 &Buffer);
422 if (ApiStatus != NERR_Success)
423 goto done;
424
425 if (GroupInfo != NULL)
426 {
427 FreeGroupInfo(GroupInfo);
428 GroupInfo = NULL;
429 }
430
431 EnumContext->Index++;
432
433 (*entriesread)++;
434
435 if (EnumContext->Index == EnumContext->Returned)
436 {
437 switch (EnumContext->Phase)
438 {
439 case BuiltinPhase:
440 EnumContext->Phase = AccountPhase;
441 EnumContext->DomainHandle = EnumContext->AccountDomainHandle;
442 EnumContext->EnumerationContext = 0;
443 EnumContext->Index = 0;
444 EnumContext->Returned = 0;
445
446 if (EnumContext->Buffer != NULL)
447 {
448 for (i = 0; i < EnumContext->Returned; i++)
449 {
450 SamFreeMemory(EnumContext->Buffer[i].Name.Buffer);
451 }
452
453 SamFreeMemory(EnumContext->Buffer);
454 EnumContext->Buffer = NULL;
455 }
456 break;
457
458 case AccountPhase:
459 case DonePhase:
460 EnumContext->Phase = DonePhase;
461 break;
462 }
463 }
464 // }
465
466 done:
467 if (ApiStatus == NERR_Success && EnumContext != NULL && EnumContext->Phase != DonePhase)
468 ApiStatus = ERROR_MORE_DATA;
469
470 if (EnumContext != NULL)
471 *totalentries = EnumContext->Returned;
472
473 if (resume_handle == NULL || ApiStatus != ERROR_MORE_DATA)
474 {
475 if (EnumContext != NULL)
476 {
477 if (EnumContext->BuiltinDomainHandle != NULL)
478 SamCloseHandle(EnumContext->BuiltinDomainHandle);
479
480 if (EnumContext->AccountDomainHandle != NULL)
481 SamCloseHandle(EnumContext->AccountDomainHandle);
482
483 if (EnumContext->ServerHandle != NULL)
484 SamCloseHandle(EnumContext->ServerHandle);
485
486 if (EnumContext->Buffer != NULL)
487 {
488 for (i = 0; i < EnumContext->Returned; i++)
489 {
490 SamFreeMemory(EnumContext->Buffer[i].Name.Buffer);
491 }
492
493 SamFreeMemory(EnumContext->Buffer);
494 }
495
496 NetApiBufferFree(EnumContext);
497 EnumContext = NULL;
498 }
499 }
500
501 if (GroupHandle != NULL)
502 SamCloseHandle(GroupHandle);
503
504 if (GroupInfo != NULL)
505 FreeGroupInfo(GroupInfo);
506
507 if (resume_handle != NULL)
508 *resume_handle = (DWORD_PTR)EnumContext;
509
510 *bufptr = (LPBYTE)Buffer;
511
512 TRACE("return %lu\n", ApiStatus);
513
514 return ApiStatus;
515 }
516
517
518 NET_API_STATUS
519 WINAPI
520 NetGroupGetInfo(
521 _In_opt_ LPCWSTR servername,
522 _In_ LPCWSTR groupname,
523 _In_ DWORD level,
524 _Out_ LPBYTE *bufptr)
525 {
526 UNICODE_STRING ServerName;
527 UNICODE_STRING GroupName;
528 SAM_HANDLE ServerHandle = NULL;
529 SAM_HANDLE DomainHandle = NULL;
530 SAM_HANDLE GroupHandle = NULL;
531 PGROUP_GENERAL_INFORMATION GroupInfo = NULL;
532 PVOID Buffer = NULL;
533 ULONG RelativeId;
534 NET_API_STATUS ApiStatus = NERR_Success;
535 NTSTATUS Status = STATUS_SUCCESS;
536
537 TRACE("NetGroupGetInfo(%s, %s, %d, %p)\n",
538 debugstr_w(servername), debugstr_w(groupname), level, bufptr);
539
540 if (servername != NULL)
541 RtlInitUnicodeString(&ServerName, servername);
542
543 RtlInitUnicodeString(&GroupName, groupname);
544
545 /* Connect to the SAM Server */
546 Status = SamConnect((servername != NULL) ? &ServerName : NULL,
547 &ServerHandle,
548 SAM_SERVER_CONNECT | SAM_SERVER_LOOKUP_DOMAIN,
549 NULL);
550 if (!NT_SUCCESS(Status))
551 {
552 ERR("SamConnect failed (Status %08lx)\n", Status);
553 ApiStatus = NetpNtStatusToApiStatus(Status);
554 goto done;
555 }
556
557 /* Open the Acount Domain */
558 Status = OpenAccountDomain(ServerHandle,
559 (servername != NULL) ? &ServerName : NULL,
560 DOMAIN_LOOKUP,
561 &DomainHandle);
562 if (!NT_SUCCESS(Status))
563 {
564 ERR("OpenAccountDomain failed (Status %08lx)\n", Status);
565 ApiStatus = NetpNtStatusToApiStatus(Status);
566 goto done;
567 }
568
569 /* Open the group account in the account domain */
570 ApiStatus = OpenGroupByName(DomainHandle,
571 &GroupName,
572 GROUP_READ_INFORMATION,
573 &GroupHandle,
574 &RelativeId);
575 if (ApiStatus != NERR_Success)
576 {
577 ERR("OpenGroupByName failed (ApiStatus %lu)\n", ApiStatus);
578 if (ApiStatus == ERROR_NONE_MAPPED)
579 ApiStatus = NERR_GroupNotFound;
580 goto done;
581 }
582
583 Status = SamQueryInformationGroup(GroupHandle,
584 GroupGeneralInformation,
585 (PVOID *)&GroupInfo);
586 if (!NT_SUCCESS(Status))
587 {
588 ERR("SamQueryInformationGroup failed (Status %08lx)\n", Status);
589 ApiStatus = NetpNtStatusToApiStatus(Status);
590 goto done;
591 }
592
593 ApiStatus = BuildGroupInfoBuffer(GroupInfo,
594 level,
595 RelativeId,
596 &Buffer);
597 if (ApiStatus != NERR_Success)
598 goto done;
599
600 done:
601 if (GroupInfo != NULL)
602 FreeGroupInfo(GroupInfo);
603
604 if (GroupHandle != NULL)
605 SamCloseHandle(GroupHandle);
606
607 if (DomainHandle != NULL)
608 SamCloseHandle(DomainHandle);
609
610 if (ServerHandle != NULL)
611 SamCloseHandle(ServerHandle);
612
613 *bufptr = (LPBYTE)Buffer;
614
615 return ApiStatus;
616 }
617
618 /* EOF */