/*++

Copyright (c) 1991-92  Microsoft Corporation

Module Name:

    scconfig.cxx

Abstract:

    This module contains routines for manipulating configuration
    information.

    Configuration information is kept in the registry.
    This file contains the following functions:


        ScGetImageFileName
        ScInitSecurityProcess
        ScCreateLoadOrderGroupList
        ScGenerateServiceDB
        ScOpenServiceConfigKey
        ScReadServiceType
        ScReadStartName
        ScReadFailureActions
        ScWriteDependencies
        ScWriteErrorControl
        ScWriteGroupForThisService
        ScWriteImageFileName
        ScWriteServiceType
        ScWriteStartType
        ScWriteStartName
        ScWriteFailureActions
        ScWriteCurrentServiceValue
        ScReadServiceType
        ScReadStartType
        ScReadErrorControl
        ScReadServiceConfig
        ScAllocateAndReadConfigValue
        ScReadNoInteractiveFlag
        ScReadOptionalString
        ScWriteOptionalString

        ScGetToken
        ScOpenServicesKey
        ScRegCreateKeyExW
        ScRegOpenKeyExW
        ScRegQueryValueExW
        ScRegSetValueExW
        ScRegEnumKeyW

        ScRegDeleteKeyW
        ScRegQueryInfoKeyW
        ScRegGetKeySecurity
        ScRegSetKeySecurity
        ScRegEnumValueW
        ScHandleProviderChange
        ScMarkForDelete
        ScTakeOwnership

Author:

    Dan Lafferty (danl)     01-Apr-1991

Environment:

    User Mode -Win32

Revision History:

    04-Apr-1991     danl
        created
    21-Apr-1992     JohnRo
        Export ScAllocateAndReadConfigValue().  Added ScOpenServiceConfigKey().
        Added ScWriteServiceType() and other ScWrite routines.
        Use SC_LOG0(), etc.  Use FORMAT_ equates.
    24-Apr-1992     JohnRo
        Make ScWriteStartType() write a DWORD, not a string, for consistency.
        Call ScWriteStartType() from ScTransferServiceToRegistry().
        Must call RegSetValueExW (not RegSetValueW) for non-strings.
    29-Apr-1992     JohnRo
        Move registry stuff from System\Services to
        System\Services\CurrentControlSet.
        Undo all group operations (ifdef USE_GROUPS).
        Undo reading from nt.cfg (we've got real registry) (ifdef
        USE_OLDCONFIG).
        They changed winreg APIs so REG_SZ is now UNICODE, so avoid REG_USZ.
    08-Aug-1992     Danl
        Added ScMarkForDelete & ScDeleteFlagIsSet.  ScReadServiceConfig is
        called for each service when generating the service database.  At the
        end of this routine, we check to see if the delete flag is set in
        the registry entry.  If it is, the delete flag is set in the service
        record so it can be deleted later.  After the list of service records
        is complete - and before the dependencies are generated, we call
        ScDeleteMarkedServices which walks through the list and deletes any
        service (in both the registry and linked list) that is marked for
        deletion.
    03-Nov-1992     Danl
        ScReadServiceConfig: If the ScAddCOnfigInfoServiceRecord call fails,
        we just want to skip the database entry - rather than fail the
        ScReadServiceConfig fuction.  Failing ScReadServiceConfig is a fatal
        error for the service controller.
    05-Nov-1992     Danl
        Added ScWriteDisplayName and ScReadDisplayName.  Modified
        ReadServiceConfig to read in the display name.
    29-Mar-1993     Danl
        Added SERVICE_RECOGNIZER_DRIVER as a type that is ignored when reading
        in the Service Database.
    01-Apr-1993 Danl
        Added ScTakeOwnership.  It is called when opening a key that
        complains about access denied.
    30-Apr-1993 Danl
        Put security descriptor in a separate key that only allows read
        access to LocalSystem and Administrators.  Also, we now delete the
        dependencies values from the registry when asked to write an empty
        string of dependencies.
    05-Aug-1993 Danl
        ScRegQueryValueExW: It there is no pointer to a buffer for the data
        to be returned in, then we always want to return
        STATUS_BUFFER_OVERFLOW, even if we successfully read the data into
        the functions internal buffer.
    20-Oct-1993 Danl
        InitSecurityProcess:  Use a global NetLogon service name, and set
        the ScConnectedToSecProc flag when we succeed in connecting to the
        SecurityProcess.
    16-Mar-1994 Danl
        ScRegOpenKeyExW:  Fixed Memory Leak. KeyPath was not being free'd.
        ScRegEnumKeyW:  Fixed Memory Leak. KeyInformation was not being free'd.
    12-Apr-1995 AnirudhS
        Added AccountName field to image record.
    04-Aug-1995 AnirudhS
        Close Lsa Event handle after use.
    05-Feb-1996 AnirudhS
        ScWriteSd: Don't close registry handle twice.  Don't close it at all
        if it's invalid.
    18-Nov-1998 jschwart
        Added ScValidateMultiSZ, since the SCM was assuming all MULTI_SZ
        values were properly double-NUL terminated and AVing when this
        was not the case.

--*/

#include "precomp.hxx"
#include <stdlib.h>     // wide character c runtimes.
#include <string.h>     // ansi character c runtimes.
#include <tstr.h>       // Unicode string macros
#include <sclib.h>      // ScConvertToAnsi
#include <control.h>    // ScWaitForConnect
#include "scconfig.h"   // ScGetToken
#include <valid.h>      // SERVICE_TYPE_INVALID().
#include <strarray.h>   // ScDisplayWStrArray
#include <scseclib.h>   // ScCreateAndSetSD
#include <regrpc.h>     // RPC_SECURITY_DESCRIPTOR
#include "depend.h"     // ScInHardwareProfile

#define ScWinRegErrorToApiStatus( regError ) \
    ( (DWORD) RegError )


//
// Constants
//

#define SECURITY_SERVICES_STARTED   TEXT("SECURITY_SERVICES_STARTED")
#define LSA_RPC_SERVER_ACTIVE       L"LSA_RPC_SERVER_ACTIVE"

#define REG_DELETE_FLAG             L"DeleteFlag"

//
// Registry keys/values
//
#define SERVICES_TREE               L"System\\CurrentControlSet\\Services"
#define CONTROL_TREE                L"System\\CurrentControlSet\\Control"
#define CURRENT_KEY                 L"ServiceCurrent"

#define DEFAULT_SERVICE_TYPE        SERVICE_DRIVER

//
// Used for the Nt Registry API.
//
#define SC_HKEY_LOCAL_MACHINE   L"\\REGISTRY\\MACHINE\\"


//
// Average Number of Bytes in a service record (including name).
//
#define AVE_SR_SIZE         260

//
// Static Global Variables
//

STATIC HKEY ScSGOKey = NULL;
STATIC DWORD Buffer;


//
// Local Function Prototypes
//


DWORD
ScReadServiceConfig(
    IN HKEY ServiceNameKey,
    IN LPWSTR ServiceName
    );

BOOL
ScDeleteFlagIsSet(
    HKEY    ServiceKeyHandle
    );

DWORD
ScTakeOwnership(
    POBJECT_ATTRIBUTES  pObja
    );

DWORD
ScOpenSecurityKey(
    IN HKEY     ServiceNameKey,
    IN DWORD    DesiredAccess,
    IN BOOL     CreateIfMissing,
    OUT PHKEY   pSecurityKey
    );

VOID
ScWaitForLsa(
    );



DWORD
ScGetEnvironment (
    IN  LPWSTR  ServiceName,
    OUT LPVOID  *Environment
    )

/*++


Routine Description:

    Retrieves the environment block for the service. This is stored
    in the registry under the Environment value. The cluster service
    uses this to pass an environment block to services under control
    of the cluster software.

    This routine allocates storage for the environment block and the
    caller is responsible for freeing this with LocalFree.

Arguments:

    ServiceName - This is a pointer to a service name.  This identifies
        the service for which we desire an environment

    Environment - Returns a pointer to a location where the environment
        is to be placed.  This memory should be freed with LocalFree.

Return Value:

    NO_ERROR - The operation was successful.

    ERROR_PATH_NOT_FOUND - The environment could not be found
        or there was a registry error.

--*/
{
    DWORD ApiStatus;
    HKEY ServiceKey;
    DWORD EnvironmentSize;

    SC_ASSERT( ServiceName != NULL );

    //
    // Open the service key.
    //
    ApiStatus = ScOpenServiceConfigKey(
                   ServiceName,
                   KEY_READ,                    // desired access
                   FALSE,                       // don't create if missing.
                   &ServiceKey
                   );

    if (ApiStatus != NO_ERROR) {
        return ERROR_PATH_NOT_FOUND;
    }

    //
    // Read the binary path name
    //
    ApiStatus = ScAllocateAndReadConfigValue(ServiceKey,
                                             ENVIRONMENT_VALUENAME_W,
                                             (LPWSTR *)Environment,
                                             &EnvironmentSize);
    ScRegCloseKey(ServiceKey);
    if (ApiStatus != NO_ERROR) {
        return ERROR_PATH_NOT_FOUND;
    }

    ApiStatus = ScValidateMultiSZ((LPWSTR) *Environment,
                                  EnvironmentSize);

    if (ApiStatus != NO_ERROR) {

        LocalFree(*Environment);
        *Environment = NULL;
    }

    return ApiStatus;
}


DWORD
ScGetImageFileName (
    IN  LPWSTR  ServiceName,
    OUT LPWSTR  *ImageNamePtr
    )

/*++


Routine Description:

    Retreives the Name of the Image File in which the specified service
    can be found.  This routine allocates storage for the name so that
    a pointer to that name can be returned.

Arguments:

    ServiceName - This is a pointer to a service name.  This identifies
        the service for which we desire an image file name.

    ImageNamePtr - Returns a pointer to a location where the Image Name
        pointer is to be placed.  This memory should be freed with
        LocalFree.

Return Value:

    NO_ERROR - The operation was successful.

    ERROR_PATH_NOT_FOUND - The configuration component could not be found
        or there was a registry error.

--*/
{
    DWORD ApiStatus;
    HKEY ServiceKey;

    SC_ASSERT( ServiceName != NULL );

    //
    // Open the service key.
    //
    ApiStatus = ScOpenServiceConfigKey(
                   ServiceName,
                   KEY_READ,                    // desired access
                   FALSE,                       // don't create if missing.
                   &ServiceKey
                   );

    if (ApiStatus != NO_ERROR) {
        return ERROR_PATH_NOT_FOUND;
    }

    //
    // Read the binary path name
    //
    if (ScAllocateAndReadConfigValue(
              ServiceKey,
              IMAGE_VALUENAME_W,
              ImageNamePtr,
              NULL
              ) != NO_ERROR) {
        (void) ScRegCloseKey(ServiceKey);
        return ERROR_PATH_NOT_FOUND;
    }

    (void) ScRegCloseKey(ServiceKey);

    SC_LOG1(CONFIG, "ScGetImageFileName got " FORMAT_LPWSTR " from registry\n",
            *ImageNamePtr);

    return NO_ERROR;
}

#ifndef _CAIRO_

BOOL
ScInitSecurityProcess(
    LPSERVICE_RECORD    ServiceRecord
    )

/*++

Routine Description:

    This function determines the name of the security process, and then
    initializes a control pipe for it.  A global named event is then
    set.  This causes the security process to start its control dispatcher.
    The control dispatcher should then open the other end of the pipe and
    send its process id.  The processId and the name of the image file
    are stored in an image record for the security process.  The service
    instance count is incremented in this image record so that the
    record will never be deleted and the security process is never
    terminated.


    QUESTION:
        What is the proper behavior if this fails?

Arguments:

    ServiceRecord -- The service record of the service being started.
                     Note that as per the check in ScStartService, this
                     service runs in the security process (and is the
                     first service in that process being started)

Return Value:

    TRUE - The initialization was successful.

    FALSE - The initialization failed.  This indicates means that the
        service controller shouldn't continue with its initialization.
        If FALSE is returned, the service's service record has been
        marked (in the START_TYPE field) as disabled.

--*/
{
    DWORD               status;
    HANDLE              pipeHandle;
    LPIMAGE_RECORD      imageRecord;
    HANDLE              eventHandle;
    DWORD               processId;

    //
    // Create an instance of the control pipe.  Use an ID of 0 for lsass.exe
    // since it's possible for it to create its end of the pipe before we
    // ever get to this function.
    //

    status = ScCreateControlInstance (&pipeHandle, 0, LocalSystemSid);

    if (status != NO_ERROR) {

        SC_LOG1(ERROR,
                "ScInitSecurityProcess: ScCreateControlInstance Failure "
                    FORMAT_DWORD "\n",
                status);

        ServiceRecord->StartType = SERVICE_DISABLED;
        return FALSE;
    }

    //
    // Set the event that will cause the Control dispatcher in the
    // Security Process to be started.
    //

    eventHandle = CreateEvent( NULL,    // No special security
                               TRUE,    // Must be manually reset
                               FALSE,   // The event is initially not signalled
                               SECURITY_SERVICES_STARTED );


    if (eventHandle == NULL){
        status = GetLastError();

        //
        // If the event already exists, the security process beat us to
        // creating it.  Just open it.
        //

        if ( status == ERROR_ALREADY_EXISTS ) {

            eventHandle = OpenEvent( GENERIC_WRITE,
                                     FALSE,
                                     SECURITY_SERVICES_STARTED );

        }

        if (eventHandle == NULL ) {

            SC_LOG1(ERROR,"ScInitSecurityProcess: OpenEvent Failed "
                    FORMAT_DWORD "\n", status);

            CloseHandle(pipeHandle);
            ServiceRecord->StartType = SERVICE_DISABLED;
            return FALSE;
        }
    }

    if (!SetEvent(eventHandle)) {

        SC_LOG1(ERROR,"ScInitSecurityProcess: SetEvent Failed " FORMAT_DWORD
                "\n", GetLastError());
        CloseHandle(pipeHandle);
        CloseHandle(eventHandle);
        ServiceRecord->StartType = SERVICE_DISABLED;
        return FALSE;
    }

    //
    // Wait for the Security Process to attach to the pipe and get its PID
    //

    status = ScWaitForConnect(pipeHandle,
                              NULL,
                              ServiceRecord->DisplayName,
                              &processId);

    if (status != NO_ERROR) {

        SC_LOG1(ERROR,"ScInitSecurityProcess:"
                "SecurityProcess did not attach to pipe " FORMAT_DWORD "\n",
                status);
        CloseHandle(pipeHandle);
        CloseHandle(eventHandle);
        ServiceRecord->StartType = SERVICE_DISABLED;
        return FALSE;
    }

    //
    // Don't close the event handle until we know the security process has
    // seen the event.
    //

    CloseHandle(eventHandle);

    //
    //  NOTE:  The image record does not have a valid processHandle.
    //  Therefore, we will never be able to terminate it.  This is desired
    //  behavior though.  We should never terminate the security process.
    //

    status = ScCreateImageRecord (
                &imageRecord,
                ScGlobalSecurityExePath,
                NULL,           // Account name is LocalSystem
                processId,
                pipeHandle,
                NULL,           // The process handle is NULL.
                NULL,           // Token handle is also NULL -- LocalSystem
                NULL,           // No user profile loaded -- LocalSystem
                CANSHARE_FLAG |
                    IS_SYSTEM_SERVICE);

    if (status != NO_ERROR) {

        SC_LOG0(ERROR,"Failed to create ImageRecord for Security Process\n");
        ServiceRecord->StartType = SERVICE_DISABLED;
        return FALSE;
    }

    imageRecord->ServiceCount = 1;

    ScConnectedToSecProc = TRUE;

    return TRUE;
}
#endif // _CAIRO_


BOOL
ScCreateLoadOrderGroupList(
    VOID
    )
