|
|
// 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);
// CryptServiceMain(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 <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <svcs.h> // SVCS_
#include <wincrypt.h>
#include <cryptui.h>
#include "keysvr.h"
#include "lenroll.h"
#include "keysvc.h"
#include "keysvcc.h"
#include "cerrpc.h"
#include "service.h"
#include "unicode.h"
#include "unicode5.h"
#include "catdb.h"
#define CRYPTSVC_EVENT_STOP "CRYPTSVC_EVENT_STOP"
#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 CryptServiceMain(DWORD dwArgc, LPWSTR *lpszArgv);
extern BOOL _CatDBServiceInit(BOOL fUnInit);
//
// forward declaration for security callbacks
//
RPC_IF_CALLBACK_FN CryptSvcSecurityCallback;
long __stdcall CryptSvcSecurityCallback(void * Interface, void *Context) { RPC_STATUS Status; unsigned int RpcClientLocalFlag;
Status = I_RpcBindingIsClientLocal(NULL, &RpcClientLocalFlag); if (Status != RPC_S_OK) { return (RPC_S_ACCESS_DENIED); }
if (RpcClientLocalFlag == 0) { return (RPC_S_ACCESS_DENIED); }
return (RPC_S_OK); }
BOOL WINAPI DllMain( HMODULE hInst, DWORD dwReason, LPVOID lpReserved ) {
if( dwReason == DLL_PROCESS_ATTACH ) { g_hInst = hInst; DisableThreadLibraryCalls(hInst); }
return TRUE; }
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)CryptServiceMain }, { 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 { CryptServiceMain( 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: CryptServiceMain
//
// 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 CryptServiceMain(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;
case SERVICE_CONTROL_SHUTDOWN: 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 | SERVICE_ACCEPT_SHUTDOWN;
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
}
// this event is signalled when the
// service should end
//
HANDLE hServerStopEvent = 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 );
//
// FUNCTION: ServiceStart
//
// COMMENTS:
// The service
// stops when hServerStopEvent is signalled
DWORD ServiceStart( HINSTANCE hInstance, int nCmdShow ) {
DWORD dwLastError = ERROR_SUCCESS; BOOL fStartedKeyService = FALSE; BOOL bListConstruct = FALSE;
LPWSTR pwszServerPrincipalName = NULL; // 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 : CRYPTSVC_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; }
//
// 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; }
dwLastError = StartKeyService(); if(ERROR_SUCCESS != dwLastError) { dwLastError = GetLastError(); goto cleanup; }
if (!_CatDBServiceInit(FALSE)) { dwLastError = GetLastError(); goto cleanup; }
//
// Initialize the RPC interfaces for
// Key Service, ICertProt, etc.
RPC_STATUS status;
status = RpcServerUseProtseqEpW(KEYSVC_DEFAULT_PROT_SEQ, RPC_C_PROTSEQ_MAX_REQS_DEFAULT, KEYSVC_DEFAULT_ENDPOINT, NULL); //Security Descriptor
if(RPC_S_DUPLICATE_ENDPOINT == status) { status = RPC_S_OK; }
if (status) { dwLastError = status; goto cleanup; }
status = RpcServerUseProtseqEpW(KEYSVC_LOCAL_PROT_SEQ, //ncalrpc
RPC_C_PROTSEQ_MAX_REQS_DEFAULT, KEYSVC_LOCAL_ENDPOINT, //keysvc
NULL); //Security Descriptor
if(RPC_S_DUPLICATE_ENDPOINT == status) { status = RPC_S_OK; }
if (status) { dwLastError = status; goto cleanup; }
status = RpcServerRegisterIfEx(s_IKeySvc_v1_0_s_ifspec, NULL, NULL, RPC_IF_AUTOLISTEN, RPC_C_PROTSEQ_MAX_REQS_DEFAULT, NULL); if (status) { dwLastError = status; goto cleanup; }
status = RpcServerRegisterIfEx(s_IKeySvcR_v1_0_s_ifspec, NULL, NULL, RPC_IF_AUTOLISTEN, RPC_C_PROTSEQ_MAX_REQS_DEFAULT, NULL); if (status) { dwLastError = status; goto cleanup; }
status = RpcServerRegisterIfEx(s_ICertProtectFunctions_v1_0_s_ifspec, NULL, NULL, RPC_IF_AUTOLISTEN, RPC_C_PROTSEQ_MAX_REQS_DEFAULT, CryptSvcSecurityCallback);
if (status) { dwLastError = status; goto cleanup; }
status = RpcServerRegisterIfEx(s_ICatDBSvc_v1_0_s_ifspec, NULL, NULL, RPC_IF_AUTOLISTEN, RPC_C_PROTSEQ_MAX_REQS_DEFAULT, CryptSvcSecurityCallback); if (status) { dwLastError = status; goto cleanup; }
//
// 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; }
// allow clients to make authenticated requests
status = RpcServerInqDefaultPrincName(RPC_C_AUTHN_GSS_NEGOTIATE, &pwszServerPrincipalName); if (RPC_S_OK != status) { //BUGBUG: Shouldn't prevent service from startin just because auth failed.
// We should log an event here.
} else { status = RpcServerRegisterAuthInfo (pwszServerPrincipalName, RPC_C_AUTHN_GSS_NEGOTIATE, NULL, // Use default key acquisiting function
NULL // Use default creds
); if (RPC_S_OK != status) { //BUGBUG: Shouldn't prevent service from startin just because auth failed.
// We should log an event here.
} }
//
// 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(); }
if (NULL != pwszServerPrincipalName) { RpcStringFreeW(&pwszServerPrincipalName); } return dwLastError;
cleanup:
TeardownServer( dwLastError );
if (NULL != pwszServerPrincipalName) { RpcStringFreeW(&pwszServerPrincipalName); } 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 ) { RPC_STATUS rpcStatus; DWORD dwErrToReport = dwLastError;
//
// Unregister RPC Interfaces
rpcStatus = RpcServerUnregisterIf(s_IKeySvc_v1_0_s_ifspec, 0, 0); if ((rpcStatus != RPC_S_OK) && (dwErrToReport == ERROR_SUCCESS)) { dwErrToReport = rpcStatus; }
rpcStatus = RpcServerUnregisterIf(s_IKeySvcR_v1_0_s_ifspec, 0, 0); if ((rpcStatus != RPC_S_OK) && (dwErrToReport == ERROR_SUCCESS)) { dwErrToReport = rpcStatus; }
rpcStatus = RpcServerUnregisterIf(s_ICertProtectFunctions_v1_0_s_ifspec, 0, 0); if ((rpcStatus != RPC_S_OK) && (dwErrToReport == ERROR_SUCCESS)) { dwErrToReport = rpcStatus; }
rpcStatus = RpcServerUnregisterIf(s_ICatDBSvc_v1_0_s_ifspec, 0, 0); if ((rpcStatus != RPC_S_OK) && (dwErrToReport == ERROR_SUCCESS)) { dwErrToReport = rpcStatus; }
//
// stop backup key server
// Note: this function knows internally whether the backup key server
// really started or not.
//
StopKeyService();
_CatDBServiceInit(TRUE);
if(hServerStopEvent) { SetEvent(hServerStopEvent); // make event signalled to release anyone waiting for termination
CloseHandle(hServerStopEvent); hServerStopEvent = NULL; }
ReportStatusToSCMgr( SERVICE_STOPPED, dwErrToReport, 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(hServerStopEvent) { PulseEvent(hServerStopEvent); // signal waiting threads and reset to non-signalled
} }
extern "C" {
/*********************************************************************/ /* MIDL allocate and free */ /*********************************************************************/
void __RPC_FAR * __RPC_API midl_user_allocate(size_t len) { return(LocalAlloc(LMEM_ZEROINIT, 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, LocalSize( ptr ) ); LocalFree(ptr); }
void __RPC_FAR * __RPC_API midl_user_reallocate(void __RPC_FAR * ptr, size_t len) { return(LocalReAlloc(ptr, len, LMEM_MOVEABLE | LMEM_ZEROINIT)); }
}
|