2025-11-28 00:35:46 +09:00

467 lines
11 KiB
C++

// MonSvc_PvtNameSpace.cxx : Sample service to illustrate usage of private namespaces and service SIDs
// MonSvc creates a private namespace and sets an event there when the service is stopped.
// Clients that rendezvous with this event will be guaranteed that it was created by MonSvc service.
#include <windows.h>
#include <tchar.h>
#include <strsafe.h>
//
// Globals
//
SERVICE_STATUS ssService;
SERVICE_STATUS_HANDLE hssService;
HANDLE g_hEvent;
// **************************************************************************
// Function Name: SvcDebugOut()
//
// Purpose: Output error message to the debugger.
//
// Arguments:
// IN LPTSTR String - Error message
// IN DWORD Status - Error code
//
// Return Values:
// None
VOID SvcDebugOut(LPTSTR String, DWORD Status);
// **************************************************************************
// Function Name: CreateNamespaceSD()
//
// Purpose: Create the SD for use with the private namespace.
//
// Arguments:
// OUT PSECURITY_ATTRIBUTES pSecurityAttributes - SECURITY_ATTRIBUTES structure
// IN PSID ServiceSID - Service SID of the namespace creator service
//
// Return Values:
// Win32 Exit code
DWORD CreateNamespaceSD(PSECURITY_ATTRIBUTES pSecurityAttributes, PSID ServiceSID);
// **************************************************************************
// Function Name: FreeSD()
//
// Purpose: Frees the Security Descriptor that is passed in.
//
// Arguments:
// IN PSECURITY_DESCRIPTOR pSecDesc - Security Descriptor
//
// Return Values:
// None
VOID FreeSD(PSECURITY_DESCRIPTOR pSecDesc);
// **************************************************************************
// Function Name: ServiceStopAlert
//
// Purpose: Alert clients when the service is stopped. Uses a private namespace
// to rendezvous with the client.
//
// Return Values:
// Win32 Exit code
DWORD ServiceStopAlert (VOID);
// **************************************************************************
// Function Name: ServiceStart
//
// Purpose: Service entry point
//
// Return Values:
// None
VOID WINAPI ServiceStart(DWORD argc, LPTSTR *argv);
DWORD WINAPI ServiceCtrlHandler(DWORD Opcode, DWORD EventType, PVOID pEventData, PVOID pContext);
/****************************************************************************/
int __cdecl wmain (DWORD argc, LPWSTR argv[])
{
SERVICE_TABLE_ENTRY DispatchTable[] =
{
{TEXT("MonSvc"), ServiceStart},
{NULL, NULL}
};
StartServiceCtrlDispatcher(DispatchTable);
ExitProcess ( 0 );
return 0;
}
VOID WINAPI ServiceStart(DWORD argc, LPTSTR *argv)
{
HANDLE hAutostartEvent = NULL;
DWORD dwError = ERROR_SUCCESS;
hssService = RegisterServiceCtrlHandlerEx(TEXT("MonSvc"), ServiceCtrlHandler, NULL);
if (hssService == (SERVICE_STATUS_HANDLE)0)
{
dwError = GetLastError();
SvcDebugOut(TEXT("RegisterServiceCtrlHandler failed - "), dwError);
ExitProcess(dwError);
}
// Initialize the service status structure
ssService.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
ssService.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
ssService.dwServiceSpecificExitCode = 0;
// Notification for stopping service.
g_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (g_hEvent == NULL)
{
dwError = GetLastError();
SvcDebugOut(TEXT("CreateEvent error"), dwError);
ssService.dwCurrentState = SERVICE_STOPPED;
ssService.dwCheckPoint = 0;
ssService.dwWaitHint = 0;
ssService.dwWin32ExitCode = dwError;
if (!SetServiceStatus (hssService, &ssService))
{
SvcDebugOut(TEXT("SetServiceStatus error - "), GetLastError());
}
return;
}
//
// Initialization complete - report running status. We start accepting stop and
// shutdown controls only after reporting a status of SERVICE_RUNNING. It is easier
// to design services that don't accept stop/shutdown controls while they are
// in SERVICE_START_PENDING state.
//
ssService.dwCurrentState = SERVICE_RUNNING;
ssService.dwCheckPoint = 0;
ssService.dwWaitHint = 0;
ssService.dwWin32ExitCode = 0;
SvcDebugOut(TEXT("Initialized and running - "), 0);
if (!SetServiceStatus (hssService, &ssService))
{
SvcDebugOut(TEXT("SetServiceStatus error - "), GetLastError());
}
dwError = ServiceStopAlert();
ssService.dwCurrentState = SERVICE_STOPPED;
ssService.dwCheckPoint = 0;
ssService.dwWaitHint = 0;
ssService.dwWin32ExitCode = dwError;
if (!SetServiceStatus (hssService, &ssService))
{
SvcDebugOut(TEXT("SetServiceStatus error - "), GetLastError());
}
if(g_hEvent != NULL)
{
CloseHandle(g_hEvent);
g_hEvent = NULL;
}
return;
}
DWORD ServiceStopAlert(VOID)
{
DWORD dwError = ERROR_SUCCESS;
DWORD dwStatus = 0;
BOOL bRet = FALSE;
HANDLE hNameSpace = NULL;
HANDLE hEvent = NULL;
WCHAR szBDName[] = L"MonSvcBD";
WCHAR szDomainName[MAX_PATH + 1] = {0};
DWORD cchDomainName = MAX_PATH + 1;
PSID pSid = NULL;
BYTE Sid[SECURITY_MAX_SID_SIZE];
DWORD cbSid = SECURITY_MAX_SID_SIZE;
SID_NAME_USE sidNameUse;
HANDLE BoundaryDescriptor = NULL;
SECURITY_ATTRIBUTES nameSpaceAttribute;
// Get the Service SID
pSid = (PSID) Sid;
bRet = LookupAccountName(NULL, L"NT Service\\MonSvc", pSid, &cbSid, szDomainName, &cchDomainName, &sidNameUse);
if(!bRet)
{
dwError = GetLastError();
SvcDebugOut(TEXT("Error looking up service SID - "), dwError);
goto FnExit;
}
// Create the Boundary Descriptor for the private namespace
BoundaryDescriptor = CreateBoundaryDescriptor(szBDName, 0);
if(BoundaryDescriptor == NULL)
{
dwError = GetLastError();
SvcDebugOut(TEXT("Error creating Boundary Descriptor for private namespace - "), dwError);
goto FnExit;
}
// Add the Service SID to the Boundary Descriptor
bRet = AddSIDToBoundaryDescriptor(&BoundaryDescriptor, pSid);
if(!bRet)
{
dwError = GetLastError();
SvcDebugOut(TEXT("Error adding SID to Boundary Descriptor - "), dwError);
goto FnExit;
}
// Create SD for the namespace
nameSpaceAttribute.nLength = sizeof(SECURITY_ATTRIBUTES);
nameSpaceAttribute.bInheritHandle = FALSE;
dwError = CreateNamespaceSD(&nameSpaceAttribute, pSid);
if(dwError != ERROR_SUCCESS)
{
goto FnExit;
}
// Create the private namespace
hNameSpace = CreatePrivateNamespace(&nameSpaceAttribute, BoundaryDescriptor, L"MonSvcNamespace");
if(hNameSpace == NULL)
{
dwError = GetLastError();
SvcDebugOut(TEXT("Error creating private namespace - "), dwError);
goto FnExit;
}
// Create an event in the private namespace
hEvent = CreateEvent(&nameSpaceAttribute, FALSE, FALSE, L"MonSvcNamespace\\StopAlert");
if(hEvent == NULL)
{
dwError = GetLastError();
SvcDebugOut(TEXT("Error creating event - "), dwError);
goto FnExit;
}
while(TRUE)
{
// Wait for STOP control
dwStatus = WaitForSingleObjectEx(g_hEvent, INFINITE, FALSE);
// Check if this was signaled due to a SERVICE_STOP control
if(dwStatus == WAIT_OBJECT_0)
{
break;
}
}
// Alert the clients about service STOP
SetEvent(hEvent);
FnExit:
if(BoundaryDescriptor != NULL)
{
DeleteBoundaryDescriptor(BoundaryDescriptor);
BoundaryDescriptor = NULL;
}
if(hNameSpace != NULL)
{
ClosePrivateNamespace(hNameSpace, PRIVATE_NAMESPACE_FLAG_DESTROY);
hNameSpace = NULL;
}
if(hEvent != NULL)
{
CloseHandle(hEvent);
hEvent = NULL;
}
FreeSD(nameSpaceAttribute.lpSecurityDescriptor);
return dwError;
}
DWORD WINAPI ServiceCtrlHandler(DWORD Opcode, DWORD EventType, PVOID pEventData, PVOID pContext)
{
switch(Opcode)
{
case SERVICE_CONTROL_SHUTDOWN:
SvcDebugOut(TEXT("Shutdown command received - "), Opcode);
//
// Fall through to STOP case
//
case SERVICE_CONTROL_STOP:
ssService.dwWin32ExitCode = ERROR_SUCCESS;
ssService.dwCurrentState = SERVICE_STOP_PENDING;
ssService.dwCheckPoint = 1;
ssService.dwWaitHint = 10000;
break;
default:
SvcDebugOut(TEXT("Unrecognized opcode - "), Opcode);
}
if (!SetServiceStatus(hssService, &ssService))
{
SvcDebugOut(TEXT("SetServiceStatus error - "), GetLastError());
}
// Notify the service thread
if (Opcode == SERVICE_CONTROL_STOP || Opcode == SERVICE_CONTROL_SHUTDOWN)
{
SetEvent(g_hEvent);
}
return ( ERROR_SUCCESS );
}
DWORD CreateNamespaceSD(PSECURITY_ATTRIBUTES pSecurityAttributes, PSID ServiceSid)
{
BOOL bRet = FALSE;
DWORD dwError = ERROR_SUCCESS;
PSECURITY_DESCRIPTOR pSecDesc = NULL;
ULONG ACLSize = 0;
PACL pACL = NULL;
PSID pWorldSid = NULL;
BYTE Sid[SECURITY_MAX_SID_SIZE];
DWORD cbSid = SECURITY_MAX_SID_SIZE;
pSecDesc = (PSECURITY_DESCRIPTOR) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SECURITY_DESCRIPTOR));
if(pSecDesc == NULL)
{
goto FnExit;
}
// Initialize Security Descriptor
bRet = InitializeSecurityDescriptor(pSecDesc, SECURITY_DESCRIPTOR_REVISION);
if(!bRet)
{
goto FnExit;
}
//Get the World SID
pWorldSid = (PSID) Sid;
bRet = CreateWellKnownSid(WinWorldSid, NULL, pWorldSid, &cbSid);
if(!bRet)
{
goto FnExit;
}
// Calculate ACL size
ACLSize = sizeof(ACL) +
(sizeof( ACCESS_ALLOWED_ACE ) - sizeof(DWORD)) * 2 +
GetLengthSid( ServiceSid ) + GetLengthSid(pWorldSid);
pACL = (PACL) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ACLSize);
if(NULL == pACL)
{
goto FnExit;
}
// Initialize ACL
bRet = InitializeAcl(pACL, ACLSize, ACL_REVISION);
if(!bRet)
{
goto FnExit;
}
// Add Service SID to Allowed Access ACE
bRet = AddAccessAllowedAce(pACL, ACL_REVISION, GENERIC_ALL, ServiceSid);
if(!bRet || !IsValidAcl(pACL))
{
goto FnExit;
}
// Add World SID to Allowed Access ACE
bRet = AddAccessAllowedAce(pACL, ACL_REVISION, GENERIC_READ | SYNCHRONIZE, pWorldSid);
if(!bRet || !IsValidAcl(pACL))
{
goto FnExit;
}
// Set the DACL in the security descriptor
bRet = SetSecurityDescriptorDacl(pSecDesc, TRUE, pACL, FALSE);
if(!bRet)
{
goto FnExit;
}
// Set the Owner Sid to be the Service SID
bRet = SetSecurityDescriptorOwner(pSecDesc, ServiceSid, FALSE);
if(!bRet)
{
goto FnExit;
}
// Set the group Sid to be the Service SID
bRet = SetSecurityDescriptorGroup(pSecDesc, ServiceSid, FALSE);
if(!bRet)
{
goto FnExit;
}
pSecurityAttributes->lpSecurityDescriptor = pSecDesc;
if(IsValidSecurityDescriptor(pSecDesc))
{
return dwError;
}
FnExit:
if(pSecDesc != NULL)
{
HeapFree(GetProcessHeap(), 0, pSecDesc);
}
if(pACL != NULL)
{
HeapFree(GetProcessHeap(), 0, pACL);
}
dwError = GetLastError();
SvcDebugOut(TEXT("Error creating security attributes - "), dwError);
return dwError;
}
VOID FreeSD(PSECURITY_DESCRIPTOR pSecDesc)
{
BOOL bDACLPresent = FALSE;
BOOL bDACLDefault = FALSE;
PACL pDACL = NULL;
BOOL bRet = FALSE;
if(pSecDesc != NULL)
{
bRet = GetSecurityDescriptorDacl(pSecDesc, &bDACLPresent, &pDACL, &bDACLDefault);
if(bRet && bDACLPresent)
{
HeapFree(GetProcessHeap(), 0, pDACL);
}
HeapFree(GetProcessHeap(), 0, pSecDesc);
}
}
VOID SvcDebugOut(LPTSTR String, DWORD Status)
{
HRESULT hr = S_OK;
TCHAR Buffer[1024];
hr = StringCchPrintf(Buffer, 1024, TEXT("%s %d"), String, Status);
if(hr == S_OK)
{
OutputDebugString(Buffer);
}
else
{
OutputDebugString(TEXT("Error in Dbg string"));
}
}