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.

1823 lines
46 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. rmlogon.c
  5. Abstract:
  6. This module implements the kernel mode logon tracking performed by the
  7. reference monitor. Logon tracking is performed by keeping a count of
  8. how many tokens exist for each active logon in a system. When a logon
  9. session's reference count drops to zero, the LSA is notified so that
  10. authentication packages can clean up any related context data.
  11. Author:
  12. Jim Kelly (JimK) 21-April-1991
  13. Environment:
  14. Kernel mode only.
  15. Revision History:
  16. --*/
  17. //#define SEP_TRACK_LOGON_SESSION_REFS
  18. #include "pch.h"
  19. #pragma hdrstop
  20. #include "rmp.h"
  21. #include <bugcodes.h>
  22. #include <stdio.h>
  23. #include <zwapi.h>
  24. #ifdef ALLOC_DATA_PRAGMA
  25. #pragma data_seg("PAGEDATA")
  26. #endif
  27. SEP_LOGON_SESSION_TERMINATED_NOTIFICATION
  28. SeFileSystemNotifyRoutinesHead = {0};
  29. ////////////////////////////////////////////////////////////////////////////
  30. // //
  31. // Internally defined data types //
  32. // //
  33. ////////////////////////////////////////////////////////////////////////////
  34. typedef struct _SEP_FILE_SYSTEM_NOTIFY_CONTEXT {
  35. WORK_QUEUE_ITEM WorkItem;
  36. LUID LogonId;
  37. } SEP_FILE_SYSTEM_NOTIFY_CONTEXT, *PSEP_FILE_SYSTEM_NOTIFY_CONTEXT;
  38. ////////////////////////////////////////////////////////////////////////////
  39. // //
  40. // Internally defined routines //
  41. // //
  42. ////////////////////////////////////////////////////////////////////////////
  43. VOID
  44. SepInformLsaOfDeletedLogon(
  45. IN PLUID LogonId
  46. );
  47. VOID
  48. SepInformFileSystemsOfDeletedLogon(
  49. IN PLUID LogonId
  50. );
  51. VOID
  52. SepNotifyFileSystems(
  53. IN PVOID Context
  54. );
  55. NTSTATUS
  56. SepCleanupLUIDDeviceMapDirectory(
  57. PLUID pLogonId
  58. );
  59. NTSTATUS
  60. SeGetLogonIdDeviceMap(
  61. IN PLUID pLogonId,
  62. OUT PDEVICE_MAP* ppDevMap
  63. );
  64. //
  65. // declared in ntos\ob\obp.h
  66. // defined in ntos\ob\obdir.c
  67. // Used to dereference the LUID device map
  68. //
  69. VOID
  70. FASTCALL
  71. ObfDereferenceDeviceMap(
  72. IN PDEVICE_MAP DeviceMap
  73. );
  74. #ifdef ALLOC_PRAGMA
  75. #pragma alloc_text(PAGE,SepRmCreateLogonSessionWrkr)
  76. #pragma alloc_text(PAGE,SepRmDeleteLogonSessionWrkr)
  77. #pragma alloc_text(PAGE,SepReferenceLogonSession)
  78. #pragma alloc_text(PAGE,SepCleanupLUIDDeviceMapDirectory)
  79. #pragma alloc_text(PAGE,SepDeReferenceLogonSession)
  80. #pragma alloc_text(PAGE,SepCreateLogonSessionTrack)
  81. #pragma alloc_text(PAGE,SepDeleteLogonSessionTrack)
  82. #pragma alloc_text(PAGE,SepInformLsaOfDeletedLogon)
  83. #pragma alloc_text(PAGE,SeRegisterLogonSessionTerminatedRoutine)
  84. #pragma alloc_text(PAGE,SeUnregisterLogonSessionTerminatedRoutine)
  85. #pragma alloc_text(PAGE,SeMarkLogonSessionForTerminationNotification)
  86. #pragma alloc_text(PAGE,SepInformFileSystemsOfDeletedLogon)
  87. #pragma alloc_text(PAGE,SepNotifyFileSystems)
  88. #pragma alloc_text(PAGE,SeGetLogonIdDeviceMap)
  89. #if DBG || TOKEN_LEAK_MONITOR
  90. #pragma alloc_text(PAGE,SepAddTokenLogonSession)
  91. #pragma alloc_text(PAGE,SepRemoveTokenLogonSession)
  92. #endif
  93. #endif
  94. ////////////////////////////////////////////////////////////////////////////
  95. // //
  96. // Local macros //
  97. // //
  98. ////////////////////////////////////////////////////////////////////////////
  99. //
  100. // This macro is used to obtain an index into the logon session tracking
  101. // array given a logon session ID (a LUID).
  102. //
  103. #define SepLogonSessionIndex( PLogonId ) ( \
  104. (PLogonId)->LowPart & SEP_LOGON_TRACK_INDEX_MASK \
  105. )
  106. ////////////////////////////////////////////////////////////////////////////
  107. // //
  108. // Exported Services //
  109. // //
  110. ////////////////////////////////////////////////////////////////////////////
  111. VOID
  112. SepRmCreateLogonSessionWrkr(
  113. IN PRM_COMMAND_MESSAGE CommandMessage,
  114. OUT PRM_REPLY_MESSAGE ReplyMessage
  115. )
  116. /*++
  117. Routine Description:
  118. This function is the dispatch routine for the LSA --> RM
  119. "CreateLogonSession" call.
  120. The arguments passed to this routine are defined by the
  121. type SEP_RM_COMMAND_WORKER.
  122. Arguments:
  123. CommandMessage - Points to structure containing RM command message
  124. information consisting of an LPC PORT_MESSAGE structure followed
  125. by the command number (RmComponentTestCommand) and a command-specific
  126. body. The command-specific body of this parameter is a LUID of the
  127. logon session to be created.
  128. ReplyMessage - Pointer to structure containing LSA reply message
  129. information consisting of an LPC PORT_MESSAGE structure followed
  130. by the command ReturnedStatus field in which a status code from the
  131. command will be returned.
  132. Return Value:
  133. VOID
  134. --*/
  135. {
  136. NTSTATUS Status;
  137. LUID LogonId;
  138. PAGED_CODE();
  139. //
  140. // Check that command is expected type
  141. //
  142. ASSERT( CommandMessage->CommandNumber == RmCreateLogonSession );
  143. //
  144. // Typecast the command parameter to what we expect.
  145. //
  146. LogonId = *((LUID UNALIGNED *) CommandMessage->CommandParams);
  147. //
  148. // Try to create the logon session tracking record
  149. //
  150. Status = SepCreateLogonSessionTrack( &LogonId );
  151. //
  152. // Set the reply status
  153. //
  154. ReplyMessage->ReturnedStatus = Status;
  155. return;
  156. }
  157. VOID
  158. SepRmDeleteLogonSessionWrkr(
  159. IN PRM_COMMAND_MESSAGE CommandMessage,
  160. OUT PRM_REPLY_MESSAGE ReplyMessage
  161. )
  162. /*++
  163. Routine Description:
  164. This function is the dispatch routine for the LSA --> RM
  165. "DeleteLogonSession" call.
  166. The arguments passed to this routine are defined by the
  167. type SEP_RM_COMMAND_WORKER.
  168. Arguments:
  169. CommandMessage - Points to structure containing RM command message
  170. information consisting of an LPC PORT_MESSAGE structure followed
  171. by the command number (RmComponentTestCommand) and a command-specific
  172. body. The command-specific body of this parameter is a LUID of the
  173. logon session to be created.
  174. ReplyMessage - Pointer to structure containing LSA reply message
  175. information consisting of an LPC PORT_MESSAGE structure followed
  176. by the command ReturnedStatus field in which a status code from the
  177. command will be returned.
  178. Return Value:
  179. VOID
  180. --*/
  181. {
  182. NTSTATUS Status;
  183. LUID LogonId;
  184. PAGED_CODE();
  185. //
  186. // Check that command is expected type
  187. //
  188. ASSERT( CommandMessage->CommandNumber == RmDeleteLogonSession );
  189. //
  190. // Typecast the command parameter to what we expect.
  191. //
  192. LogonId = *((LUID UNALIGNED *) CommandMessage->CommandParams);
  193. //
  194. // Try to create the logon session tracking record
  195. //
  196. Status = SepDeleteLogonSessionTrack( &LogonId );
  197. //
  198. // Set the reply status
  199. //
  200. ReplyMessage->ReturnedStatus = Status;
  201. return;
  202. }
  203. NTSTATUS
  204. SepReferenceLogonSession(
  205. IN PLUID LogonId,
  206. OUT PSEP_LOGON_SESSION_REFERENCES *ReturnSession
  207. )
  208. /*++
  209. Routine Description:
  210. This routine increments the reference count of a logon session
  211. tracking record.
  212. Arguments:
  213. LogonId - Pointer to the logon session ID whose logon track is
  214. to be incremented.
  215. ReturnSession - The found session is returned here on success.
  216. Return Value:
  217. STATUS_SUCCESS - The reference count was successfully incremented.
  218. STATUS_NO_SUCH_LOGON_SESSION - The specified logon session doesn't
  219. exist in the reference monitor's database.
  220. --*/
  221. {
  222. ULONG SessionArrayIndex;
  223. PSEP_LOGON_SESSION_REFERENCES *Previous, Current;
  224. ULONG Refs;
  225. PAGED_CODE();
  226. SessionArrayIndex = SepLogonSessionIndex( LogonId );
  227. Previous = &SepLogonSessions[ SessionArrayIndex ];
  228. //
  229. // Protect modification of reference monitor database
  230. //
  231. SepRmAcquireDbWriteLock(SessionArrayIndex);
  232. //
  233. // Now walk the list for our logon session array hash index.
  234. //
  235. Current = *Previous;
  236. while (Current != NULL) {
  237. //
  238. // If we found it, increment the reference count and return
  239. //
  240. if (RtlEqualLuid( LogonId, &Current->LogonId) ) {
  241. Refs = InterlockedIncrement (&Current->ReferenceCount);
  242. SepRmReleaseDbWriteLock(SessionArrayIndex);
  243. #ifdef SEP_TRACK_LOGON_SESSION_REFS
  244. DbgPrint("SE (rm): ++ logon session: (%d, %d) to %d by (%d, %d)\n",
  245. LogonId->HighPart, LogonId->LowPart, Refs,
  246. PsGetCurrentThread()->Cid.UniqueProcess,
  247. PsGetCurrentThread()->Cid.UniqueThread);
  248. #endif //SEP_TRACK_LOGON_SESSION_REFS
  249. *ReturnSession = Current;
  250. return STATUS_SUCCESS;
  251. }
  252. Current = Current->Next;
  253. }
  254. SepRmReleaseDbWriteLock(SessionArrayIndex);
  255. //
  256. // Bad news, someone asked us to increment the reference count of
  257. // a logon session we didn't know existed. This might be a new
  258. // token being created, so return an error status and let the caller
  259. // decide if it warrants a bug check or not.
  260. //
  261. return STATUS_NO_SUCH_LOGON_SESSION;
  262. }
  263. NTSTATUS
  264. SepCleanupLUIDDeviceMapDirectory(
  265. PLUID pLogonId
  266. )
  267. /*++
  268. Routine Description:
  269. Make the contents of the (LUID's device map)'s directory object
  270. temporary so that their names go away.
  271. Arguments:
  272. pLogonId - Pointer to the logon session ID whose device is to be
  273. cleaned up
  274. Return Value:
  275. STATUS_SUCCESS - cleaned up the entire device map
  276. STATUS_INVALID_PARAMETER - pLogonId is a NULL pointer
  277. STATUS_NO_MEMORY - could not allocate memory to hold the handle
  278. buffer
  279. appropriate NTSTATUS code
  280. --*/
  281. {
  282. NTSTATUS Status;
  283. OBJECT_ATTRIBUTES Attributes;
  284. UNICODE_STRING UnicodeString;
  285. HANDLE LinkHandle;
  286. POBJECT_DIRECTORY_INFORMATION DirInfo = NULL;
  287. BOOLEAN RestartScan;
  288. WCHAR szString[64]; // \Sessions\0\DosDevices\x-x = 10+1+12+(8)+1+(8)+1 = 41
  289. ULONG Context = 0;
  290. ULONG ReturnedLength;
  291. HANDLE DosDevicesDirectory;
  292. HANDLE *HandleArray;
  293. ULONG Size = 100;
  294. ULONG i, Count = 0;
  295. ULONG dirInfoLength = 0;
  296. PAGED_CODE();
  297. if (pLogonId == NULL) {
  298. return( STATUS_INVALID_PARAMETER );
  299. }
  300. //
  301. // Open a handle to the directory object for the LUID device map
  302. // Get a kernel handle
  303. //
  304. _snwprintf( szString,
  305. (sizeof(szString)/sizeof(WCHAR)) - 1,
  306. L"\\Sessions\\0\\DosDevices\\%08x-%08x",
  307. pLogonId->HighPart,
  308. pLogonId->LowPart );
  309. RtlInitUnicodeString(&UnicodeString, szString);
  310. InitializeObjectAttributes(&Attributes,
  311. &UnicodeString,
  312. OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
  313. NULL,
  314. NULL);
  315. Status = ZwOpenDirectoryObject(&DosDevicesDirectory,
  316. DIRECTORY_QUERY,
  317. &Attributes);
  318. if (!NT_SUCCESS(Status)) {
  319. return Status;
  320. }
  321. Restart:
  322. //
  323. // Create an array of handles to close with each scan
  324. // of the directory
  325. //
  326. HandleArray = (HANDLE *)ExAllocatePoolWithTag(
  327. PagedPool,
  328. (Size * sizeof(HANDLE)),
  329. 'aHeS'
  330. );
  331. if (HandleArray == NULL) {
  332. ZwClose(DosDevicesDirectory);
  333. if (DirInfo != NULL) {
  334. ExFreePool(DirInfo);
  335. }
  336. return STATUS_NO_MEMORY;
  337. }
  338. RestartScan = TRUE;
  339. while (TRUE) {
  340. do {
  341. //
  342. // ZwQueryDirectoryObject returns one element at a time
  343. //
  344. Status = ZwQueryDirectoryObject( DosDevicesDirectory,
  345. (PVOID)DirInfo,
  346. dirInfoLength,
  347. TRUE,
  348. RestartScan,
  349. &Context,
  350. &ReturnedLength );
  351. if (Status == STATUS_BUFFER_TOO_SMALL) {
  352. dirInfoLength = ReturnedLength;
  353. if (DirInfo != NULL) {
  354. ExFreePool(DirInfo);
  355. }
  356. DirInfo = ExAllocatePoolWithTag( PagedPool, dirInfoLength, 'bDeS' );
  357. if (DirInfo == NULL) {
  358. Status = STATUS_INSUFFICIENT_RESOURCES;
  359. }
  360. }
  361. }while (Status == STATUS_BUFFER_TOO_SMALL);
  362. //
  363. // Check the status of the operation.
  364. //
  365. if (!NT_SUCCESS(Status)) {
  366. if (Status == STATUS_NO_MORE_ENTRIES) {
  367. Status = STATUS_SUCCESS;
  368. }
  369. break;
  370. }
  371. //
  372. // Check that the element is a symbolic link
  373. //
  374. if (!wcscmp(DirInfo->TypeName.Buffer, L"SymbolicLink")) {
  375. //
  376. // check if the handle array is full
  377. //
  378. if ( Count >= Size ) {
  379. //
  380. // empty the handle array by closing all the handles
  381. // and free the handle array so that we can create
  382. // a bigger handle array
  383. // Need to restart the directory scan
  384. //
  385. for (i = 0; i < Count ; i++) {
  386. ZwClose (HandleArray[i]);
  387. }
  388. Size += 20;
  389. Count = 0;
  390. ExFreePool((PVOID)HandleArray);
  391. HandleArray = NULL;
  392. goto Restart;
  393. }
  394. InitializeObjectAttributes( &Attributes,
  395. &DirInfo->Name,
  396. OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
  397. DosDevicesDirectory,
  398. NULL);
  399. Status = ZwOpenSymbolicLinkObject( &LinkHandle,
  400. SYMBOLIC_LINK_ALL_ACCESS,
  401. &Attributes);
  402. if (NT_SUCCESS(Status)) {
  403. //
  404. // Make the object temporary so that its name goes away from
  405. // the Object Manager's namespace
  406. //
  407. Status = ZwMakeTemporaryObject( LinkHandle );
  408. if (NT_SUCCESS( Status )) {
  409. HandleArray[Count] = LinkHandle;
  410. Count++;
  411. }
  412. else {
  413. ZwClose( LinkHandle );
  414. }
  415. }
  416. }
  417. RestartScan = FALSE;
  418. }
  419. //
  420. // Close all the handles
  421. //
  422. for (i = 0; i < Count ; i++) {
  423. ZwClose (HandleArray[i]);
  424. }
  425. if (HandleArray != NULL) {
  426. ExFreePool((PVOID)HandleArray);
  427. }
  428. if (DirInfo != NULL) {
  429. ExFreePool(DirInfo);
  430. }
  431. if (DosDevicesDirectory != NULL) {
  432. ZwClose(DosDevicesDirectory);
  433. }
  434. return Status;
  435. }
  436. VOID
  437. SepDeReferenceLogonSession(
  438. IN PLUID LogonId
  439. )
  440. /*++
  441. Routine Description:
  442. This routine decrements the reference count of a logon session
  443. tracking record.
  444. If the reference count is decremented to zero, then there is no
  445. possibility for any more tokens to exist for the logon session.
  446. In this case, the LSA is notified that a logon session has
  447. terminated.
  448. Arguments:
  449. LogonId - Pointer to the logon session ID whose logon track is
  450. to be decremented.
  451. Return Value:
  452. None.
  453. --*/
  454. {
  455. ULONG SessionArrayIndex;
  456. PSEP_LOGON_SESSION_REFERENCES *Previous, Current;
  457. PDEVICE_MAP pDeviceMap = NULL;
  458. ULONG Refs;
  459. PAGED_CODE();
  460. SessionArrayIndex = SepLogonSessionIndex( LogonId );
  461. Previous = &SepLogonSessions[ SessionArrayIndex ];
  462. //
  463. // Protect modification of reference monitor database
  464. //
  465. SepRmAcquireDbWriteLock(SessionArrayIndex);
  466. //
  467. // Now walk the list for our logon session array hash index.
  468. //
  469. Current = *Previous;
  470. while (Current != NULL) {
  471. //
  472. // If we found it, decrement the reference count and return
  473. //
  474. if (RtlEqualLuid( LogonId, &Current->LogonId) ) {
  475. Refs = InterlockedDecrement (&Current->ReferenceCount);
  476. if (Refs == 0) {
  477. //
  478. // Pull it from the list
  479. //
  480. *Previous = Current->Next;
  481. //
  482. // No longer need to protect our pointer to this
  483. // record.
  484. //
  485. SepRmReleaseDbWriteLock(SessionArrayIndex);
  486. //
  487. // If the Device Map exist for this LUID,
  488. // dereference the pointer to the Device Map
  489. //
  490. if (Current->pDeviceMap != NULL) {
  491. //
  492. // Dereference our reference on the device map
  493. // our reference should be the last reference,
  494. // thus the system will delete the device map
  495. // for the LUID
  496. //
  497. pDeviceMap = Current->pDeviceMap;
  498. Current->pDeviceMap = NULL;
  499. }
  500. //
  501. // Make all the contents of the LUID's device map temporary,
  502. // so that the names go away from the Object Manager's
  503. // namespace
  504. // Remove our reference on the LUID's device map
  505. //
  506. if (pDeviceMap != NULL) {
  507. SepCleanupLUIDDeviceMapDirectory( LogonId );
  508. ObfDereferenceDeviceMap( pDeviceMap );
  509. }
  510. //
  511. // Asynchronoously inform file systems that this logon session
  512. // is going away, if at least one FS expressed interest in this
  513. // logon session.
  514. //
  515. if (Current->Flags & SEP_TERMINATION_NOTIFY) {
  516. SepInformFileSystemsOfDeletedLogon( LogonId );
  517. }
  518. //
  519. // Deallocate the logon session track record.
  520. //
  521. ExFreePool( (PVOID)Current );
  522. #ifdef SEP_TRACK_LOGON_SESSION_REFS
  523. DbgPrint("SE (rm): -- ** logon session: (%d, %d) to ZERO by (%d, %d)\n",
  524. LogonId->HighPart, LogonId->LowPart,
  525. PsGetCurrentThread()->Cid.UniqueProcess,
  526. PsGetCurrentThread()->Cid.UniqueThread);
  527. #endif //SEP_TRACK_LOGON_SESSION_REFS
  528. //
  529. // Inform the LSA about the deletion of this logon session.
  530. //
  531. SepInformLsaOfDeletedLogon( LogonId );
  532. return;
  533. }
  534. //
  535. // reference count was decremented, but not to zero.
  536. //
  537. SepRmReleaseDbWriteLock(SessionArrayIndex);
  538. #ifdef SEP_TRACK_LOGON_SESSION_REFS
  539. DbgPrint("SE (rm): -- logon session: (%d, %d) to %d by (%d, %d)\n",
  540. LogonId->HighPart, LogonId->LowPart, Refs,
  541. PsGetCurrentThread()->Cid.UniqueProcess,
  542. PsGetCurrentThread()->Cid.UniqueThread);
  543. #endif //SEP_TRACK_LOGON_SESSION_REFS
  544. return;
  545. }
  546. Previous = &Current->Next;
  547. Current = *Previous;
  548. }
  549. SepRmReleaseDbWriteLock(SessionArrayIndex);
  550. //
  551. // Bad news, someone asked us to decrement the reference count of
  552. // a logon session we didn't know existed.
  553. //
  554. KeBugCheckEx( DEREF_UNKNOWN_LOGON_SESSION, 0, 0, 0, 0 );
  555. return;
  556. }
  557. NTSTATUS
  558. SepCreateLogonSessionTrack(
  559. IN PLUID LogonId
  560. )
  561. /*++
  562. Routine Description:
  563. This routine creates a new logon session tracking record.
  564. This should only be called as a dispatch routine for a LSA->RM
  565. call (and once during system initialization).
  566. If the specified logon session already exists, then an error is returned.
  567. Arguments:
  568. LogonId - Pointer to the logon session ID for which a new logon track is
  569. to be created.
  570. Return Value:
  571. STATUS_SUCCESS - The logon session track was created successfully.
  572. STATUS_LOGON_SESSION_EXISTS - The logon session already exists.
  573. A new one has not been created.
  574. --*/
  575. {
  576. ULONG SessionArrayIndex;
  577. PSEP_LOGON_SESSION_REFERENCES *Previous, Current;
  578. PSEP_LOGON_SESSION_REFERENCES LogonSessionTrack;
  579. PAGED_CODE();
  580. #if DBG || TOKEN_LEAK_MONITOR
  581. if (SepTokenLeakTracking) {
  582. DbgPrint("\nLOGON : 0x%x 0x%x\n\n", LogonId->HighPart, LogonId->LowPart);
  583. }
  584. #endif
  585. //
  586. // Make sure we can allocate a new logon session track record
  587. //
  588. LogonSessionTrack = (PSEP_LOGON_SESSION_REFERENCES)
  589. ExAllocatePoolWithTag(
  590. PagedPool,
  591. sizeof(SEP_LOGON_SESSION_REFERENCES),
  592. 'sLeS'
  593. );
  594. if (LogonSessionTrack == NULL) {
  595. return STATUS_INSUFFICIENT_RESOURCES;
  596. }
  597. RtlZeroMemory(LogonSessionTrack, sizeof(SEP_LOGON_SESSION_REFERENCES));
  598. LogonSessionTrack->LogonId = (*LogonId);
  599. LogonSessionTrack->ReferenceCount = 0;
  600. LogonSessionTrack->pDeviceMap = NULL;
  601. #if DBG || TOKEN_LEAK_MONITOR
  602. InitializeListHead(&LogonSessionTrack->TokenList);
  603. #endif
  604. SessionArrayIndex = SepLogonSessionIndex( LogonId );
  605. Previous = &SepLogonSessions[ SessionArrayIndex ];
  606. //
  607. // Protect modification of reference monitor database
  608. //
  609. SepRmAcquireDbWriteLock(SessionArrayIndex);
  610. //
  611. // Now walk the list for our logon session array hash index
  612. // looking for a duplicate logon session ID.
  613. //
  614. Current = *Previous;
  615. while (Current != NULL) {
  616. if (RtlEqualLuid( LogonId, &Current->LogonId) ) {
  617. //
  618. // One already exists. Hmmm.
  619. //
  620. SepRmReleaseDbWriteLock(SessionArrayIndex);
  621. ExFreePool(LogonSessionTrack);
  622. return STATUS_LOGON_SESSION_EXISTS;
  623. }
  624. Current = Current->Next;
  625. }
  626. //
  627. // Reached the end of the list without finding a duplicate.
  628. // Add the new one.
  629. //
  630. LogonSessionTrack->Next = *Previous;
  631. *Previous = LogonSessionTrack;
  632. SepRmReleaseDbWriteLock(SessionArrayIndex);
  633. return STATUS_SUCCESS;
  634. }
  635. NTSTATUS
  636. SepDeleteLogonSessionTrack(
  637. IN PLUID LogonId
  638. )
  639. /*++
  640. Routine Description:
  641. This routine creates a new logon session tracking record.
  642. This should only be called as a dispatch routine for a LSA->RM
  643. call (and once during system initialization).
  644. If the specified logon session already exists, then an error is returned.
  645. Arguments:
  646. LogonId - Pointer to the logon session ID whose logon track is
  647. to be deleted.
  648. Return Value:
  649. STATUS_SUCCESS - The logon session track was deleted successfully.
  650. STATUS_BAD_LOGON_SESSION_STATE - The logon session has a non-zero
  651. reference count and can not be deleted.
  652. STATUS_NO_SUCH_LOGON_SESSION - The specified logon session does not
  653. exist.
  654. --*/
  655. {
  656. ULONG SessionArrayIndex;
  657. PSEP_LOGON_SESSION_REFERENCES *Previous, Current;
  658. PDEVICE_MAP pDeviceMap = NULL;
  659. PAGED_CODE();
  660. SessionArrayIndex = SepLogonSessionIndex( LogonId );
  661. Previous = &SepLogonSessions[ SessionArrayIndex ];
  662. //
  663. // Protect modification of reference monitor database
  664. //
  665. SepRmAcquireDbWriteLock(SessionArrayIndex);
  666. //
  667. // Now walk the list for our logon session array hash index.
  668. //
  669. Current = *Previous;
  670. while (Current != NULL) {
  671. //
  672. // If we found it, make sure reference count is zero
  673. //
  674. if (RtlEqualLuid( LogonId, &Current->LogonId) ) {
  675. if (Current->ReferenceCount == 0) {
  676. //
  677. // Pull it from the list
  678. //
  679. *Previous = Current->Next;
  680. //
  681. // If the Device Map exist for this LUID,
  682. // dereference the pointer to the Device Map
  683. //
  684. if (Current->pDeviceMap != NULL) {
  685. //
  686. // Dereference our reference on the device map
  687. // our reference should be the last reference,
  688. // thus the system will delete the device map
  689. // for the LUID
  690. //
  691. pDeviceMap = Current->pDeviceMap;
  692. Current->pDeviceMap = NULL;
  693. }
  694. //
  695. // No longer need to protect our pointer to this
  696. // record.
  697. //
  698. SepRmReleaseDbWriteLock(SessionArrayIndex);
  699. //
  700. // Make all the contents of the LUID's device map temporary,
  701. // so that the names go away from the Object Manager's
  702. // namespace
  703. // Remove our reference on the LUID's device map
  704. //
  705. if (pDeviceMap != NULL) {
  706. SepCleanupLUIDDeviceMapDirectory( LogonId );
  707. ObfDereferenceDeviceMap( pDeviceMap );
  708. }
  709. //
  710. // Deallocate the logon session track record.
  711. //
  712. ExFreePool( (PVOID)Current );
  713. return STATUS_SUCCESS;
  714. }
  715. //
  716. // reference count was not zero. This is not considered
  717. // a healthy situation. Return an error and let someone
  718. // else declare the bug check.
  719. //
  720. SepRmReleaseDbWriteLock(SessionArrayIndex);
  721. return STATUS_BAD_LOGON_SESSION_STATE;
  722. }
  723. Previous = &Current->Next;
  724. Current = *Previous;
  725. }
  726. SepRmReleaseDbWriteLock(SessionArrayIndex);
  727. //
  728. // Someone asked us to delete a logon session that isn't
  729. // in the database.
  730. //
  731. return STATUS_NO_SUCH_LOGON_SESSION;
  732. }
  733. VOID
  734. SepInformLsaOfDeletedLogon(
  735. IN PLUID LogonId
  736. )
  737. /*++
  738. Routine Description:
  739. This routine informs the LSA about the deletion of a logon session.
  740. Note that we can not be guaranteed that we are in a whole (or wholesome)
  741. thread, since we may be in the middle of process deletion and object
  742. rundown. Therefore, we must queue the work off to a worker thread which
  743. can then make an LPC call to the LSA.
  744. Arguments:
  745. LogonId - Pointer to the logon session ID which has been deleted.
  746. Return Value:
  747. None.
  748. --*/
  749. {
  750. PSEP_LSA_WORK_ITEM DeleteLogonItem;
  751. PAGED_CODE();
  752. //
  753. // Pass the LUID value along with the work queue item.
  754. // Note that the worker thread is responsible for freeing the WorkItem data
  755. // structure.
  756. //
  757. DeleteLogonItem = ExAllocatePoolWithTag( PagedPool, sizeof(SEP_LSA_WORK_ITEM), 'wLeS' );
  758. if (DeleteLogonItem == NULL) {
  759. //
  760. // I don't know what to do here... we loose track of a logon session,
  761. // but the system isn't really harmed in any way.
  762. //
  763. ASSERT("Failed to allocate DeleteLogonItem." && FALSE);
  764. return;
  765. }
  766. DeleteLogonItem->CommandParams.LogonId = (*LogonId);
  767. DeleteLogonItem->CommandNumber = LsapLogonSessionDeletedCommand;
  768. DeleteLogonItem->CommandParamsLength = sizeof( LUID );
  769. DeleteLogonItem->ReplyBuffer = NULL;
  770. DeleteLogonItem->ReplyBufferLength = 0;
  771. DeleteLogonItem->CleanupFunction = NULL;
  772. DeleteLogonItem->CleanupParameter = 0;
  773. DeleteLogonItem->Tag = SepDeleteLogon;
  774. DeleteLogonItem->CommandParamsMemoryType = SepRmImmediateMemory;
  775. if (!SepQueueWorkItem( DeleteLogonItem, TRUE )) {
  776. ExFreePool( DeleteLogonItem );
  777. }
  778. return;
  779. }
  780. NTSTATUS
  781. SeRegisterLogonSessionTerminatedRoutine(
  782. IN PSE_LOGON_SESSION_TERMINATED_ROUTINE CallbackRoutine
  783. )
  784. /*++
  785. Routine Description:
  786. This routine is called by file systems that are interested in being
  787. notified when a logon session is being deleted.
  788. Arguments:
  789. CallbackRoutine - Address of routine to call back when a logon session
  790. is being deleted.
  791. Return Value:
  792. STATUS_SUCCESS - Successfully registered routine
  793. STATUS_INVALID_PARAMETER - CallbackRoutine is NULL
  794. STATUS_INSUFFICIENT_RESOURCE - Unable to allocate list entry.
  795. --*/
  796. {
  797. PSEP_LOGON_SESSION_TERMINATED_NOTIFICATION NewCallback;
  798. PAGED_CODE();
  799. if (CallbackRoutine == NULL) {
  800. return( STATUS_INVALID_PARAMETER );
  801. }
  802. NewCallback = ExAllocatePoolWithTag(
  803. PagedPool | POOL_COLD_ALLOCATION,
  804. sizeof(SEP_LOGON_SESSION_TERMINATED_NOTIFICATION),
  805. 'SFeS');
  806. if (NewCallback == NULL) {
  807. return( STATUS_INSUFFICIENT_RESOURCES );
  808. }
  809. SepRmAcquireDbWriteLock(SEP_HARDCODED_LOCK_INDEX);
  810. NewCallback->Next = SeFileSystemNotifyRoutinesHead.Next;
  811. NewCallback->CallbackRoutine = CallbackRoutine;
  812. SeFileSystemNotifyRoutinesHead.Next = NewCallback;
  813. SepRmReleaseDbWriteLock(SEP_HARDCODED_LOCK_INDEX);
  814. return( STATUS_SUCCESS );
  815. }
  816. NTSTATUS
  817. SeUnregisterLogonSessionTerminatedRoutine(
  818. IN PSE_LOGON_SESSION_TERMINATED_ROUTINE CallbackRoutine
  819. )
  820. /*++
  821. Routine Description:
  822. This is the dual of SeRegisterLogonSessionTerminatedRoutine. A File System
  823. *MUST* call this before it is unloaded.
  824. Arguments:
  825. CallbackRoutine - Address of routine that was originally passed in to
  826. SeRegisterLogonSessionTerminatedRoutine.
  827. Return Value:
  828. STATUS_SUCCESS - Successfully removed callback routine
  829. STATUS_INVALID_PARAMETER - CallbackRoutine is NULL
  830. STATUS_NOT_FOUND - Didn't find and entry for CallbackRoutine
  831. --*/
  832. {
  833. NTSTATUS Status;
  834. PSEP_LOGON_SESSION_TERMINATED_NOTIFICATION PreviousEntry;
  835. PSEP_LOGON_SESSION_TERMINATED_NOTIFICATION NotifyEntry;
  836. PAGED_CODE();
  837. if (CallbackRoutine == NULL) {
  838. return( STATUS_INVALID_PARAMETER );
  839. }
  840. SepRmAcquireDbWriteLock(SEP_HARDCODED_LOCK_INDEX);
  841. for (PreviousEntry = &SeFileSystemNotifyRoutinesHead,
  842. NotifyEntry = SeFileSystemNotifyRoutinesHead.Next;
  843. NotifyEntry != NULL;
  844. PreviousEntry = NotifyEntry,
  845. NotifyEntry = NotifyEntry->Next) {
  846. if (NotifyEntry->CallbackRoutine == CallbackRoutine)
  847. break;
  848. }
  849. if (NotifyEntry != NULL) {
  850. PreviousEntry->Next = NotifyEntry->Next;
  851. SepRmReleaseDbWriteLock(SEP_HARDCODED_LOCK_INDEX);
  852. ExFreePool( NotifyEntry );
  853. Status = STATUS_SUCCESS;
  854. } else {
  855. SepRmReleaseDbWriteLock(SEP_HARDCODED_LOCK_INDEX);
  856. Status = STATUS_NOT_FOUND;
  857. }
  858. return( Status );
  859. }
  860. NTSTATUS
  861. SeMarkLogonSessionForTerminationNotification(
  862. IN PLUID LogonId
  863. )
  864. /*++
  865. Routine Description:
  866. File systems that have registered for logon-termination notification
  867. can mark logon sessions they are interested in for callback by calling
  868. this routine.
  869. Arguments:
  870. LogonId - The logon id for which the file system should be notified
  871. when the logon session is terminated.
  872. Returns:
  873. Nothing.
  874. --*/
  875. {
  876. ULONG SessionArrayIndex;
  877. PSEP_LOGON_SESSION_REFERENCES *Previous, Current;
  878. PAGED_CODE();
  879. SessionArrayIndex = SepLogonSessionIndex( LogonId );
  880. Previous = &SepLogonSessions[ SessionArrayIndex ];
  881. //
  882. // Protect modification of reference monitor database
  883. //
  884. SepRmAcquireDbWriteLock(SessionArrayIndex);
  885. //
  886. // Now walk the list for our logon session array hash index.
  887. //
  888. Current = *Previous;
  889. while (Current != NULL) {
  890. //
  891. // If we found it, decrement the reference count and return
  892. //
  893. if (RtlEqualLuid( LogonId, &Current->LogonId) ) {
  894. Current->Flags |= SEP_TERMINATION_NOTIFY;
  895. break;
  896. }
  897. Current = Current->Next;
  898. }
  899. SepRmReleaseDbWriteLock(SessionArrayIndex);
  900. return( (Current != NULL) ? STATUS_SUCCESS : STATUS_NOT_FOUND );
  901. }
  902. VOID
  903. SepInformFileSystemsOfDeletedLogon(
  904. IN PLUID LogonId
  905. )
  906. /*++
  907. Routine Description:
  908. This routine informs interested file systems of a deleted logon.
  909. Note that we can not be guaranteed that we are in a whole (or wholesome)
  910. thread, since we may be in the middle of process deletion and object
  911. rundown. Therefore, we must queue the work off to a worker thread.
  912. Arguments:
  913. LogonId - Pointer to the logon session ID which has been deleted.
  914. Return Value:
  915. None.
  916. --*/
  917. {
  918. PSEP_FILE_SYSTEM_NOTIFY_CONTEXT FSNotifyContext;
  919. PAGED_CODE();
  920. FSNotifyContext = ExAllocatePoolWithTag(
  921. NonPagedPool,
  922. sizeof(SEP_FILE_SYSTEM_NOTIFY_CONTEXT),
  923. 'SFeS');
  924. if (FSNotifyContext == NULL) {
  925. //
  926. // I don't know what to do here... file systems will loose track of a
  927. // logon session, but the system isn't really harmed in any way.
  928. //
  929. ASSERT("Failed to allocate FSNotifyContext." && FALSE);
  930. return;
  931. }
  932. FSNotifyContext->LogonId = *LogonId;
  933. ExInitializeWorkItem( &FSNotifyContext->WorkItem,
  934. (PWORKER_THREAD_ROUTINE) SepNotifyFileSystems,
  935. (PVOID) FSNotifyContext);
  936. ExQueueWorkItem( &FSNotifyContext->WorkItem, DelayedWorkQueue );
  937. }
  938. VOID
  939. SepNotifyFileSystems(
  940. IN PVOID Context
  941. )
  942. {
  943. PSEP_FILE_SYSTEM_NOTIFY_CONTEXT FSNotifyContext =
  944. (PSEP_FILE_SYSTEM_NOTIFY_CONTEXT) Context;
  945. PSEP_LOGON_SESSION_TERMINATED_NOTIFICATION NextCallback;
  946. PAGED_CODE();
  947. //
  948. // Protect modification of the list of FS callbacks.
  949. //
  950. SepRmAcquireDbReadLock(SEP_HARDCODED_LOCK_INDEX);
  951. NextCallback = SeFileSystemNotifyRoutinesHead.Next;
  952. while (NextCallback != NULL) {
  953. NextCallback->CallbackRoutine( &FSNotifyContext->LogonId );
  954. NextCallback = NextCallback->Next;
  955. }
  956. SepRmReleaseDbReadLock(SEP_HARDCODED_LOCK_INDEX);
  957. ExFreePool( FSNotifyContext );
  958. }
  959. NTSTATUS
  960. SeGetLogonIdDeviceMap(
  961. IN PLUID pLogonId,
  962. OUT PDEVICE_MAP* ppDevMap
  963. )
  964. /*++
  965. Routine Description:
  966. This routine is called by components that want a handle to the
  967. Device Map for the specified LUID
  968. Arguments:
  969. LogonID - LUID of the user
  970. Return Value:
  971. STATUS_SUCCESS - Successfully registered routine
  972. STATUS_INVALID_PARAMETER - invalid parameter
  973. STATUS_INSUFFICIENT_RESOURCE - Unable to allocate list entry.
  974. --*/
  975. {
  976. PSEP_LOGON_SESSION_REFERENCES *Previous, Current;
  977. ULONG SessionArrayIndex;
  978. LONG OldValue;
  979. PAGED_CODE();
  980. if( pLogonId == NULL ) {
  981. return( STATUS_INVALID_PARAMETER );
  982. }
  983. if( ppDevMap == NULL ) {
  984. return( STATUS_INVALID_PARAMETER );
  985. }
  986. SessionArrayIndex = SepLogonSessionIndex( pLogonId );
  987. Previous = &SepLogonSessions[ SessionArrayIndex ];
  988. //
  989. // Protect modification of reference monitor database
  990. //
  991. SepRmAcquireDbWriteLock(SessionArrayIndex);
  992. //
  993. // Now walk the list for our logon session array hash index.
  994. //
  995. Current = *Previous;
  996. while (Current != NULL) {
  997. //
  998. // If we found it, return a handle to the device map
  999. //
  1000. if (RtlEqualLuid( pLogonId, &(Current->LogonId) )) {
  1001. NTSTATUS Status;
  1002. Status = STATUS_SUCCESS;
  1003. //
  1004. // Check if the Device Map does not exist for this LUID
  1005. //
  1006. if (Current->pDeviceMap == NULL) {
  1007. WCHAR szString[64]; // \Sessions\0\DosDevices\x-x = 10+1+12+(8)+1+(8)+1 = 41
  1008. OBJECT_ATTRIBUTES Obja;
  1009. UNICODE_STRING UnicodeString, SymLinkUnicodeString;
  1010. HANDLE hDevMap, hSymLink;
  1011. PDEVICE_MAP pDeviceMap = NULL;
  1012. //
  1013. // Drop the lock while we create the devmap
  1014. //
  1015. InterlockedIncrement (&Current->ReferenceCount);
  1016. SepRmReleaseDbWriteLock(SessionArrayIndex);
  1017. _snwprintf( szString,
  1018. (sizeof(szString)/sizeof(WCHAR)) - 1,
  1019. L"\\Sessions\\0\\DosDevices\\%08x-%08x",
  1020. pLogonId->HighPart,
  1021. pLogonId->LowPart );
  1022. RtlInitUnicodeString( &UnicodeString, szString );
  1023. //
  1024. // Device Map for LUID does not exist
  1025. // Create the Device Map for the LUID
  1026. //
  1027. InitializeObjectAttributes( &Obja,
  1028. &UnicodeString,
  1029. OBJ_CASE_INSENSITIVE | OBJ_OPENIF | OBJ_KERNEL_HANDLE,
  1030. NULL,
  1031. NULL );
  1032. Status = ZwCreateDirectoryObject( &hDevMap,
  1033. DIRECTORY_ALL_ACCESS,
  1034. &Obja );
  1035. if (NT_SUCCESS( Status )) {
  1036. //
  1037. // Set the DeviceMap for this directory object
  1038. //
  1039. Status = ObSetDirectoryDeviceMap( &pDeviceMap,
  1040. hDevMap );
  1041. if (NT_SUCCESS( Status )) {
  1042. //
  1043. // Create the Global SymLink to the global DosDevices
  1044. //
  1045. RtlInitUnicodeString( &SymLinkUnicodeString, L"Global" );
  1046. RtlInitUnicodeString( &UnicodeString, L"\\Global??" );
  1047. InitializeObjectAttributes(
  1048. &Obja,
  1049. &SymLinkUnicodeString,
  1050. OBJ_PERMANENT | OBJ_CASE_INSENSITIVE | OBJ_OPENIF | OBJ_KERNEL_HANDLE,
  1051. hDevMap,
  1052. NULL );
  1053. Status = ZwCreateSymbolicLinkObject( &hSymLink,
  1054. SYMBOLIC_LINK_ALL_ACCESS,
  1055. &Obja,
  1056. &UnicodeString );
  1057. if (NT_SUCCESS( Status )) {
  1058. ZwClose( hSymLink );
  1059. }
  1060. else {
  1061. ObfDereferenceDeviceMap(pDeviceMap);
  1062. }
  1063. }
  1064. ZwClose( hDevMap );
  1065. }
  1066. //
  1067. // Reaquire the lock and modify the LUID structures
  1068. //
  1069. SepRmAcquireDbWriteLock(SessionArrayIndex);
  1070. if (!NT_SUCCESS( Status )) {
  1071. *ppDevMap = NULL;
  1072. }
  1073. else {
  1074. if(Current->pDeviceMap == NULL) {
  1075. Current->pDeviceMap = pDeviceMap;
  1076. }
  1077. else {
  1078. ObfDereferenceDeviceMap(pDeviceMap);
  1079. }
  1080. *ppDevMap = Current->pDeviceMap;
  1081. }
  1082. SepRmReleaseDbWriteLock(SessionArrayIndex);
  1083. //
  1084. // Remove the reference we just added
  1085. //
  1086. SepDeReferenceLogonSessionDirect(Current);
  1087. return Status;
  1088. }
  1089. else {
  1090. //
  1091. // Device Map for LUID already exist
  1092. // return the handle to the Device Map
  1093. //
  1094. *ppDevMap = Current->pDeviceMap;
  1095. }
  1096. SepRmReleaseDbWriteLock(SessionArrayIndex);
  1097. return ( Status );
  1098. }
  1099. Current = Current->Next;
  1100. }
  1101. SepRmReleaseDbWriteLock(SessionArrayIndex);
  1102. //
  1103. // Bad news, someone asked us for a device map of
  1104. // a logon session we didn't know existed. This might be a new
  1105. // token being created, so return an error status and let the caller
  1106. // decide if it warrants a bug check or not.
  1107. //
  1108. return STATUS_NO_SUCH_LOGON_SESSION;
  1109. }
  1110. #if DBG || TOKEN_LEAK_MONITOR
  1111. VOID
  1112. SepAddTokenLogonSession(
  1113. IN PTOKEN Token
  1114. )
  1115. /*++
  1116. Routine Description
  1117. Adds SEP_LOGON_SESSION_TOKEN to a reference monitor track.
  1118. Arguments
  1119. Token - token to add
  1120. Return Value
  1121. None
  1122. --*/
  1123. {
  1124. ULONG SessionArrayIndex;
  1125. PSEP_LOGON_SESSION_REFERENCES Current;
  1126. PSEP_LOGON_SESSION_TOKEN TokenTrack = NULL;
  1127. PLUID LogonId = &Token->AuthenticationId;
  1128. PAGED_CODE();
  1129. SessionArrayIndex = SepLogonSessionIndex( LogonId );
  1130. //
  1131. // Protect modification of reference monitor database
  1132. //
  1133. SepRmAcquireDbWriteLock(SessionArrayIndex);
  1134. Current = SepLogonSessions[ SessionArrayIndex ];
  1135. //
  1136. // Now walk the list for our logon session array hash index.
  1137. //
  1138. while (Current != NULL) {
  1139. //
  1140. // If we found it, increment the reference count and return
  1141. //
  1142. if (RtlEqualLuid( LogonId, &Current->LogonId )) {
  1143. //
  1144. // Stick the token address into the track. Find the last in the list of tokens
  1145. // for this session.
  1146. //
  1147. TokenTrack = ExAllocatePoolWithTag(PagedPool, sizeof(SEP_LOGON_SESSION_TOKEN), 'sLeS');
  1148. if (TokenTrack) {
  1149. RtlZeroMemory(TokenTrack, sizeof(SEP_LOGON_SESSION_TOKEN));
  1150. TokenTrack->Token = Token;
  1151. InsertTailList(&Current->TokenList, &TokenTrack->ListEntry);
  1152. }
  1153. SepRmReleaseDbWriteLock(SessionArrayIndex);
  1154. return;
  1155. }
  1156. Current = Current->Next;
  1157. }
  1158. ASSERT(FALSE && L"Failed to add logon session token track.");
  1159. SepRmReleaseDbWriteLock(SessionArrayIndex );
  1160. }
  1161. VOID
  1162. SepRemoveTokenLogonSession(
  1163. IN PTOKEN Token
  1164. )
  1165. /*++
  1166. Routine Description
  1167. Removes a SEP_LOGON_SESSION_TOKEN from a reference monitor logon track.
  1168. Arguments
  1169. Token - token to remove
  1170. Return Value
  1171. None.
  1172. --*/
  1173. {
  1174. ULONG SessionArrayIndex;
  1175. PSEP_LOGON_SESSION_REFERENCES Current;
  1176. PSEP_LOGON_SESSION_TOKEN TokenTrack = NULL;
  1177. PLUID LogonId = &Token->AuthenticationId;
  1178. PLIST_ENTRY ListEntry;
  1179. PAGED_CODE();
  1180. if (Token->TokenFlags & TOKEN_SESSION_NOT_REFERENCED) {
  1181. return;
  1182. }
  1183. SessionArrayIndex = SepLogonSessionIndex( LogonId );
  1184. //
  1185. // Protect modification of reference monitor database
  1186. //
  1187. SepRmAcquireDbWriteLock(SessionArrayIndex);
  1188. Current = SepLogonSessions[ SessionArrayIndex ];
  1189. //
  1190. // Now walk the list for our logon session array hash index.
  1191. //
  1192. while (Current != NULL) {
  1193. if (RtlEqualLuid( LogonId, &Current->LogonId )) {
  1194. //
  1195. // Remove the token from the token list for this session.
  1196. //
  1197. ListEntry = Current->TokenList.Flink;
  1198. while (ListEntry != &Current->TokenList) {
  1199. TokenTrack = CONTAINING_RECORD (ListEntry, SEP_LOGON_SESSION_TOKEN, ListEntry);
  1200. if (TokenTrack->Token == Token) {
  1201. RemoveEntryList (ListEntry);
  1202. SepRmReleaseDbWriteLock(SessionArrayIndex);
  1203. ExFreePool(TokenTrack);
  1204. TokenTrack = NULL;
  1205. return;
  1206. }
  1207. ListEntry = ListEntry->Flink;
  1208. }
  1209. }
  1210. Current = Current->Next;
  1211. }
  1212. #if DBG
  1213. DbgPrint("Failed to delete logon session token track.");
  1214. #endif
  1215. SepRmReleaseDbWriteLock(SessionArrayIndex);
  1216. }
  1217. #endif
  1218. #ifdef ALLOC_DATA_PRAGMA
  1219. #pragma data_seg()
  1220. #endif