Create this branch to work on loading of different Kernel-Debugger DLL providers...
[reactos.git] / dll / win32 / samsrv / database.c
1 /*
2 * PROJECT: Local Security Authority Server DLL
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: dll/win32/samsrv/database.c
5 * PURPOSE: SAM object database
6 * COPYRIGHT: Copyright 2012 Eric Kohl
7 */
8
9 /* INCLUDES ****************************************************************/
10
11 #include "samsrv.h"
12
13 WINE_DEFAULT_DEBUG_CHANNEL(samsrv);
14
15
16 /* GLOBALS *****************************************************************/
17
18 static HANDLE SamKeyHandle = NULL;
19
20
21 /* FUNCTIONS ***************************************************************/
22
23 static NTSTATUS
24 SampOpenSamKey(VOID)
25 {
26 OBJECT_ATTRIBUTES ObjectAttributes;
27 UNICODE_STRING KeyName;
28 NTSTATUS Status;
29
30 RtlInitUnicodeString(&KeyName,
31 L"\\Registry\\Machine\\SAM");
32
33 InitializeObjectAttributes(&ObjectAttributes,
34 &KeyName,
35 OBJ_CASE_INSENSITIVE,
36 NULL,
37 NULL);
38
39 Status = RtlpNtOpenKey(&SamKeyHandle,
40 KEY_READ | KEY_CREATE_SUB_KEY | KEY_ENUMERATE_SUB_KEYS,
41 &ObjectAttributes,
42 0);
43
44 return Status;
45 }
46
47
48 NTSTATUS
49 SampInitDatabase(VOID)
50 {
51 NTSTATUS Status;
52
53 TRACE("SampInitDatabase()\n");
54
55 Status = SampOpenSamKey();
56 if (!NT_SUCCESS(Status))
57 {
58 ERR("Failed to open the SAM key (Status: 0x%08lx)\n", Status);
59 return Status;
60 }
61
62 #if 0
63 if (!LsapIsDatabaseInstalled())
64 {
65 Status = LsapCreateDatabaseKeys();
66 if (!NT_SUCCESS(Status))
67 {
68 ERR("Failed to create the LSA database keys (Status: 0x%08lx)\n", Status);
69 return Status;
70 }
71
72 Status = LsapCreateDatabaseObjects();
73 if (!NT_SUCCESS(Status))
74 {
75 ERR("Failed to create the LSA database objects (Status: 0x%08lx)\n", Status);
76 return Status;
77 }
78 }
79 else
80 {
81 Status = LsapUpdateDatabase();
82 if (!NT_SUCCESS(Status))
83 {
84 ERR("Failed to update the LSA database (Status: 0x%08lx)\n", Status);
85 return Status;
86 }
87 }
88 #endif
89
90 TRACE("SampInitDatabase() done\n");
91
92 return STATUS_SUCCESS;
93 }
94
95
96 NTSTATUS
97 SampCreateDbObject(IN PSAM_DB_OBJECT ParentObject,
98 IN LPWSTR ContainerName,
99 IN LPWSTR ObjectName,
100 IN ULONG RelativeId,
101 IN SAM_DB_OBJECT_TYPE ObjectType,
102 IN ACCESS_MASK DesiredAccess,
103 OUT PSAM_DB_OBJECT *DbObject)
104 {
105 PSAM_DB_OBJECT NewObject;
106 OBJECT_ATTRIBUTES ObjectAttributes;
107 UNICODE_STRING KeyName;
108 HANDLE ParentKeyHandle;
109 HANDLE ContainerKeyHandle = NULL;
110 HANDLE ObjectKeyHandle = NULL;
111 HANDLE MembersKeyHandle = NULL;
112 NTSTATUS Status;
113
114 if (DbObject == NULL)
115 return STATUS_INVALID_PARAMETER;
116
117 if (ParentObject == NULL)
118 ParentKeyHandle = SamKeyHandle;
119 else
120 ParentKeyHandle = ParentObject->KeyHandle;
121
122 if (ContainerName != NULL)
123 {
124 /* Open the container key */
125 RtlInitUnicodeString(&KeyName,
126 ContainerName);
127
128 InitializeObjectAttributes(&ObjectAttributes,
129 &KeyName,
130 OBJ_CASE_INSENSITIVE,
131 ParentKeyHandle,
132 NULL);
133
134 Status = NtOpenKey(&ContainerKeyHandle,
135 KEY_ALL_ACCESS,
136 &ObjectAttributes);
137 if (!NT_SUCCESS(Status))
138 {
139 return Status;
140 }
141
142 /* Open the object key */
143 RtlInitUnicodeString(&KeyName,
144 ObjectName);
145
146 InitializeObjectAttributes(&ObjectAttributes,
147 &KeyName,
148 OBJ_CASE_INSENSITIVE,
149 ContainerKeyHandle,
150 NULL);
151
152 Status = NtCreateKey(&ObjectKeyHandle,
153 KEY_ALL_ACCESS,
154 &ObjectAttributes,
155 0,
156 NULL,
157 0,
158 NULL);
159
160 if (ObjectType == SamDbAliasObject)
161 {
162 /* Open the object key */
163 RtlInitUnicodeString(&KeyName,
164 L"Members");
165
166 InitializeObjectAttributes(&ObjectAttributes,
167 &KeyName,
168 OBJ_CASE_INSENSITIVE | OBJ_OPENIF,
169 ContainerKeyHandle,
170 NULL);
171
172 Status = NtCreateKey(&MembersKeyHandle,
173 KEY_ALL_ACCESS,
174 &ObjectAttributes,
175 0,
176 NULL,
177 0,
178 NULL);
179 }
180
181 NtClose(ContainerKeyHandle);
182
183 if (!NT_SUCCESS(Status))
184 {
185 return Status;
186 }
187 }
188 else
189 {
190 RtlInitUnicodeString(&KeyName,
191 ObjectName);
192
193 InitializeObjectAttributes(&ObjectAttributes,
194 &KeyName,
195 OBJ_CASE_INSENSITIVE,
196 ParentKeyHandle,
197 NULL);
198
199 Status = NtCreateKey(&ObjectKeyHandle,
200 KEY_ALL_ACCESS,
201 &ObjectAttributes,
202 0,
203 NULL,
204 0,
205 NULL);
206 if (!NT_SUCCESS(Status))
207 {
208 return Status;
209 }
210 }
211
212 NewObject = RtlAllocateHeap(RtlGetProcessHeap(),
213 HEAP_ZERO_MEMORY,
214 sizeof(SAM_DB_OBJECT));
215 if (NewObject == NULL)
216 {
217 if (MembersKeyHandle != NULL)
218 NtClose(MembersKeyHandle);
219 NtClose(ObjectKeyHandle);
220 return STATUS_NO_MEMORY;
221 }
222
223 NewObject->Name = RtlAllocateHeap(RtlGetProcessHeap(),
224 0,
225 (wcslen(ObjectName) + 1) * sizeof(WCHAR));
226 if (NewObject->Name == NULL)
227 {
228 if (MembersKeyHandle != NULL)
229 NtClose(MembersKeyHandle);
230 NtClose(ObjectKeyHandle);
231 RtlFreeHeap(RtlGetProcessHeap(), 0, NewObject);
232 return STATUS_NO_MEMORY;
233 }
234
235 wcscpy(NewObject->Name, ObjectName);
236
237 NewObject->Signature = SAMP_DB_SIGNATURE;
238 NewObject->RefCount = 1;
239 NewObject->ObjectType = ObjectType;
240 NewObject->Access = DesiredAccess;
241 NewObject->KeyHandle = ObjectKeyHandle;
242 NewObject->MembersKeyHandle = MembersKeyHandle;
243 NewObject->RelativeId = RelativeId;
244 NewObject->ParentObject = ParentObject;
245
246 if (ParentObject != NULL)
247 NewObject->Trusted = ParentObject->Trusted;
248
249 *DbObject = NewObject;
250
251 return STATUS_SUCCESS;
252 }
253
254
255 NTSTATUS
256 SampOpenDbObject(IN PSAM_DB_OBJECT ParentObject,
257 IN LPWSTR ContainerName,
258 IN LPWSTR ObjectName,
259 IN ULONG RelativeId,
260 IN SAM_DB_OBJECT_TYPE ObjectType,
261 IN ACCESS_MASK DesiredAccess,
262 OUT PSAM_DB_OBJECT *DbObject)
263 {
264 PSAM_DB_OBJECT NewObject;
265 OBJECT_ATTRIBUTES ObjectAttributes;
266 UNICODE_STRING KeyName;
267 HANDLE ParentKeyHandle;
268 HANDLE ContainerKeyHandle = NULL;
269 HANDLE ObjectKeyHandle = NULL;
270 HANDLE MembersKeyHandle = NULL;
271 NTSTATUS Status;
272
273 if (DbObject == NULL)
274 return STATUS_INVALID_PARAMETER;
275
276 if (ParentObject == NULL)
277 ParentKeyHandle = SamKeyHandle;
278 else
279 ParentKeyHandle = ParentObject->KeyHandle;
280
281 if (ContainerName != NULL)
282 {
283 /* Open the container key */
284 RtlInitUnicodeString(&KeyName,
285 ContainerName);
286
287 InitializeObjectAttributes(&ObjectAttributes,
288 &KeyName,
289 OBJ_CASE_INSENSITIVE,
290 ParentKeyHandle,
291 NULL);
292
293 Status = NtOpenKey(&ContainerKeyHandle,
294 KEY_ALL_ACCESS,
295 &ObjectAttributes);
296 if (!NT_SUCCESS(Status))
297 {
298 return Status;
299 }
300
301 /* Open the object key */
302 RtlInitUnicodeString(&KeyName,
303 ObjectName);
304
305 InitializeObjectAttributes(&ObjectAttributes,
306 &KeyName,
307 OBJ_CASE_INSENSITIVE,
308 ContainerKeyHandle,
309 NULL);
310
311 Status = NtOpenKey(&ObjectKeyHandle,
312 KEY_ALL_ACCESS,
313 &ObjectAttributes);
314
315 if (ObjectType == SamDbAliasObject)
316 {
317 /* Open the object key */
318 RtlInitUnicodeString(&KeyName,
319 L"Members");
320
321 InitializeObjectAttributes(&ObjectAttributes,
322 &KeyName,
323 OBJ_CASE_INSENSITIVE | OBJ_OPENIF,
324 ContainerKeyHandle,
325 NULL);
326
327 Status = NtCreateKey(&MembersKeyHandle,
328 KEY_ALL_ACCESS,
329 &ObjectAttributes,
330 0,
331 NULL,
332 0,
333 NULL);
334 }
335
336 NtClose(ContainerKeyHandle);
337
338 if (!NT_SUCCESS(Status))
339 {
340 return Status;
341 }
342 }
343 else
344 {
345 /* Open the object key */
346 RtlInitUnicodeString(&KeyName,
347 ObjectName);
348
349 InitializeObjectAttributes(&ObjectAttributes,
350 &KeyName,
351 OBJ_CASE_INSENSITIVE,
352 ParentKeyHandle,
353 NULL);
354
355 Status = NtOpenKey(&ObjectKeyHandle,
356 KEY_ALL_ACCESS,
357 &ObjectAttributes);
358 if (!NT_SUCCESS(Status))
359 {
360 return Status;
361 }
362 }
363
364 NewObject = RtlAllocateHeap(RtlGetProcessHeap(),
365 HEAP_ZERO_MEMORY,
366 sizeof(SAM_DB_OBJECT));
367 if (NewObject == NULL)
368 {
369 if (MembersKeyHandle != NULL)
370 NtClose(MembersKeyHandle);
371 NtClose(ObjectKeyHandle);
372 return STATUS_NO_MEMORY;
373 }
374
375 NewObject->Name = RtlAllocateHeap(RtlGetProcessHeap(),
376 0,
377 (wcslen(ObjectName) + 1) * sizeof(WCHAR));
378 if (NewObject->Name == NULL)
379 {
380 if (MembersKeyHandle != NULL)
381 NtClose(MembersKeyHandle);
382 NtClose(ObjectKeyHandle);
383 RtlFreeHeap(RtlGetProcessHeap(), 0, NewObject);
384 return STATUS_NO_MEMORY;
385 }
386
387 wcscpy(NewObject->Name, ObjectName);
388 NewObject->Signature = SAMP_DB_SIGNATURE;
389 NewObject->RefCount = 1;
390 NewObject->ObjectType = ObjectType;
391 NewObject->Access = DesiredAccess;
392 NewObject->KeyHandle = ObjectKeyHandle;
393 NewObject->MembersKeyHandle = MembersKeyHandle;
394 NewObject->RelativeId = RelativeId;
395 NewObject->ParentObject = ParentObject;
396
397 if (ParentObject != NULL)
398 NewObject->Trusted = ParentObject->Trusted;
399
400 *DbObject = NewObject;
401
402 return STATUS_SUCCESS;
403 }
404
405
406 NTSTATUS
407 SampValidateDbObject(SAMPR_HANDLE Handle,
408 SAM_DB_OBJECT_TYPE ObjectType,
409 ACCESS_MASK DesiredAccess,
410 PSAM_DB_OBJECT *DbObject)
411 {
412 PSAM_DB_OBJECT LocalObject = (PSAM_DB_OBJECT)Handle;
413 BOOLEAN bValid = FALSE;
414
415 _SEH2_TRY
416 {
417 if (LocalObject->Signature == SAMP_DB_SIGNATURE)
418 {
419 if ((ObjectType == SamDbIgnoreObject) ||
420 (LocalObject->ObjectType == ObjectType))
421 bValid = TRUE;
422 }
423 }
424 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
425 {
426 bValid = FALSE;
427 }
428 _SEH2_END;
429
430 if (bValid == FALSE)
431 return STATUS_INVALID_HANDLE;
432
433 if (DesiredAccess != 0)
434 {
435 /* Check for granted access rights */
436 if ((LocalObject->Access & DesiredAccess) != DesiredAccess)
437 {
438 ERR("SampValidateDbObject access check failed %08lx %08lx\n",
439 LocalObject->Access, DesiredAccess);
440 return STATUS_ACCESS_DENIED;
441 }
442 }
443
444 if (DbObject != NULL)
445 *DbObject = LocalObject;
446
447 return STATUS_SUCCESS;
448 }
449
450
451 NTSTATUS
452 SampCloseDbObject(PSAM_DB_OBJECT DbObject)
453 {
454 PSAM_DB_OBJECT ParentObject = NULL;
455 NTSTATUS Status = STATUS_SUCCESS;
456
457 DbObject->RefCount--;
458
459 if (DbObject->RefCount > 0)
460 return STATUS_SUCCESS;
461
462 if (DbObject->KeyHandle != NULL)
463 NtClose(DbObject->KeyHandle);
464
465 if (DbObject->MembersKeyHandle != NULL)
466 NtClose(DbObject->MembersKeyHandle);
467
468 if (DbObject->ParentObject != NULL)
469 ParentObject = DbObject->ParentObject;
470
471 if (DbObject->Name != NULL)
472 RtlFreeHeap(RtlGetProcessHeap(), 0, DbObject->Name);
473
474 RtlFreeHeap(RtlGetProcessHeap(), 0, DbObject);
475
476 return Status;
477 }
478
479
480 NTSTATUS
481 SampDeleteAccountDbObject(PSAM_DB_OBJECT DbObject)
482 {
483 LPCWSTR ContainerName;
484 LPWSTR AccountName = NULL;
485 HKEY ContainerKey;
486 HKEY NamesKey;
487 ULONG Length = 0;
488 NTSTATUS Status = STATUS_SUCCESS;
489
490 TRACE("(%p)\n", DbObject);
491
492 /* Server and Domain objects cannot be deleted */
493 switch (DbObject->ObjectType)
494 {
495 case SamDbAliasObject:
496 ContainerName = L"Aliases";
497 break;
498
499 case SamDbGroupObject:
500 ContainerName = L"Groups";
501 break;
502
503 case SamDbUserObject:
504 ContainerName = L"Users";
505 break;
506
507 default:
508 return STATUS_INVALID_PARAMETER;
509 }
510
511 /* Get the account name */
512 Status = SampGetObjectAttribute(DbObject,
513 L"Name",
514 NULL,
515 NULL,
516 &Length);
517 if (Status != STATUS_BUFFER_OVERFLOW)
518 {
519 TRACE("Status 0x%08lx\n", Status);
520 goto done;
521 }
522
523 AccountName = RtlAllocateHeap(RtlGetProcessHeap(),
524 HEAP_ZERO_MEMORY,
525 Length);
526 if (AccountName == NULL)
527 {
528 Status = STATUS_INSUFFICIENT_RESOURCES;
529 goto done;
530 }
531
532 Status = SampGetObjectAttribute(DbObject,
533 L"Name",
534 NULL,
535 (PVOID)AccountName,
536 &Length);
537 if (!NT_SUCCESS(Status))
538 {
539 TRACE("SampGetObjectAttribute failed (Status 0x%08lx)\n", Status);
540 goto done;
541 }
542
543 if (DbObject->KeyHandle != NULL)
544 NtClose(DbObject->KeyHandle);
545
546 if (DbObject->ObjectType == SamDbAliasObject)
547 {
548 if (DbObject->MembersKeyHandle != NULL)
549 NtClose(DbObject->MembersKeyHandle);
550
551 SampRegDeleteKey(DbObject->KeyHandle,
552 L"Members");
553 }
554
555 /* Open the domain container key */
556 Status = SampRegOpenKey(DbObject->ParentObject->KeyHandle,
557 ContainerName,
558 DELETE | KEY_SET_VALUE,
559 &ContainerKey);
560 if (!NT_SUCCESS(Status))
561 {
562 TRACE("SampRegOpenKey failed (Status 0x%08lx)\n", Status);
563 goto done;
564 }
565
566 /* Open the Names key */
567 Status = SampRegOpenKey(ContainerKey,
568 L"Names",
569 KEY_SET_VALUE,
570 &NamesKey);
571 if (!NT_SUCCESS(Status))
572 {
573 TRACE("SampRegOpenKey failed (Status 0x%08lx)\n", Status);
574 goto done;
575 }
576
577 /* Remove the account from the Names key */
578 Status = SampRegDeleteValue(NamesKey,
579 AccountName);
580 if (!NT_SUCCESS(Status))
581 {
582 TRACE("SampRegDeleteValue failed (Status 0x%08lx)\n", Status);
583 goto done;
584 }
585
586 /* Remove the account key from the container */
587 Status = SampRegDeleteKey(ContainerKey,
588 DbObject->Name);
589 if (!NT_SUCCESS(Status))
590 {
591 TRACE("SampRegDeleteKey failed (Status 0x%08lx)\n", Status);
592 goto done;
593 }
594
595 /* Release the database object name */
596 if (DbObject->Name != NULL)
597 RtlFreeHeap(RtlGetProcessHeap(), 0, DbObject->Name);
598
599 /* Release the database object */
600 RtlFreeHeap(RtlGetProcessHeap(), 0, DbObject);
601
602 Status = STATUS_SUCCESS;
603
604 done:
605 if (NamesKey != NULL)
606 SampRegCloseKey(NamesKey);
607
608 if (ContainerKey != NULL)
609 SampRegCloseKey(ContainerKey);
610
611 if (AccountName != NULL)
612 RtlFreeHeap(RtlGetProcessHeap(), 0, AccountName);
613
614 return Status;
615 }
616
617
618 NTSTATUS
619 SampSetObjectAttribute(PSAM_DB_OBJECT DbObject,
620 LPWSTR AttributeName,
621 ULONG AttributeType,
622 LPVOID AttributeData,
623 ULONG AttributeSize)
624 {
625 UNICODE_STRING ValueName;
626
627 RtlInitUnicodeString(&ValueName,
628 AttributeName);
629
630 return ZwSetValueKey(DbObject->KeyHandle,
631 &ValueName,
632 0,
633 AttributeType,
634 AttributeData,
635 AttributeSize);
636 }
637
638
639 NTSTATUS
640 SampGetObjectAttribute(PSAM_DB_OBJECT DbObject,
641 LPWSTR AttributeName,
642 PULONG AttributeType,
643 LPVOID AttributeData,
644 PULONG AttributeSize)
645 {
646 PKEY_VALUE_PARTIAL_INFORMATION ValueInfo;
647 UNICODE_STRING ValueName;
648 ULONG BufferLength = 0;
649 NTSTATUS Status;
650
651 RtlInitUnicodeString(&ValueName,
652 AttributeName);
653
654 if (AttributeSize != NULL)
655 BufferLength = *AttributeSize;
656
657 BufferLength += FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
658
659 /* Allocate memory for the value */
660 ValueInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength);
661 if (ValueInfo == NULL)
662 return STATUS_NO_MEMORY;
663
664 /* Query the value */
665 Status = ZwQueryValueKey(DbObject->KeyHandle,
666 &ValueName,
667 KeyValuePartialInformation,
668 ValueInfo,
669 BufferLength,
670 &BufferLength);
671 if ((NT_SUCCESS(Status)) || (Status == STATUS_BUFFER_OVERFLOW))
672 {
673 if (AttributeType != NULL)
674 *AttributeType = ValueInfo->Type;
675
676 if (AttributeSize != NULL)
677 *AttributeSize = ValueInfo->DataLength;
678 }
679
680 /* Check if the caller wanted data back, and we got it */
681 if ((NT_SUCCESS(Status)) && (AttributeData != NULL))
682 {
683 /* Copy it */
684 RtlMoveMemory(AttributeData,
685 ValueInfo->Data,
686 ValueInfo->DataLength);
687 }
688
689 /* Free the memory and return status */
690 RtlFreeHeap(RtlGetProcessHeap(), 0, ValueInfo);
691
692 return Status;
693 }
694
695
696 NTSTATUS
697 SampGetObjectAttributeString(PSAM_DB_OBJECT DbObject,
698 LPWSTR AttributeName,
699 RPC_UNICODE_STRING *String)
700 {
701 ULONG Length = 0;
702 NTSTATUS Status;
703
704 Status = SampGetObjectAttribute(DbObject,
705 AttributeName,
706 NULL,
707 NULL,
708 &Length);
709 if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW)
710 {
711 TRACE("Status 0x%08lx\n", Status);
712 goto done;
713 }
714
715 String->Length = (USHORT)(Length - sizeof(WCHAR));
716 String->MaximumLength = (USHORT)Length;
717 String->Buffer = midl_user_allocate(Length);
718 if (String->Buffer == NULL)
719 {
720 Status = STATUS_INSUFFICIENT_RESOURCES;
721 goto done;
722 }
723
724 TRACE("Length: %lu\n", Length);
725 Status = SampGetObjectAttribute(DbObject,
726 AttributeName,
727 NULL,
728 (PVOID)String->Buffer,
729 &Length);
730 if (!NT_SUCCESS(Status))
731 {
732 TRACE("Status 0x%08lx\n", Status);
733 goto done;
734 }
735
736 done:
737 if (!NT_SUCCESS(Status))
738 {
739 if (String->Buffer != NULL)
740 {
741 midl_user_free(String->Buffer);
742 String->Buffer = NULL;
743 }
744 }
745
746 return Status;
747 }
748
749 /* EOF */
750