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.
1380 lines
35 KiB
1380 lines
35 KiB
/*++
|
|
|
|
Copyright (c) 1991-1992 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
wsmain.c
|
|
|
|
Abstract:
|
|
|
|
This is the main routine for the NT LAN Manager Workstation service.
|
|
|
|
Author:
|
|
|
|
Rita Wong (ritaw) 06-May-1991
|
|
|
|
Environment:
|
|
|
|
User Mode - Win32
|
|
|
|
Revision History:
|
|
|
|
15-May-1992 JohnRo
|
|
Implement registry watch.
|
|
11-Jun-1992 JohnRo
|
|
Ifdef-out winreg notify stuff until we can fix logoff problem.
|
|
Added assertion checks on registry watch stuff.
|
|
18-Oct-1993 terryk
|
|
Removed WsInitializeLogon stuff
|
|
20-Oct-1993 terryk
|
|
Remove WsInitializeMessage stuff
|
|
|
|
--*/
|
|
|
|
|
|
#include "wsutil.h" // Common routines and data
|
|
#include "wssec.h" // WkstaObjects create & destroy
|
|
#include "wsdevice.h" // Device init & shutdown
|
|
#include "wsuse.h" // UseStructures create & destroy
|
|
#include "wsconfig.h" // Configuration loading
|
|
#include "wslsa.h" // Lsa initialization
|
|
#include "wsmsg.h" // Message send initialization
|
|
#include "wswksta.h" // WsUpdateRedirToMatchWksta
|
|
#include "wsmain.h" // Service related global definitions
|
|
#include "wsdfs.h" // Dfs related routines
|
|
|
|
#include <lmserver.h> // SV_TYPE_WORKSTATION
|
|
#include <srvann.h> // I_ScSetServiceBits
|
|
#include <configp.h> // Need NET_CONFIG_HANDLE typedef
|
|
#include <confname.h> // NetpAllocConfigName().
|
|
#include <prefix.h> // PREFIX_ equates.
|
|
|
|
|
|
//-------------------------------------------------------------------//
|
|
// //
|
|
// Structures
|
|
// //
|
|
//-------------------------------------------------------------------//
|
|
typedef struct _REG_NOTIFY_INFO {
|
|
HANDLE NotifyEventHandle;
|
|
DWORD Timeout;
|
|
HANDLE WorkItemHandle;
|
|
HANDLE RegistryHandle;
|
|
} REG_NOTIFY_INFO, *PREG_NOTIFY_INFO, *LPREG_NOTIFY_INFO;
|
|
|
|
//-------------------------------------------------------------------//
|
|
// //
|
|
// Global variables //
|
|
// //
|
|
//-------------------------------------------------------------------//
|
|
|
|
WS_GLOBAL_DATA WsGlobalData;
|
|
|
|
PSVCHOST_GLOBAL_DATA WsLmsvcsGlobalData;
|
|
|
|
REG_NOTIFY_INFO RegNotifyInfo = {0};
|
|
|
|
HANDLE TerminateWorkItem = NULL;
|
|
|
|
CRITICAL_SECTION WsWorkerCriticalSection;
|
|
|
|
BOOL WsIsTerminating=FALSE;
|
|
BOOL WsLUIDDeviceMapsEnabled=FALSE;
|
|
DWORD WsNumWorkerThreads=0;
|
|
|
|
// Used by the termination routine:
|
|
|
|
BOOL ConfigHandleOpened = FALSE;
|
|
BOOL WsWorkerCriticalSectionInitialized = FALSE;
|
|
HKEY ConfigHandle;
|
|
HANDLE RegistryChangeEvent = NULL;
|
|
LPTSTR RegPathToWatch = NULL;
|
|
DWORD WsInitState = 0;
|
|
|
|
// Stores the status with which WsInitializeWorkstation failed, so that it can later
|
|
// be passed to WsShutdownWorkstation
|
|
NET_API_STATUS WsInitializeStatusError = NERR_Success;
|
|
|
|
|
|
//-------------------------------------------------------------------//
|
|
// //
|
|
// Function prototypes //
|
|
// //
|
|
//-------------------------------------------------------------------//
|
|
|
|
STATIC
|
|
NET_API_STATUS
|
|
WsInitializeWorkstation(
|
|
OUT LPDWORD WsInitState
|
|
);
|
|
|
|
STATIC
|
|
VOID
|
|
WsShutdownWorkstation(
|
|
IN NET_API_STATUS ErrorCode,
|
|
IN DWORD WsInitState
|
|
);
|
|
|
|
STATIC
|
|
NET_API_STATUS
|
|
WsCreateApiStructures(
|
|
IN OUT LPDWORD WsInitState
|
|
);
|
|
|
|
STATIC
|
|
VOID
|
|
WsDestroyApiStructures(
|
|
IN DWORD WsInitState
|
|
);
|
|
|
|
VOID
|
|
WkstaControlHandler(
|
|
IN DWORD Opcode
|
|
);
|
|
|
|
BOOL
|
|
WsReInitChangeNotify(
|
|
PREG_NOTIFY_INFO pNotifyInfo
|
|
);
|
|
|
|
DWORD
|
|
WsRegistryNotify(
|
|
LPVOID pParms,
|
|
BOOLEAN fWaitStatus
|
|
);
|
|
|
|
VOID
|
|
WsTerminationNotify(
|
|
LPVOID pParms,
|
|
BOOLEAN fWaitStatus
|
|
);
|
|
|
|
|
|
STATIC
|
|
BOOL
|
|
WsGetLUIDDeviceMapsEnabled(
|
|
VOID
|
|
);
|
|
|
|
|
|
RPC_STATUS WsRpcSecurityCallback(
|
|
IN RPC_IF_HANDLE *Interface,
|
|
IN void *Context
|
|
);
|
|
|
|
VOID
|
|
SvchostPushServiceGlobals(
|
|
PSVCHOST_GLOBAL_DATA pGlobals
|
|
)
|
|
{
|
|
WsLmsvcsGlobalData = pGlobals;
|
|
}
|
|
|
|
|
|
VOID
|
|
ServiceMain(
|
|
DWORD NumArgs,
|
|
LPTSTR *ArgsArray
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the main routine of the Workstation Service which registers
|
|
itself as an RPC server and notifies the Service Controller of the
|
|
Workstation service control entry point.
|
|
|
|
After the workstation is started, this thread is used (since it's
|
|
otherwise unused) to watch for changes to the registry.
|
|
|
|
Arguments:
|
|
|
|
NumArgs - Supplies the number of strings specified in ArgsArray.
|
|
|
|
ArgsArray - Supplies string arguments that are specified in the
|
|
StartService API call. This parameter is ignored by the
|
|
Workstation service.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
|
|
NET_API_STATUS ApiStatus;
|
|
|
|
UNREFERENCED_PARAMETER(NumArgs);
|
|
UNREFERENCED_PARAMETER(ArgsArray);
|
|
|
|
//
|
|
// Make sure svchost.exe gave us the global data
|
|
//
|
|
ASSERT(WsLmsvcsGlobalData != NULL);
|
|
|
|
WsInitState = 0;
|
|
WsIsTerminating=FALSE;
|
|
WsLUIDDeviceMapsEnabled=FALSE;
|
|
WsWorkerCriticalSectionInitialized = FALSE;
|
|
RegNotifyInfo.NotifyEventHandle = NULL;
|
|
RegNotifyInfo.Timeout = 0;
|
|
RegNotifyInfo.WorkItemHandle = NULL;
|
|
RegNotifyInfo.RegistryHandle = NULL;
|
|
|
|
//
|
|
// Initialize the workstation.
|
|
//
|
|
if ((ApiStatus = WsInitializeWorkstation(&WsInitState)) != NERR_Success) {
|
|
DbgPrint("WKSSVC failed to initialize workstation %x\n",WsInitState);
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Set up to wait for registry change or terminate event.
|
|
//
|
|
ApiStatus = NetpAllocConfigName(
|
|
SERVICES_ACTIVE_DATABASE,
|
|
SERVICE_WORKSTATION,
|
|
NULL, // default area ("Parameters")
|
|
&RegPathToWatch
|
|
);
|
|
|
|
if (ApiStatus != NERR_Success) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
NetpAssert(RegPathToWatch != NULL && *RegPathToWatch != TCHAR_EOS);
|
|
|
|
ApiStatus = (NET_API_STATUS) RegOpenKeyEx(
|
|
HKEY_LOCAL_MACHINE, // hKey
|
|
RegPathToWatch, // lpSubKey
|
|
0L, // ulOptions (reserved)
|
|
KEY_READ | KEY_NOTIFY, // desired access
|
|
&ConfigHandle // Newly Opened Key Handle
|
|
);
|
|
if (ApiStatus != NO_ERROR) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
ConfigHandleOpened = TRUE;
|
|
|
|
RegistryChangeEvent = CreateEvent(
|
|
NULL, // no security descriptor
|
|
FALSE, // use automatic reset
|
|
FALSE, // initial state: not signalled
|
|
NULL // no name
|
|
);
|
|
|
|
if (RegistryChangeEvent == NULL) {
|
|
ApiStatus = (NET_API_STATUS) GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
ApiStatus = RtlRegisterWait(
|
|
&TerminateWorkItem, // work item handle
|
|
WsGlobalData.TerminateNowEvent, // wait handle
|
|
WsTerminationNotify, // callback fcn
|
|
NULL, // parameter
|
|
INFINITE, // timeout
|
|
WT_EXECUTEONLYONCE | // flags
|
|
WT_EXECUTELONGFUNCTION);
|
|
|
|
if (!NT_SUCCESS(ApiStatus)) {
|
|
ApiStatus = RtlNtStatusToDosError(ApiStatus);
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Setup to monitor registry changes.
|
|
//
|
|
RegNotifyInfo.NotifyEventHandle = RegistryChangeEvent;
|
|
RegNotifyInfo.Timeout = INFINITE;
|
|
RegNotifyInfo.WorkItemHandle = NULL;
|
|
RegNotifyInfo.RegistryHandle = ConfigHandle;
|
|
|
|
|
|
EnterCriticalSection(&WsWorkerCriticalSection);
|
|
|
|
if (!WsReInitChangeNotify(&RegNotifyInfo)) {
|
|
ApiStatus = GetLastError();
|
|
RtlDeregisterWait(TerminateWorkItem);
|
|
TerminateWorkItem = NULL;
|
|
LeaveCriticalSection(&WsWorkerCriticalSection);
|
|
goto Cleanup;
|
|
}
|
|
|
|
LeaveCriticalSection(&WsWorkerCriticalSection);
|
|
|
|
//
|
|
// We are done with starting the Workstation service. Tell Service
|
|
// Controller our new status.
|
|
//
|
|
WsGlobalData.Status.dwCurrentState = SERVICE_RUNNING;
|
|
WsGlobalData.Status.dwControlsAccepted = SERVICE_ACCEPT_STOP |
|
|
SERVICE_ACCEPT_PAUSE_CONTINUE |
|
|
SERVICE_ACCEPT_SHUTDOWN;
|
|
WsGlobalData.Status.dwCheckPoint = 0;
|
|
WsGlobalData.Status.dwWaitHint = 0;
|
|
|
|
if ((ApiStatus = WsUpdateStatus()) != NERR_Success) {
|
|
|
|
WsInitializeStatusError = ApiStatus;
|
|
DbgPrint("WKSSVC failed with WsUpdateStatus %x\n",ApiStatus);
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// This thread has done all that it can do. So we can return it
|
|
// to the service controller.
|
|
//
|
|
return;
|
|
|
|
Cleanup:
|
|
DbgPrint("WKSSVC ServiceMain returned with %x\n",ApiStatus);
|
|
|
|
WsTerminationNotify(NULL, NO_ERROR);
|
|
return;
|
|
}
|
|
|
|
|
|
BOOL
|
|
WsReInitChangeNotify(
|
|
PREG_NOTIFY_INFO pNotifyInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
NOTE: This function should only be called when in the
|
|
WsWorkerCriticalSection.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
|
|
--*/
|
|
{
|
|
BOOL bStat = TRUE;
|
|
NTSTATUS Status;
|
|
|
|
Status = RegNotifyChangeKeyValue (ConfigHandle,
|
|
TRUE, // watch a subtree
|
|
REG_NOTIFY_CHANGE_LAST_SET,
|
|
RegistryChangeEvent,
|
|
TRUE // async call
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
NetpKdPrint((PREFIX_WKSTA "Couldn't Initialize Registry Notify %d\n",
|
|
RtlNtStatusToDosError(Status)));
|
|
bStat = FALSE;
|
|
goto CleanExit;
|
|
}
|
|
//
|
|
// Add the work item that is to be called when the
|
|
// RegistryChangeEvent is signalled.
|
|
//
|
|
Status = RtlRegisterWait(
|
|
&pNotifyInfo->WorkItemHandle,
|
|
pNotifyInfo->NotifyEventHandle,
|
|
WsRegistryNotify,
|
|
(PVOID)pNotifyInfo,
|
|
pNotifyInfo->Timeout,
|
|
WT_EXECUTEONLYONCE | WT_EXECUTEINPERSISTENTIOTHREAD);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
NetpKdPrint((PREFIX_WKSTA "Couldn't add Reg Notify work item\n"));
|
|
bStat = FALSE;
|
|
}
|
|
|
|
CleanExit:
|
|
if (bStat) {
|
|
if (WsNumWorkerThreads == 0) {
|
|
WsNumWorkerThreads++;
|
|
}
|
|
}
|
|
else {
|
|
if (WsNumWorkerThreads == 1) {
|
|
WsNumWorkerThreads--;
|
|
}
|
|
}
|
|
|
|
return(bStat);
|
|
|
|
}
|
|
|
|
DWORD
|
|
WsRegistryNotify(
|
|
LPVOID pParms,
|
|
BOOLEAN fWaitStatus
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Handles Workstation Registry Notification. This function is called by a
|
|
thread pool Worker thread when the event used for registry notification is
|
|
signaled.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
NET_API_STATUS ApiStatus;
|
|
PREG_NOTIFY_INFO pNotifyinfo=(PREG_NOTIFY_INFO)pParms;
|
|
NET_CONFIG_HANDLE NetConfigHandle;
|
|
|
|
UNREFERENCED_PARAMETER(fWaitStatus);
|
|
|
|
//
|
|
// The NT thread pool requires explicit work item deregistration,
|
|
// even if we specified the WT_EXECUTEONLYONCE flag
|
|
//
|
|
RtlDeregisterWait(pNotifyinfo->WorkItemHandle);
|
|
|
|
EnterCriticalSection(&WsWorkerCriticalSection);
|
|
if (WsIsTerminating) {
|
|
WsNumWorkerThreads--;
|
|
SetEvent(WsGlobalData.TerminateNowEvent);
|
|
|
|
LeaveCriticalSection(&WsWorkerCriticalSection);
|
|
return(NO_ERROR);
|
|
}
|
|
|
|
//
|
|
// Serialize write access to config information
|
|
//
|
|
if (RtlAcquireResourceExclusive(&WsInfo.ConfigResource, TRUE)) {
|
|
|
|
//
|
|
// Update the redir fields based on change notify.
|
|
// WsUpdateWkstaToMatchRegistry expects a NET_CONFIG_HANDLE
|
|
// handle, so we conjure up one from the HKEY handle.
|
|
//
|
|
NetConfigHandle.WinRegKey = ConfigHandle;
|
|
|
|
WsUpdateWkstaToMatchRegistry(&NetConfigHandle, FALSE);
|
|
|
|
ApiStatus = WsUpdateRedirToMatchWksta(
|
|
PARMNUM_ALL,
|
|
NULL
|
|
);
|
|
|
|
// NetpAssert( ApiStatus == NO_ERROR );
|
|
|
|
RtlReleaseResource(&WsInfo.ConfigResource);
|
|
}
|
|
|
|
if (!WsReInitChangeNotify(&RegNotifyInfo)) {
|
|
//
|
|
// If we can't add the work item, then we just won't
|
|
// listen for registry changes. There's not a whole
|
|
// lot we can do here.
|
|
//
|
|
ApiStatus = GetLastError();
|
|
}
|
|
|
|
LeaveCriticalSection(&WsWorkerCriticalSection);
|
|
|
|
return(NO_ERROR);
|
|
}
|
|
|
|
|
|
VOID
|
|
WsTerminationNotify(
|
|
LPVOID pParms,
|
|
BOOLEAN fWaitStatus
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function gets called by a services worker thread when the
|
|
termination event gets signaled.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
UNREFERENCED_PARAMETER(pParms);
|
|
UNREFERENCED_PARAMETER(fWaitStatus);
|
|
|
|
IF_DEBUG(MAIN) {
|
|
NetpKdPrint((PREFIX_WKSTA "WORKSTATION_main: cleaning up, "
|
|
"api status.\n"));
|
|
}
|
|
|
|
//
|
|
// The NT thread pool requires explicit work item deregistration,
|
|
// even if we specified the WT_EXECUTEONLYONCE flag
|
|
//
|
|
if (TerminateWorkItem != NULL) {
|
|
RtlDeregisterWait(TerminateWorkItem);
|
|
TerminateWorkItem = NULL;
|
|
}
|
|
|
|
if ( !WsWorkerCriticalSectionInitialized ) {
|
|
// We try to initialize this at the very beginning.
|
|
// So, if that failed, then nothing else has been done.
|
|
// So, just exit.
|
|
return;
|
|
}
|
|
|
|
EnterCriticalSection(&WsWorkerCriticalSection);
|
|
WsIsTerminating = TRUE;
|
|
|
|
//
|
|
// Must close winreg handle (which turns off notify) before event handle.
|
|
// Closing the regkey handle generates a change notify event!
|
|
//
|
|
if (ConfigHandleOpened) {
|
|
(VOID) RegCloseKey(ConfigHandle);
|
|
ConfigHandleOpened = FALSE;
|
|
#if DBG
|
|
//
|
|
// Workaround for a benign winreg assertion caused by us
|
|
// closing the RegistryChangeEvent handle which it wants
|
|
// to signal.
|
|
//
|
|
Sleep(2000);
|
|
#endif
|
|
}
|
|
if (RegPathToWatch != NULL) {
|
|
(VOID) NetApiBufferFree(RegPathToWatch);
|
|
RegPathToWatch = NULL;
|
|
}
|
|
if ((RegistryChangeEvent != NULL) && (WsNumWorkerThreads != 0)) {
|
|
|
|
//
|
|
// There is still a RegistryNotify Work Item in the system, we
|
|
// will attempt to remove it by setting the event to wake it up.
|
|
//
|
|
|
|
ResetEvent(WsGlobalData.TerminateNowEvent);
|
|
|
|
LeaveCriticalSection(&WsWorkerCriticalSection);
|
|
SetEvent(RegistryChangeEvent);
|
|
//
|
|
// Wait until the WsRegistryNotify Thread is finished.
|
|
// We will give it 60 seconds. If the thread isn't
|
|
// finished in that time frame, we will go on anyway.
|
|
//
|
|
WaitForSingleObject(
|
|
WsGlobalData.TerminateNowEvent,
|
|
60000);
|
|
|
|
if (WsNumWorkerThreads != 0) {
|
|
NetpKdPrint((PREFIX_WKSTA "WsTerminationNotify: "
|
|
"Registry Notification thread didn't terminate\n"));
|
|
}
|
|
|
|
EnterCriticalSection(&WsWorkerCriticalSection);
|
|
}
|
|
if ( RegistryChangeEvent != NULL ) {
|
|
(VOID) CloseHandle(RegistryChangeEvent);
|
|
RegistryChangeEvent = NULL;
|
|
}
|
|
|
|
//
|
|
// Shutting down
|
|
//
|
|
// NOTE: We must synchronize with the RegistryNotification Thread.
|
|
//
|
|
WsShutdownWorkstation(
|
|
WsInitializeStatusError,
|
|
WsInitState
|
|
);
|
|
|
|
WsIsTerminating = FALSE;
|
|
|
|
LeaveCriticalSection(&WsWorkerCriticalSection);
|
|
DeleteCriticalSection(&WsWorkerCriticalSection);
|
|
WsWorkerCriticalSectionInitialized = FALSE;
|
|
|
|
return;
|
|
}
|
|
|
|
STATIC
|
|
NET_API_STATUS
|
|
WsInitializeWorkstation(
|
|
OUT LPDWORD WsInitState
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function initializes the Workstation service.
|
|
|
|
Arguments:
|
|
|
|
WsInitState - Returns a flag to indicate how far we got with initializing
|
|
the Workstation service before an error occured.
|
|
|
|
Return Value:
|
|
|
|
NET_API_STATUS - NERR_Success or reason for failure.
|
|
|
|
--*/
|
|
{
|
|
NET_API_STATUS status;
|
|
RPC_STATUS rpcStatus;
|
|
int i;
|
|
|
|
//
|
|
// Initialize all the status fields so that subsequent calls to
|
|
// SetServiceStatus need to only update fields that changed.
|
|
//
|
|
WsGlobalData.Status.dwServiceType = SERVICE_WIN32;
|
|
WsGlobalData.Status.dwCurrentState = SERVICE_START_PENDING;
|
|
WsGlobalData.Status.dwControlsAccepted = 0;
|
|
WsGlobalData.Status.dwCheckPoint = 1;
|
|
WsGlobalData.Status.dwWaitHint = WS_WAIT_HINT_TIME;
|
|
|
|
SET_SERVICE_EXITCODE(
|
|
NO_ERROR,
|
|
WsGlobalData.Status.dwWin32ExitCode,
|
|
WsGlobalData.Status.dwServiceSpecificExitCode
|
|
);
|
|
|
|
//
|
|
// Initialize workstation to receive service requests by registering the
|
|
// control handler.
|
|
//
|
|
if ((WsGlobalData.StatusHandle = RegisterServiceCtrlHandler(
|
|
SERVICE_WORKSTATION,
|
|
WkstaControlHandler
|
|
)) == (SERVICE_STATUS_HANDLE) 0) {
|
|
|
|
status = GetLastError();
|
|
WsInitializeStatusError = status;
|
|
DbgPrint("WKSSVC failed with RegisterServiceCtrlHandler %x\n",status);
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Notify the Service Controller for the first time that we are alive
|
|
// and we are start pending
|
|
//
|
|
if ((status = WsUpdateStatus()) != NERR_Success) {
|
|
|
|
WsInitializeStatusError = status;
|
|
DbgPrint("WKSSVC failed with WsUpdateStatus %x\n",status);
|
|
return status;
|
|
}
|
|
|
|
try {
|
|
InitializeCriticalSection(&WsWorkerCriticalSection);
|
|
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
|
|
status = ERROR_NOT_ENOUGH_MEMORY;
|
|
return status;
|
|
}
|
|
WsWorkerCriticalSectionInitialized = TRUE;
|
|
|
|
//
|
|
// Initialize NetJoin logging
|
|
//
|
|
NetpInitializeLogFile();
|
|
|
|
//
|
|
// Initialize the resource for serializing access to configuration
|
|
// information.
|
|
//
|
|
// This must be done before the redir is initialized
|
|
|
|
try {
|
|
RtlInitializeResource(&WsInfo.ConfigResource);
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
return RtlNtStatusToDosError(GetExceptionCode());
|
|
}
|
|
(*WsInitState) |= WS_CONFIG_RESOURCE_INITIALIZED;
|
|
|
|
//
|
|
// Create an event which is used by the service control handler to notify
|
|
// the Workstation service that it is time to terminate.
|
|
//
|
|
if ((WsGlobalData.TerminateNowEvent =
|
|
CreateEvent(
|
|
NULL, // Event attributes
|
|
TRUE, // Event must be manually reset
|
|
FALSE,
|
|
NULL // Initial state not signalled
|
|
)) == NULL) {
|
|
|
|
status = GetLastError();
|
|
WsInitializeStatusError = status;
|
|
return status;
|
|
}
|
|
(*WsInitState) |= WS_TERMINATE_EVENT_CREATED;
|
|
|
|
//
|
|
// Initialize the workstation as a logon process with LSA, and
|
|
// get the MS V 1.0 authentication package ID.
|
|
//
|
|
|
|
if ((status = WsInitializeLsa()) != NERR_Success) {
|
|
|
|
WsInitializeStatusError = status;
|
|
DbgPrint("WKSSVC failed with WsInitializeLsa %x\n",status);
|
|
return status;
|
|
}
|
|
|
|
(*WsInitState) |= WS_LSA_INITIALIZED;
|
|
|
|
//
|
|
// Read the configuration information to initialize the redirector and
|
|
// datagram receiver
|
|
//
|
|
if ((status = WsInitializeRedirector()) != NERR_Success) {
|
|
|
|
WsInitializeStatusError = status;
|
|
DbgPrint("WKSSVC failed with WsInitializeRedirector %x\n",status);
|
|
return status;
|
|
}
|
|
(*WsInitState) |= WS_DEVICES_INITIALIZED;
|
|
|
|
//
|
|
// Service install still pending. Update checkpoint counter and the
|
|
// status with the Service Controller.
|
|
//
|
|
(WsGlobalData.Status.dwCheckPoint)++;
|
|
(void) WsUpdateStatus();
|
|
|
|
//
|
|
// Bind to transports
|
|
//
|
|
if ((status = WsBindToTransports()) != NERR_Success) {
|
|
|
|
WsInitializeStatusError = status;
|
|
DbgPrint("WKSSVC failed with WsBindToTransports %x\n",status);
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Service install still pending. Update checkpoint counter and the
|
|
// status with the Service Controller.
|
|
//
|
|
(WsGlobalData.Status.dwCheckPoint)++;
|
|
(void) WsUpdateStatus();
|
|
|
|
//
|
|
// Add domain names.
|
|
//
|
|
if ((status = WsAddDomains()) != NERR_Success) {
|
|
|
|
WsInitializeStatusError = status;
|
|
DbgPrint("WKSSVC failed with WsAddDomains %x\n",status);
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Service start still pending. Update checkpoint counter and the
|
|
// status with the Service Controller.
|
|
//
|
|
(WsGlobalData.Status.dwCheckPoint)++;
|
|
(void) WsUpdateStatus();
|
|
|
|
//
|
|
// Create Workstation service API data structures
|
|
//
|
|
if ((status = WsCreateApiStructures(WsInitState)) != NERR_Success) {
|
|
|
|
WsInitializeStatusError = status;
|
|
DbgPrint("WKSSVC failed with WsCreateApiStructures %x\n",status);
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Initialize the workstation service to receive RPC requests
|
|
//
|
|
// NOTE: Now all RPC servers in services.exe share the same pipe name.
|
|
// However, in order to support communication with version 1.0 of WinNt,
|
|
// it is necessary for the Client Pipe name to remain the same as
|
|
// it was in version 1.0. Mapping to the new name is performed in
|
|
// the Named Pipe File System code.
|
|
//
|
|
|
|
//
|
|
// Register with our protseq and the endpoint we expect to get called on
|
|
//
|
|
rpcStatus = RpcServerUseProtseqEpW(
|
|
L"ncacn_np",
|
|
RPC_C_PROTSEQ_MAX_REQS_DEFAULT,
|
|
L"\\PIPE\\wkssvc",
|
|
NULL
|
|
);
|
|
// duplicate endpoint is ok
|
|
if ( rpcStatus == RPC_S_DUPLICATE_ENDPOINT ) {
|
|
rpcStatus = RPC_S_OK;
|
|
}
|
|
|
|
//
|
|
// Register our interface as an autolisten interface, and allow clients to call us,
|
|
// even unauthenticated ones. We do the access check separately on a per function
|
|
// basis when that function is called.
|
|
// Some functions can be called by guest / anonymous
|
|
//
|
|
if (rpcStatus == RPC_S_OK) {
|
|
rpcStatus = RpcServerRegisterIfEx(wkssvc_ServerIfHandle,
|
|
NULL,
|
|
NULL,
|
|
RPC_IF_AUTOLISTEN | RPC_IF_ALLOW_CALLBACKS_WITH_NO_AUTH ,
|
|
RPC_C_LISTEN_MAX_CALLS_DEFAULT,
|
|
WsRpcSecurityCallback
|
|
);
|
|
}
|
|
status = (rpcStatus == RPC_S_OK) ? NERR_Success : rpcStatus;
|
|
if (status != NERR_Success ) {
|
|
WsInitializeStatusError = status;
|
|
DbgPrint("WKSSVC failed with StartRpcServer %x\n",status);
|
|
return status;
|
|
}
|
|
|
|
(*WsInitState) |= WS_RPC_SERVER_STARTED;
|
|
|
|
|
|
|
|
//
|
|
// Lastly, we create a thread to communicate with the
|
|
// Dfs-enabled MUP driver.
|
|
//
|
|
|
|
if ((status = WsInitializeDfs()) != NERR_Success) {
|
|
WsInitializeStatusError = status;
|
|
DbgPrint("WKSSVC failed with WsInitializeDfs %x\n",status);
|
|
return status;
|
|
}
|
|
|
|
(*WsInitState) |= WS_DFS_THREAD_STARTED;
|
|
|
|
WsLUIDDeviceMapsEnabled = WsGetLUIDDeviceMapsEnabled();
|
|
|
|
(void) I_ScSetServiceBits(
|
|
WsGlobalData.StatusHandle,
|
|
SV_TYPE_WORKSTATION,
|
|
TRUE,
|
|
TRUE,
|
|
NULL
|
|
);
|
|
|
|
|
|
IF_DEBUG(MAIN) {
|
|
NetpKdPrint(("[Wksta] Successful Initialization\n"));
|
|
}
|
|
|
|
return NERR_Success;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
WsShutdownWorkstation(
|
|
IN NET_API_STATUS ErrorCode,
|
|
IN DWORD WsInitState
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function shuts down the Workstation service.
|
|
|
|
Arguments:
|
|
|
|
ErrorCode - Supplies the error code of the failure
|
|
|
|
WsInitState - Supplies a flag to indicate how far we got with initializing
|
|
the Workstation service before an error occured, thus the amount of
|
|
clean up needed.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
NET_API_STATUS status = NERR_Success;
|
|
|
|
|
|
//
|
|
// Service stop still pending. Update checkpoint counter and the
|
|
// status with the Service Controller.
|
|
//
|
|
(WsGlobalData.Status.dwCheckPoint)++;
|
|
(void) WsUpdateStatus();
|
|
|
|
if (WsInitState & WS_DFS_THREAD_STARTED) {
|
|
|
|
//
|
|
// Stop the Dfs thread
|
|
//
|
|
WsShutdownDfs();
|
|
}
|
|
|
|
|
|
if (WsInitState & WS_RPC_SERVER_STARTED) {
|
|
//
|
|
// Stop the RPC server
|
|
//
|
|
status = RpcServerUnregisterIf( wkssvc_ServerIfHandle,
|
|
NULL,
|
|
1
|
|
);
|
|
if ( status != NERR_Success ) {
|
|
DbgPrint("WKSSVC failed to unregister rpc interface with status %x\n",status);
|
|
}
|
|
}
|
|
|
|
if (WsInitState & WS_API_STRUCTURES_CREATED) {
|
|
//
|
|
// Destroy data structures created for Workstation APIs
|
|
//
|
|
WsDestroyApiStructures(WsInitState);
|
|
}
|
|
|
|
WsShutdownMessageSend();
|
|
|
|
//
|
|
// Don't need to ask redirector to unbind from its transports when
|
|
// cleaning up because the redirector will tear down the bindings when
|
|
// it stops.
|
|
//
|
|
|
|
if (WsInitState & WS_DEVICES_INITIALIZED) {
|
|
//
|
|
// Shut down the redirector and datagram receiver
|
|
//
|
|
status = WsShutdownRedirector();
|
|
}
|
|
|
|
//
|
|
// Delete resource for serializing access to config information
|
|
// This must be done only after the redir is shutdown
|
|
// We do this here, (the Init is done in WsInitializeWorkstation routine above)
|
|
// to avoid putting additional synchronization on delete
|
|
// Otherwise we get into the situation where, the redir is shutting down and
|
|
// it deletes the resource while someone has acquired it, causing bad things.
|
|
//
|
|
if ( WsInitState & WS_CONFIG_RESOURCE_INITIALIZED ) {
|
|
RtlDeleteResource(&WsInfo.ConfigResource);
|
|
}
|
|
|
|
if (WsInitState & WS_LSA_INITIALIZED) {
|
|
//
|
|
// Deregister workstation as logon process
|
|
//
|
|
WsShutdownLsa();
|
|
}
|
|
|
|
if (WsInitState & WS_TERMINATE_EVENT_CREATED) {
|
|
//
|
|
// Close handle to termination event
|
|
//
|
|
CloseHandle(WsGlobalData.TerminateNowEvent);
|
|
}
|
|
|
|
//
|
|
// Shut down NetJoin logging
|
|
//
|
|
NetpShutdownLogFile();
|
|
|
|
I_ScSetServiceBits(
|
|
WsGlobalData.StatusHandle,
|
|
SV_TYPE_WORKSTATION,
|
|
FALSE,
|
|
TRUE,
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// We are done with cleaning up. Tell Service Controller that we are
|
|
// stopped.
|
|
//
|
|
WsGlobalData.Status.dwCurrentState = SERVICE_STOPPED;
|
|
WsGlobalData.Status.dwControlsAccepted = 0;
|
|
|
|
|
|
if ((ErrorCode == NERR_Success) &&
|
|
(status == ERROR_REDIRECTOR_HAS_OPEN_HANDLES)) {
|
|
ErrorCode = status;
|
|
}
|
|
|
|
SET_SERVICE_EXITCODE(
|
|
ErrorCode,
|
|
WsGlobalData.Status.dwWin32ExitCode,
|
|
WsGlobalData.Status.dwServiceSpecificExitCode
|
|
);
|
|
|
|
WsGlobalData.Status.dwCheckPoint = 0;
|
|
WsGlobalData.Status.dwWaitHint = 0;
|
|
|
|
(void) WsUpdateStatus();
|
|
|
|
}
|
|
|
|
|
|
|
|
NET_API_STATUS
|
|
WsUpdateStatus(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function updates the Workstation service status with the Service
|
|
Controller.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
NET_API_STATUS - NERR_Success or reason for failure.
|
|
|
|
--*/
|
|
{
|
|
NET_API_STATUS status = NERR_Success;
|
|
|
|
if (WsGlobalData.StatusHandle == (SERVICE_STATUS_HANDLE) 0) {
|
|
NetpKdPrint((
|
|
"[Wksta] Cannot call SetServiceStatus, no status handle.\n"
|
|
));
|
|
|
|
return ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
if (! SetServiceStatus(WsGlobalData.StatusHandle, &WsGlobalData.Status)) {
|
|
|
|
status = GetLastError();
|
|
|
|
IF_DEBUG(MAIN) {
|
|
NetpKdPrint(("[Wksta] SetServiceStatus error %lu\n", status));
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
STATIC
|
|
NET_API_STATUS
|
|
WsCreateApiStructures(
|
|
IN OUT LPDWORD WsInitState
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function creates and initializes all the data structures required
|
|
for the Workstation APIs.
|
|
|
|
Arguments:
|
|
|
|
WsInitState - Returns the supplied flag of how far we got in the
|
|
Workstation service initialization process.
|
|
|
|
Return Value:
|
|
|
|
NET_API_STATUS - NERR_Success or reason for failure.
|
|
|
|
--*/
|
|
{
|
|
NET_API_STATUS status;
|
|
|
|
|
|
//
|
|
// Create workstation security objects
|
|
//
|
|
if ((status = WsCreateWkstaObjects()) != NERR_Success) {
|
|
return status;
|
|
}
|
|
(*WsInitState) |= WS_SECURITY_OBJECTS_CREATED;
|
|
|
|
//
|
|
// Create Use Table
|
|
//
|
|
if ((status = WsInitUseStructures()) != NERR_Success) {
|
|
return status;
|
|
}
|
|
(*WsInitState) |= WS_USE_TABLE_CREATED;
|
|
|
|
return NERR_Success;
|
|
}
|
|
|
|
|
|
|
|
STATIC
|
|
VOID
|
|
WsDestroyApiStructures(
|
|
IN DWORD WsInitState
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function destroys the data structures created for the Workstation
|
|
APIs.
|
|
|
|
Arguments:
|
|
|
|
WsInitState - Supplies a flag which tells us what API structures
|
|
were created in the initialization process and now have to be
|
|
cleaned up.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
if (WsInitState & WS_USE_TABLE_CREATED) {
|
|
//
|
|
// Destroy Use Table
|
|
//
|
|
WsDestroyUseStructures();
|
|
}
|
|
|
|
if (WsInitState & WS_SECURITY_OBJECTS_CREATED) {
|
|
//
|
|
// Destroy workstation security objects
|
|
//
|
|
WsDestroyWkstaObjects();
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
WkstaControlHandler(
|
|
IN DWORD Opcode
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the service control handler of the Workstation service.
|
|
|
|
Arguments:
|
|
|
|
Opcode - Supplies a value which specifies the action for the Workstation
|
|
service to perform.
|
|
|
|
Arg - Supplies a value which tells a service specifically what to do
|
|
for an operation specified by Opcode.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
IF_DEBUG(MAIN) {
|
|
NetpKdPrint(("[Wksta] In Control Handler\n"));
|
|
}
|
|
|
|
switch (Opcode) {
|
|
|
|
case SERVICE_CONTROL_PAUSE:
|
|
|
|
//
|
|
// Pause redirection of print and comm devices
|
|
//
|
|
WsPauseOrContinueRedirection(
|
|
PauseRedirection
|
|
);
|
|
|
|
break;
|
|
|
|
case SERVICE_CONTROL_CONTINUE:
|
|
|
|
//
|
|
// Resume redirection of print and comm devices
|
|
//
|
|
WsPauseOrContinueRedirection(
|
|
ContinueRedirection
|
|
);
|
|
|
|
break;
|
|
|
|
case SERVICE_CONTROL_SHUTDOWN:
|
|
|
|
//
|
|
// Lack of break is intentional!
|
|
//
|
|
|
|
case SERVICE_CONTROL_STOP:
|
|
|
|
if (WsGlobalData.Status.dwCurrentState != SERVICE_STOP_PENDING) {
|
|
|
|
IF_DEBUG(MAIN) {
|
|
NetpKdPrint(("[Wksta] Stopping workstation...\n"));
|
|
}
|
|
|
|
WsGlobalData.Status.dwCurrentState = SERVICE_STOP_PENDING;
|
|
WsGlobalData.Status.dwCheckPoint = 1;
|
|
WsGlobalData.Status.dwWaitHint = WS_WAIT_HINT_TIME;
|
|
|
|
//
|
|
// Send the status response.
|
|
//
|
|
(void) WsUpdateStatus();
|
|
|
|
if (! SetEvent(WsGlobalData.TerminateNowEvent)) {
|
|
|
|
//
|
|
// Problem with setting event to terminate Workstation
|
|
// service.
|
|
//
|
|
NetpKdPrint(("[Wksta] Error setting TerminateNowEvent "
|
|
FORMAT_API_STATUS "\n", GetLastError()));
|
|
NetpAssert(FALSE);
|
|
}
|
|
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case SERVICE_CONTROL_INTERROGATE:
|
|
break;
|
|
|
|
default:
|
|
IF_DEBUG(MAIN) {
|
|
NetpKdPrint(("Unknown workstation opcode " FORMAT_HEX_DWORD
|
|
"\n", Opcode));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Send the status response.
|
|
//
|
|
(void) WsUpdateStatus();
|
|
}
|
|
|
|
|
|
STATIC
|
|
BOOL
|
|
WsGetLUIDDeviceMapsEnabled(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function calls NtQueryInformationProcess() to determine if
|
|
LUID device maps are enabled
|
|
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
TRUE - LUID device maps are enabled
|
|
|
|
FALSE - LUID device maps are disabled
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
ULONG LUIDDeviceMapsEnabled;
|
|
BOOL Result;
|
|
|
|
Status = NtQueryInformationProcess( NtCurrentProcess(),
|
|
ProcessLUIDDeviceMapsEnabled,
|
|
&LUIDDeviceMapsEnabled,
|
|
sizeof(LUIDDeviceMapsEnabled),
|
|
NULL
|
|
);
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
IF_DEBUG(MAIN) {
|
|
NetpKdPrint(("[Wksta] NtQueryInformationProcess(WsLUIDDeviceMapsEnabled) failed "
|
|
"FORMAT_API_STATUS" "\n",Status));
|
|
}
|
|
Result = FALSE;
|
|
}
|
|
else {
|
|
Result = (LUIDDeviceMapsEnabled != 0);
|
|
}
|
|
|
|
return( Result );
|
|
}
|
|
|
|
|
|
RPC_STATUS WsRpcSecurityCallback(
|
|
IN RPC_IF_HANDLE *Interface,
|
|
IN void *Context
|
|
)
|
|
/*++
|
|
Routine description:
|
|
RPC security callback - called before any call is passed on to a function.
|
|
Check the protseq to see whether the client is using the protseq we expect - named pipes.
|
|
Lifted from NwRpcSecurityCallback
|
|
|
|
Return value:
|
|
RPC_S_ACCESS_DENIED if the protseq doesnt match named pipes,
|
|
RPC_S_OK otherwise.
|
|
|
|
--*/
|
|
{
|
|
RPC_STATUS Status;
|
|
RPC_BINDING_HANDLE ServerIfHandle;
|
|
LPWSTR binding = NULL;
|
|
LPWSTR protseq = NULL;
|
|
|
|
Status = RpcBindingServerFromClient((RPC_IF_HANDLE)Context, &ServerIfHandle);
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
return (RPC_S_ACCESS_DENIED);
|
|
}
|
|
Status = RpcBindingToStringBinding(ServerIfHandle, &binding);
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
Status = RPC_S_ACCESS_DENIED;
|
|
goto CleanUp;
|
|
}
|
|
Status = RpcStringBindingParse(binding, NULL, &protseq, NULL, NULL, NULL);
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
Status = RPC_S_ACCESS_DENIED;
|
|
}
|
|
else
|
|
{
|
|
if (lstrcmp(protseq, L"ncacn_np") != 0)
|
|
Status = RPC_S_ACCESS_DENIED;
|
|
}
|
|
CleanUp:
|
|
RpcBindingFree(&ServerIfHandle);
|
|
if ( binding )
|
|
{
|
|
RpcStringFreeW( &binding );
|
|
}
|
|
if ( protseq )
|
|
{
|
|
RpcStringFreeW( &protseq );
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|