mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
5214 lines
125 KiB
5214 lines
125 KiB
/*++
|
|
|
|
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
|
|
|