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.
 
 
 
 
 
 

700 lines
21 KiB

// mpsmpssv.cpp
//
// This is the main file containing the entry points.
#include "NTServApp.h"
#include "PMSPservice.h"
#include <nserror.h>
#include "svchost.h"
#include <Sddl.h>
#include <aclapi.h>
#include <crtdbg.h>
#include <wmsstd.h>
HRESULT AddToSvcHostGroup();
BOOL UnregisterOldServer( SC_HANDLE hSCM );
STDAPI DllUnregisterServer(void);
#define SVCHOST_SUBKEY "netsvcs"
#define SVCHOST_SUBKEYW L"netsvcs"
//#define DEBUG_STOP { _asm { int 3 }; }
#define DEBUG_STOP
HMODULE g_hDll = NULL;
BOOL APIENTRY DllMain( HINSTANCE hModule, DWORD ul_reason_for_call, LPVOID lpReserved )
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
g_hDll = (HMODULE)hModule;
InitializeCriticalSection (&g_csLock);
DisableThreadLibraryCalls (hModule);
break;
case DLL_PROCESS_DETACH:
DeleteCriticalSection (&g_csLock);
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
_ASSERTE(0);
break;
}
return TRUE;
}
// Main entry point to start service
void ServiceMain(DWORD dwArgc, LPTSTR* lpszArgv)
{
// We grab the lock so that any attempt to stop the service while
// the object is being constructed or registered will be pended.
EnterCriticalSection (&g_csLock);
_ASSERTE(g_pService == NULL);
DEBUG_STOP
CNTService::DebugMsg("Entering CNTService::ServiceMain()");
DWORD dwLastError;
// Allocate this on the heap rather than the stack so that
// we have a chance to call its destructor if the service
// terminates ungracefully.
CPMSPService* pService = new CPMSPService(dwLastError);
if (pService == NULL)
{
LeaveCriticalSection (&g_csLock);
dwLastError = ERROR_NOT_ENOUGH_MEMORY;
// @@@@: What message do we log here
// CNTService::LogEvent(EVENTLOG_ERROR_TYPE, EVMSG_CTRLHANDLERNOTINSTALLED);
CNTService::DebugMsg("Leaving CNTService::ServiceMain() CPMSPService constructor failed - last error %u", dwLastError);
return;
}
CPMSPService& service = *pService;
if (dwLastError != ERROR_SUCCESS)
{
LeaveCriticalSection (&g_csLock);
// @@@@: What message do we log here
// CNTService::LogEvent(EVENTLOG_ERROR_TYPE, EVMSG_CTRLHANDLERNOTINSTALLED);
CNTService::DebugMsg("Leaving CNTService::ServiceMain() CPMSPService constructor failed - last error %u", dwLastError);
delete pService;
return;
}
// Register the control request handler
service.m_hServiceStatus = RegisterServiceCtrlHandler( SERVICE_NAME,
CNTService::Handler );
if (service.m_hServiceStatus == NULL)
{
LeaveCriticalSection (&g_csLock);
CNTService::LogEvent(EVENTLOG_ERROR_TYPE, EVMSG_CTRLHANDLERNOTINSTALLED);
CNTService::DebugMsg("Leaving CNTService::ServiceMain() RegisterServiceCtrlHandler failed");
delete pService;
return;
}
service.SetStatus(SERVICE_START_PENDING);
// Start the initialisation
__try
{
g_pService = &service; // The Handler method will need to get a hold of this object
LeaveCriticalSection (&g_csLock);
if (service.Initialize()) {
// Do the real work.
// When the Run function returns, the service has stopped.
service.m_bIsRunning = TRUE;
service.Run();
}
}
__finally
{
// Tell the service manager we are stopped and reset g_pService.
// Note that we hold the crit sect while calling SetStatus so that
// we have the final say on the status reported to the SCM.
// Note: If the thread dies (e.g., av's), we clean up, but svchost
// does not, so it is not possible to re-start the service. Consider
// adding our own exception handler.
EnterCriticalSection (&g_csLock);
service.SetStatus(SERVICE_STOPPED);
g_pService = NULL;
LeaveCriticalSection (&g_csLock);
CNTService::DebugMsg("Leaving CNTService::ServiceMain()");
delete pService;
}
}
HRESULT ModifySD(SC_HANDLE hService)
{
PACL pDacl = NULL;
PACL pNewDacl = NULL;
PSECURITY_DESCRIPTOR pSD = NULL;
DWORD Err = ERROR_SUCCESS;
PSID pAuthenUserSid = NULL;
__try
{
//
// Get DACL for the service object.
//
Err = GetSecurityInfo(hService, SE_SERVICE, DACL_SECURITY_INFORMATION,
NULL, NULL, &pDacl, NULL, &pSD
);
if(Err != ERROR_SUCCESS)
{
__leave;
}
SID_IDENTIFIER_AUTHORITY Auth = SECURITY_NT_AUTHORITY;
if(0 == AllocateAndInitializeSid(&Auth, 1, SECURITY_INTERACTIVE_RID,
0, 0, 0, 0, 0, 0, 0, &pAuthenUserSid)
)
{
Err = GetLastError();
__leave;
}
//
// Initialize an EXPLICIT_ACCESS structure for the new ACE. The new ACE allows
// authenticated users to start/stop our service.
//
EXPLICIT_ACCESS ExpAccess;
ZeroMemory(&ExpAccess, sizeof(EXPLICIT_ACCESS));
ExpAccess.grfAccessPermissions = SERVICE_START; // | SERVICE_STOP ;
ExpAccess.grfAccessMode = GRANT_ACCESS;
ExpAccess.grfInheritance = NO_INHERITANCE;
ExpAccess.Trustee.pMultipleTrustee = NULL;
ExpAccess.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
ExpAccess.Trustee.TrusteeForm = TRUSTEE_IS_SID;
ExpAccess.Trustee.TrusteeType = TRUSTEE_IS_UNKNOWN;
ExpAccess.Trustee.ptstrName = (LPTSTR)pAuthenUserSid;
//Create new DACL
Err = SetEntriesInAcl(1, &ExpAccess, pDacl, &pNewDacl) ;
if(ERROR_SUCCESS == Err)
{
// Update the security descriptor on the service
Err = SetSecurityInfo(hService, SE_SERVICE, DACL_SECURITY_INFORMATION, NULL, NULL,
pNewDacl, NULL);
}
}
__finally
{
if(pSD)
{
LocalFree(pSD);
}
if(pAuthenUserSid){
FreeSid(pAuthenUserSid);
}
if(pNewDacl)
{
LocalFree(pNewDacl);
}
}
return HRESULT_FROM_WIN32(Err);
}
// Install and start service
STDAPI DllRegisterServer(void)
{
HRESULT hr = E_FAIL;
SC_HANDLE hSCM = NULL;
SC_HANDLE hService = NULL;
TCHAR pszDisplayName[256];
char szKey[256];
HKEY hKey = NULL;
DEBUG_STOP;
// Already installed?
if( CNTService::IsInstalled() )
{
hr = DllUnregisterServer();
if( !SUCCEEDED(hr) )
{
return hr;
}
}
if( g_hDll == NULL )
{
return E_FAIL;
}
// Open the Service Control Manager
hSCM = ::OpenSCManager( NULL, // local machine
NULL, // ServicesActive database
SC_MANAGER_ALL_ACCESS); // full access
if (!hSCM)
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto Error;
}
//
// On Win2k we have this service running as separate process which should
// be uninstalled.
//
UnregisterOldServer( hSCM );
// Get the path of this dll
char szFilePath[MAX_PATH];
if (::GetModuleFileName( g_hDll, szFilePath, ARRAYSIZE(szFilePath)) == 0)
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto Error;
}
// Create the service
if (FormatMessage( FORMAT_MESSAGE_FROM_HMODULE, g_hDll, EVMSG_DISPLAYNAME,
0, pszDisplayName, ARRAYSIZE(pszDisplayName), NULL ) == 0)
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto Error;
}
hService = ::CreateService( hSCM,
SERVICE_NAME,
pszDisplayName,
SERVICE_ALL_ACCESS,
SERVICE_WIN32_SHARE_PROCESS,
SERVICE_DEMAND_START,
SERVICE_ERROR_NORMAL,
"%SystemRoot%\\System32\\svchost.exe -k " SVCHOST_SUBKEY,
NULL,
NULL,
NULL,
NULL,
NULL);
if (!hService)
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto Error;
}
//
// Modify the security descriptor on the created service so that
// Authenticated Users can start/stop services. By default only admins can start/stop
// services
//
hr = ModifySD(hService);
if(!SUCCEEDED(hr))
{
goto Error;
}
// Set description of service, method only avalible for OS >= Win2K so we
// need to load the dll, method in runtime.
{
typedef BOOL (WINAPI *funCSC2)(SC_HANDLE, DWORD, LPVOID );
funCSC2 pChangeServiceConfig2 = NULL;
HINSTANCE hDll = NULL;
hDll = ::LoadLibraryExA( "advapi32.dll", NULL, 0 );
if( hDll != NULL )
{
pChangeServiceConfig2 = (funCSC2)GetProcAddress( hDll, "ChangeServiceConfig2W");
if( pChangeServiceConfig2 )
{
WCHAR pszDescription[1024];
int iCharsLoaded = 0;
SERVICE_DESCRIPTIONW sd;
iCharsLoaded = FormatMessageW( FORMAT_MESSAGE_FROM_HMODULE, g_hDll, EVMSG_DESCRIPTION,
0, pszDescription, sizeof(pszDescription)/sizeof(pszDescription[0]), NULL );
if( iCharsLoaded )
{
sd.lpDescription = pszDescription;
pChangeServiceConfig2( hService,
SERVICE_CONFIG_DESCRIPTION,
&sd);
}
}
FreeLibrary( hDll );
}
}
// Add parameters subkey
{
strcpy(szKey, "SYSTEM\\CurrentControlSet\\Services\\");
strcat(szKey, SERVICE_NAME);
strcat(szKey, "\\Parameters");
hr = ::RegCreateKey(HKEY_LOCAL_MACHINE, szKey, &hKey);
if( hr != ERROR_SUCCESS)
{
hr = HRESULT_FROM_WIN32(hr);
goto Error;
}
// Add the Event ID message-file name to the 'EventMessageFile' subkey.
hr = ::RegSetValueEx(hKey,
"ServiceDll",
0,
REG_EXPAND_SZ,
(CONST BYTE*)szFilePath,
strlen(szFilePath) + 1);
if( hr != ERROR_SUCCESS)
{
hr = HRESULT_FROM_WIN32(hr);
::RegCloseKey(hKey);
goto Error;
}
::RegCloseKey(hKey);
}
hr = AddToSvcHostGroup();
if( FAILED(hr) ) goto Error;
// make registry entries to support logging messages
// Add the source name as a subkey under the Application
// key in the EventLog service portion of the registry.
{
strcpy(szKey, "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\");
strcat(szKey, SERVICE_NAME);
hr = ::RegCreateKey(HKEY_LOCAL_MACHINE, szKey, &hKey);
if( hr != ERROR_SUCCESS)
{
hr = HRESULT_FROM_WIN32(hr);
goto Error;
}
// Add the Event ID message-file name to the 'EventMessageFile' subkey.
hr = ::RegSetValueEx(hKey,
"EventMessageFile",
0,
REG_EXPAND_SZ,
(CONST BYTE*)szFilePath,
strlen(szFilePath) + 1);
if( hr != ERROR_SUCCESS)
{
hr = HRESULT_FROM_WIN32(hr);
::RegCloseKey(hKey);
goto Error;
}
// Set the supported types flags.
DWORD dwData = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE;
hr = ::RegSetValueEx(hKey,
"TypesSupported",
0,
REG_DWORD,
(CONST BYTE*)&dwData,
sizeof(DWORD));
if( hr != ERROR_SUCCESS)
{
hr = HRESULT_FROM_WIN32(hr);
::RegCloseKey(hKey);
goto Error;
}
::RegCloseKey(hKey);
}
#if 0
// Start service
{
SERVICE_STATUS ServiceStatus;
if( !QueryServiceStatus( hService, &ServiceStatus ) )
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto Error;
}
if( ServiceStatus.dwCurrentState != SERVICE_RUNNING )
{
// start the service
BOOL bStarted;
bStarted = StartService(hService, 0, NULL);
if( !bStarted )
{
hr = HRESULT_FROM_WIN32(GetLastError());
// The service can not be started if it just was added to the svchost group.
// The svchost needs to be restarted first.
// (The svchost only reads it's service array at startup)
if( hr == HRESULT_FROM_WIN32(ERROR_SERVICE_NOT_IN_EXE) )
{
// This error code will be handled by the installer
hr = NS_S_REBOOT_REQUIRED; // 0x000D2AF9L
}
goto Error;
}
}
}
#endif
CNTService::LogEvent(EVENTLOG_INFORMATION_TYPE, EVMSG_INSTALLED, SERVICE_NAME);
hr = S_OK;
Error:
if( hService ) ::CloseServiceHandle(hService);
if( hSCM ) ::CloseServiceHandle(hSCM);
//
// Check: should we return here NS_S_REBOOT_REQUIRED, if the service is installed.
return hr;
}
// Stop and Uninstall service
STDAPI DllUnregisterServer(void)
{
HRESULT hr = E_FAIL;
char szKey[256];
HKEY hKey = NULL;
SC_HANDLE hSCM = NULL;
SC_HANDLE hService = NULL;
DEBUG_STOP
// Not installed ?
if( !CNTService::IsInstalled() )
{
return S_FALSE;
}
// Open the Service Control Manager
hSCM = ::OpenSCManager( NULL, // local machine
NULL, // ServicesActive database
SC_MANAGER_ALL_ACCESS); // full access
if (!hSCM)
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto Error;
}
hService = ::OpenService( hSCM,
SERVICE_NAME,
SERVICE_ALL_ACCESS);
// Remove service
if (hService)
{
// Stop service
{
SERVICE_STATUS ServiceStatus;
if( !QueryServiceStatus( hService, &ServiceStatus ) )
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto Error;
}
if( ServiceStatus.dwCurrentState != SERVICE_STOPPED )
{
// start the service
SERVICE_STATUS ss;
BOOL bStopped;
bStopped = ControlService( hService,
SERVICE_CONTROL_STOP,
&ss);
if( !bStopped )
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto Error;
}
}
}
if (::DeleteService(hService))
{
CNTService::LogEvent(EVENTLOG_INFORMATION_TYPE, EVMSG_REMOVED, SERVICE_NAME);
hr = S_OK;
}
else
{
CNTService::LogEvent(EVENTLOG_ERROR_TYPE, EVMSG_NOTREMOVED, SERVICE_NAME);
hr = HRESULT_FROM_WIN32(GetLastError());
// Do not delete eventlog related registry keys unless the service has been deleted
goto Error;
}
}
else
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto Error;
}
// Delete EventLog entry in registry
strcpy(szKey, "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\");
strcat(szKey, SERVICE_NAME);
RegDeleteKey( HKEY_LOCAL_MACHINE, szKey );
Error:
if(hSCM) ::CloseServiceHandle(hSCM);
if(hService) ::CloseServiceHandle(hService);
return hr;
}
// Add entry to the right svchost group, (netsvcs)
HRESULT AddToSvcHostGroup()
{
HRESULT hr = S_OK;
DWORD dwOrgSize;
DWORD dwDestSize;
long lResult;
DWORD dwStrIndex;
DWORD dwType;
HKEY hKey = NULL;
WCHAR* pwszStringOrg = NULL;
WCHAR* pwszStringDest = NULL;
DEBUG_STOP
lResult = RegCreateKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Svchost", &hKey);
if( lResult != ERROR_SUCCESS )
{
hr = E_FAIL;
goto Error;
}
lResult = RegQueryValueExW( hKey,
SVCHOST_SUBKEYW, // subkey name
NULL,
&dwType,
NULL, // string buffer
&dwOrgSize ); // size of returned string
if( lResult != ERROR_SUCCESS )
{
hr = E_FAIL;
goto Error;
}
if (dwType != REG_SZ && dwType != REG_MULTI_SZ && dwType != REG_EXPAND_SZ)
{
hr = E_FAIL;
goto Error;
}
dwDestSize = dwOrgSize + (wcslen( SERVICE_NAMEW ) +1)*sizeof(WCHAR);
pwszStringOrg = (WCHAR*)new BYTE[dwOrgSize];
pwszStringDest = (WCHAR*)new BYTE[dwDestSize];
if( pwszStringOrg == NULL || pwszStringDest == NULL )
{
hr = E_OUTOFMEMORY;
goto Error;
}
lResult = RegQueryValueExW( hKey,
SVCHOST_SUBKEYW, // subkey name
NULL,
&dwType,
(BYTE*)pwszStringOrg, // string buffer
&dwOrgSize ); // size of returned string
if( lResult != ERROR_SUCCESS )
{
hr = E_FAIL;
goto Error;
}
if (dwType != REG_SZ && dwType != REG_MULTI_SZ && dwType != REG_EXPAND_SZ)
{
hr = E_FAIL;
goto Error;
}
// Copy the org string to the dest, check to see if our string is already there
memset( pwszStringDest, 0, dwDestSize );
for( dwStrIndex = 0;
(dwStrIndex*sizeof(WCHAR) < dwOrgSize) && ((pwszStringOrg)[dwStrIndex] != '\0');
dwStrIndex += wcslen( &((WCHAR*)pwszStringOrg)[dwStrIndex] ) +1 )
{
// Check this string in the [array] of strings
if( wcscmp( &((WCHAR*)pwszStringOrg)[dwStrIndex], SERVICE_NAMEW ) == 0 )
{
hr = S_OK; // String already added
goto Error;
}
wcscpy( &pwszStringDest[dwStrIndex], &pwszStringOrg[dwStrIndex] );
}
// Add this new string to the array of strings. Terminate the array with two '\0' chars
wcscpy( &pwszStringDest[dwStrIndex], SERVICE_NAMEW );
dwStrIndex += wcslen( SERVICE_NAMEW ) + 1;
dwDestSize = (dwStrIndex +1)* sizeof(WCHAR); // Add space for terminating extra '\0'
lResult = RegSetValueExW(hKey,
SVCHOST_SUBKEYW, // subkey name
NULL,
dwType,
(BYTE*)pwszStringDest, // string buffer
dwDestSize ); // size of returned string
Error:
if( pwszStringOrg ) delete [] pwszStringOrg;
if( pwszStringDest ) delete [] pwszStringDest;
if( hKey ) RegCloseKey(hKey);
return hr;
}
// Stop and Uninstall the old .exe service
BOOL UnregisterOldServer( SC_HANDLE hSCM )
{
char szKey[256];
BOOL bRet = TRUE;
SC_HANDLE hServiceOld;
SERVICE_STATUS ss;
if( !hSCM ) return FALSE;
hServiceOld = OpenService( hSCM,
SERVICE_OLD_NAME,
SERVICE_ALL_ACCESS);
// Could not find the old service
if( !hServiceOld )
{
bRet = FALSE;
goto Error;
}
// stop the service
bRet = ControlService(hServiceOld,
SERVICE_CONTROL_STOP,
&ss);
// Delete the service
if ( !::DeleteService(hServiceOld))
{
bRet = FALSE;
}
// Delete old EventLog entry in registry
strcpy(szKey, "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\");
strcat(szKey, SERVICE_OLD_NAME);
RegDeleteKey( HKEY_LOCAL_MACHINE, szKey );
Error:
if(hServiceOld) CloseServiceHandle(hServiceOld);
return bRet;
}