/*++

Routine Description:

    This function creates the load order group list from the group
    order information found in HKEY_LOCAL_SYSTEM\Service_Group_Order

Arguments:

    None

Return Value:

    TRUE - The operation was completely successful.

    FALSE - An error occurred.

Note:

    The GroupListLock must be held exclusively prior to calling this routine.

--*/
{
    DWORD status;
    DWORD dwGroupBytes;

    LONG RegError;
    LPWSTR Groups;
    LPWSTR GroupPtr;
    LPWSTR GroupName;

    SC_ASSERT(ScGroupListLock.HaveExclusive());

    //
    // Open the HKEY_LOCAL_MACHINE
    // System\CurrentControlSet\Control\ServiceGroupOrder key.
    //
    RegError = ScRegOpenKeyExW(
                   HKEY_LOCAL_MACHINE,
                   LOAD_ORDER_GROUP_LIST_KEY,
                   REG_OPTION_NON_VOLATILE,   // options
                   KEY_READ,                  // desired access
                   &ScSGOKey
                   );

    if (RegError != ERROR_SUCCESS) {
        SC_LOG1(ERROR,
               "ScCreateLoadOrderGroupList: "
               "ScRegOpenKeyExW of HKEY_LOCAL_MACHINE\\System failed "
               FORMAT_LONG "\n", RegError);
        return FALSE;
    }

    //
    // Read the List value
    //
    if (ScAllocateAndReadConfigValue(
              ScSGOKey,
              GROUPLIST_VALUENAME_W,
              &Groups,
              &dwGroupBytes
              ) != NO_ERROR) {

        ScRegCloseKey(ScSGOKey);
        ScSGOKey = NULL;
        return FALSE;
    }

    if (ScValidateMultiSZ(
              Groups,
              dwGroupBytes
              ) != NO_ERROR) {

        LocalFree(Groups);
        ScRegCloseKey(ScSGOKey);
        ScSGOKey = NULL;
        return FALSE;
    }

    //
    // Leave the ServiceGroupOrder key open for change notify later
    //

    SC_LOG0(DEPEND_DUMP, "ScCreateLoadOrderGroupList: ServiceGroupOrder:\n");
    ScDisplayWStrArray(Groups);

    GroupPtr = Groups;
    while (*GroupPtr != 0) {

        if (ScGetToken(&GroupPtr, &GroupName)) {

            //
            // Add the group to the end of the load order group list
            //
            status = ScCreateOrderGroupEntry(
                         GroupName
                         );

            if (status != NO_ERROR) {
                //
                // Fatal error
                //
                LocalFree(Groups);
                return FALSE;
            }
        }
    }

    LocalFree(Groups);
    return TRUE;
}


BOOL
ScGenerateServiceDB(
    VOID
    )
/*++

Routine Description:

    This function creates the service record list from the information
    which resides in the registry.

Arguments:

    None

Return Value:

    TRUE - The operation was completely successful.

    FALSE - An error occurred.

NOTE:
    This function holds the GroupListLock.

--*/
{
#define MAX_SERVICE_NAME_LENGTH   256

    WCHAR ServiceName[MAX_SERVICE_NAME_LENGTH];
    DWORD Index = 0;

    LONG RegError;
    LONG lTempError;    // Used for debug messages only
    HKEY ServicesKey;
    HKEY ServiceNameKey;

    WCHAR       ClassName[ MAX_PATH ];
    DWORD       ClassNameLength = MAX_PATH;
    DWORD       NumberOfSubKeys;
    DWORD       MaxSubKeyLength;
    DWORD       MaxClassLength;
    DWORD       NumberOfValues;
    DWORD       MaxValueNameLength;
    DWORD       MaxValueDataLength;
    DWORD       SecurityDescriptorLength;
    FILETIME    LastWriteTime;
    DWORD       HeapSize;


    //
    // Since there is only one thread at the time this function is called,
    // these locks are not really needed, but they are included to quell
    // assertions in the routines called herein.
    //
    CGroupListExclusiveLock GLock;
    CServiceListExclusiveLock LLock;
    CServiceRecordExclusiveLock RLock;

    //
    // Read in the group order list from the registry
    //
    if (! ScCreateLoadOrderGroupList()) {
        return FALSE;
    }

    //
    // Read in all the services entries from the registry
    //

    //
    // Open the key to the Services tree.
    //
    RegError = ScRegOpenKeyExW(
                   HKEY_LOCAL_MACHINE,
                   SERVICES_TREE,
                   REG_OPTION_NON_VOLATILE,   // options
                   KEY_READ,                  // desired access
                   &ServicesKey
                   );

    if (RegError != ERROR_SUCCESS) {
        SC_LOG1(ERROR,
                "ScGenerateServiceDB: ScRegOpenKeyExW of Services tree failed "
                FORMAT_LONG "\n", RegError);
        return FALSE;
    }


    //
    // Find out how many service keys there are, and allocate a heap
    // that is twice as large.
    //
    RegError = ScRegQueryInfoKeyW(
                ServicesKey,
                ClassName,
                &ClassNameLength,
                NULL,
                &NumberOfSubKeys,
                &MaxSubKeyLength,
                &MaxClassLength,
                &NumberOfValues,
                &MaxValueNameLength,
                &MaxValueDataLength,
                &SecurityDescriptorLength,
                &LastWriteTime);

    if (RegError != NO_ERROR) {
        SC_LOG1(ERROR,"ScGenerateServiceDatabase: RegQueryInfoKey failed %d\n",
        RegError);
        HeapSize = 0x8000;
    }
    else {
        SC_LOG1(INFO,"ScGenerateServiceDatabase: %d SubKeys\n",NumberOfSubKeys);
        HeapSize = NumberOfSubKeys*2*AVE_SR_SIZE;
    }

    if (!ScAllocateSRHeap(HeapSize)) {
        return(FALSE);
    }

    //
    // Enumerate all the service name keys
    //
    do {

        RegError = ScRegEnumKeyW(
                       ServicesKey,
                       Index,
                       ServiceName,
                       MAX_SERVICE_NAME_LENGTH * sizeof(WCHAR)
                       );

        if (RegError != ERROR_SUCCESS) {

            if (RegError == ERROR_NO_MORE_ITEMS) {
                //
                // No more entries
                //
                SC_LOG1(CONFIG,
                       "ScGenerateServiceDB: ScRegEnumKeyW returns ERROR_NO_MORE_ITEMS"
                       "(no more entries) for index " FORMAT_DWORD "\n",
                       Index);
            }
            else {
                //
                // Error trying to enumerate next service name key
                //
                SC_LOG1(ERROR,
                        "ScGenerateServiceDB: ScRegEnumKeyW of services tree failed "
                        FORMAT_LONG "\n", RegError );
                ScRegCloseKey(ServicesKey);
                return FALSE;
            }
        }
        else {
            //
            // Got the name of a new service key.  Open a handle to it.
            //
            SC_LOG1(CONFIG, "Service name key " FORMAT_LPWSTR "\n",
                    ServiceName);

            lTempError = ScRegOpenKeyExW(
                           ServicesKey,
                           ServiceName,
                           REG_OPTION_NON_VOLATILE,   // options
                           KEY_READ,                  // desired access
                           &ServiceNameKey);

            if (lTempError == ERROR_SUCCESS)
            {
                //
                // Read service config info from the registry and build the
                // service record.
                //
                lTempError = ScReadServiceConfig(
                               ServiceNameKey,
                               ServiceName);

                ScRegCloseKey(ServiceNameKey);

                if (lTempError != NO_ERROR)
                {
                    //
                    // Skip this key
                    //
                    SC_LOG2(ERROR,
                            "ScGenerateServiceDB: ScReadServiceConfig of "
                                   FORMAT_LPWSTR " failed " FORMAT_LONG "\n",
                           ServiceName,
                           lTempError);
                }
            }
            else
            {
                //
                // Skip this key
                //
                SC_LOG2(ERROR,
                        "ScGenerateServiceDB: ScRegOpenKeyExW of "
                               FORMAT_LPWSTR " failed " FORMAT_LONG "\n",
                       ServiceName,
                       lTempError);
            }
        }

        Index++;

    } while (RegError == ERROR_SUCCESS);

    ScRegCloseKey(ServicesKey);

    //
    // Wait for LSA to start since we are about to make our first call to
    // LSA and it typically is not already started yet.
    //
    ScWaitForLsa();

    //
    // Go through entire service record list and remove any services marked
    // for deletion.
    //
    ScDeleteMarkedServices();

    //
    // Go through entire service record list and resolve dependencies chain
    //
    ScGenerateDependencies();

#if DBG
    ScDumpGroups();
    ScDumpServiceDependencies();
#endif // DBG


    return TRUE;
}

VOID
ScWaitForLsa(
    )
/*++

Routine Description:

    This routine either creates or opens the event called LSA_RPC_SERVER_ACTIVE
    event and waits on it indefinitely until LSA signals it.  We need
    to know when LSA is available so that we can call LSA APIs.

Arguments:

    None.

Return Value:

    None.

--*/
{
    DWORD Status;
    HANDLE EventHandle;


    //
    // Create the named event LSA will set.
    //
    EventHandle = CreateEventW(
                      NULL,   // No special security
                      TRUE,   // Must be manually reset
                      FALSE,  // The event is initially not signalled
                      LSA_RPC_SERVER_ACTIVE
                      );

    if ( EventHandle == NULL ) {

        Status = GetLastError();

        //
        // If the event already exists, LSA has already created it.
        // Just open.
        //

        if ( Status == ERROR_ALREADY_EXISTS ) {

            EventHandle = OpenEventW(
                              SYNCHRONIZE,
                              FALSE,
                              LSA_RPC_SERVER_ACTIVE
                              );
        }

        if ( EventHandle == NULL ) {

            SC_LOG1(ERROR, "ScWaitForLsa: OpenEvent of LSA_RPC_SERVER_ACTIVE failed %d\n",
                    GetLastError());

            return;
        }
    }

    //
    // Wait for LSA to come up.
    //
    (VOID) WaitForSingleObject( EventHandle, INFINITE );

    CloseHandle( EventHandle );
}


DWORD
ScOpenServiceConfigKey(
    IN LPWSTR ServiceName,
    IN DWORD DesiredAccess,
    IN BOOL CreateIfMissing,
    OUT PHKEY ServiceKey
    )

/*++

Routine Description:



Arguments:



Return Value:



--*/
{
    HKEY ServicesKey;
    HKEY ServiceNameKey;
    DWORD   ServicesAccess = KEY_READ;

    LONG RegError;

    SC_ASSERT( ServiceName != NULL );
    if (CreateIfMissing) {
        ServicesAccess |= KEY_CREATE_SUB_KEY;
    }

    //
    // Open the key to the Services tree.
    //
    RegError = ScRegOpenKeyExW(
                   HKEY_LOCAL_MACHINE,
                   SERVICES_TREE,
                   REG_OPTION_NON_VOLATILE, // options
                   ServicesAccess,          // desired access (this level)
                   &ServicesKey
                   );

    if (RegError != ERROR_SUCCESS) {
        SC_LOG1(ERROR, "ScOpenServiceConfigKey: "
                "ScRegOpenKeyExW of Services tree failed, reg error "
                FORMAT_LONG "\n", RegError);

        return ((DWORD) RegError);
    }

    if ( !CreateIfMissing ) {
        //
        // Open the existing service key.
        //
        RegError = ScRegOpenKeyExW(
               ServicesKey,
               ServiceName,
               REG_OPTION_NON_VOLATILE,   // options
               DesiredAccess,             // desired access
               & ServiceNameKey );

        if (RegError != ERROR_SUCCESS) {
            SC_LOG2(ERROR, "ScOpenServiceConfigKey: "
                    "ScRegOpenKeyExW of " FORMAT_LPWSTR " failed "
                    FORMAT_LONG "\n", ServiceName, RegError);
            (void) ScRegCloseKey(ServicesKey);
            return ((DWORD) RegError);
        }

    } else {

        DWORD Disposition;

        //
        // Create a new service key (or open existing one).
        //
        RegError = ScRegCreateKeyExW(
                ServicesKey,
                ServiceName,
                0,
                0,
                REG_OPTION_NON_VOLATILE, // options
                DesiredAccess,           // desired access
                NULL,
                &ServiceNameKey,
                &Disposition);

         if (RegError != ERROR_SUCCESS) {
             SC_LOG2(ERROR, "ScOpenServiceConfigKey: "
                     "ScRegCreateKeyExW of " FORMAT_LPWSTR " failed "
                     FORMAT_LONG "\n", ServiceName, RegError);
             ScRegCloseKey(ServicesKey);
             return ((DWORD) RegError);
         }

    }

    (void) ScRegCloseKey(ServicesKey);

    //
    // Give the service key back to caller.
    //
    *ServiceKey = ServiceNameKey;

    return NO_ERROR;

} // ScOpenServiceConfigKey


DWORD
ScWriteCurrentServiceValue(
    OUT LPDWORD  lpdwID
    )

/*++

Routine Description:

    Writes the value to be used in the next service's pipe name to the registry

Arguments:


Return Value:


--*/
{
    LONG                 RegError;
    NTSTATUS             ntstatus;
    SECURITY_ATTRIBUTES  SecurityAttr;
    PSECURITY_DESCRIPTOR SecurityDescriptor;
    DWORD                Disposition;

    //
    // Unique ID for the service to be started.  Start
    // at 1 since ID 0 is reserved for lsass.exe
    //
    static DWORD         s_dwCurrentService = 1;
    static HKEY          s_hCurrentKey      = NULL;


    SC_ASSERT(lpdwID != NULL);

    if (s_hCurrentKey == NULL)
    {
        HKEY  hKey;

        //
        // Open the key to the Services tree.
        //
        RegError = ScRegOpenKeyExW(
                       HKEY_LOCAL_MACHINE,
                       CONTROL_TREE,
                       0,                       // options (ignored)
                       KEY_WRITE,               // KEY_SET_VALUE | KEY_CREATE_SUB_KEY
                       &hKey
                       );

        if (RegError != ERROR_SUCCESS)
        {
            SC_LOG1(ERROR,
                    "ScWriteCurrentServiceValue: ScRegOpenKeyExW of Control tree failed, reg error "
                        FORMAT_LONG "\n",
                    RegError);

            return ((DWORD) RegError);
        }


#define SC_KEY_ACE_COUNT 2

        SC_ACE_DATA AceData[SC_KEY_ACE_COUNT] = {

            {ACCESS_ALLOWED_ACE_TYPE, CONTAINER_INHERIT_ACE, 0,
                   GENERIC_ALL,                &LocalSystemSid},
            {ACCESS_ALLOWED_ACE_TYPE, CONTAINER_INHERIT_ACE, 0,
                   GENERIC_READ,               &WorldSid}

            };


        //
        // Create a security descriptor for the registry key we are about
        // to create.  This gives everyone read access, and all access to
        // ourselves only.
        //
        ntstatus = ScCreateAndSetSD(
                       AceData,
                       SC_KEY_ACE_COUNT,
                       LocalSystemSid,
                       LocalSystemSid,
                       &SecurityDescriptor
                       );

#undef SC_KEY_ACE_COUNT

        if (! NT_SUCCESS(ntstatus)) {
            SC_LOG1(ERROR, "ScCreateAndSetSD failed " FORMAT_NTSTATUS
                    "\n", ntstatus);

            ScRegCloseKey(hKey);
            return(RtlNtStatusToDosError(ntstatus));
        }

        SecurityAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
        SecurityAttr.lpSecurityDescriptor = SecurityDescriptor;
        SecurityAttr.bInheritHandle = FALSE;

        //
        // Create a new  key (or open existing one).
        //
        RegError = ScRegCreateKeyExW(
                        hKey,
                        CURRENT_KEY,
                        0,
                        0,
                        REG_OPTION_VOLATILE, // options
                        KEY_SET_VALUE,       // desired access
                        &SecurityAttr,
                        &s_hCurrentKey,
                        &Disposition);

        RtlDeleteSecurityObject(&SecurityDescriptor);
        ScRegCloseKey(hKey);

        if (RegError != ERROR_SUCCESS)
        {
            SC_LOG1(ERROR,
                    "ScWriteCurrentServiceValue: ScRegCreateKeyExW of "
                        "CURRENT_KEY failed " FORMAT_LONG "\n",
                    RegError);

            return ((DWORD) RegError);
        }
    }

    //
    // Write the value in the key
    //

    RegError = ScRegSetValueExW(
                   s_hCurrentKey,
                   NULL,           // Use key's unnamed value
                   0,
                   REG_DWORD,
                   (LPBYTE) &s_dwCurrentService,
                   sizeof(DWORD));

    if (RegError != ERROR_SUCCESS)
    {
        SC_LOG1(ERROR,
                "ScWriteCurrentServiceValue: ScRegCreateKeyExW of "
                    "CURRENT_KEY failed " FORMAT_LONG "\n",
                RegError);

        return ((DWORD) RegError);
    }

    *lpdwID = s_dwCurrentService;
    s_dwCurrentService++;

    return NO_ERROR;

} // ScWriteCurrentServiceValue


DWORD
ScReadServiceType(
    IN HKEY ServiceNameKey,
    OUT LPDWORD ServiceTypePtr
    )
/*++

Routine Description:



Arguments:



Return Value:



--*/
{
    DWORD BytesRequired = sizeof(DWORD);
    LONG RegError;

    SC_ASSERT( ServiceNameKey != NULL );
    SC_ASSERT( ServiceTypePtr != NULL );

    *ServiceTypePtr = 0;

    RegError = ScRegQueryValueExW(
                   ServiceNameKey,
                   SERVICETYPE_VALUENAME_W,
                   NULL,
                   NULL,
                   (LPBYTE) ServiceTypePtr,
                   &BytesRequired
                   );

    if (RegError != ERROR_SUCCESS) {
        SC_LOG3(TRACE, "ScReadServiceType: ScRegQueryValueExW of " FORMAT_LPWSTR
                " failed "
                FORMAT_LONG ", BytesRequired " FORMAT_DWORD "\n",
                SERVICETYPE_VALUENAME_W, RegError, BytesRequired);
    }

    return (ScWinRegErrorToApiStatus( RegError ) );

} // ScReadServiceType

