[NTOSKRNL] Implement ObpFreeDosDevicesProtection to free DACL
[reactos.git] / ntoskrnl / ob / obname.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ob/obname.c
5 * PURPOSE: Manages all functions related to the Object Manager name-
6 * space, such as finding objects or querying their names.
7 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
8 * Eric Kohl
9 * Thomas Weidenmueller (w3seek@reactos.org)
10 */
11
12 /* INCLUDES ******************************************************************/
13
14 #include <ntoskrnl.h>
15 #define NDEBUG
16 #include <debug.h>
17
18 BOOLEAN ObpCaseInsensitive = TRUE;
19 POBJECT_DIRECTORY ObpRootDirectoryObject;
20 POBJECT_DIRECTORY ObpTypeDirectoryObject;
21
22 /* DOS Device Prefix \??\ and \?? */
23 ALIGNEDNAME ObpDosDevicesShortNamePrefix = {{L'\\',L'?',L'?',L'\\'}};
24 ALIGNEDNAME ObpDosDevicesShortNameRoot = {{L'\\',L'?',L'?',L'\0'}};
25 UNICODE_STRING ObpDosDevicesShortName =
26 {
27 sizeof(ObpDosDevicesShortNamePrefix),
28 sizeof(ObpDosDevicesShortNamePrefix),
29 (PWSTR)&ObpDosDevicesShortNamePrefix
30 };
31
32 WCHAR ObpUnsecureGlobalNamesBuffer[128] = {0};
33 ULONG ObpUnsecureGlobalNamesLength = sizeof(ObpUnsecureGlobalNamesBuffer);
34
35 /* PRIVATE FUNCTIONS *********************************************************/
36
37 INIT_FUNCTION
38 NTSTATUS
39 NTAPI
40 ObpGetDosDevicesProtection(OUT PSECURITY_DESCRIPTOR SecurityDescriptor)
41 {
42 PACL Dacl;
43 ULONG AclSize;
44
45 /* Initialize the SD */
46 RtlCreateSecurityDescriptor(SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION);
47
48 if (ObpProtectionMode & 1)
49 {
50 AclSize = sizeof(ACL) +
51 sizeof(ACE) + RtlLengthSid(SeWorldSid) +
52 sizeof(ACE) + RtlLengthSid(SeLocalSystemSid) +
53 sizeof(ACE) + RtlLengthSid(SeWorldSid) +
54 sizeof(ACE) + RtlLengthSid(SeAliasAdminsSid) +
55 sizeof(ACE) + RtlLengthSid(SeLocalSystemSid) +
56 sizeof(ACE) + RtlLengthSid(SeCreatorOwnerSid);
57
58 /* Allocate the ACL */
59 Dacl = ExAllocatePoolWithTag(PagedPool, AclSize, 'lcaD');
60 if (Dacl == NULL)
61 {
62 return STATUS_INSUFFICIENT_RESOURCES;
63 }
64
65 /* Initialize the DACL */
66 RtlCreateAcl(Dacl, AclSize, ACL_REVISION);
67
68 /* Add the ACEs */
69 RtlAddAccessAllowedAce(Dacl,
70 ACL_REVISION,
71 GENERIC_READ | GENERIC_EXECUTE,
72 SeWorldSid);
73
74 RtlAddAccessAllowedAce(Dacl,
75 ACL_REVISION,
76 GENERIC_ALL,
77 SeLocalSystemSid);
78
79 RtlAddAccessAllowedAceEx(Dacl,
80 ACL_REVISION,
81 INHERIT_ONLY_ACE | CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE,
82 GENERIC_EXECUTE,
83 SeWorldSid);
84
85 RtlAddAccessAllowedAceEx(Dacl,
86 ACL_REVISION,
87 INHERIT_ONLY_ACE | CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE,
88 GENERIC_ALL,
89 SeAliasAdminsSid);
90
91 RtlAddAccessAllowedAceEx(Dacl,
92 ACL_REVISION,
93 INHERIT_ONLY_ACE | CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE,
94 GENERIC_ALL,
95 SeLocalSystemSid);
96
97 RtlAddAccessAllowedAceEx(Dacl,
98 ACL_REVISION,
99 INHERIT_ONLY_ACE | CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE,
100 GENERIC_ALL,
101 SeCreatorOwnerSid);
102 }
103 else
104 {
105 AclSize = sizeof(ACL) +
106 sizeof(ACE) + RtlLengthSid(SeLocalSystemSid) +
107 sizeof(ACE) + RtlLengthSid(SeWorldSid) +
108 sizeof(ACE) + RtlLengthSid(SeLocalSystemSid);
109
110 /* Allocate the ACL */
111 Dacl = ExAllocatePoolWithTag(PagedPool, AclSize, 'lcaD');
112 if (Dacl == NULL)
113 {
114 return STATUS_INSUFFICIENT_RESOURCES;
115 }
116
117 /* Initialize the DACL */
118 RtlCreateAcl(Dacl, AclSize, ACL_REVISION);
119
120 /* Add the ACEs */
121 RtlAddAccessAllowedAce(Dacl,
122 ACL_REVISION,
123 GENERIC_READ | GENERIC_EXECUTE | GENERIC_WRITE,
124 SeWorldSid);
125
126 RtlAddAccessAllowedAce(Dacl,
127 ACL_REVISION,
128 GENERIC_ALL,
129 SeLocalSystemSid);
130
131 RtlAddAccessAllowedAceEx(Dacl,
132 ACL_REVISION,
133 INHERIT_ONLY_ACE | CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE,
134 GENERIC_ALL,
135 SeWorldSid);
136 }
137
138 /* Attach the DACL to the SD */
139 RtlSetDaclSecurityDescriptor(SecurityDescriptor, TRUE, Dacl, FALSE);
140
141 return STATUS_SUCCESS;
142 }
143
144 INIT_FUNCTION
145 VOID
146 NTAPI
147 ObpFreeDosDevicesProtection(OUT PSECURITY_DESCRIPTOR SecurityDescriptor)
148 {
149 PACL Dacl;
150 NTSTATUS Status;
151 BOOLEAN DaclPresent, DaclDefaulted;
152
153 Status = RtlGetDaclSecurityDescriptor(SecurityDescriptor, &DaclPresent, &Dacl, &DaclDefaulted);
154 ASSERT(NT_SUCCESS(Status));
155 ASSERT(DaclPresent);
156 ASSERT(Dacl != NULL);
157 ExFreePoolWithTag(Dacl, 'lcaD');
158 }
159
160 INIT_FUNCTION
161 NTSTATUS
162 NTAPI
163 ObpCreateDosDevicesDirectory(VOID)
164 {
165 OBJECT_ATTRIBUTES ObjectAttributes;
166 UNICODE_STRING RootName, TargetName, LinkName;
167 HANDLE Handle, SymHandle;
168 SECURITY_DESCRIPTOR DosDevicesSD;
169 NTSTATUS Status;
170
171 /* Create a custom security descriptor for the global DosDevices directory */
172 Status = ObpGetDosDevicesProtection(&DosDevicesSD);
173 if (!NT_SUCCESS(Status))
174 return Status;
175
176 /* Create the global DosDevices directory \?? */
177 RtlInitUnicodeString(&RootName, L"\\GLOBAL??");
178 InitializeObjectAttributes(&ObjectAttributes,
179 &RootName,
180 OBJ_PERMANENT,
181 NULL,
182 &DosDevicesSD);
183 Status = NtCreateDirectoryObject(&Handle,
184 DIRECTORY_ALL_ACCESS,
185 &ObjectAttributes);
186 if (!NT_SUCCESS(Status))
187 goto done;
188
189 /* Create the system device map */
190 Status = ObSetDeviceMap(NULL, Handle);
191 if (!NT_SUCCESS(Status))
192 goto done;
193
194 /*********************************************\
195 |*** HACK until we support device mappings ***|
196 |*** Add a symlink \??\ <--> \GLOBAL??\ ***|
197 \*********************************************/
198 RtlInitUnicodeString(&LinkName, L"\\??");
199 InitializeObjectAttributes(&ObjectAttributes,
200 &LinkName,
201 OBJ_PERMANENT,
202 NULL,
203 &DosDevicesSD);
204 Status = NtCreateSymbolicLinkObject(&SymHandle,
205 SYMBOLIC_LINK_ALL_ACCESS,
206 &ObjectAttributes,
207 &RootName);
208 if (NT_SUCCESS(Status)) NtClose(SymHandle);
209 /*********************************************\
210 \*********************************************/
211
212 // FIXME: Create a device mapping for the global \?? directory
213
214 /*
215 * Initialize the \??\GLOBALROOT symbolic link
216 * pointing to the root directory \ .
217 */
218 RtlInitUnicodeString(&LinkName, L"GLOBALROOT");
219 RtlInitUnicodeString(&TargetName, L"");
220 InitializeObjectAttributes(&ObjectAttributes,
221 &LinkName,
222 OBJ_PERMANENT,
223 Handle,
224 &DosDevicesSD);
225 Status = NtCreateSymbolicLinkObject(&SymHandle,
226 SYMBOLIC_LINK_ALL_ACCESS,
227 &ObjectAttributes,
228 &TargetName);
229 if (NT_SUCCESS(Status)) NtClose(SymHandle);
230
231 /*
232 * Initialize the \??\Global symbolic link pointing to the global
233 * DosDevices directory \?? . It is used to access the global \??
234 * by user-mode components which, by default, use a per-session
235 * DosDevices directory.
236 */
237 RtlInitUnicodeString(&LinkName, L"Global");
238 InitializeObjectAttributes(&ObjectAttributes,
239 &LinkName,
240 OBJ_PERMANENT,
241 Handle,
242 &DosDevicesSD);
243 Status = NtCreateSymbolicLinkObject(&SymHandle,
244 SYMBOLIC_LINK_ALL_ACCESS,
245 &ObjectAttributes,
246 &RootName);
247 if (NT_SUCCESS(Status)) NtClose(SymHandle);
248
249 /* Close the directory handle */
250 NtClose(Handle);
251 if (!NT_SUCCESS(Status))
252 goto done;
253
254 /*
255 * Initialize the \DosDevices symbolic link pointing to the global
256 * DosDevices directory \?? , for backward compatibility with
257 * Windows NT-2000 systems.
258 */
259 RtlCreateUnicodeString(&LinkName, L"\\DosDevices");
260 RtlInitUnicodeString(&RootName, (PCWSTR)&ObpDosDevicesShortNameRoot);
261 InitializeObjectAttributes(&ObjectAttributes,
262 &LinkName,
263 OBJ_PERMANENT,
264 NULL,
265 &DosDevicesSD);
266 Status = NtCreateSymbolicLinkObject(&SymHandle,
267 SYMBOLIC_LINK_ALL_ACCESS,
268 &ObjectAttributes,
269 &RootName);
270 if (NT_SUCCESS(Status)) NtClose(SymHandle);
271
272 done:
273 ObpFreeDosDevicesProtection(&DosDevicesSD);
274
275 /* Return status */
276 return Status;
277 }
278
279 /*++
280 * @name ObpDeleteNameCheck
281 *
282 * The ObpDeleteNameCheck routine checks if a named object should be
283 * removed from the object directory namespace.
284 *
285 * @param Object
286 * Pointer to the object to check for possible removal.
287 *
288 * @return None.
289 *
290 * @remarks An object is removed if the following 4 criteria are met:
291 * 1) The object has 0 handles open
292 * 2) The object is in the directory namespace and has a name
293 * 3) The object is not permanent
294 *
295 *--*/
296 VOID
297 NTAPI
298 ObpDeleteNameCheck(IN PVOID Object)
299 {
300 POBJECT_HEADER ObjectHeader;
301 OBP_LOOKUP_CONTEXT Context;
302 POBJECT_HEADER_NAME_INFO ObjectNameInfo;
303 POBJECT_TYPE ObjectType;
304 PVOID Directory = NULL;
305
306 /* Get object structures */
307 ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object);
308 ObjectNameInfo = ObpReferenceNameInfo(ObjectHeader);
309 ObjectType = ObjectHeader->Type;
310
311 /*
312 * Check if the handle count is 0, if the object is named,
313 * and if the object isn't a permanent object.
314 */
315 if (!(ObjectHeader->HandleCount) &&
316 (ObjectNameInfo) &&
317 (ObjectNameInfo->Name.Length) &&
318 (ObjectNameInfo->Directory) &&
319 !(ObjectHeader->Flags & OB_FLAG_PERMANENT))
320 {
321 /* Setup a lookup context */
322 ObpInitializeLookupContext(&Context);
323
324 /* Lock the directory */
325 ObpAcquireDirectoryLockExclusive(ObjectNameInfo->Directory, &Context);
326
327 /* Do the lookup */
328 Object = ObpLookupEntryDirectory(ObjectNameInfo->Directory,
329 &ObjectNameInfo->Name,
330 0,
331 FALSE,
332 &Context);
333 if (Object)
334 {
335 /* Lock the object */
336 ObpAcquireObjectLock(ObjectHeader);
337
338 /* Make sure we can still delete the object */
339 if (!(ObjectHeader->HandleCount) &&
340 !(ObjectHeader->Flags & OB_FLAG_PERMANENT))
341 {
342 /* First delete it from the directory */
343 ObpDeleteEntryDirectory(&Context);
344
345 /* Check if this is a symbolic link */
346 if (ObjectType == ObpSymbolicLinkObjectType)
347 {
348 /* Remove internal name */
349 ObpDeleteSymbolicLinkName(Object);
350 }
351
352 /* Check if the kernel exclusive is set */
353 ObjectNameInfo = OBJECT_HEADER_TO_NAME_INFO(ObjectHeader);
354 if ((ObjectNameInfo) &&
355 (ObjectNameInfo->QueryReferences & OB_FLAG_KERNEL_EXCLUSIVE))
356 {
357 /* Remove protection flag */
358 InterlockedExchangeAdd((PLONG)&ObjectNameInfo->QueryReferences,
359 -OB_FLAG_KERNEL_EXCLUSIVE);
360 }
361
362 /* Get the directory */
363 Directory = ObjectNameInfo->Directory;
364 }
365
366 /* Release the lock */
367 ObpReleaseObjectLock(ObjectHeader);
368 }
369
370 /* Cleanup after lookup */
371 ObpReleaseLookupContext(&Context);
372
373 /* Remove another query reference since we added one on top */
374 ObpDereferenceNameInfo(ObjectNameInfo);
375
376 /* Check if we were inserted in a directory */
377 if (Directory)
378 {
379 /* We were, so first remove the extra reference we had added */
380 ObpDereferenceNameInfo(ObjectNameInfo);
381
382 /* Now dereference the object as well */
383 ObDereferenceObject(Object);
384 }
385 }
386 else
387 {
388 /* Remove the reference we added */
389 ObpDereferenceNameInfo(ObjectNameInfo);
390 }
391 }
392
393 BOOLEAN
394 NTAPI
395 ObpIsUnsecureName(IN PUNICODE_STRING ObjectName,
396 IN BOOLEAN CaseInSensitive)
397 {
398 BOOLEAN Unsecure;
399 PWSTR UnsecureBuffer;
400 UNICODE_STRING UnsecureName;
401
402 /* No unsecure names known, quit */
403 if (ObpUnsecureGlobalNamesBuffer[0] == UNICODE_NULL)
404 {
405 return FALSE;
406 }
407
408 /* By default, we have a secure name */
409 Unsecure = FALSE;
410 /* We will browse the whole string */
411 UnsecureBuffer = &ObpUnsecureGlobalNamesBuffer[0];
412 while (TRUE)
413 {
414 /* Initialize the unicode string */
415 RtlInitUnicodeString(&UnsecureName, UnsecureBuffer);
416 /* We're at the end of the multisz string! */
417 if (UnsecureName.Length == 0)
418 {
419 break;
420 }
421
422 /*
423 * Does the unsecure name prefix the object name?
424 * If so, that's an unsecure name, and return so
425 */
426 if (RtlPrefixUnicodeString(&UnsecureName, ObjectName, CaseInSensitive))
427 {
428 Unsecure = TRUE;
429 break;
430 }
431
432 /*
433 * Move to the next string. As a reminder, ObpUnsecureGlobalNamesBuffer is
434 * a multisz, so we move the string next to the current UNICODE_NULL char
435 */
436 UnsecureBuffer = (PWSTR)((ULONG_PTR)UnsecureBuffer + UnsecureName.Length + sizeof(UNICODE_NULL));
437 }
438
439 /* Return our findings */
440 return Unsecure;
441 }
442
443 NTSTATUS
444 NTAPI
445 ObpLookupObjectName(IN HANDLE RootHandle OPTIONAL,
446 IN OUT PUNICODE_STRING ObjectName,
447 IN ULONG Attributes,
448 IN POBJECT_TYPE ObjectType,
449 IN KPROCESSOR_MODE AccessMode,
450 IN OUT PVOID ParseContext,
451 IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL,
452 IN PVOID InsertObject OPTIONAL,
453 IN OUT PACCESS_STATE AccessState,
454 OUT POBP_LOOKUP_CONTEXT LookupContext,
455 OUT PVOID *FoundObject)
456 {
457 PVOID Object;
458 POBJECT_HEADER ObjectHeader;
459 UNICODE_STRING ComponentName, RemainingName;
460 BOOLEAN Reparse = FALSE, SymLink = FALSE;
461 POBJECT_DIRECTORY Directory = NULL, ParentDirectory = NULL, RootDirectory;
462 POBJECT_DIRECTORY ReferencedDirectory = NULL, ReferencedParentDirectory = NULL;
463 KIRQL CalloutIrql;
464 OB_PARSE_METHOD ParseRoutine;
465 NTSTATUS Status;
466 KPROCESSOR_MODE AccessCheckMode;
467 PWCHAR NewName;
468 POBJECT_HEADER_NAME_INFO ObjectNameInfo;
469 ULONG MaxReparse = 30;
470 PAGED_CODE();
471 OBTRACE(OB_NAMESPACE_DEBUG,
472 "%s - Finding Object: %wZ. Expecting: %p\n",
473 __FUNCTION__,
474 ObjectName,
475 InsertObject);
476
477 /* Initialize starting state */
478 ObpInitializeLookupContext(LookupContext);
479 *FoundObject = NULL;
480 Status = STATUS_SUCCESS;
481 Object = NULL;
482
483 /* Check if case-insensitivity is checked */
484 if (ObpCaseInsensitive)
485 {
486 /* Check if the object type requests this */
487 if (!(ObjectType) || (ObjectType->TypeInfo.CaseInsensitive))
488 {
489 /* Add the flag to disable case sensitivity */
490 Attributes |= OBJ_CASE_INSENSITIVE;
491 }
492 }
493
494 /* Check if this is a access checks are being forced */
495 AccessCheckMode = (Attributes & OBJ_FORCE_ACCESS_CHECK) ?
496 UserMode : AccessMode;
497
498 /* Check if we got a Root Directory */
499 if (RootHandle)
500 {
501 /* We did. Reference it */
502 Status = ObReferenceObjectByHandle(RootHandle,
503 0,
504 NULL,
505 AccessMode,
506 (PVOID*)&RootDirectory,
507 NULL);
508 if (!NT_SUCCESS(Status)) return Status;
509
510 /* Get the header */
511 ObjectHeader = OBJECT_TO_OBJECT_HEADER(RootDirectory);
512
513 /* The name cannot start with a separator, unless this is a file */
514 if ((ObjectName->Buffer) &&
515 (ObjectName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR) &&
516 (ObjectHeader->Type != IoFileObjectType))
517 {
518 /* The syntax is bad, so fail this request */
519 ObDereferenceObject(RootDirectory);
520 return STATUS_OBJECT_PATH_SYNTAX_BAD;
521 }
522
523 /* Don't parse a Directory */
524 if (ObjectHeader->Type != ObpDirectoryObjectType)
525 {
526 /* Make sure the Object Type has a parse routine */
527 ParseRoutine = ObjectHeader->Type->TypeInfo.ParseProcedure;
528 if (!ParseRoutine)
529 {
530 /* We can't parse a name if we don't have a parse routine */
531 ObDereferenceObject(RootDirectory);
532 return STATUS_INVALID_HANDLE;
533 }
534
535 /* Set default parse count */
536 MaxReparse = 30;
537
538 /* Now parse */
539 while (TRUE)
540 {
541 /* Start with the full name */
542 RemainingName = *ObjectName;
543
544 /* Call the Parse Procedure */
545 ObpCalloutStart(&CalloutIrql);
546 Status = ParseRoutine(RootDirectory,
547 ObjectType,
548 AccessState,
549 AccessCheckMode,
550 Attributes,
551 ObjectName,
552 &RemainingName,
553 ParseContext,
554 SecurityQos,
555 &Object);
556 ObpCalloutEnd(CalloutIrql, "Parse", ObjectHeader->Type, Object);
557
558 /* Check for success or failure, so not reparse */
559 if ((Status != STATUS_REPARSE) &&
560 (Status != STATUS_REPARSE_OBJECT))
561 {
562 /* Check for failure */
563 if (!NT_SUCCESS(Status))
564 {
565 /* Parse routine might not have cleared this, do it */
566 Object = NULL;
567 }
568 else if (!Object)
569 {
570 /* Modify status to reflect failure inside Ob */
571 Status = STATUS_OBJECT_NAME_NOT_FOUND;
572 }
573
574 /* We're done, return the status and object */
575 *FoundObject = Object;
576 ObDereferenceObject(RootDirectory);
577 return Status;
578 }
579 else if ((!ObjectName->Length) ||
580 (!ObjectName->Buffer) ||
581 (ObjectName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR))
582 {
583 /* Reparsed to the root directory, so start over */
584 ObDereferenceObject(RootDirectory);
585 RootDirectory = ObpRootDirectoryObject;
586
587 /* Don't use this anymore, since we're starting at root */
588 RootHandle = NULL;
589 goto ParseFromRoot;
590 }
591 else if (--MaxReparse)
592 {
593 /* Try reparsing again */
594 continue;
595 }
596 else
597 {
598 /* Reparsed too many times */
599 ObDereferenceObject(RootDirectory);
600
601 /* Return the object and normalized status */
602 *FoundObject = Object;
603 if (!Object) Status = STATUS_OBJECT_NAME_NOT_FOUND;
604 return Status;
605 }
606 }
607 }
608 else if (!(ObjectName->Length) || !(ObjectName->Buffer))
609 {
610 /* Just return the Root Directory if we didn't get a name */
611 Status = ObReferenceObjectByPointer(RootDirectory,
612 0,
613 ObjectType,
614 AccessMode);
615 if (NT_SUCCESS(Status)) Object = RootDirectory;
616
617 /* Remove the first reference we added and return the object */
618 ObDereferenceObject(RootDirectory);
619 *FoundObject = Object;
620 return Status;
621 }
622 }
623 else
624 {
625 /* We did not get a Root Directory, so use the root */
626 RootDirectory = ObpRootDirectoryObject;
627
628 /* It must start with a path separator */
629 if (!(ObjectName->Length) ||
630 !(ObjectName->Buffer) ||
631 (ObjectName->Buffer[0] != OBJ_NAME_PATH_SEPARATOR))
632 {
633 /* This name is invalid, so fail */
634 return STATUS_OBJECT_PATH_SYNTAX_BAD;
635 }
636
637 /* Check if the name is only the path separator */
638 if (ObjectName->Length == sizeof(OBJ_NAME_PATH_SEPARATOR))
639 {
640 /* So the caller only wants the root directory; do we have one? */
641 if (!RootDirectory)
642 {
643 /* This must be the first time we're creating it... right? */
644 if (InsertObject)
645 {
646 /* Yes, so return it to ObInsert so that it can create it */
647 Status = ObReferenceObjectByPointer(InsertObject,
648 0,
649 ObjectType,
650 AccessMode);
651 if (NT_SUCCESS(Status)) *FoundObject = InsertObject;
652 return Status;
653 }
654 else
655 {
656 /* This should never really happen */
657 ASSERT(FALSE);
658 return STATUS_INVALID_PARAMETER;
659 }
660 }
661 else
662 {
663 /* We do have the root directory, so just return it */
664 Status = ObReferenceObjectByPointer(RootDirectory,
665 0,
666 ObjectType,
667 AccessMode);
668 if (NT_SUCCESS(Status)) *FoundObject = RootDirectory;
669 return Status;
670 }
671 }
672 else
673 {
674 ParseFromRoot:
675 /* FIXME: Check if we have a device map */
676
677 /* Check if this is a possible DOS name */
678 if (!((ULONG_PTR)(ObjectName->Buffer) & 7))
679 {
680 /*
681 * This could be one. Does it match the prefix?
682 * Note that as an optimization, the match is done as 64-bit
683 * compare since the prefix is "\??\" which is exactly 8 bytes.
684 *
685 * In the second branch, we test for "\??" which is also valid.
686 * This time, we use a 32-bit compare followed by a Unicode
687 * character compare (16-bit), since the sum is 6 bytes.
688 */
689 if ((ObjectName->Length >= ObpDosDevicesShortName.Length) &&
690 (*(PULONGLONG)(ObjectName->Buffer) ==
691 ObpDosDevicesShortNamePrefix.Alignment.QuadPart))
692 {
693 /* FIXME! */
694 }
695 else if ((ObjectName->Length == ObpDosDevicesShortName.Length -
696 sizeof(WCHAR)) &&
697 (*(PULONG)(ObjectName->Buffer) ==
698 ObpDosDevicesShortNameRoot.Alignment.LowPart) &&
699 (*((PWCHAR)(ObjectName->Buffer) + 2) ==
700 (WCHAR)(ObpDosDevicesShortNameRoot.Alignment.HighPart)))
701 {
702 /* FIXME! */
703 }
704 }
705 }
706 }
707
708 /* Check if we were reparsing a symbolic link */
709 if (!SymLink)
710 {
711 /* Allow reparse */
712 Reparse = TRUE;
713 MaxReparse = 30;
714 }
715
716 /* Reparse */
717 while (Reparse && MaxReparse)
718 {
719 /* Get the name */
720 RemainingName = *ObjectName;
721
722 /* Disable reparsing again */
723 Reparse = FALSE;
724
725 /* Start parse loop */
726 while (TRUE)
727 {
728 /* Clear object */
729 Object = NULL;
730
731 /* Check if the name starts with a path separator */
732 if ((RemainingName.Length) &&
733 (RemainingName.Buffer[0] == OBJ_NAME_PATH_SEPARATOR))
734 {
735 /* Skip the path separator */
736 RemainingName.Buffer++;
737 RemainingName.Length -= sizeof(OBJ_NAME_PATH_SEPARATOR);
738 }
739
740 /* Find the next Part Name */
741 ComponentName = RemainingName;
742 while (RemainingName.Length)
743 {
744 /* Break if we found the \ ending */
745 if (RemainingName.Buffer[0] == OBJ_NAME_PATH_SEPARATOR) break;
746
747 /* Move on */
748 RemainingName.Buffer++;
749 RemainingName.Length -= sizeof(OBJ_NAME_PATH_SEPARATOR);
750 }
751
752 /* Get its size and make sure it's valid */
753 ComponentName.Length -= RemainingName.Length;
754 if (!ComponentName.Length)
755 {
756 /* Invalid size, fail */
757 Status = STATUS_OBJECT_NAME_INVALID;
758 break;
759 }
760
761 /* Check if we're in the root */
762 if (!Directory) Directory = RootDirectory;
763
764 /* Check if this is a user-mode call that needs to traverse */
765 if ((AccessCheckMode != KernelMode) &&
766 !(AccessState->Flags & TOKEN_HAS_TRAVERSE_PRIVILEGE))
767 {
768 /* We shouldn't have referenced a directory yet */
769 ASSERT(ReferencedDirectory == NULL);
770
771 /* Reference the directory */
772 ObReferenceObject(Directory);
773 ReferencedDirectory = Directory;
774
775 /* Check if we have a parent directory */
776 if (ParentDirectory)
777 {
778 /* Check for traverse access */
779 if (!ObpCheckTraverseAccess(ParentDirectory,
780 DIRECTORY_TRAVERSE,
781 AccessState,
782 FALSE,
783 AccessCheckMode,
784 &Status))
785 {
786 /* We don't have it, fail */
787 break;
788 }
789 }
790 }
791
792 /* Check if we don't have a remaining name yet */
793 if (!RemainingName.Length)
794 {
795 /* Check if we don't have a referenced directory yet */
796 if (!ReferencedDirectory)
797 {
798 /* Reference it */
799 ObReferenceObject(Directory);
800 ReferencedDirectory = Directory;
801 }
802
803 /* Check if we are inserting an object */
804 if (InsertObject)
805 {
806 /* Lock the directory */
807 ObpAcquireDirectoryLockExclusive(Directory, LookupContext);
808 }
809 }
810
811 /* Do the lookup */
812 Object = ObpLookupEntryDirectory(Directory,
813 &ComponentName,
814 Attributes,
815 InsertObject ? FALSE : TRUE,
816 LookupContext);
817 if (!Object)
818 {
819 /* We didn't find it... do we still have a path? */
820 if (RemainingName.Length)
821 {
822 /* Then tell the caller the path wasn't found */
823 Status = STATUS_OBJECT_PATH_NOT_FOUND;
824 break;
825 }
826 else if (!InsertObject)
827 {
828 /* Otherwise, we have a path, but the name isn't valid */
829 Status = STATUS_OBJECT_NAME_NOT_FOUND;
830 break;
831 }
832
833 /* Check create access for the object */
834 if (!ObCheckCreateObjectAccess(Directory,
835 ObjectType == ObpDirectoryObjectType ?
836 DIRECTORY_CREATE_SUBDIRECTORY :
837 DIRECTORY_CREATE_OBJECT,
838 AccessState,
839 &ComponentName,
840 FALSE,
841 AccessCheckMode,
842 &Status))
843 {
844 /* We don't have create access, fail */
845 break;
846 }
847
848 /* Get the object header */
849 ObjectHeader = OBJECT_TO_OBJECT_HEADER(InsertObject);
850
851 /*
852 * Deny object creation if:
853 * That's a section object or a symbolic link
854 * Which isn't in the same section that root directory
855 * That doesn't have the SeCreateGlobalPrivilege
856 * And that is not a known unsecure name
857 */
858 if (RootDirectory->SessionId != -1)
859 {
860 if (ObjectHeader->Type == MmSectionObjectType ||
861 ObjectHeader->Type == ObpSymbolicLinkObjectType)
862 {
863 if (RootDirectory->SessionId != PsGetCurrentProcessSessionId() &&
864 !SeSinglePrivilegeCheck(SeCreateGlobalPrivilege, AccessCheckMode) &&
865 !ObpIsUnsecureName(&ComponentName, BooleanFlagOn(Attributes, OBJ_CASE_INSENSITIVE)))
866 {
867 Status = STATUS_ACCESS_DENIED;
868 break;
869 }
870 }
871 }
872
873 /* Create Object Name */
874 NewName = ExAllocatePoolWithTag(PagedPool,
875 ComponentName.Length,
876 OB_NAME_TAG);
877 if (!(NewName) ||
878 !(ObpInsertEntryDirectory(Directory,
879 LookupContext,
880 ObjectHeader)))
881 {
882 /* Either couldn't allocate the name, or insert failed */
883 if (NewName) ExFreePoolWithTag(NewName, OB_NAME_TAG);
884
885 /* Fail due to memory reasons */
886 Status = STATUS_INSUFFICIENT_RESOURCES;
887 break;
888 }
889
890 /* Reference newly to be inserted object */
891 ObReferenceObject(InsertObject);
892
893 /* Get the name information */
894 ObjectNameInfo = OBJECT_HEADER_TO_NAME_INFO(ObjectHeader);
895
896 /* Reference the directory */
897 ObReferenceObject(Directory);
898
899 /* Copy the Name */
900 RtlCopyMemory(NewName,
901 ComponentName.Buffer,
902 ComponentName.Length);
903
904 /* Check if we had an old name */
905 if (ObjectNameInfo->Name.Buffer)
906 {
907 /* Free it */
908 ExFreePoolWithTag(ObjectNameInfo->Name.Buffer, OB_NAME_TAG);
909 }
910
911 /* Write new one */
912 ObjectNameInfo->Name.Buffer = NewName;
913 ObjectNameInfo->Name.Length = ComponentName.Length;
914 ObjectNameInfo->Name.MaximumLength = ComponentName.Length;
915
916 /* Return Status and the Expected Object */
917 Status = STATUS_SUCCESS;
918 Object = InsertObject;
919
920 /* Get out of here */
921 break;
922 }
923
924 ReparseObject:
925 /* We found it, so now get its header */
926 ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object);
927
928 /*
929 * Check for a parse Procedure, but don't bother to parse for an insert
930 * unless it's a Symbolic Link, in which case we MUST parse
931 */
932 ParseRoutine = ObjectHeader->Type->TypeInfo.ParseProcedure;
933 if ((ParseRoutine) &&
934 (!(InsertObject) || (ParseRoutine == ObpParseSymbolicLink)))
935 {
936 /* Use the Root Directory next time */
937 Directory = NULL;
938
939 /* Increment the pointer count */
940 InterlockedExchangeAddSizeT(&ObjectHeader->PointerCount, 1);
941
942 /* Cleanup from the first lookup */
943 ObpReleaseLookupContext(LookupContext);
944
945 /* Check if we have a referenced directory */
946 if (ReferencedDirectory)
947 {
948 /* We do, dereference it */
949 ObDereferenceObject(ReferencedDirectory);
950 ReferencedDirectory = NULL;
951 }
952
953 /* Check if we have a referenced parent directory */
954 if (ReferencedParentDirectory)
955 {
956 /* We do, dereference it */
957 ObDereferenceObject(ReferencedParentDirectory);
958 ReferencedParentDirectory = NULL;
959 }
960
961 /* Call the Parse Procedure */
962 ObpCalloutStart(&CalloutIrql);
963 Status = ParseRoutine(Object,
964 ObjectType,
965 AccessState,
966 AccessCheckMode,
967 Attributes,
968 ObjectName,
969 &RemainingName,
970 ParseContext,
971 SecurityQos,
972 &Object);
973 ObpCalloutEnd(CalloutIrql, "Parse", ObjectHeader->Type, Object);
974
975 /* Remove our extra reference */
976 ObDereferenceObject(&ObjectHeader->Body);
977
978 /* Check if we have to reparse */
979 if ((Status == STATUS_REPARSE) ||
980 (Status == STATUS_REPARSE_OBJECT))
981 {
982 /* Reparse again */
983 Reparse = TRUE;
984 --MaxReparse;
985 if (MaxReparse == 0)
986 {
987 Object = NULL;
988 break;
989 }
990
991 /* Start over from root if we got sent back there */
992 if ((Status == STATUS_REPARSE_OBJECT) ||
993 (ObjectName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR))
994 {
995 /* Check if we got a root directory */
996 if (RootHandle)
997 {
998 /* Stop using it, because we have a new directory now */
999 ObDereferenceObject(RootDirectory);
1000 RootHandle = NULL;
1001 }
1002
1003 /* Start at Root */
1004 ParentDirectory = NULL;
1005 RootDirectory = ObpRootDirectoryObject;
1006
1007 /* Check for reparse status */
1008 if (Status == STATUS_REPARSE_OBJECT)
1009 {
1010 /* Don't reparse again */
1011 Reparse = FALSE;
1012
1013 /* Did we actually get an object to which to reparse? */
1014 if (!Object)
1015 {
1016 /* We didn't, so set a failure status */
1017 Status = STATUS_OBJECT_NAME_NOT_FOUND;
1018 }
1019 else
1020 {
1021 /* We did, so we're free to parse the new object */
1022 goto ReparseObject;
1023 }
1024 }
1025 else
1026 {
1027 /* This is a symbolic link */
1028 SymLink = TRUE;
1029 goto ParseFromRoot;
1030 }
1031 }
1032 else if (RootDirectory == ObpRootDirectoryObject)
1033 {
1034 /* We got STATUS_REPARSE but are at the Root Directory */
1035 Object = NULL;
1036 Status = STATUS_OBJECT_NAME_NOT_FOUND;
1037 Reparse = FALSE;
1038 }
1039 }
1040 else if (!NT_SUCCESS(Status))
1041 {
1042 /* Total failure */
1043 Object = NULL;
1044 }
1045 else if (!Object)
1046 {
1047 /* We didn't reparse but we didn't find the Object Either */
1048 Status = STATUS_OBJECT_NAME_NOT_FOUND;
1049 }
1050
1051 /* Break out of the loop */
1052 break;
1053 }
1054 else
1055 {
1056 /* No parse routine...do we still have a remaining name? */
1057 if (!RemainingName.Length)
1058 {
1059 /* Are we creating an object? */
1060 if (!InsertObject)
1061 {
1062 /* Check if this is a user-mode call that needs to traverse */
1063 if ((AccessCheckMode != KernelMode) &&
1064 !(AccessState->Flags & TOKEN_HAS_TRAVERSE_PRIVILEGE))
1065 {
1066 /* Check if we can get it */
1067 if (!ObpCheckTraverseAccess(Directory,
1068 DIRECTORY_TRAVERSE,
1069 AccessState,
1070 FALSE,
1071 AccessCheckMode,
1072 &Status))
1073 {
1074 /* We don't have access, fail */
1075 Object = NULL;
1076 break;
1077 }
1078 }
1079
1080 /* Reference the Object */
1081 Status = ObReferenceObjectByPointer(Object,
1082 0,
1083 ObjectType,
1084 AccessMode);
1085 if (!NT_SUCCESS(Status)) Object = NULL;
1086 }
1087
1088 /* And get out of the reparse loop */
1089 break;
1090 }
1091 else
1092 {
1093 /* We still have a name; check if this is a directory object */
1094 if (ObjectHeader->Type == ObpDirectoryObjectType)
1095 {
1096 /* Check if we have a referenced parent directory */
1097 if (ReferencedParentDirectory)
1098 {
1099 /* Dereference it */
1100 ObDereferenceObject(ReferencedParentDirectory);
1101 }
1102
1103 /* Restart the lookup from this directory */
1104 ReferencedParentDirectory = ReferencedDirectory;
1105 ParentDirectory = Directory;
1106 Directory = Object;
1107 ReferencedDirectory = NULL;
1108 }
1109 else
1110 {
1111 /* We still have a name, but no parse routine for it */
1112 Status = STATUS_OBJECT_TYPE_MISMATCH;
1113 Object = NULL;
1114 break;
1115 }
1116 }
1117 }
1118 }
1119 }
1120
1121 /* Check if we failed */
1122 if (!NT_SUCCESS(Status))
1123 {
1124 /* Cleanup after lookup */
1125 ObpReleaseLookupContext(LookupContext);
1126 }
1127
1128 /* Check if we have a device map and dereference it if so */
1129 //if (DeviceMap) ObfDereferenceDeviceMap(DeviceMap);
1130
1131 /* Check if we have a referenced directory and dereference it if so */
1132 if (ReferencedDirectory) ObDereferenceObject(ReferencedDirectory);
1133
1134 /* Check if we have a referenced parent directory */
1135 if (ReferencedParentDirectory)
1136 {
1137 /* We do, dereference it */
1138 ObDereferenceObject(ReferencedParentDirectory);
1139 }
1140
1141 /* Set the found object and check if we got one */
1142 *FoundObject = Object;
1143 if (!Object)
1144 {
1145 /* Nothing was found. Did we reparse or get success? */
1146 if ((Status == STATUS_REPARSE) || (NT_SUCCESS(Status)))
1147 {
1148 /* Set correct failure */
1149 Status = STATUS_OBJECT_NAME_NOT_FOUND;
1150 }
1151 }
1152
1153 /* Check if we had a root directory */
1154 if (RootHandle) ObDereferenceObject(RootDirectory);
1155
1156 /* Return status to caller */
1157 OBTRACE(OB_NAMESPACE_DEBUG,
1158 "%s - Found Object: %p. Expected: %p\n",
1159 __FUNCTION__,
1160 *FoundObject,
1161 InsertObject);
1162 return Status;
1163 }
1164
1165 /* PUBLIC FUNCTIONS *********************************************************/
1166
1167 NTSTATUS
1168 NTAPI
1169 ObQueryNameString(IN PVOID Object,
1170 OUT POBJECT_NAME_INFORMATION ObjectNameInfo,
1171 IN ULONG Length,
1172 OUT PULONG ReturnLength)
1173 {
1174 POBJECT_HEADER_NAME_INFO LocalInfo;
1175 POBJECT_HEADER ObjectHeader;
1176 POBJECT_DIRECTORY ParentDirectory;
1177 ULONG NameSize;
1178 PWCH ObjectName;
1179 BOOLEAN ObjectIsNamed;
1180 NTSTATUS Status = STATUS_SUCCESS;
1181
1182 /* Get the Kernel Meta-Structures */
1183 ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object);
1184 LocalInfo = OBJECT_HEADER_TO_NAME_INFO(ObjectHeader);
1185
1186 /* Check if a Query Name Procedure is available */
1187 if (ObjectHeader->Type->TypeInfo.QueryNameProcedure)
1188 {
1189 /* Call the procedure inside SEH */
1190 ObjectIsNamed = ((LocalInfo) && (LocalInfo->Name.Length > 0));
1191
1192 _SEH2_TRY
1193 {
1194 Status = ObjectHeader->Type->TypeInfo.QueryNameProcedure(Object,
1195 ObjectIsNamed,
1196 ObjectNameInfo,
1197 Length,
1198 ReturnLength,
1199 KernelMode);
1200 }
1201 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1202 {
1203 /* Return the exception code */
1204 Status = _SEH2_GetExceptionCode();
1205 }
1206 _SEH2_END;
1207
1208 return Status;
1209 }
1210
1211 /* Check if the object doesn't even have a name */
1212 if (!(LocalInfo) || !(LocalInfo->Name.Buffer))
1213 {
1214 Status = STATUS_SUCCESS;
1215
1216 _SEH2_TRY
1217 {
1218 /* We're returning the name structure */
1219 *ReturnLength = sizeof(OBJECT_NAME_INFORMATION);
1220
1221 /* Check if we were given enough space */
1222 if (*ReturnLength > Length)
1223 {
1224 Status = STATUS_INFO_LENGTH_MISMATCH;
1225 }
1226 else
1227 {
1228 /* Return an empty buffer */
1229 RtlInitEmptyUnicodeString(&ObjectNameInfo->Name, NULL, 0);
1230 }
1231 }
1232 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1233 {
1234 /* Return the exception code */
1235 Status = _SEH2_GetExceptionCode();
1236 }
1237 _SEH2_END;
1238
1239 return Status;
1240 }
1241
1242 /*
1243 * Find the size needed for the name. We won't do
1244 * this during the Name Creation loop because we want
1245 * to let the caller know that the buffer isn't big
1246 * enough right at the beginning, not work our way through
1247 * and find out at the end
1248 */
1249 _SEH2_TRY
1250 {
1251 if (Object == ObpRootDirectoryObject)
1252 {
1253 /* Size of the '\' string */
1254 NameSize = sizeof(OBJ_NAME_PATH_SEPARATOR);
1255 }
1256 else
1257 {
1258 /* Get the Object Directory and add name of Object */
1259 ParentDirectory = LocalInfo->Directory;
1260 NameSize = sizeof(OBJ_NAME_PATH_SEPARATOR) + LocalInfo->Name.Length;
1261
1262 /* Loop inside the directory to get the top-most one (meaning root) */
1263 while ((ParentDirectory != ObpRootDirectoryObject) && (ParentDirectory))
1264 {
1265 /* Get the Name Information */
1266 LocalInfo = OBJECT_HEADER_TO_NAME_INFO(
1267 OBJECT_TO_OBJECT_HEADER(ParentDirectory));
1268
1269 /* Add the size of the Directory Name */
1270 if (LocalInfo && LocalInfo->Directory)
1271 {
1272 /* Size of the '\' string + Directory Name */
1273 NameSize += sizeof(OBJ_NAME_PATH_SEPARATOR) +
1274 LocalInfo->Name.Length;
1275
1276 /* Move to next parent Directory */
1277 ParentDirectory = LocalInfo->Directory;
1278 }
1279 else
1280 {
1281 /* Directory with no name. We append "...\" */
1282 NameSize += sizeof(L"...") + sizeof(OBJ_NAME_PATH_SEPARATOR);
1283 break;
1284 }
1285 }
1286 }
1287
1288 /* Finally, add the name of the structure and the null char */
1289 *ReturnLength = NameSize +
1290 sizeof(OBJECT_NAME_INFORMATION) +
1291 sizeof(UNICODE_NULL);
1292
1293 /* Check if we were given enough space */
1294 if (*ReturnLength > Length) _SEH2_YIELD(return STATUS_INFO_LENGTH_MISMATCH);
1295
1296 /*
1297 * Now we will actually create the name. We work backwards because
1298 * it's easier to start off from the Name we have and walk up the
1299 * parent directories. We use the same logic as Name Length calculation.
1300 */
1301 LocalInfo = OBJECT_HEADER_TO_NAME_INFO(ObjectHeader);
1302 ObjectName = (PWCH)((ULONG_PTR)ObjectNameInfo + *ReturnLength);
1303 *--ObjectName = UNICODE_NULL;
1304
1305 /* Check if the object is actually the Root directory */
1306 if (Object == ObpRootDirectoryObject)
1307 {
1308 /* This is already the Root Directory, return "\\" */
1309 *--ObjectName = OBJ_NAME_PATH_SEPARATOR;
1310 ObjectNameInfo->Name.Length = (USHORT)NameSize;
1311 ObjectNameInfo->Name.MaximumLength = (USHORT)(NameSize +
1312 sizeof(UNICODE_NULL));
1313 ObjectNameInfo->Name.Buffer = ObjectName;
1314 Status = STATUS_SUCCESS;
1315 }
1316 else
1317 {
1318 /* Start by adding the Object's Name */
1319 ObjectName = (PWCH)((ULONG_PTR)ObjectName -
1320 LocalInfo->Name.Length);
1321 RtlCopyMemory(ObjectName,
1322 LocalInfo->Name.Buffer,
1323 LocalInfo->Name.Length);
1324
1325 /* Now parse the Parent directories until we reach the top */
1326 ParentDirectory = LocalInfo->Directory;
1327 while ((ParentDirectory != ObpRootDirectoryObject) && (ParentDirectory))
1328 {
1329 /* Get the name information */
1330 LocalInfo = OBJECT_HEADER_TO_NAME_INFO(
1331 OBJECT_TO_OBJECT_HEADER(ParentDirectory));
1332
1333 /* Add the "\" */
1334 *(--ObjectName) = OBJ_NAME_PATH_SEPARATOR;
1335
1336 /* Add the Parent Directory's Name */
1337 if (LocalInfo && LocalInfo->Name.Buffer)
1338 {
1339 /* Add the name */
1340 ObjectName = (PWCH)((ULONG_PTR)ObjectName -
1341 LocalInfo->Name.Length);
1342 RtlCopyMemory(ObjectName,
1343 LocalInfo->Name.Buffer,
1344 LocalInfo->Name.Length);
1345
1346 /* Move to next parent */
1347 ParentDirectory = LocalInfo->Directory;
1348 }
1349 else
1350 {
1351 /* Directory without a name, we add "..." */
1352 ObjectName = (PWCH)((ULONG_PTR)ObjectName -
1353 sizeof(L"...") +
1354 sizeof(UNICODE_NULL));
1355 RtlCopyMemory(ObjectName, L"...", sizeof(L"..."));
1356 break;
1357 }
1358 }
1359
1360 /* Add Root Directory Name */
1361 *(--ObjectName) = OBJ_NAME_PATH_SEPARATOR;
1362 ObjectNameInfo->Name.Length = (USHORT)NameSize;
1363 ObjectNameInfo->Name.MaximumLength =
1364 (USHORT)(NameSize + sizeof(UNICODE_NULL));
1365 ObjectNameInfo->Name.Buffer = ObjectName;
1366 }
1367 }
1368 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1369 {
1370 /* Return the exception code */
1371 Status = _SEH2_GetExceptionCode();
1372 }
1373 _SEH2_END;
1374
1375 /* Return success */
1376 return Status;
1377 }
1378
1379 /* EOF */