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