DWORD
ScReadNoInteractiveFlag(
    IN HKEY ServiceNameKey,
    OUT LPDWORD NoInteractivePtr
    )
/*++

Routine Description:



Arguments:



Return Value:



--*/
{
    DWORD BytesRequired = sizeof(DWORD);
    LONG RegError;

    SC_ASSERT( ServiceNameKey != NULL );
    SC_ASSERT( NoInteractivePtr != NULL );

    *NoInteractivePtr = 0;

    RegError = ScRegQueryValueExW(
                   ServiceNameKey,
                   NOINTERACTIVE_VALUENAME_W,
                   NULL,
                   NULL,
                   (LPBYTE) NoInteractivePtr,
                   &BytesRequired
                   );

    if (RegError != ERROR_SUCCESS) {
        SC_LOG3(ERROR, "ScReadNoInteractiveFlag: ScRegQueryValueExW of " FORMAT_LPWSTR
                " failed "
                FORMAT_LONG ", BytesRequired " FORMAT_DWORD "\n",
                NOINTERACTIVE_VALUENAME_W, RegError, BytesRequired);
    }

    return (ScWinRegErrorToApiStatus( RegError ) );

} // ScReadServiceType


DWORD
ScReadStartType(
    IN HKEY ServiceNameKey,
    OUT LPDWORD StartTypePtr
    )
/*++

Routine Description:



Arguments:



Return Value:



--*/
{
    DWORD BytesRequired = sizeof(DWORD);
    LONG RegError;

    SC_ASSERT( ServiceNameKey != NULL );
    SC_ASSERT( StartTypePtr != NULL );

    *StartTypePtr = 0;

    RegError = ScRegQueryValueExW(
                   ServiceNameKey,
                   START_VALUENAME_W,
                   NULL,
                   NULL,
                   (LPBYTE) StartTypePtr,
                   &BytesRequired
                   );

    if (RegError != ERROR_SUCCESS) {
        SC_LOG3(ERROR, "ScReadStartType: ScRegQueryValueExW of " FORMAT_LPWSTR
                " failed "
                FORMAT_LONG ", BytesRequired " FORMAT_DWORD "\n",
                START_VALUENAME_W, RegError, BytesRequired);
    }

    return (ScWinRegErrorToApiStatus( RegError ));

} // ScReadStartType


DWORD
ScReadTag(
    IN HKEY ServiceNameKey,
    OUT LPDWORD TagPtr
    )
/*++

Routine Description:



Arguments:



Return Value:



--*/
{
    DWORD BytesRequired = sizeof(DWORD);
    LONG RegError;

    SC_ASSERT( ServiceNameKey != NULL );
    SC_ASSERT( TagPtr != NULL );

    *TagPtr = 0;

    RegError = ScRegQueryValueExW(
                   ServiceNameKey,
                   TAG_VALUENAME_W,
                   NULL,
                   NULL,
                   (LPBYTE) TagPtr,
                   &BytesRequired
                   );

    if (RegError != ERROR_SUCCESS) {
        SC_LOG3(CONFIG, "ScReadTag: ScRegQueryValueExW of " FORMAT_LPWSTR
                " failed "
                FORMAT_LONG ", BytesRequired " FORMAT_DWORD "\n",
                START_VALUENAME_W, RegError, BytesRequired);
    }

    return (ScWinRegErrorToApiStatus( RegError ));

} // ScReadTag


DWORD
ScReadErrorControl(
    IN HKEY ServiceNameKey,
    OUT LPDWORD ErrorControlPtr
    )
/*++

Routine Description:



Arguments:



Return Value:



--*/
{
    DWORD BytesRequired = sizeof(DWORD);
    LONG RegError;

    SC_ASSERT( ServiceNameKey != NULL );
    SC_ASSERT( ErrorControlPtr != NULL );

    *ErrorControlPtr = 0;

    RegError = ScRegQueryValueExW(
                   ServiceNameKey,
                   ERRORCONTROL_VALUENAME_W,
                   NULL,
                   NULL,
                   (LPBYTE) ErrorControlPtr,
                   &BytesRequired
                   );

    if (RegError != ERROR_SUCCESS) {
        SC_LOG3(ERROR, "ScReadErrorControl: ScRegQueryValueExW of " FORMAT_LPWSTR
                " failed "
                FORMAT_LONG ", BytesRequired " FORMAT_DWORD "\n",
                ERRORCONTROL_VALUENAME_W, RegError, BytesRequired);
    }

    return (ScWinRegErrorToApiStatus( RegError ));


} // ScReadErrorControl



DWORD
ScReadFailureActions(
    IN HKEY ServiceNameKey,
    OUT LPSERVICE_FAILURE_ACTIONS_WOW64 * FailActPtr,
    IN OUT LPDWORD TotalBytes OPTIONAL
    )
/*++

Routine Description:

    This function attempts to read the value for the non-string portion
    of the service's failure actions configuration from the registry.
    If the value does not exist, or is invalid, this function sets the
    pointer to the value to NULL and returns NO_ERROR.  If any other error
    occurs, the error is returned.

    NOTE:  On return from this function, a buffer with the value will be
        allocated, or the pointer will be NULL.  If a buffer is allocated,
        it contains both the fixed-size structure and the array of actions.

Arguments:

    ServiceNameKey - This is the Service's Key handle.

    FailActPtr - This is a pointer to a location where the pointer to
        the failure actions information is to be placed.

    TotalBytes - If present, this DWORD is INCREMENTED by the number of bytes
        needed to store the string.

Return Value:


--*/
{
    DWORD BytesReturned;
    LONG RegError = ScAllocateAndReadConfigValue(
                ServiceNameKey,
                FAILUREACTIONS_VALUENAME_W,
                (LPWSTR *) FailActPtr,
                &BytesReturned
                );

    if (RegError != ERROR_SUCCESS)
    {
        if (RegError == ERROR_FILE_NOT_FOUND)
        {
            RegError = NO_ERROR;
        }

        *FailActPtr = NULL;

        return RegError;
    }

    //
    // Validate the value read.  Treat a bogus value as no value.
    //
    if ((BytesReturned < sizeof(SERVICE_FAILURE_ACTIONS_WOW64)) ||
        (BytesReturned != sizeof(SERVICE_FAILURE_ACTIONS_WOW64) +
                          (*FailActPtr)->cActions * sizeof(SC_ACTION)))
    {
        LocalFree(*FailActPtr);
        *FailActPtr = NULL;
        return NO_ERROR;
    }

    //
    // Fix up the pointer to the array.
    //
    (*FailActPtr)->dwsaActionsOffset = sizeof(SERVICE_FAILURE_ACTIONS_WOW64);

    //
    // Increment the total number of bytes used.
    //
    if (ARGUMENT_PRESENT(TotalBytes))
    {
        *TotalBytes += BytesReturned;
    }

    return NO_ERROR;

} // ScReadFailureActions


DWORD
ScReadOptionalString(
    IN  HKEY    ServiceNameKey,
    IN  LPCWSTR ValueName,
    OUT LPWSTR  *Value,
    IN OUT LPDWORD TotalBytes OPTIONAL
    )
/*++

Routine Description:

    This function attempts to read the value for the optional string
    configuration parameter from the registry.  If this read fails because
    the value does no exist, then this function sets the pointer to the
    value string to NULL, and returns NO_ERROR.  If any other error occurs,
    the error is returned.

    NOTE:  On successful return from this function, a buffer with the
        string value will be allocated, or the pointer will be NULL.
        If a string is returned, it is guaranteed to be non-empty and
        null-terminated (if the registry value was not null-terminated,
        its last character will be overwritten).

Arguments:

    ServiceNameKey - This is the Service's Key handle.

    ValueName - Name of the registry value from which to read.

    Value - This is a pointer to a location where the pointer to the
        string is to be placed.

    TotalBytes - If present, this DWORD is INCREMENTED by the number of bytes
        needed to store the string.

Return Value:



--*/
{
    DWORD BytesReturned;
    LONG RegError = ScAllocateAndReadConfigValue(
                ServiceNameKey,
                ValueName,
                Value,
                &BytesReturned
                );

    if (RegError != ERROR_SUCCESS)
    {
        // Nothing read from the registry.
        if (RegError == ERROR_FILE_NOT_FOUND)
        {
            RegError = NO_ERROR;
        }

        *Value = NULL;
        BytesReturned = 0;
    }
    else
    {
        // We read something from the registry.  Make sure it's
        // null-terminated.
        if (BytesReturned < sizeof(L" "))
        {
            LocalFree(*Value);
            *Value = NULL;
            BytesReturned = 0;
        }
        else
        {
            (*Value)[BytesReturned/sizeof(WCHAR) - 1] = L'\0';
        }
    }

    //
    // Increment the total number of bytes used.
    //
    if (ARGUMENT_PRESENT(TotalBytes))
    {
        *TotalBytes += (BytesReturned/sizeof(WCHAR)) * sizeof(WCHAR);
    }

    return RegError;

} // ScReadOptionalString


DWORD
ScReadStartName(
    IN HKEY ServiceNameKey,
    OUT LPWSTR *AccountName
    )
/*++

Routine Description:



Arguments:



Return Value:



--*/
{
    return ScAllocateAndReadConfigValue(
               ServiceNameKey,
               STARTNAME_VALUENAME_W,
               AccountName,
               NULL
               );

} // ScReadStartName


DWORD
ScReadSd(
    IN HKEY ServiceNameKey,
    OUT PSECURITY_DESCRIPTOR *Sd
    )
/*++

Routine Description:

    This function reads the security descriptor for the service

Arguments:



Return Value:



--*/
{
    LONG    RegError;
    HKEY    SecurityKey;
    DWORD   status;


    //
    // Open the Security Sub-key (under the services key).
    // NOTE:  This key may not exist, and that is ok.
    //
    RegError = ScOpenSecurityKey(
                ServiceNameKey,
                KEY_READ,
                FALSE,              // Do not create if missing.
                &SecurityKey);

    if (RegError != NO_ERROR) {
        SC_LOG1(TRACE,"ScReadSd:ScOpenSecurityKey Failed %d\n",RegError);
        return(ScWinRegErrorToApiStatus(RegError));
    }

    //
    // Read the Security Descriptor value stored under the security key.
    //
    status = ScAllocateAndReadConfigValue(
                 SecurityKey,
                 SD_VALUENAME_W,
                 (LPWSTR *) Sd,
                 NULL);

    if (status == NO_ERROR)
    {
        if (RtlValidSecurityDescriptor(*Sd))
        {
            status = NO_ERROR;
        }
        else
        {
            LocalFree(*Sd);
            *Sd = NULL;
            status = ERROR_FILE_NOT_FOUND;
        }
    }

    RegCloseKey(SecurityKey);
    return status;

} // ScReadSd



DWORD
ScWriteDependencies(
    IN HKEY ServiceNameKey,
    IN LPWSTR Dependencies,
    IN DWORD DependSize
    )
/*++

Routine Description:



Arguments:



Return Value:



--*/
{
    LONG RegError;
    LPWSTR DependOnService;
    LPWSTR DependOnGroup;
    LPWSTR DestService;
    LPWSTR DestGroup;
    DWORD DependencyLength;


    SC_ASSERT( ServiceNameKey != NULL );
    SC_ASSERT( Dependencies != NULL );

    //
    // If the dependencies string is empty, then delete the dependency
    // values from the registry and return.  If errors occur during the
    // delete, we ignore them.  It could be that there aren't any existing
    // dependencies, so that the depend values don't exist to begin with.
    // Also, it the delete fails, we can't do anything about it anyway.
    //
    if (*Dependencies == L'\0') {

        RegError = ScRegDeleteValue(ServiceNameKey,DEPENDONSERVICE_VALUENAME_W);
        if ((RegError != ERROR_SUCCESS) && (RegError != ERROR_FILE_NOT_FOUND)) {
            SC_LOG1(ERROR, "Failed to delete DependOnService Value "
                "" FORMAT_LONG "\n",RegError);
        }
        RegError = ScRegDeleteValue(ServiceNameKey,DEPENDONGROUP_VALUENAME_W);
        if ((RegError != ERROR_SUCCESS) && (RegError != ERROR_FILE_NOT_FOUND)) {
            SC_LOG1(ERROR, "Failed to delete DependOnGroup Value "
                "" FORMAT_LONG "\n",RegError);
        }
        return(NO_ERROR);
    }

    //
    // Allocate a buffer which is twice the size of DependSize so that
    // we can split the Dependencies array string into a DependOnService,
    // and a DependOnGroup array strings.
    //
    if ((DependOnService = (LPWSTR)LocalAlloc(
                               LMEM_ZEROINIT,
                               (UINT) (2 * DependSize)
                               )) == NULL) {
        SC_LOG1(ERROR, "ScWriteDependencies: LocalAlloc failed " FORMAT_DWORD "\n",
                GetLastError());
        return ERROR_NOT_ENOUGH_MEMORY;
    }

    DependOnGroup = (LPWSTR) ((DWORD_PTR) DependOnService + DependSize);

    DestService = DependOnService;
    DestGroup = DependOnGroup;

    while ((*Dependencies) != 0) {

        if (*Dependencies == SC_GROUP_IDENTIFIERW) {

            Dependencies++;
            DependencyLength = (DWORD) wcslen(Dependencies) + 1;

            wcscpy(DestGroup, Dependencies);
            DestGroup += DependencyLength;
        }
        else {

            DependencyLength = (DWORD) wcslen(Dependencies) + 1;

            wcscpy(DestService, Dependencies);
            DestService += DependencyLength;
        }

        Dependencies += DependencyLength;
    }

    //
    // Write the DependOnService array string
    //
    RegError = ScRegSetValueExW(
                   ServiceNameKey,                  // open handle (to section)
                   DEPENDONSERVICE_VALUENAME_W,
                   0,
                   REG_MULTI_SZ,                    // type (NULL-NULL UNICODE string)
                   (LPBYTE) DependOnService,        // data
                   ScWStrArraySize(DependOnService) // byte count for data
                   );

    if (RegError != ERROR_SUCCESS) {
#if DBG
        SC_LOG1(ERROR, "ScWriteDependOnService: ScRegSetValueExW returned "
                FORMAT_LONG "\n", RegError);
        ScDisplayWStrArray(DependOnService);
#endif
        goto CleanExit;
    }

    //
    // Write the DependOnGroup array string
    //
    RegError = ScRegSetValueExW(
                   ServiceNameKey,                  // open handle (to section)
                   DEPENDONGROUP_VALUENAME_W,
                   0,
                   REG_MULTI_SZ,                    // type (NULL-NULL UNICODE string)
                   (LPBYTE) DependOnGroup,          // data
                   ScWStrArraySize(DependOnGroup)   // byte count for data
                   );

    if (RegError != ERROR_SUCCESS) {
#if DBG
        SC_LOG1(ERROR, "ScWriteDependOnGroup: ScRegSetValueExW returned "
                FORMAT_LONG "\n", RegError);
        ScDisplayWStrArray(DependOnGroup);
#endif
        goto CleanExit;
    }

CleanExit:
    LocalFree(DependOnService);
    if (RegError != NO_ERROR) {
        SC_LOG2(ERROR, "ScWriteDependencies (%ws) Error %d \n",
        Dependencies,RegError);
    }

    return (ScWinRegErrorToApiStatus( RegError ));

} // ScWriteDependencies


DWORD
ScWriteOptionalString(
    IN HKEY ServiceNameKey,
    IN LPCWSTR ValueName,
    IN LPCWSTR Value
    )
