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.
1054 lines
26 KiB
1054 lines
26 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);
|
|
// 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 <stdio.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"
|
|
#include "cryptmsg.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);
|
|
BOOL IsAdministrator(VOID);
|
|
|
|
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);
|
|
}
|
|
|
|
//
|
|
// Returns RPC_S_OK if the binding represents a local administrator on this box,
|
|
// AND if the binding is local.
|
|
//
|
|
long __stdcall
|
|
KeySvcSecurityCallback(void * Interface, void *Context)
|
|
{
|
|
BOOL fIsImpersonatingClient = FALSE;
|
|
RPC_STATUS rpcStatus;
|
|
unsigned int RpcClientLocalFlag;
|
|
|
|
rpcStatus = I_RpcBindingIsClientLocal(NULL, &RpcClientLocalFlag);
|
|
if (rpcStatus != RPC_S_OK)
|
|
goto error;
|
|
|
|
if (!RpcClientLocalFlag) {
|
|
rpcStatus = RPC_S_ACCESS_DENIED;
|
|
goto error;
|
|
}
|
|
|
|
rpcStatus = RpcImpersonateClient(NULL);
|
|
if (rpcStatus != RPC_S_OK)
|
|
goto error;
|
|
fIsImpersonatingClient = TRUE;
|
|
|
|
if (!IsAdministrator()) {
|
|
rpcStatus = RPC_S_ACCESS_DENIED;
|
|
goto error;
|
|
}
|
|
|
|
rpcStatus = RPC_S_OK;
|
|
error:
|
|
if (fIsImpersonatingClient) {
|
|
RPC_STATUS rpcStatus2 = RpcRevertToSelfEx(NULL);
|
|
if (RPC_S_OK != rpcStatus2) {
|
|
MyLogErrorMessage(rpcStatus2, MSG_KEYSVC_REVERT_TO_SELF_FAILED);
|
|
}
|
|
}
|
|
return rpcStatus;
|
|
}
|
|
|
|
//
|
|
// Returns RPC_S_OK if the binding represents a local administrator on this box.
|
|
// Does not require that the binding be local.
|
|
//
|
|
long __stdcall
|
|
RKeySvcSecurityCallback(void * Interface, void *Context)
|
|
{
|
|
BOOL fIsImpersonatingClient = FALSE;
|
|
RPC_STATUS rpcStatus;
|
|
|
|
rpcStatus = RpcImpersonateClient(NULL);
|
|
if (rpcStatus != RPC_S_OK)
|
|
goto error;
|
|
fIsImpersonatingClient = TRUE;
|
|
|
|
if (!IsAdministrator()) {
|
|
rpcStatus = RPC_S_ACCESS_DENIED;
|
|
goto error;
|
|
}
|
|
|
|
rpcStatus = RPC_S_OK;
|
|
error:
|
|
if (fIsImpersonatingClient) {
|
|
RPC_STATUS rpcStatus2 = RpcRevertToSelfEx(NULL);
|
|
if (RPC_S_OK != rpcStatus2) {
|
|
MyLogErrorMessage(rpcStatus2, MSG_KEYSVC_REVERT_TO_SELF_FAILED);
|
|
}
|
|
}
|
|
return rpcStatus;
|
|
}
|
|
|
|
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
|
|
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
VOID MyLogEvent(WORD wType, DWORD dwEventID, unsigned int nStrings, const WCHAR ** rgwszStrings) {
|
|
HANDLE hEventLog = NULL;
|
|
|
|
hEventLog = RegisterEventSource(NULL, L"cryptsvc");
|
|
if (NULL == hEventLog) {
|
|
goto error;
|
|
}
|
|
|
|
if (!ReportEvent(hEventLog, wType, 0/*category*/, dwEventID, NULL, (WORD)nStrings, 0, rgwszStrings, NULL)) {
|
|
goto error;
|
|
}
|
|
|
|
error:
|
|
if (NULL != hEventLog) {
|
|
DeregisterEventSource(hEventLog);
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
VOID MyLogErrorMessage(DWORD dwErr, DWORD dwMsgId) {
|
|
const WCHAR *rgwszStrings[2];
|
|
WCHAR wszNumberBuf[20];
|
|
WCHAR *pwszError = NULL;
|
|
|
|
swprintf(wszNumberBuf, L"0x%08X", dwErr);
|
|
rgwszStrings[0] = wszNumberBuf;
|
|
|
|
if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
FORMAT_MESSAGE_FROM_SYSTEM |
|
|
FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
NULL,
|
|
dwErr,
|
|
0,
|
|
(LPWSTR) &pwszError,
|
|
0,
|
|
NULL)) {
|
|
rgwszStrings[1] = pwszError;
|
|
} else {
|
|
rgwszStrings[1] = L"";
|
|
}
|
|
|
|
MyLogEvent(EVENTLOG_ERROR_TYPE, dwMsgId, 2, rgwszStrings);
|
|
|
|
if (NULL != pwszError) {
|
|
LocalFree(pwszError);
|
|
}
|
|
}
|
|
|
|
|
|
BOOL IsAdministrator(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
This function determines if the calling user is an Administrator.
|
|
|
|
On Windows NT, the caller of this function must be impersonating
|
|
the user which is to be queried. If the caller is not impersonating,
|
|
this function will always return FALSE.
|
|
|
|
--*/
|
|
{
|
|
HANDLE hAccessToken;
|
|
SID_IDENTIFIER_AUTHORITY siaNtAuthority = SECURITY_NT_AUTHORITY;
|
|
PSID psidAdministrators = NULL;
|
|
BOOL bSuccess;
|
|
|
|
if(!OpenThreadToken(
|
|
GetCurrentThread(),
|
|
TOKEN_QUERY,
|
|
TRUE,
|
|
&hAccessToken
|
|
)) return FALSE;
|
|
|
|
bSuccess = AllocateAndInitializeSid(
|
|
&siaNtAuthority,
|
|
2,
|
|
SECURITY_BUILTIN_DOMAIN_RID,
|
|
DOMAIN_ALIAS_RID_ADMINS,
|
|
0, 0, 0, 0, 0, 0,
|
|
&psidAdministrators
|
|
);
|
|
|
|
if( bSuccess ) {
|
|
BOOL fIsMember = FALSE;
|
|
|
|
bSuccess = CheckTokenMembership( hAccessToken, psidAdministrators, &fIsMember );
|
|
|
|
if( bSuccess && !fIsMember )
|
|
bSuccess = FALSE;
|
|
|
|
}
|
|
|
|
CloseHandle( hAccessToken );
|
|
|
|
if(psidAdministrators)
|
|
FreeSid(psidAdministrators);
|
|
|
|
return bSuccess;
|
|
}
|
|
|
|
// 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_IF_ALLOW_SECURE_ONLY,
|
|
RPC_C_PROTSEQ_MAX_REQS_DEFAULT,
|
|
KeySvcSecurityCallback); // require admin and LRPC
|
|
if (status)
|
|
{
|
|
dwLastError = status;
|
|
goto cleanup;
|
|
}
|
|
|
|
status = RpcServerRegisterIfEx(s_IKeySvcR_v1_0_s_ifspec,
|
|
NULL,
|
|
NULL,
|
|
RPC_IF_AUTOLISTEN | RPC_IF_ALLOW_SECURE_ONLY,
|
|
RPC_C_PROTSEQ_MAX_REQS_DEFAULT,
|
|
RKeySvcSecurityCallback); // require admin, LRPC not required
|
|
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)
|
|
{
|
|
// Shouldn't prevent service from startin just because auth failed.
|
|
// Just log an event here.
|
|
MyLogErrorMessage(status, MSG_RKEYSVC_COULDNT_INIT_SECURITY);
|
|
}
|
|
else
|
|
{
|
|
status = RpcServerRegisterAuthInfo
|
|
(pwszServerPrincipalName,
|
|
RPC_C_AUTHN_GSS_NEGOTIATE,
|
|
NULL, // Use default key acquisiting function
|
|
NULL // Use default creds
|
|
);
|
|
if (RPC_S_OK != status)
|
|
{
|
|
// Shouldn't prevent service from startin just because auth failed.
|
|
// Just log an event here.
|
|
MyLogErrorMessage(status, MSG_RKEYSVC_COULDNT_INIT_SECURITY);
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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)
|
|
{
|
|
SetEvent(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));
|
|
}
|
|
|
|
}
|