* Implemented/fixed NtSetSecurityObject() and NtQuerySecurityObject().
[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 #ifdef WIN32_REGDBG
10 #include "cm_win32.h"
11 #else
12 #include <ddk/ntddk.h>
13 #include <roscfg.h>
14 #include <internal/ob.h>
15 #include <limits.h>
16 #include <string.h>
17 #include <internal/pool.h>
18 #include <internal/registry.h>
19 #include <ntos/minmax.h>
20
21 #define NDEBUG
22 #include <internal/debug.h>
23
24 #include "cm.h"
25 #endif
26
27
28 static NTSTATUS
29 CmiGetLinkTarget(PREGISTRY_HIVE RegistryHive,
30 PKEY_CELL KeyCell,
31 PUNICODE_STRING TargetPath);
32
33 /* FUNCTONS *****************************************************************/
34
35 NTSTATUS STDCALL
36 CmiObjectParse(PVOID ParsedObject,
37 PVOID *NextObject,
38 PUNICODE_STRING FullPath,
39 PWSTR *Path,
40 ULONG Attributes)
41 {
42 BLOCK_OFFSET BlockOffset;
43 PKEY_OBJECT FoundObject;
44 PKEY_OBJECT ParsedKey;
45 PKEY_CELL SubKeyCell;
46 CHAR cPath[MAX_PATH];
47 NTSTATUS Status;
48 PWSTR StartPtr;
49 PWSTR EndPtr;
50 ULONG Length;
51 UNICODE_STRING LinkPath;
52 UNICODE_STRING TargetPath;
53
54 ParsedKey = ParsedObject;
55
56 VERIFY_KEY_OBJECT(ParsedKey);
57
58 *NextObject = NULL;
59
60 if ((*Path) == NULL)
61 {
62 DPRINT("*Path is NULL\n");
63 return STATUS_UNSUCCESSFUL;
64 }
65
66 DPRINT("Path '%S'\n", *Path);
67
68 /* Extract relevant path name */
69 StartPtr = *Path;
70 if (*StartPtr == L'\\')
71 StartPtr++;
72
73 EndPtr = wcschr(StartPtr, L'\\');
74 if (EndPtr != NULL)
75 Length = ((PCHAR)EndPtr - (PCHAR)StartPtr) / sizeof(WCHAR);
76 else
77 Length = wcslen(StartPtr);
78
79 wcstombs(cPath, StartPtr, Length);
80 cPath[Length] = 0;
81
82
83 FoundObject = CmiScanKeyList(ParsedKey, cPath, Attributes);
84 if (FoundObject == NULL)
85 {
86 Status = CmiScanForSubKey(ParsedKey->RegistryHive,
87 ParsedKey->KeyCell,
88 &SubKeyCell,
89 &BlockOffset,
90 cPath,
91 0,
92 Attributes);
93 if (!NT_SUCCESS(Status) || (SubKeyCell == NULL))
94 {
95 return(STATUS_UNSUCCESSFUL);
96 }
97
98 if ((SubKeyCell->Type == REG_LINK_KEY_CELL_TYPE) &&
99 !((Attributes & OBJ_OPENLINK) && (EndPtr == NULL) /*(end == NULL)*/))
100 {
101 RtlInitUnicodeString(&LinkPath, NULL);
102 Status = CmiGetLinkTarget(ParsedKey->RegistryHive,
103 SubKeyCell,
104 &LinkPath);
105 if (NT_SUCCESS(Status))
106 {
107 DPRINT("LinkPath '%wZ'\n", &LinkPath);
108
109 /* build new FullPath for reparsing */
110 TargetPath.MaximumLength = LinkPath.MaximumLength;
111 if (EndPtr != NULL)
112 {
113 TargetPath.MaximumLength += (wcslen(EndPtr) * sizeof(WCHAR));
114 }
115 TargetPath.Length = TargetPath.MaximumLength - sizeof(WCHAR);
116 TargetPath.Buffer = ExAllocatePool(NonPagedPool,
117 TargetPath.MaximumLength);
118 wcscpy(TargetPath.Buffer, LinkPath.Buffer);
119 if (EndPtr != NULL)
120 {
121 wcscat(TargetPath.Buffer, EndPtr);
122 }
123
124 RtlFreeUnicodeString(FullPath);
125 RtlFreeUnicodeString(&LinkPath);
126 FullPath->Length = TargetPath.Length;
127 FullPath->MaximumLength = TargetPath.MaximumLength;
128 FullPath->Buffer = TargetPath.Buffer;
129
130 DPRINT("FullPath '%wZ'\n", FullPath);
131
132 /* reinitialize Path for reparsing */
133 *Path = FullPath->Buffer;
134
135 *NextObject = NULL;
136 return(STATUS_REPARSE);
137 }
138 }
139
140 /* Create new key object and put into linked list */
141 DPRINT("CmiObjectParse: %s\n", cPath);
142 Status = ObCreateObject(NULL,
143 STANDARD_RIGHTS_REQUIRED,
144 NULL,
145 CmiKeyType,
146 (PVOID*)&FoundObject);
147 if (!NT_SUCCESS(Status))
148 {
149 return(Status);
150 }
151
152 FoundObject->Flags = 0;
153 FoundObject->Name = SubKeyCell->Name;
154 FoundObject->NameSize = SubKeyCell->NameSize;
155 FoundObject->KeyCell = SubKeyCell;
156 FoundObject->BlockOffset = BlockOffset;
157 FoundObject->RegistryHive = ParsedKey->RegistryHive;
158 CmiAddKeyToList(ParsedKey, FoundObject);
159 DPRINT("Created object 0x%x\n", FoundObject);
160 }
161 else
162 {
163 if ((FoundObject->KeyCell->Type == REG_LINK_KEY_CELL_TYPE) &&
164 !((Attributes & OBJ_OPENLINK) && (EndPtr == NULL)/*(end == NULL)*/))
165 {
166 DPRINT("Found link\n");
167
168 RtlInitUnicodeString(&LinkPath, NULL);
169 Status = CmiGetLinkTarget(FoundObject->RegistryHive,
170 FoundObject->KeyCell,
171 &LinkPath);
172 if (NT_SUCCESS(Status))
173 {
174 DPRINT("LinkPath '%wZ'\n", &LinkPath);
175
176 /* build new FullPath for reparsing */
177 TargetPath.MaximumLength = LinkPath.MaximumLength;
178 if (EndPtr != NULL)
179 {
180 TargetPath.MaximumLength += (wcslen(EndPtr) * sizeof(WCHAR));
181 }
182 TargetPath.Length = TargetPath.MaximumLength - sizeof(WCHAR);
183 TargetPath.Buffer = ExAllocatePool(NonPagedPool,
184 TargetPath.MaximumLength);
185 wcscpy(TargetPath.Buffer, LinkPath.Buffer);
186 if (EndPtr != NULL)
187 {
188 wcscat(TargetPath.Buffer, EndPtr);
189 }
190
191 RtlFreeUnicodeString(FullPath);
192 RtlFreeUnicodeString(&LinkPath);
193 FullPath->Length = TargetPath.Length;
194 FullPath->MaximumLength = TargetPath.MaximumLength;
195 FullPath->Buffer = TargetPath.Buffer;
196
197 DPRINT("FullPath '%wZ'\n", FullPath);
198
199 /* reinitialize Path for reparsing */
200 *Path = FullPath->Buffer;
201
202 *NextObject = NULL;
203 return(STATUS_REPARSE);
204 }
205 }
206
207 ObReferenceObjectByPointer(FoundObject,
208 STANDARD_RIGHTS_REQUIRED,
209 NULL,
210 UserMode);
211 }
212 #ifndef WIN32_REGDBG
213 DPRINT("CmiObjectParse: %s\n", FoundObject->Name);
214 #else
215 {
216 char buffer[_BUFFER_LEN];
217 memset(buffer, 0, _BUFFER_LEN);
218 strncpy(buffer, FoundObject->Name, min(FoundObject->NameSize, _BUFFER_LEN - 1));
219 DPRINT("CmiObjectParse: %s\n", buffer);
220 }
221 #endif
222
223 *Path = EndPtr;
224
225 VERIFY_KEY_OBJECT(FoundObject);
226
227 *NextObject = FoundObject;
228
229 return(STATUS_SUCCESS);
230 }
231
232
233 NTSTATUS STDCALL
234 CmiObjectCreate(PVOID ObjectBody,
235 PVOID Parent,
236 PWSTR RemainingPath,
237 POBJECT_ATTRIBUTES ObjectAttributes)
238 {
239 PKEY_OBJECT pKey = ObjectBody;
240
241 pKey->ParentKey = Parent;
242 if (RemainingPath)
243 {
244 if(RemainingPath[0]== L'\\')
245 {
246 pKey->Name = (PCHAR)(&RemainingPath[1]);
247 pKey->NameSize = wcslen(RemainingPath) - 1;
248 }
249 else
250 {
251 pKey->Name = (PCHAR)RemainingPath;
252 pKey->NameSize = wcslen(RemainingPath);
253 }
254 }
255 else
256 {
257 pKey->NameSize = 0;
258 }
259
260 return STATUS_SUCCESS;
261 }
262
263
264 VOID STDCALL
265 CmiObjectDelete(PVOID DeletedObject)
266 {
267 PKEY_OBJECT KeyObject;
268
269 DPRINT("Delete object key\n");
270
271 KeyObject = (PKEY_OBJECT) DeletedObject;
272
273 if (!NT_SUCCESS(CmiRemoveKeyFromList(KeyObject)))
274 {
275 DPRINT1("Key not found in parent list ???\n");
276 }
277
278 if (KeyObject->Flags & KO_MARKED_FOR_DELETE)
279 {
280 DPRINT("delete really key\n");
281
282 CmiRemoveSubKey(KeyObject->RegistryHive,
283 KeyObject->ParentKey,
284 KeyObject);
285
286 if (IsPermanentHive(KeyObject->RegistryHive))
287 CmiSyncHives();
288 }
289 else
290 {
291 CmiReleaseBlock(KeyObject->RegistryHive, KeyObject->KeyCell);
292 }
293 }
294
295
296 NTSTATUS STDCALL
297 CmiObjectSecurity(PVOID ObjectBody,
298 SECURITY_OPERATION_CODE OperationCode,
299 SECURITY_INFORMATION SecurityInformation,
300 PSECURITY_DESCRIPTOR SecurityDescriptor,
301 PULONG BufferLength)
302 {
303 DPRINT1("CmiObjectSecurity() called\n");
304
305 return(STATUS_SUCCESS);
306 }
307
308
309 VOID
310 CmiAddKeyToList(PKEY_OBJECT ParentKey,
311 PKEY_OBJECT NewKey)
312 {
313 KIRQL OldIrql;
314
315 DPRINT("ParentKey %.08x\n", ParentKey);
316
317 KeAcquireSpinLock(&CmiKeyListLock, &OldIrql);
318
319 if (ParentKey->SizeOfSubKeys <= ParentKey->NumberOfSubKeys)
320 {
321 PKEY_OBJECT *tmpSubKeys = ExAllocatePool(NonPagedPool,
322 (ParentKey->NumberOfSubKeys + 1) * sizeof(DWORD));
323
324 if (ParentKey->NumberOfSubKeys > 0)
325 {
326 memcpy(tmpSubKeys,
327 ParentKey->SubKeys,
328 ParentKey->NumberOfSubKeys * sizeof(DWORD));
329 }
330
331 if (ParentKey->SubKeys)
332 ExFreePool(ParentKey->SubKeys);
333
334 ParentKey->SubKeys = tmpSubKeys;
335 ParentKey->SizeOfSubKeys = ParentKey->NumberOfSubKeys + 1;
336 }
337
338 /* FIXME: Please maintain the list in alphabetic order */
339 /* to allow a dichotomic search */
340 ParentKey->SubKeys[ParentKey->NumberOfSubKeys++] = NewKey;
341
342 DPRINT("Reference parent key: 0x%x\n", ParentKey);
343
344 ObReferenceObjectByPointer(ParentKey,
345 STANDARD_RIGHTS_REQUIRED,
346 NULL,
347 UserMode);
348 NewKey->ParentKey = ParentKey;
349 KeReleaseSpinLock(&CmiKeyListLock, OldIrql);
350 }
351
352
353 NTSTATUS
354 CmiRemoveKeyFromList(PKEY_OBJECT KeyToRemove)
355 {
356 PKEY_OBJECT ParentKey;
357 KIRQL OldIrql;
358 DWORD Index;
359
360 ParentKey = KeyToRemove->ParentKey;
361 KeAcquireSpinLock(&CmiKeyListLock, &OldIrql);
362 /* FIXME: If list maintained in alphabetic order, use dichotomic search */
363 for (Index = 0; Index < ParentKey->NumberOfSubKeys; Index++)
364 {
365 if (ParentKey->SubKeys[Index] == KeyToRemove)
366 {
367 if (Index < ParentKey->NumberOfSubKeys-1)
368 RtlMoveMemory(&ParentKey->SubKeys[Index],
369 &ParentKey->SubKeys[Index + 1],
370 (ParentKey->NumberOfSubKeys - Index - 1) * sizeof(PKEY_OBJECT));
371 ParentKey->NumberOfSubKeys--;
372 KeReleaseSpinLock(&CmiKeyListLock, OldIrql);
373
374 DPRINT("Dereference parent key: 0x%x\n", ParentKey);
375
376 ObDereferenceObject(ParentKey);
377 return STATUS_SUCCESS;
378 }
379 }
380 KeReleaseSpinLock(&CmiKeyListLock, OldIrql);
381
382 return STATUS_UNSUCCESSFUL;
383 }
384
385
386 PKEY_OBJECT
387 CmiScanKeyList(PKEY_OBJECT Parent,
388 PCHAR KeyName,
389 ULONG Attributes)
390 {
391 PKEY_OBJECT CurKey;
392 KIRQL OldIrql;
393 WORD NameSize;
394 DWORD Index;
395
396 #ifndef WIN32_REGDBG
397 DPRINT("Scanning key list for: %s (Parent: %s)\n",
398 KeyName, Parent->Name);
399 #else
400 {
401 char buffer[_BUFFER_LEN];
402 memset(buffer, 0, _BUFFER_LEN);
403 strncpy(buffer, Parent->Name, min(Parent->NameSize, _BUFFER_LEN - 1));
404 DPRINT("Scanning key list for: %s (Parent: %s)\n", KeyName, buffer);
405 }
406 #endif
407
408 NameSize = strlen(KeyName);
409 KeAcquireSpinLock(&CmiKeyListLock, &OldIrql);
410 /* FIXME: if list maintained in alphabetic order, use dichotomic search */
411 for (Index=0; Index < Parent->NumberOfSubKeys; Index++)
412 {
413 CurKey = Parent->SubKeys[Index];
414 if (Attributes & OBJ_CASE_INSENSITIVE)
415 {
416 if ((NameSize == CurKey->NameSize)
417 && (_strnicmp(KeyName, CurKey->Name, NameSize) == 0))
418 {
419 KeReleaseSpinLock(&CmiKeyListLock, OldIrql);
420 return CurKey;
421 }
422 }
423 else
424 {
425 if ((NameSize == CurKey->NameSize)
426 && (strncmp(KeyName,CurKey->Name,NameSize) == 0))
427 {
428 KeReleaseSpinLock(&CmiKeyListLock, OldIrql);
429 return CurKey;
430 }
431 }
432 }
433 KeReleaseSpinLock(&CmiKeyListLock, OldIrql);
434
435 return NULL;
436 }
437
438
439 static NTSTATUS
440 CmiGetLinkTarget(PREGISTRY_HIVE RegistryHive,
441 PKEY_CELL KeyCell,
442 PUNICODE_STRING TargetPath)
443 {
444 UNICODE_STRING LinkName = UNICODE_STRING_INITIALIZER(L"SymbolicLinkValue");
445 PVALUE_CELL ValueCell;
446 PDATA_CELL DataCell;
447 NTSTATUS Status;
448
449 DPRINT("CmiGetLinkTarget() called\n");
450
451 /* Get Value block of interest */
452 Status = CmiScanKeyForValue(RegistryHive,
453 KeyCell,
454 &LinkName,
455 &ValueCell,
456 NULL);
457 if (!NT_SUCCESS(Status))
458 {
459 DPRINT1("CmiScanKeyForValue() failed (Status %lx)\n", Status);
460 return(Status);
461 }
462
463 if (ValueCell->DataType != REG_LINK)
464 {
465 DPRINT1("Type != REG_LINK\n!");
466 return(STATUS_UNSUCCESSFUL);
467 }
468
469 if (TargetPath->Buffer == NULL && TargetPath->MaximumLength == 0)
470 {
471 TargetPath->Length = 0;
472 TargetPath->MaximumLength = ValueCell->DataSize + sizeof(WCHAR);
473 TargetPath->Buffer = ExAllocatePool(NonPagedPool,
474 TargetPath->MaximumLength);
475 }
476
477 TargetPath->Length = min(TargetPath->MaximumLength - sizeof(WCHAR),
478 (ULONG) ValueCell->DataSize);
479
480 if (ValueCell->DataSize > 0)
481 {
482 DataCell = CmiGetBlock(RegistryHive, ValueCell->DataOffset, NULL);
483 RtlCopyMemory(TargetPath->Buffer,
484 DataCell->Data,
485 TargetPath->Length);
486 TargetPath->Buffer[TargetPath->Length / sizeof(WCHAR)] = 0;
487 CmiReleaseBlock(RegistryHive, DataCell);
488 }
489 else
490 {
491 RtlCopyMemory(TargetPath->Buffer,
492 &ValueCell->DataOffset,
493 TargetPath->Length);
494 TargetPath->Buffer[TargetPath->Length / sizeof(WCHAR)] = 0;
495 }
496
497 DPRINT("TargetPath '%wZ'\n", TargetPath);
498
499 return(STATUS_SUCCESS);
500 }
501
502 /* EOF */