/*++

Routine Description:

    This function writes the specified string value to the registry for the
    particular key.  If the value is a NULL pointer, we don't do anything.  If
    the value is an empty string, we delete the registry value.

Arguments:



Return Value:



--*/
{
    LONG RegError;

    SC_ASSERT( ServiceNameKey != NULL );
    SC_ASSERT( ValueName != NULL && ValueName[0] != L'\0' );

    //
    // A NULL value means no change.
    //
    if (Value == NULL)
    {
        return NO_ERROR;
    }

    if (Value[0] != L'\0')
    {
        //
        // Write the Value
        //
        RegError = ScRegSetValueExW(
                       ServiceNameKey,           // open key handle
                       ValueName,                // value name
                       0,
                       REG_SZ,                   // type (zero-terminated UNICODE)
                       (LPBYTE) Value,           // data
                       (DWORD) WCSSIZE(Value));  // byte count for data

        if (RegError != ERROR_SUCCESS)
        {
            SC_LOG3(ERROR, "ScWriteStringParm: ScRegSetValueExW of \"%ws\" "
                    "to reg value %ws failed %ld\n",
                    Value, ValueName, RegError);
        }

        return RegError;
    }
    else
    {
        //
        // The value is specifically being cleared.  So we
        // want to delete the registry value.
        //
        RegError = ScRegDeleteValue(ServiceNameKey, ValueName);
        if (RegError != ERROR_SUCCESS)
        {
            if (RegError == ERROR_FILE_NOT_FOUND)
            {
                RegError = ERROR_SUCCESS;
            }
            else
            {
                SC_LOG2(ERROR, "ScWriteStringParm: ScRegDeleteValue of "
                        "reg value %ws failed %ld\n", ValueName, RegError);
            }
        }

        return RegError;
    }

} // ScWriteOptionalString



DWORD
ScWriteFailureActions(
    IN HKEY ServiceNameKey,
    IN LPSERVICE_FAILURE_ACTIONSW psfa
    )
/*++

Routine Description:

    This function writes ONLY the non-string fields of the
    SERVICE_FAILURE_ACTIONS structure to the registry for the specified
    key.  If the structure is a NULL pointer, we don't do anything.  If
    the structure contains no failure actions, we delete the registry value.

Arguments:


Return Value:


--*/
{
    SC_ASSERT( ServiceNameKey != NULL );

    //
    // A NULL structure or NULL array means no change.
    //
    if (psfa == NULL || psfa->lpsaActions == NULL)
    {
        return NO_ERROR;
    }

    if (psfa->cActions != 0)
    {
        //
        // Write the Value
        //

        //
        // Combine the SERVICE_FAILURE_ACTIONSW structure and the
        // array of SC_ACTION into a contiguous block.
        // The structure includes the string pointers, though we don't
        // actually use them when reading the structure back.
        //
        // Always write this structure out with 32-bit "pointers" since
        // that's the format we expect when we read it in (required for
        // backwards compatibility).
        //

        DWORD cbValueLen = sizeof(SERVICE_FAILURE_ACTIONS_WOW64) +
                           psfa->cActions * sizeof(SC_ACTION);

        LPSERVICE_FAILURE_ACTIONS_WOW64 psfaValue =
            (LPSERVICE_FAILURE_ACTIONS_WOW64) LocalAlloc(0, cbValueLen);

        if (psfaValue == NULL)
        {
            return (GetLastError());
        }

        psfaValue->dwResetPeriod     = psfa->dwResetPeriod;
        psfaValue->dwRebootMsgOffset = psfa->lpRebootMsg ? 1 : 0;
        psfaValue->dwCommandOffset   = psfa->lpCommand ? 1 : 0;
        psfaValue->cActions          = psfa->cActions;

        RtlCopyMemory(psfaValue + 1,
                      psfa->lpsaActions,
                      psfa->cActions * sizeof(SC_ACTION));

        //
        // Write the block to the registry
        //
        LONG RegError = ScRegSetValueExW(
                            ServiceNameKey,
                            FAILUREACTIONS_VALUENAME_W,
                            0,
                            REG_BINARY,
                            psfaValue,
                            cbValueLen
                            );

        if (RegError != ERROR_SUCCESS)
        {
            SC_LOG(ERROR, "ScWriteFailureActions: ScRegSetValueExW failed %ld\n",
                   RegError);
        }

        LocalFree(psfaValue);

        return RegError;
    }
    else
    {
        //
        // There are no failure actions to store.  So we
        // want to delete the registry value.
        //
        LONG RegError = ScRegDeleteValue(
                            ServiceNameKey,
                            FAILUREACTIONS_VALUENAME_W
                            );
        if (RegError != ERROR_SUCCESS)
        {
            if (RegError == ERROR_FILE_NOT_FOUND)
            {
                RegError = ERROR_SUCCESS;
            }
            else
            {
                SC_LOG(ERROR, "ScWriteFailureActions: ScRegDeleteValue failed %ld\n",
                        RegError);
            }
        }

        return RegError;
    }

} // ScWriteFailureActions


DWORD
ScWriteErrorControl(
    IN HKEY ServiceNameKey,
    IN DWORD ErrorControl
    )
/*++

Routine Description:



Arguments:



Return Value:



--*/
{
    LONG RegError;

    SC_ASSERT( ServiceNameKey != NULL );
    SC_ASSERT( !ERROR_CONTROL_INVALID( ErrorControl ) );

    RegError = ScRegSetValueExW(
            ServiceNameKey,                     // key
            ERRORCONTROL_VALUENAME_W,           // value name
            0,
            REG_DWORD,                          // data type
            (LPBYTE) & ErrorControl,            // data
            sizeof(DWORD) );                    // byte count

    SC_ASSERT( RegError == ERROR_SUCCESS );

    return (ScWinRegErrorToApiStatus( RegError ) );

} // ScWriteErrorControl


DWORD
ScWriteSd(
    IN HKEY ServiceNameKey,
    IN PSECURITY_DESCRIPTOR Security
    )
/*++

Routine Description:

    This routine write the specified security descriptor to the registry.

Arguments:



Return Value:



--*/
{
    LONG    RegError;
    HKEY    SecurityKey;
    ULONG   SdLength;

    SC_ASSERT( ServiceNameKey != NULL );

    if (Security == NULL) {
        return NO_ERROR;
    }
    SdLength = RtlLengthSecurityDescriptor(Security);
    if (SdLength == 0) {
        return(NO_ERROR);
    }

    SC_LOG1(SECURITY, "ScWriteSd: Size of security descriptor %lu\n", SdLength);

    //
    // Open the Security Sub-key (under the service key).
    //
    RegError = ScOpenSecurityKey(
                ServiceNameKey,
                KEY_READ | KEY_WRITE,
                TRUE,                   // CreateIfMissing
                &SecurityKey);

    if (RegError != NO_ERROR) {
        SC_LOG1(ERROR,"ScWriteSd:ScOpenSecurityKey Failed %d\n",RegError);
    }
    else
    {
        //
        // Write the Security Descriptor to the Security Value in the Security
        // Key.
        //
        RegError = ScRegSetValueExW(
                SecurityKey,                        // key
                SD_VALUENAME_W,                     // value name
                0,                                  // reserved
                REG_BINARY,                         // data type
                (LPBYTE) Security,                  // data
                SdLength                            // byte count
                );

        if (RegError != NO_ERROR) {
            SC_LOG1(ERROR,"ScWriteSd:ScRegSetValueExW Failed %d\n",RegError);
        }

        RegCloseKey(SecurityKey);
    }

    return (ScWinRegErrorToApiStatus( RegError ) );

} // ScWriteSd


#ifdef USE_GROUPS
DWORD
ScWriteGroupForThisService(
    IN HKEY ServiceNameKey,
    IN LPWSTR Group
    )
/*++

Routine Description:



Arguments:



Return Value:



--*/
{
    LONG RegError;

    SC_ASSERT( ServiceNameKey != NULL );
    SC_ASSERT( Group != NULL );

    //
    // Write the group
    //
    RegError = ScRegSetValueExW(
                   ServiceNameKey,           // open handle (to section)
                   GROUP_VALUENAME_W,        // value name
                   0,
                   REG_SZ,                   // type (zero-terminated UNICODE)
                   (LPBYTE) Group,           // data
                   (DWORD) WCSSIZE(Group));  // byte count for data

    if (RegError != ERROR_SUCCESS) {
        SC_LOG2(ERROR, "ScWriteGroupForThisService: ScRegSetValueExW of "
                FORMAT_LPWSTR " failed " FORMAT_LONG "\n",
                Group, RegError);
    }

    return (ScWinRegErrorToApiStatus( RegError ) );

} // ScWriteGroupForThisService
#endif // USE_GROUPS


DWORD
ScWriteImageFileName(
    IN HKEY hServiceKey,
    IN LPWSTR ImageFileName
    )
/*++

Routine Description:



Arguments:



Return Value:



--*/
{
    LONG RegError;

    SC_ASSERT( hServiceKey != NULL );
    SC_ASSERT( ImageFileName != NULL );

    //
    // Write the binary path name
    //
    RegError = ScRegSetValueExW(
            hServiceKey,                      // open handle (to section)
            IMAGE_VALUENAME_W,                // value name
            0,
            REG_EXPAND_SZ,                    // type (zero-terminated UNICODE)
            (LPBYTE) ImageFileName,           // data
            (DWORD) WCSSIZE(ImageFileName));  // byte count for data

    if (RegError != ERROR_SUCCESS) {
        SC_LOG2(ERROR, "ScWriteImageFileName: ScRegSetValueExW of "
                FORMAT_LPWSTR " failed " FORMAT_LONG "\n",
                ImageFileName, RegError);
    }

    SC_ASSERT( RegError == ERROR_SUCCESS );

    return ( (DWORD) RegError );

} // ScWriteImageFileName


DWORD
ScWriteServiceType(
    IN HKEY hServiceKey,
    IN DWORD dwServiceType
    )
/*++

Routine Description:



Arguments:



Return Value:



--*/
{
    LONG RegError;

    SC_ASSERT( hServiceKey != NULL );
    SC_ASSERT( !SERVICE_TYPE_INVALID( dwServiceType ) );
    SC_ASSERT( dwServiceType != SERVICE_WIN32 );  // Don't write ambig info.

    RegError = ScRegSetValueExW(
            hServiceKey,                        // key
            SERVICETYPE_VALUENAME_W,            // value name
            0,
            REG_DWORD,                          // data type
            (LPBYTE) & dwServiceType,           // data
            sizeof(DWORD) );                    // byte count

    SC_ASSERT( RegError == ERROR_SUCCESS );

    return (ScWinRegErrorToApiStatus( RegError ) );

} // ScWriteServiceType


DWORD
ScWriteStartType(
    IN HKEY hServiceKey,
    IN DWORD dwStartType
    )
/*++

Routine Description:



Arguments:



Return Value:



--*/
{
    LONG RegError;

    SC_ASSERT( hServiceKey != NULL );
    SC_ASSERT( !START_TYPE_INVALID( dwStartType ) );

    RegError = ScRegSetValueExW(
            hServiceKey,                        // key
            START_VALUENAME_W,                  // value name
            0,
            REG_DWORD,                          // data type
            (LPBYTE) &dwStartType,              // data
            sizeof( DWORD ) );                  // byte count

    SC_ASSERT( RegError == ERROR_SUCCESS );

    return (ScWinRegErrorToApiStatus( RegError ) );

} // ScWriteStartType


DWORD
ScWriteTag(
    IN HKEY hServiceKey,
    IN DWORD dwTag
    )
/*++

Routine Description:



Arguments:



Return Value:



--*/
{
    LONG RegError;

    SC_ASSERT( hServiceKey != NULL );

    RegError = ScRegSetValueExW(
            hServiceKey,                        // key
            TAG_VALUENAME_W,                    // value name
            0,
            REG_DWORD,                          // data type
            (LPBYTE) &dwTag,                    // data
            sizeof( DWORD ) );                  // byte count

    SC_ASSERT( RegError == ERROR_SUCCESS );

    return (ScWinRegErrorToApiStatus( RegError ) );

} // ScWriteTag


VOID
ScDeleteTag(
    IN HKEY hServiceKey
    )
/*++

Routine Description:



Arguments:



Return Value:



--*/
{
    LONG RegError;

    SC_ASSERT( hServiceKey != NULL );

    RegError = ScRegDeleteValue(
            hServiceKey,                        // key
            TAG_VALUENAME_W);                   // value name

    SC_LOG1(DEPEND, "ScRegDeleteValue of Tag returns %ld\n", RegError);

} // ScDeleteTag


DWORD
ScWriteStartName(
    IN HKEY ServiceNameKey,
    IN LPWSTR StartName
    )
/*++

Routine Description:



Arguments:



Return Value:



--*/
{
    LONG RegError;

    SC_ASSERT( ServiceNameKey != NULL );
    SC_ASSERT( StartName != NULL );

    //
    // Write the StartName
    //
    RegError = ScRegSetValueExW(
                   ServiceNameKey,               // open handle (to section)
                   STARTNAME_VALUENAME_W,        // value name
                   0,
                   REG_SZ,                       // type (zero-terminated UNICODE)
                   (LPBYTE) StartName,           // data
                   (DWORD) WCSSIZE(StartName));  // byte count for data

    if (RegError != ERROR_SUCCESS) {
        SC_LOG2(ERROR, "ScWriteStartName: ScRegSetValueExW of " FORMAT_LPWSTR
                " failed " FORMAT_LONG "\n",
                StartName, RegError);
    }

    SC_ASSERT( RegError == ERROR_SUCCESS );

    return (ScWinRegErrorToApiStatus( RegError ) );

} // ScWriteStartName



DWORD
ScReadServiceConfig(
    IN HKEY ServiceNameKey,
    IN LPWSTR ServiceName
    )
/*++

Routine Description:

    This function reads the service configuration information and
    creates a service record in memory with the information.

Arguments:

    ServiceNameKey - Supplies opened handle to the service key to read
        from.

    ServiceName - Supplies name of the service.

Return Value:

    TRUE - Service record is created successfully.

    FALSE - Error in creating the service record.  If an error occurs here,
        it is generally considered a fatal error which will cause the
        service controller to fail to start.

Note:

    The GroupListLock must be held exclusively prior to calling this routine.

--*/
{
    DWORD status;

    DWORD StartType;
    DWORD ServiceType;
    DWORD ErrorControl;
    DWORD Tag;
    LPWSTR Group = NULL;
    LPWSTR Dependencies = NULL;
    LPWSTR DisplayName=NULL;
    PSECURITY_DESCRIPTOR Sd = NULL;

    LPSERVICE_RECORD ServiceRecord;

    SC_ASSERT(ScGroupListLock.HaveExclusive());

    //
    // Get the Service Type information from the registry
    //
    status = ScReadServiceType(ServiceNameKey, &ServiceType);
    if (status != NO_ERROR) {
        SC_LOG1(TRACE, "Ignored " FORMAT_LPWSTR ".  No ServiceType\n",
                ServiceName);
        return NO_ERROR;  // Skip service entry and ignore error.
    }

    //
    // If service type is not one of type SERVICE_WIN32 or SERVICE_DRIVER,
    // do not bother saving it in a service record because it's data
    // for services.
    //
    if (SERVICE_TYPE_INVALID(ServiceType)) {
        if ((ServiceType != SERVICE_ADAPTER) &&
            (ServiceType != SERVICE_RECOGNIZER_DRIVER)) {
            SC_LOG2(ERROR, "Ignored " FORMAT_LPWSTR ".  Invalid ServiceType "
                    FORMAT_HEX_DWORD "\n", ServiceName, ServiceType);
        }
        return NO_ERROR;
    }
    SC_LOG1(CONFIG, "    ServiceType " FORMAT_HEX_DWORD "\n", ServiceType);


    //
    // Read the StartType value
    //
    status = ScReadStartType(ServiceNameKey, &StartType);
    if (status != NO_ERROR) {
        SC_LOG1(ERROR, "Ignored " FORMAT_LPWSTR ".  No StartType\n",
                ServiceName);
        return NO_ERROR;  // Skip service entry and ignore error.
    }
    SC_LOG1(CONFIG, "    StartType " FORMAT_HEX_DWORD "\n", StartType);

    //
    // Read the ErrorControl value
    //
    status = ScReadErrorControl(ServiceNameKey, &ErrorControl);
    if (status != NO_ERROR) {
        SC_LOG1(ERROR, "Ignored " FORMAT_LPWSTR ".  No ErrorControl\n",
                ServiceName);
        return NO_ERROR;  // Skip service entry and ignore error.
    }
    SC_LOG1(CONFIG, "    ErrorControl " FORMAT_HEX_DWORD "\n", ErrorControl);


    //
    // Read the optional Tag value.  0 means no tag.
    //
    status = ScReadTag(ServiceNameKey, &Tag);
    if (status != NO_ERROR) {
        Tag = 0;
    }

    //
    // Read the Group value
    //
    if (ScAllocateAndReadConfigValue(
            ServiceNameKey,
            GROUP_VALUENAME_W,
            &Group,
            NULL
            ) != NO_ERROR) {

        Group = NULL;
    }
    else {
        SC_LOG1(CONFIG, "    Belongs to group " FORMAT_LPWSTR "\n", Group);
    }

    //
    // Read the Dependencies
    //

    status = ScReadDependencies(ServiceNameKey, &Dependencies, ServiceName);
    if (status != NO_ERROR) {
        Dependencies = NULL;
    }


    //
    // Read the security descriptor
    //
    if (ScReadSd(
            ServiceNameKey,
            &Sd
            ) != NO_ERROR) {

        Sd = NULL;
    }

    //
    // Read the Display Name
    // NOTE: If an error occurs, or the name doesn't exist, then a NULL
    // pointer is returned from this call.
    //
    ScReadDisplayName(ServiceNameKey, &DisplayName);

    //
    // Get an exclusive lock on the database so we can read and
    // make modifications.
    //
    SC_ASSERT(ScServiceListLock.HaveExclusive());
    SC_ASSERT(ScServiceRecordLock.HaveExclusive());

    //
    // See if the service record already exists
    //
    status = ScGetNamedServiceRecord(
                 ServiceName,
                 &ServiceRecord
                 );

    if (status == ERROR_SERVICE_DOES_NOT_EXIST) {

        //
        // Create a service record for this service
        //
        status = ScCreateServiceRecord(
                    ServiceName,
                    &ServiceRecord
                    );
    }

    if (status != NO_ERROR) {
        goto CleanExit;
    }

    //
    // Insert the config information into the service record
    //
    status = ScAddConfigInfoServiceRecord(
                ServiceRecord,
                ServiceType,
                StartType,
                ErrorControl,
                Group,
                Tag,
                Dependencies,
                DisplayName,
                Sd
                );

    if (status != NO_ERROR) {
        //
        // Fail to set meaningful data into service record.  Remove the service
        // record from the service record list and delete it.  This is not
        // a fatal error.  Instead, we just leave this entry out of the
        // database.
        //
        REMOVE_FROM_LIST(ServiceRecord);

        ScFreeServiceRecord(ServiceRecord);

        status = NO_ERROR;
    }
    else {

        //
        // Should the service be deleted?
        // The service entry in the registry cannot be deleted while we
        // are enumerating services, therefore we must mark it and delete it
        // later.
        //
        if (ScDeleteFlagIsSet(ServiceNameKey)) {
            SC_LOG(TRACE,"ScReadServiceConfig: %ws service marked for delete\n",
                ServiceRecord->ServiceName);
            SET_DELETE_FLAG(ServiceRecord);
        }
    }
CleanExit:

    LocalFree(Group);
    LocalFree(Dependencies);
    LocalFree(DisplayName);

    return status;
}


