Leaked source code of windows server 2003
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.
 
 
 
 
 
 

909 lines
21 KiB

// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (C) 1993-1995 Microsoft Corporation. All Rights Reserved.
//
// MODULE: service.c
//
// PURPOSE: Implements functions required by all services
// and takes into account dumbed-down win95 services
//
// FUNCTIONS:
// main(int argc, char **argv);
// service_ctrl(DWORD dwCtrlCode);
// PSTOREServiceMain(DWORD dwArgc, LPWSTR *lpszArgv);
// WinNTDebugService(int argc, char **argv);
// ControlHandler ( DWORD dwCtrlType );
//
// COMMENTS:
//
// AUTHOR: Craig Link - Microsoft Developer Support
// MODIFIED: Matt Thomlinson
// Scott Field
//
#include <pch.cpp>
#pragma hdrstop
#include <svcs.h>
#include "service.h"
// this event is signalled when the
// service should end
//
HANDLE hServerStopEvent = NULL;
//
// this event is used to allow external code to determine if we are initialized
// and running. Currently, this is only used by the WinNT and Win95 credential
// managers to prevent logon delays when the service is not available.
//
HANDLE hServiceStarted = NULL;
PACL pDaclInitEvent = NULL;
extern DWORD GlobalSecurityMask;
extern BOOL g_bAudit;
//
// waitable thread pool handle.
//
HANDLE hRegisteredWait = NULL;
VOID
TeardownServer(
DWORD dwLastError
);
VOID
NTAPI
TerminationNotify(
PVOID Context,
BOOLEAN TimerOrWaitFired
);
#define RTN_OK 0 // no errors
#define RTN_USAGE 1 // usage error (invalid commandline)
#define RTN_ERROR_INIT 2 // error during service initialization
#define RTN_ERROR_INSTALL 13 // error during -install or -remove
#define RTN_ERROR_INSTALL_SIG 14 // error installing signature(s)
#define RTN_ERROR_INSTALL_START 15 // could not start service during install
#define RTN_ERROR_INSTALL_SHEXT 16 // error installing shell extension
//
// global module handle used to reference resources contained in this module.
//
HINSTANCE g_hInst = NULL;
// internal variables
static SERVICE_STATUS ssStatus; // current status of the service
SERVICE_STATUS_HANDLE sshStatusHandle;
// internal function prototypes
void WINAPI service_ctrl(DWORD dwCtrlCode);
void WINAPI PSTOREServiceMain(DWORD dwArgc, LPWSTR *lpszArgv);
DWORD
WINAPI
Start(
LPVOID lpV
);
BOOL
WINAPI
DllMain(
HMODULE hInst,
DWORD dwReason,
LPVOID lpReserved
)
{
if( dwReason == DLL_PROCESS_ATTACH ) {
g_hInst = hInst;
DisableThreadLibraryCalls(hInst);
}
return TRUE;
}
VOID
WINAPI
ServiceEntry(
DWORD NumArgs,
LPWSTR *ArgsArray,
PVOID LmsvcsGlobalData,
HANDLE SvcRefHandle
)
{
Start( NULL );
}
DWORD
WINAPI
Start(
LPVOID lpV
)
{
BOOL fIsNT = FIsWinNT();
int iRet;
//
// surpress dialog boxes generated by missing files, etc.
//
SetErrorMode(SEM_NOOPENFILEERRORBOX);
SERVICE_TABLE_ENTRYW dispatchTable[] =
{
{ SZSERVICENAME, (LPSERVICE_MAIN_FUNCTIONW)PSTOREServiceMain },
{ NULL, NULL }
};
#ifdef WIN95_LEGACY
if (!fIsNT)
goto dispatch95;
#endif // WIN95_LEGACY
// if it doesn't match any of the above parameters
// the service control manager may be starting the service
// so we must call StartServiceCtrlDispatcher
if(!FIsWinNT5()) {
if (!StartServiceCtrlDispatcherW(dispatchTable))
AddToMessageLog(L"StartServiceCtrlDispatcher failed.");
} else {
PSTOREServiceMain( 0, NULL );
}
return RTN_OK;
#ifdef WIN95_LEGACY
dispatch95:
//
// Win95 doesn't support services, except as pseudo-.exe files
//
HMODULE hKernel = GetModuleHandleA("kernel32.dll");
if (NULL == hKernel)
{
AddToMessageLog(L"RegisterServiceProcess module handle failed");
return RTN_ERROR_INIT;
}
// inline typedef: COOL!
typedef DWORD REGISTERSERVICEPROCESS(
DWORD dwProcessId,
DWORD dwServiceType);
REGISTERSERVICEPROCESS* pfnRegSvcProc = NULL;
// Make sure Win95 Logoff won't stop our .exe
if (NULL == (pfnRegSvcProc = (REGISTERSERVICEPROCESS*)GetProcAddress(hKernel, "RegisterServiceProcess")))
{
AddToMessageLog(L"RegisterServiceProcess failed");
return RTN_ERROR_INIT;
}
pfnRegSvcProc(GetCurrentProcessId(), TRUE); // register this process ID as a service process
//
// call re-entry point and return with result of it.
//
iRet = ServiceStart(0, 0);
if(iRet != ERROR_SUCCESS)
AddToMessageLog(L"ServiceStart error!");
return iRet;
#endif // WIN95_LEGACY
}
//
// FUNCTION: PSTOREServiceMain
//
// PURPOSE: To perform actual initialization of the service
//
// PARAMETERS:
// dwArgc - number of command line arguments
// lpszArgv - array of command line arguments
//
// RETURN VALUE:
// none
//
// COMMENTS:
// This routine performs the service initialization and then calls
// the user defined ServiceStart() routine to perform majority
// of the work.
//
void WINAPI PSTOREServiceMain(DWORD dwArgc, LPWSTR * /*lpszArgv*/)
{
DWORD dwLastError = ERROR_SUCCESS;
// register our service control handler:
//
sshStatusHandle = RegisterServiceCtrlHandlerW( SZSERVICENAME, service_ctrl);
if (!sshStatusHandle)
return;
// SERVICE_STATUS members that don't change in example
//
ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
ssStatus.dwServiceSpecificExitCode = 0;
// report the status to the service control manager.
//
if (!ReportStatusToSCMgr(
SERVICE_START_PENDING, // service state
NO_ERROR, // exit code
3000 // wait hint
)) return ;
dwLastError = ServiceStart(0, 0);
return;
}
//
// FUNCTION: service_ctrl
//
// PURPOSE: This function is called by the SCM whenever
// ControlService() is called on this service.
//
// PARAMETERS:
// dwCtrlCode - type of control requested
//
// RETURN VALUE:
// none
//
// COMMENTS:
//
VOID WINAPI service_ctrl(DWORD dwCtrlCode)
{
// Handle the requested control code.
//
switch(dwCtrlCode)
{
// Stop the service.
//
case SERVICE_CONTROL_STOP:
//
// tell the SCM we are stopping before triggering StopService() code
// to avoid potential race condition during STOP_PENDING -> STOPPED transition
//
ssStatus.dwCurrentState = SERVICE_STOP_PENDING;
ReportStatusToSCMgr(ssStatus.dwCurrentState, NO_ERROR, 0);
ServiceStop();
return;
// Update the service status.
//
case SERVICE_CONTROL_INTERROGATE:
break;
// invalid control code
//
default:
break;
}
ReportStatusToSCMgr(ssStatus.dwCurrentState, NO_ERROR, 0);
}
//
// FUNCTION: ReportStatusToSCMgr()
//
// PURPOSE: Sets the current status of the service and
// reports it to the Service Control Manager
//
// PARAMETERS:
// dwCurrentState - the state of the service
// dwWin32ExitCode - error code to report
// dwWaitHint - worst case estimate to next checkpoint
//
// RETURN VALUE:
// TRUE - success
// FALSE - failure
//
// COMMENTS:
//
BOOL ReportStatusToSCMgr(DWORD dwCurrentState,
DWORD dwWin32ExitCode,
DWORD dwWaitHint)
{
static DWORD dwCheckPoint = 1;
BOOL fResult = TRUE;
if (dwCurrentState == SERVICE_START_PENDING)
ssStatus.dwControlsAccepted = 0;
else
ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
ssStatus.dwCurrentState = dwCurrentState;
if(dwWin32ExitCode == 0) {
ssStatus.dwWin32ExitCode = 0;
} else {
ssStatus.dwServiceSpecificExitCode = dwWin32ExitCode;
ssStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
}
ssStatus.dwWaitHint = dwWaitHint;
if ( ( dwCurrentState == SERVICE_RUNNING ) ||
( dwCurrentState == SERVICE_STOPPED ) )
ssStatus.dwCheckPoint = 0;
else
ssStatus.dwCheckPoint = dwCheckPoint++;
// Report the status of the service to the service control manager.
//
if (!(fResult = SetServiceStatus( sshStatusHandle, &ssStatus))) {
AddToMessageLog(L"SetServiceStatus");
}
return fResult;
}
//
// FUNCTION: AddToMessageLog(LPWSTR lpszMsg)
//
// PURPOSE: Allows any thread to log an error message
//
// PARAMETERS:
// lpszMsg - text for message
//
// RETURN VALUE:
// none
//
// COMMENTS:
//
VOID AddToMessageLog(LPWSTR lpszMsg)
{
DWORD dwLastError = GetLastError();
if(FIsWinNT()) {
//
// WinNT: Use event logging to log the error.
//
WCHAR szMsg[512];
HANDLE hEventSource;
LPWSTR lpszStrings[2];
hEventSource = RegisterEventSourceW(NULL, SZSERVICENAME);
if(hEventSource == NULL)
return;
wsprintfW(szMsg, L"%s error: %lu", SZSERVICENAME, dwLastError);
lpszStrings[0] = szMsg;
lpszStrings[1] = lpszMsg;
ReportEventW(hEventSource, // handle of event source
EVENTLOG_ERROR_TYPE, // event type
0, // event category
0, // event ID
NULL, // current user's SID
2, // strings in lpszStrings
0, // no bytes of raw data
(LPCWSTR*)lpszStrings, // array of error strings
NULL); // no raw data
(VOID) DeregisterEventSource(hEventSource);
}
#ifdef WIN95_LEGACY
else {
//
// Win95: log error to file
//
HANDLE hFile;
SYSTEMTIME st;
CHAR szMsgOut[512];
DWORD cchMsgOut;
DWORD dwBytesWritten;
hFile = CreateFileA(
"pstore.log",
GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
OPEN_ALWAYS,
0,
NULL
);
if(hFile == INVALID_HANDLE_VALUE)
return;
GetSystemTime( &st );
cchMsgOut = wsprintfA(szMsgOut, "%.2u-%.2u-%.2u %.2u:%.2u:%.2u %ls (rc=%lu)\015\012",
st.wMonth, st.wDay, st.wYear, st.wHour, st.wMinute, st.wSecond,
lpszMsg,
dwLastError
);
//
// seek to EOF
//
SetFilePointer(hFile, 0, NULL, FILE_END);
WriteFile(hFile, szMsgOut, cchMsgOut, &dwBytesWritten, NULL);
CloseHandle(hFile);
}
#endif // WIN95_LEGACY
}
RPC_STATUS
RPC_ENTRY
PstoreCallback(
RPC_IF_HANDLE idIF,
PVOID pCtx)
{
RPC_STATUS Status;
PWSTR pBinding = NULL;
PWSTR pProtSeq = NULL;
Status = RpcBindingToStringBinding(pCtx, &pBinding);
if(Status != RPC_S_OK)
{
goto cleanup;
}
Status = RpcStringBindingParse(pBinding,
NULL,
&pProtSeq,
NULL,
NULL,
NULL);
if(Status != RPC_S_OK)
{
goto cleanup;
}
// Make sure caller is using local RPC
if(CompareString(LOCALE_INVARIANT,
NORM_IGNORECASE,
pProtSeq,
-1,
PSTORE_LOCAL_PROT_SEQ,
-1) != CSTR_EQUAL)
{
Status = ERROR_ACCESS_DENIED;
goto cleanup;
}
Status = RPC_S_OK;
cleanup:
if(pProtSeq)
{
RpcStringFree(&pProtSeq);
}
if(pBinding)
{
RpcStringFree(&pBinding);
}
return Status;
}
//
// FUNCTION: ServiceStart
//
// COMMENTS:
// The service
// stops when hServerStopEvent is signalled
DWORD
ServiceStart(
HINSTANCE hInstance,
int nCmdShow
)
{
SECURITY_ATTRIBUTES sa;
PSID pEveryoneSid = NULL;
SECURITY_DESCRIPTOR sdInitEvent;
RPC_STATUS status = 0;
SID_IDENTIFIER_AUTHORITY sia = SECURITY_WORLD_SID_AUTHORITY;
DWORD EveryoneSidBuffer[6];
DWORD dwAclSize;
DWORD dwLastError = ERROR_SUCCESS;
BOOL fStartedKeyService = FALSE;
BOOL bListConstruct = FALSE;
pEveryoneSid = (PSID)EveryoneSidBuffer;
InitializeSid(pEveryoneSid, &sia, 1);
*(GetSidSubAuthority( pEveryoneSid, 0)) = SECURITY_WORLD_RID;
dwAclSize = sizeof(ACL) +
1 * ( sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) ) +
GetLengthSid(pEveryoneSid) ;
pDaclInitEvent = (PACL)SSAlloc(dwAclSize);
if(pDaclInitEvent == NULL)
{
dwLastError = ERROR_NOT_ENOUGH_MEMORY;
goto cleanup;
}
if(!InitializeAcl(pDaclInitEvent, dwAclSize, ACL_REVISION))
{
dwLastError = GetLastError();
goto cleanup;
}
if(!AddAccessAllowedAce(
pDaclInitEvent,
ACL_REVISION,
SYNCHRONIZE,
pEveryoneSid
))
{
dwLastError = GetLastError();
goto cleanup;
}
if(!InitializeSecurityDescriptor(
&sdInitEvent,
SECURITY_DESCRIPTOR_REVISION
))
{
dwLastError = GetLastError();
goto cleanup;
}
if(!SetSecurityDescriptorDacl(
&sdInitEvent,
TRUE,
pDaclInitEvent,
FALSE
))
{
dwLastError = GetLastError();
goto cleanup;
}
sa.lpSecurityDescriptor = &sdInitEvent;
//
// create the event object. The control handler function signals
// this event when it receives the "stop" control code.
// On WinNT, we let security default to local system+admins access.
// On WinNT, the ServiceStop() API is the correct way to cause a service
// to stop, so we let Service Control manager dictate who can do it.
//
// Only on Win95 do we use a named event, in order to support shutting
// down the server cleanly on that platform, since Win95 does not support
// real services.
hServerStopEvent = CreateEventA(
NULL,
TRUE, // manual reset event
FALSE, // not-signalled
(FIsWinNT() ? NULL : PST_EVENT_STOP) // WinNT: unnamed, Win95 named
);
//
// if event already exists, terminate quietly so that only one instance
// of the service is allowed.
//
if(hServerStopEvent && GetLastError() == ERROR_ALREADY_EXISTS) {
CloseHandle(hServerStopEvent);
hServerStopEvent = NULL;
}
if(hServerStopEvent == NULL) {
dwLastError = GetLastError();
goto cleanup;
}
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = FALSE;
hServiceStarted = CreateEventA(
&sa, // security attributes for WinNT
TRUE, // manual reset event
FALSE, // not-signalled
PST_EVENT_INIT_NT5
);
if(hServiceStarted == NULL) {
dwLastError = GetLastError();
goto cleanup;
}
//
// free Dacl on event, since we no longer need it.
//
if(pDaclInitEvent) {
SSFree(pDaclInitEvent);
pDaclInitEvent = NULL;
}
//
// report the status to the service control manager.
// (service start still pending).
//
if (!ReportStatusToSCMgr(
SERVICE_START_PENDING, // service state
NO_ERROR, // exit code
3000 // wait hint
)) {
dwLastError = GetLastError();
goto cleanup;
}
bListConstruct = ListConstruct();
if(!bListConstruct)
{
dwLastError = GetLastError();
goto cleanup;
}
status = RpcServerUseProtseqEpW(PSTORE_LOCAL_PROT_SEQ, //ncalrpc
RPC_C_PROTSEQ_MAX_REQS_DEFAULT,
PSTORE_LOCAL_ENDPOINT, //protected_storage
NULL); //Security Descriptor
if(RPC_S_DUPLICATE_ENDPOINT == status)
{
status = RPC_S_OK;
}
if ( status != RPC_S_OK )
{
dwLastError = status;
goto cleanup;
}
status = RpcServerRegisterIfEx(s_IPStoreProv_v1_0_s_ifspec,
NULL,
NULL,
RPC_IF_AUTOLISTEN,
RPC_C_PROTSEQ_MAX_REQS_DEFAULT,
PstoreCallback);
if ( status != RPC_S_OK )
{
dwLastError = status;
goto cleanup;
}
SetEvent(hServiceStarted); // signal service is ready to take requests
//
// report the status to the service control manager.
//
if (!ReportStatusToSCMgr(
SERVICE_RUNNING, // service state
NO_ERROR, // exit code
0 // wait hint
)) {
dwLastError = GetLastError();
goto cleanup;
}
//
// on WinNT5, ask services.exe to notify us when the service is shutting
// down, and return this thread to the work item queue.
//
if(!RegisterWaitForSingleObject(
&hRegisteredWait,
hServerStopEvent, // wait handle
TerminationNotify, // callback fcn
NULL, // parameter
INFINITE, // timeout
WT_EXECUTELONGFUNCTION | WT_EXECUTEONLYONCE
)) {
hRegisteredWait = NULL;
dwLastError = GetLastError();
}
return dwLastError;
cleanup:
TeardownServer( dwLastError );
return dwLastError;
}
VOID
NTAPI
TerminationNotify(
PVOID Context,
BOOLEAN TimerOrWaitFired
)
/*++
Routine Description:
This function gets called by a services worker thread when the
termination event gets signaled.
Arguments:
Return Value:
--*/
{
//
// per JSchwart:
// safe to unregister during callback.
//
if( hRegisteredWait ) {
UnregisterWaitEx( hRegisteredWait, NULL );
hRegisteredWait = NULL;
}
TeardownServer( ERROR_SUCCESS );
}
VOID
TeardownServer(
DWORD dwLastError
)
{
//
// ignore errors because we are shutting down
//
RpcServerUnregisterIf(s_IPStoreProv_v1_0_s_ifspec, 0, 0);
if(pDaclInitEvent) {
SSFree(pDaclInitEvent);
pDaclInitEvent = NULL;
}
if(hServiceStarted) {
ResetEvent(hServiceStarted);
CloseHandle(hServiceStarted);
hServiceStarted = NULL;
}
if(hServerStopEvent) {
SetEvent(hServerStopEvent); // make event signalled to release anyone waiting for termination
CloseHandle(hServerStopEvent);
hServerStopEvent = NULL;
}
ListTeardown();
ReportStatusToSCMgr(
SERVICE_STOPPED,
dwLastError,
0
);
}
// FUNCTION: ServiceStop
//
// PURPOSE: Stops the service
//
// COMMENTS:
// If a ServiceStop procedure is going to
// take longer than 3 seconds to execute,
// it should spawn a thread to execute the
// stop code, and return. Otherwise, the
// ServiceControlManager will believe that
// the service has stopped responding.
//
VOID ServiceStop()
{
if(hServiceStarted) {
ResetEvent(hServiceStarted);
CloseHandle(hServiceStarted);
hServiceStarted = NULL;
}
if(hServerStopEvent)
PulseEvent(hServerStopEvent); // signal waiting threads and reset to non-signalled
}
/*********************************************************************/
/* MIDL allocate and free */
/*********************************************************************/
void __RPC_FAR * __RPC_API midl_user_allocate(size_t len)
{
return(SSAlloc(len));
}
void __RPC_API midl_user_free(void __RPC_FAR * ptr)
{
//
// sfield: zero memory before freeing it.
// do this because RPC allocates alot on our behalf, and we want to
// be as sanitary as possible with respect to not letting anything
// sensitive go to pagefile.
//
ZeroMemory( ptr, SSSize( ptr ) );
SSFree(ptr);
}