mirror of https://github.com/lianthony/NT4.0
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.
1500 lines
35 KiB
1500 lines
35 KiB
/*++
|
|
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
auloop.c
|
|
|
|
Abstract:
|
|
|
|
Local Security Authority LPC Port listener and API dispatcher.
|
|
|
|
Author:
|
|
|
|
Jim Kelly (JimK) 7-Mar-1991
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "ausrvp.h"
|
|
#include <windows.h>
|
|
|
|
|
|
//
|
|
// These variables are used to control the number of
|
|
// threads used for processing Logon Process calls.
|
|
// They have the following uses:
|
|
//
|
|
// LsapAuActiveThreads - Indicates how many threads are
|
|
// currently active (both free and in-use) for processing
|
|
// Logon Process calls.
|
|
//
|
|
// LsapAuFreeThreads - Indicates how many threads are available
|
|
// to process LPC calls. When this is less than
|
|
// LsapAuFreeThreadsGoal then we create another thread
|
|
// (bounded by LsapAuMaximumThreads).
|
|
//
|
|
// LsapAuFreeThreadsGoal - Indicates how many threads should be kept
|
|
// available to process new LPC calls. This is necessary to
|
|
// prevent a deadlock with csrss.
|
|
//
|
|
// LsapAuMinimumThreads - Indicates the minimum number of
|
|
// threads to have available for processing LogonProcess calls.
|
|
// LsapAuActiveThreads should never be decremented
|
|
// below this value.
|
|
//
|
|
// LsapAuMaximumThreads - Indicates the maximum number of
|
|
// threads to create for processing LogonProcess calls.
|
|
// LsapAuActiveThreads should never be incremented
|
|
// above this value.
|
|
//
|
|
// LsapAuCallsToProcess - Controls how many LPC calls each
|
|
// dynamic thread will serve before exiting. This is
|
|
// used to prevent rampant thread creation/deletion races.
|
|
//
|
|
// Note: The following conditions may cause LsapAuActiveThreads
|
|
// to go outside the Minimum/Maximum bounds:
|
|
//
|
|
// 1) Initialization - We may start out with fewer threads
|
|
// than the minimum.
|
|
//
|
|
// 2) Race conditions at thread exit - we are sloppy (but
|
|
// fast) in dealing with decrementing the Active count.
|
|
//
|
|
// 3) Changes in max or min values - obviously may make the
|
|
// active count outside the max/min range.
|
|
//
|
|
// 4) Race conditions in thread activation - we are sloppy
|
|
// (but fast) in determining exactly how many threads
|
|
// are active and free. This may result in more threads
|
|
// than expected, but won't result in fewer threads
|
|
// than expected.
|
|
//
|
|
|
|
|
|
RTL_RESOURCE LsapAuThreadCountLock;
|
|
LONG LsapAuActiveThreads;
|
|
LONG LsapAuFreeThreads;
|
|
LONG LsapAuFreeThreadsGoal;
|
|
LONG LsapAuMinimumThreads;
|
|
LONG LsapAuMaximumThreads;
|
|
LONG LsapAuCallsToProcess;
|
|
|
|
|
|
|
|
//
|
|
// This event is used to signal the AU Server thread manager that
|
|
// there are additional server threads needed.
|
|
//
|
|
|
|
|
|
HANDLE LsapAuThreadManagementEvent;
|
|
|
|
|
|
|
|
//
|
|
// Authentication API routine dispatch table
|
|
//
|
|
|
|
PLSAP_AU_API_DISPATCH LsapAuApiDispatch[LsapAuMaxApiNumber] = {
|
|
LsapAuApiDispatchLookupPackage,
|
|
LsapAuApiDispatchLogonUser,
|
|
LsapAuApiDispatchCallPackage,
|
|
LsapAuApiDeregisterLogonProcess
|
|
};
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// Prototypes for routines private to this module //
|
|
// //
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
VOID
|
|
LsapAuGetCountOfThreadsToCreate(
|
|
OUT PLONG ThreadsToCreate,
|
|
OUT PLONG ThreadIndex
|
|
);
|
|
|
|
|
|
VOID
|
|
LsapAuProvideWorkerThreads( VOID );
|
|
|
|
|
|
VOID
|
|
LsapAuCreateServerThreads( VOID );
|
|
|
|
|
|
NTSTATUS
|
|
LsapAuThreadManager (
|
|
IN PVOID ThreadParameter
|
|
);
|
|
|
|
|
|
ULONG
|
|
LsapAuInterlockedIncrement(
|
|
IN PULONG Value
|
|
);
|
|
|
|
ULONG
|
|
LsapAuInterlockedDecrement(
|
|
IN PULONG Value
|
|
);
|
|
|
|
ULONG
|
|
LsapAuInterlockedRead(
|
|
IN PULONG Value
|
|
);
|
|
|
|
|
|
NTSTATUS
|
|
LsapAuCreatePortSD(
|
|
PSECURITY_DESCRIPTOR * SecurityDescriptor
|
|
);
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
LsapAuHandleConnectionRequest(
|
|
IN PLSAP_AU_API_MESSAGE Message
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This loop waits for connection requests from logon processes.
|
|
|
|
When such a connection is received, the caller is validated as
|
|
being a logon process. A logon process is a process with the
|
|
SeTcbPrivilege. If the connect request message is all zeroes,
|
|
than an untrusted connection is created.
|
|
|
|
Arguments:
|
|
|
|
ThreadParameter - Not used.
|
|
|
|
Return Value:
|
|
|
|
Success - but this thread never exits.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
PLSAP_LOGON_PROCESS LogonProcessContext;
|
|
REMOTE_PORT_VIEW ClientView;
|
|
PLSAP_AU_REGISTER_CONNECT_INFO ConnectInfo;
|
|
BOOLEAN Accept;
|
|
HANDLE CommPort;
|
|
|
|
ConnectInfo = &Message->ConnectionRequest;
|
|
|
|
//
|
|
// Validate that the caller has the SeTcbPrivilege.
|
|
// If valid, a new logon process context is automatically
|
|
// created.
|
|
//
|
|
// Pass in ConnectInfo so we can extract the name of the
|
|
// logon process.
|
|
//
|
|
|
|
ConnectInfo->CompletionStatus = LsapValidLogonProcess(
|
|
&Message->PortMessage.ClientId,
|
|
ConnectInfo,
|
|
&LogonProcessContext
|
|
);
|
|
|
|
|
|
if ( ConnectInfo->CompletionStatus == STATUS_SUCCESS ) {
|
|
Accept = TRUE;
|
|
#ifdef LSAP_AU_TRACK_CONTEXT
|
|
DbgPrint("New: 0x%lx\n", LogonProcessContext);
|
|
#endif //LSAP_AU_TRACK_CONTEXT
|
|
} else {
|
|
Accept = FALSE;
|
|
}
|
|
|
|
|
|
|
|
ClientView.Length = sizeof(ClientView);
|
|
Status = NtAcceptConnectPort(
|
|
&CommPort,
|
|
(PVOID *) LogonProcessContext,
|
|
(PPORT_MESSAGE) Message,
|
|
Accept,
|
|
NULL,
|
|
&ClientView
|
|
);
|
|
#if DBG
|
|
if (!NT_SUCCESS(Status)) {
|
|
DbgPrint("LsaSrv: (Register) NtAcceptConnectPort failed: 0x%lx\n", Status);
|
|
}
|
|
#endif //DBG
|
|
ASSERT( NT_SUCCESS(Status) );
|
|
|
|
if ( Accept ) {
|
|
|
|
|
|
LogonProcessContext->CommPort = CommPort;
|
|
|
|
|
|
//
|
|
// Add this context to our list of valid logon process contexts.
|
|
// This has to happen before the call to complete the port because
|
|
// calls may come in while the port is being completed.
|
|
//
|
|
|
|
LsapAuAddClientContext(LogonProcessContext);
|
|
|
|
|
|
//
|
|
// And complete the connection.
|
|
//
|
|
|
|
Status = NtCompleteConnectPort(CommPort);
|
|
ASSERT( NT_SUCCESS(Status) );
|
|
|
|
//
|
|
// We don't need to access the context any more, so
|
|
// dereference it.
|
|
//
|
|
|
|
LsapAuDereferenceClientContext( LogonProcessContext );
|
|
|
|
}
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapAuServerLoop (
|
|
IN PVOID ThreadParameter
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the main listener loop for calls to the LSA from
|
|
logon processes. This routine dispatches LSA authentication
|
|
requests to their dispatch routines.
|
|
|
|
Arguments:
|
|
|
|
ThreadParameter - Indicates how many active threads there currently
|
|
are.
|
|
|
|
Return Value:
|
|
|
|
Success - but it never exits.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS
|
|
Status;
|
|
|
|
LSAP_AU_API_MESSAGE
|
|
Request;
|
|
|
|
PLSAP_AU_API_MESSAGE
|
|
Reply;
|
|
|
|
LSAP_CLIENT_REQUEST
|
|
ClientRequest;
|
|
|
|
LONG
|
|
ThreadIndex,
|
|
FreeCount,
|
|
ActiveCount;
|
|
|
|
BOOLEAN
|
|
ExitWhenDone = FALSE;
|
|
|
|
ULONG
|
|
KeepAroundForAWhile;
|
|
|
|
BOOLEAN
|
|
ContextFound,
|
|
InvalidateContext,
|
|
TrustedClient;
|
|
|
|
|
|
#if LSAP_DIAGNOSTICS
|
|
LUID
|
|
ThreadLuid;
|
|
|
|
Status = NtAllocateLocallyUniqueId( &ThreadLuid );
|
|
ASSERT(NT_SUCCESS(Status));
|
|
#endif //LSAP_DIAGNOSTICS
|
|
|
|
|
|
//
|
|
// Don't exit right away - handle bursts.
|
|
//
|
|
|
|
KeepAroundForAWhile = LsapAuCallsToProcess;
|
|
|
|
|
|
//
|
|
// Increment the active server thread count
|
|
//
|
|
|
|
ThreadIndex = (ULONG)(ThreadParameter);
|
|
if (ThreadIndex > LsapAuMinimumThreads) {
|
|
ExitWhenDone = TRUE;
|
|
|
|
LsapDiagPrint( AU_TRACK_THREADS,
|
|
("Lsa (au): Server thread (%lx, %lx) created and will exit when done.\n"
|
|
" ThreadIndex: 0x%lx\n",
|
|
ThreadLuid.HighPart, ThreadLuid.LowPart, ThreadIndex) );
|
|
#if LSAP_DIAGNOSTICS
|
|
} else {
|
|
LsapDiagPrint( AU_TRACK_THREADS,
|
|
("Lsa (au): Server thread (%lx, %lx) created and will NOT exit when done.\n"
|
|
" ThreadIndex: 0x%lx\n",
|
|
ThreadLuid.HighPart, ThreadLuid.LowPart, ThreadIndex) );
|
|
#endif //LSAP_DIAGNOSTICS
|
|
}
|
|
|
|
|
|
//
|
|
// Set the client call context to point to the request message buffer.
|
|
//
|
|
|
|
ClientRequest.Request = &Request;
|
|
|
|
|
|
//
|
|
// First time through, there is no reply.
|
|
//
|
|
|
|
Reply = NULL;
|
|
|
|
|
|
//
|
|
// Coming into this thread, the thread is marked as free.
|
|
// However, the loop below expects the thread to be busy,
|
|
// and so it marks it as free before waiting for another
|
|
// message.
|
|
//
|
|
// Mark our thread as busy to meet the initial condition
|
|
// requirements of the loop.
|
|
//
|
|
|
|
FreeCount = LsapAuInterlockedDecrement(&LsapAuFreeThreads);
|
|
|
|
//
|
|
// Now loop indefinitely, processing requests
|
|
//
|
|
|
|
for(;;) {
|
|
|
|
|
|
FreeCount = LsapAuInterlockedIncrement(&LsapAuFreeThreads);
|
|
Status = NtReplyWaitReceivePort(
|
|
LsapAuApiPort,
|
|
(PVOID *) &ClientRequest.LogonProcessContext,
|
|
(PPORT_MESSAGE) Reply,
|
|
(PPORT_MESSAGE) &Request
|
|
);
|
|
|
|
#if LSAP_DIAGNOSTICS
|
|
if (Request.PortMessage.u2.s2.Type != LPC_CONNECTION_REQUEST) {
|
|
if (Request.PortMessage.u2.s2.Type == LPC_PORT_CLOSED) {
|
|
LsapDiagPrint( AU_MESSAGES, ("** Port Closed **\n") );
|
|
LsapDiagPrint( AU_MESSAGES, (" Context = 0x%lx\n",
|
|
ClientRequest.LogonProcessContext) );
|
|
} else if (Request.PortMessage.u2.s2.Type == LPC_CLIENT_DIED) {
|
|
LsapDiagPrint( AU_MESSAGES, ("** Client Died **\n") );
|
|
LsapDiagPrint( AU_MESSAGES, (" Context = 0x%lx\n",
|
|
ClientRequest.LogonProcessContext) );
|
|
} else {
|
|
ASSERT(Request.PortMessage.u2.s2.Type == LPC_REQUEST);
|
|
LsapDiagPrint( AU_MESSAGES, (" Call: ") );
|
|
switch (Request.ApiNumber) {
|
|
case LsapAuLookupPackageApi:
|
|
LsapDiagPrint( AU_MESSAGES, ("Lookup Package\n") );
|
|
break;
|
|
case LsapAuLogonUserApi:
|
|
LsapDiagPrint( AU_MESSAGES, ("Logon User\n") );
|
|
break;
|
|
case LsapAuCallPackageApi:
|
|
LsapDiagPrint( AU_MESSAGES, ("Call Package\n") );
|
|
LsapDiagPrint( AU_MESSAGES, (" Function: TBD\n") );
|
|
break;
|
|
case LsapAuDeregisterLogonProcessApi:
|
|
LsapDiagPrint( AU_MESSAGES, ("Deregister\n") );
|
|
break;
|
|
default:
|
|
LsapDiagPrint( AU_MESSAGES, ("Unknown (%d)\n",
|
|
Request.ApiNumber) );
|
|
break;
|
|
} //end switch
|
|
LsapDiagPrint( AU_MESSAGES, (" Context = 0x%lx\n",
|
|
ClientRequest.LogonProcessContext) );
|
|
}
|
|
}
|
|
#endif //LSAP_DIAGNOSTICS
|
|
|
|
|
|
FreeCount = LsapAuInterlockedDecrement(&LsapAuFreeThreads);
|
|
if (ExitWhenDone == TRUE) {
|
|
KeepAroundForAWhile --;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
// It is highly unusual, but not completely impossible,
|
|
// that a logon process will evaporate from underneath
|
|
// us while we are processing an LPC call.
|
|
//
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
if (Status == STATUS_INVALID_CID) {
|
|
//
|
|
// See if we can find the client's context and delete it.
|
|
//
|
|
|
|
ContextFound = LsapAuReferenceClientContext(
|
|
&ClientRequest,
|
|
TRUE, // Remove context
|
|
&TrustedClient );
|
|
|
|
if (ContextFound) {
|
|
|
|
//
|
|
// Dereferencing the context will cause it to be rundown.
|
|
//
|
|
|
|
LsapAuDereferenceClientContext(ClientRequest.LogonProcessContext);
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Unusual - we got an unexpected error.
|
|
// Continue as best we can.
|
|
//
|
|
|
|
#if DBG
|
|
DbgPrint("\nLsa (au): Unexpected error on NtReplyWaitRecievePort()\n Status = 0x%lx\n", Status);
|
|
#endif //DBG
|
|
}
|
|
|
|
//
|
|
// Do another wait, this time with no reply.
|
|
//
|
|
|
|
Reply = NULL;
|
|
|
|
continue;
|
|
}
|
|
|
|
|
|
//
|
|
// Ensure there are enough server worker threads.
|
|
//
|
|
|
|
LsapAuProvideWorkerThreads();
|
|
|
|
|
|
|
|
|
|
//
|
|
// Dispatch the call . . .
|
|
//
|
|
|
|
if (Request.PortMessage.u2.s2.Type == LPC_CONNECTION_REQUEST) {
|
|
Status = LsapAuHandleConnectionRequest( &Request );
|
|
Reply = NULL;
|
|
|
|
} else if (
|
|
(Request.PortMessage.u2.s2.Type == LPC_PORT_CLOSED) ||
|
|
(Request.PortMessage.u2.s2.Type == LPC_CLIENT_DIED)
|
|
|
|
) {
|
|
|
|
//
|
|
// These messages could be received in any of the following
|
|
// conditions:
|
|
//
|
|
// 1) A bug in the client side has inadvertantly closed
|
|
// the port handle.
|
|
//
|
|
// 2) The LsaDeregisterLogonProcess() call has called
|
|
// lsa, recevied completion status, and then beat
|
|
// lsa in closing the port (a race condition that
|
|
// can not be eliminated).
|
|
//
|
|
// 3) The client has died and the comm port is being
|
|
// rundown as part of process rundown.
|
|
//
|
|
// The first case is a bug, and the client is bound to find
|
|
// out about it real soon. In the second case, we will normally
|
|
// not have a client context left to reference, so looking for it
|
|
// won't hurt anything. So, the correct behaviour here is to
|
|
// try to reference and then delete the client's context.
|
|
|
|
ContextFound = LsapAuReferenceClientContext(
|
|
&ClientRequest,
|
|
TRUE, // Remove context
|
|
&TrustedClient );
|
|
|
|
if (ContextFound) {
|
|
|
|
//
|
|
// Dereferencing the context will cause it to be rundown.
|
|
//
|
|
|
|
LsapAuDereferenceClientContext(ClientRequest.LogonProcessContext);
|
|
}
|
|
|
|
//
|
|
// In any of these cases, there is nobody to reply to
|
|
//
|
|
|
|
Reply = NULL;
|
|
|
|
|
|
} else if (Request.ApiNumber >= LsapAuMaxApiNumber ) {
|
|
|
|
//
|
|
// This is an error in the client
|
|
//
|
|
|
|
#if DBG
|
|
DbgPrint( "LSA AU: Invalid Api Number (%lx)\n",
|
|
Request.ApiNumber
|
|
);
|
|
|
|
#endif
|
|
|
|
Reply = &Request;
|
|
Reply->ReturnedStatus = STATUS_INVALID_SYSTEM_SERVICE;
|
|
|
|
} else if (Request.PortMessage.u2.s2.Type == LPC_REQUEST) {
|
|
|
|
//
|
|
// If this is a Deregister call, then we want to invalidate
|
|
// the client's context when we reference it. Otherwise,
|
|
// leave the context valid.
|
|
//
|
|
|
|
if (Request.ApiNumber == LsapAuDeregisterLogonProcessApi) {
|
|
InvalidateContext = TRUE;
|
|
} else {
|
|
InvalidateContext = FALSE;
|
|
}
|
|
|
|
//
|
|
// Try to refrence the context. If one isn't found,
|
|
// then we must be deleting it in another thread.
|
|
//
|
|
|
|
ContextFound = LsapAuReferenceClientContext(
|
|
&ClientRequest,
|
|
InvalidateContext,
|
|
&TrustedClient);
|
|
|
|
if (ContextFound) {
|
|
|
|
//
|
|
// If the request is to deregister, send a reply and
|
|
// then dereference the context. This will cause it
|
|
// to be deleted since we already invalidated it when
|
|
// we referenced it.
|
|
//
|
|
|
|
if (Request.ApiNumber == LsapAuDeregisterLogonProcessApi) {
|
|
Reply = &Request;
|
|
Reply->ReturnedStatus = STATUS_SUCCESS;
|
|
Status = NtReplyPort(
|
|
LsapAuApiPort,
|
|
(PPORT_MESSAGE) Reply
|
|
);
|
|
|
|
//
|
|
// Make sure when we do the next wait, we don't
|
|
// send another reply.
|
|
//
|
|
|
|
Reply = NULL;
|
|
|
|
//
|
|
// No api to dispatch to, the dereference of the
|
|
// context will do all the necessary work.
|
|
//
|
|
|
|
} else {
|
|
|
|
|
|
//
|
|
// Valid API number other than deregister - dispatch it
|
|
//
|
|
|
|
Status = (LsapAuApiDispatch[Request.ApiNumber])(
|
|
&ClientRequest,
|
|
TrustedClient
|
|
);
|
|
|
|
|
|
Reply = &Request;
|
|
Reply->ReturnedStatus = Status;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Whatever the API number, we now need to dereference
|
|
// the client context we referenced. Note that in the
|
|
// case of a Deregister call, this will cause the context
|
|
// to be rundown.
|
|
//
|
|
|
|
LsapAuDereferenceClientContext(
|
|
ClientRequest.LogonProcessContext);
|
|
}
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
//
|
|
// This is a totally unexpected situation, but we will
|
|
// cover our posterier just in case we come across an
|
|
// unexpected error.
|
|
//
|
|
|
|
Reply = NULL;
|
|
|
|
} // end_if
|
|
|
|
|
|
//
|
|
// There are a number of conditions which require us to
|
|
// send a response before we can again wait for another
|
|
// request. These are:
|
|
//
|
|
// 1) We are a temporary worker thread and it is time for
|
|
// us to exit.
|
|
//
|
|
// 2) We need to create another worker thread. We can't do
|
|
// this while processing an LPC call because we will cause
|
|
// a call to be made to csrss (for "new thread notification")
|
|
// and this might result in a deadlock.
|
|
//
|
|
|
|
|
|
if (ExitWhenDone && (KeepAroundForAWhile == 0)) {
|
|
|
|
//
|
|
// Send a reply if one is required
|
|
//
|
|
|
|
if ( Reply != NULL ) {
|
|
|
|
|
|
Status = NtReplyPort(
|
|
LsapAuApiPort,
|
|
(PPORT_MESSAGE) Reply
|
|
);
|
|
//
|
|
// Don't send a reply when we do the next wait
|
|
//
|
|
|
|
Reply = NULL;
|
|
|
|
#if DBG
|
|
if (!NT_SUCCESS(Status)) {
|
|
DbgPrint("Lsa\\server\\auloop.c: Reply to client failed.\n");
|
|
DbgPrint(" Status: 0x%lx\n", Status);
|
|
}
|
|
#endif //DBG
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// If it is time for this thread to exit, then do that now.
|
|
//
|
|
|
|
if (ExitWhenDone && KeepAroundForAWhile == 0) {
|
|
ActiveCount = LsapAuInterlockedDecrement(&LsapAuActiveThreads);
|
|
|
|
LsapDiagPrint( AU_TRACK_THREADS,
|
|
("Lsa (au): Temporary server thread (%lx, %lx) exiting.\n"
|
|
" Active count decremented to: 0x%lx\n",
|
|
ThreadLuid.HighPart, ThreadLuid.LowPart,
|
|
ActiveCount) );
|
|
#if DBG
|
|
if (ActiveCount == 0) {
|
|
DbgPrint("Lsa\\server\\auloop.c: Last active server thread exiting.\n");
|
|
DbgPrint(" This is bad. Nobody can logon.\n");
|
|
}
|
|
#endif //DBG
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
} // end_for
|
|
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
LsapAuLoopInitialize(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function creates a port for communicating with logon processes.
|
|
It then creates threads to listen for logon process connections
|
|
and to act upon calls from those logon processes.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES PortObjectAttributes;
|
|
STRING PortName;
|
|
UNICODE_STRING UnicodePortName;
|
|
DWORD Ignore;
|
|
HANDLE Thread;
|
|
PSECURITY_DESCRIPTOR PortSD = NULL;
|
|
|
|
Status = LsapAuCreatePortSD(
|
|
&PortSD
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Create the LPC port
|
|
//
|
|
|
|
RtlInitString(&PortName,"\\LsaAuthenticationPort");
|
|
Status = RtlAnsiStringToUnicodeString( &UnicodePortName, &PortName, TRUE );
|
|
ASSERT( NT_SUCCESS(Status) );
|
|
InitializeObjectAttributes(
|
|
&PortObjectAttributes,
|
|
&UnicodePortName,
|
|
0,
|
|
NULL,
|
|
PortSD
|
|
);
|
|
|
|
Status = NtCreatePort(
|
|
&LsapAuApiPort,
|
|
&PortObjectAttributes,
|
|
sizeof(LSAP_AU_REGISTER_CONNECT_INFO),
|
|
sizeof(LSAP_AU_API_MESSAGE),
|
|
sizeof(LSAP_AU_API_MESSAGE) * 32
|
|
);
|
|
RtlFreeUnicodeString( &UnicodePortName );
|
|
ASSERT( NT_SUCCESS(Status) );
|
|
|
|
//
|
|
// Create the Thread Management thread
|
|
//
|
|
|
|
Status = NtCreateEvent(
|
|
&LsapAuThreadManagementEvent,
|
|
EVENT_QUERY_STATE | EVENT_MODIFY_STATE | SYNCHRONIZE,
|
|
NULL,
|
|
SynchronizationEvent,
|
|
FALSE
|
|
);
|
|
|
|
LsapDiagPrint( AU_TRACK_THREADS,
|
|
("Lsa (au): Thread management event created. (0x%lx)\n",
|
|
Status));
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
|
|
//
|
|
// Create the thread management thread
|
|
//
|
|
|
|
Thread = CreateThread(
|
|
NULL,
|
|
0L,
|
|
(LPTHREAD_START_ROUTINE)LsapAuThreadManager,
|
|
0L,
|
|
0L,
|
|
&Ignore
|
|
);
|
|
|
|
|
|
|
|
|
|
//
|
|
// Set up the dynamic thread controls based upon our product type.
|
|
// Workstations have lower limits than servers.
|
|
//
|
|
|
|
RtlInitializeResource(&LsapAuThreadCountLock);
|
|
|
|
LsapAuActiveThreads = 1; // 1 assumes we will create the
|
|
LsapAuFreeThreads = 1; // initial thread below.
|
|
|
|
if (LsapProductType == NtProductWinNt) {
|
|
|
|
LsapAuFreeThreadsGoal = 3;
|
|
LsapAuMinimumThreads = 3;
|
|
LsapAuMaximumThreads = 20;
|
|
LsapAuCallsToProcess = 8;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Server values
|
|
//
|
|
|
|
LsapAuFreeThreadsGoal = 6;
|
|
LsapAuMinimumThreads = 6;
|
|
LsapAuMaximumThreads = 24;
|
|
LsapAuCallsToProcess = 20;
|
|
}
|
|
|
|
LsapDiagPrint( AU_TRACK_THREADS,
|
|
("Lsa (au): Thread tracking values -\n"
|
|
" LsapAuActiveThreads: %ld\n"
|
|
" LsapAuFreeThreads: %ld\n"
|
|
" LsapAuFreeThreadsGoal: %ld\n"
|
|
" LsapAuMinimumThreads: %ld\n"
|
|
" LsapAuMaximumThreads: %ld\n"
|
|
" LsapAuCallsToProcess: %ld\n",
|
|
LsapAuActiveThreads, LsapAuFreeThreads,
|
|
LsapAuFreeThreadsGoal, LsapAuMinimumThreads,
|
|
LsapAuMaximumThreads, LsapAuCallsToProcess ) );
|
|
|
|
|
|
//
|
|
// Create a thread to process connects and requests to our AuApiPort
|
|
//
|
|
|
|
Thread = CreateThread(
|
|
NULL,
|
|
0L,
|
|
(LPTHREAD_START_ROUTINE)LsapAuServerLoop,
|
|
0L, // Initial thread has an index of 0
|
|
0L,
|
|
&Ignore
|
|
);
|
|
|
|
#if DBG
|
|
if (Thread == NULL) {
|
|
DbgPrint("\nLSASS: Couldn't Start Thread To Service Logon Process Calls.\n");
|
|
DbgPrint(" This is bad and will prevent logon to all accounts\n");
|
|
DbgPrint(" DEVL: except the SYSTEM account\n");
|
|
DbgPrint(" Status is: %dl (0x%lx)\n\n", GetLastError(),GetLastError() );
|
|
}
|
|
#endif //DBG
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
LsapAuGetCountOfThreadsToCreate(
|
|
OUT PLONG ThreadsToCreate,
|
|
OUT PLONG ThreadIndex
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function determines how many threads need to be created
|
|
to ensure there are 'LsapAuFreeThreadsGoal' free threads.
|
|
|
|
Overflows are not checked for.
|
|
|
|
|
|
Arguments:
|
|
|
|
ThreadsToCreate - Receives the number of threads that should
|
|
be created.
|
|
|
|
ThreadIndex - Indicates how many threads are already active.
|
|
This value should be incremented for each thread created.
|
|
This value is used by the created threads to determine
|
|
whether they are permanent threads (index <= LsapAuMinimumThreads)
|
|
or temporary threads (index > LsapAuMinumumThreads). This
|
|
value is only returned if ThreadsToCreate is greater than
|
|
zero.
|
|
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
LONG
|
|
CreateCount;
|
|
|
|
//
|
|
// Acquire the interlock
|
|
//
|
|
|
|
(VOID)RtlAcquireResourceExclusive( &LsapAuThreadCountLock, TRUE );
|
|
|
|
if (LsapAuFreeThreads < LsapAuFreeThreadsGoal) {
|
|
CreateCount = LsapAuFreeThreadsGoal - LsapAuFreeThreads;
|
|
LsapAuFreeThreads = LsapAuFreeThreadsGoal;
|
|
(*ThreadIndex) = LsapAuActiveThreads;
|
|
LsapAuActiveThreads += CreateCount;
|
|
} else {
|
|
CreateCount = 0;
|
|
}
|
|
|
|
(*ThreadsToCreate) = CreateCount;
|
|
|
|
|
|
//
|
|
// Release the interlock and return
|
|
//
|
|
|
|
(VOID)RtlReleaseResource( &LsapAuThreadCountLock );
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
LsapAuProvideWorkerThreads( VOID )
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function determines whether or not any worker threads need
|
|
to be created to ensure there are 'LsapAuFreeThreadsGoal' free
|
|
threads.
|
|
|
|
If any need to be created, then an asynchronous request is made
|
|
of our thread management thread to create the additional threads.
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
None - global variables are used.
|
|
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
LONG
|
|
Free,
|
|
FreeGoal,
|
|
Active,
|
|
MaximumThreads;
|
|
|
|
|
|
//
|
|
// Acquire the interlock
|
|
//
|
|
|
|
(VOID)RtlAcquireResourceExclusive( &LsapAuThreadCountLock, TRUE );
|
|
|
|
|
|
Free = LsapAuFreeThreads;
|
|
FreeGoal = LsapAuFreeThreadsGoal;
|
|
Active = LsapAuActiveThreads;
|
|
MaximumThreads = LsapAuMaximumThreads;
|
|
|
|
//
|
|
// Release the interlock
|
|
//
|
|
|
|
(VOID)RtlReleaseResource( &LsapAuThreadCountLock );
|
|
|
|
if ( (Active < MaximumThreads ) &&
|
|
(Free < FreeGoal) ) {
|
|
|
|
//
|
|
// Need to create additional threads
|
|
//
|
|
|
|
LsapAuCreateServerThreads();
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
LsapAuCreateServerThreads( VOID )
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Signal an event that will cause our ThreadManagement thread to
|
|
create more threads if necessary.
|
|
|
|
|
|
Arguments:
|
|
|
|
None - global variables are used.
|
|
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
NTSTATUS
|
|
NtStatus;
|
|
|
|
NtStatus = NtSetEvent( LsapAuThreadManagementEvent, NULL );
|
|
LsapDiagPrint( AU_TRACK_THREADS,
|
|
("Lsa (au): Signalled ThreadManagement event (0x%lx)\n",
|
|
NtStatus));
|
|
ASSERT(NT_SUCCESS(NtStatus));
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
LsapAuThreadManager (
|
|
IN PVOID ThreadParameter
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This thread is used to create AU worker threads when
|
|
needed. It is signaled via an event. When signalled,
|
|
it checks to see if any worker threads need to be created
|
|
and, if so, creates them.
|
|
|
|
Worker thread creation use to be done in the main AU loop
|
|
just after receiving an LPC message. This ended up causing a
|
|
deadlock if CSR was making a remote file access.
|
|
|
|
|
|
Arguments:
|
|
|
|
ThreadParameter - Not used.
|
|
|
|
|
|
Return Value:
|
|
|
|
This thread never exits.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS
|
|
Status,
|
|
Ignore;
|
|
|
|
ULONG
|
|
ThreadsToCreate,
|
|
ThreadIndex,
|
|
i;
|
|
|
|
HANDLE
|
|
Thread;
|
|
|
|
|
|
//
|
|
// Loop forever waiting to be given the opportunity to
|
|
// serve the greater good.
|
|
//
|
|
|
|
for (; ; ) {
|
|
|
|
//
|
|
// Wait to be notified that there is work to be done
|
|
//
|
|
|
|
Status =
|
|
NtWaitForSingleObject( LsapAuThreadManagementEvent, TRUE, NULL);
|
|
|
|
//
|
|
// Now create any threads, if necessary
|
|
//
|
|
|
|
LsapAuGetCountOfThreadsToCreate( &ThreadsToCreate, &ThreadIndex );
|
|
|
|
#if LSAP_DIAGNOSTICS
|
|
if (ThreadsToCreate > 0) {
|
|
LsapDiagPrint( AU_TRACK_THREADS,
|
|
("Lsa (au): creating %ld new server threads.\n",
|
|
ThreadsToCreate) );
|
|
}
|
|
#endif //LSAP_DIAGNOSTICS
|
|
|
|
for (i=0; i < ThreadsToCreate; i++) {
|
|
LsapDiagPrint( AU_TRACK_THREADS,
|
|
("Lsa (au): Server thread %ld created.\n", i) );
|
|
Thread = CreateThread(
|
|
NULL,
|
|
0L,
|
|
(LPTHREAD_START_ROUTINE)LsapAuServerLoop,
|
|
(LPVOID)ThreadIndex++,
|
|
0L,
|
|
&Ignore
|
|
);
|
|
#if DBG
|
|
if (Thread == NULL) {
|
|
DbgPrint("\nLSASS: Couldn't Start auxiliary Thread To Service Logon Process Calls.\n");
|
|
DbgPrint(" This is non fatal and usually indicates a resource\n");
|
|
DbgPrint(" has been depleted.\n");
|
|
DbgPrint(" Thread number: %dl\n", LsapAuActiveThreads);
|
|
DbgPrint(" Status is: %d (0x%lx)\n\n", GetLastError(),GetLastError() );
|
|
}
|
|
#endif //DBG
|
|
(VOID) CloseHandle( Thread );
|
|
}
|
|
|
|
}
|
|
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
|
|
ULONG
|
|
LsapAuInterlockedIncrement(
|
|
IN PULONG Value
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function performs in interlocked increment of the provided
|
|
value, returning the resultant value.
|
|
|
|
Overflows are not checked for.
|
|
|
|
|
|
Arguments:
|
|
|
|
Value - Address of the value to increment.
|
|
|
|
Return Value:
|
|
|
|
The resultant value (following the increment).
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG ReturnValue;
|
|
|
|
//
|
|
// Acquire the interlock
|
|
//
|
|
|
|
(VOID)RtlAcquireResourceExclusive( &LsapAuThreadCountLock, TRUE );
|
|
|
|
|
|
//
|
|
// Increment the value and save the resultant value.
|
|
//
|
|
|
|
(*Value) += 1;
|
|
ReturnValue = (*Value);
|
|
|
|
|
|
//
|
|
// Release the interlock and return
|
|
//
|
|
|
|
(VOID)RtlReleaseResource( &LsapAuThreadCountLock );
|
|
|
|
return(ReturnValue);
|
|
|
|
|
|
}
|
|
|
|
|
|
ULONG
|
|
LsapAuInterlockedDecrement(
|
|
IN PULONG Value
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function performs in interlocked decrement of the provided
|
|
value, returning the resultant value.
|
|
|
|
Overflows are not checked for.
|
|
|
|
|
|
Arguments:
|
|
|
|
Value - Address of the value to decrement.
|
|
|
|
Return Value:
|
|
|
|
The resultant value (following the decrement).
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG ReturnValue;
|
|
|
|
//
|
|
// Acquire the interlock
|
|
//
|
|
|
|
(VOID)RtlAcquireResourceExclusive( &LsapAuThreadCountLock, TRUE );
|
|
|
|
|
|
//
|
|
// Decrement the value and save the resultant value.
|
|
//
|
|
|
|
(*Value) -= 1;
|
|
ReturnValue = (*Value);
|
|
|
|
|
|
//
|
|
// Release the interlock and return
|
|
//
|
|
|
|
(VOID)RtlReleaseResource( &LsapAuThreadCountLock );
|
|
|
|
return(ReturnValue);
|
|
|
|
|
|
}
|
|
|
|
|
|
ULONG
|
|
LsapAuInterlockedRead(
|
|
IN PULONG Value
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function performs in interlocked read of the provided
|
|
value.
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
Value - Address of the value to read.
|
|
|
|
Return Value:
|
|
|
|
The read value.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG ReturnValue;
|
|
|
|
//
|
|
// Acquire the interlock
|
|
//
|
|
|
|
(VOID)RtlAcquireResourceExclusive( &LsapAuThreadCountLock, TRUE );
|
|
|
|
|
|
//
|
|
// Decrement the value and save the resultant value.
|
|
//
|
|
|
|
ReturnValue = (*Value);
|
|
|
|
|
|
//
|
|
// Release the interlock and return
|
|
//
|
|
|
|
(VOID)RtlReleaseResource( &LsapAuThreadCountLock );
|
|
|
|
return(ReturnValue);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
LsapAuCreatePortSD(
|
|
PSECURITY_DESCRIPTOR * SecurityDescriptor
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function creates a security descriptor for the LSA LPC port. It
|
|
grants World PORT_CONNECT access and local system GENERIC_ALL and
|
|
Administrators GENERIC_READ, GENERIC_EXECUTE, and READ_CONTROL access.
|
|
|
|
Arguments:
|
|
|
|
SecurityDescriptor - Receives a pointer to the new security descriptor.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS
|
|
Status;
|
|
|
|
ULONG
|
|
AclLength;
|
|
|
|
PACL
|
|
PortDacl = NULL;
|
|
|
|
|
|
//
|
|
// Set up a default ACLs
|
|
//
|
|
// Public: WORLD:execute, SYSTEM:all, ADMINS:(read|execute|read_control)
|
|
// System: SYSTEM:all, ADMINS:(read|execute|read_control)
|
|
|
|
AclLength = (ULONG)sizeof(ACL) +
|
|
(3*((ULONG)sizeof(ACCESS_ALLOWED_ACE))) +
|
|
RtlLengthSid( LsapLocalSystemSid ) +
|
|
RtlLengthSid( LsapAliasAdminsSid ) +
|
|
RtlLengthSid( LsapWorldSid );
|
|
|
|
|
|
PortDacl = (PACL) LsapAllocateLsaHeap( AclLength );
|
|
if (PortDacl == NULL) {
|
|
return( STATUS_INSUFFICIENT_RESOURCES );
|
|
}
|
|
|
|
*SecurityDescriptor = (PSECURITY_DESCRIPTOR)
|
|
LsapAllocateLsaHeap( sizeof(SECURITY_DESCRIPTOR) );
|
|
|
|
if (*SecurityDescriptor == NULL) {
|
|
LsapFreeLsaHeap( PortDacl );
|
|
return( STATUS_INSUFFICIENT_RESOURCES );
|
|
}
|
|
|
|
|
|
Status = RtlCreateAcl( PortDacl, AclLength, ACL_REVISION2);
|
|
|
|
//
|
|
// WORLD access
|
|
//
|
|
|
|
Status = RtlAddAccessAllowedAce (
|
|
PortDacl,
|
|
ACL_REVISION2,
|
|
PORT_CONNECT,
|
|
LsapWorldSid
|
|
);
|
|
ASSERT( NT_SUCCESS(Status) );
|
|
|
|
|
|
//
|
|
// SYSTEM access
|
|
|
|
//
|
|
|
|
Status = RtlAddAccessAllowedAce (
|
|
PortDacl,
|
|
ACL_REVISION2,
|
|
GENERIC_ALL,
|
|
LsapLocalSystemSid
|
|
);
|
|
ASSERT( NT_SUCCESS(Status) );
|
|
|
|
|
|
//
|
|
// ADMINISTRATORS access
|
|
//
|
|
|
|
Status = RtlAddAccessAllowedAce (
|
|
PortDacl,
|
|
ACL_REVISION2,
|
|
GENERIC_READ | GENERIC_EXECUTE | READ_CONTROL,
|
|
LsapAliasAdminsSid
|
|
);
|
|
ASSERT( NT_SUCCESS(Status) );
|
|
|
|
|
|
|
|
//
|
|
// Now initialize security descriptors
|
|
// that export this protection
|
|
//
|
|
|
|
|
|
|
|
Status = RtlCreateSecurityDescriptor(
|
|
*SecurityDescriptor,
|
|
SECURITY_DESCRIPTOR_REVISION1
|
|
);
|
|
ASSERT( NT_SUCCESS(Status) );
|
|
Status = RtlSetDaclSecurityDescriptor(
|
|
*SecurityDescriptor,
|
|
TRUE, // DaclPresent
|
|
PortDacl,
|
|
FALSE // DaclDefaulted
|
|
);
|
|
ASSERT( NT_SUCCESS(Status) );
|
|
|
|
|
|
return( STATUS_SUCCESS );
|
|
}
|