DWORD
ScAllocateAndReadConfigValue(
    IN  HKEY    Key,
    IN  LPCWSTR  ValueName,
    OUT LPWSTR  *Value,
    OUT LPDWORD BytesReturned OPTIONAL
    )
/*++

Routine Description:

    This function allocates the output buffer and reads the requested
    value from the registry into it.  It is useful for reading string
    data of undeterministic length.


Arguments:

    Key - Supplies opened handle to the key to read from.

    ValueName - Supplies name of the value to retrieve data.

    Value - Returns a pointer to the output buffer which points to
        the memory allocated and contains the data read in from the
        registry.

Return Value:

    ERROR_NOT_ENOUGH_MEMORY - Failed to create buffer to read value into.

    Error from registry call.

--*/
{
    LONG    RegError;
    DWORD   NumRequired = 0;
    WCHAR   Temp[1];
    LPWSTR  TempValue = NULL;
    DWORD   ValueType;
    DWORD   CharsReturned;


    //
    // Set returned buffer pointer to NULL.
    //
    *Value = NULL;

    RegError = ScRegQueryValueExW(
                   Key,
                   ValueName,
                   NULL,
                   &ValueType,
                   (LPBYTE) NULL,
                   &NumRequired
                   );

    if (RegError != ERROR_SUCCESS && NumRequired > 0) {

        SC_LOG3(CONFIG, "ScAllocateAndReadConfig: ScRegQueryKeyExW of "
                FORMAT_LPWSTR " failed " FORMAT_LONG ", NumRequired "
                FORMAT_DWORD "\n",
                ValueName, RegError, NumRequired);

        if ((TempValue = (LPWSTR)LocalAlloc(
                          LMEM_ZEROINIT,
                          (UINT) NumRequired
                          )) == NULL) {
            SC_LOG2(ERROR, "ScAllocateAndReadConfig: LocalAlloc of size "
                    FORMAT_DWORD " failed " FORMAT_DWORD "\n",
                    NumRequired, GetLastError());
            return ERROR_NOT_ENOUGH_MEMORY;
        }

        RegError = ScRegQueryValueExW(
                       Key,
                       ValueName,
                       NULL,
                       &ValueType,
                       (LPBYTE) TempValue,
                       &NumRequired
                       );
    }

    if (RegError != ERROR_SUCCESS) {

        if (RegError != ERROR_FILE_NOT_FOUND) {
            SC_LOG3(ERROR, "ScAllocateAndReadConfig: ScRegQueryKeyExW of "
                    FORMAT_LPWSTR " failed " FORMAT_LONG ", NumRequired "
                    FORMAT_DWORD "\n",
                    ValueName, RegError, NumRequired);
        }

        LocalFree(TempValue);

        return (DWORD) RegError;
    }

    if (ValueType != REG_EXPAND_SZ || TempValue == NULL) {
        *Value = TempValue;
        if (BytesReturned != NULL) {
            *BytesReturned = NumRequired;
        }
        return(NO_ERROR);
    }

    //
    // If the ValueType is REG_EXPAND_SZ, then we must call the
    // function to expand environment variables.
    //
    SC_LOG1(CONFIG,"ScAllocateAndReadConfig: Must expand the string for "
        FORMAT_LPWSTR "\n", ValueName);

    //
    // Make the first call just to get the number of characters that
    // will be returned.
    //
    NumRequired = ExpandEnvironmentStringsW (TempValue,Temp, 1);

    if (NumRequired > 1) {

        *Value = (LPWSTR)LocalAlloc(LMEM_ZEROINIT, (UINT) (NumRequired * sizeof(WCHAR)));

        if (*Value == NULL) {

            SC_LOG2(ERROR, "ScAllocateAndReadConfig: LocalAlloc of numChar= "
                FORMAT_DWORD " failed " FORMAT_DWORD "\n",
                NumRequired, GetLastError());

            LocalFree(TempValue);
            return(ERROR_NOT_ENOUGH_MEMORY);
        }

        CharsReturned = ExpandEnvironmentStringsW (
                            TempValue,
                            *Value,
                            NumRequired);

        if (CharsReturned > NumRequired) {
            SC_LOG1(ERROR, "ScAllocAndReadConfig: ExpandEnvironmentStrings "
                " failed for " FORMAT_LPWSTR " \n", ValueName);

            LocalFree(*Value);
            *Value = NULL;
            LocalFree(TempValue);
            return(ERROR_NOT_ENOUGH_MEMORY);
        }

        LocalFree(TempValue);

        if (BytesReturned != NULL) {
            *BytesReturned = CharsReturned * sizeof(WCHAR);
        }
        return(NO_ERROR);

    }
    else {
        //
        // This call should have failed because of our ridiculously small
        // buffer size.
        //

        SC_LOG0(ERROR, "ScAllocAndReadConfig: ExpandEnvironmentStrings "
            " Should have failed because we gave it a BufferSize=1\n");

        //
        // This could happen if the string was a single byte long and
        // didn't really have any environment values to expand.  In this
        // case, we return the TempValue buffer pointer.
        //
        *Value = TempValue;

        if (BytesReturned != NULL) {
            *BytesReturned = sizeof(WCHAR);
        }
        return(NO_ERROR);
    }
}



DWORD
ScGetGroupVector(
    IN  LPWSTR Group,
    OUT LPBYTE *Buffer,
    OUT LPDWORD BufferSize
    )

{
    DWORD status;
    LONG RegError;
    HKEY VectorsKey;


    //
    // Open the HKEY_LOCAL_MACHINE
    // System\CurrentControlSet\Control\GroupOrderList key.
    //
    RegError = ScRegOpenKeyExW(
                   HKEY_LOCAL_MACHINE,
                   GROUP_VECTORS_KEY,
                   REG_OPTION_NON_VOLATILE,   // options
                   KEY_READ,                  // desired access
                   &VectorsKey
                   );

    if (RegError != ERROR_SUCCESS) {
        SC_LOG(ERROR, "ScGetGroupVector: Open of GroupOrderList key failed "
               FORMAT_LONG "\n", RegError);

        return (DWORD) RegError;
    }

    //
    // Read the value with the valuename of the specified group
    //
    status = ScAllocateAndReadConfigValue(
                 VectorsKey,
                 Group,
                 (LPWSTR *)Buffer,
                 BufferSize
                 );

    (void) ScRegCloseKey(VectorsKey);

    return status;
}


BOOL
ScGetToken(
    IN OUT LPWSTR *CurrentPtr,
    OUT    LPWSTR *TokenPtr
    )
/*++

Routine Description:

    This function takes a pointer into a given NULL-NULL-terminated buffer
    and isolates the next string token in it.  The CurrentPtr is incremented
    past the NULL byte of the token found if it is not the end of the buffer.
    The TokenPtr returned points to the token in the buffer and is NULL-
    terminated.

Arguments:

    CurrentPtr - Supplies a pointer to the buffer to extract the next token.
        On output, this pointer is set past the token found.

    TokenPtr - Supplies the pointer to the token found.

Return Value:

    TRUE - If a token is found.

    FALSE - No token is found.

--*/
{

    if (*(*CurrentPtr) == 0) {
        return FALSE;
    }

    *TokenPtr = *CurrentPtr;

    *CurrentPtr = ScNextWStrArrayEntry((*CurrentPtr));

    return TRUE;

}

DWORD
ScOpenServicesKey(
    OUT PHKEY ServicesKey
    )
{
    LONG RegError;

    RegError = ScRegOpenKeyExW(
                   HKEY_LOCAL_MACHINE,
                   SERVICES_TREE,
                   REG_OPTION_NON_VOLATILE,   // options
                   KEY_READ | DELETE,         // desired access
                   ServicesKey
                   );

    return (ScWinRegErrorToApiStatus( RegError ));
}

DWORD
ScRegCreateKeyExW(
    IN  HKEY                    hKey,
    IN  LPWSTR                  lpSubKey,
    IN  DWORD                   dwReserved,
    IN  LPWSTR                  lpClass,
    IN  DWORD                   dwOptions,
    IN  REGSAM                  samDesired,
    IN  LPSECURITY_ATTRIBUTES   lpSecurityAttributes,
    OUT PHKEY                   phKeyResult,
    OUT LPDWORD                 lpdwDisposition
    )

/*++

Routine Description:

    NOTE:  This routine only creates one key at a time.  If the lpSubKey
        parameter includes keys that don't exist, an error will result.
        For instance, if "\\new\\key\\here" is passed in, "new" and "key"
        are expected to exist.  They will not be created by this call.

Arguments:


Return Value:


Note:


--*/
{
    NTSTATUS            ntStatus;
    OBJECT_ATTRIBUTES   Obja;
    UNICODE_STRING      KeyName;
    UNICODE_STRING      ClassString;

    UNREFERENCED_PARAMETER(dwReserved);

    RtlInitUnicodeString(&KeyName,lpSubKey);
    RtlInitUnicodeString(&ClassString,lpClass);

    InitializeObjectAttributes(
        &Obja,
        &KeyName,
        OBJ_CASE_INSENSITIVE,
        hKey,
        ARGUMENT_PRESENT(lpSecurityAttributes) ?
            lpSecurityAttributes->lpSecurityDescriptor :
            NULL);


    ntStatus = NtCreateKey(
                (PHANDLE)phKeyResult,
                (ACCESS_MASK)samDesired,
                &Obja,
                0,
                &ClassString,
                (ULONG)dwOptions,
                (PULONG)lpdwDisposition);


    return(RtlNtStatusToDosError(ntStatus));
}


DWORD
ScRegOpenKeyExW(
    IN  HKEY    hKey,
    IN  LPWSTR  lpSubKey,
    IN  DWORD   dwOptions,
    IN  REGSAM  samDesired,
    OUT PHKEY   phKeyResult
    )
/*++

Routine Description:

    NOTE:  This function will only accept one of the WinReg Pre-defined
        handles - HKEY_LOCAL_MACHINE.  Passing any other type of Pre-defined
        handle will cause an error.

Arguments:


Return Value:


Note:


--*/
{
    NTSTATUS            ntStatus;
    DWORD               status;
    OBJECT_ATTRIBUTES   Obja;
    UNICODE_STRING      KeyNameString;
    LPWSTR              KeyPath;
    DWORD               stringSize;
    LPWSTR              HKeyLocalMachine = SC_HKEY_LOCAL_MACHINE;
    HKEY                tempHKey;
    BOOL                KeyPathIsAllocated=FALSE;


    UNREFERENCED_PARAMETER(dwOptions);

    //
    // If we are opening the Pre-Defined Key (HKEY_LOCAL_MACHINE), then
    // pre-pend "\\REGISTRY\\MACHINE\\" to the subKey string.
    //
    if (hKey == HKEY_LOCAL_MACHINE) {
        stringSize = (DWORD) WCSSIZE(HKeyLocalMachine) + (DWORD) WCSSIZE(lpSubKey);
        KeyPath = (LPWSTR)LocalAlloc(LMEM_ZEROINIT, (UINT) stringSize);
        if (KeyPath == NULL) {
            SC_LOG0(ERROR,"ScRegOpenKeyExW: Local Alloc Failed\n");
            return(GetLastError());
        }
        KeyPathIsAllocated=TRUE;
        wcscpy(KeyPath,HKeyLocalMachine);
        wcscat(KeyPath,lpSubKey);
        tempHKey = NULL;
    }
    else {
        KeyPath = lpSubKey;
        tempHKey = hKey;
    }

    RtlInitUnicodeString(&KeyNameString,KeyPath);

    InitializeObjectAttributes(
        &Obja,
        &KeyNameString,
        OBJ_CASE_INSENSITIVE,
        tempHKey,
        NULL);

    ntStatus = NtOpenKey(
                (PHANDLE)phKeyResult,
                (ACCESS_MASK)samDesired,
                &Obja);

    if (ntStatus == STATUS_ACCESS_DENIED) {

        SC_LOG0(ERROR,"ScOpenKeyExW: NtOpenKey ACCESS_DENIED try to Take Ownership\n");

        status = ScTakeOwnership(&Obja);
        if (status != NO_ERROR) {
            if (KeyPathIsAllocated) {
                LocalFree(KeyPath);
            }
            return(status);
        }

        //
        // Now try to open the key with the desired access.
        //
        ntStatus = NtOpenKey(
                    (PHANDLE)phKeyResult,
                    (ACCESS_MASK)samDesired,
                    &Obja);
        if (!NT_SUCCESS(ntStatus)) {
            SC_LOG(ERROR, "ScRegOpenKeyExW: NtOpenKey(final try) failed %x\n",
            ntStatus);
        }
    }

    if (KeyPathIsAllocated) {
        LocalFree(KeyPath);
    }
    return(RtlNtStatusToDosError(ntStatus));
}

DWORD
ScRegQueryValueExW(
    IN      HKEY    hKey,
    IN      LPCWSTR lpValueName,
    OUT     LPDWORD lpReserved,
    OUT     LPDWORD lpType,
    OUT     LPBYTE  lpData,
    IN OUT  LPDWORD lpcbData
    )
/*++

Routine Description:


Arguments:


Return Value:


Note:


--*/

{

    NTSTATUS                    ntStatus;
    UNICODE_STRING              ValueName;
    PKEY_VALUE_PARTIAL_INFORMATION KeyValueInfo;
    DWORD                       bufSize;

    UNREFERENCED_PARAMETER(lpReserved);

    //
    // Make sure we have a buffer size if the buffer is present.
    //

    if ((ARGUMENT_PRESENT(lpData)) && (!ARGUMENT_PRESENT(lpcbData))) {
        return(ERROR_INVALID_PARAMETER);
    }

    RtlInitUnicodeString(&ValueName, lpValueName);

    //
    // Compute size of value information and allocate buffer.
    //

    bufSize = 0;
    if (ARGUMENT_PRESENT(lpcbData)) {
        bufSize = *lpcbData;
    }

    bufSize += FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
    KeyValueInfo =
            (PKEY_VALUE_PARTIAL_INFORMATION)LocalAlloc(LMEM_ZEROINIT, bufSize);

    if (KeyValueInfo == NULL) {
        SC_LOG0(ERROR,"ScRegQueryValueExW: LocalAlloc Failed");
        return ERROR_NOT_ENOUGH_MEMORY;
    }

    ntStatus = NtQueryValueKey(
                hKey,
                &ValueName,
                KeyValuePartialInformation,
                KeyValueInfo,
                bufSize,
                &bufSize);

    if (NT_SUCCESS(ntStatus) || (ntStatus == STATUS_BUFFER_OVERFLOW)) {
        if (ARGUMENT_PRESENT(lpcbData)) {
            *lpcbData = KeyValueInfo->DataLength;
        }

        if (ARGUMENT_PRESENT(lpType)) {
            *lpType = KeyValueInfo->Type;
        }
    }

    if (NT_SUCCESS(ntStatus) && ARGUMENT_PRESENT(lpData)) {
        RtlCopyMemory(lpData, &KeyValueInfo->Data[0], KeyValueInfo->DataLength);
    }

    LocalFree(KeyValueInfo);
    return RtlNtStatusToDosError(ntStatus);
}


