Windows2000/private/ntos/se/rmlogon.c
2020-09-30 17:12:32 +02:00

670 lines
22 KiB
C

/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
rmlogon.c
Abstract:
This module implements the kernel mode logon tracking performed by the
reference monitor. Logon tracking is performed by keeping a count of
how many tokens exist for each active logon in a system. When a logon
session's reference count drops to zero, the LSA is notified so that
authentication packages can clean up any related context data.
Author:
Jim Kelly (JimK) 21-April-1991
Environment:
Kernel mode only.
--*/
//#define SEP_TRACK_LOGON_SESSION_REFS
#include "rmp.h"
#include <bugcodes.h>
SEP_LOGON_SESSION_TERMINATED_NOTIFICATION
SeFileSystemNotifyRoutinesHead = {0};
// Internally defined data types //
typedef struct _SEP_FILE_SYSTEM_NOTIFY_CONTEXT {
WORK_QUEUE_ITEM WorkItem;
LUID LogonId;
} SEP_FILE_SYSTEM_NOTIFY_CONTEXT, * PSEP_FILE_SYSTEM_NOTIFY_CONTEXT;
// Internally defined routines //
NTSTATUS SepGetLogonSessionTrack(IN PLUID LogonId);
VOID SepInformLsaOfDeletedLogon(IN PLUID LogonId);
VOID SepInformFileSystemsOfDeletedLogon(IN PLUID LogonId);
VOID SepNotifyFileSystems(IN PVOID Context);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE,SeRegisterLogonSessionTerminatedRoutine)
#pragma alloc_text(PAGE,SeUnregisterLogonSessionTerminatedRoutine)
#pragma alloc_text(PAGE,SeMarkLogonSessionForTerminationNotification)
#pragma alloc_text(PAGE,SepRmCreateLogonSessionWrkr)
#pragma alloc_text(PAGE,SepRmDeleteLogonSessionWrkr)
#pragma alloc_text(PAGE,SepReferenceLogonSession)
#pragma alloc_text(PAGE,SepDeReferenceLogonSession)
#pragma alloc_text(PAGE,SepCreateLogonSessionTrack)
#pragma alloc_text(PAGE,SepDeleteLogonSessionTrack)
#pragma alloc_text(PAGE,SepInformLsaOfDeletedLogon)
#pragma alloc_text(PAGE,SepInformFileSystemsOfDeletedLogon)
#pragma alloc_text(PAGE,SepNotifyFileSystems)
#endif
// Local macros //
// This macro is used to obtain an index into the logon session tracking
// array given a logon session ID (a LUID).
#define SepLogonSessionIndex( PLogonId ) ( \
(PLogonId)->LowPart & SEP_LOGON_TRACK_INDEX_MASK \
)
// Exported Services //
VOID SepRmCreateLogonSessionWrkr(IN PRM_COMMAND_MESSAGE CommandMessage, OUT PRM_REPLY_MESSAGE ReplyMessage)
/*++
Routine Description:
This function is the dispatch routine for the LSA --> RM
"CreateLogonSession" call.
The arguments passed to this routine are defined by the type SEP_RM_COMMAND_WORKER.
Arguments:
CommandMessage - Points to structure containing RM command message
information consisting of an LPC PORT_MESSAGE structure followed
by the command number (RmComponentTestCommand) and a command-specific
body. The command-specific body of this parameter is a LUID of the logon session to be created.
ReplyMessage - Pointer to structure containing LSA reply message
information consisting of an LPC PORT_MESSAGE structure followed
by the command ReturnedStatus field in which a status code from the command will be returned.
Return Value:
VOID
--*/
{
NTSTATUS Status;
LUID LogonId;
PAGED_CODE();
// Check that command is expected type
ASSERT(CommandMessage->CommandNumber == RmCreateLogonSession);
// Typecast the command parameter to what we expect.
LogonId = *((LUID UNALIGNED*) CommandMessage->CommandParams);
// Try to create the logon session tracking record
Status = SepCreateLogonSessionTrack(&LogonId);
// Set the reply status
ReplyMessage->ReturnedStatus = Status;
return;
}
VOID SepRmDeleteLogonSessionWrkr(IN PRM_COMMAND_MESSAGE CommandMessage, OUT PRM_REPLY_MESSAGE ReplyMessage)
/*++
Routine Description:
This function is the dispatch routine for the LSA --> RM
"DeleteLogonSession" call.
The arguments passed to this routine are defined by the type SEP_RM_COMMAND_WORKER.
Arguments:
CommandMessage - Points to structure containing RM command message
information consisting of an LPC PORT_MESSAGE structure followed
by the command number (RmComponentTestCommand) and a command-specific
body. The command-specific body of this parameter is a LUID of the logon session to be created.
ReplyMessage - Pointer to structure containing LSA reply message
information consisting of an LPC PORT_MESSAGE structure followed
by the command ReturnedStatus field in which a status code from the command will be returned.
Return Value:
VOID
--*/
{
NTSTATUS Status;
LUID LogonId;
PAGED_CODE();
// Check that command is expected type
ASSERT(CommandMessage->CommandNumber == RmDeleteLogonSession);
// Typecast the command parameter to what we expect.
LogonId = *((LUID UNALIGNED*) CommandMessage->CommandParams);
// Try to create the logon session tracking record
Status = SepDeleteLogonSessionTrack(&LogonId);
// Set the reply status
ReplyMessage->ReturnedStatus = Status;
return;
}
NTSTATUS SepReferenceLogonSession(IN PLUID LogonId)
/*++
Routine Description:
This routine increments the reference count of a logon session tracking record.
Arguments:
LogonId - Pointer to the logon session ID whose logon track is to be incremented.
Return Value:
STATUS_SUCCESS - The reference count was successfully incremented.
STATUS_NO_SUCH_LOGON_SESSION - The specified logon session doesn't exist in the reference monitor's database.
--*/
{
ULONG SessionArrayIndex;
PSEP_LOGON_SESSION_REFERENCES Previous, Current;
#ifdef SEP_TRACK_LOGON_SESSION_REFS
ULONG Refs;
#endif //SEP_TRACK_LOGON_SESSION_REFS
PAGED_CODE();
SessionArrayIndex = SepLogonSessionIndex(LogonId);
// Protect modification of reference monitor database
SepRmAcquireDbWriteLock();
// Now walk the list for our logon session array hash index.
Previous = (PSEP_LOGON_SESSION_REFERENCES)((PVOID)&SepLogonSessions[SessionArrayIndex]);
Current = Previous->Next;
while (Current != NULL) {
// If we found it, increment the reference count and return
if (RtlEqualLuid(LogonId, &Current->LogonId)) {
#ifdef SEP_TRACK_LOGON_SESSION_REFS
ULONG Refs;
#endif //SEP_TRACK_LOGON_SESSION_REFS
Current->ReferenceCount += 1;
#ifdef SEP_TRACK_LOGON_SESSION_REFS
Refs = Current->ReferenceCount;
#endif //SEP_TRACK_LOGON_SESSION_REFS
SepRmReleaseDbWriteLock();
#ifdef SEP_TRACK_LOGON_SESSION_REFS
DbgPrint("SE (rm): ++ logon session: (%d, %d) to %d by (%d, %d)\n",
LogonId->HighPart, LogonId->LowPart, Refs,
PsGetCurrentThread()->Cid.UniqueProcess,
PsGetCurrentThread()->Cid.UniqueThread);
#endif //SEP_TRACK_LOGON_SESSION_REFS
return STATUS_SUCCESS;
}
Previous = Current;
Current = Current->Next;
}
SepRmReleaseDbWriteLock();
// Bad news, someone asked us to increment the reference count of
// a logon session we didn't know existed. This might be a new
// token being created, so return an error status and let the caller
// decide if it warrants a bug check or not.
return STATUS_NO_SUCH_LOGON_SESSION;
}
VOID SepDeReferenceLogonSession(IN PLUID LogonId)
/*++
Routine Description:
This routine decrements the reference count of a logon session tracking record.
If the reference count is decremented to zero, then there is no
possibility for any more tokens to exist for the logon session.
In this case, the LSA is notified that a logon session has terminated.
Arguments:
LogonId - Pointer to the logon session ID whose logon track is to be decremented.
Return Value:
None.
--*/
{
ULONG SessionArrayIndex;
PSEP_LOGON_SESSION_REFERENCES Previous, Current;
#ifdef SEP_TRACK_LOGON_SESSION_REFS
ULONG Refs;
#endif //SEP_TRACK_LOGON_SESSION_REFS
PAGED_CODE();
SessionArrayIndex = SepLogonSessionIndex(LogonId);
// Protect modification of reference monitor database
SepRmAcquireDbWriteLock();
// Now walk the list for our logon session array hash index.
Previous = (PSEP_LOGON_SESSION_REFERENCES)((PVOID)&SepLogonSessions[SessionArrayIndex]);
Current = Previous->Next;
while (Current != NULL) {
// If we found it, decrement the reference count and return
if (RtlEqualLuid(LogonId, &Current->LogonId)) {
Current->ReferenceCount -= 1;
if (Current->ReferenceCount == 0) {
// Pull it from the list
Previous->Next = Current->Next;
// No longer need to protect our pointer to this record.
SepRmReleaseDbWriteLock();
// Asynchronoously inform file systems that this logon session
// is going away, if atleast one FS expressed interest in this logon session.
if (Current->Flags & SEP_TERMINATION_NOTIFY) {
SepInformFileSystemsOfDeletedLogon(LogonId);
}
// Deallocate the logon session track record.
ExFreePool((PVOID)Current);
#ifdef SEP_TRACK_LOGON_SESSION_REFS
DbgPrint("SE (rm): -- ** logon session: (%d, %d) to ZERO by (%d, %d)\n",
LogonId->HighPart, LogonId->LowPart,
PsGetCurrentThread()->Cid.UniqueProcess,
PsGetCurrentThread()->Cid.UniqueThread);
#endif //SEP_TRACK_LOGON_SESSION_REFS
// Inform the LSA about the deletion of this logon session.
SepInformLsaOfDeletedLogon(LogonId);
return;
}
// reference count was incremented, but not to zero.
#ifdef SEP_TRACK_LOGON_SESSION_REFS
Refs = Current->ReferenceCount;
#endif //SEP_TRACK_LOGON_SESSION_REFS
SepRmReleaseDbWriteLock();
#ifdef SEP_TRACK_LOGON_SESSION_REFS
DbgPrint("SE (rm): -- logon session: (%d, %d) to %d by (%d, %d)\n",
LogonId->HighPart, LogonId->LowPart, Refs,
PsGetCurrentThread()->Cid.UniqueProcess,
PsGetCurrentThread()->Cid.UniqueThread);
#endif //SEP_TRACK_LOGON_SESSION_REFS
return;
}
Previous = Current;
Current = Current->Next;
}
SepRmReleaseDbWriteLock();
// Bad news, someone asked us to decrement the reference count of a logon session we didn't know existed.
KeBugCheck(DEREF_UNKNOWN_LOGON_SESSION);
return;
}
NTSTATUS SepCreateLogonSessionTrack(IN PLUID LogonId)
/*++
Routine Description:
This routine creates a new logon session tracking record.
This should only be called as a dispatch routine for a LSA->RM
call (and once during system initialization).
If the specified logon session already exists, then an error is returned.
Arguments:
LogonId - Pointer to the logon session ID for which a new logon track is to be created.
Return Value:
STATUS_SUCCESS - The logon session track was created successfully.
STATUS_LOGON_SESSION_EXISTS - The logon session already exists.
A new one has not been created.
--*/
{
ULONG SessionArrayIndex;
PSEP_LOGON_SESSION_REFERENCES Previous, Current;
PSEP_LOGON_SESSION_REFERENCES LogonSessionTrack;
PAGED_CODE();
// Make sure we can allocate a new logon session track record
LogonSessionTrack = (PSEP_LOGON_SESSION_REFERENCES)
ExAllocatePoolWithTag(PagedPool, sizeof(SEP_LOGON_SESSION_REFERENCES), 'sLeS');
if (LogonSessionTrack == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
LogonSessionTrack->LogonId = (*LogonId);
LogonSessionTrack->ReferenceCount = 0;
SessionArrayIndex = SepLogonSessionIndex(LogonId);
// Protect modification of reference monitor database
SepRmAcquireDbWriteLock();
// Now walk the list for our logon session array hash index
// looking for a duplicate logon session ID.
Previous = (PSEP_LOGON_SESSION_REFERENCES)((PVOID)&SepLogonSessions[SessionArrayIndex]);
Current = Previous->Next;
while (Current != NULL) {
if (RtlEqualLuid(LogonId, &Current->LogonId)) {
// One already exists. Hmmm.
SepRmReleaseDbWriteLock();
ExFreePool(LogonSessionTrack);
return STATUS_LOGON_SESSION_EXISTS;
}
Previous = Current;
Current = Current->Next;
}
// Reached the end of the list without finding a duplicate.
// Add the new one.
LogonSessionTrack->Next = SepLogonSessions[SessionArrayIndex];
SepLogonSessions[SessionArrayIndex] = LogonSessionTrack;
SepRmReleaseDbWriteLock();
return STATUS_SUCCESS;
}
NTSTATUS SepDeleteLogonSessionTrack(IN PLUID LogonId)
/*++
Routine Description:
This routine creates a new logon session tracking record.
This should only be called as a dispatch routine for a LSA->RM
call (and once during system initialization).
If the specified logon session already exists, then an error is returned.
Arguments:
LogonId - Pointer to the logon session ID whose logon track is to be deleted.
Return Value:
STATUS_SUCCESS - The logon session track was deleted successfully.
STATUS_BAD_LOGON_SESSION_STATE - The logon session has a non-zero reference count and can not be deleted.
STATUS_NO_SUCH_LOGON_SESSION - The specified logon session does not exist.
--*/
{
ULONG SessionArrayIndex;
PSEP_LOGON_SESSION_REFERENCES Previous, Current;
PAGED_CODE();
SessionArrayIndex = SepLogonSessionIndex(LogonId);
// Protect modification of reference monitor database
SepRmAcquireDbWriteLock();
// Now walk the list for our logon session array hash index.
Previous = (PSEP_LOGON_SESSION_REFERENCES)((PVOID)&SepLogonSessions[SessionArrayIndex]);
Current = Previous->Next;
while (Current != NULL) {
// If we found it, make sure reference count is zero
if (RtlEqualLuid(LogonId, &Current->LogonId)) {
if (Current->ReferenceCount == 0) {
// Pull it from the list
Previous->Next = Current->Next;
// No longer need to protect our pointer to this record.
SepRmReleaseDbWriteLock();
// Deallocate the logon session track record.
ExFreePool((PVOID)Current);
return STATUS_SUCCESS;
}
// reference count was not zero. This is not considered
// a healthy situation. Return an error and let someone
// else declare the bug check.
SepRmReleaseDbWriteLock();
return STATUS_BAD_LOGON_SESSION_STATE;
}
Previous = Current;
Current = Current->Next;
}
SepRmReleaseDbWriteLock();
// Someone asked us to delete a logon session that isn't in the database.
return STATUS_NO_SUCH_LOGON_SESSION;
}
VOID SepInformLsaOfDeletedLogon(IN PLUID LogonId)
/*++
Routine Description:
This routine informs the LSA about the deletion of a logon session.
Note that we can not be guaranteed that we are in a whole (or wholesome)
thread, since we may be in the middle of process deletion and object
rundown. Therefore, we must queue the work off to a worker thread which
can then make an LPC call to the LSA.
Arguments:
LogonId - Pointer to the logon session ID which has been deleted.
Return Value:
None.
--*/
{
PSEP_LSA_WORK_ITEM DeleteLogonItem;
PAGED_CODE();
// Pass the LUID value along with the work queue item.
// Note that the worker thread is responsible for freeing the WorkItem data structure.
DeleteLogonItem = ExAllocatePoolWithTag(PagedPool, sizeof(SEP_LSA_WORK_ITEM), 'wLeS');
if (DeleteLogonItem == NULL) {
// I don't know what to do here... we loose track of a logon session,
// but the system isn't really harmed in any way.
return;
}
DeleteLogonItem->CommandParams.LogonId = (*LogonId);
DeleteLogonItem->CommandNumber = LsapLogonSessionDeletedCommand;
DeleteLogonItem->CommandParamsLength = sizeof(LUID);
DeleteLogonItem->ReplyBuffer = NULL;
DeleteLogonItem->ReplyBufferLength = 0;
DeleteLogonItem->CleanupFunction = NULL;
DeleteLogonItem->CleanupParameter = 0;
DeleteLogonItem->Tag = SepDeleteLogon;
DeleteLogonItem->CommandParamsMemoryType = SepRmImmediateMemory;
if (!SepQueueWorkItem(DeleteLogonItem, TRUE)) {
ExFreePool(DeleteLogonItem);
}
return;
}
NTSTATUS SeRegisterLogonSessionTerminatedRoutine(IN PSE_LOGON_SESSION_TERMINATED_ROUTINE CallbackRoutine)
/*++
Routine Description:
This routine is called by file systems that are interested in being notified when a logon session is being deleted.
Arguments:
CallbackRoutine - Address of routine to call back when a logon session is being deleted.
Return Value:
STATUS_SUCCESS - Successfully registered routine
STATUS_INVALID_PARAMETER - CallbackRoutine is NULL
STATUS_INSUFFICIENT_RESOURCE - Unable to allocate list entry.
--*/
{
PSEP_LOGON_SESSION_TERMINATED_NOTIFICATION NewCallback;
PAGED_CODE();
if (CallbackRoutine == NULL) {
return(STATUS_INVALID_PARAMETER);
}
NewCallback = ExAllocatePoolWithTag(PagedPool, sizeof(SEP_LOGON_SESSION_TERMINATED_NOTIFICATION), 'SFeS');
if (NewCallback == NULL) {
return(STATUS_INSUFFICIENT_RESOURCES);
}
SepRmAcquireDbWriteLock();
NewCallback->Next = SeFileSystemNotifyRoutinesHead.Next;
NewCallback->CallbackRoutine = CallbackRoutine;
SeFileSystemNotifyRoutinesHead.Next = NewCallback;
SepRmReleaseDbWriteLock();
return(STATUS_SUCCESS);
}
NTSTATUS SeUnregisterLogonSessionTerminatedRoutine(IN PSE_LOGON_SESSION_TERMINATED_ROUTINE CallbackRoutine)
/*++
Routine Description:
This is the dual of SeRegisterLogonSessionTerminatedRoutine.
A File System *MUST* call this before it is unloaded.
Arguments:
CallbackRoutine - Address of routine that was originally passed in to SeRegisterLogonSessionTerminatedRoutine.
Return Value:
STATUS_SUCCESS - Successfully removed callback routine
STATUS_INVALID_PARAMETER - CallbackRoutine is NULL
STATUS_NOT_FOUND - Didn't find and entry for CallbackRoutine
--*/
{
NTSTATUS Status;
PSEP_LOGON_SESSION_TERMINATED_NOTIFICATION PreviousEntry;
PSEP_LOGON_SESSION_TERMINATED_NOTIFICATION NotifyEntry;
PAGED_CODE();
if (CallbackRoutine == NULL) {
return(STATUS_INVALID_PARAMETER);
}
SepRmAcquireDbWriteLock();
for (PreviousEntry = &SeFileSystemNotifyRoutinesHead, NotifyEntry = SeFileSystemNotifyRoutinesHead.Next;
NotifyEntry != NULL;
PreviousEntry = NotifyEntry, NotifyEntry = NotifyEntry->Next) {
if (NotifyEntry->CallbackRoutine == CallbackRoutine)
break;
}
if (NotifyEntry != NULL) {
PreviousEntry->Next = NotifyEntry->Next;
ExFreePool(NotifyEntry);
Status = STATUS_SUCCESS;
} else {
Status = STATUS_NOT_FOUND;
}
SepRmReleaseDbWriteLock();
return(Status);
}
NTSTATUS SeMarkLogonSessionForTerminationNotification(IN PLUID LogonId)
/*++
Routine Description:
File systems that have registered for logon-termination notification
can mark logon sessions they are interested in for callback by calling this routine.
Arguments:
LogonId - The logon id for which the file system should be notified when the logon session is terminated.
Returns:
Nothing.
--*/
{
ULONG SessionArrayIndex;
PSEP_LOGON_SESSION_REFERENCES Previous, Current;
PAGED_CODE();
SessionArrayIndex = SepLogonSessionIndex(LogonId);
// Protect modification of reference monitor database
SepRmAcquireDbWriteLock();
// Now walk the list for our logon session array hash index.
Previous = (PSEP_LOGON_SESSION_REFERENCES)((PVOID)&SepLogonSessions[SessionArrayIndex]);
Current = Previous->Next;
while (Current != NULL) {
// If we found it, decrement the reference count and return
if (RtlEqualLuid(LogonId, &Current->LogonId)) {
Current->Flags |= SEP_TERMINATION_NOTIFY;
break;
}
Previous = Current;
Current = Current->Next;
}
SepRmReleaseDbWriteLock();
return((Current != NULL) ? STATUS_SUCCESS : STATUS_NOT_FOUND);
}
VOID SepInformFileSystemsOfDeletedLogon(IN PLUID LogonId)
/*++
Routine Description:
This routine informs interested file systems of a deleted logon.
Note that we can not be guaranteed that we are in a whole (or wholesome)
thread, since we may be in the middle of process deletion and object
rundown. Therefore, we must queue the work off to a worker thread.
Arguments:
LogonId - Pointer to the logon session ID which has been deleted.
Return Value:
None.
--*/
{
PSEP_FILE_SYSTEM_NOTIFY_CONTEXT FSNotifyContext;
PAGED_CODE();
FSNotifyContext = ExAllocatePoolWithTag(NonPagedPool, sizeof(SEP_FILE_SYSTEM_NOTIFY_CONTEXT), 'SFeS');
if (FSNotifyContext == NULL) {
// I don't know what to do here... file systems will loose track of a
// logon session, but the system isn't really harmed in any way.
return;
}
FSNotifyContext->LogonId = *LogonId;
ExInitializeWorkItem(&FSNotifyContext->WorkItem, (PWORKER_THREAD_ROUTINE)SepNotifyFileSystems, (PVOID)FSNotifyContext);
ExQueueWorkItem(&FSNotifyContext->WorkItem, DelayedWorkQueue);
}
VOID SepNotifyFileSystems(IN PVOID Context)
{
PSEP_FILE_SYSTEM_NOTIFY_CONTEXT FSNotifyContext = (PSEP_FILE_SYSTEM_NOTIFY_CONTEXT)Context;
PSEP_LOGON_SESSION_TERMINATED_NOTIFICATION NextCallback;
PAGED_CODE();
// Protect modification of the list of FS callbacks.
SepRmAcquireDbReadLock();
NextCallback = SeFileSystemNotifyRoutinesHead.Next;
while (NextCallback != NULL) {
NextCallback->CallbackRoutine(&FSNotifyContext->LogonId);
NextCallback = NextCallback->Next;
}
SepRmReleaseDbReadLock();
ExFreePool(FSNotifyContext);
}