/*++

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



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;


    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 ) {
            //
            // 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 (!(ConfigOptions & SCE_SETUP_SERVICE_NOSTARTTYPE)) {

                                    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 (ConfigOptions & SCE_SETUP_SERVICE_NOSTARTTYPE) {
                    //
                    // 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 {

                    //
                    // 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) ) {
                                        //
                                        // congigure the service startup
                                        //
                                        if ( !ChangeServiceConfig(
                                                    hService,
                                                    pConfig->dwServiceType,
                                                    pNode->Startup,
                                                    pConfig->dwErrorControl,
                                                    pConfig->lpBinaryPathName,
                                                    pConfig->lpLoadOrderGroup,
                                                    NULL,
                                                    pConfig->lpDependencies,
                                                    pConfig->lpServiceStartName,
                                                    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 if ( !(ConfigOptions & SCE_SETUP_SERVICE_NOSTARTTYPE) &&
                            pNode->Startup == SERVICE_DISABLED ) {
                    //
                    // if the service type is "disabled", we should also stop the service
                    //
                    if ( hService = OpenService(
                                        hScManager,
                                        pNode->ServiceName,
                                        SERVICE_STOP
                                        ))  {

                        SERVICE_STATUS ServiceStatus;

                        if (!ControlService(hService,
                                       SERVICE_CONTROL_STOP,
                                       &ServiceStatus
                                      )) {
                            if ( ERROR_SERVICE_NOT_ACTIVE != GetLastError() ) {
                                ScepLogOutput3(2, GetLastError(), SCEDLL_SCP_ERROR_STOP, pNode->ServiceName);
                            }
                        }

                        CloseServiceHandle (hService);
                        hService = NULL;

                    } else {
                        ScepLogOutput3(2, GetLastError(), SCEDLL_SCP_ERROR_OPENFORSTOP, pNode->ServiceName);
                    }

                }

            } 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;
            }
        }

        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);

}


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);
    }

    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 ( 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 ) {

                swprintf(Value+1, L"%s", 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
                                        );
                        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
                                        );
                        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) );

}