DWORD
ScRegSetValueExW(
    IN  HKEY    hKey,
    IN  LPCWSTR lpValueName,
    IN  DWORD   lpReserved,
    IN  DWORD   dwType,
    IN  LPVOID  lpData,
    IN  DWORD   cbData
    )


/*++

Routine Description:


Arguments:


Return Value:


Note:


--*/
{
    DWORD                       status;
    NTSTATUS                    ntStatus;
    UNICODE_STRING              ValueName;


    UNREFERENCED_PARAMETER(lpReserved);

    RtlInitUnicodeString(&ValueName,lpValueName);

    ntStatus = NtSetValueKey(
                hKey,
                &ValueName,
                0,
                (ULONG)dwType,
                (PVOID)lpData,
                (ULONG)cbData);

    status = RtlNtStatusToDosError(ntStatus);

    if (status != NO_ERROR)
    {
        ScLogEvent(
            NEVENT_CALL_TO_FUNCTION_FAILED_II,
            L"ScRegSetValueExW",
            lpValueName,
            status
            );
    }

    return(status);

}

DWORD
ScRegDeleteValue(
    IN  HKEY    hKey,
    IN  LPCWSTR lpValueName
    )


/*++

Routine Description:


Arguments:


Return Value:


Note:


--*/
{
    NTSTATUS                    ntStatus;
    UNICODE_STRING              ValueName;


    RtlInitUnicodeString(&ValueName,lpValueName);

    ntStatus = NtDeleteValueKey(
                hKey,
                &ValueName);

    return(RtlNtStatusToDosError(ntStatus));

}


DWORD
ScRegEnumKeyW(
    HKEY    hKey,
    DWORD   dwIndex,
    LPWSTR  lpName,
    DWORD   cbName
    )

/*++

Routine Description:



Arguments:



Return Value:



--*/
{
    NTSTATUS                    ntStatus;
    PKEY_BASIC_INFORMATION      KeyInformation;
    ULONG                       resultLength;
    DWORD                       bufSize;

    //
    // Allocate a buffer for the Key Information.
    //
    bufSize = sizeof(KEY_BASIC_INFORMATION) + cbName;
    KeyInformation = (PKEY_BASIC_INFORMATION)LocalAlloc(LMEM_ZEROINIT, (UINT) bufSize);
    if (KeyInformation == NULL){
        SC_LOG0(ERROR,"ScRegEnumKey: LocalAlloc Failed\n");
        return(ERROR_NOT_ENOUGH_MEMORY);
    }

    ntStatus = NtEnumerateKey(
                (HANDLE)hKey,
                (ULONG)dwIndex,
                KeyBasicInformation,
                (PVOID)KeyInformation,
                (ULONG)bufSize,
                (PULONG)&resultLength);

    if (!NT_SUCCESS(ntStatus)) {
        LocalFree(KeyInformation);
        return(RtlNtStatusToDosError(ntStatus));
    }

    if (cbName < (KeyInformation->NameLength + sizeof(WCHAR))) {
        LocalFree(KeyInformation);
        return(ERROR_MORE_DATA);
    }

    RtlCopyMemory(lpName, KeyInformation->Name, KeyInformation->NameLength);
    *(lpName + (KeyInformation->NameLength/sizeof(WCHAR))) = L'\0';

    LocalFree(KeyInformation);
    return(NO_ERROR);
}


DWORD
ScRegDeleteKeyW (
    HKEY    hKey,
    LPWSTR  lpSubKey
    )
/*++

Routine Description:



Arguments:



Return Value:



--*/
{
    DWORD       status;
    NTSTATUS    ntStatus;
    HKEY        keyToDelete;

    status = ScRegOpenKeyExW(
                hKey,
                lpSubKey,
                0,
                KEY_READ | READ_CONTROL | DELETE,
                &keyToDelete);

    if (status != NO_ERROR) {
        SC_LOG2(ERROR, "ScRegDeleteKeyW: ScRegOpenKeyExW (%ws) Failed %d\n",
            lpSubKey,
            status);
        return(status);
    }

    ntStatus = NtDeleteKey(keyToDelete);

    NtClose(keyToDelete);

    return(RtlNtStatusToDosError(ntStatus));
}

DWORD
ScRegQueryInfoKeyW (
    HKEY hKey,
    LPWSTR lpClass,
    LPDWORD lpcbClass,
    LPDWORD lpReserved,
    LPDWORD lpcSubKeys,
    LPDWORD lpcbMaxSubKeyLen,
    LPDWORD lpcbMaxClassLen,
    LPDWORD lpcValues,
    LPDWORD lpcbMaxValueNameLen,
    LPDWORD lpcbMaxValueLen,
    LPDWORD lpcbSecurityDescriptor,
    PFILETIME lpftLastWriteTime
    )

/*++

Routine Description:



Arguments:



Return Value:



--*/
{
    DWORD                   status;
    NTSTATUS                ntStatus;
    NTSTATUS                ntStatus2;
    PSECURITY_DESCRIPTOR    SecurityDescriptor=NULL;
    ULONG                   SecurityDescriptorLength;
    PKEY_FULL_INFORMATION   KeyInfo;
    DWORD                   bufSize;
    DWORD                   bytesReturned;
    DWORD                   classBufSize;


    UNREFERENCED_PARAMETER(lpReserved);

    classBufSize = *lpcbClass;
    bufSize = sizeof(KEY_FULL_INFORMATION) + *lpcbClass;

    KeyInfo = (PKEY_FULL_INFORMATION)LocalAlloc(LMEM_ZEROINIT, bufSize);
    if (KeyInfo == NULL) {
        SC_LOG0(ERROR,"RegQueryInfoKeyW: LocalAlloc failed\n");
        return(ERROR_NOT_ENOUGH_MEMORY);
    }

    ntStatus = NtQueryKey(
                hKey,
                KeyFullInformation,
                (PVOID)KeyInfo,
                bufSize,
                &bytesReturned);

    status = RtlNtStatusToDosError(ntStatus);

    if (ntStatus == STATUS_SUCCESS) {
        ntStatus2 = NtQuerySecurityObject(
                        hKey,
                        OWNER_SECURITY_INFORMATION
                        | GROUP_SECURITY_INFORMATION
                        | DACL_SECURITY_INFORMATION,
                        SecurityDescriptor,
                        0,
                        lpcbSecurityDescriptor
                        );
        //
        // If getting the size of the SECURITY_DESCRIPTOR failed (probably
        // due to the lack of READ_CONTROL access) return zero.
        //

        if( ntStatus2 != STATUS_BUFFER_TOO_SMALL ) {

            *lpcbSecurityDescriptor = 0;

        } else {

            //
            // Try again to get the size of the key's SECURITY_DESCRIPTOR,
            // this time asking for SACL as well. This should normally
            // fail but may succeed if the caller has SACL access.
            //

            ntStatus2 = NtQuerySecurityObject(
                            hKey,
                            OWNER_SECURITY_INFORMATION
                            | GROUP_SECURITY_INFORMATION
                            | DACL_SECURITY_INFORMATION
                            | SACL_SECURITY_INFORMATION,
                            SecurityDescriptor,
                            0,
                            &SecurityDescriptorLength
                            );


            if( ntStatus2 == STATUS_BUFFER_TOO_SMALL ) {

                //
                // The caller had SACL access so update the returned
                // length.
                //

                *lpcbSecurityDescriptor = SecurityDescriptorLength;
            }

        }

        *lpcbClass              = KeyInfo->ClassLength;
        *lpcSubKeys             = KeyInfo->SubKeys;
        *lpcbMaxSubKeyLen       = KeyInfo->MaxNameLen;
        *lpcbMaxClassLen        = KeyInfo->MaxClassLen;
        *lpcValues              = KeyInfo->Values;
        *lpcbMaxValueNameLen    = KeyInfo->MaxValueNameLen;
        *lpcbMaxValueLen        = KeyInfo->MaxValueDataLen;
        *lpftLastWriteTime      = *(PFILETIME) &KeyInfo->LastWriteTime;

        if (KeyInfo->ClassLength > classBufSize) {
            LocalFree(KeyInfo);
            return(RtlNtStatusToDosError(STATUS_BUFFER_TOO_SMALL));
        }
        RtlCopyMemory(
            lpClass,
            (LPBYTE)KeyInfo->Class,
            KeyInfo->ClassLength);

        //
        // NUL terminate the class name.
        //
        *(lpClass + (KeyInfo->ClassLength/sizeof(WCHAR))) = UNICODE_NULL;

    }
    else
    {
        //
        // NtQueryKey failed
        //

        ScLogEvent(
            NEVENT_CALL_TO_FUNCTION_FAILED,
            L"ScRegQueryInfoKeyW",
            status
            );
    }

    LocalFree(KeyInfo);

    return(status);
}

DWORD
ScRegGetKeySecurity (
    HKEY hKey,
    SECURITY_INFORMATION SecurityInformation,
    PSECURITY_DESCRIPTOR pSecurityDescriptor,
    LPDWORD lpcbSecurityDescriptor
    )

/*++

Routine Description:



Arguments:



Return Value:



--*/
{
    RPC_SECURITY_DESCRIPTOR     RpcSD;
    DWORD                       status;

    //
    // Convert the supplied SECURITY_DESCRIPTOR to a RPCable version.
    //
    RpcSD.lpSecurityDescriptor    = (PBYTE) pSecurityDescriptor;
    RpcSD.cbInSecurityDescriptor  = *lpcbSecurityDescriptor;
    RpcSD.cbOutSecurityDescriptor = 0;

    status = (DWORD)BaseRegGetKeySecurity(
                            hKey,
                            SecurityInformation,
                            &RpcSD
                            );
    //
    // Extract the size of the SECURITY_DESCRIPTOR from the RPCable version.
    //
    *lpcbSecurityDescriptor = RpcSD.cbInSecurityDescriptor;

    return(status);
}


DWORD
ScRegSetKeySecurity (
    HKEY hKey,
    SECURITY_INFORMATION SecurityInformation,
    PSECURITY_DESCRIPTOR pSecurityDescriptor
    )

/*++

Routine Description:



Arguments:



Return Value:



--*/
{
    RPC_SECURITY_DESCRIPTOR     RpcSD;
    DWORD                       status;

    //
    // Convert the supplied SECURITY_DESCRIPTOR to a RPCable version.
    //
    RpcSD.lpSecurityDescriptor = NULL;

    status = MapSDToRpcSD(
        pSecurityDescriptor,
        &RpcSD
        );

    if( status != ERROR_SUCCESS )
    {
        SC_LOG1(ERROR,"ScRegSetKeySecurity: MapSDToRpcSD failed %lu\n",
                status);

        ScLogEvent(
            NEVENT_CALL_TO_FUNCTION_FAILED,
            L"MapSDToRpcSD",
            status
            );

        return (status);
    }

    status = (DWORD)BaseRegSetKeySecurity (
                        hKey,
                        SecurityInformation,
                        &RpcSD
                        );

    //
    // Free the buffer allocated by MapSDToRpcSD.
    //

    RtlFreeHeap(
        RtlProcessHeap( ), 0,
        RpcSD.lpSecurityDescriptor
        );

    return (status);
}

DWORD
ScRegEnumValueW (
    HKEY    hKey,
    DWORD   dwIndex,
    LPWSTR  lpValueName,
    LPDWORD lpcbValueName,
    LPDWORD lpReserved,
    LPDWORD lpType,
    LPBYTE  lpData,
    LPDWORD lpcbData
    )

/*++

Routine Description:



Arguments:



Return Value:



--*/
{

    NTSTATUS                    ntStatus;
    PKEY_VALUE_FULL_INFORMATION KeyValueInfo;
    DWORD                       bufSize;
    DWORD                       resultSize;
    DWORD                       totalSize;    // size of string including NUL
    BOOL                        stringData = FALSE;

    UNREFERENCED_PARAMETER(lpReserved);
    //
    // Make sure we have a buffer size if the buffer is present.
    //
    if ((ARGUMENT_PRESENT(lpData)) && (!ARGUMENT_PRESENT(lpcbData))) {
        return(ERROR_INVALID_PARAMETER);
    }

    //
    // Compute size of KeyValueInfo, round to pointer size, and allocate
    // buffer.
    //

    bufSize = FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name) + (MAX_PATH * sizeof(WCHAR));
    bufSize = (bufSize + sizeof(PVOID) - 1) & ~(sizeof(PVOID) - 1);
    bufSize += *lpcbData;

    KeyValueInfo = (PKEY_VALUE_FULL_INFORMATION)LocalAlloc(
                    LMEM_ZEROINIT,
                    (UINT) bufSize);
    if (KeyValueInfo == NULL) {
        SC_LOG0(ERROR,"ScRegEnumValueW: LocalAlloc Failed\n");
        return(ERROR_NOT_ENOUGH_MEMORY);
    }

    ntStatus = NtEnumerateValueKey(
                (HANDLE)hKey,
                (ULONG)dwIndex,
                KeyValueFullInformation,
                (PVOID)KeyValueInfo,
                (ULONG)bufSize,
                (PULONG)&resultSize);

    if (ntStatus == STATUS_BUFFER_OVERFLOW) {

        LocalFree(KeyValueInfo);

        KeyValueInfo = (PKEY_VALUE_FULL_INFORMATION)LocalAlloc(
                        LMEM_ZEROINIT,
                        (UINT) resultSize);
        if (KeyValueInfo == NULL) {
            SC_LOG0(ERROR,"ScRegEnumValueW: LocalAlloc (2nd try) Failed\n");
            return(ERROR_NOT_ENOUGH_MEMORY);
        }

        ntStatus = NtEnumerateValueKey(
                    hKey,
                    (ULONG)dwIndex,
                    KeyValueFullInformation,
                    (PVOID)KeyValueInfo,
                    (ULONG)bufSize,
                    (PULONG)&resultSize);

        if (ntStatus != STATUS_SUCCESS) {
            LocalFree(KeyValueInfo);
            return(RtlNtStatusToDosError(ntStatus));
        }
    }
    else if (ntStatus != STATUS_SUCCESS) {
        LocalFree(KeyValueInfo);
        return(RtlNtStatusToDosError(ntStatus));
    }

    //
    // The API was successful (from our point of view.  Now see if the
    // callers buffers were large enough.
    //
    totalSize = KeyValueInfo->NameLength+sizeof(WCHAR);  // add 1 for the NUL terminator.

    if (*lpcbValueName < totalSize) {
        *lpcbValueName = totalSize;
        *lpcbData = KeyValueInfo->DataLength;
        LocalFree(KeyValueInfo);
        return(ERROR_INSUFFICIENT_BUFFER);
    }
    else {
        RtlCopyMemory(
            lpValueName,
            (LPBYTE)KeyValueInfo->Name,
            KeyValueInfo->NameLength);

        *lpcbValueName = totalSize;

        //
        // NUL terminate the Value name.
        //
        *(lpValueName + (KeyValueInfo->NameLength/sizeof(WCHAR))) = UNICODE_NULL;

    }

    if (ARGUMENT_PRESENT(lpData)) {

        totalSize = KeyValueInfo->DataLength;

#ifdef REMOVE
        //
        // I believe I can remove this because data strings will be
        // stored with NULL terminators.
        //

        if((KeyValueInfo->Type == REG_SZ)        ||
           (KeyValueInfo->Type == REG_EXPAND_SZ) ||
           (KeyValueInfo->Type == REG_MULTI_SZ))  {

            totalSize += sizeof(WCHAR);
            stringData = TRUE;
        }

#endif // REMOVE

        if (*lpcbData < totalSize) {
            *lpcbData = totalSize;
            LocalFree(KeyValueInfo);
            return(ERROR_INSUFFICIENT_BUFFER);
        }
        else {
            RtlCopyMemory(
                lpData,
                (LPBYTE)KeyValueInfo + KeyValueInfo->DataOffset,
                KeyValueInfo->DataLength);

            *lpcbData = KeyValueInfo->DataLength;
            if (stringData) {
                *lpcbData += sizeof(WCHAR);
                //
                // NUL terminate the string Data.
                //
                *((LPWSTR)lpData + (KeyValueInfo->DataLength/sizeof(WCHAR))) = UNICODE_NULL;
            }
        }
    }

    if (ARGUMENT_PRESENT(lpType)) {
        *lpType = KeyValueInfo->Type;
    }

    LocalFree(KeyValueInfo);
    return(NO_ERROR);

}


