summaryrefslogtreecommitdiffstats
path: root/private/ntos/se/rmmain.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/ntos/se/rmmain.c')
-rw-r--r--private/ntos/se/rmmain.c1294
1 files changed, 1294 insertions, 0 deletions
diff --git a/private/ntos/se/rmmain.c b/private/ntos/se/rmmain.c
new file mode 100644
index 000000000..8ae60c8f1
--- /dev/null
+++ b/private/ntos/se/rmmain.c
@@ -0,0 +1,1294 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ rmmain.c
+
+Abstract:
+
+ Security Reference Monitor - Init, Control and State Change
+
+Author:
+
+ Scott Birrell (ScottBi) March 12, 1991
+
+Environment:
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntlsa.h>
+#include "sep.h"
+#include <zwapi.h>
+#include "rmp.h"
+#include "adt.h"
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(INIT,SepRmInitPhase0)
+#pragma alloc_text(INIT,SeRmInitPhase1)
+#pragma alloc_text(PAGE,SepRmCommandServerThread)
+#pragma alloc_text(PAGE,SepRmCommandServerThreadInit)
+#pragma alloc_text(PAGE,SepRmComponentTestCommandWrkr)
+#pragma alloc_text(PAGE,SepRmSendCommandToLsaWrkr)
+#pragma alloc_text(PAGE,SepRmCallLsa)
+#endif
+
+
+
+
+
+//
+// Reference Monitor Command Worker Table
+//
+
+//
+// Keep this in sync with RM_COMMAND_NUMBER in ntrmlsa.h
+//
+
+SEP_RM_COMMAND_WORKER SepRmCommandDispatch[] = {
+ SepRmComponentTestCommandWrkr,
+ SepRmSetAuditEventWrkr,
+ SepRmSendCommandToLsaWrkr,
+ SepRmComponentTestCommandWrkr,
+ SepRmCreateLogonSessionWrkr,
+ SepRmDeleteLogonSessionWrkr
+ };
+
+
+BOOLEAN
+SeRmInitPhase1(
+ )
+
+/*++
+
+Routine Description:
+
+ This function is called by Phase 1 System Initialization to initialize
+ the Security Reference Monitor. Note that initialization of the
+ Reference Monitor Global State has already been performed in Phase 0
+ initialization to allow access validation routines to operate without
+ having to check that Reference Monitor Initialization is complete.
+
+ The steps listed below are performed in this routine. The remainder
+ of Reference Monitor initialization requires the LSA subsystem to have run,
+ so that initialization is performed in a separate thread (the RM Command
+ Server Thread, see below), so that the present thread can create the
+ Session Manager which execs the LSA.
+
+ o Create the Reference Monitor Command LPC port. The LSA subsystem sends
+ commands (e.g. turn on auditing) which change the Reference Monitor
+ Global State.
+ o Create an Event for use in synchronizing with the LSA subsystem. The
+ LSA will signal the event when the portion of LSA initialization upon
+ with the Reference Monitor depends is complete. The Reference Monitor
+ uses another LPC port, called the LSA Command Port to send commands
+ to the LSA, so the RM must know that this port has been created before
+ trying to connect to it.
+ o Create the Reference Monitor Command Server Thread. This thread is
+ a permanent thread of the System Init process that fields the Reference
+ Monitor State Change commands described above.
+
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ BOOLEAN - TRUE if Rm Initialization (Phase 1) succeeded, else FALSE
+
+--*/
+
+{
+ NTSTATUS Status;
+ STRING RmCommandPortName;
+ UNICODE_STRING UnicodeRmCommandPortName;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ STRING LsaInitEventName;
+ UNICODE_STRING UnicodeLsaInitEventName;
+ OBJECT_ATTRIBUTES LsaInitEventObjectAttributes;
+ SECURITY_DESCRIPTOR LsaInitEventSecurityDescriptor;
+ ULONG AclSize;
+
+ PAGED_CODE();
+
+ //
+ // Create an LPC port called the Reference Monitor Command Port.
+ // This will be used by the LSA to send commands to the Reference
+ // Monitor to update its state data.
+ //
+
+ RtlInitString( &RmCommandPortName, "\\SeRmCommandPort" );
+ Status = RtlAnsiStringToUnicodeString(
+ &UnicodeRmCommandPortName,
+ &RmCommandPortName,
+ TRUE ); ASSERT( NT_SUCCESS(Status) );
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &UnicodeRmCommandPortName,
+ 0,
+ NULL,
+ NULL
+ );
+
+ Status = ZwCreatePort(
+ &SepRmState.RmCommandPortHandle,
+ &ObjectAttributes,
+ sizeof(SEP_RM_CONNECT_INFO),
+ sizeof(RM_COMMAND_MESSAGE),
+ sizeof(RM_COMMAND_MESSAGE) * 32
+ );
+ RtlFreeUnicodeString( &UnicodeRmCommandPortName );
+
+ if( !NT_SUCCESS(Status) ) {
+
+ KdPrint(("Security: Rm Create Command Port failed 0x%lx\n", Status));
+ return FALSE;
+ }
+
+ //
+ // Prepare to create an event for synchronizing with the LSA.
+ // First, build the Security Descriptor for the Init Event Object
+ //
+
+ Status = RtlCreateSecurityDescriptor(
+ &LsaInitEventSecurityDescriptor,
+ SECURITY_DESCRIPTOR_REVISION
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ KdPrint(("Security: Creating Lsa Init Event Desc failed 0x%lx\n",
+ Status));
+ return FALSE;
+ }
+
+ //
+ // Allocate a temporary buffer from the paged pool. It is a fatal
+ // system error if the allocation fails since security cannot be
+ // enabled.
+ //
+
+ AclSize = sizeof(ACL) +
+ sizeof(ACCESS_ALLOWED_ACE) +
+ SeLengthSid(SeLocalSystemSid);
+ LsaInitEventSecurityDescriptor.Dacl =
+ ExAllocatePoolWithTag(PagedPool, AclSize, 'cAeS');
+
+ if (LsaInitEventSecurityDescriptor.Dacl == NULL) {
+
+ KdPrint(("Security LSA: Insufficient resources to initialize\n"));
+ return FALSE;
+ }
+
+ //
+ // Now create the Discretionary ACL within the Security Descriptor
+ //
+
+ Status = RtlCreateAcl(
+ LsaInitEventSecurityDescriptor.Dacl,
+ AclSize,
+ ACL_REVISION2
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ KdPrint(("Security: Creating Lsa Init Event Dacl failed 0x%lx\n",
+ Status));
+ return FALSE;
+ }
+
+ //
+ // Now add an ACE giving GENERIC_ALL access to the User ID
+ //
+
+ Status = RtlAddAccessAllowedAce(
+ LsaInitEventSecurityDescriptor.Dacl,
+ ACL_REVISION2,
+ GENERIC_ALL,
+ SeLocalSystemSid
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ KdPrint(("Security: Adding Lsa Init Event ACE failed 0x%lx\n",
+ Status));
+ return FALSE;
+ }
+
+ //
+ // Set up the Object Attributes for the Lsa Initialization Event
+ //
+
+ RtlInitString( &LsaInitEventName, "\\SeLsaInitEvent" );
+ Status = RtlAnsiStringToUnicodeString(
+ &UnicodeLsaInitEventName,
+ &LsaInitEventName,
+ TRUE ); ASSERT( NT_SUCCESS(Status) );
+ InitializeObjectAttributes(
+ &LsaInitEventObjectAttributes,
+ &UnicodeLsaInitEventName,
+ 0,
+ NULL,
+ &LsaInitEventSecurityDescriptor
+ );
+
+ //
+ // Create an event for use in synchronizing with the LSA. The LSA will
+ // signal this event when LSA initialization has reached the point
+ // where the LSA's Reference Monitor Server Port has been created.
+ //
+
+ Status = ZwCreateEvent(
+ &(SepRmState.LsaInitEventHandle),
+ EVENT_MODIFY_STATE,
+ &LsaInitEventObjectAttributes,
+ NotificationEvent,
+ FALSE);
+
+ RtlFreeUnicodeString( &UnicodeLsaInitEventName );
+
+ if (!NT_SUCCESS(Status)) {
+
+ KdPrint(("Security: LSA init event creation failed.0x%xl\n",
+ Status));
+ return FALSE;
+ }
+
+ //
+ // Deallocate the pool memory used for the Init Event DACL
+ //
+
+ ExFreePool( LsaInitEventSecurityDescriptor.Dacl );
+
+ //
+ // Create a permanent thread of the Sysinit Process, called the
+ // Reference Monitor Server Thread. This thread is dedicated to
+ // receiving Reference Monitor commands and dispatching them.
+ //
+
+ Status = PsCreateSystemThread(
+ &SepRmState.SepRmThreadHandle,
+ THREAD_GET_CONTEXT |
+ THREAD_SET_CONTEXT |
+ THREAD_SET_INFORMATION,
+ NULL,
+ NULL,
+ NULL,
+ SepRmCommandServerThread,
+ NULL
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ KdPrint(("Security: Rm Server Thread creation failed 0x%lx\n", Status));
+ return FALSE;
+ }
+
+ //
+ // Initialize data from the registry. This must go here because all other
+ // Se initialization takes place before the registry is initialized.
+ //
+
+ SepAdtInitializeCrashOnFail();
+ SepAdtInitializePrivilegeAuditing();
+
+ //
+ // Reference Monitor initialization is successful if we get to here.
+ //
+
+ ZwClose( SepRmState.SepRmThreadHandle );
+ SepRmState.SepRmThreadHandle = NULL;
+ return TRUE;
+}
+
+
+VOID
+SepRmCommandServerThread(
+ IN PVOID StartContext
+)
+
+/*++
+
+Routine Description:
+
+ This function is executed indefinitely by a dedicated permanent thread
+ of the Sysinit Process, called the Reference Monitor Server Thread.
+ This thread updates Reference Monitor Global State Data by dispatching
+ commands sent from the LSA through the the Reference Monitor LPC Command
+ Port. The following steps are repeated indefinitely:
+
+ o Initialize RM Command receive and reply buffer headers
+ o Perform remaining Reference Monitor initialization involving LSA
+ o Wait for RM command sent from LSA, send reply to previous command
+ (if any)
+ o Validate command
+ o Dispatch to command worker routine to execute command.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PRM_REPLY_MESSAGE Reply;
+ RM_COMMAND_MESSAGE CommandMessage;
+ RM_REPLY_MESSAGE ReplyMessage;
+
+ PAGED_CODE();
+
+ //
+ // Perform the rest of the Reference Monitor initialization, involving
+ // synchronization with the LSA or dependency on the LSA having run.
+ //
+
+ if (!SepRmCommandServerThreadInit()) {
+
+ KdPrint(("Security: Terminating Rm Command Server Thread\n"));
+ return;
+ }
+
+ //
+ // Initialize LPC port message header type and length fields for the
+ // received command message.
+ //
+
+ CommandMessage.MessageHeader.u2.ZeroInit = 0;
+ CommandMessage.MessageHeader.u1.s1.TotalLength =
+ (CSHORT) sizeof(RM_COMMAND_MESSAGE);
+ CommandMessage.MessageHeader.u1.s1.DataLength =
+ CommandMessage.MessageHeader.u1.s1.TotalLength -
+ (CSHORT) sizeof(PORT_MESSAGE);
+
+ //
+ // Initialize the LPC port message header type and data sizes for
+ // for the reply message.
+ //
+
+ ReplyMessage.MessageHeader.u2.ZeroInit = 0;
+ ReplyMessage.MessageHeader.u1.s1.TotalLength =
+ (CSHORT) sizeof(RM_COMMAND_MESSAGE);
+ ReplyMessage.MessageHeader.u1.s1.DataLength =
+ ReplyMessage.MessageHeader.u1.s1.TotalLength -
+ (CSHORT) sizeof(PORT_MESSAGE);
+
+ //
+ // First time through, there is no reply.
+ //
+
+ Reply = NULL;
+
+ //
+ // Now loop indefinitely, processing incoming Rm commands from the LSA.
+ //
+
+ for(;;) {
+
+ //
+ // Wait for Command, send reply to previous command (if any)
+ //
+
+ Status = ZwReplyWaitReceivePort(
+ SepRmState.RmCommandPortHandle,
+ NULL,
+ (PPORT_MESSAGE) Reply,
+ (PPORT_MESSAGE) &CommandMessage
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ KdPrint(("Security: RM message receive from Lsa failed %lx\n",
+ Status));
+ return;
+ }
+
+ //
+ // Now dispatch to a routine to handle the command. Allow
+ // command errors to occur without bringing system down just now.
+ //
+
+ if ( CommandMessage.MessageHeader.u2.s2.Type == LPC_REQUEST ) {
+ (*(SepRmCommandDispatch[CommandMessage.CommandNumber]))
+ (&CommandMessage, &ReplyMessage);
+
+ //
+ // Initialize the client thread info and message id for the
+ // reply message. First time through, the reply message structure
+ // is not used.
+ //
+
+ ReplyMessage.MessageHeader.ClientId =
+ CommandMessage.MessageHeader.ClientId;
+ ReplyMessage.MessageHeader.MessageId =
+ CommandMessage.MessageHeader.MessageId;
+
+ Reply = &ReplyMessage;
+
+ } else {
+
+ Reply = NULL;
+ }
+ } // end_for
+
+ //
+ // Make compiler ferme la bouche
+ //
+
+ StartContext;
+}
+
+
+BOOLEAN
+SepRmCommandServerThreadInit(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This function performs initialization of the Reference Monitor Server
+ thread. The following steps are performed.
+
+ o Wait on the LSA signalling the event. When the event is signalled,
+ the LSA has already created the LSA Command Server LPC Port
+ o Close the LSA Init Event Handle. The event is not used again.
+ o Listen for the LSA to connect to the Port
+ o Accept the connection.
+ o Connect to the LSA Command Server LPC Port
+
+Arguments:
+
+ None.
+
+Return Value:
+
+--*/
+
+{
+ NTSTATUS Status;
+ UNICODE_STRING LsaCommandPortName;
+ PORT_MESSAGE ConnectionRequest;
+ SECURITY_QUALITY_OF_SERVICE DynamicQos;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ PORT_VIEW ClientView;
+ REMOTE_PORT_VIEW LsaClientView;
+ BOOLEAN BooleanStatus = TRUE;
+
+ PAGED_CODE();
+
+ //
+ // Save a pointer to our process so we can get back into this process
+ // to send commands to the LSA (using a handle to an LPC port created
+ // below).
+ //
+
+ SepRmLsaCallProcess = PsGetCurrentProcess();
+
+ ObReferenceObject(SepRmLsaCallProcess);
+
+ //
+ // Wait on the LSA signalling the event. This means that the LSA
+ // has created its command port, not that LSA initialization is
+ // complete.
+ //
+
+ Status = ZwWaitForSingleObject(
+ SepRmState.LsaInitEventHandle,
+ FALSE,
+ NULL);
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ KdPrint(("Security Rm Init: Waiting for LSA Init Event failed 0x%lx\n", Status));
+ goto RmCommandServerThreadInitError;
+ }
+
+ //
+ // Close the LSA Init Event Handle. The event is not used again.
+ //
+
+ ZwClose(SepRmState.LsaInitEventHandle);
+
+ //
+ // Listen for a connection to be made by the LSA to the Reference Monitor
+ // Command Port. This connection will be made by the LSA process.
+ //
+
+ ConnectionRequest.u1.s1.TotalLength = sizeof(ConnectionRequest);
+ ConnectionRequest.u1.s1.DataLength = (CSHORT)0;
+ Status = ZwListenPort(
+ SepRmState.RmCommandPortHandle,
+ &ConnectionRequest
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ KdPrint(("Security Rm Init: Listen to Command Port failed 0x%lx\n",
+ Status));
+ goto RmCommandServerThreadInitError;
+ }
+
+ //
+ // Obtain a handle to the LSA process for use when auditing.
+ //
+
+ InitializeObjectAttributes( &ObjectAttributes, NULL, 0, NULL, NULL );
+
+ Status = ZwOpenProcess(
+ &SepLsaHandle,
+ PROCESS_VM_OPERATION | PROCESS_VM_WRITE,
+ &ObjectAttributes,
+ &ConnectionRequest.ClientId
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ KdPrint(("Security Rm Init: Open Listen to Command Port failed 0x%lx\n",
+ Status));
+ goto RmCommandServerThreadInitError;
+ }
+
+ //
+ // Accept the connection made by the LSA process.
+ //
+
+ LsaClientView.Length = sizeof(LsaClientView);
+
+ Status = ZwAcceptConnectPort(
+ &SepRmState.RmCommandPortHandle,
+ NULL,
+ &ConnectionRequest,
+ TRUE,
+ NULL,
+ &LsaClientView
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ KdPrint(("Security Rm Init: Accept Connect to Command Port failed 0x%lx\n",
+ Status));
+
+ goto RmCommandServerThreadInitError;
+ }
+
+ //
+ // Complete the connection.
+ //
+
+ Status = ZwCompleteConnectPort(SepRmState.RmCommandPortHandle);
+
+ if (!NT_SUCCESS(Status)) {
+
+ KdPrint(("Security Rm Init: Complete Connect to Command Port failed 0x%lx\n",
+ Status));
+ goto RmCommandServerThreadInitError;
+ }
+
+ //
+ // Set up the security quality of service parameters to use over the
+ // Lsa Command LPC port. Use the most efficient (least overhead) - which
+ // is dynamic rather than static tracking.
+ //
+
+ DynamicQos.ImpersonationLevel = SecurityImpersonation;
+ DynamicQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ DynamicQos.EffectiveOnly = TRUE;
+
+ //
+ // Create the section to be used as unnamed shared memory for
+ // communication between the RM and LSA.
+ //
+
+ SepRmState.LsaCommandPortSectionSize.LowPart = PAGE_SIZE;
+ SepRmState.LsaCommandPortSectionSize.HighPart = 0;
+
+ Status = ZwCreateSection(
+ &SepRmState.LsaCommandPortSectionHandle,
+ SECTION_ALL_ACCESS,
+ NULL, // ObjectAttributes
+ &SepRmState.LsaCommandPortSectionSize,
+ PAGE_READWRITE,
+ SEC_COMMIT,
+ NULL // FileHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ KdPrint(("Security Rm Init: Create Memory Section for LSA port failed: %X\n", Status));
+ goto RmCommandServerThreadInitError;
+ }
+
+ //
+ // Set up for a call to NtConnectPort and connect to the LSA port.
+ // This setup includes a description of the port memory section so that
+ // the LPC connection logic can make the section visible to both the
+ // client and server processes.
+ //
+
+ ClientView.Length = sizeof(ClientView);
+ ClientView.SectionHandle = SepRmState.LsaCommandPortSectionHandle;
+ ClientView.SectionOffset = 0;
+ ClientView.ViewSize = SepRmState.LsaCommandPortSectionSize.LowPart;
+ ClientView.ViewBase = 0;
+ ClientView.ViewRemoteBase = 0;
+
+ //
+ // Set up the security quality of service parameters to use over the
+ // port. Use dynamic tracking so that XACTSRV will impersonate the
+ // user that we are impersonating when we call NtRequestWaitReplyPort.
+ // If we used static tracking, XACTSRV would impersonate the context
+ // when the connection is made.
+ //
+
+ DynamicQos.ImpersonationLevel = SecurityImpersonation;
+ DynamicQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ DynamicQos.EffectiveOnly = TRUE;
+
+ //
+ // Connect to the Lsa Command LPC Port. This port is used to send
+ // commands from the RM to the LSA.
+ //
+
+ RtlInitUnicodeString( &LsaCommandPortName, L"\\SeLsaCommandPort" );
+
+ Status = ZwConnectPort(
+ &SepRmState.LsaCommandPortHandle,
+ &LsaCommandPortName,
+ &DynamicQos,
+ &ClientView,
+ NULL, // ServerView
+ NULL, // MaxMessageLength
+ NULL, // ConnectionInformation
+ NULL // ConnectionInformationLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ KdPrint(("Security Rm Init: Connect to LSA Port failed 0x%lx\n", Status));
+ goto RmCommandServerThreadInitError;
+ }
+
+ //
+ // Store information about the section so that we can create pointers
+ // meaningful to LSA.
+ //
+
+ SepRmState.RmViewPortMemory = ClientView.ViewBase;
+ SepRmState.LsaCommandPortMemoryDelta = (LONG)( (ULONG) ClientView.ViewRemoteBase -
+ (ULONG) ClientView.ViewBase );
+ SepRmState.LsaViewPortMemory = ClientView.ViewRemoteBase;
+
+/* BugWarning - ScottBi - probably don't need the resource
+
+ //
+ // Create the resource serializing access to the port. This
+ // resource prevents the port and the shared memory from being
+ // deleted while worker threads are processing requests.
+ //
+
+ if ( !SepRmState.LsaCommandPortResourceInitialized ) {
+
+ ExInitializeResource( &SepRmState.LsaCommandPortResource );
+ SepRmState.LsaCommandPortResourceInitialized = TRUE;
+ }
+
+ SepRmState.LsaCommandPortActive = TRUE;
+
+*/
+
+RmCommandServerThreadInitFinish:
+
+ //
+ // Dont need this section handle any more, even if returning
+ // success.
+ //
+
+ if ( SepRmState.LsaCommandPortSectionHandle != NULL ) {
+
+ NtClose( SepRmState.LsaCommandPortSectionHandle );
+ SepRmState.LsaCommandPortSectionHandle = NULL;
+ }
+
+ //
+ // The Reference Monitor Thread has successfully initialized.
+ //
+
+ return BooleanStatus;
+
+RmCommandServerThreadInitError:
+
+ if ( SepRmState.LsaCommandPortHandle != NULL ) {
+
+ NtClose( SepRmState.LsaCommandPortHandle );
+ SepRmState.LsaCommandPortHandle = NULL;
+ }
+
+ BooleanStatus = FALSE;
+ goto RmCommandServerThreadInitFinish;
+}
+
+
+
+VOID
+SepRmComponentTestCommandWrkr(
+ IN PRM_COMMAND_MESSAGE CommandMessage,
+ OUT PRM_REPLY_MESSAGE ReplyMessage
+ )
+
+/*++
+
+Routine Description:
+
+ BUGWARNING - Remove this command when other RM commands are implemented.
+ Until then, this command is the only way a CT can verify that
+ an RM command with parameters is sent correctly.
+
+ This function processes the Component Test RM command.
+ This is a temporary command that can be used to verify that the link
+ from RM to LSA is working. This command verifies that the link
+ is working by receiving a ULONG parameter and verifying that it
+ has the expected value.
+
+Arguments:
+
+ CommandMessage - Pointer to structure containing RM command message
+ information consisting of an LPC PORT_MESSAGE structure followed
+ by the command number (RmComponentTestCommand). This command
+ currently has one parameter, a fixed ulong value.
+
+ 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
+
+--*/
+
+{
+ PAGED_CODE();
+
+ ReplyMessage->ReturnedStatus = STATUS_SUCCESS;
+
+ //
+ // Strict check that command is correct.
+ //
+
+ ASSERT( CommandMessage->CommandNumber == RmComponentTestCommand );
+
+ KdPrint(("Security: RM Component Test Command Received\n"));
+
+ //
+ // Verify that the parameter value passed is as expected.
+ //
+
+ if (*((ULONG *) CommandMessage->CommandParams) !=
+ RM_CT_COMMAND_PARAM_VALUE ) {
+
+ ReplyMessage->ReturnedStatus = STATUS_INVALID_PARAMETER;
+ }
+
+ return;
+}
+
+
+
+VOID
+SepRmSendCommandToLsaWrkr(
+ IN PRM_COMMAND_MESSAGE CommandMessage,
+ OUT PRM_REPLY_MESSAGE ReplyMessage
+ )
+
+/*++
+
+Routine Description:
+
+ This function carries out the special Rm Send Command To Lsa Command. This
+ command is used only by the ctlsarm component test which checks that
+ LSA to RM and RM to LSA communication via LPC is working.
+
+Arguments:
+
+ CommandMessage - Pointer to structure containing RM command message
+ information consisting of an LPC PORT_MESSAGE structure followed
+ by the command number (RmDisableAuditCommand), followed by the
+ command parameters. The parameters of this special command consists
+ of the Command Number of an LSA command and its parameters (if any).
+
+ ReplyMessage - Pointer to structure containing RM 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
+
+--*/
+
+{
+ //
+ // Obtain a pointer to the LSA command's params, and the size of the
+ // params in bytes. If there are no params, set the pointer to NULL.
+ //
+
+ PVOID LsaCommandParams =
+ ((RM_SEND_COMMAND_TO_LSA_PARAMS *)
+ (CommandMessage->CommandParams))->LsaCommandParams;
+ ULONG LsaCommandParamsLength =
+ ((RM_SEND_COMMAND_TO_LSA_PARAMS *)
+ (CommandMessage->CommandParams))->LsaCommandParamsLength;
+
+ PAGED_CODE();
+
+ if (LsaCommandParamsLength == 0) {
+
+ LsaCommandParams = NULL;
+
+ }
+
+ //
+ // Strict check that command is correct one for this worker.
+ //
+
+ ASSERT( CommandMessage->CommandNumber == RmSendCommandToLsaCommand );
+
+ KdPrint(("Security: RM Send Command back to LSA Command Received\n"));
+
+ ReplyMessage->ReturnedStatus = STATUS_SUCCESS;
+
+// Status = SepRmCallLsa(
+// ((RM_SEND_COMMAND_TO_LSA_PARAMS *)
+// (CommandMessage->CommandParams))->LsaCommandNumber,
+// LsaCommandParams,
+// LsaCommandParamsLength,
+// NULL,
+// 0,
+// NULL,
+// NULL
+// );
+
+
+ return;
+
+}
+
+
+
+
+NTSTATUS
+SepRmCallLsa(
+ PSEP_WORK_ITEM SepWorkItem
+ )
+/*++
+
+Routine Description:
+
+ This function sends a command to the LSA via the LSA Reference Monitor
+ Server Command LPC Port. If the command has parameters, they will be
+ copied directly into a message structure and sent via LPC, therefore,
+ the supplied parameters may not contain any absolute pointers. A caller
+ must remove pointers by "marshalling" them into the buffer CommandParams.
+
+ This function will create a queue of requests. This is in order to allow
+ greater throughput for the majority if its callers. If a thread enters
+ this routine and finds the queue empty, it is the responsibility of that
+ thread to service all requests that come in while it is working until the
+ queue is empty again. Other threads that enter will simply hook their work
+ item onto the queue and exit.
+
+
+ To implement a new LSA command, do the following:
+ ================================================
+
+ (1) If the command takes no parameters, just call this routine directly
+ and provide an LSA worker routine called Lsap<command>Wrkr. See
+ file lsa\server\lsarm.c for examples
+
+ (2) If the command takes parameters, provide a routine called
+ SepRmSend<command>Command that takes the parameters in unmarshalled
+ form and calls SepRmCallLsa() with the command id, marshalled
+ parameters, length of marshalled parameters and pointer to
+ optional reply message. The marshalled parameters are free format:
+ the only restriction is that there must be no absolute address
+ pointers. These parameters are all placed in the passed LsaWorkItem
+ structure.
+
+ (3) In file private\inc\ntrmlsa.h, append a command name to the
+ enumerated type LSA_COMMAND_NUMBER defined in file
+ private\inc\ntrmlsa.h. Change the #define for LsapMaximumCommand
+ to reference the new command.
+
+ (4) Add the Lsap<command>Wrkr to the command dispatch table structure
+ LsapCommandDispatch[] in file lsarm.c.
+
+ (5) Add function prototypes to lsap.h and sep.h.
+
+
+Arguments:
+
+ LsaWorkItem - Supplies a pointer to an SE_LSA_WORK_ITEM containing the
+ information to be passed to LSA. This structure will be freed
+ asynchronously by some invocation of this routine, not necessarily
+ in the current context.
+
+ !THIS PARAMETER MUST BE ALLOCATED OUT OF NONPAGED POOL!
+
+Return Value:
+
+ NTSTATUS - Result Code. This is either a result code returned from
+ trying to send the command/receive the reply, or a status code
+ from the command itself.
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ LSA_COMMAND_MESSAGE CommandMessage;
+ LSA_REPLY_MESSAGE ReplyMessage;
+ PSEP_LSA_WORK_ITEM WorkQueueItem;
+ ULONG LocalListLength = 0;
+ ULONG RegionSize;
+ PVOID CopiedCommandParams = NULL;
+ PVOID LsaViewCopiedCommandParams = NULL;
+
+ PAGED_CODE();
+
+#if 0
+ DbgPrint("Entering SepRmCallLsa\n");
+#endif
+
+ WorkQueueItem = SepWorkListHead();
+
+ KeAttachProcess( &SepRmLsaCallProcess->Pcb );
+
+ while ( WorkQueueItem ) {
+
+#if 0
+ DbgPrint("Got a work item from head of queue, processing\n");
+#endif
+
+ //
+ // Construct a message for LPC. First, fill in the message header
+ // fields for LPC, specifying the message type and data sizes for
+ // the outgoing CommandMessage and the incoming ReplyMessage.
+ //
+
+ CommandMessage.MessageHeader.u2.ZeroInit = 0;
+ CommandMessage.MessageHeader.u1.s1.TotalLength =
+ ((CSHORT) RM_COMMAND_MESSAGE_HEADER_SIZE +
+ (CSHORT) WorkQueueItem->CommandParamsLength);
+ CommandMessage.MessageHeader.u1.s1.DataLength =
+ CommandMessage.MessageHeader.u1.s1.TotalLength -
+ (CSHORT) sizeof(PORT_MESSAGE);
+
+ ReplyMessage.MessageHeader.u2.ZeroInit = 0;
+ ReplyMessage.MessageHeader.u1.s1.DataLength = (CSHORT) WorkQueueItem->ReplyBufferLength;
+ ReplyMessage.MessageHeader.u1.s1.TotalLength =
+ ReplyMessage.MessageHeader.u1.s1.DataLength +
+ (CSHORT) sizeof(PORT_MESSAGE);
+
+ //
+ // Next, fill in the header info needed by the LSA.
+ //
+
+ CommandMessage.CommandNumber = WorkQueueItem->CommandNumber;
+ ReplyMessage.ReturnedStatus = STATUS_SUCCESS;
+
+ //
+ // Set up the Command Parameters either in the LPC Command Message
+ // itself, in the preallocated Lsa shared memory block, or in a
+ // specially allocated block. The parameters are either
+ // immediate (i.e. in the WorkQueueItem itself, or are in a buffer
+ // pointed to by the address in the WorkQueueItem.
+ //
+
+ switch (WorkQueueItem->CommandParamsMemoryType) {
+
+ case SepRmImmediateMemory:
+
+ //
+ // The Command Parameters are in the CommandParams buffer
+ // in the Work Queue Item. Just copy them to the corresponding
+ // buffer in the CommandMessage buffer.
+ //
+
+ CommandMessage.CommandParamsMemoryType = SepRmImmediateMemory;
+
+ RtlMoveMemory(
+ CommandMessage.CommandParams,
+ &WorkQueueItem->CommandParams,
+ WorkQueueItem->CommandParamsLength
+ );
+
+ break;
+
+ case SepRmPagedPoolMemory:
+ case SepRmUnspecifiedMemory:
+
+ //
+ // The Command Parameters are contained in paged pool memory.
+ // Since this memory is is not accessible by the LSA, we must
+ // copy of them either to the LPC Command Message Block, or
+ // into LSA shared memory.
+ //
+
+ if (WorkQueueItem->CommandParamsLength <= LSA_MAXIMUM_COMMAND_PARAM_SIZE) {
+
+ //
+ // Parameters will fit into the LPC Command Message block.
+ //
+
+ CopiedCommandParams = CommandMessage.CommandParams;
+
+ RtlMoveMemory(
+ CopiedCommandParams,
+ WorkQueueItem->CommandParams.BaseAddress,
+ WorkQueueItem->CommandParamsLength
+ );
+
+ CommandMessage.CommandParamsMemoryType = SepRmImmediateMemory;
+
+ } else {
+
+ //
+ // Parameters too large for LPC Command Message block.
+ // If possible, copy them to the preallocated Lsa Shared
+ // Memory block. If they are too large to fit, copy them
+ // to an individually allocated chunk of Shared Virtual
+ // Memory.
+ //
+
+ if (WorkQueueItem->CommandParamsLength <= SEP_RM_LSA_SHARED_MEMORY_SIZE) {
+
+ RtlMoveMemory(
+ SepRmState.RmViewPortMemory,
+ WorkQueueItem->CommandParams.BaseAddress,
+ WorkQueueItem->CommandParamsLength
+ );
+
+ LsaViewCopiedCommandParams = SepRmState.LsaViewPortMemory;
+ CommandMessage.CommandParamsMemoryType = SepRmLsaCommandPortSharedMemory;
+
+ } else {
+
+ Status = SepAdtCopyToLsaSharedMemory(
+ SepLsaHandle,
+ WorkQueueItem->CommandParams.BaseAddress,
+ WorkQueueItem->CommandParamsLength,
+ &LsaViewCopiedCommandParams
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ //
+ // An error occurred, most likely in allocating
+ // shared virtual memory. For now, just ignore
+ // the error and discard the Audit Record. Later,
+ // we may consider generating a warning record
+ // indicating some records lost.
+ //
+
+ break;
+
+ }
+
+ CommandMessage.CommandParamsMemoryType = SepRmLsaCustomSharedMemory;
+ }
+
+ //
+ // Buffer has been successfully copied to a shared Lsa
+ // memory buffer. Place the address of the buffer valid in
+ // the LSA's process context in the Command Message.
+ //
+
+ *((PVOID *) CommandMessage.CommandParams) =
+ LsaViewCopiedCommandParams;
+
+ CommandMessage.MessageHeader.u1.s1.TotalLength =
+ ((CSHORT) RM_COMMAND_MESSAGE_HEADER_SIZE +
+ (CSHORT) sizeof( LsaViewCopiedCommandParams ));
+ CommandMessage.MessageHeader.u1.s1.DataLength =
+ CommandMessage.MessageHeader.u1.s1.TotalLength -
+ (CSHORT) sizeof(PORT_MESSAGE);
+ }
+
+ //
+ // Free input command params buffer if Paged Pool.
+ //
+
+ if (WorkQueueItem->CommandParamsMemoryType == SepRmPagedPoolMemory) {
+
+ ExFreePool( WorkQueueItem->CommandParams.BaseAddress );
+ }
+
+ break;
+
+ default:
+
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Send Message to the LSA via the LSA Server Command LPC Port.
+ // This must be done in the process in which the handle was created.
+ //
+
+ Status = ZwRequestWaitReplyPort(
+ SepRmState.LsaCommandPortHandle,
+ (PPORT_MESSAGE) &CommandMessage,
+ (PPORT_MESSAGE) &ReplyMessage
+ );
+
+ //
+ // If the command was successful, copy the data back to the output
+ // buffer.
+ //
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Move output from command (if any) to buffer. Note that this
+ // is done even if the command returns status, because some status
+ // values are not errors.
+ //
+
+ if (ARGUMENT_PRESENT(WorkQueueItem->ReplyBuffer)) {
+
+ RtlMoveMemory(
+ WorkQueueItem->ReplyBuffer,
+ ReplyMessage.ReplyBuffer,
+ WorkQueueItem->ReplyBufferLength
+ );
+ }
+
+ //
+ // Return status from command.
+ //
+
+ Status = ReplyMessage.ReturnedStatus;
+
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("Security: Command sent from RM to LSA returned 0x%lx\n",
+ Status));
+ }
+
+ } else {
+
+ KdPrint(("Security: Sending Command RM to LSA failed 0x%lx\n", Status));
+ }
+
+ //
+ // On return from the LPC call to the LSA, we expect the called
+ // LSA worker routine to have copied the Command Parameters
+ // buffer (if any). If a custom shared memory boffer was allocated,
+ // free it now.
+ //
+
+ if (CommandMessage.CommandParamsMemoryType == SepRmLsaCustomSharedMemory) {
+
+ RegionSize = 0;
+
+ Status = ZwFreeVirtualMemory(
+ SepLsaHandle,
+ (PVOID *) &CommandMessage.CommandParams,
+ &RegionSize,
+ MEM_RELEASE
+ );
+
+ ASSERT(NT_SUCCESS(Status));
+ }
+
+ }
+
+
+ //
+ // Clean up. We must call the cleanup functions on its parameter
+ // and then free the used WorkQueueItem itself.
+ //
+
+ if ( ARGUMENT_PRESENT( WorkQueueItem->CleanupFunction)) {
+
+ (WorkQueueItem->CleanupFunction)(WorkQueueItem->CleanupParameter);
+ }
+
+ //
+ // Determine if there is more work to do on this list
+ //
+
+ WorkQueueItem = SepDequeueWorkItem();
+#if 0
+ if ( WorkQueueItem ) {
+ DbgPrint("Got another item from list, going back\n");
+ } else {
+ DbgPrint("List is empty, leaving\n");
+ }
+#endif
+
+
+ }
+
+ KeDetachProcess();
+
+ if ( LocalListLength > SepLsaQueueLength ) {
+ SepLsaQueueLength = LocalListLength;
+ }
+
+ return Status;
+}
+
+
+
+
+
+BOOLEAN
+SepRmInitPhase0(
+ )
+
+/*++
+
+Routine Description:
+
+ This function performs Reference Monitor Phase 0 initialization.
+ This includes initializing the reference monitor database to a state
+ which allows access validation routines to operate (always granting
+ access) prior to the main init of the Reference Monitor in Phase 1
+ initialization, without having to check if the RM is initialized.
+
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ BOOLEAN - TRUE if successful, else FALSE
+
+--*/
+
+{
+
+ BOOLEAN CompletionStatus;
+
+ PAGED_CODE();
+
+ CompletionStatus = SepRmDbInitialization();
+
+ return CompletionStatus;
+}