/*++ Copyright (c) 1996 Microsoft Corporation Module Name: service.cpp Abstract: Routines to configure/analyze general settings of services plus some helper APIs Author: Jin Huang (jinhuang) 25-Jun-1997 Revision History: --*/ #include "headers.h" #include "serverp.h" #include "service.h" #include "pfp.h" //#define SCESVC_DBG 1 DWORD ScepPollOnServiceStartStop( IN BOOL bPollOnStart, IN SC_HANDLE hService ); VOID ScepStopServiceAndAncestorServices( IN SC_HANDLE hScManager, IN PWSTR pszServiceName ); SCESTATUS ScepConfigureGeneralServices( IN PSCECONTEXT hProfile, IN PSCE_SERVICES pServiceList, IN DWORD ConfigOptions ) /* Routine Descripton: Configure startup and security descriptor settings for the list of services passed in. Arguments: pServiceList - the list of services to configure Return Value: SCE status */ { SCESTATUS SceErr=SCESTATUS_SUCCESS; PSCE_SERVICES pNode; DWORD nServices=0; BOOL bDoneSettingSaclDacl = FALSE; NTSTATUS NtStatus = 0; SID_IDENTIFIER_AUTHORITY IdAuth=SECURITY_NT_AUTHORITY; DWORD rcSaveRsop = ERROR_SUCCESS; PSCESECTION hSectionDomain=NULL; PSCESECTION hSectionTattoo=NULL; PSCE_SERVICES pServiceCurrent=NULL; DWORD ServiceLen=0; BOOL bIgnoreStartupType = FALSE; if ( (ConfigOptions & SCE_POLICY_TEMPLATE) && ScepIsSystemShutDown() ) { return(SCESTATUS_SERVICE_NOT_SUPPORT); } if ( pServiceList != NULL ) { SC_HANDLE hScManager; // // open the manager // hScManager = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS // SC_MANAGER_CONNECT | // SC_MANAGER_QUERY_LOCK_STATUS | // SC_MANAGER_MODIFY_BOOT_CONFIG ); SC_HANDLE hService=NULL; DWORD rc=NO_ERROR; if ( NULL == hScManager ) { rc = GetLastError(); ScepLogOutput3(1, rc, SCEDLL_ERROR_OPEN, L"Service Control Manager"); ScepPostProgress(TICKS_GENERAL_SERVICES, AREA_SYSTEM_SERVICE, NULL); return( ScepDosErrorToSceStatus(rc) ); } LPQUERY_SERVICE_CONFIG pConfig=NULL; DWORD BytesNeeded; // // Adjust privilege for setting SACL // rc = SceAdjustPrivilege( SE_SECURITY_PRIVILEGE, TRUE, NULL ); // // if can't adjust privilege, ignore (will error out later if SACL is requested) // if ( rc != NO_ERROR ) { ScepLogOutput3(1, rc, SCEDLL_ERROR_ADJUST, L"SE_SECURITY_PRIVILEGE"); rc = NO_ERROR; } // // Adjust privilege for setting ownership (if required) // rc = SceAdjustPrivilege( SE_TAKE_OWNERSHIP_PRIVILEGE, TRUE, NULL ); // // if can't adjust privilege, ignore (will error out later if acls need to be written) // if ( rc != NO_ERROR ) { ScepLogOutput3(1, rc, SCEDLL_ERROR_ADJUST, L"SE_TAKE_OWNERSHIP_PRIVILEGE"); rc = NO_ERROR; } // // get AdminsSid in case need to take ownership later // NtStatus = RtlAllocateAndInitializeSid( &IdAuth, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &AdminsSid ); // // open the policy/tattoo tables // if ( ConfigOptions & SCE_POLICY_TEMPLATE ) { ScepTattooOpenPolicySections( hProfile, szServiceGeneral, &hSectionDomain, &hSectionTattoo ); } // // Loop through each service to set general setting // for ( pNode=pServiceList; pNode != NULL && rc == NO_ERROR; pNode = pNode->Next ) { // // to ignore startup type, the inf template will have svcname,,"SDDL" // on import, the database gets svcname,0,"SDDL" // so we have to ignore the startuptype of 0 for this service // if (pNode->Startup == 0) { bIgnoreStartupType = TRUE; } // // print the service name // if ( nServices < TICKS_GENERAL_SERVICES ) { ScepPostProgress(1, AREA_SYSTEM_SERVICE, pNode->ServiceName); nServices++; } ScepLogOutput3(2,0, SCEDLL_SCP_CONFIGURE, pNode->ServiceName); if ( (ConfigOptions & SCE_POLICY_TEMPLATE) && ScepIsSystemShutDown() ) { rc = ERROR_NOT_SUPPORTED; break; } ServiceLen = 0; if ( (ConfigOptions & SCE_POLICY_TEMPLATE) && hSectionDomain && hSectionTattoo ) { // // check if we need to query current setting for the service // ServiceLen = wcslen(pNode->ServiceName); if ( ScepTattooIfQueryNeeded(hSectionDomain, hSectionTattoo, pNode->ServiceName, ServiceLen, NULL, NULL ) ) { rc = ScepQueryAndAddService( hScManager, pNode->ServiceName, NULL, &pServiceCurrent ); if ( ERROR_SUCCESS != rc ) { ScepLogOutput3(1,0,SCESRV_POLICY_TATTOO_ERROR_QUERY,rc,pNode->ServiceName); rc = NO_ERROR; } else { ScepLogOutput3(3,0,SCESRV_POLICY_TATTOO_QUERY,pNode->ServiceName); } } } bDoneSettingSaclDacl = FALSE; rcSaveRsop = ERROR_SUCCESS; // // open the service // hService = OpenService( hScManager, pNode->ServiceName, SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG | READ_CONTROL | WRITE_DAC | // WRITE_OWNER | owner can't be set for a service ACCESS_SYSTEM_SECURITY ); // if access was denied, try to take ownership // and try to open service again if (hService == NULL && (ERROR_ACCESS_DENIED == (rc = GetLastError())) && pNode->General.pSecurityDescriptor) { DWORD rcTakeOwnership = NO_ERROR; if (AdminsSid) { if ( NO_ERROR == (rcTakeOwnership = SetNamedSecurityInfo( (LPWSTR)pNode->ServiceName, SE_SERVICE, OWNER_SECURITY_INFORMATION, AdminsSid, NULL, NULL, NULL ))) { // // ownership changed, open service again and set SACL and DACL // get a handle to set security // if ( hService = OpenService( hScManager, pNode->ServiceName, READ_CONTROL | WRITE_DAC | ACCESS_SYSTEM_SECURITY )) { if ( SetServiceObjectSecurity( hService, pNode->SeInfo & (DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION), pNode->General.pSecurityDescriptor ) ) { bDoneSettingSaclDacl = TRUE; CloseServiceHandle(hService); hService = NULL; // // re-open the service only if there are other config info // to set (startup type). // So when NOSTARTTYPE is set, do not need to reopen the service // if (FALSE == bIgnoreStartupType) { if (!(hService = OpenService( hScManager, pNode->ServiceName, SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG )) ) { rc = GetLastError(); } else { // //clear any error we have seen so far since everything has succeeded // rc = NO_ERROR; } } } else { // // shouldn't fail here unless Service Control Manager // fails for some reason. // rc = GetLastError(); } } else { // // still fail to open the service to set DACL. this should // not happen for admin logons since the current logon is // one of the owner. But for normal user logon, this could // fail (actually normal user logon should fail to set // the owner rc = GetLastError(); } } } else { // // AdminSid failed to be initialized, get the error // rcTakeOwnership = RtlNtStatusToDosError(NtStatus); } if ( NO_ERROR != rcTakeOwnership || NO_ERROR != rc ) { // // log the error occurred in take ownership process // reset error back to access denied so it will also be // logged as failure to open the service // if (NO_ERROR != rcTakeOwnership) ScepLogOutput3(2,rcTakeOwnership, SCEDLL_ERROR_TAKE_OWNER, (LPWSTR)pNode->ServiceName); else ScepLogOutput3(2, rc, SCEDLL_ERROR_OPEN, (LPWSTR)pNode->ServiceName); rc = ERROR_ACCESS_DENIED; } } if ( hService != NULL ) { if (bIgnoreStartupType == TRUE) { // // do not configure service start type // if ( pNode->General.pSecurityDescriptor != NULL ) { if ( !SetServiceObjectSecurity( hService, pNode->SeInfo & (DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION), pNode->General.pSecurityDescriptor ) ) { rc = GetLastError(); } else bDoneSettingSaclDacl = TRUE; } } else { // // Phase-1 (in phase-2 the service will be started/stopped real-time) // // // query the length of config // if ( !QueryServiceConfig( hService, NULL, 0, &BytesNeeded ) ) { rc = GetLastError(); if ( rc == ERROR_INSUFFICIENT_BUFFER ) { pConfig = (LPQUERY_SERVICE_CONFIG)ScepAlloc(0, BytesNeeded); if ( pConfig != NULL ) { // // the real query of config // if ( QueryServiceConfig( hService, pConfig, BytesNeeded, &BytesNeeded ) ) { rc = ERROR_SUCCESS; // // change pConfig->dwStartType to the new value // if ( pNode->Startup != (BYTE)(pConfig->dwStartType) ) { // // configure the service startup // if ( !ChangeServiceConfig( hService, pConfig->dwServiceType, pNode->Startup, pConfig->dwErrorControl, pConfig->lpBinaryPathName, pConfig->lpLoadOrderGroup, NULL, pConfig->lpDependencies, NULL, NULL, pConfig->lpDisplayName ) ) { rc = GetLastError(); } } if ( rc == NO_ERROR && pNode->General.pSecurityDescriptor != NULL && !bDoneSettingSaclDacl) { if ( !SetServiceObjectSecurity( hService, pNode->SeInfo & (DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION), pNode->General.pSecurityDescriptor ) ) { rc = GetLastError(); } else bDoneSettingSaclDacl = TRUE; } } else { rc = GetLastError(); ScepLogOutput3(3,rc, SCEDLL_ERROR_QUERY_INFO, pNode->ServiceName); } ScepFree(pConfig); pConfig = NULL; } else { // // cannot allocate pConfig // rc = ERROR_NOT_ENOUGH_MEMORY; } } else { ScepLogOutput3(3,rc, SCEDLL_ERROR_QUERY_INFO, pNode->ServiceName); } } else { // // should not fall in here // rc = ERROR_SUCCESS; } } CloseServiceHandle (hService); hService = NULL; if ( rc != NO_ERROR ) { ScepLogOutput3(1, rc, SCEDLL_SCP_ERROR_CONFIGURE, pNode->ServiceName); rcSaveRsop = rc; if ( ERROR_INVALID_OWNER == rc || ERROR_INVALID_PRIMARY_GROUP == rc || ERROR_INVALID_SECURITY_DESCR == rc || ERROR_INVALID_ACL == rc || ERROR_ACCESS_DENIED == rc ) { gWarningCode = rc; rc = NO_ERROR; } } } else { // // cannot open the service or some error taking ownership // if (rc != NO_ERROR) { ScepLogOutput3(1, rc, SCEDLL_ERROR_OPEN, pNode->ServiceName); // either of setting security/startup type failed - save it for RSOP log rcSaveRsop = (rcSaveRsop == ERROR_SUCCESS ? rc: rcSaveRsop); if ( rc == ERROR_SERVICE_DOES_NOT_EXIST ) rc = NO_ERROR; } } if (ConfigOptions & SCE_RSOP_CALLBACK) ScepRsopLog(SCE_RSOP_SERVICES_INFO, rcSaveRsop != NO_ERROR ? rcSaveRsop : rc, pNode->ServiceName, 0, 0); if ( (ConfigOptions & SCE_POLICY_TEMPLATE) && hSectionDomain && hSectionTattoo ) { // // manage the tattoo value of this one // ScepTattooManageOneServiceValue( hSectionDomain, hSectionTattoo, pNode->ServiceName, ServiceLen, pServiceCurrent, rc ); } if ( pServiceCurrent ) { SceFreePSCE_SERVICES(pServiceCurrent); pServiceCurrent = NULL; } bIgnoreStartupType = FALSE; } if ( !(ConfigOptions & SCE_SERVICE_NO_REALTIME_ENFORCE) ) { // // real-time start/stop only if NOT in setup/dcpromo // i.e. whenever SCE_SETUP_SERVICE_NOSTARTTYPE was used before // // // Phase-2 (in phase-1 the startup-type was only configured but not enforced real-time) // for ( pNode=pServiceList; pNode != NULL ; pNode = pNode->Next ) { if (pNode->Startup == SERVICE_DISABLED) { // // we should also stop the ancestor services // ScepStopServiceAndAncestorServices(hScManager, pNode->ServiceName); } else if (pNode->Startup == SERVICE_AUTO_START) { // // if the service type is "automatic", we should start the service // Note: dependencies are already taken care of by SCM // if ( hService = OpenService( hScManager, pNode->ServiceName, SERVICE_START | SERVICE_QUERY_STATUS )) { SERVICE_STATUS ServiceStatus; if (!StartService(hService, 0, NULL )) { if ( ERROR_SERVICE_ALREADY_RUNNING != GetLastError() ) { ScepLogOutput3(2, GetLastError(), SCEDLL_SCP_ERROR_START, pNode->ServiceName); } } else { DWORD dwError; dwError = ScepPollOnServiceStartStop( TRUE , hService ); if ( dwError != ERROR_SUCCESS ) { ScepLogOutput3(2, dwError, SCEDLL_SCP_ERROR_START, pNode->ServiceName); } } CloseServiceHandle (hService); hService = NULL; } else { ScepLogOutput3(2, GetLastError(), SCEDLL_SCP_ERROR_OPENFORSTART, pNode->ServiceName); } } } } CloseServiceHandle (hScManager); if (AdminsSid) { RtlFreeSid(AdminsSid); AdminsSid = NULL; } SceAdjustPrivilege( SE_TAKE_OWNERSHIP_PRIVILEGE, FALSE, NULL ); SceAdjustPrivilege( SE_SECURITY_PRIVILEGE, FALSE, NULL ); SceErr = ScepDosErrorToSceStatus(rc); } if ( nServices < TICKS_GENERAL_SERVICES ) { ScepPostProgress(TICKS_GENERAL_SERVICES-nServices, AREA_SYSTEM_SERVICE, NULL); } SceJetCloseSection(&hSectionDomain, TRUE); SceJetCloseSection(&hSectionTattoo, TRUE); return(SceErr); } DWORD ScepPollOnServiceStartStop( IN BOOL bPollOnStart, IN SC_HANDLE hService ) /* Routine Descripton: This routine polls on a service until it is really started or stopped using time-slice hints. Arguments: bPollOnStart - if TRUE (FALSE), polls until really started (stopped) hService - handle to service to poll on Return Value: win32 error code - ERROR_SUCCESS or other error */ { SERVICE_STATUS ssStatus; DWORD dwOldCheckPoint; DWORD dwStartTickCount; DWORD dwWaitTime; DWORD dwStatus = ERROR_SUCCESS; // // Check the status until the service is no longer pending (start or stop) // if (!QueryServiceStatus( hService, &ssStatus) ) { dwStatus = GetLastError(); goto ExitHandler; } // // Save the tick count and initial checkpoint. // dwStartTickCount = GetTickCount(); dwOldCheckPoint = ssStatus.dwCheckPoint; // // Poll until service has started or stopped // while (!((bPollOnStart && ssStatus.dwCurrentState == SERVICE_RUNNING) || (!bPollOnStart && ssStatus.dwCurrentState == SERVICE_STOPPED ))) { // // Do not wait longer than the wait hint. A good interval is // one tenth the wait hint, but no less than 1 second and no // more than 10 seconds. // dwWaitTime = ssStatus.dwWaitHint / 10; if( dwWaitTime < 1000 ) dwWaitTime = 1000; else if ( dwWaitTime > 10000 ) dwWaitTime = 10000; Sleep( dwWaitTime ); // // Check the status again. // if (!QueryServiceStatus( hService, &ssStatus) ) { dwStatus = GetLastError(); goto ExitHandler; } if ( ssStatus.dwCheckPoint > dwOldCheckPoint ) { // // The service is making progress since the checkpoint has been updated. // dwStartTickCount = GetTickCount(); dwOldCheckPoint = ssStatus.dwCheckPoint; } else { if(GetTickCount()-dwStartTickCount > ssStatus.dwWaitHint) { // // No progress made within the wait hint - stop polling // break; } } } // // final check on desired condition // if (!((bPollOnStart && ssStatus.dwCurrentState == SERVICE_RUNNING) || (!bPollOnStart && ssStatus.dwCurrentState == SERVICE_STOPPED ))) dwStatus = ERROR_SERVICE_REQUEST_TIMEOUT; ExitHandler: return dwStatus; } VOID ScepStopServiceAndAncestorServices( IN SC_HANDLE hScManager, IN PWSTR pszServiceName ) /* Routine Description: Stop the named service and all other services that are dependent on it. Arguments: hScManager - handle to the Service Control Manager pszServiceName - name of the service to be stopped Return Value: None: */ { SC_HANDLE hService=NULL; LPENUM_SERVICE_STATUS pArrServices = NULL; if ( hService = OpenService( hScManager, pszServiceName, SERVICE_STOP | SERVICE_ENUMERATE_DEPENDENTS | SERVICE_QUERY_STATUS )) { // // get an array of ancestor services, greatest-ancestor first // DWORD dwBufSizeSupplied = 0; DWORD dwBufSizeRequired = 0; DWORD dwNumServicesReturned = 0; // // first, get the required size of the array // if (!EnumDependentServices( hService, SERVICE_STATE_ALL, pArrServices, 0, &dwBufSizeRequired, &dwNumServicesReturned )) { if (ERROR_MORE_DATA != GetLastError()) { ScepLogOutput3(2, GetLastError(), SCEDLL_SCP_ERROR_STOP, pszServiceName); goto ExitHandler; } } pArrServices = (ENUM_SERVICE_STATUS *) LocalAlloc (LMEM_ZEROINIT, dwBufSizeRequired); if (pArrServices == NULL) { ScepLogOutput3(2, ERROR_NOT_ENOUGH_MEMORY, SCEDLL_SCP_ERROR_STOP, pszServiceName); goto ExitHandler; } // // second, get the array of dependent services // if (!EnumDependentServices( hService, SERVICE_STATE_ALL, pArrServices, dwBufSizeRequired, &dwBufSizeRequired, &dwNumServicesReturned )) { ScepLogOutput3(2, GetLastError(), SCEDLL_SCP_ERROR_STOP, pszServiceName); goto ExitHandler; } // // first stop all the ancestor services // if any of them fails to stop, log it and continue // for (DWORD dwServiceIndex = 0; dwServiceIndex < dwNumServicesReturned; dwServiceIndex++ ) { SC_HANDLE hAncestorService = NULL; if ( hAncestorService = OpenService( hScManager, pArrServices[dwServiceIndex].lpServiceName, SERVICE_STOP | SERVICE_QUERY_STATUS )) { SERVICE_STATUS ServiceStatus; if (!ControlService(hAncestorService, SERVICE_CONTROL_STOP, &ServiceStatus )) { if ( ERROR_SERVICE_NOT_ACTIVE != GetLastError() ) { ScepLogOutput3(2, GetLastError(), SCEDLL_SCP_ERROR_STOP, pArrServices[dwServiceIndex].lpServiceName); } } else { // // move on only if this service stopped // DWORD dwError; dwError = ScepPollOnServiceStartStop( FALSE , hAncestorService ); if ( dwError != ERROR_SUCCESS ) { ScepLogOutput3(2, dwError, SCEDLL_SCP_ERROR_STOP, pArrServices[dwServiceIndex].lpServiceName); } } CloseServiceHandle (hAncestorService); hAncestorService = NULL; } else { ScepLogOutput3(2, GetLastError(), SCEDLL_SCP_ERROR_OPENFORSTOP, pArrServices[dwServiceIndex].lpServiceName); } } LocalFree ( pArrServices ); pArrServices = NULL; // // finally, stop the service itself // SERVICE_STATUS ServiceStatus; if (!ControlService(hService, SERVICE_CONTROL_STOP, &ServiceStatus )) { if ( ERROR_SERVICE_NOT_ACTIVE != GetLastError() ) { ScepLogOutput3(2, GetLastError(), SCEDLL_SCP_ERROR_STOP, pszServiceName); } } else { DWORD dwError; dwError = ScepPollOnServiceStartStop( FALSE , hService ); if ( dwError != ERROR_SUCCESS ) { ScepLogOutput3(2, dwError, SCEDLL_SCP_ERROR_STOP, pszServiceName); } } CloseServiceHandle (hService); hService = NULL; } else { ScepLogOutput3(2, GetLastError(), SCEDLL_SCP_ERROR_OPENFORSTOP, pszServiceName); } ExitHandler: if ( hService ) CloseServiceHandle (hService); if ( pArrServices ) LocalFree ( pArrServices ); } SCESTATUS ScepAnalyzeGeneralServices( IN PSCECONTEXT hProfile, IN DWORD Options ) /* Routine Description: Analyze all available services on the current system. The base profile (SCEP) is in hProfile Arguments: hProfile - the database context handle Return Value: SCE status */ { if ( hProfile == NULL ) { ScepPostProgress(TICKS_GENERAL_SERVICES, AREA_SYSTEM_SERVICE, NULL); return(SCESTATUS_INVALID_PARAMETER); } SCESTATUS rc; PSCE_SERVICES pServiceList=NULL; DWORD nServices=0; rc = SceEnumerateServices( &pServiceList, FALSE ); rc = ScepDosErrorToSceStatus(rc); if ( rc == SCESTATUS_SUCCESS ) { PSCESECTION hSectionScep=NULL, hSectionSap=NULL; // // open the sap section. If it is not there, creates it // rc = ScepStartANewSection( hProfile, &hSectionSap, (Options & SCE_GENERATE_ROLLBACK) ? SCEJET_TABLE_SMP : SCEJET_TABLE_SAP, szServiceGeneral ); if ( rc == SCESTATUS_SUCCESS ) { PSCE_SERVICES pNode = pServiceList; // // open SCEP section. should be success always because the StartANewSection // creates the section if it is not there // rc = ScepOpenSectionForName( hProfile, (Options & SCE_GENERATE_ROLLBACK) ? SCE_ENGINE_SMP : SCE_ENGINE_SCP, // SCE_ENGINE_SMP, szServiceGeneral, &hSectionScep ); if ( rc == SCESTATUS_SUCCESS ) { // // analyze each service // PSCE_SERVICES pOneService=NULL; BOOL IsDifferent; for ( pNode=pServiceList; pNode != NULL; pNode=pNode->Next ) { ScepLogOutput3(2, 0, SCEDLL_SAP_ANALYZE, pNode->ServiceName); if ( nServices < TICKS_SPECIFIC_SERVICES ) { ScepPostProgress(1, AREA_SYSTEM_SERVICE, NULL); nServices++; } // // get setting from the SMP profile // rc = ScepGetSingleServiceSetting( hSectionScep, pNode->ServiceName, &pOneService ); if ( rc == SCESTATUS_SUCCESS ) { // // there is a SMP entry for the service, compare and save // rc = ScepCompareSingleServiceSetting( pOneService, pNode, &IsDifferent ); if ( rc == SCESTATUS_SUCCESS && IsDifferent ) { // // write the service as mismatch // pNode->Status = (Options & SCE_GENERATE_ROLLBACK) ? 0 : SCE_STATUS_MISMATCH; pNode->SeInfo = pOneService->SeInfo; rc = ScepSetSingleServiceSetting( hSectionSap, pNode ); } } else if ( rc == SCESTATUS_RECORD_NOT_FOUND ) { // // this service is not defined // if ( !(Options & SCE_GENERATE_ROLLBACK) ) { // // save the record with not configured status // pNode->Status = SCE_STATUS_NOT_CONFIGURED; rc = ScepSetSingleServiceSetting( hSectionSap, pNode ); } else { // // ignore this one // rc = SCESTATUS_SUCCESS; } } SceFreePSCE_SERVICES(pOneService); pOneService = NULL; if ( rc != SCESTATUS_SUCCESS ) { ScepLogOutput3(1, ScepSceStatusToDosError(rc), SCEDLL_SAP_ERROR_ANALYZE, pNode->ServiceName); if ( SCESTATUS_ACCESS_DENIED == rc ) { gWarningCode = ScepSceStatusToDosError(rc); if ( !(Options & SCE_GENERATE_ROLLBACK) ) { // // raise a error status // pNode->Status = SCE_STATUS_ERROR_NOT_AVAILABLE; rc = ScepSetSingleServiceSetting( hSectionSap, pNode ); } rc = SCESTATUS_SUCCESS; } else { break; } } } SceJetCloseSection(&hSectionScep, TRUE); } if ( !(Options & SCE_GENERATE_ROLLBACK ) ) { // // raise any error item // for ( PSCE_SERVICES pNodeTmp=pNode; pNodeTmp != NULL; pNodeTmp = pNodeTmp->Next ) { pNodeTmp->Status = SCE_STATUS_ERROR_NOT_AVAILABLE; ScepSetSingleServiceSetting( hSectionSap, pNode ); } } SceJetCloseSection(&hSectionSap, TRUE); } if ( rc != SCESTATUS_SUCCESS ) ScepLogOutput3(1, ScepSceStatusToDosError(rc), SCEDLL_SAP_ERROR_OUT); } if ( nServices < TICKS_GENERAL_SERVICES ) { ScepPostProgress(TICKS_GENERAL_SERVICES-nServices, AREA_SYSTEM_SERVICE, NULL); } SceFreePSCE_SERVICES(pServiceList); return(rc); } SCESTATUS ScepGetSingleServiceSetting( IN PSCESECTION hSection, IN PWSTR ServiceName, OUT PSCE_SERVICES *pOneService ) /* Routine Description: Get service settings for the service from the section Arguments: hSection - the section handle ServiceName - the service name pOneService - the service settings Return Value: SCE status */ { if ( hSection == NULL || ServiceName == NULL || pOneService == NULL ) { return(SCESTATUS_INVALID_PARAMETER); } SCESTATUS rc; DWORD ValueLen; // // seek to the record and get length for name and value // rc = SceJetGetValue( hSection, SCEJET_EXACT_MATCH_NO_CASE, ServiceName, NULL, 0, NULL, NULL, 0, &ValueLen ); if ( rc == SCESTATUS_SUCCESS ) { PWSTR Value=NULL; // // allocate memory for the service name and value string // Value = (PWSTR)ScepAlloc( LMEM_ZEROINIT, ValueLen+2); if ( Value != NULL ) { // // Get the service and its value // rc = SceJetGetValue( hSection, SCEJET_CURRENT, NULL, NULL, 0, NULL, Value, ValueLen, &ValueLen ); if ( rc == SCESTATUS_SUCCESS ) { Value[ValueLen/2] = L'\0'; DWORD Win32Rc=NO_ERROR; PSECURITY_DESCRIPTOR pTempSD=NULL; DWORD SDsize=0; SECURITY_INFORMATION SeInfo=0; if ( ValueLen >= 2 && Value[1] != L'\0' ) { // // convert to security descriptor // Win32Rc = ConvertTextSecurityDescriptor( Value+1, &pTempSD, &SDsize, &SeInfo ); } if ( Win32Rc == NO_ERROR ) { ScepChangeAclRevision(pTempSD, ACL_REVISION); // // create this service node // *pOneService = (PSCE_SERVICES)ScepAlloc( LMEM_FIXED, sizeof(SCE_SERVICES) ); if ( *pOneService != NULL ) { (*pOneService)->ServiceName = (PWSTR)ScepAlloc(LMEM_FIXED, (wcslen(ServiceName)+1)*sizeof(WCHAR)); if ( (*pOneService)->ServiceName != NULL ) { wcscpy( (*pOneService)->ServiceName, ServiceName); (*pOneService)->DisplayName = NULL; (*pOneService)->Status = *((BYTE *)Value); (*pOneService)->Startup = *((BYTE *)Value+1); (*pOneService)->General.pSecurityDescriptor = pTempSD; (*pOneService)->SeInfo = SeInfo; (*pOneService)->Next = NULL; // // DO NOT free the following buffers // pTempSD = NULL; } else { rc = SCESTATUS_NOT_ENOUGH_RESOURCE; ScepFree(*pOneService); } } else { rc = SCESTATUS_NOT_ENOUGH_RESOURCE; } if ( pTempSD != NULL ) { ScepFree(pTempSD); } } else { rc = ScepDosErrorToSceStatus(Win32Rc); } } ScepFree(Value); } else rc = SCESTATUS_NOT_ENOUGH_RESOURCE; } return(rc); } SCESTATUS ScepCompareSingleServiceSetting( IN PSCE_SERVICES pNode1, IN PSCE_SERVICES pNode2, OUT PBOOL pIsDifferent ) /* Routine Description: Comare two service settings. Arguments: pNode1 - the first service pNode2 - the second service pIsDifferent - output TRUE if different Return Value: SCE status */ { SCESTATUS rc=SCESTATUS_SUCCESS; // // if Startup == 0, we should ignore comparing the startup types symmetrically // if ( pNode1->Startup == 0 || pNode2->Startup == 0 || pNode1->Startup == pNode2->Startup ) { BYTE resultSD = 0; rc = ScepCompareObjectSecurity( SE_SERVICE, FALSE, pNode1->General.pSecurityDescriptor, pNode2->General.pSecurityDescriptor, pNode1->SeInfo & (DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION), &resultSD ); if ( resultSD ) { *pIsDifferent = TRUE; } else *pIsDifferent = FALSE; } else *pIsDifferent = TRUE; return(rc); } SCESTATUS ScepSetSingleServiceSetting( IN PSCESECTION hSection, IN PSCE_SERVICES pOneService ) /* Routine Description: Set service settings for the service from the section Arguments: hSection - the section handle pOneService - the service settings Return Value: SCE status */ { if ( hSection == NULL || pOneService == NULL ) { return(SCESTATUS_INVALID_PARAMETER); } SCESTATUS rc=SCESTATUS_SUCCESS; PWSTR SDspec=NULL; DWORD SDsize=0; if ( (pOneService->Status != SCE_STATUS_NOT_ANALYZED) && (pOneService->Status != SCE_STATUS_ERROR_NOT_AVAILABLE) && (pOneService->General.pSecurityDescriptor != NULL) ) { DWORD Win32Rc; Win32Rc = ConvertSecurityDescriptorToText ( pOneService->General.pSecurityDescriptor, pOneService->SeInfo, &SDspec, &SDsize // number of w-chars ); rc = ScepDosErrorToSceStatus(Win32Rc); } if ( rc == SCESTATUS_SUCCESS ) { PWSTR Value=NULL; DWORD ValueLen; ValueLen = (SDsize+1)*sizeof(WCHAR); Value = (PWSTR)ScepAlloc( (UINT)0, ValueLen+sizeof(WCHAR) ); if ( Value != NULL ) { // // The first byte is status, the second byte is startup // *((BYTE *)Value) = pOneService->Status; *((BYTE *)Value+1) = pOneService->Startup; if ( SDspec != NULL ) { wcscpy(Value+1, SDspec); } Value[SDsize+1] = L'\0'; //terminate this string // // set the value // rc = SceJetSetLine( hSection, pOneService->ServiceName, FALSE, Value, ValueLen, 0 ); ScepFree( Value ); switch ( pOneService->Status ) { case SCE_STATUS_ERROR_NOT_AVAILABLE: ScepLogOutput3(2, 0, SCEDLL_STATUS_ERROR, pOneService->ServiceName); break; case SCE_STATUS_NOT_CONFIGURED: ScepLogOutput3(2, 0, SCEDLL_STATUS_NC, pOneService->ServiceName); break; case SCE_STATUS_NOT_ANALYZED: ScepLogOutput3(2, 0, SCEDLL_STATUS_NEW, pOneService->ServiceName); break; default: ScepLogOutput3(2, 0, SCEDLL_STATUS_MISMATCH, pOneService->ServiceName); break; } } else rc = SCESTATUS_NOT_ENOUGH_RESOURCE; } if ( SDspec != NULL ) { ScepFree( SDspec ); } return(rc); } SCESTATUS ScepInvokeSpecificServices( IN PSCECONTEXT hProfile, IN BOOL bConfigure, IN SCE_ATTACHMENT_TYPE aType ) /* Routine Description: Call each service engine for configure or analyze Arguments: hProfile - the profile handle bConfigure - TRUE = to configure, FALSE=to analyze aType - attachment type "services" or "policy" Return Value: SCE status */ { // // for posting progress // DWORD nServices=0; AREA_INFORMATION Area=0; DWORD nMaxTicks=0; switch(aType) { case SCE_ATTACHMENT_SERVICE: Area = AREA_SYSTEM_SERVICE; nMaxTicks = TICKS_SPECIFIC_SERVICES; break; case SCE_ATTACHMENT_POLICY: Area = AREA_SECURITY_POLICY; nMaxTicks = TICKS_SPECIFIC_POLICIES; break; } if ( hProfile == NULL ) { ScepPostProgress(nMaxTicks, Area, NULL); return(SCESTATUS_INVALID_PARAMETER); } // // call available service engines to configure specific setting // SCESTATUS SceErr ; PSCE_SERVICES pSvcEngineList=NULL; SCEP_HANDLE sceHandle; SCESVC_CALLBACK_INFO sceCbInfo; SceErr = ScepEnumServiceEngines(&pSvcEngineList, aType); if ( SceErr == SCESTATUS_SUCCESS) { HINSTANCE hDll; PF_ConfigAnalyzeService pfTemp; for ( PSCE_SERVICES pNode=pSvcEngineList; pNode != NULL; pNode = pNode->Next ) { ScepLogOutput3(2, 0, SCEDLL_LOAD_ATTACHMENT, pNode->ServiceName); if ( nServices < nMaxTicks ) { ScepPostProgress(1, Area, pNode->ServiceName); nServices++; } // // load the dll. // hDll = LoadLibrary(pNode->General.ServiceEngineName); if ( hDll != NULL ) { if ( bConfigure ) { // // call SceSvcAttachmentConfig from the dll // pfTemp = (PF_ConfigAnalyzeService) GetProcAddress(hDll, "SceSvcAttachmentConfig") ; } else { // // call SceSvcAttachmentAnalyze from the dll // pfTemp = (PF_ConfigAnalyzeService) GetProcAddress(hDll, "SceSvcAttachmentAnalyze") ; } if ( pfTemp != NULL ) { // // prepare the handle first // sceHandle.hProfile = (PVOID)hProfile; sceHandle.ServiceName = (PCWSTR)(pNode->ServiceName); sceCbInfo.sceHandle = &sceHandle; sceCbInfo.pfQueryInfo = &SceCbQueryInfo; sceCbInfo.pfSetInfo = &SceCbSetInfo; sceCbInfo.pfFreeInfo = &SceSvcpFreeMemory; sceCbInfo.pfLogInfo = &ScepLogOutput2; // // call the SceSvcAttachmentConfig/Analyze from the DLL // __try { SceErr = (*pfTemp)((PSCESVC_CALLBACK_INFO)&sceCbInfo); } __except (EXCEPTION_EXECUTE_HANDLER) { SceErr = SCESTATUS_SERVICE_NOT_SUPPORT; } } else { // // this API is not supported // SceErr = SCESTATUS_SERVICE_NOT_SUPPORT; } // // try to free the library handle. If it fails, just leave it // to to the process to terminate // FreeLibrary(hDll); } else SceErr = SCESTATUS_SERVICE_NOT_SUPPORT; if ( SceErr == SCESTATUS_SERVICE_NOT_SUPPORT ) { if ( bConfigure ) ScepLogOutput3(1, ScepSceStatusToDosError(SceErr), SCEDLL_SCP_NOT_SUPPORT); else ScepLogOutput3(1, ScepSceStatusToDosError(SceErr), SCEDLL_SAP_NOT_SUPPORT); SceErr = SCESTATUS_SUCCESS; } else if ( SceErr != SCESTATUS_SUCCESS && SceErr != SCESTATUS_RECORD_NOT_FOUND ) { ScepLogOutput3(1, ScepSceStatusToDosError(SceErr), SCEDLL_ERROR_LOAD, pNode->ServiceName); } if ( SceErr != SCESTATUS_SUCCESS && SceErr != SCESTATUS_SERVICE_NOT_SUPPORT && SceErr != SCESTATUS_RECORD_NOT_FOUND ) break; } // // free the buffer // SceFreePSCE_SERVICES(pSvcEngineList); } else if ( SceErr != SCESTATUS_SUCCESS && SceErr != SCESTATUS_PROFILE_NOT_FOUND && SceErr != SCESTATUS_RECORD_NOT_FOUND ) { ScepLogOutput3(1, ScepSceStatusToDosError(SceErr), SCEDLL_SAP_ERROR_ENUMERATE, L"services"); } if ( SceErr == SCESTATUS_PROFILE_NOT_FOUND || SceErr == SCESTATUS_RECORD_NOT_FOUND || SceErr == SCESTATUS_SERVICE_NOT_SUPPORT ) { // // no service engine defined // SceErr = SCESTATUS_SUCCESS; } if ( nServices < nMaxTicks ) { ScepPostProgress(nMaxTicks-nServices, Area, NULL); } return(SceErr); } SCESTATUS ScepEnumServiceEngines( OUT PSCE_SERVICES *pSvcEngineList, IN SCE_ATTACHMENT_TYPE aType ) /* Routine Description: Query all services which has a service engine for security manager The service engine information is in the registry: MACHINE\Software\Microsoft\Windows NT\CurrentVersion\SeCEdit Arguments: pSvcEngineList - the service engine list aType - attachment type (service or policy) Return Value: SCE status */ { if ( pSvcEngineList == NULL ) { return(SCESTATUS_INVALID_PARAMETER); } DWORD Win32Rc; HKEY hKey=NULL; switch ( aType ) { case SCE_ATTACHMENT_SERVICE: Win32Rc = RegOpenKeyEx( HKEY_LOCAL_MACHINE, SCE_ROOT_SERVICE_PATH, 0, KEY_READ, &hKey ); break; case SCE_ATTACHMENT_POLICY: Win32Rc = RegOpenKeyEx( HKEY_LOCAL_MACHINE, SCE_ROOT_POLICY_PATH, 0, KEY_READ, &hKey ); break; default: return SCESTATUS_INVALID_PARAMETER; } if ( Win32Rc == ERROR_SUCCESS ) { TCHAR Buffer[MAX_PATH]; DWORD BufSize; DWORD index = 0; DWORD EnumRc; FILETIME LastWriteTime; PWSTR BufTmp=NULL; PWSTR EngineName=NULL; DWORD RegType; // // enumerate all subkeys of the key // do { memset(Buffer, '\0', MAX_PATH*sizeof(WCHAR)); BufSize = MAX_PATH; EnumRc = RegEnumKeyEx( hKey, index, Buffer, &BufSize, NULL, NULL, NULL, &LastWriteTime); if ( EnumRc == ERROR_SUCCESS ) { index++; // // get the service name, query service engine name // BufSize += wcslen(SCE_ROOT_SERVICE_PATH) + 1; //62; BufTmp = (PWSTR)ScepAlloc( 0, (BufSize+1)*sizeof(WCHAR)); if ( BufTmp != NULL ) { switch ( aType ) { case SCE_ATTACHMENT_SERVICE: swprintf(BufTmp, L"%s\\%s", SCE_ROOT_SERVICE_PATH, Buffer); Win32Rc = ScepRegQueryValue( HKEY_LOCAL_MACHINE, BufTmp, L"ServiceAttachmentPath", (PVOID *)&EngineName, &RegType, NULL ); break; case SCE_ATTACHMENT_POLICY: // policies swprintf(BufTmp, L"%s\\%s", SCE_ROOT_POLICY_PATH, Buffer); Win32Rc = ScepRegQueryValue( HKEY_LOCAL_MACHINE, BufTmp, L"PolicyAttachmentPath", (PVOID *)&EngineName, &RegType, NULL ); break; } if ( Win32Rc == ERROR_SUCCESS ) { // // get the service engine name and service name // add them to the service node // Win32Rc = ScepAddOneServiceToList( Buffer, // service name NULL, 0, (PVOID)EngineName, 0, FALSE, pSvcEngineList ); // // free the buffer if it's not added to the list // if ( Win32Rc != ERROR_SUCCESS && EngineName ) { ScepFree(EngineName); } EngineName = NULL; } else if ( Win32Rc == ERROR_FILE_NOT_FOUND ) { // // if no service engine name, ignore this service // Win32Rc = ERROR_SUCCESS; } ScepFree(BufTmp); BufTmp = NULL; } else { Win32Rc = ERROR_NOT_ENOUGH_MEMORY; } if ( Win32Rc != ERROR_SUCCESS ) { break; } } } while ( EnumRc != ERROR_NO_MORE_ITEMS ); RegCloseKey(hKey); // // remember the error code from enumeration // if ( EnumRc != ERROR_SUCCESS && EnumRc != ERROR_NO_MORE_ITEMS ) { if ( Win32Rc == ERROR_SUCCESS ) Win32Rc = EnumRc; } } if ( Win32Rc != NO_ERROR && *pSvcEngineList != NULL ) { // // free memory allocated for the list // SceFreePSCE_SERVICES(*pSvcEngineList); *pSvcEngineList = NULL; } return( ScepDosErrorToSceStatus(Win32Rc) ); }