VOID
ScHandleProviderChange(
    PVOID   pContext,
    BOOLEAN fWaitStatus
    )

/*++

Routine Description:

    Processes changes to the list of network providers in the registry
    and publishes a list of those that are currently active in the HW
    profile for mpr.dll to use.

Arguments:


Return Value:


--*/
{
    DWORD   dwStatus;
    LPWSTR  lpProviderList = NULL;

    DWORD   dwLength;
    DWORD   dwTempLength;
    UINT    i;
    DWORD   dwCurrentChar;
    DWORD   dwNameStart;

    BOOL    fWriteList = TRUE;
    LPWSTR  lpList = NULL;

    HKEY                 hProviderHwKey;
    HKEY                 hProviderKey;
    DWORD                dwDisposition;
    SECURITY_ATTRIBUTES  SecurityAttr;
    PSECURITY_DESCRIPTOR SecurityDescriptor;

    static  HANDLE  s_hWorkItem;

#define SC_KEY_ACE_COUNT 2

    SC_ACE_DATA AceData[SC_KEY_ACE_COUNT] = {

        {ACCESS_ALLOWED_ACE_TYPE, CONTAINER_INHERIT_ACE, 0,
               GENERIC_ALL,                &LocalSystemSid},
        {ACCESS_ALLOWED_ACE_TYPE, CONTAINER_INHERIT_ACE, 0,
               GENERIC_READ,               &WorldSid}

        };


    SC_ASSERT(fWaitStatus == FALSE);
    SC_ASSERT(g_hProviderKey != NULL);

    if (ScShutdownInProgress)
    {
        return;
    }

    if (s_hWorkItem != NULL)
    {
        dwStatus = RtlDeregisterWait(s_hWorkItem);

        if (!NT_SUCCESS(dwStatus))
        {
            SC_LOG(ERROR,
                   "ScHandleProviderChange: RtlDeregisterWait FAILED %#x\n",
                   dwStatus);
        }
    }

    //
    // Reset the event
    //
    ResetEvent((HANDLE)pContext);

    SC_LOG0(TRACE, "ScHandleProviderChange: ProviderOrder key changed\n");

    //
    // Reregister for registry change notifications in case the key
    // changes while we're in this routine.  Note that there's no
    // race condition since the work item is a one-shot -- only one
    // thread can be in this routine at a time.
    //
    dwStatus = RegNotifyChangeKeyValue(
                   g_hProviderKey,
                   FALSE,                      // Don't watch subkeys
                   REG_NOTIFY_CHANGE_LAST_SET, // Watch for value changes
                   (HANDLE)pContext,           // Event to signal
                   TRUE);                      // Asynchronous

    if (dwStatus != NO_ERROR)
    {
        //
        // We won't pick up any further changes to the provider list.
        // Keep going so we at least pick up this one.
        //
        SC_LOG(ERROR,
               "ScHandleProviderChange: RegNotifyChangeKeyValue FAILED %d\n",
               dwStatus);
    }

    dwStatus = ScAllocateAndReadConfigValue(g_hProviderKey,
                                            PROVIDER_VALUE,
                                            &lpProviderList,
                                            &dwLength);

    if (dwStatus != NO_ERROR)
    {
        SC_LOG(ERROR,
               "ScHandleProviderChange: Unable to read ProviderOrder %d\n",
               dwStatus);

        goto Reregister;
    }

    //
    // This should be a REG_SZ -- check the basics
    //
    if ((dwLength % 2 != 0)
          ||
        (dwLength < sizeof(UNICODE_NULL))
          ||
        (lpProviderList[dwLength / sizeof(WCHAR) - 1] != UNICODE_NULL))
    {
        SC_LOG0(ERROR,
                "ScHandleProviderChange: Invalid REG_SZ for ProviderOrder\n");

        goto Reregister;
    }

    dwTempLength  = dwLength;
    dwCurrentChar = 0;
    dwNameStart   = 0;

    //
    // For each character in the original string
    //
    for (i = 0; i < dwTempLength; i += sizeof(WCHAR))
    {
        WCHAR  wcTemp = lpProviderList[dwCurrentChar];

        //
        // The provider list is comma-delimited
        //
        if (wcTemp == L',' || wcTemp == UNICODE_NULL)
        {
            lpProviderList[dwCurrentChar] = UNICODE_NULL;

            if (!ScInHardwareProfile(&lpProviderList[dwNameStart], 0))
            {
                //
                // The string plus the trailing UNICODE_NULL
                //
                DWORD dwBytes = (dwCurrentChar - dwNameStart + 1) * sizeof(WCHAR);

                //
                // Service is disabled in the HW profile
                //
                SC_LOG(TRACE,
                       "ScHandleProviderChange: Service %ws is disabled\n",
                       &lpProviderList[dwNameStart]);

                //
                // Shift over the remaining characters in the buffer.
                //
                RtlMoveMemory(&lpProviderList[dwNameStart],
                              &lpProviderList[dwCurrentChar + 1],
                              dwLength - (dwCurrentChar + 1) * sizeof(WCHAR));

                //
                // This may cause dwCurrentChar to underflow to
                // 0xffffffff (if the first provider was deleted).
                // This is OK -- it'll be incremented (to 0) below.
                //
                dwLength     -= dwBytes;
                dwCurrentChar = dwNameStart - 1;
            }
            else
            {
                //
                // Restore the temp character and move
                // to the start of the next provider name.
                //
                lpProviderList[dwCurrentChar] = wcTemp;
                dwNameStart = dwCurrentChar + 1;
            }
        }

        dwCurrentChar++;
    }

    //
    // If the last provider name was deleted, the string will
    // end with a ',' instead of a '\0'.  Note that if all the
    // provider names were deleted, dwCurrentChar will be 0 --
    // we increment it to empty out the provider list.
    //
    if (dwCurrentChar == 0)
    {
        dwCurrentChar++;
    }

    lpProviderList[dwCurrentChar - 1] = UNICODE_NULL;

    SC_LOG(TRACE,
           "ScHandleProviderChange: Provider list is now %ws\n",
           lpProviderList);

    dwStatus = ScRegOpenKeyExW(HKEY_LOCAL_MACHINE,
                               PROVIDER_KEY_BASE,
                               REG_OPTION_NON_VOLATILE,
                               KEY_WRITE | KEY_READ,
                               &hProviderKey);

    if (dwStatus != NO_ERROR)
    {
        SC_LOG(ERROR,
               "ScHandleProviderChange: Unable to open provider key %d\n",
               dwStatus);

        goto Reregister;
    }

    //
    // Create a security descriptor for the registry key we are about
    // to create.  This gives everyone read access, and all access to
    // ourselves only.
    //
    dwStatus = ScCreateAndSetSD(AceData,
                                SC_KEY_ACE_COUNT,
                                LocalSystemSid,
                                LocalSystemSid,
                                &SecurityDescriptor);

#undef SC_KEY_ACE_COUNT

    if (!NT_SUCCESS(dwStatus))
    {
       SC_LOG1(ERROR,
               "ScHandleProviderChange: ScCreateAndSetSD failed %#x\n",
               dwStatus);

       ScRegCloseKey(hProviderKey);
       goto Reregister;
    }

    SecurityAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
    SecurityAttr.lpSecurityDescriptor = SecurityDescriptor;
    SecurityAttr.bInheritHandle = FALSE;

    //
    // Create a new HW provider subkey (or open existing one).
    //
    dwStatus = ScRegCreateKeyExW(hProviderKey,
                                 PROVIDER_KEY_HW,
                                 0,
                                 0,
                                 REG_OPTION_VOLATILE,
                                 KEY_SET_VALUE | KEY_QUERY_VALUE,
                                 &SecurityAttr,
                                 &hProviderHwKey,
                                 &dwDisposition);

    RtlDeleteSecurityObject(&SecurityDescriptor);
    ScRegCloseKey(hProviderKey);

    if (dwStatus != NO_ERROR)
    {
        SC_LOG(ERROR,
               "ScHandleProviderChange: Unable to open HW subkey %d\n",
               dwStatus);

        goto Reregister;
    }

    //
    // Write the modified list to the registry, but only if it is
    // different from the list already there.  This will prevent
    // mpr.dll from getting hyperactive on spurious (or repeated)
    // registry change notifications.
    //
    dwStatus = ScAllocateAndReadConfigValue(hProviderHwKey,
                                            PROVIDER_VALUE,
                                            &lpList,
                                            &dwTempLength);

    if (dwStatus == NO_ERROR)
    {
        //
        // If the string lengths are different, there's
        // definitely been a provider change.
        //
        if (dwTempLength == dwLength)
        {
            fWriteList = (_wcsnicmp(lpList,
                                    lpProviderList,
                                    dwTempLength / sizeof(WCHAR)) != 0);
        }

        LocalFree(lpList);
    }

    if (fWriteList)
    {
        SC_LOG0(TRACE,
                "Active provider list is different -- writing new list\n");

        dwStatus = ScRegSetValueExW(hProviderHwKey,
                                    PROVIDER_VALUE,
                                    0,
                                    REG_SZ,
                                    (LPBYTE) lpProviderList,
                                    dwLength);

        if (dwStatus != NO_ERROR)
        {
            SC_LOG(ERROR,
                   "ScHandleProviderChange: Unable to write HW-aware list %d\n",
                   dwStatus);
        }
    }
    else
    {
        SC_LOG0(TRACE,
                "Active provider list is the same -- not writing\n");
    }

    ScRegCloseKey(hProviderHwKey);

Reregister:

    LocalFree(lpProviderList);

    dwStatus = RtlRegisterWait(&s_hWorkItem,           // work item handle
                               (HANDLE) pContext,      // watiable handle
                               ScHandleProviderChange, // callback
                               (HANDLE) pContext,      // callback arg
                               INFINITE,
                               WT_EXECUTEINPERSISTENTIOTHREAD |
                                   WT_EXECUTEONLYONCE);

    if (!NT_SUCCESS(dwStatus))
    {
        SC_LOG(ERROR,
               "ScHandleProviderChange: RtlRegisterWait FAILED %#x\n",
               dwStatus);
    }
}


VOID
ScMarkForDelete(
    LPSERVICE_RECORD  ServiceRecord
    )

/*++

Routine Description:

    This function adds a DeleteFlag value to a service key in the registry.

Arguments:

    ServiceName - This is a pointer to the service name string.

Return Value:

    none.

--*/
{
    DWORD   status;
    HKEY    hServiceKey;
    DWORD   deleteFlag=1;

    status = ScOpenServiceConfigKey(
                ServiceRecord->ServiceName,
                KEY_WRITE,              // desired access
                FALSE,                  // don't create if missing
                &hServiceKey);

    if (status != NO_ERROR) {
        SC_LOG1(TRACE,"ScMarkForDelete:ScOpenServiceConfigKey failed %d\n",status);
        return;
    }

    status = ScRegSetValueExW(
                hServiceKey,
                REG_DELETE_FLAG,
                0,
                REG_DWORD,
                (LPBYTE)&deleteFlag,
                sizeof(DWORD));

    if (status != NO_ERROR) {
        SC_LOG1(TRACE,"ScMarkForDelete:ScRegSetValueExW failed %d\n",status);
        (void) ScRegCloseKey(hServiceKey);
        return;
    }

    //
    // Make sure we're disabling the service in case it's a driver started by the
    // kernel before we get a chance to delete the key on the next boot
    //
    ASSERT(ServiceRecord->StartType == SERVICE_DISABLED);

    status = ScWriteStartType(hServiceKey, ServiceRecord->StartType);

    if (status != NO_ERROR) {
        SC_LOG1(TRACE,"ScMarkForDelete:ScRegSetValueExW failed %d\n",status);
    }

    (void) ScRegCloseKey(hServiceKey);

    return;
}

BOOL
ScDeleteFlagIsSet(
    HKEY    ServiceKeyHandle
    )

/*++

Routine Description:

    This function looks for a delete flag value stored in the registry for
    this service.

Arguments:

    ServiceKeyHandle - This is a handle to the service key.

Return Value:

    TRUE - if the delete flag exists.
    FALSE - otherwise.

--*/
{
    DWORD   status;
    DWORD   value;
    DWORD   valueSize = sizeof(DWORD);
    DWORD   type;

    status = ScRegQueryValueExW(
                ServiceKeyHandle,
                REG_DELETE_FLAG,
                NULL,
                &type,
                (LPBYTE)&value,
                &valueSize);

    if (status == NO_ERROR) {
        return(TRUE);
    }
    return(FALSE);
}


DWORD
ScReadDependencies(
    HKEY    ServiceNameKey,
    LPWSTR  *Dependencies,
    LPWSTR  ServiceName
    )

/*++

Routine Description:


Arguments:


Return Value:


Note:


--*/
{
    LPWSTR  DependOnService      = NULL;
    LPWSTR  DependOnGroup        = NULL;
    DWORD   DependOnServiceSize  = 0;
    DWORD   DependOnGroupSize    = 0;
    DWORD   status               = NO_ERROR;

    //
    // Read the DependOnService value
    //
    if (ScAllocateAndReadConfigValue(
              ServiceNameKey,
              DEPENDONSERVICE_VALUENAME_W,
              &DependOnService,
              &DependOnServiceSize
              ) != NO_ERROR)
    {
        DependOnService     = NULL;
        DependOnServiceSize = 0;
    }

    //
    // We write a length of 2 bytes into the
    // registry for an empty REG_MULTI_SZ.
    //
    else if ((DependOnServiceSize >= sizeof(WCHAR)) && (*DependOnService != L'\0'))
    {
        //
        // Make sure we got a valid MULTI_SZ
        //
        status = ScValidateMultiSZ(DependOnService,
                                   DependOnServiceSize);

        if (status != NO_ERROR) {

            SC_LOG2(CONFIG,
                    "ScReadDependencies: ScValidateMultiSZ failed %d for service %ws\n",
                    status,
                    ServiceName);

            //
            // Set this to NULL since we'll LocalFree it in CleanExit below
            //
            LocalFree(DependOnService);
            DependOnService     = NULL;
            DependOnServiceSize = 0;
        }

#if DBG
        SC_LOG1(CONFIG, "    " FORMAT_LPWSTR " DependOnService\n", ServiceName);
        ScDisplayWStrArray(DependOnService);
#endif

    }

    //
    // Read the DependOnGroup value
    //
    if (ScAllocateAndReadConfigValue(
              ServiceNameKey,
              DEPENDONGROUP_VALUENAME_W,
              &DependOnGroup,
              &DependOnGroupSize
              ) != NO_ERROR)
    {
        DependOnGroup     = NULL;
        DependOnGroupSize = 0;
    }

    //
    // We write a length of 2 bytes into the
    // registry for an empty REG_MULTI_SZ.
    //
    else if ((DependOnGroupSize >= sizeof(WCHAR)) && (*DependOnGroup != L'\0'))
    {
        //
        // Make sure we got a valid MULTI_SZ
        //
        status = ScValidateMultiSZ(DependOnGroup,
                                   DependOnGroupSize);

        if (status != NO_ERROR) {

            SC_LOG2(CONFIG,
                    "ScReadDependencies: ScValidateMultiSZ failed %d for service %ws\n",
                    status,
                    ServiceName);

            //
            // Set this to NULL since we'll LocalFree it in CleanExit below
            //
            LocalFree(DependOnGroup);
            DependOnGroup     = NULL;
            DependOnGroupSize = 0;
        }

#if DBG
        SC_LOG1(CONFIG, "    " FORMAT_LPWSTR " DependOnGroup\n", ServiceName);
        ScDisplayWStrArray(DependOnGroup);
#endif

    }

    //
    // Concatenate the DependOnService and DependOnGroup string arrays
    // to make the Dependencies array string.
    //
    if (DependOnService == NULL && DependOnGroup == NULL) {
        *Dependencies = NULL;
    }
    else {

        LPWSTR Entry;
        LPWSTR DestPtr;

        if (DependOnService != NULL) {
            DependOnServiceSize -= sizeof(WCHAR);  // subtract the NULL terminator
        }

        if (DependOnGroup != NULL) {

            Entry = DependOnGroup;

            while (*Entry != 0) {

                //
                // Add extra space for the group name to be prefixed
                // by SC_GROUP_IDENTIFIERW.
                //
                DependOnGroupSize += sizeof(WCHAR);

                Entry = (LPWSTR) ((DWORD_PTR) Entry + WCSSIZE(Entry));
            }
        }

        //
        // Allocate the total amount of memory needed for DependOnService
        // and DependOnGroup strings.
        //
        *Dependencies = (LPWSTR) LocalAlloc(LMEM_ZEROINIT,
                                            DependOnServiceSize +
                                                DependOnGroupSize +
                                                sizeof(WCHAR));     // NULL terminator

        if (*Dependencies == NULL) {

            SC_LOG1(ERROR,
                    "ScReadDependencies: LocalAlloc failed " FORMAT_DWORD "\n",
                    GetLastError());

            status = ERROR_NOT_ENOUGH_MEMORY;
            goto CleanExit;
        }

        if (DependOnService != NULL) {

            RtlCopyMemory(*Dependencies, DependOnService, DependOnServiceSize);
        }

        if (DependOnGroup != NULL) {

            DWORD  EntrySize;

            DestPtr = (LPWSTR) ((DWORD_PTR) *Dependencies + DependOnServiceSize);
            Entry = DependOnGroup;

            while (*Entry != 0) {

                EntrySize = (DWORD) wcslen(Entry) + 1;

                *DestPtr = SC_GROUP_IDENTIFIERW;
                DestPtr++;

                wcscpy(DestPtr, Entry);

                DestPtr += EntrySize;
                Entry   += EntrySize;
            }
        }

#if DBG
        SC_LOG0(CONFIG, "    Dependencies\n");
        ScDisplayWStrArray(*Dependencies);
#endif

    }

CleanExit:

    LocalFree(DependOnService);
    LocalFree(DependOnGroup);
    return(status);
}


