//+-------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1996-1998 // // File: service.c // // Contents: Hydra License Server Service Control Manager Interface // // History: 12-09-97 HueiWang Modified from MSDN RPC Service Sample // //--------------------------------------------------------------------------- #include "pch.cpp" #include #include #include "server.h" #include "globals.h" #include "init.h" #include "postsrv.h" #include "tlsbkup.h" #include "Lmaccess.h" #include "Dsgetdc.h" #define NULL_SESSION_KEY_NAME _TEXT("SYSTEM\\CurrentControlSet\\Services\\LanmanServer\\Parameters") #define NULL_SESSION_VALUE_NAME _TEXT("NullSessionPipes") #define SERVICE_WAITHINT 60*1000 // WaitHint 1 mins. #define SERVICE_SHUTDOWN_WAITTIME 15*60*1000 // must have shutdown already. #define TSLSLOCALGROUPNAMELENGTH 64 #define TSLSLOCALGROUPDESLENGTH 128 #define ALLDOMAINCOMPUTERS L"Domain Computers" PSECURITY_DESCRIPTOR g_pSecDes = NULL; //Security Descriptor for local group PSID g_pSid = NULL; //Sid for local group PACL g_Dacl = NULL; //Dacl for local group //--------------------------------------------------------------------------- // // internal function prototypes // BOOL ReportStatusToSCMgr( DWORD, DWORD, DWORD ); DWORD ServiceStart( DWORD, LPTSTR *, BOOL bDebug=FALSE ); VOID WINAPI ServiceCtrl( DWORD ); VOID WINAPI ServiceMain( DWORD, LPTSTR * ); VOID CmdDebugService( int, char **, BOOL ); BOOL WINAPI ControlHandler( DWORD ); extern "C" VOID ServiceStop(); VOID ServicePause(); VOID ServiceContinue(); HANDLE hRpcPause=NULL; /////////////////////////////////////////////////////////// // // internal variables // SERVICE_STATUS_HANDLE sshStatusHandle; DWORD ssCurrentStatus; // current status of the service BOOL g_bReportToSCM = TRUE; HANDLE gSafeToTerminate=NULL; HRESULT hrStatus = NULL; DEFINE_GUID(TLS_WRITER_GUID, 0x5382579c, 0x98df, 0x47a7, 0xac, 0x6c, 0x98, 0xa6, 0xd7, 0x10, 0x6e, 0x9); GUID idWriter = TLS_WRITER_GUID; CTlsVssJetWriter *g_pWriter = NULL; CTlsVssJetWriter::CTlsVssJetWriter() : CVssJetWriter() { } CTlsVssJetWriter::~CTlsVssJetWriter() { } HRESULT CTlsVssJetWriter::Initialize() { return CVssJetWriter::Initialize(idWriter, L"TermServLicensing", TRUE, FALSE, L"", L""); } void CTlsVssJetWriter::Uninitialize() { return CVssJetWriter::Uninitialize(); } bool STDMETHODCALLTYPE CTlsVssJetWriter::OnIdentify(IN IVssCreateWriterMetadata *pMetadata) { HRESULT hr= E_FAIL; hr = pMetadata->SetRestoreMethod( VSS_RME_RESTORE_AT_REBOOT, NULL, NULL, VSS_WRE_NEVER, true); if(ERROR_SUCCESS == hr) return CVssJetWriter::OnIdentify(pMetadata); else return FALSE; } SERVICE_TABLE_ENTRY dispatchTable[] = { { _TEXT(SZSERVICENAME), (LPSERVICE_MAIN_FUNCTION)ServiceMain }, { NULL, NULL } }; //----------------------------------------------------------------- // Internal routine //----------------------------------------------------------------- void print_usage() { _ftprintf( stdout, _TEXT("Usage : %s can't be run as a console app\n"), _TEXT(SZAPPNAME) ); return; } #ifdef DISALLOW_ANONYMOUS_RPC DWORD RemoveStringFromMultiSz( LPTSTR pszRemoveString1, LPTSTR pszRemoveString2, HKEY hKey, LPCTSTR pszValueName) { DWORD dwErr; LPTSTR wszData = NULL, pwsz; DWORD cbData, cbDataRemaining; BOOL fFound = FALSE; if ((NULL == pszRemoveString1) || (NULL == pszRemoveString2) || (NULL == pszValueName) || (NULL == hKey)) { return ERROR_INVALID_PARAMETER; } // // Retrieve existing MULTI_SZ // dwErr = RegQueryValueEx(hKey, pszValueName, NULL, NULL, NULL, &cbData); if (dwErr != ERROR_SUCCESS) { if (dwErr == ERROR_FILE_NOT_FOUND) { // // Value isn't there // return ERROR_SUCCESS; } else { return dwErr; } } wszData = (LPTSTR) LocalAlloc(LPTR, cbData); if (NULL == wszData) { return ERROR_OUTOFMEMORY; } dwErr = RegQueryValueEx(hKey, pszValueName, NULL, NULL, (LPBYTE) wszData, &cbData); if (dwErr != ERROR_SUCCESS) { LocalFree(wszData); return dwErr; } pwsz = wszData; cbDataRemaining = cbData; while (*pwsz) { DWORD cchDataToMove = _tcslen (pwsz) + 1; if ((0 == _tcsicmp(pwsz,pszRemoveString1)) || (0 == _tcsicmp(pwsz,pszRemoveString2))) { LPTSTR pwszRemain = pwsz + cchDataToMove; MoveMemory(pwsz, pwszRemain, cbDataRemaining - (cchDataToMove * sizeof(TCHAR))); cbData -= cchDataToMove * sizeof(TCHAR); fFound = TRUE; } else { pwsz += cchDataToMove; } cbDataRemaining -= cchDataToMove * sizeof(TCHAR); } if (fFound) { dwErr = RegSetValueEx( hKey, wszData, 0, REG_MULTI_SZ, (LPBYTE) wszData, cbData); } LocalFree(wszData); return dwErr; } DWORD RemoveNullSessions() { HKEY hKey; DWORD dwErr; dwErr = RegOpenKeyEx( HKEY_LOCAL_MACHINE, NULL_SESSION_KEY_NAME, 0, KEY_READ | KEY_WRITE, &hKey ); if (dwErr != ERROR_SUCCESS) { // // Key doesn't exist - success // return ERROR_SUCCESS; } dwErr = RemoveStringFromMultiSz(_TEXT(HLSPIPENAME), _TEXT(SZSERVICENAME), hKey, NULL_SESSION_VALUE_NAME); RegCloseKey(hKey); return dwErr; } #endif // DISALLOW_ANONYMOUS_RPC //----------------------------------------------------------------- DWORD AddNullSessionPipe( IN LPTSTR szPipeName ) /*++ Abstract: Add our RPC namedpipe into registry to allow unrestricted access. Parameter: szPipeName : name of the pipe to append. Returns: ERROR_SUCCESS or error code --*/ { HKEY hKey; DWORD dwStatus; LPTSTR pbData=NULL, pbOrg=NULL; DWORD cbData = 0; dwStatus = RegOpenKeyEx( HKEY_LOCAL_MACHINE, NULL_SESSION_KEY_NAME, 0, KEY_ALL_ACCESS, &hKey ); if(dwStatus != ERROR_SUCCESS) return dwStatus; dwStatus = RegQueryValueEx( hKey, NULL_SESSION_VALUE_NAME, NULL, NULL, NULL, &cbData ); if(dwStatus != ERROR_MORE_DATA && dwStatus != ERROR_SUCCESS) return dwStatus; // pre-allocate our pipe name if(!(pbData = (LPTSTR)AllocateMemory(cbData + (_tcslen(szPipeName) + 1) * sizeof(TCHAR)))) return GetLastError(); dwStatus = RegQueryValueEx( hKey, NULL_SESSION_VALUE_NAME, NULL, NULL, (LPBYTE)pbData, &cbData ); BOOL bAddPipe=TRUE; pbOrg = pbData; // check pipe name while(*pbData) { if(!_tcsicmp(pbData, szPipeName)) { bAddPipe=FALSE; break; } pbData += _tcslen(pbData) + 1; } if(bAddPipe) { _tcscat(pbData, szPipeName); cbData += (_tcslen(szPipeName) + 1) * sizeof(TCHAR); dwStatus = RegSetValueEx( hKey, NULL_SESSION_VALUE_NAME, 0, REG_MULTI_SZ, (PBYTE)pbOrg, cbData ); } FreeMemory(pbOrg); RegCloseKey(hKey); return dwStatus; } //----------------------------------------------------------------- void _cdecl main( int argc, char **argv ) /*++ Abstract Entry point. ++*/ { // LARGE_INTEGER Time = USER_SHARED_DATA->SystemExpirationDate; gSafeToTerminate = CreateEvent( NULL, TRUE, FALSE, NULL ); if(gSafeToTerminate == NULL) { TLSLogErrorEvent(TLS_E_ALLOCATE_RESOURCE); // out of resource. return; } if(g_bReportToSCM == FALSE) { CmdDebugService( argc, argv, !g_bReportToSCM ); } else if(!StartServiceCtrlDispatcher(dispatchTable)) { TLSLogErrorEvent(TLS_E_SC_CONNECT); } WaitForSingleObject(gSafeToTerminate, INFINITE); CloseHandle(gSafeToTerminate); } //----------------------------------------------------------------- void WINAPI ServiceMain( IN DWORD dwArgc, IN LPTSTR *lpszArgv ) /*++ Abstract: To perform actual initialization of the service Parameter: dwArgc - number of command line arguments lpszArgv - array of command line arguments Returns: none ++*/ { DWORD dwStatus; // register our service control handler: sshStatusHandle = RegisterServiceCtrlHandler( _TEXT(SZSERVICENAME), ServiceCtrl ); if (sshStatusHandle) { ssCurrentStatus=SERVICE_START_PENDING; // report the status to the service control manager. // if(ReportStatusToSCMgr( SERVICE_START_PENDING, // service state NO_ERROR, // exit code SERVICE_WAITHINT)) // wait hint { dwStatus = ServiceStart( dwArgc, lpszArgv ); if(dwStatus != ERROR_SUCCESS) { ReportStatusToSCMgr( SERVICE_STOPPED, dwStatus, 0 ); } else { ReportStatusToSCMgr( SERVICE_STOPPED, NO_ERROR, 0 ); } } } else { dwStatus = GetLastError(); TLSLogErrorEvent(TLS_E_SC_CONNECT); } DBGPrintf( DBG_INFORMATION, DBG_FACILITY_INIT, DBGLEVEL_FUNCTION_TRACE, _TEXT("Service terminated...\n") ); return; } //------------------------------------------------------------- VOID WINAPI ServiceCtrl( IN DWORD dwCtrlCode ) /*+++ Abstract: This function is called by the SCM whenever ControlService() is called on this service. Parameter: dwCtrlCode - type of control requested from SCM. +++*/ { // Handle the requested control code. // switch(dwCtrlCode) { // Stop the service. // case SERVICE_CONTROL_SHUTDOWN: case SERVICE_CONTROL_STOP: ReportStatusToSCMgr( SERVICE_STOP_PENDING, NO_ERROR, 0 ); ServiceStop(); break; // We don't really accept pause and continue case SERVICE_CONTROL_PAUSE: ReportStatusToSCMgr( SERVICE_PAUSED, NO_ERROR, 0 ); ServicePause(); break; case SERVICE_CONTROL_CONTINUE: ReportStatusToSCMgr( SERVICE_RUNNING, NO_ERROR, 0 ); ServiceContinue(); break; // Update the service status. case SERVICE_CONTROL_INTERROGATE: ReportStatusToSCMgr( ssCurrentStatus, NO_ERROR, 0 ); break; // invalid control code default: break; } } //------------------------------------------------------------------ DWORD ServiceShutdownThread( void *p ) /*++ Abstract: Entry point into thread that shutdown server (mainly database). Parameter: Ignore ++*/ { ServerShutdown(); ExitThread(ERROR_SUCCESS); return ERROR_SUCCESS; } //------------------------------------------------------------------ DWORD RPCServiceStartThread( void *p ) /*++ Abstract: Entry point to thread that startup RPC. Parameter: None. Return: Thread exit code. ++*/ { RPC_BINDING_VECTOR *pbindingVector = NULL; RPC_STATUS status = RPC_S_OK; WCHAR *pszEntryName = _TEXT(RPC_ENTRYNAME); DWORD dwNumSuccessRpcPro=0; do { // // local procedure call // status = RpcServerUseProtseq( _TEXT(RPC_PROTOSEQLPC), RPC_C_PROTSEQ_MAX_REQS_DEFAULT, NULL // &SecurityDescriptor ); if(status == RPC_S_OK) { dwNumSuccessRpcPro++; } // // NT4 backward compatible issue, let NT4 termsrv serivce // client connect so still set security descriptor // // 11/10/98 Tested on NT4 and NT5 // // // Namedpipe // status = RpcServerUseProtseqEp( _TEXT(RPC_PROTOSEQNP), RPC_C_PROTSEQ_MAX_REQS_DEFAULT, _TEXT(LSNAMEPIPE), NULL //&SecurityDescriptor ); if(status == RPC_S_OK) { dwNumSuccessRpcPro++; } // // TCP/IP // status = RpcServerUseProtseq( _TEXT(RPC_PROTOSEQTCP), RPC_C_PROTSEQ_MAX_REQS_DEFAULT, NULL //&SecurityDescriptor ); if(status == RPC_S_OK) { dwNumSuccessRpcPro++; } // Must have at least one protocol. if(dwNumSuccessRpcPro == 0) { status = TLS_E_RPC_PROTOCOL; break; } // Get server binding handles status = RpcServerInqBindings(&pbindingVector); if (status != RPC_S_OK) { status = TLS_E_RPC_INQ_BINDING; break; } // Register interface(s) and binding(s) (endpoints) with // the endpoint mapper. status = RpcEpRegister( TermServLicensing_v1_0_s_ifspec, // from rpcsvc.h pbindingVector, NULL, // &export_uuid, L"" ); if (status != RPC_S_OK) { status = TLS_E_RPC_EP_REGISTER; break; } status = RpcServerRegisterIf( TermServLicensing_v1_0_s_ifspec, NULL, NULL); if(status != RPC_S_OK) { status = TLS_E_RPC_REG_INTERFACE; break; } // Register interface(s) and binding(s) (endpoints) with // the endpoint mapper. status = RpcEpRegister( HydraLicenseService_v1_0_s_ifspec, // from rpcsvc.h pbindingVector, NULL, // &export_uuid, L""); if (status != RPC_S_OK) { status = TLS_E_RPC_EP_REGISTER; break; } status = RpcServerRegisterIf( HydraLicenseService_v1_0_s_ifspec, NULL, NULL); if(status != RPC_S_OK) { status = TLS_E_RPC_REG_INTERFACE; break; } // Register interface(s) and binding(s) (endpoints) with // the endpoint mapper. status = RpcEpRegister( TermServLicensingBackup_v1_0_s_ifspec, // from rpcsvc.h pbindingVector, NULL, // &export_uuid, L""); if (status != RPC_S_OK) { status = TLS_E_RPC_EP_REGISTER; break; } status = RpcServerRegisterIf( TermServLicensingBackup_v1_0_s_ifspec, NULL, NULL); if(status != RPC_S_OK) { status = TLS_E_RPC_REG_INTERFACE; break; } // Enable NT LM Security Support Provider (NtLmSsp service) status = RpcServerRegisterAuthInfo(0, RPC_C_AUTHN_GSS_NEGOTIATE, 0, 0); if (status != RPC_S_OK) { status = TLS_E_RPC_SET_AUTHINFO; break; } } while(FALSE); if(status != RPC_S_OK) { TLSLogEvent( EVENTLOG_ERROR_TYPE, TLS_E_SERVICEINIT, TLS_E_INITRPC, status ); status = TLS_E_SERVICE_STARTUP; } ExitThread(status); return status; } //------------------------------------------------------------------------ DWORD SetupNamedPipes() { DWORD dwStatus = ERROR_SUCCESS; #ifdef DISALLOW_ANONYMOUS_RPC BOOL fInDomain = FALSE; TLSInDomain(&fInDomain,NULL); if (!fInDomain) { #endif dwStatus = AddNullSessionPipe(_TEXT(HLSPIPENAME)); if (dwStatus != ERROR_SUCCESS) { return dwStatus; } dwStatus = AddNullSessionPipe(_TEXT(SZSERVICENAME)); #ifdef DISALLOW_ANONYMOUS_RPC } else { dwStatus = RemoveNullSessions(); } #endif return dwStatus; } //--------------------------------------------------------------------------- /****************************************************************************/ // LSCreateLocalGroup // // Create Terminal Server Computers local group if not exist // and create the security descriptor of this local group /****************************************************************************/ BOOL TSLSCreateLocalGroupSecDes(BOOL fEnterpriseServer) { DWORD dwStatus; LPWSTR ReferencedDomainName = NULL; ULONG SidSize, ReferencedDomainNameSize; SID_NAME_USE SidNameUse; WCHAR TSLSLocalGroupName[TSLSLOCALGROUPNAMELENGTH]; WCHAR TSLSLocalGroupDes[TSLSLOCALGROUPDESLENGTH]; GROUP_INFO_1 TSLSGroupInfo = {TSLSLocalGroupName, TSLSLocalGroupDes}; HMODULE HModule = NULL; LOCALGROUP_MEMBERS_INFO_3 DomainComputers = {ALLDOMAINCOMPUTERS}; DWORD cbAcl; DWORD SecurityDescriptorSize; NET_API_STATUS NetStatus; HModule = GetModuleHandle(NULL); if (HModule == NULL) { dwStatus = GetLastError(); if(dwStatus != ERROR_SUCCESS) { TLSLogEvent( EVENTLOG_ERROR_TYPE, TLS_E_SERVICEINIT, TLS_E_CREATETSLSGROUP ); } goto cleanup; } LoadString(HModule, IDS_TSLSLOCALGROUP_NAME, TSLSLocalGroupName, sizeof(TSLSLocalGroupName) / sizeof(WCHAR)); LoadString(HModule, IDS_TSLSLOCALGROUP_DES, TSLSLocalGroupDes, sizeof(TSLSLocalGroupDes) / sizeof(WCHAR)); for( int i = 0; i < 3; i++) { // Create local group if not exist NetStatus = NetLocalGroupAdd( NULL, 1, (LPBYTE)&TSLSGroupInfo, NULL ); if(NERR_Success == NetStatus || NERR_GroupExists == NetStatus || ERROR_ALIAS_EXISTS == NetStatus ) break; Sleep (5000); } if(NERR_Success != NetStatus) { if((NERR_GroupExists != NetStatus) && (ERROR_ALIAS_EXISTS != NetStatus)) { dwStatus = ERROR_ACCESS_DENIED; // // Didn't create the group and group doesn't exist either. // TLSLogEvent( EVENTLOG_ERROR_TYPE, TLS_E_SERVICEINIT, TLS_E_CREATETSLSGROUP ); goto cleanup; } } // // Group created. Now lookup the SID. // SidSize = ReferencedDomainNameSize = 0; ReferencedDomainName = NULL; NetStatus = LookupAccountName( NULL, TSLSGroupInfo.grpi1_name, NULL, &SidSize, NULL, &ReferencedDomainNameSize, &SidNameUse); if( NetStatus ) { dwStatus = GetLastError(); if( ERROR_INSUFFICIENT_BUFFER != dwStatus ) goto cleanup; } g_pSid = (PSID)LocalAlloc(LMEM_FIXED, SidSize); if (NULL == g_pSid) { goto cleanup; } ReferencedDomainName = (LPWSTR)LocalAlloc(LMEM_FIXED, sizeof(WCHAR)*(1+ReferencedDomainNameSize)); if (NULL == ReferencedDomainName) { goto cleanup; } NetStatus = LookupAccountName( NULL, TSLSGroupInfo.grpi1_name, g_pSid, &SidSize, ReferencedDomainName, &ReferencedDomainNameSize, &SidNameUse ); if( 0 == NetStatus ) { // // Failed. // dwStatus = GetLastError(); if(dwStatus != ERROR_SUCCESS) { TLSLogEvent( EVENTLOG_ERROR_TYPE, TLS_E_SERVICEINIT, TLS_E_CREATETSLSGROUP ); } goto cleanup; } // Create Security Descriptor // The size is equal to the size of an SD + twice the length of the SID // (for owner and group) + size of the DACL = sizeof ACL + size of the // ACE, which is an ACE + length of the SID. SecurityDescriptorSize = sizeof(SECURITY_DESCRIPTOR) + sizeof(ACCESS_ALLOWED_ACE) + sizeof(ACL) + 3 * GetLengthSid(g_pSid); g_pSecDes = (PSECURITY_DESCRIPTOR)LocalAlloc(LMEM_FIXED, SecurityDescriptorSize); if (NULL == g_pSecDes) { goto cleanup; } if (!InitializeSecurityDescriptor(g_pSecDes, SECURITY_DESCRIPTOR_REVISION)) { dwStatus = GetLastError(); if(dwStatus != ERROR_SUCCESS) { TLSLogEvent( EVENTLOG_ERROR_TYPE, TLS_E_SERVICEINIT, TLS_E_CREATETSLSGROUP ); } goto cleanup; } SetSecurityDescriptorOwner(g_pSecDes, g_pSid, FALSE); SetSecurityDescriptorGroup(g_pSecDes, g_pSid, FALSE); // Add acl to security descriptor cbAcl = sizeof(ACL) + sizeof (ACCESS_ALLOWED_ACE) - sizeof (DWORD)+ GetLengthSid(g_pSid); g_Dacl = (PACL) LocalAlloc(LMEM_FIXED, cbAcl); if (NULL == g_Dacl) { goto cleanup; } if(!InitializeAcl(g_Dacl, cbAcl, ACL_REVISION)) { dwStatus = GetLastError(); if(dwStatus != ERROR_SUCCESS) { TLSLogEvent( EVENTLOG_ERROR_TYPE, TLS_E_SERVICEINIT, TLS_E_CREATETSLSGROUP ); } goto cleanup; } if(!AddAccessAllowedAce(g_Dacl, ACL_REVISION, STANDARD_RIGHTS_READ, g_pSid)) { dwStatus = GetLastError(); if(dwStatus != ERROR_SUCCESS) { TLSLogEvent( EVENTLOG_ERROR_TYPE, TLS_E_SERVICEINIT, TLS_E_CREATETSLSGROUP ); } goto cleanup; } if(!SetSecurityDescriptorDacl(g_pSecDes, TRUE, g_Dacl, FALSE)) { dwStatus = GetLastError(); if(dwStatus != ERROR_SUCCESS) { TLSLogEvent( EVENTLOG_ERROR_TYPE, TLS_E_SERVICEINIT, TLS_E_CREATETSLSGROUP ); } goto cleanup; } return TRUE; cleanup: if (ReferencedDomainName) LocalFree(ReferencedDomainName); if (g_pSid) LocalFree(g_pSid); if (g_Dacl) LocalFree(g_Dacl); if (g_pSecDes) LocalFree(g_pSecDes); return FALSE; } //------------------------------------------------------------------------------ DWORD ServiceStart( IN DWORD dwArgc, IN LPTSTR *lpszArgv, IN BOOL bDebug ) /* */ { RPC_BINDING_VECTOR *pbindingVector = NULL; WCHAR *pszEntryName = _TEXT(RPC_ENTRYNAME); HANDLE hInitThread=NULL; HANDLE hRpcThread=NULL; HANDLE hMailslotThread=NULL; HANDLE hShutdownThread=NULL; DWORD dump; HANDLE hEvent=NULL; DWORD dwStatus=ERROR_SUCCESS; WORD wVersionRequested; WSADATA wsaData; int err; if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, SERVICE_WAITHINT)) { // resource leak but something went wrong already. dwStatus = TLS_E_SC_REPORT_STATUS; goto cleanup; } hrStatus = CoInitializeEx (NULL, COINIT_MULTITHREADED); if (FAILED (hrStatus)) { DBGPrintf( DBG_INFORMATION, DBG_FACILITY_INIT, DBGLEVEL_FUNCTION_DETAILSIMPLE, _TEXT("CoInitializeEx failed with error code %08x...\n"), hrStatus ); } if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, SERVICE_WAITHINT)) { // resource leak but something went wrong already. dwStatus = TLS_E_SC_REPORT_STATUS; goto cleanup; } if (SUCCEEDED (hrStatus)) { hrStatus = CoInitializeSecurity( NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_CONNECT, RPC_C_IMP_LEVEL_IDENTIFY, NULL, EOAC_NONE, NULL ); } if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, SERVICE_WAITHINT)) { // resource leak but something went wrong already. dwStatus = TLS_E_SC_REPORT_STATUS; goto cleanup; } if (SUCCEEDED (hrStatus)) { g_pWriter = new CTlsVssJetWriter; if (NULL == g_pWriter) { DBGPrintf( DBG_INFORMATION, DBG_FACILITY_INIT, DBGLEVEL_FUNCTION_DETAILSIMPLE, _TEXT("new CVssJetWriter failed...\n") ); hrStatus = HRESULT_FROM_WIN32 (ERROR_NOT_ENOUGH_MEMORY); } } // Report the status to the service control manager. if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, SERVICE_WAITHINT)) { // resource leak but something went wrong already. dwStatus = TLS_E_SC_REPORT_STATUS; goto cleanup; } { DWORD dwConsole; DWORD dwDbLevel; DWORD dwType; DWORD dwSize = sizeof(dwConsole); DWORD status; HKEY hKey=NULL; status = RegOpenKeyEx( HKEY_LOCAL_MACHINE, LSERVER_PARAMETERS_KEY, 0, KEY_ALL_ACCESS, &hKey); if(status == ERROR_SUCCESS) { if(RegQueryValueEx( hKey, LSERVER_PARAMETERS_CONSOLE, NULL, &dwType, (LPBYTE)&dwConsole, &dwSize ) != ERROR_SUCCESS) { dwConsole = 0; } dwSize = sizeof(dwDbLevel); if(RegQueryValueEx( hKey, LSERVER_PARAMETERS_LOGLEVEL, NULL, &dwType, (LPBYTE)&dwDbLevel, &dwSize ) == ERROR_SUCCESS) { InitDBGPrintf( dwConsole != 0, _TEXT(SZSERVICENAME), dwDbLevel ); } RegCloseKey(hKey); } } // Report the status to the service control manager. if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, SERVICE_WAITHINT)) { // resource leak but something went wrong already. dwStatus = TLS_E_SC_REPORT_STATUS; goto cleanup; } do { dwStatus = SetupNamedPipes(); if (dwStatus != ERROR_SUCCESS) { break; } wVersionRequested = MAKEWORD( 1, 1 ); err = WSAStartup( wVersionRequested, &wsaData ); if(err != 0) { // None critical error TLSLogWarningEvent( TLS_E_SERVICE_WSASTARTUP ); } else { char hostname[(MAXTCPNAME+1)*sizeof(TCHAR)]; err=gethostname(hostname, MAXTCPNAME*sizeof(TCHAR)); if(err == 0) { struct addrinfo *paddrinfo; struct addrinfo hints; memset(&hints,0,sizeof(hints)); hints.ai_flags = AI_CANONNAME; hints.ai_family = PF_UNSPEC; if (0 == getaddrinfo(hostname,NULL,&hints,&paddrinfo) && paddrinfo && paddrinfo->ai_canonname) { err = (MultiByteToWideChar( GetACP(), MB_ERR_INVALID_CHARS, paddrinfo->ai_canonname, -1, g_szHostName, g_cbHostName) == 0) ? -1 : 0; } else { err = -1; } freeaddrinfo(paddrinfo); } } if(err != 0) { if(GetComputerName(g_szHostName, &g_cbHostName) == FALSE) { dwStatus = GetLastError(); DBGPrintf( DBG_INFORMATION, DBG_FACILITY_INIT, DBGLEVEL_FUNCTION_DETAILSIMPLE, _TEXT("GetComputerName() failed with %d...\n"), dwStatus ); // this shoule not happen... TLSLogErrorEvent(TLS_E_INIT_GENERAL); break; } } if(GetComputerName(g_szComputerName, &g_cbComputerName) == FALSE) { dwStatus = GetLastError(); DBGPrintf( DBG_INFORMATION, DBG_FACILITY_INIT, DBGLEVEL_FUNCTION_DETAILSIMPLE, _TEXT("GetComputerName() failed with %d...\n"), dwStatus ); // this shoule not happen... TLSLogErrorEvent(TLS_E_INIT_GENERAL); break; } hRpcPause=CreateEvent(NULL, TRUE, TRUE, NULL); if(!hRpcPause) { TLSLogErrorEvent(TLS_E_ALLOCATE_RESOURCE); dwStatus = TLS_E_ALLOCATE_RESOURCE; break; } // // start up general server and RPC initialization thread // hInitThread=ServerInit(bDebug); if(hInitThread==NULL) { TLSLogErrorEvent(TLS_E_SERVICE_STARTUP_CREATE_THREAD); dwStatus = TLS_E_SERVICE_STARTUP_CREATE_THREAD; break; } dwStatus = ERROR_SUCCESS; // // Wait for general server init. thread to terminate // while(WaitForSingleObject( hInitThread, 100 ) == WAIT_TIMEOUT) { // Report the status to the service control manager. if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, SERVICE_WAITHINT)) { // resource leak but something went wrong already. dwStatus = TLS_E_SC_REPORT_STATUS; break; } } if(dwStatus != ERROR_SUCCESS) { break; } // Check thread exit code. GetExitCodeThread( hInitThread, &dwStatus ); if(dwStatus != ERROR_SUCCESS) { // // Server init. thread logs its own error // dwStatus = TLS_E_SERVICE_STARTUP_INIT_THREAD_ERROR; break; } CloseHandle(hInitThread); hInitThread=NULL; // Create the Terminal Servers group in case of Domain a/ Enterprise LS BOOL fInDomain; DWORD dwErr; BOOL fEnterprise = FALSE; if(GetLicenseServerRole() & TLSERVER_ENTERPRISE_SERVER) { fEnterprise = TRUE; } else { dwErr = TLSInDomain(&fInDomain,NULL); } if(fEnterprise == TRUE || ( dwErr == ERROR_SUCCESS && fInDomain == TRUE)) { // Create the License Server group that contains the list of Terminal servers that have access to it. if (!TSLSCreateLocalGroupSecDes(fEnterprise)) { TLSLogErrorEvent(TLS_E_CREATETSLSGROUP); goto cleanup; } } // timing, if we startup RPC init thread but database init thread // can't initialize, service will be in forever stop state. hRpcThread=CreateThread( NULL, 0, RPCServiceStartThread, ULongToPtr(bDebug), 0, &dump ); if(hRpcThread == NULL) { TLSLogErrorEvent(TLS_E_SERVICE_STARTUP_CREATE_THREAD); dwStatus=TLS_E_SERVICE_STARTUP_CREATE_THREAD; break; } dwStatus = ERROR_SUCCESS; // // Wait for RPC init. thread to terminate // while(WaitForSingleObject( hRpcThread, 100 ) == WAIT_TIMEOUT) { // Report the status to the service control manager. if (!ReportStatusToSCMgr(SERVICE_START_PENDING, // service state NO_ERROR, // exit code SERVICE_WAITHINT)) // wait hint { dwStatus = TLS_E_SC_REPORT_STATUS; break; } } if(dwStatus != ERROR_SUCCESS) { break; } // Check thread exit code. GetExitCodeThread(hRpcThread, &dwStatus); if(dwStatus != ERROR_SUCCESS) { dwStatus = TLS_E_SERVICE_STARTUP_RPC_THREAD_ERROR; break; } CloseHandle(hRpcThread); hRpcThread=NULL; // // Tell server control manager that we are ready. // if (!ReportStatusToSCMgr( SERVICE_RUNNING, // service state NO_ERROR, // exit code SERVICE_WAITHINT // wait hint )) { dwStatus = TLS_E_SC_REPORT_STATUS; break; } // // Post service init. load self-signed certificate and init. crypt. // this is needed after reporting service running status back to // service control manager because it may need to manually call // StartService() to startup protected storage service. // if(InitCryptoAndCertificate() != ERROR_SUCCESS) { dwStatus = TLS_E_SERVICE_STARTUP_POST_INIT; break; } TLSLogInfoEvent(TLS_I_SERVICE_START); // RpcMgmtWaitServerListen() will block until the server has // stopped listening. If this service had something better to // do with this thread, it would delay this call until // ServiceStop() had been called. (Set an event in ServiceStop()). // BOOL bOtherServiceStarted = FALSE; do { WaitForSingleObject(hRpcPause, INFINITE); if(ssCurrentStatus == SERVICE_STOP_PENDING) { break; } // Start accepting client calls.PostServiceInit dwStatus = RpcServerListen( RPC_MINIMUMCALLTHREADS, RPC_MAXIMUMCALLTHREADS, TRUE ); if(dwStatus != RPC_S_OK) { TLSLogErrorEvent(TLS_E_RPC_LISTEN); dwStatus = TLS_E_SERVICE_RPC_LISTEN; break; } // // Initialize all policy module // if(bOtherServiceStarted == FALSE) { dwStatus = PostServiceInit(); if(dwStatus != ERROR_SUCCESS) { // faild to initialize. break; } //ServiceInitPolicyModule(); } bOtherServiceStarted = TRUE; DBGPrintf( DBG_INFORMATION, DBG_FACILITY_INIT, DBGLEVEL_FUNCTION_DETAILSIMPLE, _TEXT("Ready to accept request...\n") ); dwStatus = RpcMgmtWaitServerListen(); assert(dwStatus == RPC_S_OK); } while(TRUE); // tell service control manager we are stopping ReportStatusToSCMgr( SERVICE_STOP_PENDING, NO_ERROR, SERVICE_WAITHINT ); // // Terminate - ignore all error here on // dwStatus = RpcServerUnregisterIf( TermServLicensingBackup_v1_0_s_ifspec, NULL, TRUE ); // tell service control manager we are stopping ReportStatusToSCMgr( SERVICE_STOP_PENDING, NO_ERROR, SERVICE_WAITHINT ); dwStatus = RpcServerUnregisterIf( HydraLicenseService_v1_0_s_ifspec, NULL, TRUE ); // tell service control manager we are stopping ReportStatusToSCMgr( SERVICE_STOP_PENDING, NO_ERROR, SERVICE_WAITHINT ); dwStatus = RpcServerUnregisterIf( TermServLicensing_v1_0_s_ifspec, // from rpcsvc.h NULL, TRUE ); // tell service control manager we are stopping ReportStatusToSCMgr( SERVICE_STOP_PENDING, NO_ERROR, SERVICE_WAITHINT ); // Remove entries from the endpoint mapper database. dwStatus = RpcEpUnregister( HydraLicenseService_v1_0_s_ifspec, // from rpcsvc.h pbindingVector, NULL ); // tell service control manager we are stopping ReportStatusToSCMgr( SERVICE_STOP_PENDING, NO_ERROR, SERVICE_WAITHINT ); // Remove entries from the endpoint mapper database. dwStatus = RpcEpUnregister( TermServLicensing_v1_0_s_ifspec, // from rpcsvc.h pbindingVector, NULL ); // tell service control manager we are stopping ReportStatusToSCMgr( SERVICE_STOP_PENDING, NO_ERROR, SERVICE_WAITHINT ); // Remove entries from the endpoint mapper database. dwStatus = RpcEpUnregister( TermServLicensingBackup_v1_0_s_ifspec, // from rpcsvc.h pbindingVector, NULL ); // Get server binding handles dwStatus = RpcServerInqBindings( &pbindingVector ); if(dwStatus == ERROR_SUCCESS) { dwStatus = RpcBindingVectorFree( &pbindingVector ); } // Create entry name in name database first // Only work for NT 5.0 // status = RpcNsMgmtEntryDelete(RPC_C_NS_SYNTAX_DEFAULT, pszEntryName); // try to report the stopped status to the service control manager. // // Initialize Crypto. } while(FALSE); if(hInitThread != NULL) { CloseHandle(hInitThread); } if(hRpcThread != NULL) { CloseHandle(hRpcThread); } if(hMailslotThread != NULL) { CloseHandle(hMailslotThread); } if(hEvent != NULL) { CloseHandle(hEvent); } if(hRpcPause != NULL) { CloseHandle(hRpcPause); } if(err == 0) { WSACleanup(); } ReportStatusToSCMgr( SERVICE_STOP_PENDING, dwStatus, //NO_ERROR, SERVICE_WAITHINT ); // // Create another thread to shutdown server. // hShutdownThread=CreateThread( NULL, 0, ServiceShutdownThread, (VOID *)NULL, 0, &dump ); if(hShutdownThread == NULL) { // Report the status to the service control manager with // long wait hint time. ReportStatusToSCMgr( SERVICE_STOP_PENDING, NO_ERROR, SERVICE_SHUTDOWN_WAITTIME ); // // can't create thread, just call shutdown directory // ServerShutdown(); } else { // // report in 5 second interval to SC. // DWORD dwMaxWaitTime = SERVICE_SHUTDOWN_WAITTIME / 5000; DWORD dwTimes=0; // // Wait for general server shutdown thread to terminate // Gives max 1 mins to shutdown // while(WaitForSingleObject( hShutdownThread, SC_WAITHINT ) == WAIT_TIMEOUT && dwTimes++ < dwMaxWaitTime) { // Report the status to the service control manager. ReportStatusToSCMgr( SERVICE_STOP_PENDING, NO_ERROR, SERVICE_WAITHINT ); } CloseHandle(hShutdownThread); } cleanup: if (NULL != g_pWriter) { g_pWriter->Uninitialize(); delete g_pWriter; g_pWriter = NULL; } CoUninitialize( ); // Signal we are safe to shutting down SetEvent(gSafeToTerminate); return dwStatus; } //----------------------------------------------------------------- VOID ServiceStop() /*++ ++*/ { ReportStatusToSCMgr( SERVICE_STOP_PENDING, NO_ERROR, 0 ); // Stop's the server, wakes the main thread. SetEvent(hRpcPause); // // Signal currently waiting RPC call to terminate // ServiceSignalShutdown(); // this is the actual time we receive shutdown request. SetServiceLastShutdownTime(); (VOID)RpcMgmtStopServerListening(NULL); TLSLogInfoEvent(TLS_I_SERVICE_STOP); } //----------------------------------------------------------------- VOID ServicePause() /*++ ++*/ { ResetEvent(hRpcPause); (VOID)RpcMgmtStopServerListening(NULL); TLSLogInfoEvent(TLS_I_SERVICE_PAUSED); } //----------------------------------------------------------------- VOID ServiceContinue() /*++ ++*/ { SetEvent(hRpcPause); TLSLogInfoEvent(TLS_I_SERVICE_CONTINUE); } //----------------------------------------------------------------- BOOL ReportStatusToSCMgr( IN DWORD dwCurrentState, IN DWORD dwExitCode, IN DWORD dwWaitHint ) /*++ Abstract: Sets the current status of the service and reports it to the Service Control Manager Parameter: dwCurrentState - the state of the service dwWin32ExitCode - error code to report dwWaitHint - worst case estimate to next checkpoint Returns: TRUE if success, FALSE otherwise */ { BOOL fResult=TRUE; if(g_bReportToSCM == TRUE) { SERVICE_STATUS ssStatus; static DWORD dwCheckPoint = 1; ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; // // global - current status of process // ssCurrentStatus = dwCurrentState; if (dwCurrentState == SERVICE_START_PENDING) { ssStatus.dwControlsAccepted = 0; } else { ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_CONTROL_SHUTDOWN; } ssStatus.dwCurrentState = dwCurrentState; if(dwExitCode != NO_ERROR) { ssStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR; ssStatus.dwServiceSpecificExitCode = dwExitCode; } else { ssStatus.dwWin32ExitCode = dwExitCode; } 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. // fResult = SetServiceStatus( sshStatusHandle, &ssStatus ); if(fResult == FALSE) { DBGPrintf( DBG_INFORMATION, DBG_FACILITY_INIT, DBGLEVEL_FUNCTION_TRACE, _TEXT("Failed to set service status %d...\n"), GetLastError() ); TLSLogErrorEvent(TLS_E_SC_REPORT_STATUS); } } return fResult; } /////////////////////////////////////////////////////////////////// // // The following code is for running the service as a console app // void CmdDebugService( IN int argc, IN char ** argv, IN BOOL bDebug ) /* */ { int dwArgc; LPTSTR *lpszArgv; #ifdef UNICODE lpszArgv = CommandLineToArgvW(GetCommandLineW(), &(dwArgc) ); #else dwArgc = (DWORD) argc; lpszArgv = argv; #endif _tprintf( _TEXT("Debugging %s.\n"), _TEXT(SZSERVICEDISPLAYNAME) ); SetConsoleCtrlHandler( ControlHandler, TRUE ); ServiceStart( dwArgc, lpszArgv, bDebug ); } //------------------------------------------------------------------ BOOL WINAPI ControlHandler( IN DWORD dwCtrlType ) /*++ Abstract: Parameter: IN dwCtrlType : control type Return: ++*/ { switch( dwCtrlType ) { case CTRL_BREAK_EVENT: // use Ctrl+C or Ctrl+Break to simulate case CTRL_C_EVENT: // SERVICE_CONTROL_STOP in debug mode _tprintf( _TEXT("Stopping %s.\n"), _TEXT(SZSERVICEDISPLAYNAME) ); ssCurrentStatus = SERVICE_STOP_PENDING; ServiceStop(); return TRUE; break; } return FALSE; }