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