/*++ Copyright (c) 1989 Microsoft Corporation Module Name: data.c Abstract: This module contains global data for SAC. Author: Sean Selitrennikoff (v-seans) - Jan 11, 1999 Revision History: --*/ #include "sac.h" NTSTATUS CreateAdminSecurityDescriptor( IN PSAC_DEVICE_CONTEXT DeviceContext ); NTSTATUS BuildDeviceAcl( OUT PACL *DeviceAcl ); VOID WorkerThreadStartUp( IN PVOID StartContext ); #ifdef ALLOC_PRAGMA #pragma alloc_text( INIT, InitializeGlobalData) #pragma alloc_text( INIT, CreateAdminSecurityDescriptor ) #pragma alloc_text( INIT, BuildDeviceAcl ) #endif // // Globally defined variables are here. // BOOLEAN GlobalDataInitialized = FALSE; UCHAR TmpBuffer[sizeof(PROCESS_PRIORITY_CLASS)]; BOOLEAN IoctlSubmitted; LONG ProcessingType; HANDLE SACEventHandle; PKEVENT SACEvent=NULL; LONG Attempts; #if DBG ULONG SACDebug = 0x0; #endif BOOLEAN InitializeGlobalData( IN PUNICODE_STRING RegistryPath, IN PDRIVER_OBJECT DriverObject ) /*++ Routine Description: This routine initializes all the driver components that are shared across devices. Arguments: RegistryPath - A pointer to the location in the registry to read values from. DriverObject - pointer to DriverObject Return Value: TRUE if successful, else FALSE --*/ { NTSTATUS Status; UNICODE_STRING DosName; UNICODE_STRING NtName; UNICODE_STRING UnicodeString; UNREFERENCED_PARAMETER(RegistryPath); PAGED_CODE(); IF_SAC_DEBUG(SAC_DEBUG_FUNC_TRACE, KdPrint(("SAC InitializeGlobalData: Entering.\n"))); if (!GlobalDataInitialized) { // // Create a symbolic link from a DosDevice to this device so that a user-mode app can open us. // RtlInitUnicodeString(&DosName, SAC_DOSDEVICE_NAME); RtlInitUnicodeString(&NtName, SAC_DEVICE_NAME); Status = IoCreateSymbolicLink(&DosName, &NtName); if (!NT_SUCCESS(Status)) { return FALSE; } // // Initialize internal memory system // if (!InitializeMemoryManagement()) { IoDeleteSymbolicLink(&DosName); IF_SAC_DEBUG(SAC_DEBUG_FUNC_TRACE, KdPrint(("SAC InitializeGlobalData: Exiting with status FALSE\n"))); return FALSE; } Status = PreloadGlobalMessageTable(DriverObject->DriverStart); if (!NT_SUCCESS(Status)) { IoDeleteSymbolicLink(&DosName); IF_SAC_DEBUG(SAC_DEBUG_FAILS, KdPrint(( "SAC DriverEntry: unable to pre-load message table: %X\n", Status ))); return FALSE; } Utf8ConversionBuffer = (PUCHAR)ALLOCATE_POOL(MEMORY_INCREMENT, GENERAL_POOL_TAG); if (!Utf8ConversionBuffer) { TearDownGlobalMessageTable(); IoDeleteSymbolicLink(&DosName); IF_SAC_DEBUG(SAC_DEBUG_FAILS, KdPrint(( "SAC DriverEntry: unable to allocate memory for UTF8 translation." ))); return FALSE; } GlobalDataInitialized = TRUE; ProcessingType = SAC_NO_OP; IoctlSubmitted = FALSE; Attempts = 0; // // Setup notification event // RtlInitUnicodeString(&UnicodeString, L"\\SACEvent"); SACEvent = IoCreateSynchronizationEvent(&UnicodeString, &SACEventHandle); if (SACEvent == NULL) { IF_SAC_DEBUG(SAC_DEBUG_FUNC_TRACE, KdPrint(("SAC InitializeGlobalData: Exiting with Event NULL\n"))); return FALSE; } // // Retrieve all the machine-specific identification information. // InitializeMachineInformation(); } IF_SAC_DEBUG(SAC_DEBUG_FUNC_TRACE, KdPrint(("SAC InitializeGlobalData: Exiting with status TRUE\n"))); return TRUE; } // InitializeGlobalData VOID FreeGlobalData( VOID ) /*++ Routine Description: This routine frees all shared components. Arguments: None. Return Value: None. --*/ { UNICODE_STRING DosName; IF_SAC_DEBUG(SAC_DEBUG_FUNC_TRACE, KdPrint(("SAC FreeGlobalData: Entering.\n"))); if (GlobalDataInitialized) { if(SACEvent != NULL){ ZwClose(SACEventHandle); SACEvent = NULL; } TearDownGlobalMessageTable(); RtlInitUnicodeString(&DosName, SAC_DOSDEVICE_NAME); IoDeleteSymbolicLink(&DosName); FreeMemoryManagement(); GlobalDataInitialized = FALSE; } IF_SAC_DEBUG(SAC_DEBUG_FUNC_TRACE, KdPrint(("SAC FreeGlobalData: Exiting.\n"))); } // FreeGlobalData BOOLEAN InitializeDeviceData( PDEVICE_OBJECT DeviceObject ) /*++ Routine Description: This routine initializes all the parts specific for each device. Arguments: DeviceObject - pointer to device object to be initialized. Return Value: TRUE if successful, else FALSE --*/ { NTSTATUS Status; LARGE_INTEGER Time; LONG Priority; HEADLESS_CMD_ENABLE_TERMINAL Command; PSAC_DEVICE_CONTEXT DeviceContext; PAGED_CODE(); IF_SAC_DEBUG(SAC_DEBUG_FUNC_TRACE, KdPrint(("SAC InitializeDeviceData: Entering.\n"))); DeviceContext = (PSAC_DEVICE_CONTEXT)DeviceObject->DeviceExtension; if (!DeviceContext->InitializedAndReady) { DeviceObject->StackSize = DEFAULT_IRP_STACK_SIZE; DeviceObject->Flags |= DO_DIRECT_IO; DeviceContext->DeviceObject = DeviceObject; DeviceContext->PriorityBoost = DEFAULT_PRIORITY_BOOST; DeviceContext->AdminSecurityDescriptor = NULL; DeviceContext->UnloadDeferred = FALSE; DeviceContext->Processing = FALSE; KeInitializeTimer(&(DeviceContext->Timer)); KeInitializeDpc(&(DeviceContext->Dpc), &TimerDpcRoutine, DeviceContext); KeInitializeSpinLock(&(DeviceContext->SpinLock)); KeInitializeEvent(&(DeviceContext->ProcessEvent), SynchronizationEvent, FALSE); // // Enable the terminal // Command.Enable = TRUE; Status = HeadlessDispatch(HeadlessCmdEnableTerminal, &Command, sizeof(HEADLESS_CMD_ENABLE_TERMINAL), NULL, NULL ); if (!NT_SUCCESS(Status)) { IF_SAC_DEBUG(SAC_DEBUG_FUNC_TRACE, KdPrint(("SAC InitializeDeviceData: Exiting (1) with status FALSE\n"))); return FALSE; } // // Remember a pointer to the system process. We'll use this pointer // for KeAttachProcess() calls so that we can open handles in the // context of the system process. // DeviceContext->SystemProcess = (PKPROCESS)IoGetCurrentProcess(); // // Create the security descriptor used for raw access checks. // Status = CreateAdminSecurityDescriptor(DeviceContext); if (!NT_SUCCESS(Status)) { IF_SAC_DEBUG(SAC_DEBUG_FUNC_TRACE, KdPrint(("SAC InitializeDeviceData: Exiting (2) with status FALSE\n"))); Command.Enable = FALSE; HeadlessDispatch(HeadlessCmdEnableTerminal, &Command, sizeof(HEADLESS_CMD_ENABLE_TERMINAL), NULL, NULL ); return FALSE; } // // Start a thread to handle requests // Status = PsCreateSystemThread(&(DeviceContext->ThreadHandle), PROCESS_ALL_ACCESS, NULL, NULL, NULL, WorkerThreadStartUp, DeviceContext ); if (!NT_SUCCESS(Status)) { IF_SAC_DEBUG(SAC_DEBUG_FUNC_TRACE, KdPrint(("SAC InitializeDeviceData: Exiting (3) with status FALSE\n"))); Command.Enable = FALSE; HeadlessDispatch(HeadlessCmdEnableTerminal, &Command, sizeof(HEADLESS_CMD_ENABLE_TERMINAL), NULL, NULL ); return FALSE; } // // Set this thread to the real-time highest priority so that it will be // responsive. // Priority = HIGH_PRIORITY; Status = NtSetInformationThread(DeviceContext->ThreadHandle, ThreadPriority, &Priority, sizeof(Priority) ); if (!NT_SUCCESS(Status)) { IF_SAC_DEBUG(SAC_DEBUG_FUNC_TRACE, KdPrint(("SAC InitializeDeviceData: Exiting (6) with status FALSE\n"))); // // Tell thread to exit. // DeviceContext->UnloadDeferred = TRUE; KeInitializeEvent(&(DeviceContext->UnloadEvent), SynchronizationEvent, FALSE); KeSetEvent(&(DeviceContext->ProcessEvent), DeviceContext->PriorityBoost, FALSE); Status = KeWaitForSingleObject((PVOID)&(DeviceContext->UnloadEvent), Executive, KernelMode, FALSE, NULL); ASSERT(Status == STATUS_SUCCESS); Command.Enable = FALSE; HeadlessDispatch(HeadlessCmdEnableTerminal, &Command, sizeof(HEADLESS_CMD_ENABLE_TERMINAL), NULL, NULL ); return FALSE; } // // Start our timer // Time.QuadPart = Int32x32To64((LONG)100, -1000); // 100ms from now. KeSetTimerEx(&(DeviceContext->Timer), Time, (LONG)100, &(DeviceContext->Dpc)); // every 100ms // // Display the prompt // SacPutSimpleMessage( SAC_ENTER ); SacPutSimpleMessage( SAC_INITIALIZED ); SacPutSimpleMessage( SAC_ENTER ); SacPutSimpleMessage( SAC_PROMPT ); DeviceContext->InitializedAndReady = TRUE; } IF_SAC_DEBUG(SAC_DEBUG_FUNC_TRACE, KdPrint(("SAC InitializeDeviceData: Exiting with status TRUE\n"))); return TRUE; } // InitializeDeviceData VOID FreeDeviceData( PDEVICE_OBJECT DeviceObject ) /*++ Routine Description: This routine frees all components specific to a device.. Arguments: DeviceContext - The device to work on. Return Value: It will stop and wait, if necessary, for any processing to complete. --*/ { KIRQL OldIrql; NTSTATUS Status; PVOID MemToFree; PSAC_DEVICE_CONTEXT DeviceContext; IF_SAC_DEBUG(SAC_DEBUG_FUNC_TRACE, KdPrint(("SAC FreeDeviceData: Entering.\n"))); DeviceContext = (PSAC_DEVICE_CONTEXT)DeviceObject->DeviceExtension; if (!GlobalDataInitialized || !DeviceContext->InitializedAndReady) { IF_SAC_DEBUG(SAC_DEBUG_FUNC_TRACE, KdPrint(("SAC FreeDeviceData: Exiting.\n"))); return; } // // Wait for all processing to complete // KeAcquireSpinLock(&(DeviceContext->SpinLock), &OldIrql); DeviceContext->UnloadDeferred = TRUE; while (DeviceContext->Processing) { IF_SAC_DEBUG(SAC_DEBUG_FUNC_TRACE, KdPrint(("SAC FreeDeviceData: Waiting....\n"))); KeInitializeEvent(&(DeviceContext->UnloadEvent), SynchronizationEvent, FALSE); KeReleaseSpinLock(&(DeviceContext->SpinLock), OldIrql); Status = KeWaitForSingleObject((PVOID)&(DeviceContext->UnloadEvent), Executive, KernelMode, FALSE, NULL); ASSERT(Status == STATUS_SUCCESS); KeAcquireSpinLock(&(DeviceContext->SpinLock), &OldIrql); } DeviceContext->Processing = TRUE; KeReleaseSpinLock(&(DeviceContext->SpinLock), OldIrql); KeCancelTimer(&(DeviceContext->Timer)); KeAcquireSpinLock(&(DeviceContext->SpinLock), &OldIrql); DeviceContext->Processing = FALSE; MemToFree = (PVOID)DeviceContext->AdminSecurityDescriptor; DeviceContext->AdminSecurityDescriptor = NULL; // // Signal the thread to exit // KeInitializeEvent(&(DeviceContext->UnloadEvent), SynchronizationEvent, FALSE); KeReleaseSpinLock(&(DeviceContext->SpinLock), OldIrql); KeSetEvent(&(DeviceContext->ProcessEvent), DeviceContext->PriorityBoost, FALSE); Status = KeWaitForSingleObject((PVOID)&(DeviceContext->UnloadEvent), Executive, KernelMode, FALSE, NULL); ASSERT(Status == STATUS_SUCCESS); // // Finish up cleaning up. // IoUnregisterShutdownNotification(DeviceObject); KeAcquireSpinLock(&(DeviceContext->SpinLock), &OldIrql); DeviceContext->InitializedAndReady = FALSE; DeviceContext->UnloadDeferred = FALSE; KeReleaseSpinLock(&(DeviceContext->SpinLock), OldIrql); if (MemToFree != NULL) { ExFreePool(MemToFree); } IF_SAC_DEBUG(SAC_DEBUG_FUNC_TRACE, KdPrint(("SAC FreeDeviceData: Exiting.\n"))); } // FreeDeviceData NTSTATUS BuildDeviceAcl( OUT PACL *DeviceAcl ) /*++ Routine Description: This routine builds an ACL which gives Administrators and LocalSystem principals full access. All other principals have no access. Arguments: DeviceAcl - Output pointer to the new ACL. Return Value: STATUS_SUCCESS or an appropriate error code. --*/ { PGENERIC_MAPPING GenericMapping; PSID AdminsSid; PSID SystemSid; ULONG AclLength; NTSTATUS Status; ACCESS_MASK AccessMask = GENERIC_ALL; PACL NewAcl; IF_SAC_DEBUG(SAC_DEBUG_FUNC_TRACE, KdPrint(("SAC BuildDeviceAcl: Entering.\n"))); // // Enable access to all the globally defined SIDs // GenericMapping = IoGetFileObjectGenericMapping(); RtlMapGenericMask(&AccessMask, GenericMapping); //SeEnableAccessToExports(); AdminsSid = SeExports->SeAliasAdminsSid; SystemSid = SeExports->SeLocalSystemSid; AclLength = sizeof(ACL) + (2 * sizeof(ACCESS_ALLOWED_ACE)) + RtlLengthSid(AdminsSid) + RtlLengthSid(SystemSid) - (2 * sizeof(ULONG)); NewAcl = ALLOCATE_POOL(AclLength, SECURITY_POOL_TAG); if (NewAcl == NULL) { return(STATUS_INSUFFICIENT_RESOURCES); } Status = RtlCreateAcl(NewAcl, AclLength, ACL_REVISION); if (!NT_SUCCESS(Status)) { FREE_POOL(&NewAcl); IF_SAC_DEBUG(SAC_DEBUG_FUNC_TRACE, KdPrint(("SAC BuildDeviceAcl: Exiting with status 0x%x\n", Status))); return(Status); } Status = RtlAddAccessAllowedAce(NewAcl, ACL_REVISION2, AccessMask, AdminsSid ); ASSERT(NT_SUCCESS(Status)); Status = RtlAddAccessAllowedAce(NewAcl, ACL_REVISION2, AccessMask, SystemSid ); ASSERT(NT_SUCCESS(Status)); *DeviceAcl = NewAcl; IF_SAC_DEBUG(SAC_DEBUG_FUNC_TRACE, KdPrint(("SAC BuildDeviceAcl: Exiting with status 0x%x\n", STATUS_SUCCESS))); return(STATUS_SUCCESS); } // BuildDeviceAcl NTSTATUS CreateAdminSecurityDescriptor( PSAC_DEVICE_CONTEXT DeviceContext ) /*++ Routine Description: This routine creates a security descriptor which gives access only to Administrtors and LocalSystem. This descriptor is used to access check raw endpoint opens and exclisive access to transport addresses. Arguments: DeviceContext - A pointer to the device to work on. Return Value: STATUS_SUCCESS or an appropriate error code. --*/ { PACL RawAcl = NULL; NTSTATUS Status; BOOLEAN MemoryAllocated = FALSE; PSECURITY_DESCRIPTOR SecurityDescriptor; ULONG SecurityDescriptorLength; CHAR Buffer[SECURITY_DESCRIPTOR_MIN_LENGTH]; PSECURITY_DESCRIPTOR LocalSecurityDescriptor = (PSECURITY_DESCRIPTOR) &Buffer; PSECURITY_DESCRIPTOR LocalAdminSecurityDescriptor; SECURITY_INFORMATION SecurityInformation = DACL_SECURITY_INFORMATION; IF_SAC_DEBUG(SAC_DEBUG_FUNC_TRACE, KdPrint(("SAC CreateAdminSecDesc: Entering.\n"))); // // Get a pointer to the security descriptor from the device object. // Status = ObGetObjectSecurity(DeviceContext->DeviceObject, &SecurityDescriptor, &MemoryAllocated ); if (!NT_SUCCESS(Status)) { IF_SAC_DEBUG(SAC_DEBUG_FAILS, KdPrint(("SAC: Unable to get security descriptor, error: %x\n", Status))); ASSERT(MemoryAllocated == FALSE); IF_SAC_DEBUG(SAC_DEBUG_FUNC_TRACE, KdPrint(("SAC CreateAdminSecDesc: Exiting with status 0x%x\n", Status))); return(Status); } // // Build a local security descriptor with an ACL giving only // administrators and system access. // Status = BuildDeviceAcl(&RawAcl); if (!NT_SUCCESS(Status)) { IF_SAC_DEBUG(SAC_DEBUG_FAILS, KdPrint(("SAC CreateAdminSecDesc: Unable to create Raw ACL, error: %x\n", Status))); goto ErrorExit; } (VOID)RtlCreateSecurityDescriptor(LocalSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION ); (VOID)RtlSetDaclSecurityDescriptor(LocalSecurityDescriptor, TRUE, RawAcl, FALSE ); // // Make a copy of the security descriptor. This copy will be the raw descriptor. // SecurityDescriptorLength = RtlLengthSecurityDescriptor(SecurityDescriptor); LocalAdminSecurityDescriptor = ExAllocatePoolWithTag(PagedPool, SecurityDescriptorLength, SECURITY_POOL_TAG ); if (LocalAdminSecurityDescriptor == NULL) { IF_SAC_DEBUG(SAC_DEBUG_FAILS, KdPrint(("SAC CreateAdminSecDesc: couldn't allocate security descriptor\n"))); goto ErrorExit; } RtlMoveMemory(LocalAdminSecurityDescriptor, SecurityDescriptor, SecurityDescriptorLength ); DeviceContext->AdminSecurityDescriptor = LocalAdminSecurityDescriptor; // // Now apply the local descriptor to the raw descriptor. // Status = SeSetSecurityDescriptorInfo(NULL, &SecurityInformation, LocalSecurityDescriptor, &(DeviceContext->AdminSecurityDescriptor), NonPagedPool, IoGetFileObjectGenericMapping() ); if (!NT_SUCCESS(Status)) { IF_SAC_DEBUG(SAC_DEBUG_FAILS, KdPrint(("SAC CreateAdminSecDesc: SeSetSecurity failed, %lx\n", Status))); ASSERT(DeviceContext->AdminSecurityDescriptor == LocalAdminSecurityDescriptor); ExFreePool(DeviceContext->AdminSecurityDescriptor); DeviceContext->AdminSecurityDescriptor = NULL; goto ErrorExit; } if (DeviceContext->AdminSecurityDescriptor != LocalAdminSecurityDescriptor) { ExFreePool(LocalAdminSecurityDescriptor); } Status = STATUS_SUCCESS; ErrorExit: ObReleaseObjectSecurity(SecurityDescriptor, MemoryAllocated); if (RawAcl != NULL) { FREE_POOL(&RawAcl); } IF_SAC_DEBUG(SAC_DEBUG_FUNC_TRACE, KdPrint(("SAC CreateAdminSecDesc: Exiting with status 0x%x\n", Status))); return(Status); } VOID WorkerThreadStartUp( IN PVOID StartContext ) /*++ Routine Description: This routine is the start up routine for the worker thread. It justn sends the worker thread to the processing routine. Arguments: StartContext - A pointer to the device to work on. Return Value: None. --*/ { WorkerProcessEvents((PSAC_DEVICE_CONTEXT)StartContext); }