- Add security function for file and registry key objects.
[reactos.git] / reactos / ntoskrnl / cm / regobj.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/cm/regobj.c
5 * PURPOSE: Registry object manipulation routines.
6 * UPDATE HISTORY:
7 */
8
9 #define NTOS_MODE_KERNEL
10 #include <ntos.h>
11 #include <string.h>
12 #include <roscfg.h>
13 #include <internal/ob.h>
14 #include <ntos/minmax.h>
15 #include <reactos/bugcodes.h>
16 #include <rosrtl/string.h>
17
18 #define NDEBUG
19 #include <internal/debug.h>
20
21 #include "cm.h"
22
23
24 static NTSTATUS
25 CmiGetLinkTarget(PREGISTRY_HIVE RegistryHive,
26 PKEY_CELL KeyCell,
27 PUNICODE_STRING TargetPath);
28
29 /* FUNCTONS *****************************************************************/
30
31 NTSTATUS STDCALL
32 CmiObjectParse(PVOID ParsedObject,
33 PVOID *NextObject,
34 PUNICODE_STRING FullPath,
35 PWSTR *Path,
36 ULONG Attributes)
37 {
38 BLOCK_OFFSET BlockOffset;
39 PKEY_OBJECT FoundObject;
40 PKEY_OBJECT ParsedKey;
41 PKEY_CELL SubKeyCell;
42 NTSTATUS Status;
43 PWSTR StartPtr;
44 PWSTR EndPtr;
45 ULONG Length;
46 UNICODE_STRING LinkPath;
47 UNICODE_STRING TargetPath;
48 UNICODE_STRING KeyName;
49
50 ParsedKey = ParsedObject;
51
52 VERIFY_KEY_OBJECT(ParsedKey);
53
54 *NextObject = NULL;
55
56 if ((*Path) == NULL)
57 {
58 DPRINT("*Path is NULL\n");
59 return STATUS_UNSUCCESSFUL;
60 }
61
62 DPRINT("Path '%S'\n", *Path);
63
64 /* Extract relevant path name */
65 StartPtr = *Path;
66 if (*StartPtr == L'\\')
67 StartPtr++;
68
69 EndPtr = wcschr(StartPtr, L'\\');
70 if (EndPtr != NULL)
71 Length = ((PCHAR)EndPtr - (PCHAR)StartPtr) / sizeof(WCHAR);
72 else
73 Length = wcslen(StartPtr);
74
75
76 KeyName.Length = Length * sizeof(WCHAR);
77 KeyName.MaximumLength = KeyName.Length + sizeof(WCHAR);
78 KeyName.Buffer = ExAllocatePool(NonPagedPool,
79 KeyName.MaximumLength);
80 RtlCopyMemory(KeyName.Buffer,
81 StartPtr,
82 KeyName.Length);
83 KeyName.Buffer[KeyName.Length / sizeof(WCHAR)] = 0;
84
85
86 FoundObject = CmiScanKeyList(ParsedKey,
87 &KeyName,
88 Attributes);
89 if (FoundObject == NULL)
90 {
91 Status = CmiScanForSubKey(ParsedKey->RegistryHive,
92 ParsedKey->KeyCell,
93 &SubKeyCell,
94 &BlockOffset,
95 &KeyName,
96 0,
97 Attributes);
98 if (!NT_SUCCESS(Status) || (SubKeyCell == NULL))
99 {
100 RtlFreeUnicodeString(&KeyName);
101 return(STATUS_UNSUCCESSFUL);
102 }
103
104 if ((SubKeyCell->Flags & REG_KEY_LINK_CELL) &&
105 !((Attributes & OBJ_OPENLINK) && (EndPtr == NULL)))
106 {
107 RtlInitUnicodeString(&LinkPath, NULL);
108 Status = CmiGetLinkTarget(ParsedKey->RegistryHive,
109 SubKeyCell,
110 &LinkPath);
111 if (NT_SUCCESS(Status))
112 {
113 DPRINT("LinkPath '%wZ'\n", &LinkPath);
114
115 /* build new FullPath for reparsing */
116 TargetPath.MaximumLength = LinkPath.MaximumLength;
117 if (EndPtr != NULL)
118 {
119 TargetPath.MaximumLength += (wcslen(EndPtr) * sizeof(WCHAR));
120 }
121 TargetPath.Length = TargetPath.MaximumLength - sizeof(WCHAR);
122 TargetPath.Buffer = ExAllocatePool(NonPagedPool,
123 TargetPath.MaximumLength);
124 wcscpy(TargetPath.Buffer, LinkPath.Buffer);
125 if (EndPtr != NULL)
126 {
127 wcscat(TargetPath.Buffer, EndPtr);
128 }
129
130 RtlFreeUnicodeString(FullPath);
131 RtlFreeUnicodeString(&LinkPath);
132 FullPath->Length = TargetPath.Length;
133 FullPath->MaximumLength = TargetPath.MaximumLength;
134 FullPath->Buffer = TargetPath.Buffer;
135
136 DPRINT("FullPath '%wZ'\n", FullPath);
137
138 /* reinitialize Path for reparsing */
139 *Path = FullPath->Buffer;
140
141 *NextObject = NULL;
142
143 RtlFreeUnicodeString(&KeyName);
144 return(STATUS_REPARSE);
145 }
146 }
147
148 /* Create new key object and put into linked list */
149 DPRINT("CmiObjectParse: %s\n", Path);
150 Status = ObCreateObject(KernelMode,
151 CmiKeyType,
152 NULL,
153 KernelMode,
154 NULL,
155 sizeof(KEY_OBJECT),
156 0,
157 0,
158 (PVOID*)&FoundObject);
159 if (!NT_SUCCESS(Status))
160 {
161 RtlFreeUnicodeString(&KeyName);
162 return(Status);
163 }
164
165 FoundObject->Flags = 0;
166 FoundObject->KeyCell = SubKeyCell;
167 FoundObject->KeyCellOffset = BlockOffset;
168 FoundObject->RegistryHive = ParsedKey->RegistryHive;
169 RtlCreateUnicodeString(&FoundObject->Name,
170 KeyName.Buffer);
171 CmiAddKeyToList(ParsedKey, FoundObject);
172 DPRINT("Created object 0x%x\n", FoundObject);
173 }
174 else
175 {
176 if ((FoundObject->KeyCell->Flags & REG_KEY_LINK_CELL) &&
177 !((Attributes & OBJ_OPENLINK) && (EndPtr == NULL)))
178 {
179 DPRINT("Found link\n");
180
181 RtlInitUnicodeString(&LinkPath, NULL);
182 Status = CmiGetLinkTarget(FoundObject->RegistryHive,
183 FoundObject->KeyCell,
184 &LinkPath);
185 if (NT_SUCCESS(Status))
186 {
187 DPRINT("LinkPath '%wZ'\n", &LinkPath);
188
189 /* build new FullPath for reparsing */
190 TargetPath.MaximumLength = LinkPath.MaximumLength;
191 if (EndPtr != NULL)
192 {
193 TargetPath.MaximumLength += (wcslen(EndPtr) * sizeof(WCHAR));
194 }
195 TargetPath.Length = TargetPath.MaximumLength - sizeof(WCHAR);
196 TargetPath.Buffer = ExAllocatePool(NonPagedPool,
197 TargetPath.MaximumLength);
198 wcscpy(TargetPath.Buffer, LinkPath.Buffer);
199 if (EndPtr != NULL)
200 {
201 wcscat(TargetPath.Buffer, EndPtr);
202 }
203
204 RtlFreeUnicodeString(FullPath);
205 RtlFreeUnicodeString(&LinkPath);
206 FullPath->Length = TargetPath.Length;
207 FullPath->MaximumLength = TargetPath.MaximumLength;
208 FullPath->Buffer = TargetPath.Buffer;
209
210 DPRINT("FullPath '%wZ'\n", FullPath);
211
212 /* reinitialize Path for reparsing */
213 *Path = FullPath->Buffer;
214
215 *NextObject = NULL;
216
217 RtlFreeUnicodeString(&KeyName);
218 return(STATUS_REPARSE);
219 }
220 }
221
222 ObReferenceObjectByPointer(FoundObject,
223 STANDARD_RIGHTS_REQUIRED,
224 NULL,
225 UserMode);
226 }
227
228 DPRINT("CmiObjectParse: %s\n", FoundObject->Name);
229
230 *Path = EndPtr;
231
232 VERIFY_KEY_OBJECT(FoundObject);
233
234 *NextObject = FoundObject;
235
236 RtlFreeUnicodeString(&KeyName);
237
238 return(STATUS_SUCCESS);
239 }
240
241
242 NTSTATUS STDCALL
243 CmiObjectCreate(PVOID ObjectBody,
244 PVOID Parent,
245 PWSTR RemainingPath,
246 POBJECT_ATTRIBUTES ObjectAttributes)
247 {
248 PKEY_OBJECT KeyObject = ObjectBody;
249 PWSTR Start;
250
251 KeyObject->ParentKey = Parent;
252 if (RemainingPath)
253 {
254 Start = RemainingPath;
255 if(*Start == L'\\')
256 Start++;
257 RtlCreateUnicodeString(&KeyObject->Name,
258 Start);
259 }
260 else
261 {
262 RtlInitUnicodeString(&KeyObject->Name,
263 NULL);
264 }
265
266 return STATUS_SUCCESS;
267 }
268
269
270 VOID STDCALL
271 CmiObjectDelete(PVOID DeletedObject)
272 {
273 PKEY_OBJECT ParentKeyObject;
274 PKEY_OBJECT KeyObject;
275
276 DPRINT("Delete key object (%p)\n", DeletedObject);
277
278 KeyObject = (PKEY_OBJECT) DeletedObject;
279 ParentKeyObject = KeyObject->ParentKey;
280
281 ObReferenceObject (ParentKeyObject);
282
283 if (!NT_SUCCESS(CmiRemoveKeyFromList(KeyObject)))
284 {
285 DPRINT1("Key not found in parent list ???\n");
286 }
287
288 RtlFreeUnicodeString(&KeyObject->Name);
289
290 if (KeyObject->Flags & KO_MARKED_FOR_DELETE)
291 {
292 DPRINT("delete really key\n");
293
294 CmiRemoveSubKey(KeyObject->RegistryHive,
295 ParentKeyObject,
296 KeyObject);
297
298 NtQuerySystemTime (&ParentKeyObject->KeyCell->LastWriteTime);
299 CmiMarkBlockDirty (ParentKeyObject->RegistryHive,
300 ParentKeyObject->KeyCellOffset);
301
302 if (!IsNoFileHive (KeyObject->RegistryHive) ||
303 !IsNoFileHive (ParentKeyObject->RegistryHive))
304 {
305 CmiSyncHives ();
306 }
307 }
308
309 ObDereferenceObject (ParentKeyObject);
310
311 if (KeyObject->NumberOfSubKeys)
312 {
313 KEBUGCHECK(REGISTRY_ERROR);
314 }
315
316 if (KeyObject->SizeOfSubKeys)
317 {
318 ExFreePool(KeyObject->SubKeys);
319 }
320 }
321
322
323 NTSTATUS STDCALL
324 CmiObjectSecurity(PVOID ObjectBody,
325 SECURITY_OPERATION_CODE OperationCode,
326 SECURITY_INFORMATION SecurityInformation,
327 PSECURITY_DESCRIPTOR SecurityDescriptor,
328 PULONG BufferLength)
329 {
330 DPRINT("CmiObjectSecurity() called\n");
331
332 switch (OperationCode)
333 {
334 case SetSecurityDescriptor:
335 DPRINT("Set security descriptor\n");
336 return STATUS_SUCCESS;
337
338 case QuerySecurityDescriptor:
339 DPRINT("Query security descriptor\n");
340 return STATUS_UNSUCCESSFUL;
341
342 case DeleteSecurityDescriptor:
343 DPRINT("Delete security descriptor\n");
344 return STATUS_SUCCESS;
345
346 case AssignSecurityDescriptor:
347 DPRINT("Assign security descriptor\n");
348 return STATUS_SUCCESS;
349 }
350
351 return STATUS_UNSUCCESSFUL;
352 }
353
354
355 NTSTATUS STDCALL
356 CmiObjectQueryName (PVOID ObjectBody,
357 POBJECT_NAME_INFORMATION ObjectNameInfo,
358 ULONG Length,
359 PULONG ReturnLength)
360 {
361 POBJECT_NAME_INFORMATION LocalInfo;
362 PKEY_OBJECT KeyObject;
363 ULONG LocalReturnLength;
364 NTSTATUS Status;
365
366 DPRINT ("CmiObjectQueryName() called\n");
367
368 KeyObject = (PKEY_OBJECT)ObjectBody;
369
370 LocalInfo = ExAllocatePool (NonPagedPool,
371 sizeof(OBJECT_NAME_INFORMATION) +
372 MAX_PATH * sizeof(WCHAR));
373 if (LocalInfo == NULL)
374 return STATUS_INSUFFICIENT_RESOURCES;
375
376 if (KeyObject->ParentKey != KeyObject)
377 {
378 Status = ObQueryNameString (KeyObject->ParentKey,
379 LocalInfo,
380 MAX_PATH * sizeof(WCHAR),
381 &LocalReturnLength);
382 }
383 else
384 {
385 /* KeyObject is the root key */
386 Status = ObQueryNameString (BODY_TO_HEADER(KeyObject)->Parent,
387 LocalInfo,
388 MAX_PATH * sizeof(WCHAR),
389 &LocalReturnLength);
390 }
391
392 if (!NT_SUCCESS (Status))
393 {
394 ExFreePool (LocalInfo);
395 return Status;
396 }
397 DPRINT ("Parent path: %wZ\n", &LocalInfo->Name);
398
399 Status = RtlAppendUnicodeStringToString (&ObjectNameInfo->Name,
400 &LocalInfo->Name);
401 ExFreePool (LocalInfo);
402 if (!NT_SUCCESS (Status))
403 return Status;
404
405 Status = RtlAppendUnicodeToString (&ObjectNameInfo->Name,
406 L"\\");
407 if (!NT_SUCCESS (Status))
408 return Status;
409
410 Status = RtlAppendUnicodeStringToString (&ObjectNameInfo->Name,
411 &KeyObject->Name);
412 if (NT_SUCCESS (Status))
413 {
414 DPRINT ("Total path: %wZ\n", &ObjectNameInfo->Name);
415 }
416
417 return Status;
418 }
419
420
421 VOID
422 CmiAddKeyToList(PKEY_OBJECT ParentKey,
423 PKEY_OBJECT NewKey)
424 {
425 KIRQL OldIrql;
426
427 DPRINT("ParentKey %.08x\n", ParentKey);
428
429 KeAcquireSpinLock(&CmiKeyListLock, &OldIrql);
430
431 if (ParentKey->SizeOfSubKeys <= ParentKey->NumberOfSubKeys)
432 {
433 PKEY_OBJECT *tmpSubKeys = ExAllocatePool(NonPagedPool,
434 (ParentKey->NumberOfSubKeys + 1) * sizeof(ULONG));
435
436 if (ParentKey->NumberOfSubKeys > 0)
437 {
438 RtlCopyMemory (tmpSubKeys,
439 ParentKey->SubKeys,
440 ParentKey->NumberOfSubKeys * sizeof(ULONG));
441 }
442
443 if (ParentKey->SubKeys)
444 ExFreePool(ParentKey->SubKeys);
445
446 ParentKey->SubKeys = tmpSubKeys;
447 ParentKey->SizeOfSubKeys = ParentKey->NumberOfSubKeys + 1;
448 }
449
450 /* FIXME: Please maintain the list in alphabetic order */
451 /* to allow a dichotomic search */
452 ParentKey->SubKeys[ParentKey->NumberOfSubKeys++] = NewKey;
453
454 DPRINT("Reference parent key: 0x%x\n", ParentKey);
455
456 ObReferenceObjectByPointer(ParentKey,
457 STANDARD_RIGHTS_REQUIRED,
458 NULL,
459 UserMode);
460 NewKey->ParentKey = ParentKey;
461 KeReleaseSpinLock(&CmiKeyListLock, OldIrql);
462 }
463
464
465 NTSTATUS
466 CmiRemoveKeyFromList(PKEY_OBJECT KeyToRemove)
467 {
468 PKEY_OBJECT ParentKey;
469 KIRQL OldIrql;
470 DWORD Index;
471
472 ParentKey = KeyToRemove->ParentKey;
473 KeAcquireSpinLock(&CmiKeyListLock, &OldIrql);
474 /* FIXME: If list maintained in alphabetic order, use dichotomic search */
475 for (Index = 0; Index < ParentKey->NumberOfSubKeys; Index++)
476 {
477 if (ParentKey->SubKeys[Index] == KeyToRemove)
478 {
479 if (Index < ParentKey->NumberOfSubKeys-1)
480 RtlMoveMemory(&ParentKey->SubKeys[Index],
481 &ParentKey->SubKeys[Index + 1],
482 (ParentKey->NumberOfSubKeys - Index - 1) * sizeof(PKEY_OBJECT));
483 ParentKey->NumberOfSubKeys--;
484 KeReleaseSpinLock(&CmiKeyListLock, OldIrql);
485
486 DPRINT("Dereference parent key: 0x%x\n", ParentKey);
487
488 ObDereferenceObject(ParentKey);
489 return STATUS_SUCCESS;
490 }
491 }
492 KeReleaseSpinLock(&CmiKeyListLock, OldIrql);
493
494 return STATUS_UNSUCCESSFUL;
495 }
496
497
498 PKEY_OBJECT
499 CmiScanKeyList(PKEY_OBJECT Parent,
500 PUNICODE_STRING KeyName,
501 ULONG Attributes)
502 {
503 PKEY_OBJECT CurKey;
504 KIRQL OldIrql;
505 ULONG Index;
506
507 DPRINT("Scanning key list for: %wZ (Parent: %wZ)\n",
508 KeyName, &Parent->Name);
509
510 KeAcquireSpinLock(&CmiKeyListLock, &OldIrql);
511 /* FIXME: if list maintained in alphabetic order, use dichotomic search */
512 for (Index=0; Index < Parent->NumberOfSubKeys; Index++)
513 {
514 CurKey = Parent->SubKeys[Index];
515 if (Attributes & OBJ_CASE_INSENSITIVE)
516 {
517 if ((KeyName->Length == CurKey->Name.Length)
518 && (_wcsicmp(KeyName->Buffer, CurKey->Name.Buffer) == 0))
519 {
520 KeReleaseSpinLock(&CmiKeyListLock, OldIrql);
521 return CurKey;
522 }
523 }
524 else
525 {
526 if ((KeyName->Length == CurKey->Name.Length)
527 && (wcscmp(KeyName->Buffer, CurKey->Name.Buffer) == 0))
528 {
529 KeReleaseSpinLock(&CmiKeyListLock, OldIrql);
530 return CurKey;
531 }
532 }
533 }
534 KeReleaseSpinLock(&CmiKeyListLock, OldIrql);
535
536 return NULL;
537 }
538
539
540 static NTSTATUS
541 CmiGetLinkTarget(PREGISTRY_HIVE RegistryHive,
542 PKEY_CELL KeyCell,
543 PUNICODE_STRING TargetPath)
544 {
545 UNICODE_STRING LinkName = ROS_STRING_INITIALIZER(L"SymbolicLinkValue");
546 PVALUE_CELL ValueCell;
547 PDATA_CELL DataCell;
548 NTSTATUS Status;
549
550 DPRINT("CmiGetLinkTarget() called\n");
551
552 /* Get Value block of interest */
553 Status = CmiScanKeyForValue(RegistryHive,
554 KeyCell,
555 &LinkName,
556 &ValueCell,
557 NULL);
558 if (!NT_SUCCESS(Status))
559 {
560 DPRINT1("CmiScanKeyForValue() failed (Status %lx)\n", Status);
561 return(Status);
562 }
563
564 if (ValueCell->DataType != REG_LINK)
565 {
566 DPRINT1("Type != REG_LINK\n!");
567 return(STATUS_UNSUCCESSFUL);
568 }
569
570 if (TargetPath->Buffer == NULL && TargetPath->MaximumLength == 0)
571 {
572 TargetPath->Length = 0;
573 TargetPath->MaximumLength = ValueCell->DataSize + sizeof(WCHAR);
574 TargetPath->Buffer = ExAllocatePool(NonPagedPool,
575 TargetPath->MaximumLength);
576 }
577
578 TargetPath->Length = min(TargetPath->MaximumLength - sizeof(WCHAR),
579 (ULONG) ValueCell->DataSize);
580
581 if (ValueCell->DataSize > 0)
582 {
583 DataCell = CmiGetCell (RegistryHive, ValueCell->DataOffset, NULL);
584 RtlCopyMemory(TargetPath->Buffer,
585 DataCell->Data,
586 TargetPath->Length);
587 TargetPath->Buffer[TargetPath->Length / sizeof(WCHAR)] = 0;
588 }
589 else
590 {
591 RtlCopyMemory(TargetPath->Buffer,
592 &ValueCell->DataOffset,
593 TargetPath->Length);
594 TargetPath->Buffer[TargetPath->Length / sizeof(WCHAR)] = 0;
595 }
596
597 DPRINT("TargetPath '%wZ'\n", TargetPath);
598
599 return(STATUS_SUCCESS);
600 }
601
602 /* EOF */