DWORD
ScReadConfigFromReg(
    LPSERVICE_RECORD    ServiceRecord,
    LPDWORD             lpdwServiceType,
    LPDWORD             lpdwStartType,
    LPDWORD             lpdwErrorControl,
    LPDWORD             lpdwTagId,
    LPWSTR              *Dependencies,
    LPWSTR              *LoadOrderGroup,
    LPWSTR              *DisplayName
    )

/*++

Routine Description:

    This function obtains some basic information about a service from
    the registry.

    If dependencies or load order group information are not present for
    the service in question, then NULL pointers will be returned for
    these parameters.

Arguments:



Return Value:



--*/
{
    DWORD   ApiStatus = NO_ERROR;
    HKEY    ServiceNameKey;

    ApiStatus = ScOpenServiceConfigKey(
            ServiceRecord->ServiceName,
            KEY_READ,
            FALSE,              // don't create if missing
            & ServiceNameKey );
    if (ApiStatus != NO_ERROR) {
        return(ApiStatus);
    }

    //---------------------
    // Service Type
    //---------------------
    ApiStatus = ScReadServiceType( ServiceNameKey, lpdwServiceType);
    if (ApiStatus != NO_ERROR) {
        ScRegCloseKey(ServiceNameKey);
        return(ApiStatus);
    }

    //---------------------
    // Start Type
    //---------------------
    ApiStatus = ScReadStartType( ServiceNameKey, lpdwStartType);
    if (ApiStatus != NO_ERROR) {
        ScRegCloseKey(ServiceNameKey);
        return(ApiStatus);
    }

    //---------------------
    // ErrorControl
    //---------------------
    ApiStatus = ScReadErrorControl( ServiceNameKey, lpdwErrorControl);
    if (ApiStatus != NO_ERROR) {
        ScRegCloseKey(ServiceNameKey);
        return(ApiStatus);
    }

    //---------------------
    // TagId
    //---------------------
    if (ScReadTag( ServiceNameKey, lpdwTagId) != NO_ERROR) {
        *lpdwTagId = 0;
    }

    //---------------------
    // Dependencies
    //---------------------

    if (Dependencies != NULL) {
        if (ScReadDependencies(
                        ServiceNameKey,
                        Dependencies,
                        ServiceRecord->ServiceName) != NO_ERROR) {

            *Dependencies = NULL;
        }
    }


    //---------------------
    // LoadGroupOrder
    //---------------------
    if (ScAllocateAndReadConfigValue(
            ServiceNameKey,
            GROUP_VALUENAME_W,
            LoadOrderGroup,
            NULL
            ) != NO_ERROR) {

        *LoadOrderGroup = NULL;
    }

    //---------------------
    // DisplayName
    //---------------------

    if (DisplayName != NULL) {

        ApiStatus = ScReadDisplayName(
                        ServiceNameKey,
                        DisplayName);
    }

    ScRegCloseKey(ServiceNameKey);

    return(ApiStatus);
}


DWORD
ScTakeOwnership(
    POBJECT_ATTRIBUTES  pObja
    )

/*++

Routine Description:

    This function attempts to take ownership of the key described by the
    Object Attributes.  If successful, it will modify the security descriptor
    to give LocalSystem full control over the key in question.

Arguments:

    pObja - Pointer to object attributes that describe the key.

Return Value:


--*/
{
    DWORD               status = NO_ERROR;
    NTSTATUS            ntStatus;
    HKEY                hKey;
    DWORD               SdBufSize=0;
    SECURITY_DESCRIPTOR tempSD;
    BOOL                DaclFlag;
    PACL                pDacl;
    BOOL                DaclDefaulted;
    PACL                pNewDacl=NULL;
    PACCESS_ALLOWED_ACE pMyAce=NULL;
    DWORD               bufSize;
    PISECURITY_DESCRIPTOR    pSecurityDescriptor = NULL;

    //
    // An event should be logged whenever we must resort to using this
    // routine.
    //

    ScLogEvent(
        NEVENT_TAKE_OWNERSHIP,
        pObja->ObjectName->Buffer
        );

    //
    // If we were denied access, then assume we have the privilege
    // to get WRITE_OWNER access, so that we can modify the Security
    // Descriptor.
    //
    ntStatus = NtOpenKey(
                (PHANDLE)&hKey,
                (ACCESS_MASK)WRITE_OWNER,
                pObja);

    if (!NT_SUCCESS(ntStatus)) {
        // MAKE THIS A TRACE
        SC_LOG(ERROR, "ScTakeOwnership: NtOpenKey(WRITE_OWNER) failed %x\n",ntStatus);
        return(RtlNtStatusToDosError(ntStatus));
    }

    //
    // Set the owner to be local system
    //
    if (!InitializeSecurityDescriptor(&tempSD,SECURITY_DESCRIPTOR_REVISION)) {
        status = GetLastError();
        SC_LOG(ERROR, "ScTakeOwnership: InitializeSD(1) failed %d\n",status);
        NtClose(hKey);
        return(status);
    }
    if (!SetSecurityDescriptorOwner(&tempSD, LocalSystemSid,0)) {
        status = GetLastError();
        SC_LOG(ERROR, "ScTakeOwnership: SetSDOwner failed %d\n",status);
        NtClose(hKey);
        return(status);
    }

    status = ScRegSetKeySecurity(
                hKey,
                OWNER_SECURITY_INFORMATION,
                &tempSD);

    if (status != NO_ERROR) {
        SC_LOG(ERROR, "ScRegOpenKeyExW: ScRegSetKeySecurity (take ownership)"
        " failed %d\n",status);
    }
    NtClose(hKey);

    //
    // Now open the handle again so that the DACL can be modified to
    // allow LocalSystem Full Access.
    //

    ntStatus = NtOpenKey(
                (PHANDLE)&hKey,
                (ACCESS_MASK)READ_CONTROL | WRITE_DAC,
                pObja);

    if (!NT_SUCCESS(ntStatus)) {
        // MAKE THIS A TRACE
        SC_LOG(ERROR, "ScTakeOwnership: NtOpenKey(WRITE_DAC) failed %x\n",ntStatus);
        return(RtlNtStatusToDosError(ntStatus));
    }
    status = ScRegGetKeySecurity(
                hKey,
                DACL_SECURITY_INFORMATION,
                pSecurityDescriptor,
                &SdBufSize);

    if (status != ERROR_INSUFFICIENT_BUFFER) {
        SC_LOG(ERROR, "ScTakeOwnership: ScRegGetKeySecurity(1) failed %d\n",
        status);
        NtClose(hKey);
        return(status);
    }
    pSecurityDescriptor = (PISECURITY_DESCRIPTOR) LocalAlloc(LMEM_FIXED,SdBufSize);
    if (pSecurityDescriptor == NULL) {
        status = GetLastError();
        SC_LOG(ERROR, "ScTakeOwnership: LocalAlloc failed %d\n",status);
        NtClose(hKey);
        return(status);
    }
    status = ScRegGetKeySecurity(
                hKey,
                DACL_SECURITY_INFORMATION,
                pSecurityDescriptor,
                &SdBufSize);

    if (status != NO_ERROR) {
        SC_LOG(ERROR, "ScTakeOwnership: ScRegGetKeySecurity(2) failed %d\n",
        status);
        goto CleanExit;
    }

    //
    // Modify the DACL to allow LocalSystem to have all access.
    //
    // Get size of DACL

    if (!GetSecurityDescriptorDacl (
            pSecurityDescriptor,
            &DaclFlag,
            &pDacl,
            &DaclDefaulted)) {

        status = GetLastError();
        SC_LOG(ERROR, "ScTakeOwnership: GetSecurityDescriptorDacl "
            " failed %d\n",status);
        goto CleanExit;
    }

    //
    // Create new ACE.
    //
    bufSize = sizeof(ACE_HEADER) +
              sizeof(ACCESS_MASK) +
              GetLengthSid(LocalSystemSid);

    pMyAce = (PACCESS_ALLOWED_ACE) LocalAlloc(LMEM_ZEROINIT, bufSize);

    if (pMyAce == NULL) {
        status = GetLastError();
        SC_LOG(ERROR, "ScTakeOwnership: LocalAlloc(Ace) failed %d\n",status);
        goto CleanExit;
    }
    pMyAce->Header.AceType = ACCESS_ALLOWED_ACE_TYPE;
    pMyAce->Header.AceFlags = CONTAINER_INHERIT_ACE;
    pMyAce->Header.AceSize = (WORD)bufSize;
    pMyAce->Mask = GENERIC_ALL;
    if (!CopySid(
            GetLengthSid(LocalSystemSid),
            &(pMyAce->SidStart),
            LocalSystemSid)) {

        status = GetLastError();
        SC_LOG(ERROR, "ScTakeOwnership: CopySid failed %d\n",status);
        goto CleanExit;
    }

    //
    // Allocate buffer for DACL and new ACE.
    //
    bufSize += pDacl->AclSize;

    pNewDacl = (PACL) LocalAlloc(LMEM_ZEROINIT, bufSize);
    if (pNewDacl == NULL) {
        status = GetLastError();
        SC_LOG(ERROR, "ScTakeOwnership: LocalAlloc (DACL) "
            " failed %d\n",status);
        goto CleanExit;
    }
    if (!InitializeAcl(pNewDacl, bufSize, ACL_REVISION)) {
        status = GetLastError();
        SC_LOG(ERROR, "ScTakeOwnership: InitializeAcl failed %d\n",status);
        goto CleanExit;
    }

    //
    // Add the ACE to the DACL
    //
    if (!AddAce(
        pNewDacl,                           // pACL
        pDacl->AclRevision,                 // dwACLRevision
        0,                                  // dwStartingAceIndex
        pMyAce,                             // pAceList
        (DWORD)pMyAce->Header.AceSize)) {   // cbAceList

        status = GetLastError();
        SC_LOG(ERROR, "ScTakeOwnership: AddAce failed %d\n",status);
        goto CleanExit;
    }

    //
    // Initialize a new SD.
    //
    if (!InitializeSecurityDescriptor(&tempSD,SECURITY_DESCRIPTOR_REVISION)) {
        status = GetLastError();
        SC_LOG(ERROR, "ScTakeOwnership: InitializeSD failed %d\n",status);
        goto CleanExit;
    }

    //
    // Add the new DACL to the SD
    //
    if (!SetSecurityDescriptorDacl(&tempSD,TRUE,pNewDacl,FALSE)) {
        status = GetLastError();
        SC_LOG(ERROR, "ScTakeOwnership: SetSecurityDescriptorDacl failed %d\n",status);
        goto CleanExit;
    }

    //
    // Set DACL on the key's security descriptor.
    //
    status = ScRegSetKeySecurity(
                hKey,
                DACL_SECURITY_INFORMATION,
                &tempSD);

    if (status != NO_ERROR) {
        SC_LOG(ERROR, "ScTakeOwnership: ScRegSetKeySecurity(new DACL) failed %d\n",
        status);
    }

    SC_LOG0(CONFIG, "ScTakeOwnership: Changed SD, now try to open with "
    "Desired Access\n");

CleanExit:

    LocalFree(pNewDacl);
    LocalFree(pMyAce);
    LocalFree (pSecurityDescriptor);

    NtClose(hKey);
    return(status);

} // ScTakeOwnership


DWORD
ScOpenSecurityKey(
    IN HKEY     ServiceNameKey,
    IN DWORD    DesiredAccess,
    IN BOOL     CreateIfMissing,
    OUT PHKEY   pSecurityKey
    )

/*++

Routine Description:

    This function opens, or creates (if it doesn't exist), the Security Key
    that is a sub-key of the service's key.  This key is created such that
    only LocalSystem and Administrators have access.

Arguments:

    ServiceNameKey - This is a key to the service key that will contain
        the security key.

    DesiredAccess - This is the access that is desired with the SecurityKey
        that will be returned on a successful call.

    pSecurityKey - A pointer to a location where the security key is to
        be placed.

Return Value:

    NO_ERROR - if the operation is successful.

    otherwise, a registry error code is returned.


--*/
{
    LONG    RegError;

    LPWSTR  SecurityKeyName = SD_VALUENAME_W;



    DWORD                   Disposition;
    NTSTATUS                ntstatus;
    SECURITY_ATTRIBUTES     SecurityAttr;
    PSECURITY_DESCRIPTOR    SecurityDescriptor;

#define SEC_KEY_ACE_COUNT 2
    SC_ACE_DATA AceData[SEC_KEY_ACE_COUNT] = {
        {ACCESS_ALLOWED_ACE_TYPE, CONTAINER_INHERIT_ACE, 0,
               GENERIC_ALL,                &LocalSystemSid},
        {ACCESS_ALLOWED_ACE_TYPE, CONTAINER_INHERIT_ACE, 0,
               GENERIC_ALL,                &AliasAdminsSid}
        };


    if (!CreateIfMissing) {
        //
        // Open the existing security key.
        //
        RegError = ScRegOpenKeyExW(
                    ServiceNameKey,
                    SecurityKeyName,
                    REG_OPTION_NON_VOLATILE,
                    DesiredAccess,
                    pSecurityKey);
        if (RegError != ERROR_SUCCESS) {
            SC_LOG2(TRACE, "ScOpenSecurityKey: "
                    "ScRegOpenKeyExW of " FORMAT_LPWSTR " failed "
                    FORMAT_LONG "\n", SecurityKeyName, RegError);

        }
        return((DWORD)RegError);
    }

    //
    // Create a security descriptor for the registry key we are about
    // to create.  This gives everyone read access, and all access to
    // ourselves and the admins.
    //
    ntstatus = ScCreateAndSetSD(
                   AceData,
                   SEC_KEY_ACE_COUNT,
                   LocalSystemSid,
                   LocalSystemSid,
                   &SecurityDescriptor
                   );

    if (! NT_SUCCESS(ntstatus)) {
        SC_LOG1(ERROR, "ScCreateAndSetSD failed " FORMAT_NTSTATUS
                "\n", ntstatus);
        return(RtlNtStatusToDosError(ntstatus));
    }

    //
    // Protect the DACL on the SD so it can't be overridden by DACL inheritance
    // from parent keys.  Since this key can contain a SACL, we want to make
    // sure access to it is always what we expect.
    //

    ntstatus = RtlSetControlSecurityDescriptor(SecurityDescriptor,
                                               SE_DACL_PROTECTED,
                                               SE_DACL_PROTECTED);

    if (!NT_SUCCESS(ntstatus))
    {
        SC_LOG1(ERROR,
                "ScOpenSecurityKey:  RtlSetControlSecurityDescriptor failed %x\n",
                ntstatus);

        RtlDeleteSecurityObject(&SecurityDescriptor);
        return RtlNtStatusToDosError(ntstatus);
    }

    SecurityAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
    SecurityAttr.lpSecurityDescriptor = SecurityDescriptor;
    SecurityAttr.bInheritHandle = FALSE;

    //
    // Create a new service key (or open existing one).
    //
    RegError = ScRegCreateKeyExW(
           ServiceNameKey,
           SecurityKeyName,
           0,
           0,
           REG_OPTION_NON_VOLATILE, // options
           DesiredAccess,           // desired access
           &SecurityAttr,
           pSecurityKey,
           &Disposition);


    RtlDeleteSecurityObject(&SecurityDescriptor);

    if (RegError != ERROR_SUCCESS) {
        SC_LOG2(ERROR, "ScOpenSecurityKey: "
                "ScRegCreateKeyExW of " FORMAT_LPWSTR " failed "
                FORMAT_LONG "\n", SecurityKeyName, RegError);
        return ((DWORD) RegError);
    }

    return NO_ERROR;

} // ScOpenSecurityKey