Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3350 lines
97 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. obdir.c
  5. Abstract:
  6. Directory Object routines
  7. Author:
  8. Steve Wood (stevewo) 31-Mar-1989
  9. Revision History:
  10. --*/
  11. #include "obp.h"
  12. #include <stdio.h>
  13. POBJECT_DIRECTORY
  14. ObpGetShadowDirectory(
  15. POBJECT_DIRECTORY Dir
  16. );
  17. //
  18. // Defined in ntos\se\rmlogon.c
  19. // private kernel function to obtain the LUID device map's directory
  20. // object
  21. // returns a kernel handle
  22. //
  23. NTSTATUS
  24. SeGetLogonIdDeviceMap(
  25. IN PLUID pLogonId,
  26. OUT PDEVICE_MAP* ppDevMap
  27. );
  28. NTSTATUS
  29. ObpSetCurrentProcessDeviceMap(
  30. );
  31. POBJECT_HEADER_NAME_INFO
  32. ObpTryReferenceNameInfoExclusive(
  33. IN POBJECT_HEADER ObjectHeader
  34. );
  35. VOID
  36. ObpReleaseExclusiveNameLock(
  37. IN POBJECT_HEADER ObjectHeader
  38. );
  39. POBJECT_DIRECTORY_ENTRY
  40. ObpUnlinkDirectoryEntry (
  41. IN POBJECT_DIRECTORY Directory,
  42. IN ULONG HashIndex
  43. );
  44. VOID
  45. ObpLinkDirectoryEntry (
  46. IN POBJECT_DIRECTORY Directory,
  47. IN ULONG HashIndex,
  48. IN POBJECT_DIRECTORY_ENTRY NewDirectoryEntry
  49. );
  50. VOID
  51. ObpReleaseLookupContextObject (
  52. IN POBP_LOOKUP_CONTEXT LookupContext
  53. );
  54. #if defined(ALLOC_PRAGMA)
  55. #pragma alloc_text(PAGE,NtCreateDirectoryObject)
  56. #pragma alloc_text(PAGE,NtOpenDirectoryObject)
  57. #pragma alloc_text(PAGE,NtQueryDirectoryObject)
  58. #pragma alloc_text(PAGE,ObpLookupDirectoryEntry)
  59. #pragma alloc_text(PAGE,ObpInsertDirectoryEntry)
  60. #pragma alloc_text(PAGE,ObpDeleteDirectoryEntry)
  61. #pragma alloc_text(PAGE,ObpLookupObjectName)
  62. #pragma alloc_text(PAGE,NtMakePermanentObject)
  63. #ifdef OBP_PAGEDPOOL_NAMESPACE
  64. #pragma alloc_text(PAGE,ObpGetShadowDirectory)
  65. #pragma alloc_text(PAGE,ObpSetCurrentProcessDeviceMap)
  66. #pragma alloc_text(PAGE,ObpReferenceDeviceMap)
  67. #pragma alloc_text(PAGE,ObfDereferenceDeviceMap)
  68. #pragma alloc_text(PAGE,ObSwapObjectNames)
  69. #pragma alloc_text(PAGE,ObpReleaseLookupContextObject)
  70. #pragma alloc_text(PAGE,ObpLinkDirectoryEntry)
  71. #pragma alloc_text(PAGE,ObpUnlinkDirectoryEntry)
  72. #pragma alloc_text(PAGE,ObpReleaseExclusiveNameLock)
  73. #pragma alloc_text(PAGE,ObpTryReferenceNameInfoExclusive)
  74. #endif // OBP_PAGEDPOOL_NAMESPACE
  75. #endif
  76. //
  77. // Global Object manager flags to control the case sensitivity lookup
  78. // and the LUID devicemap lookup
  79. //
  80. ULONG ObpCaseInsensitive = 1;
  81. extern ULONG ObpLUIDDeviceMapsEnabled;
  82. WCHAR ObpUnsecureGlobalNamesBuffer[128] = { 0 };
  83. ULONG ObpUnsecureGlobalNamesLength = sizeof(ObpUnsecureGlobalNamesBuffer);
  84. BOOLEAN
  85. ObpIsUnsecureName(
  86. IN PUNICODE_STRING ObjectName,
  87. IN BOOLEAN CaseInsensitive
  88. )
  89. {
  90. PWCHAR CrtName;
  91. UNICODE_STRING UnsecurePrefix;
  92. if (ObpUnsecureGlobalNamesBuffer[0] == 0) {
  93. return FALSE;
  94. }
  95. CrtName = ObpUnsecureGlobalNamesBuffer;
  96. do {
  97. RtlInitUnicodeString(&UnsecurePrefix, CrtName);
  98. if (UnsecurePrefix.Length) {
  99. if (RtlPrefixUnicodeString( &UnsecurePrefix, ObjectName, CaseInsensitive)) {
  100. return TRUE;
  101. }
  102. }
  103. CrtName += (UnsecurePrefix.Length + sizeof(UNICODE_NULL)) / sizeof(WCHAR);
  104. } while ( UnsecurePrefix.Length );
  105. return FALSE;
  106. }
  107. NTSTATUS
  108. NtCreateDirectoryObject (
  109. OUT PHANDLE DirectoryHandle,
  110. IN ACCESS_MASK DesiredAccess,
  111. IN POBJECT_ATTRIBUTES ObjectAttributes
  112. )
  113. /*++
  114. Routine Description:
  115. This routine creates a new directory object according to user
  116. specified object attributes
  117. Arguments:
  118. DirectoryHandle - Receives the handle for the newly created
  119. directory object
  120. DesiredAccess - Supplies the access being requested for this
  121. new directory object
  122. ObjectAttributes - Supplies caller specified attributes for new
  123. directory object
  124. Return Value:
  125. An appropriate status value.
  126. --*/
  127. {
  128. POBJECT_DIRECTORY Directory;
  129. HANDLE Handle;
  130. KPROCESSOR_MODE PreviousMode;
  131. NTSTATUS Status;
  132. PAGED_CODE();
  133. ObpValidateIrql( "NtCreateDirectoryObject" );
  134. //
  135. // Get previous processor mode and probe output arguments if necessary.
  136. //
  137. PreviousMode = KeGetPreviousMode();
  138. if (PreviousMode != KernelMode) {
  139. try {
  140. ProbeForWriteHandle( DirectoryHandle );
  141. } except( EXCEPTION_EXECUTE_HANDLER ) {
  142. return( GetExceptionCode() );
  143. }
  144. }
  145. //
  146. // Allocate and initialize a new Directory Object. We don't need
  147. // to specify a parse context or charge any quota. The size of
  148. // the object body is simply a directory object. This call gets
  149. // us a new referenced object.
  150. //
  151. Status = ObCreateObject( PreviousMode,
  152. ObpDirectoryObjectType,
  153. ObjectAttributes,
  154. PreviousMode,
  155. NULL,
  156. sizeof( *Directory ),
  157. 0,
  158. 0,
  159. (PVOID *)&Directory );
  160. if (!NT_SUCCESS( Status )) {
  161. return( Status );
  162. }
  163. RtlZeroMemory( Directory, sizeof( *Directory ) );
  164. ExInitializePushLock( &Directory->Lock );
  165. Directory->SessionId = OBJ_INVALID_SESSION_ID;
  166. //
  167. // Insert directory object in the current processes handle table,
  168. // set directory handle value and return status.
  169. //
  170. // ObInsertObject will delete the object in the case of failure
  171. //
  172. Status = ObInsertObject( Directory,
  173. NULL,
  174. DesiredAccess,
  175. 0,
  176. (PVOID *)NULL,
  177. &Handle );
  178. try {
  179. *DirectoryHandle = Handle;
  180. } except( EXCEPTION_EXECUTE_HANDLER ) {
  181. //
  182. // Fall through, since we do not want to undo what we have done.
  183. //
  184. }
  185. return( Status );
  186. }
  187. NTSTATUS
  188. NtOpenDirectoryObject (
  189. OUT PHANDLE DirectoryHandle,
  190. IN ACCESS_MASK DesiredAccess,
  191. IN POBJECT_ATTRIBUTES ObjectAttributes
  192. )
  193. /*++
  194. Routine Description:
  195. This routine opens an existing directory object.
  196. Arguments:
  197. DirectoryHandle - Receives the handle for the newly opened directory
  198. object
  199. DesiredAccess - Supplies the access being requested for this
  200. directory object
  201. ObjectAttributes - Supplies caller specified attributes for the
  202. directory object
  203. Return Value:
  204. An appropriate status value.
  205. --*/
  206. {
  207. KPROCESSOR_MODE PreviousMode;
  208. NTSTATUS Status;
  209. HANDLE Handle;
  210. PAGED_CODE();
  211. ObpValidateIrql( "NtOpenDirectoryObject" );
  212. //
  213. // Get previous processor mode and probe output arguments if necessary.
  214. //
  215. PreviousMode = KeGetPreviousMode();
  216. if (PreviousMode != KernelMode) {
  217. try {
  218. ProbeForWriteHandle( DirectoryHandle );
  219. } except( EXCEPTION_EXECUTE_HANDLER ) {
  220. return( GetExceptionCode() );
  221. }
  222. }
  223. //
  224. // Open handle to the directory object with the specified desired access,
  225. // set directory handle value, and return service completion status.
  226. //
  227. Status = ObOpenObjectByName( ObjectAttributes,
  228. ObpDirectoryObjectType,
  229. PreviousMode,
  230. NULL,
  231. DesiredAccess,
  232. NULL,
  233. &Handle );
  234. try {
  235. *DirectoryHandle = Handle;
  236. } except( EXCEPTION_EXECUTE_HANDLER ) {
  237. //
  238. // Fall through, since we do not want to undo what we have done.
  239. //
  240. }
  241. return Status;
  242. }
  243. NTSTATUS
  244. NtQueryDirectoryObject (
  245. IN HANDLE DirectoryHandle,
  246. OUT PVOID Buffer,
  247. IN ULONG Length,
  248. IN BOOLEAN ReturnSingleEntry,
  249. IN BOOLEAN RestartScan,
  250. IN OUT PULONG Context,
  251. OUT PULONG ReturnLength OPTIONAL
  252. )
  253. /*++
  254. Routine Description:
  255. This function returns information regarding a specified object
  256. directory.
  257. Arguments:
  258. DirectoryHandle - Supplies a handle to the directory being queried
  259. Buffer - Supplies the output buffer to receive the directory
  260. information. On return this contains one or more OBJECT DIRECTORY
  261. INFORMATION structures, the last one being null. And then this is
  262. followed by the string names for the directory entries.
  263. Length - Supplies the length, in bytes, of the user supplied output
  264. buffer
  265. ReturnSingleEntry - Indicates if this routine should just return
  266. one entry in the directory
  267. RestartScan - Indicates if we are to restart the scan or continue
  268. relative to the enumeration context passed in as the next
  269. parameter
  270. Context - Supplies an enumeration context that must be resupplied
  271. to this routine on subsequent calls to keep the enumeration
  272. in sync
  273. ReturnLength - Optionally receives the length, in bytes, that this
  274. routine has stuffed into the output buffer
  275. Return Value:
  276. An appropriate status value.
  277. --*/
  278. {
  279. POBJECT_DIRECTORY Directory;
  280. POBJECT_DIRECTORY_ENTRY DirectoryEntry;
  281. POBJECT_HEADER ObjectHeader;
  282. POBJECT_HEADER_NAME_INFO NameInfo;
  283. UNICODE_STRING ObjectName;
  284. POBJECT_DIRECTORY_INFORMATION DirInfo;
  285. PWCH NameBuffer;
  286. KPROCESSOR_MODE PreviousMode;
  287. NTSTATUS Status;
  288. ULONG Bucket, EntryNumber, CapturedContext;
  289. ULONG TotalLengthNeeded, LengthNeeded, EntriesFound;
  290. PCHAR TempBuffer;
  291. OBP_LOOKUP_CONTEXT LookupContext;
  292. PAGED_CODE();
  293. ObpValidateIrql( "NtQueryDirectoryObject" );
  294. ObpInitializeLookupContext( &LookupContext );
  295. //
  296. // Get previous processor mode and probe output arguments if necessary.
  297. //
  298. PreviousMode = KeGetPreviousMode();
  299. if (PreviousMode != KernelMode) {
  300. try {
  301. ProbeForWrite( Buffer, Length, sizeof( WCHAR ) );
  302. ProbeForWriteUlong( Context );
  303. if (ARGUMENT_PRESENT( ReturnLength )) {
  304. ProbeForWriteUlong( ReturnLength );
  305. }
  306. if (RestartScan) {
  307. CapturedContext = 0;
  308. } else {
  309. CapturedContext = *Context;
  310. }
  311. } except( EXCEPTION_EXECUTE_HANDLER ) {
  312. return( GetExceptionCode() );
  313. }
  314. } else {
  315. if (RestartScan) {
  316. CapturedContext = 0;
  317. } else {
  318. CapturedContext = *Context;
  319. }
  320. }
  321. //
  322. // Test for 64 bit if Length + sizeof( OBJECT_DIRECTORY_INFORMATION ) is less than Length
  323. // Return STATUS_INVALID_PARAMETER if there is an overflow
  324. //
  325. if (ObpIsOverflow( Length, sizeof( OBJECT_DIRECTORY_INFORMATION ))) {
  326. return( STATUS_INVALID_PARAMETER );
  327. }
  328. //
  329. // Allocate space for a temporary work buffer, make sure we got it,
  330. // and then zero it out. Make sure the buffer is large enough to
  331. // hold at least one dir info record. This will make the logic work
  332. // better when the a bad length is passed in.
  333. //
  334. TempBuffer = ExAllocatePoolWithQuotaTag( PagedPool | POOL_QUOTA_FAIL_INSTEAD_OF_RAISE,
  335. Length + sizeof( OBJECT_DIRECTORY_INFORMATION ),
  336. 'mNbO' );
  337. if (TempBuffer == NULL) {
  338. return( STATUS_INSUFFICIENT_RESOURCES );
  339. }
  340. RtlZeroMemory( TempBuffer, Length );
  341. //
  342. // Reference the directory object
  343. //
  344. Status = ObReferenceObjectByHandle( DirectoryHandle,
  345. DIRECTORY_QUERY,
  346. ObpDirectoryObjectType,
  347. PreviousMode,
  348. (PVOID *)&Directory,
  349. NULL );
  350. if (!NT_SUCCESS( Status )) {
  351. ExFreePool( TempBuffer );
  352. return( Status );
  353. }
  354. //
  355. // Lock down the directory structures for the life of this
  356. // procedure
  357. //
  358. ObpLockDirectoryShared(Directory, &LookupContext);
  359. //
  360. // DirInfo is used to march through the output buffer filling
  361. // in directory information. We'll start off by making sure
  362. // there is room for a NULL entry at end.
  363. //
  364. DirInfo = (POBJECT_DIRECTORY_INFORMATION)TempBuffer;
  365. TotalLengthNeeded = sizeof( *DirInfo );
  366. //
  367. // Keep track of the number of entries found and actual
  368. // entry that we are processing
  369. //
  370. EntryNumber = 0;
  371. EntriesFound = 0;
  372. //
  373. // By default we'll say there are no more entries until the
  374. // following loop put in some data
  375. //
  376. Status = STATUS_NO_MORE_ENTRIES;
  377. //
  378. // Our outer loop processes each hash bucket in the directory object
  379. //
  380. for (Bucket=0; Bucket<NUMBER_HASH_BUCKETS; Bucket++) {
  381. DirectoryEntry = Directory->HashBuckets[ Bucket ];
  382. //
  383. // For this hash bucket we'll zip through its list of entries.
  384. // This is a singly linked list so when the next pointer is null
  385. // (i.e., false) we at the end of the hash list
  386. //
  387. while (DirectoryEntry) {
  388. //
  389. // The captured context is simply the entry count unless the
  390. // user specified otherwise we start at zero, which means
  391. // the first entry is always returned in the enumeration.
  392. // If we have an match based on the entry index then we
  393. // process this entry. We bump the captured context further
  394. // done in the code.
  395. //
  396. if (CapturedContext == EntryNumber++) {
  397. //
  398. // For this directory entry we'll get a pointer to the
  399. // object body and see if it has an object name. If it
  400. // doesn't have a name then we'll give it an empty name.
  401. //
  402. ObjectHeader = OBJECT_TO_OBJECT_HEADER( DirectoryEntry->Object );
  403. NameInfo = OBJECT_HEADER_TO_NAME_INFO( ObjectHeader );
  404. if (NameInfo != NULL) {
  405. ObjectName = NameInfo->Name;
  406. } else {
  407. RtlInitUnicodeString( &ObjectName, NULL );
  408. }
  409. //
  410. // Now compute the length needed for this entry. This would
  411. // be the size of the object directory information record,
  412. // plus the size of the object name and object type name both
  413. // null terminated.
  414. //
  415. LengthNeeded = sizeof( *DirInfo ) +
  416. ObjectName.Length + sizeof( UNICODE_NULL ) +
  417. ObjectHeader->Type->Name.Length + sizeof( UNICODE_NULL );
  418. //
  419. // If there isn't enough room then take the following error
  420. // path. If the user wanted a single entry then tell the
  421. // caller what length is really needed and say the buffer was
  422. // too small. Otherwise the user wanted multiple entries,
  423. // so we'll just say there are more entries in the directory.
  424. // In both cases we drop down the entry number because we
  425. // weren't able to fit it in on this call
  426. //
  427. if ((TotalLengthNeeded + LengthNeeded) > Length) {
  428. if (ReturnSingleEntry) {
  429. TotalLengthNeeded += LengthNeeded;
  430. Status = STATUS_BUFFER_TOO_SMALL;
  431. } else {
  432. Status = STATUS_MORE_ENTRIES;
  433. }
  434. EntryNumber -= 1;
  435. goto querydone;
  436. }
  437. //
  438. // The information will fit in the buffer. So now fill
  439. // in the output buffer. We temporarily put in pointers
  440. // to the name buffer as stored in the object and object
  441. // type. We copy the data buffer to the user buffer
  442. // right before we return to the caller
  443. //
  444. try {
  445. DirInfo->Name.Length = ObjectName.Length;
  446. DirInfo->Name.MaximumLength = (USHORT)(ObjectName.Length+sizeof( UNICODE_NULL ));
  447. DirInfo->Name.Buffer = ObjectName.Buffer;
  448. DirInfo->TypeName.Length = ObjectHeader->Type->Name.Length;
  449. DirInfo->TypeName.MaximumLength = (USHORT)(ObjectHeader->Type->Name.Length+sizeof( UNICODE_NULL ));
  450. DirInfo->TypeName.Buffer = ObjectHeader->Type->Name.Buffer;
  451. Status = STATUS_SUCCESS;
  452. } except( EXCEPTION_EXECUTE_HANDLER ) {
  453. Status = GetExceptionCode();
  454. }
  455. if (!NT_SUCCESS( Status )) {
  456. goto querydone;
  457. }
  458. //
  459. // Update the total number of bytes needed in this query.
  460. // Push the dir info pointer to the next output location,
  461. // and indicate how many entries we've processed
  462. //
  463. //
  464. TotalLengthNeeded += LengthNeeded;
  465. DirInfo++;
  466. EntriesFound++;
  467. //
  468. // If we are to return only one entry then move on to the
  469. // post processing phase, otherwise indicate that we're
  470. // processing the next entry and go back to the top of
  471. // the inner loop
  472. //
  473. if (ReturnSingleEntry) {
  474. goto querydone;
  475. } else {
  476. //
  477. // Bump the captured context by one entry.
  478. //
  479. CapturedContext++;
  480. }
  481. }
  482. //
  483. // Get the next directory entry from the singly linked hash
  484. // bucket chain
  485. //
  486. DirectoryEntry = DirectoryEntry->ChainLink;
  487. }
  488. }
  489. //
  490. // At this point we've processed the directory entries and the first
  491. // part of the output buffer now contains a bunch of object directory
  492. // information records, but the pointers in them refer to the wrong
  493. // copies. So now we have some fixup to do.
  494. //
  495. querydone:
  496. try {
  497. //
  498. // We'll only do this post processing if we've been successful
  499. // so far. Note that this means we could be returning in the
  500. // user's output buffer system address that are meaningless, but
  501. // then getting back an error status should tell the caller to
  502. // forget about everything in the output buffer. Given back
  503. // a system address also isn't harmful because there is nothing
  504. // that the user can really do with it.
  505. //
  506. if (NT_SUCCESS( Status )) {
  507. //
  508. // Null terminate the string of object directory information
  509. // records and point to where the actual names will go
  510. //
  511. RtlZeroMemory( DirInfo, sizeof( *DirInfo ));
  512. DirInfo++;
  513. NameBuffer = (PWCH)DirInfo;
  514. //
  515. // Now for every entry that we've put in the output buffer
  516. // DirInfo will point to the entry and EntriesFound kept the
  517. // count. Note that we are guaranteed space because of
  518. // the math we did earlier in computing TotalLengthNeeded.
  519. //
  520. DirInfo = (POBJECT_DIRECTORY_INFORMATION)TempBuffer;
  521. while (EntriesFound--) {
  522. //
  523. // Copy over the object name, set the dir info pointer into
  524. // the user's buffer, then null terminate the string. Note
  525. // that we are really copying the data into our temp buffer
  526. // but the pointer fix up is for the user's buffer which
  527. // we'll copy into right after this loop.
  528. //
  529. RtlCopyMemory( NameBuffer,
  530. DirInfo->Name.Buffer,
  531. DirInfo->Name.Length );
  532. DirInfo->Name.Buffer = (PVOID)((ULONG_PTR)Buffer + ((ULONG_PTR)NameBuffer - (ULONG_PTR)TempBuffer));
  533. NameBuffer = (PWCH)((ULONG_PTR)NameBuffer + DirInfo->Name.Length);
  534. *NameBuffer++ = UNICODE_NULL;
  535. //
  536. // Do the same copy with the object type name
  537. //
  538. RtlCopyMemory( NameBuffer,
  539. DirInfo->TypeName.Buffer,
  540. DirInfo->TypeName.Length );
  541. DirInfo->TypeName.Buffer = (PVOID)((ULONG_PTR)Buffer + ((ULONG_PTR)NameBuffer - (ULONG_PTR)TempBuffer));
  542. NameBuffer = (PWCH)((ULONG_PTR)NameBuffer + DirInfo->TypeName.Length);
  543. *NameBuffer++ = UNICODE_NULL;
  544. //
  545. // Move on to the next dir info record
  546. //
  547. DirInfo++;
  548. }
  549. //
  550. // Set the enumeration context to the entry number of the next
  551. // entry to return.
  552. //
  553. *Context = EntryNumber;
  554. }
  555. //
  556. // Copy over the results from our temp buffer to the users buffer.
  557. // But adjust the amount copied just in case the total length needed
  558. // exceeds the length we allocated.
  559. //
  560. RtlCopyMemory( Buffer,
  561. TempBuffer,
  562. (TotalLengthNeeded <= Length ? TotalLengthNeeded : Length) );
  563. //
  564. // In all cases we'll tell the caller how much space if really needed
  565. // provided the user asked for this information
  566. //
  567. if (ARGUMENT_PRESENT( ReturnLength )) {
  568. *ReturnLength = TotalLengthNeeded;
  569. }
  570. } except( EXCEPTION_EXECUTE_HANDLER ) {
  571. //
  572. // Fall through, since we do not want to undo what we have done.
  573. //
  574. }
  575. //
  576. // Unlock the directroy structures, dereference the directory object,
  577. // free up our temp buffer, and return to our caller
  578. //
  579. ObpUnlockDirectory( Directory, &LookupContext);
  580. ObDereferenceObject( Directory );
  581. ExFreePool( TempBuffer );
  582. return( Status );
  583. }
  584. PVOID
  585. ObpLookupDirectoryEntry (
  586. IN POBJECT_DIRECTORY Directory,
  587. IN PUNICODE_STRING Name,
  588. IN ULONG Attributes,
  589. IN BOOLEAN SearchShadow,
  590. OUT POBP_LOOKUP_CONTEXT LookupContext
  591. )
  592. /*++
  593. Routine Description:
  594. This routine will lookup a single directory entry in a given directory.
  595. If it founds an object into that directory with the give name,
  596. that object will be referenced and the name will be referenced too, in order
  597. to prevent them going away when the directory is unlocked.
  598. The referenced object is saved in the LookupContext, and the references will be released
  599. at the next lookup, or when ObpReleaseLookupContext is called.
  600. Arguments:
  601. Directory - Supplies the directory being searched
  602. Name - Supplies the name of entry we're looking for
  603. Attributes - Indicates if the lookup should be case insensitive
  604. or not
  605. SearchShadow - If TRUE, and the object name is not found in the current directory,
  606. it will search the object into the shadow directory.
  607. LookupContext - The lookup context for this call. This structure must be initialized
  608. before calling first time ObpLookupDirectoryEntry.
  609. Return Value:
  610. Returns a pointer to the corresponding object body if found and NULL
  611. otherwise.
  612. --*/
  613. {
  614. POBJECT_DIRECTORY_ENTRY *HeadDirectoryEntry;
  615. POBJECT_DIRECTORY_ENTRY DirectoryEntry;
  616. POBJECT_HEADER ObjectHeader;
  617. POBJECT_HEADER_NAME_INFO NameInfo;
  618. PWCH Buffer;
  619. WCHAR Wchar;
  620. ULONG HashIndex;
  621. ULONG WcharLength;
  622. BOOLEAN CaseInSensitive;
  623. POBJECT_DIRECTORY_ENTRY *LookupBucket;
  624. PVOID Object = NULL;
  625. PAGED_CODE();
  626. if (ObpLUIDDeviceMapsEnabled == 0) {
  627. SearchShadow = FALSE; // Disable global devmap search
  628. }
  629. //
  630. // The caller needs to specify both a directory and a name otherwise
  631. // we can't process the request
  632. //
  633. if (!Directory || !Name) {
  634. goto UPDATECONTEXT;
  635. }
  636. //
  637. // Set a local variable to tell us if the search is case sensitive
  638. //
  639. if (Attributes & OBJ_CASE_INSENSITIVE) {
  640. CaseInSensitive = TRUE;
  641. } else {
  642. CaseInSensitive = FALSE;
  643. }
  644. //
  645. // Establish our local pointer to the input name buffer and get the
  646. // number of unicode characters in the input name. Also make sure
  647. // the caller gave us a non null name
  648. //
  649. Buffer = Name->Buffer;
  650. WcharLength = Name->Length / sizeof( *Buffer );
  651. if (!WcharLength || !Buffer) {
  652. goto UPDATECONTEXT;
  653. }
  654. //
  655. // Compute the address of the head of the bucket chain for this name.
  656. //
  657. HashIndex = 0;
  658. while (WcharLength--) {
  659. Wchar = *Buffer++;
  660. HashIndex += (HashIndex << 1) + (HashIndex >> 1);
  661. if (Wchar < 'a') {
  662. HashIndex += Wchar;
  663. } else if (Wchar > 'z') {
  664. HashIndex += RtlUpcaseUnicodeChar( Wchar );
  665. } else {
  666. HashIndex += (Wchar - ('a'-'A'));
  667. }
  668. }
  669. HashIndex %= NUMBER_HASH_BUCKETS;
  670. LookupContext->HashIndex = (USHORT)HashIndex;
  671. while (1) {
  672. HeadDirectoryEntry = (POBJECT_DIRECTORY_ENTRY *)&Directory->HashBuckets[ HashIndex ];
  673. LookupBucket = HeadDirectoryEntry;
  674. //
  675. // Lock the directory for read access, if the context was not previously locked
  676. // exclusively
  677. //
  678. if (!LookupContext->DirectoryLocked) {
  679. ObpLockDirectoryShared( Directory, LookupContext);
  680. }
  681. //
  682. // Walk the chain of directory entries for this hash bucket, looking
  683. // for either a match, or the insertion point if no match in the chain.
  684. //
  685. while ((DirectoryEntry = *HeadDirectoryEntry) != NULL) {
  686. //
  687. // Get the object header and name from the object body
  688. //
  689. // This function assumes the name must exist, otherwise it
  690. // wouldn't be in a directory
  691. //
  692. ObjectHeader = OBJECT_TO_OBJECT_HEADER( DirectoryEntry->Object );
  693. NameInfo = OBJECT_HEADER_TO_NAME_INFO( ObjectHeader );
  694. //
  695. // Compare strings using appropriate function.
  696. //
  697. if ((Name->Length == NameInfo->Name.Length) &&
  698. RtlEqualUnicodeString( Name,
  699. &NameInfo->Name,
  700. CaseInSensitive )) {
  701. //
  702. // If name matches, then exit loop with DirectoryEntry
  703. // pointing to matching entry.
  704. //
  705. break;
  706. }
  707. HeadDirectoryEntry = &DirectoryEntry->ChainLink;
  708. }
  709. //
  710. // At this point, there are two possiblilities:
  711. //
  712. // - we found an entry that matched and DirectoryEntry points to that
  713. // entry. Update the bucket chain so that the entry found is at the
  714. // head of the bucket chain. This is so the ObpDeleteDirectoryEntry
  715. // and ObpInsertDirectoryEntry functions will work. Also repeated
  716. // lookups of the same name will succeed quickly.
  717. //
  718. // - we did not find an entry that matched and DirectoryEntry is NULL.
  719. //
  720. if (DirectoryEntry) {
  721. //
  722. // The following convoluted piece of code moves a directory entry
  723. // we've found to the front of the hash list.
  724. //
  725. if (HeadDirectoryEntry != LookupBucket) {
  726. if ( LookupContext->DirectoryLocked
  727. ||
  728. ExTryConvertPushLockSharedToExclusive(&Directory->Lock)) {
  729. *HeadDirectoryEntry = DirectoryEntry->ChainLink;
  730. DirectoryEntry->ChainLink = *LookupBucket;
  731. *LookupBucket = DirectoryEntry;
  732. }
  733. }
  734. //
  735. // Now return the object to our caller
  736. //
  737. Object = DirectoryEntry->Object;
  738. goto UPDATECONTEXT;
  739. } else {
  740. if (!LookupContext->DirectoryLocked) {
  741. ObpUnlockDirectory( Directory, LookupContext );
  742. }
  743. //
  744. // If this is a directory with a device map then search the second directory for an entry.
  745. //
  746. if (SearchShadow && Directory->DeviceMap != NULL) {
  747. POBJECT_DIRECTORY NewDirectory;
  748. NewDirectory = ObpGetShadowDirectory (Directory);
  749. if (NewDirectory != NULL) {
  750. Directory = NewDirectory;
  751. continue;
  752. }
  753. }
  754. goto UPDATECONTEXT;
  755. }
  756. }
  757. UPDATECONTEXT:
  758. if (Object) {
  759. //
  760. // Reference the name to keep it's directory alive and the object
  761. // before returning from the lookup
  762. //
  763. ObpReferenceNameInfo( OBJECT_TO_OBJECT_HEADER(Object) );
  764. ObReferenceObject( Object );
  765. //
  766. // We can safetly drop the lock now
  767. //
  768. if (!LookupContext->DirectoryLocked) {
  769. ObpUnlockDirectory( Directory, LookupContext );
  770. }
  771. }
  772. //
  773. // If we have a previously referenced object we can dereference it
  774. //
  775. if (LookupContext->Object) {
  776. POBJECT_HEADER_NAME_INFO PreviousNameInfo;
  777. PreviousNameInfo = OBJECT_HEADER_TO_NAME_INFO(OBJECT_TO_OBJECT_HEADER(LookupContext->Object));
  778. ObpDereferenceNameInfo(PreviousNameInfo);
  779. ObDereferenceObject(LookupContext->Object);
  780. }
  781. LookupContext->Object = Object;
  782. return Object;
  783. }
  784. BOOLEAN
  785. ObpInsertDirectoryEntry (
  786. IN POBJECT_DIRECTORY Directory,
  787. IN POBP_LOOKUP_CONTEXT LookupContext,
  788. IN POBJECT_HEADER ObjectHeader
  789. )
  790. /*++
  791. Routine Description:
  792. This routine will insert a new directory entry into a directory
  793. object. The directory must have already have been searched using
  794. ObpLookupDirectoryEntry because that routine sets the LookupContext.
  795. N.B. The ObpLookupDirectoryEntry before should be done with the LookupContext
  796. locked.
  797. Arguments:
  798. Directory - Supplies the directory object being modified. This
  799. function assumes that we earlier did a lookup on the name
  800. that was successful or we just did an insertion
  801. Object - Supplies the object to insert into the directory
  802. LookupContext - The lookupContext passed in previously to ObpLookupDirectoryEntry
  803. Return Value:
  804. TRUE if the object is inserted successfully and FALSE otherwise
  805. --*/
  806. {
  807. POBJECT_DIRECTORY_ENTRY *HeadDirectoryEntry;
  808. POBJECT_DIRECTORY_ENTRY NewDirectoryEntry;
  809. POBJECT_HEADER_NAME_INFO NameInfo = OBJECT_HEADER_TO_NAME_INFO( ObjectHeader );
  810. #if DBG
  811. //
  812. // This function should be always called with a valid Directory
  813. // and LookupContext. Test this on checked builds
  814. //
  815. if ((LookupContext->Object != NULL) ||
  816. !LookupContext->DirectoryLocked ||
  817. (Directory != LookupContext->Directory)) {
  818. DbgPrint("OB: ObpInsertDirectoryEntry - invalid context %p %ld\n",
  819. LookupContext->Object,
  820. (ULONG)LookupContext->DirectoryLocked );
  821. DbgBreakPoint();
  822. return FALSE;
  823. }
  824. #endif // DBG
  825. //
  826. // Allocate memory for a new entry, and fail if not enough memory.
  827. //
  828. NewDirectoryEntry = (POBJECT_DIRECTORY_ENTRY)ExAllocatePoolWithTag( PagedPool,
  829. sizeof( OBJECT_DIRECTORY_ENTRY ),
  830. 'iDbO' );
  831. if (NewDirectoryEntry == NULL) {
  832. return( FALSE );
  833. }
  834. //
  835. // Get the right lookup bucket based on the HashIndex
  836. //
  837. HeadDirectoryEntry = (POBJECT_DIRECTORY_ENTRY *)&Directory->HashBuckets[ LookupContext->HashIndex ];
  838. //
  839. // Link the new entry into the chain at the insertion point.
  840. // This puts the new object right at the head of the current
  841. // hash bucket chain
  842. //
  843. NewDirectoryEntry->ChainLink = *HeadDirectoryEntry;
  844. *HeadDirectoryEntry = NewDirectoryEntry;
  845. NewDirectoryEntry->Object = &ObjectHeader->Body;
  846. //
  847. // Point the object header back to the directory we just inserted
  848. // it into.
  849. //
  850. NameInfo->Directory = Directory;
  851. //
  852. // Return success.
  853. //
  854. return( TRUE );
  855. }
  856. BOOLEAN
  857. ObpDeleteDirectoryEntry (
  858. IN POBP_LOOKUP_CONTEXT LookupContext
  859. )
  860. /*++
  861. Routine Description:
  862. This routine deletes the most recently found directory entry from
  863. the specified directory object. It will only succeed after a
  864. successful ObpLookupDirectoryEntry call.
  865. Arguments:
  866. Directory - Supplies the directory being modified
  867. Return Value:
  868. TRUE if the deletion succeeded and FALSE otherwise
  869. --*/
  870. {
  871. POBJECT_DIRECTORY_ENTRY *HeadDirectoryEntry;
  872. POBJECT_DIRECTORY_ENTRY DirectoryEntry;
  873. IN POBJECT_DIRECTORY Directory = LookupContext->Directory;
  874. //
  875. // Make sure we have a directory and that it has a found entry
  876. //
  877. if (!Directory ) {
  878. return( FALSE );
  879. }
  880. //
  881. // The lookup path places the object in the front of the list, so basically
  882. // we find the object immediately
  883. //
  884. HeadDirectoryEntry = (POBJECT_DIRECTORY_ENTRY *)&Directory->HashBuckets[ LookupContext->HashIndex ];
  885. DirectoryEntry = *HeadDirectoryEntry;
  886. //
  887. // Unlink the entry from the head of the bucket chain and free the
  888. // memory for the entry.
  889. //
  890. *HeadDirectoryEntry = DirectoryEntry->ChainLink;
  891. DirectoryEntry->ChainLink = NULL;
  892. ExFreePool( DirectoryEntry );
  893. return TRUE;
  894. }
  895. POBJECT_DIRECTORY
  896. ObpGetShadowDirectory(
  897. POBJECT_DIRECTORY Dir
  898. )
  899. {
  900. POBJECT_DIRECTORY NewDir;
  901. NewDir = NULL;
  902. ObpLockDeviceMap();
  903. if (Dir->DeviceMap != NULL) {
  904. NewDir = Dir->DeviceMap->GlobalDosDevicesDirectory;
  905. }
  906. ObpUnlockDeviceMap();
  907. return NewDir;
  908. }
  909. NTSTATUS
  910. ObpSetCurrentProcessDeviceMap(
  911. )
  912. /*++
  913. Routine Description:
  914. This function sets the process' device map to the device map associated
  915. with the process token's LUID.
  916. Arguments:
  917. none
  918. Return Values:
  919. STATUS_NO_TOKEN - process does not have a primary token
  920. STATUS_OBJECT_PATH_INVALID - could not obtain the device map associated
  921. with the process token's LUID
  922. An appropriate status - unexcepted error occurred
  923. --*/
  924. {
  925. PEPROCESS pProcess;
  926. PACCESS_TOKEN pToken = NULL;
  927. LUID userLuid;
  928. LUID SystemAuthenticationId = SYSTEM_LUID; // Local_System's LUID
  929. PDEVICE_MAP DeviceMap = NULL;
  930. PDEVICE_MAP DerefDeviceMap = NULL;
  931. NTSTATUS Status = STATUS_SUCCESS;
  932. pProcess = PsGetCurrentProcess();
  933. pToken = PsReferencePrimaryToken( pProcess );
  934. if (pToken == NULL) {
  935. return (STATUS_NO_TOKEN);
  936. }
  937. Status = SeQueryAuthenticationIdToken( pToken, &userLuid );
  938. if (!NT_SUCCESS(Status)) {
  939. PsDereferencePrimaryToken( pToken );
  940. return (Status);
  941. }
  942. if (!RtlEqualLuid( &userLuid, &SystemAuthenticationId )) {
  943. PDEVICE_MAP pDevMap;
  944. //
  945. // Get a handle to the Device Map for the user's LUID
  946. //
  947. Status = SeGetLogonIdDeviceMap( &userLuid,
  948. &pDevMap );
  949. if (NT_SUCCESS(Status)) {
  950. DeviceMap = pDevMap;
  951. }
  952. }
  953. else {
  954. //
  955. // Process is Local_System, so use the System's device map
  956. //
  957. DeviceMap = ObSystemDeviceMap;
  958. }
  959. if (DeviceMap != NULL) {
  960. //
  961. // set the process' device map
  962. //
  963. ObpLockDeviceMap();
  964. DerefDeviceMap = pProcess->DeviceMap;
  965. pProcess->DeviceMap = DeviceMap;
  966. DeviceMap->ReferenceCount++;
  967. ObpUnlockDeviceMap();
  968. }
  969. else {
  970. Status = STATUS_OBJECT_PATH_INVALID;
  971. }
  972. PsDereferencePrimaryToken( pToken );
  973. //
  974. // If the process already had a device map, then deref it now
  975. //
  976. if (DerefDeviceMap != NULL) {
  977. ObfDereferenceDeviceMap (DerefDeviceMap);
  978. }
  979. return (Status);
  980. }
  981. PDEVICE_MAP
  982. ObpReferenceDeviceMap(
  983. )
  984. /*++
  985. Routine Description:
  986. This function obtains the correct device map associated with the
  987. caller.
  988. If LUID device maps are enabled,
  989. then we obtain the LUID's device map.
  990. We use the existing process device map field as a cache for the
  991. process token's LUID device map.
  992. If LUID device maps are disabled,
  993. then use the device map associated with the process
  994. Arguments:
  995. none
  996. Return Values:
  997. A pointer to the caller's device map
  998. NULL - could not obtain the user's device map
  999. --*/
  1000. {
  1001. PDEVICE_MAP DeviceMap = NULL;
  1002. BOOLEAN LocalSystemRequest = FALSE;
  1003. LOGICAL LUIDDeviceMapsEnabled;
  1004. PACCESS_TOKEN pToken = NULL;
  1005. NTSTATUS Status;
  1006. LUIDDeviceMapsEnabled = (ObpLUIDDeviceMapsEnabled != 0);
  1007. if (LUIDDeviceMapsEnabled == TRUE) {
  1008. PETHREAD Thread = NULL;
  1009. //
  1010. // Separate device maps for each user LUID
  1011. // if (thread is impersonating)
  1012. // then get user's LUID to retrieve their device map
  1013. // else use the process' device map
  1014. // if (LUID is the Local_System)
  1015. // then use the system's device map
  1016. // if (unable to retrieve LUID's device map),
  1017. // then use the process' device map
  1018. //
  1019. //
  1020. // Get the current thread & check if the thread is impersonating
  1021. //
  1022. // if impersonating,
  1023. // then take the long path
  1024. // - get thread's access token
  1025. // - read the caller's LUID from the token
  1026. // - get the device map associate with this LUID
  1027. // if not impersonating,
  1028. // then use the device map associated with the process
  1029. //
  1030. Thread = PsGetCurrentThread();
  1031. if ( PS_IS_THREAD_IMPERSONATING (Thread) ) {
  1032. BOOLEAN fCopyOnOpen;
  1033. BOOLEAN fEffectiveOnly;
  1034. SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
  1035. LUID userLuid;
  1036. //
  1037. // Get the caller's access token from the thread
  1038. //
  1039. pToken = PsReferenceImpersonationToken( Thread,
  1040. &fCopyOnOpen,
  1041. &fEffectiveOnly,
  1042. &ImpersonationLevel);
  1043. if (pToken != NULL) {
  1044. //
  1045. // query the token for the LUID
  1046. //
  1047. Status = SeQueryAuthenticationIdToken( pToken, &userLuid );
  1048. }
  1049. else {
  1050. Status = STATUS_NO_TOKEN;
  1051. userLuid.LowPart = 0;
  1052. userLuid.HighPart = 0;
  1053. }
  1054. if (NT_SUCCESS(Status)) {
  1055. LUID SystemAuthenticationId = SYSTEM_LUID; // Local_System's LUID
  1056. //
  1057. // Verify the caller is not Local_System by
  1058. // comparing the LUID with Local_System's LUID
  1059. //
  1060. if (!RtlEqualLuid( &userLuid, &SystemAuthenticationId )) {
  1061. PDEVICE_MAP pDevMap;
  1062. //
  1063. // Get a handle to the Device Map for the LUID
  1064. //
  1065. Status = SeGetLogonIdDeviceMap( &userLuid,
  1066. &pDevMap );
  1067. if (NT_SUCCESS(Status)) {
  1068. //
  1069. // Use the device map associated with the LUID
  1070. //
  1071. DeviceMap = pDevMap;
  1072. ObpLockDeviceMap();
  1073. if (DeviceMap != NULL) {
  1074. DeviceMap->ReferenceCount++;
  1075. }
  1076. ObpUnlockDeviceMap();
  1077. }
  1078. }
  1079. else {
  1080. //
  1081. // Local_System will use the system's device map
  1082. //
  1083. LocalSystemRequest = TRUE;
  1084. }
  1085. }
  1086. }
  1087. }
  1088. if (DeviceMap == NULL) {
  1089. //
  1090. // if (going to reference the process' device map and the process'
  1091. // device map is not set),
  1092. // then set the process' device map
  1093. //
  1094. if ((LUIDDeviceMapsEnabled == TRUE) &&
  1095. (LocalSystemRequest == FALSE) &&
  1096. ((PsGetCurrentProcess()->DeviceMap) == NULL)) {
  1097. Status = ObpSetCurrentProcessDeviceMap();
  1098. if (!NT_SUCCESS(Status)) {
  1099. goto Error_Exit;
  1100. }
  1101. }
  1102. ObpLockDeviceMap();
  1103. if (LocalSystemRequest == TRUE) {
  1104. //
  1105. // Use the system's device map
  1106. //
  1107. DeviceMap = ObSystemDeviceMap;
  1108. }
  1109. else {
  1110. //
  1111. // Use the device map from the process
  1112. //
  1113. DeviceMap = PsGetCurrentProcess()->DeviceMap;
  1114. }
  1115. if (DeviceMap != NULL) {
  1116. DeviceMap->ReferenceCount++;
  1117. }
  1118. ObpUnlockDeviceMap();
  1119. }
  1120. Error_Exit:
  1121. if( pToken != NULL ) {
  1122. PsDereferenceImpersonationToken(pToken);
  1123. }
  1124. return DeviceMap;
  1125. }
  1126. VOID
  1127. FASTCALL
  1128. ObfDereferenceDeviceMap(
  1129. IN PDEVICE_MAP DeviceMap
  1130. )
  1131. {
  1132. ObpLockDeviceMap();
  1133. DeviceMap->ReferenceCount--;
  1134. if (DeviceMap->ReferenceCount == 0) {
  1135. DeviceMap->DosDevicesDirectory->DeviceMap = NULL;
  1136. ObpUnlockDeviceMap();
  1137. //
  1138. // This devmap is dead so mark the directory temporary so its name will go away and dereference it.
  1139. //
  1140. ObMakeTemporaryObject (DeviceMap->DosDevicesDirectory);
  1141. ObDereferenceObject( DeviceMap->DosDevicesDirectory );
  1142. ExFreePool( DeviceMap );
  1143. } else {
  1144. ObpUnlockDeviceMap();
  1145. }
  1146. }
  1147. NTSTATUS
  1148. ObpLookupObjectName (
  1149. IN HANDLE RootDirectoryHandle OPTIONAL,
  1150. IN PUNICODE_STRING ObjectName,
  1151. IN ULONG Attributes,
  1152. IN POBJECT_TYPE ObjectType,
  1153. IN KPROCESSOR_MODE AccessMode,
  1154. IN PVOID ParseContext OPTIONAL,
  1155. IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL,
  1156. IN PVOID InsertObject OPTIONAL,
  1157. IN OUT PACCESS_STATE AccessState,
  1158. OUT POBP_LOOKUP_CONTEXT LookupContext,
  1159. OUT PVOID *FoundObject
  1160. )
  1161. /*++
  1162. Routine Description:
  1163. This function will search a given directoroy for a specified
  1164. object name. It will also create a new object specified by
  1165. InsertObject.
  1166. Arguments:
  1167. RootDirectoryHandle - Optionally supplies the directory being
  1168. searched. If not supplied then this routine searches
  1169. the root directory
  1170. ObjectName - Supplies the name of object to lookup
  1171. Attributes - Specifies the attributes for the lookup (e.g., case
  1172. insensitive)
  1173. ObjectType - Specifies the type of the object to lookup
  1174. AccessMode - Specifies the callers processor mode
  1175. ParseContext - Optionally supplies a parse context that is blindly
  1176. passed to the parse callback routines
  1177. SecurityQos - Optionally supplies a pointer to the passed Security
  1178. Quality of Service parameter that is blindly passed to the parse
  1179. callback routines
  1180. InsertObject - Optionally supplies the object we think will be found.
  1181. This is used if the caller did not give a root directory handle
  1182. and the object name is "\" and the root object directory hasn't
  1183. been created yet. In other cases where we wind up creating
  1184. a new directory entry this is the object inserted.
  1185. AccessState - Current access state, describing already granted access
  1186. types, the privileges used to get them, and any access types yet to
  1187. be granted. The access masks may not contain any generic access
  1188. types.
  1189. DirectoryLocked - Receives an indication if this routine has returned
  1190. with the input directory locked
  1191. FoundObject - Receives a pointer to the object body if found
  1192. Return Value:
  1193. An appropriate status value.
  1194. N.B. If the status returned is SUCCESS the caller has the
  1195. responsability to release the lookup context
  1196. --*/
  1197. {
  1198. POBJECT_DIRECTORY RootDirectory;
  1199. POBJECT_DIRECTORY Directory = NULL;
  1200. POBJECT_DIRECTORY ParentDirectory = NULL;
  1201. POBJECT_HEADER ObjectHeader;
  1202. POBJECT_HEADER_NAME_INFO NameInfo;
  1203. PDEVICE_MAP DeviceMap = NULL;
  1204. PVOID Object;
  1205. UNICODE_STRING RemainingName;
  1206. UNICODE_STRING ComponentName;
  1207. PWCH NewName;
  1208. NTSTATUS Status;
  1209. BOOLEAN Reparse = FALSE; // BUGBUG - remove initialization & validate
  1210. BOOLEAN ReparsedSymbolicLink = FALSE;
  1211. ULONG MaxReparse = OBJ_MAX_REPARSE_ATTEMPTS;
  1212. OB_PARSE_METHOD ParseProcedure;
  1213. extern POBJECT_TYPE IoFileObjectType;
  1214. KPROCESSOR_MODE AccessCheckMode;
  1215. ObpValidateIrql( "ObpLookupObjectName" );
  1216. //
  1217. // Initialize our output variables to say we haven't lock or found
  1218. // anything but we were successful at it
  1219. //
  1220. ObpInitializeLookupContext(LookupContext);
  1221. *FoundObject = NULL;
  1222. Status = STATUS_SUCCESS;
  1223. Object = NULL;
  1224. //
  1225. // If the global flag says that we need to perform a case-insensitive check
  1226. // we'll force the OBJ_CASE_INSENSITIVE flag in attributes at lookup
  1227. //
  1228. if ( ObpCaseInsensitive ) {
  1229. if ( (ObjectType == NULL) ||
  1230. (ObjectType->TypeInfo.CaseInsensitive)
  1231. ) {
  1232. Attributes |= OBJ_CASE_INSENSITIVE;
  1233. }
  1234. }
  1235. if (Attributes & OBJ_FORCE_ACCESS_CHECK) {
  1236. AccessCheckMode = UserMode;
  1237. } else {
  1238. AccessCheckMode = AccessMode;
  1239. }
  1240. //
  1241. // Check if the caller has given us a directory to search. Otherwise
  1242. // we'll search the root object directory
  1243. //
  1244. if (ARGUMENT_PRESENT( RootDirectoryHandle )) {
  1245. //
  1246. // Otherwise reference the directory object and make sure
  1247. // that we successfully got the object
  1248. //
  1249. Status = ObReferenceObjectByHandle( RootDirectoryHandle,
  1250. 0,
  1251. NULL,
  1252. AccessMode,
  1253. (PVOID *)&RootDirectory,
  1254. NULL );
  1255. if (!NT_SUCCESS( Status )) {
  1256. return( Status );
  1257. }
  1258. //
  1259. // Translate the directory object to its object header
  1260. //
  1261. ObjectHeader = OBJECT_TO_OBJECT_HEADER( RootDirectory );
  1262. //
  1263. // Now if the name we're looking up starts with a "\" and it
  1264. // does not have a parse procedure then the syntax is bad
  1265. //
  1266. if ((ObjectName->Buffer != NULL) &&
  1267. (*(ObjectName->Buffer) == OBJ_NAME_PATH_SEPARATOR) &&
  1268. (ObjectHeader->Type != IoFileObjectType)) {
  1269. ObDereferenceObject( RootDirectory );
  1270. return( STATUS_OBJECT_PATH_SYNTAX_BAD );
  1271. }
  1272. //
  1273. // Now make sure that we do not have the directory of the
  1274. // object types
  1275. //
  1276. if (ObjectHeader->Type != ObpDirectoryObjectType) {
  1277. //
  1278. // We have an object directory that is not the object type
  1279. // directory. So now if it doesn't have a parse routine
  1280. // then there is nothing we can
  1281. //
  1282. if (ObjectHeader->Type->TypeInfo.ParseProcedure == NULL) {
  1283. ObDereferenceObject( RootDirectory );
  1284. return( STATUS_INVALID_HANDLE );
  1285. } else {
  1286. MaxReparse = OBJ_MAX_REPARSE_ATTEMPTS;
  1287. //
  1288. // The following loop cycles cycles through the various
  1289. // parse routine to we could encounter trying to resolve
  1290. // this name through symbolic links.
  1291. //
  1292. while (TRUE) {
  1293. #if DBG
  1294. KIRQL SaveIrql;
  1295. #endif
  1296. RemainingName = *ObjectName;
  1297. //
  1298. // Invoke the callback routine to parse the remaining
  1299. // object name
  1300. //
  1301. ObpBeginTypeSpecificCallOut( SaveIrql );
  1302. Status = (*ObjectHeader->Type->TypeInfo.ParseProcedure)( RootDirectory,
  1303. ObjectType,
  1304. AccessState,
  1305. AccessCheckMode,
  1306. Attributes,
  1307. ObjectName,
  1308. &RemainingName,
  1309. ParseContext,
  1310. SecurityQos,
  1311. &Object );
  1312. ObpEndTypeSpecificCallOut( SaveIrql, "Parse", ObjectHeader->Type, Object );
  1313. //
  1314. // If the status was not to do a reparse and the lookup
  1315. // was not successful then we found nothing so we
  1316. // dereference the directory and return the status to
  1317. // our caller. If the object we got back was null then
  1318. // we'll tell our caller that we couldn't find the name.
  1319. // Lastly if we did not get a reparse and we were
  1320. // successful and the object is not null then everything
  1321. // gets nicely returned to our caller
  1322. //
  1323. if ( ( Status != STATUS_REPARSE ) &&
  1324. ( Status != STATUS_REPARSE_OBJECT )) {
  1325. if (!NT_SUCCESS( Status )) {
  1326. Object = NULL;
  1327. } else if (Object == NULL) {
  1328. Status = STATUS_OBJECT_NAME_NOT_FOUND;
  1329. }
  1330. ObDereferenceObject( RootDirectory );
  1331. *FoundObject = Object;
  1332. return( Status );
  1333. //
  1334. // We got a status reparse, which means the object
  1335. // name has been modified to have use start all over
  1336. // again. If the reparse target is now empty or it
  1337. // is a path separator then we start the parse at the
  1338. // root directory
  1339. //
  1340. } else if ((ObjectName->Length == 0) ||
  1341. (ObjectName->Buffer == NULL) ||
  1342. (*(ObjectName->Buffer) == OBJ_NAME_PATH_SEPARATOR)) {
  1343. //
  1344. // Restart the parse relative to the root directory.
  1345. //
  1346. ObDereferenceObject( RootDirectory );
  1347. RootDirectory = ObpRootDirectoryObject;
  1348. RootDirectoryHandle = NULL;
  1349. goto ParseFromRoot;
  1350. //
  1351. // We got a reparse and we actually have a new name to
  1352. // go to we if we haven't exhausted our reparse attempts
  1353. // yet then just continue to the top of this loop.
  1354. //
  1355. } else if (--MaxReparse) {
  1356. continue;
  1357. //
  1358. // We got a reparse and we've exhausted our times through
  1359. // the loop so we'll return what we found.
  1360. //
  1361. } else {
  1362. ObDereferenceObject( RootDirectory );
  1363. *FoundObject = Object;
  1364. //
  1365. // At this point we were failing in stress by
  1366. // returning to the caller with a success status but
  1367. // a null object pointer.
  1368. //
  1369. if (Object == NULL) {
  1370. Status = STATUS_OBJECT_NAME_NOT_FOUND;
  1371. }
  1372. return( Status );
  1373. }
  1374. }
  1375. }
  1376. //
  1377. // At this point the caller has given us the directory of object
  1378. // types. If the caller didn't specify a name then we'll return
  1379. // a pointer to the root object directory.
  1380. //
  1381. } else if ((ObjectName->Length == 0) ||
  1382. (ObjectName->Buffer == NULL)) {
  1383. Status = ObReferenceObjectByPointer( RootDirectory,
  1384. 0,
  1385. ObjectType,
  1386. AccessMode );
  1387. if (NT_SUCCESS( Status )) {
  1388. Object = RootDirectory;
  1389. }
  1390. ObDereferenceObject( RootDirectory );
  1391. *FoundObject = Object;
  1392. return( Status );
  1393. }
  1394. //
  1395. // Otherwise the caller did not specify a directory to search so
  1396. // we'll default to the object root directory
  1397. //
  1398. } else {
  1399. RootDirectory = ObpRootDirectoryObject;
  1400. //
  1401. // If the name we're looking for is empty then it is illformed.
  1402. // Also it has to start with a "\" or it is illformed.
  1403. //
  1404. if ((ObjectName->Length == 0) ||
  1405. (ObjectName->Buffer == NULL) ||
  1406. (*(ObjectName->Buffer) != OBJ_NAME_PATH_SEPARATOR)) {
  1407. return( STATUS_OBJECT_PATH_SYNTAX_BAD );
  1408. }
  1409. //
  1410. // Check if the name is has only one character (that is the "\")
  1411. // Which means that the caller really just wants to lookup the
  1412. // root directory.
  1413. //
  1414. if (ObjectName->Length == sizeof( OBJ_NAME_PATH_SEPARATOR )) {
  1415. //
  1416. // If there is not a root directory yet. Then we really
  1417. // can't return it, however if the caller specified
  1418. // an insert object that is the one we'll reference and
  1419. // return to our caller
  1420. //
  1421. if (!RootDirectory) {
  1422. if (InsertObject) {
  1423. Status = ObReferenceObjectByPointer( InsertObject,
  1424. 0,
  1425. ObjectType,
  1426. AccessMode );
  1427. if (NT_SUCCESS( Status )) {
  1428. *FoundObject = InsertObject;
  1429. }
  1430. return( Status );
  1431. } else {
  1432. return( STATUS_INVALID_PARAMETER );
  1433. }
  1434. //
  1435. // At this point the caller did not specify a root directory,
  1436. // the name is "\" and the root object directory exists so
  1437. // we'll simply return the real root directory object
  1438. //
  1439. } else {
  1440. Status = ObReferenceObjectByPointer( RootDirectory,
  1441. 0,
  1442. ObjectType,
  1443. AccessMode );
  1444. if (NT_SUCCESS( Status )) {
  1445. *FoundObject = RootDirectory;
  1446. }
  1447. return( Status );
  1448. }
  1449. //
  1450. // At this pointer the caller did not specify a root directory,
  1451. // and the name is more than just a "\"
  1452. //
  1453. // Now if the lookup is case insensitive, and the name buffer is a
  1454. // legitimate pointer (meaning that is it quadword aligned), and
  1455. // there is a dos device map for the process. Then we'll handle
  1456. // the situation here. First get the device map and make sure it
  1457. // doesn't go away while we're using it.
  1458. //
  1459. } else {
  1460. ParseFromRoot:
  1461. if (DeviceMap != NULL) {
  1462. ObfDereferenceDeviceMap(DeviceMap);
  1463. DeviceMap = NULL;
  1464. }
  1465. if (!((ULONG_PTR)(ObjectName->Buffer) & (sizeof(ULONGLONG)-1))) {
  1466. //
  1467. // Check if the object name is actually equal to the
  1468. // global dos devices short name prefix "\??\"
  1469. //
  1470. if ((ObjectName->Length >= ObpDosDevicesShortName.Length)
  1471. &&
  1472. (*(PULONGLONG)(ObjectName->Buffer) == ObpDosDevicesShortNamePrefix.Alignment.QuadPart)) {
  1473. if ((DeviceMap = ObpReferenceDeviceMap()) != NULL) {
  1474. if (DeviceMap->DosDevicesDirectory != NULL ) {
  1475. //
  1476. // The user gave us the dos short name prefix so we'll
  1477. // look down the directory, and start the search at the
  1478. // dos device directory
  1479. //
  1480. ParentDirectory = RootDirectory;
  1481. Directory = DeviceMap->DosDevicesDirectory;
  1482. RemainingName = *ObjectName;
  1483. RemainingName.Buffer += (ObpDosDevicesShortName.Length / sizeof( WCHAR ));
  1484. RemainingName.Length = (USHORT)(RemainingName.Length - ObpDosDevicesShortName.Length);
  1485. goto quickStart;
  1486. }
  1487. }
  1488. //
  1489. // The name is not equal to "\??\" but check if it is
  1490. // equal to "\??"
  1491. //
  1492. } else if ((ObjectName->Length == ObpDosDevicesShortName.Length - sizeof( WCHAR ))
  1493. &&
  1494. (*(PULONG)(ObjectName->Buffer) == ObpDosDevicesShortNameRoot.Alignment.LowPart)
  1495. &&
  1496. (*((PWCHAR)(ObjectName->Buffer)+2) == (WCHAR)(ObpDosDevicesShortNameRoot.Alignment.HighPart))) {
  1497. //
  1498. // The user specified "\??" so we return to dos devices
  1499. // directory to our caller
  1500. //
  1501. if ((DeviceMap = ObpReferenceDeviceMap()) != NULL) {
  1502. if (DeviceMap->DosDevicesDirectory != NULL ) {
  1503. Status = ObReferenceObjectByPointer( DeviceMap->DosDevicesDirectory,
  1504. 0,
  1505. ObjectType,
  1506. AccessMode );
  1507. if (NT_SUCCESS( Status )) {
  1508. *FoundObject = DeviceMap->DosDevicesDirectory;
  1509. }
  1510. //
  1511. // Dereference the Device Map
  1512. //
  1513. ObfDereferenceDeviceMap(DeviceMap);
  1514. return( Status );
  1515. }
  1516. }
  1517. }
  1518. }
  1519. }
  1520. }
  1521. //
  1522. // At this point either
  1523. //
  1524. // the user specified a directory that is not the object
  1525. // type directory and got repase back to the root directory
  1526. //
  1527. // the user specified the object type directory and gave us
  1528. // a name to actually look up
  1529. //
  1530. // the user did not specify a search directory (default
  1531. // to root object directory) and if the name did start off
  1532. // with the dos device prefix we've munged outselves back to
  1533. // it to the dos device directory for the process
  1534. //
  1535. if( ReparsedSymbolicLink == FALSE ) {
  1536. Reparse = TRUE;
  1537. MaxReparse = OBJ_MAX_REPARSE_ATTEMPTS;
  1538. }
  1539. while (Reparse) {
  1540. RemainingName = *ObjectName;
  1541. quickStart:
  1542. Reparse = FALSE;
  1543. while (TRUE) {
  1544. Object = NULL;
  1545. //if (RemainingName.Length == 0) {
  1546. // Status = STATUS_OBJECT_NAME_INVALID;
  1547. // break;
  1548. // }
  1549. //
  1550. // If the remaining name for the object starts with a
  1551. // "\" then just gobble up the "\"
  1552. //
  1553. if ( (RemainingName.Length != 0) &&
  1554. (*(RemainingName.Buffer) == OBJ_NAME_PATH_SEPARATOR) ) {
  1555. RemainingName.Buffer++;
  1556. RemainingName.Length -= sizeof( OBJ_NAME_PATH_SEPARATOR );
  1557. }
  1558. //
  1559. // The following piece of code will calculate the first
  1560. // component of the remaining name. If there is not
  1561. // a remaining component then the object name is illformed
  1562. //
  1563. ComponentName = RemainingName;
  1564. while (RemainingName.Length != 0) {
  1565. if (*(RemainingName.Buffer) == OBJ_NAME_PATH_SEPARATOR) {
  1566. break;
  1567. }
  1568. RemainingName.Buffer++;
  1569. RemainingName.Length -= sizeof( OBJ_NAME_PATH_SEPARATOR );
  1570. }
  1571. ComponentName.Length = (USHORT)(ComponentName.Length - RemainingName.Length);
  1572. if (ComponentName.Length == 0) {
  1573. Status = STATUS_OBJECT_NAME_INVALID;
  1574. break;
  1575. }
  1576. //
  1577. // Now we have the first component name to lookup so we'll
  1578. // look the directory is necessary
  1579. //
  1580. if ( Directory == NULL ) {
  1581. Directory = RootDirectory;
  1582. }
  1583. //
  1584. // Now if the caller does not have traverse privilege and
  1585. // there is a parent directory then we must check if the
  1586. // user has traverse access to the directory. Our local
  1587. // Reparse variable should be false at this point so we'll
  1588. // drop out of both loops
  1589. //
  1590. if ( (AccessCheckMode != KernelMode) &&
  1591. !(AccessState->Flags & TOKEN_HAS_TRAVERSE_PRIVILEGE) &&
  1592. (ParentDirectory != NULL) ) {
  1593. if (!ObpCheckTraverseAccess( ParentDirectory,
  1594. DIRECTORY_TRAVERSE,
  1595. AccessState,
  1596. FALSE,
  1597. AccessCheckMode,
  1598. &Status )) {
  1599. break;
  1600. }
  1601. }
  1602. //
  1603. // If the object already exists in this directory, find it,
  1604. // else return NULL.
  1605. //
  1606. if ((RemainingName.Length == 0) && (InsertObject != NULL)) {
  1607. //
  1608. // If we are searching the last name, and we have an object
  1609. // to insert into that directory, we lock the context
  1610. // exclusively before the lookup. An insertion is likely
  1611. // to occur after this lookup if it fails, so we need to protect
  1612. // this directory to be changed until the ObpInsertDirectoryEntry call
  1613. //
  1614. ObpLockLookupContext( LookupContext, Directory );
  1615. }
  1616. Object = ObpLookupDirectoryEntry( Directory,
  1617. &ComponentName,
  1618. Attributes,
  1619. InsertObject == NULL ? TRUE : FALSE,
  1620. LookupContext );
  1621. if (!Object) {
  1622. //
  1623. // We didn't find the object. If there is some remaining
  1624. // name left (meaning the component name is a directory in
  1625. // path we trying to break) or the caller didn't specify an
  1626. // insert object then we then we'll break out here with an
  1627. // error status
  1628. //
  1629. if (RemainingName.Length != 0) {
  1630. Status = STATUS_OBJECT_PATH_NOT_FOUND;
  1631. break;
  1632. }
  1633. if (!InsertObject) {
  1634. Status = STATUS_OBJECT_NAME_NOT_FOUND;
  1635. break;
  1636. }
  1637. //
  1638. // Check that the caller has the access to the directory
  1639. // to either create a subdirectory (in the object type
  1640. // directory) or to create an object of the given component
  1641. // name. If the call fails then we'll break out of here
  1642. // with the status value set
  1643. //
  1644. if (!ObCheckCreateObjectAccess( Directory,
  1645. ObjectType == ObpDirectoryObjectType ?
  1646. DIRECTORY_CREATE_SUBDIRECTORY :
  1647. DIRECTORY_CREATE_OBJECT,
  1648. AccessState,
  1649. &ComponentName,
  1650. FALSE,
  1651. AccessCheckMode,
  1652. &Status )) {
  1653. break;
  1654. }
  1655. ObjectHeader = OBJECT_TO_OBJECT_HEADER( InsertObject );
  1656. if ((Directory->SessionId != OBJ_INVALID_SESSION_ID)
  1657. &&
  1658. ((ObjectHeader->Type == MmSectionObjectType)
  1659. ||
  1660. (ObjectHeader->Type == ObpSymbolicLinkObjectType))) {
  1661. //
  1662. // This directory is restricted to session. Check whether we are
  1663. // in the same session with the directory, and if we have privilege to
  1664. // access it otherwise
  1665. //
  1666. if ((Directory->SessionId != PsGetCurrentProcessSessionId())
  1667. &&
  1668. !SeSinglePrivilegeCheck( SeCreateGlobalPrivilege, AccessCheckMode)
  1669. &&
  1670. !ObpIsUnsecureName( &ComponentName,
  1671. ((Attributes & OBJ_CASE_INSENSITIVE) ? TRUE : FALSE ))) {
  1672. Status = STATUS_ACCESS_DENIED;
  1673. break;
  1674. }
  1675. }
  1676. //
  1677. // The object does not exist in the directory and
  1678. // we are allowed to create one. So allocate space
  1679. // for the name and insert the name into the directory
  1680. //
  1681. NewName = ExAllocatePoolWithTag( PagedPool, ComponentName.Length, 'mNbO' );
  1682. if ((NewName == NULL) ||
  1683. !ObpInsertDirectoryEntry( Directory, LookupContext, ObjectHeader )) {
  1684. if (NewName != NULL) {
  1685. ExFreePool( NewName );
  1686. }
  1687. Status = STATUS_INSUFFICIENT_RESOURCES;
  1688. break;
  1689. }
  1690. //
  1691. // We have an insert object so now get its name info,
  1692. // because we are going to change its name and insert it
  1693. // into the directory
  1694. //
  1695. ObReferenceObject( InsertObject );
  1696. NameInfo = OBJECT_HEADER_TO_NAME_INFO( ObjectHeader );
  1697. ObReferenceObject( Directory );
  1698. RtlCopyMemory( NewName,
  1699. ComponentName.Buffer,
  1700. ComponentName.Length );
  1701. if (NameInfo->Name.Buffer) {
  1702. ExFreePool( NameInfo->Name.Buffer );
  1703. }
  1704. NameInfo->Name.Buffer = NewName;
  1705. NameInfo->Name.Length = ComponentName.Length;
  1706. NameInfo->Name.MaximumLength = ComponentName.Length;
  1707. Object = InsertObject;
  1708. Status = STATUS_SUCCESS;
  1709. break;
  1710. }
  1711. //
  1712. // At this point we've found the component name within
  1713. // the directory. So we'll now grab the components object
  1714. // header, and get its parse routine
  1715. //
  1716. ReparseObject:
  1717. ObjectHeader = OBJECT_TO_OBJECT_HEADER( Object );
  1718. ParseProcedure = ObjectHeader->Type->TypeInfo.ParseProcedure;
  1719. //
  1720. // Now if there is a parse routine for the type and we are not
  1721. // inserting a new object or the parse routine is for symbolic
  1722. // links then we'll actually call the parse routine
  1723. //
  1724. if (ParseProcedure && (!InsertObject || (ParseProcedure == ObpParseSymbolicLink))) {
  1725. #if DBG
  1726. KIRQL SaveIrql;
  1727. #endif
  1728. //
  1729. // Reference the object and then free the directory lock
  1730. // This will keep the object from going away with the
  1731. // directory unlocked
  1732. //
  1733. ObpIncrPointerCount( ObjectHeader );
  1734. Directory = NULL;
  1735. ObpReleaseLookupContext(LookupContext);
  1736. ObpBeginTypeSpecificCallOut( SaveIrql );
  1737. //
  1738. // Call the objects parse routine
  1739. //
  1740. Status = (*ParseProcedure)( Object,
  1741. (PVOID)ObjectType,
  1742. AccessState,
  1743. AccessCheckMode,
  1744. Attributes,
  1745. ObjectName,
  1746. &RemainingName,
  1747. ParseContext,
  1748. SecurityQos,
  1749. &Object );
  1750. ObpEndTypeSpecificCallOut( SaveIrql, "Parse", ObjectHeader->Type, Object );
  1751. //
  1752. // We can now decrement the object reference count
  1753. //
  1754. ObDereferenceObject( &ObjectHeader->Body );
  1755. //
  1756. // Check if we have some reparsing to do
  1757. //
  1758. if ((Status == STATUS_REPARSE) || (Status == STATUS_REPARSE_OBJECT)) {
  1759. //
  1760. // See if we've reparsed too many times already and if
  1761. // so we'll fail the request
  1762. //
  1763. if (--MaxReparse) {
  1764. //
  1765. // Tell the outer loop to continue looping
  1766. //
  1767. Reparse = TRUE;
  1768. //
  1769. // Check if we have a reparse object or the name
  1770. // starts with a "\"
  1771. //
  1772. if ((Status == STATUS_REPARSE_OBJECT) ||
  1773. (*(ObjectName->Buffer) == OBJ_NAME_PATH_SEPARATOR)) {
  1774. //
  1775. // If the user specified a start directory then
  1776. // remove this information because we're taking
  1777. // a reparse point to someplace else
  1778. //
  1779. if (ARGUMENT_PRESENT( RootDirectoryHandle )) {
  1780. ObDereferenceObject( RootDirectory );
  1781. RootDirectoryHandle = NULL;
  1782. }
  1783. //
  1784. // And where we start is the root directory
  1785. // object
  1786. //
  1787. ParentDirectory = NULL;
  1788. RootDirectory = ObpRootDirectoryObject;
  1789. //
  1790. // Now if this is a reparse object (means we have
  1791. // encountered a symbolic link that has already been
  1792. // snapped so we have an object and remaining
  1793. // name that need to be examined) and we didn't
  1794. // find an object from the parse routine object
  1795. // break out of both loops.
  1796. //
  1797. if (Status == STATUS_REPARSE_OBJECT) {
  1798. Reparse = FALSE;
  1799. if (Object == NULL) {
  1800. Status = STATUS_OBJECT_NAME_NOT_FOUND;
  1801. } else {
  1802. //
  1803. // At this point we have a reparse object
  1804. // so we'll look the directory down and
  1805. // parse the new object
  1806. //
  1807. goto ReparseObject;
  1808. }
  1809. } else {
  1810. //
  1811. // At this point Status must be equal to
  1812. // STATUS_REPARSE because [(Status equals
  1813. // (STATUS_REPARSE_OBJECT or STATUS_REPARSE))
  1814. // && (Status != STATUS_REPARSE_OBJECT)]
  1815. //
  1816. ReparsedSymbolicLink = TRUE;
  1817. goto ParseFromRoot;
  1818. }
  1819. //
  1820. // We did not have a reparse object and the name
  1821. // does not start with a "\". Meaning we got back
  1822. // STATUS_REPASE, so now check if the directory
  1823. // is the root object directory and if so then
  1824. // we didn't the name otherwise we'll drop out of
  1825. // the inner loop and reparse true to get back to
  1826. // outer loop
  1827. //
  1828. } else if (RootDirectory == ObpRootDirectoryObject) {
  1829. Object = NULL;
  1830. Status = STATUS_OBJECT_NAME_NOT_FOUND;
  1831. Reparse = FALSE;
  1832. }
  1833. } else {
  1834. //
  1835. // We return object not found if we've exhausted
  1836. // the MaxReparse time
  1837. //
  1838. Object = NULL;
  1839. Status = STATUS_OBJECT_NAME_NOT_FOUND;
  1840. }
  1841. //
  1842. // We are not reparsing and if we did not get success then
  1843. // the object is null and we'll break out of our loops
  1844. //
  1845. } else if (!NT_SUCCESS( Status )) {
  1846. Object = NULL;
  1847. //
  1848. // We are not reparsing and we got back success but check
  1849. // if the object is null because that means we really didn't
  1850. // find the object, and then break out of our loops
  1851. //
  1852. // If the object is not null then we've been successful and
  1853. // prosperous so break out with the object set.
  1854. //
  1855. } else if (Object == NULL) {
  1856. Status = STATUS_OBJECT_NAME_NOT_FOUND;
  1857. }
  1858. break;
  1859. } else {
  1860. //
  1861. // At this point we do not have a parse routine or if there
  1862. // is a parse routine it is not for symbolic links or there
  1863. // may not be a specified insert object
  1864. //
  1865. // Check to see if we have exhausted the remaining name
  1866. //
  1867. if (RemainingName.Length == 0) {
  1868. //
  1869. // Check if the caller specified an object to insert.
  1870. // If specified then we'll break out of our loops with
  1871. // the object that we've found
  1872. //
  1873. if (!InsertObject) {
  1874. //
  1875. // The user did not specify an insert object
  1876. // so we're opening an existing object. Make sure
  1877. // we have traverse access to the container
  1878. // directory.
  1879. //
  1880. if ( (AccessCheckMode != KernelMode) &&
  1881. !(AccessState->Flags & TOKEN_HAS_TRAVERSE_PRIVILEGE) ) {
  1882. if (!ObpCheckTraverseAccess( Directory,
  1883. DIRECTORY_TRAVERSE,
  1884. AccessState,
  1885. FALSE,
  1886. AccessCheckMode,
  1887. &Status )) {
  1888. Object = NULL;
  1889. break;
  1890. }
  1891. }
  1892. Status = ObReferenceObjectByPointer( Object,
  1893. 0,
  1894. ObjectType,
  1895. AccessMode );
  1896. if (!NT_SUCCESS( Status )) {
  1897. Object = NULL;
  1898. }
  1899. }
  1900. break;
  1901. } else {
  1902. //
  1903. // There is some name remaining names to process
  1904. // if the directory we're looking at is the
  1905. // directory of object types and set ourselves
  1906. // up to parse it all over again.
  1907. //
  1908. if (ObjectHeader->Type == ObpDirectoryObjectType) {
  1909. ParentDirectory = Directory;
  1910. Directory = (POBJECT_DIRECTORY)Object;
  1911. } else {
  1912. //
  1913. // Otherwise there has been a mismatch so we'll
  1914. // set our error status and break out of the
  1915. // loops
  1916. //
  1917. Status = STATUS_OBJECT_TYPE_MISMATCH;
  1918. Object = NULL;
  1919. break;
  1920. }
  1921. }
  1922. }
  1923. }
  1924. }
  1925. //
  1926. // We can release the context if our search was unsuccesful.
  1927. // We still need the directory to be locked if a new object is
  1928. // inserted into that directory, until we finish the initialization
  1929. // (i.e SD, handle, ...). Note the object is visible now and could be accessed
  1930. // via name. We need to hold the directory lock until we are done.
  1931. //
  1932. if ( !NT_SUCCESS(Status) ) {
  1933. ObpReleaseLookupContext(LookupContext);
  1934. }
  1935. //
  1936. // If the device map has been referenced then dereference it
  1937. //
  1938. if (DeviceMap != NULL) {
  1939. ObfDereferenceDeviceMap(DeviceMap);
  1940. }
  1941. //
  1942. // At this point we've parsed the object name as much as possible
  1943. // going through symbolic links as necessary. So now set the
  1944. // output object pointer, and if we really did not find an object
  1945. // then we might need to modify the error status. If the
  1946. // status was repase or some success status then translate it
  1947. // to name not found.
  1948. //
  1949. if (!(*FoundObject = Object)) {
  1950. if (Status == STATUS_REPARSE) {
  1951. Status = STATUS_OBJECT_NAME_NOT_FOUND;
  1952. } else if (NT_SUCCESS( Status )) {
  1953. Status = STATUS_OBJECT_NAME_NOT_FOUND;
  1954. }
  1955. }
  1956. //
  1957. // If the caller gave us a root directory to search (and we didn't
  1958. // zero out this value) then free up our reference
  1959. //
  1960. if (ARGUMENT_PRESENT( RootDirectoryHandle )) {
  1961. ObDereferenceObject( RootDirectory );
  1962. RootDirectoryHandle = NULL;
  1963. }
  1964. //
  1965. // And return to our caller
  1966. //
  1967. return( Status );
  1968. }
  1969. NTSTATUS
  1970. NtMakePermanentObject (
  1971. IN HANDLE Handle
  1972. )
  1973. /*++
  1974. Routine Description:
  1975. This routine makes the specified object permanent.
  1976. By default, only Local_System may make this call
  1977. Arguments:
  1978. Handle - Supplies a handle to the object being modified
  1979. Return Value:
  1980. An appropriate status value.
  1981. --*/
  1982. {
  1983. KPROCESSOR_MODE PreviousMode;
  1984. NTSTATUS Status;
  1985. PVOID Object;
  1986. OBJECT_HANDLE_INFORMATION HandleInformation;
  1987. POBJECT_HEADER ObjectHeader;
  1988. PAGED_CODE();
  1989. //
  1990. // Get previous processor mode and probe output argument if necessary.
  1991. //
  1992. PreviousMode = KeGetPreviousMode();
  1993. //
  1994. // The object is being changed to permanent, check if
  1995. // the caller has the appropriate privilege.
  1996. //
  1997. if (!SeSinglePrivilegeCheck( SeCreatePermanentPrivilege,
  1998. PreviousMode)) {
  1999. Status = STATUS_PRIVILEGE_NOT_HELD;
  2000. return( Status );
  2001. }
  2002. Status = ObReferenceObjectByHandle( Handle,
  2003. 0,
  2004. (POBJECT_TYPE)NULL,
  2005. PreviousMode,
  2006. &Object,
  2007. &HandleInformation );
  2008. if (!NT_SUCCESS( Status )) {
  2009. return( Status );
  2010. }
  2011. //
  2012. // Make the object permanant. Note that the object should still
  2013. // have a name and directory entry because its handle count is not
  2014. // zero
  2015. //
  2016. ObjectHeader = OBJECT_TO_OBJECT_HEADER( Object );
  2017. //
  2018. // Other bits are set in this flags field by the handle database code. Synchronize with that.
  2019. //
  2020. ObpLockObject( ObjectHeader );
  2021. ObjectHeader->Flags |= OB_FLAG_PERMANENT_OBJECT;
  2022. //
  2023. // This routine releases the type mutex
  2024. //
  2025. ObpUnlockObject( ObjectHeader );
  2026. ObDereferenceObject( Object );
  2027. return( Status );
  2028. }
  2029. POBJECT_HEADER_NAME_INFO
  2030. ObpTryReferenceNameInfoExclusive(
  2031. IN POBJECT_HEADER ObjectHeader
  2032. )
  2033. /*++
  2034. Routine Description:
  2035. The function references exclusively the name information for a named object
  2036. Note that if there are outstanding references to the name info this function can fail
  2037. Arguments:
  2038. ObjectHeader - Object being locked
  2039. Return Value:
  2040. Returns the name information or NULL if it cannot be locked
  2041. Assumptions:
  2042. The parent directory is assumed to be exclusively locked (so that other threads
  2043. waiting to reference the name will have first to grab the directory lock).
  2044. --*/
  2045. {
  2046. POBJECT_HEADER_NAME_INFO NameInfo;
  2047. LONG References, NewReferences;
  2048. NameInfo = OBJECT_HEADER_TO_NAME_INFO( ObjectHeader );
  2049. References = NameInfo->QueryReferences;
  2050. do {
  2051. //
  2052. // If this is not the only reference to the object then we cannot lock the name
  2053. // N.B. The caller needs also to have a reference to this name
  2054. //
  2055. if ((References != 2)
  2056. ||
  2057. (NameInfo->Directory == NULL)) {
  2058. return NULL;
  2059. }
  2060. NewReferences = InterlockedCompareExchange ( (PLONG) &NameInfo->QueryReferences,
  2061. OBP_NAME_LOCKED | References,
  2062. References);
  2063. //
  2064. // If the exchange compare completed ok then we did a reference so return true.
  2065. //
  2066. if (NewReferences == References) {
  2067. return NameInfo;
  2068. }
  2069. //
  2070. // We failed because somebody else got in and changed the refence count on us. Use the new value to
  2071. // prime the exchange again.
  2072. //
  2073. References = NewReferences;
  2074. } while (TRUE);
  2075. return NULL;
  2076. }
  2077. VOID
  2078. ObpReleaseExclusiveNameLock(
  2079. IN POBJECT_HEADER ObjectHeader
  2080. )
  2081. /*++
  2082. Routine Description:
  2083. The routine releases the exclusive lock for the name information
  2084. Arguments:
  2085. ObjectHeader - Object being locked
  2086. Return Value:
  2087. None.
  2088. --*/
  2089. {
  2090. POBJECT_HEADER_NAME_INFO NameInfo;
  2091. NameInfo = OBJECT_HEADER_TO_NAME_INFO( ObjectHeader );
  2092. InterlockedExchangeAdd((PLONG)&NameInfo->QueryReferences, -OBP_NAME_LOCKED);
  2093. }
  2094. POBJECT_DIRECTORY_ENTRY
  2095. ObpUnlinkDirectoryEntry (
  2096. IN POBJECT_DIRECTORY Directory,
  2097. IN ULONG HashIndex
  2098. )
  2099. /*++
  2100. Routine Description:
  2101. This function removes the directory entry from a directory. Note that before calling this
  2102. function a lookup is necessary.
  2103. Arguments:
  2104. Directory - Supplies the directory
  2105. HashIndex - The hash value obtained from from the lookup context
  2106. Return Value:
  2107. Returns the directory entry removed from parent
  2108. --*/
  2109. {
  2110. POBJECT_DIRECTORY_ENTRY *HeadDirectoryEntry;
  2111. POBJECT_DIRECTORY_ENTRY DirectoryEntry;
  2112. //
  2113. // The lookup path places the object in the front of the list, so basically
  2114. // we find the object immediately
  2115. //
  2116. HeadDirectoryEntry = (POBJECT_DIRECTORY_ENTRY *)&Directory->HashBuckets[ HashIndex ];
  2117. DirectoryEntry = *HeadDirectoryEntry;
  2118. //
  2119. // Unlink the entry from the head of the bucket chain and free the
  2120. // memory for the entry.
  2121. //
  2122. *HeadDirectoryEntry = DirectoryEntry->ChainLink;
  2123. DirectoryEntry->ChainLink = NULL;
  2124. return DirectoryEntry;
  2125. }
  2126. VOID
  2127. ObpLinkDirectoryEntry (
  2128. IN POBJECT_DIRECTORY Directory,
  2129. IN ULONG HashIndex,
  2130. IN POBJECT_DIRECTORY_ENTRY NewDirectoryEntry
  2131. )
  2132. /*++
  2133. Routine Description:
  2134. The function inserts a new directory entry into a directory object
  2135. Arguments:
  2136. Directory - Supplies the directory object being modified. This
  2137. function assumes that we earlier did a lookup on the name
  2138. that was successful or we just did an insertion
  2139. HashIndex - The hash value obtained from from the lookup context
  2140. NewDirectoryEntry - Supplies the directory entry to be inserted
  2141. Return Value:
  2142. None
  2143. --*/
  2144. {
  2145. POBJECT_DIRECTORY_ENTRY *HeadDirectoryEntry;
  2146. //
  2147. // Get the right lookup bucket based on the HashIndex
  2148. //
  2149. HeadDirectoryEntry = (POBJECT_DIRECTORY_ENTRY *)&Directory->HashBuckets[ HashIndex ];
  2150. //
  2151. // Link the new entry into the chain at the insertion point.
  2152. // This puts the new object right at the head of the current
  2153. // hash bucket chain
  2154. //
  2155. NewDirectoryEntry->ChainLink = *HeadDirectoryEntry;
  2156. *HeadDirectoryEntry = NewDirectoryEntry;
  2157. }
  2158. VOID
  2159. ObpReleaseLookupContextObject (
  2160. IN POBP_LOOKUP_CONTEXT LookupContext
  2161. )
  2162. /*++
  2163. Routine Description:
  2164. This function releases the object references (added during the lookup)
  2165. but still keeps the directory locked.
  2166. N.B. The caller must retain at least one reference to the object and
  2167. to the name to make sure the deletion does not happen under the lock.
  2168. Arguments:
  2169. LookupContext - Supplies the context used in previous lookup
  2170. Return Value:
  2171. None.
  2172. --*/
  2173. {
  2174. //
  2175. // Remove the references added to the name info and object
  2176. //
  2177. if (LookupContext->Object) {
  2178. POBJECT_HEADER_NAME_INFO NameInfo;
  2179. NameInfo = OBJECT_HEADER_TO_NAME_INFO(OBJECT_TO_OBJECT_HEADER(LookupContext->Object));
  2180. ObpDereferenceNameInfo( NameInfo );
  2181. ObDereferenceObject(LookupContext->Object);
  2182. LookupContext->Object = NULL;
  2183. }
  2184. }
  2185. NTSTATUS
  2186. ObSwapObjectNames (
  2187. IN HANDLE DirectoryHandle,
  2188. IN HANDLE Handle1,
  2189. IN HANDLE Handle2,
  2190. IN ULONG Flags
  2191. )
  2192. /*++
  2193. Routine Description:
  2194. The function swaps the names (and permanent object attribute) for two objects inserted into
  2195. the same directory. Both objects must be named and have the same object type.
  2196. The function can fail if another one of these objects has the name locked (for a lookup for example)
  2197. Arguments:
  2198. DirectoryHandle - Supplies the parent directory for both objects
  2199. Handle1 - Supplies the handle to the first object
  2200. Handle2 - Supplies the handle to the second object
  2201. Return Value:
  2202. NTSTATUS.
  2203. --*/
  2204. {
  2205. #define INVALID_HASH_INDEX 0xFFFFFFFF
  2206. KPROCESSOR_MODE PreviousMode;
  2207. PVOID Object1, Object2;
  2208. NTSTATUS Status;
  2209. POBJECT_HEADER_NAME_INFO NameInfo1, NameInfo2;
  2210. POBJECT_HEADER ObjectHeader1 = NULL, ObjectHeader2 = NULL;
  2211. POBJECT_DIRECTORY Directory;
  2212. OBP_LOOKUP_CONTEXT LookupContext;
  2213. POBJECT_HEADER_NAME_INFO ExclusiveNameInfo1 = NULL, ExclusiveNameInfo2 = NULL;
  2214. POBJECT_DIRECTORY_ENTRY DirectoryEntry1 = NULL, DirectoryEntry2 = NULL;
  2215. ULONG HashIndex1 = INVALID_HASH_INDEX, HashIndex2 = INVALID_HASH_INDEX;
  2216. UNICODE_STRING TmpStr;
  2217. OBJECT_HANDLE_INFORMATION HandleInformation;
  2218. PreviousMode = KeGetPreviousMode();
  2219. UNREFERENCED_PARAMETER(Flags);
  2220. Object1 = NULL;
  2221. Object2 = NULL;
  2222. NameInfo1 = NULL;
  2223. NameInfo2 = NULL;
  2224. ObpInitializeLookupContext(&LookupContext);
  2225. Status = ObReferenceObjectByHandle( DirectoryHandle,
  2226. DIRECTORY_CREATE_OBJECT | DIRECTORY_CREATE_SUBDIRECTORY,
  2227. ObpDirectoryObjectType,
  2228. PreviousMode,
  2229. (PVOID *)&Directory,
  2230. NULL );
  2231. if (!NT_SUCCESS(Status)) {
  2232. goto exit;
  2233. }
  2234. Status = ObReferenceObjectByHandle( Handle1,
  2235. DELETE,
  2236. (POBJECT_TYPE)NULL,
  2237. PreviousMode,
  2238. &Object1,
  2239. &HandleInformation );
  2240. if (!NT_SUCCESS(Status)) {
  2241. goto exit;
  2242. }
  2243. Status = ObReferenceObjectByHandle( Handle2,
  2244. DELETE,
  2245. (POBJECT_TYPE)NULL,
  2246. PreviousMode,
  2247. &Object2,
  2248. &HandleInformation );
  2249. if (!NT_SUCCESS(Status)) {
  2250. goto exit;
  2251. }
  2252. if (Object1 == Object2) {
  2253. Status = STATUS_OBJECT_NAME_COLLISION;
  2254. goto exit;
  2255. }
  2256. ObjectHeader1 = OBJECT_TO_OBJECT_HEADER( Object1 );
  2257. NameInfo1 = ObpReferenceNameInfo( ObjectHeader1 );
  2258. ObjectHeader2 = OBJECT_TO_OBJECT_HEADER( Object2 );
  2259. NameInfo2 = ObpReferenceNameInfo( ObjectHeader2 );
  2260. if (ObjectHeader1->Type != ObjectHeader2->Type) {
  2261. Status = STATUS_OBJECT_TYPE_MISMATCH;
  2262. goto exit;
  2263. }
  2264. if ((NameInfo1 == NULL)
  2265. ||
  2266. (NameInfo2 == NULL)) {
  2267. Status = STATUS_OBJECT_NAME_INVALID;
  2268. goto exit;
  2269. }
  2270. if ((Directory != NameInfo1->Directory)
  2271. ||
  2272. (Directory != NameInfo2->Directory)) {
  2273. Status = STATUS_OBJECT_NAME_INVALID;
  2274. goto exit;
  2275. }
  2276. ObpLockLookupContext ( &LookupContext, Directory );
  2277. //
  2278. // Check that the object we is still in the directory
  2279. //
  2280. if (Object1 != ObpLookupDirectoryEntry( Directory,
  2281. &NameInfo1->Name,
  2282. 0,
  2283. FALSE,
  2284. &LookupContext )) {
  2285. //
  2286. // The object is no longer in directory
  2287. //
  2288. Status = STATUS_OBJECT_NAME_NOT_FOUND;
  2289. goto exit;
  2290. }
  2291. HashIndex1 = LookupContext.HashIndex;
  2292. DirectoryEntry1 = ObpUnlinkDirectoryEntry(Directory, HashIndex1);
  2293. ObpReleaseLookupContextObject(&LookupContext);
  2294. if (Object2 != ObpLookupDirectoryEntry( Directory,
  2295. &NameInfo2->Name,
  2296. 0,
  2297. FALSE,
  2298. &LookupContext )) {
  2299. //
  2300. // The object is no longer in directory
  2301. //
  2302. Status = STATUS_OBJECT_NAME_NOT_FOUND;
  2303. goto exit;
  2304. }
  2305. HashIndex2 = LookupContext.HashIndex;
  2306. DirectoryEntry2 = ObpUnlinkDirectoryEntry(Directory, HashIndex2);
  2307. ObpReleaseLookupContextObject(&LookupContext);
  2308. //
  2309. // Now try to lock exclusively both object names
  2310. //
  2311. ExclusiveNameInfo1 = ObpTryReferenceNameInfoExclusive(ObjectHeader1);
  2312. if (ExclusiveNameInfo1 == NULL) {
  2313. Status = STATUS_LOCK_NOT_GRANTED;
  2314. goto exit;
  2315. }
  2316. ExclusiveNameInfo2 = ObpTryReferenceNameInfoExclusive(ObjectHeader2);
  2317. if (ExclusiveNameInfo2 == NULL) {
  2318. Status = STATUS_LOCK_NOT_GRANTED;
  2319. goto exit;
  2320. }
  2321. //
  2322. // We have both names exclusively locked. we can swap now them
  2323. //
  2324. TmpStr = ExclusiveNameInfo1->Name;
  2325. ExclusiveNameInfo1->Name = ExclusiveNameInfo2->Name;
  2326. ExclusiveNameInfo2->Name = TmpStr;
  2327. //
  2328. // Now link back the objects, using the swaped hashes
  2329. //
  2330. ObpLinkDirectoryEntry(Directory, HashIndex2, DirectoryEntry1);
  2331. ObpLinkDirectoryEntry(Directory, HashIndex1, DirectoryEntry2);
  2332. DirectoryEntry1 = NULL;
  2333. DirectoryEntry2 = NULL;
  2334. exit:
  2335. if (DirectoryEntry1) {
  2336. ObpLinkDirectoryEntry(Directory, HashIndex1, DirectoryEntry1);
  2337. }
  2338. if (DirectoryEntry2) {
  2339. ObpLinkDirectoryEntry(Directory, HashIndex2, DirectoryEntry2);
  2340. }
  2341. if (ExclusiveNameInfo1) {
  2342. ObpReleaseExclusiveNameLock(ObjectHeader1);
  2343. }
  2344. if (ExclusiveNameInfo2) {
  2345. ObpReleaseExclusiveNameLock(ObjectHeader2);
  2346. }
  2347. ObpReleaseLookupContext( &LookupContext );
  2348. if ( NT_SUCCESS( Status )
  2349. &&
  2350. (ObjectHeader1 != NULL)
  2351. &&
  2352. (ObjectHeader2 != NULL)) {
  2353. //
  2354. // Lock now both objects and move swap the permanent object flag, if different
  2355. //
  2356. if ((ObjectHeader1->Flags ^ ObjectHeader2->Flags) & OB_FLAG_PERMANENT_OBJECT) {
  2357. //
  2358. // Both objects are required to have the same object type. We lock them all
  2359. // before swaping the flags
  2360. //
  2361. ObpLockAllObjects(ObjectHeader1->Type);
  2362. //
  2363. // Test again under the lock whether flags were changed
  2364. //
  2365. if ((ObjectHeader1->Flags ^ ObjectHeader2->Flags) & OB_FLAG_PERMANENT_OBJECT) {
  2366. //
  2367. // swap the flags
  2368. //
  2369. ObjectHeader1->Flags ^= OB_FLAG_PERMANENT_OBJECT;
  2370. ObjectHeader2->Flags ^= OB_FLAG_PERMANENT_OBJECT;
  2371. }
  2372. ObpUnlockAllObjects(ObjectHeader1->Type);
  2373. }
  2374. }
  2375. ObpDereferenceNameInfo(NameInfo1);
  2376. ObpDereferenceNameInfo(NameInfo2);
  2377. if (Object1) {
  2378. ObpDeleteNameCheck( Object1 ); // check whether the permanent flag went away meanwhile
  2379. ObDereferenceObject( Object1 );
  2380. }
  2381. if (Object2) {
  2382. ObpDeleteNameCheck( Object2 ); // check whether the permanent flag went away meanwhile
  2383. ObDereferenceObject( Object2 );
  2384. }
  2385. return Status;
  2386. }