Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

4527 lines
107 KiB

/*++
Copyright (c) 1991-92 Microsoft Corporation
Module Name:
scconfig.c
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
ScWriteDependencies
ScWriteDisplayName
ScWriteErrorControl
ScWriteGroupForThisService
ScWriteImageFileName
ScWriteServiceType
ScWriteStartType
ScWriteStartName
ScReadDisplayName
ScReadServiceType
ScReadStartType
ScReadErrorControl
ScReadServiceConfig
ScAllocateAndReadConfigValue
ScReadNoInteractiveFlag
ScGetToken
ScOpenServicesKey
ScRegCreateKeyExW
ScRegOpenKeyExW
ScRegQueryValueExW
ScRegSetValueExW
ScRegEnumKeyW
ScRegDeleteKeyW
ScRegQueryInfoKeyW
ScRegGetKeySecurity
ScRegSetKeySecurity
ScRegEnumValueW
ScResetGroupOrderChange
ScHandleGroupOrderChange
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.
--*/
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windef.h>
#include <winbase.h> // LocalAlloc
#include <winreg.h>
#include <stdlib.h> // wide character c runtimes.
#include <string.h> // ansi character c runtimes.
#include <tstr.h> // Unicode string macros
#include <rpc.h> // needed for svcctl.h
#include <svcctl.h> // MIDL generated header file
#include <scdebug.h> // SC_LOG1(), etc.
#include "dataman.h" // LPIMAGE_RECORD
#include "scopen.h" // Handle structures and signature definitions
#include <sclib.h> // ScConvertToAnsi
#include <control.h> // ScWaitForConnect
#include "scconfig.h" // ScGetToken
#include "svcctrl.h" // ScLogEvent,ScGlobalNetLogonName
#include <valid.h> // SERVICE_TYPE_INVALID().
#include <strarray.h> // ScDisplayWStrArray
#include <scseclib.h> // ScCreateAndSetSD
#include <regrpc.h> // RPC_SECURITY_DESCRIPTOR
#define ScWinRegErrorToApiStatus( regError ) \
( (DWORD) RegError ) /* BUGBUG: Is this kosher? --JR */
//
// 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"
//
// Values for ServiceType name
//
//#define SERVICETYPE_VALUE_DRIVER_W L"Driver"
//#define SERVICETYPE_VALUE_WIN32_W L"WIN32_Service"
//#ifdef SERVICE_ADAPTER
//#define SERVICETYPE_VALUE_ADAPTER_W L"Adapter"
//#endif
//
// Values for ErrorControl name
//
//#define ERRORCONTROL_VALUE_CRITICAL_W L"Critical"
//#define ERRORCONTROL_VALUE_NORMAL_W L"Normal"
//#define ERRORCONTROL_VALUE_SEVERE_W L"Severe"
//
// Values for Start (a.k.a. StartType) name
//
//#define STARTTYPE_VALUE_AUTO_W L"Auto"
//#define STARTTYPE_VALUE_BOOT_W L"Boot"
//#define STARTTYPE_VALUE_DEMAND_W L"Demand"
//#define STARTTYPE_VALUE_DISABLED_W L"Disabled"
//#define STARTTYPE_VALUE_SYSTEM_W L"System"
//
// Registry key path to the services tree
//
#define SERVICES_TREE L"System\\CurrentControlSet\\Services"
#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 IO_STATUS_BLOCK ScIoStatusBlock;
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
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(
VOID
)
/*++
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 behaviour if this fails?
NOTE:
If the NetLogon service is not listed in the registry, then it may be
created later. So this routine could be called from RCreateService().
LOCKS:
No locks are obtained by this routine. If it is called anytime other
than init time, then it is expected that the DatabaseLock be held.
Arguments:
none
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 NetLogon service record has been
marked (in the START_TYPE field) as disabled.
Note:
--*/
{
DWORD status;
HANDLE pipeHandle;
LPSERVICE_RECORD serviceRecord;
LPIMAGE_RECORD imageRecord;
LPWSTR imageName;
HANDLE eventHandle;
DWORD processId;
//
// If the NetLogon Service is listed, get the name of its image file.
// and create an image record for it. (This is the Security Process).
//
status = ScGetNamedServiceRecord(ScGlobalNetLogonName,&serviceRecord);
if (status != NO_ERROR) {
//
// This is not an error condition.
//
SC_LOG0(TRACE,"ScInitSecurityProcess:NetLogon not listed\n");
return (TRUE);
}
status = ScGetImageFileName(serviceRecord->ServiceName, &imageName);
if (status != NO_ERROR) {
SC_LOG0(ERROR,"Cannot get image file name for security process\n");
serviceRecord->StartType = SERVICE_DISABLED;
return(FALSE);
}
//
// Create an instance of the control pipe.
//
status = ScCreateControlInstance (&pipeHandle);
if (status != NO_ERROR) {
SC_LOG1(ERROR,"ScInitSecurityProcess: ScCreateControlInstance Failure "
FORMAT_DWORD "\n", status);
(void) LocalFree(imageName);
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);
LocalFree(imageName);
CloseHandle(pipeHandle);
serviceRecord->StartType = SERVICE_DISABLED;
return(FALSE);
}
}
if (!SetEvent(eventHandle)) {
SC_LOG1(ERROR,"ScInitSecurityProcess: SetEvent Failed " FORMAT_DWORD
"\n", GetLastError());
LocalFree(imageName);
CloseHandle(pipeHandle);
CloseHandle(eventHandle);
serviceRecord->StartType = SERVICE_DISABLED;
return(FALSE);
}
//
// Wait for the Security Process to attach to the pipe.
//
status = ScWaitForConnect(pipeHandle, &processId);
if (status != NO_ERROR) {
SC_LOG1(ERROR,"ScInitSecurityProcess:"
"SecurityProcess did not attach to pipe " FORMAT_DWORD "\n",
status);
LocalFree(imageName);
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 PID or processHandle.
// Therefore, we will never be able to terminate it. This is desired
// behavior though. We should never terminate the security process.
//
//
// BUGBUG: Since we store 0 for the processId, I need to make sure
// that I handle the 0 case when I go to terminate a process.
//
ScDatabaseLock(SC_GET_SHARED,"ScInitSecurityProcess1");
status = ScCreateImageRecord (
&imageRecord,
imageName,
0,
pipeHandle,
NULL, // The process handle is NULL.
NULL, // Token handle is also NULL -- LocalSystem
NULL); // No user profile loaded -- LocalSystem
ScDatabaseLock(SC_RELEASE,"ScInitSecurityProcess2");
(void) LocalFree(imageName);
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;
LONG RegError;
LPWSTR Groups;
LPWSTR GroupPtr;
LPWSTR GroupName;
//
// 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,
NULL
) != NO_ERROR) {
(void) ScRegCloseKey(ScSGOKey);
ScSGOKey = NULL;
return FALSE;
}
//
// Leave the ServiceGroupOrder key open for change notify later
//
SC_LOG0(DEPEND_DUMP, "ScCreateLoadOrderGroupList: ServiceGroupOrder:\n");
if (SvcctrlDebugLevel & DEBUG_DEPEND_DUMP) {
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
//
(void) LocalFree(Groups);
return FALSE;
}
}
}
(void) 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;
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;
LPBYTE SecurityDescriptor = NULL;
FILETIME LastWriteTime;
DWORD HeapSize;
ScGroupListLock(SC_GET_EXCLUSIVE);
//
// 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(ERROR,"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 );
(void) 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);
RegError = ScRegOpenKeyExW(
ServicesKey,
ServiceName,
REG_OPTION_NON_VOLATILE, // options
KEY_READ, // desired access
&ServiceNameKey
);
if (RegError != ERROR_SUCCESS) {
SC_LOG2(ERROR, "ScGenerateServiceDB: ScRegOpenKeyExW of "
FORMAT_LPWSTR " failed " FORMAT_LONG "\n",
ServiceName, RegError);
(void) ScRegCloseKey(ServicesKey);
return FALSE;
}
//
// Read service config info from the registry and build the
// service record.
//
if (ScReadServiceConfig(
ServiceNameKey,
ServiceName
) != NO_ERROR) {
(void) ScRegCloseKey(ServicesKey);
(void) ScRegCloseKey(ServiceNameKey);
return FALSE;
}
(void) ScRegCloseKey(ServiceNameKey);
}
Index++;
} while (RegError == ERROR_SUCCESS);
(void) 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();
ScGroupListLock(SC_RELEASE);
#if DBG
if (SvcctrlDebugLevel & DEBUG_DEPEND_DUMP) {
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 ERROR_PATH_NOT_FOUND; // BUGBUG: Better error code? --JR
}
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;
NTSTATUS ntstatus;
SECURITY_ATTRIBUTES SecurityAttr;
PSECURITY_DESCRIPTOR SecurityDescriptor;
#define SC_KEY_ACE_COUNT 3
SC_ACE_DATA AceData[SC_KEY_ACE_COUNT] = {
{ACCESS_ALLOWED_ACE_TYPE, CONTAINER_INHERIT_ACE, 0,
GENERIC_READ |
KEY_CREATE_SUB_KEY, &WorldSid},
{ACCESS_ALLOWED_ACE_TYPE, CONTAINER_INHERIT_ACE, 0,
GENERIC_ALL, &LocalSystemSid},
{ACCESS_ALLOWED_ACE_TYPE, CONTAINER_INHERIT_ACE, 0,
GENERIC_ALL, &AliasAdminsSid}
};
//
// 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,
SC_KEY_ACE_COUNT,
LocalSystemSid,
LocalSystemSid,
&SecurityDescriptor
);
if (! NT_SUCCESS(ntstatus)) {
SC_LOG1(ERROR, "ScCreateAndSetSD failed " FORMAT_NTSTATUS
"\n", ntstatus);
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(
ServicesKey,
ServiceName,
0,
WIN31_CLASS,
REG_OPTION_NON_VOLATILE, // options
DesiredAccess, // desired access
&SecurityAttr,
&ServiceNameKey,
&Disposition);
(void) RtlDeleteSecurityObject(&SecurityDescriptor);
if (RegError != ERROR_SUCCESS) {
SC_LOG2(ERROR, "ScOpenServiceConfigKey: "
"ScRegCreateKeyExW of " FORMAT_LPWSTR " failed "
FORMAT_LONG "\n", ServiceName, RegError);
(void) ScRegCloseKey(ServicesKey);
return ((DWORD) RegError);
}
}
(void) ScRegCloseKey(ServicesKey);
//
// Give the service key back to caller.
//
*ServiceKey = ServiceNameKey;
return NO_ERROR;
} // ScOpenServiceConfigKey
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 );
RegError = ScRegQueryValueExW(
ServiceNameKey,
SERVICETYPE_VALUENAME_W,
NULL,
NULL,
(LPVOID) ServiceTypePtr,
&BytesRequired
);
if (RegError != ERROR_SUCCESS) {
SC_LOG3(ERROR, "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 );
RegError = ScRegQueryValueExW(
ServiceNameKey,
NOINTERACTIVE_VALUENAME_W,
NULL,
NULL,
(LPVOID) 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 );
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 );
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 );
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
ScReadDisplayName(
IN HKEY ServiceNameKey,
OUT LPWSTR *DisplayName
)
/*++
Routine Description:
This function attempts to read the value for the DisplayName in the
registry. If this read fails because the key does no exist, then
this function sets the pointer to the DisplayName 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
displayName will be allocated, or the pointer will be NULL.
Arguments:
ServiceNameKey - This is the Service's Key handle.
DisplayName - This is a pointer to a location where the pointer to
the DisplayName is to be placed.
Return Value:
--*/
{
LONG RegError;
RegError = ScAllocateAndReadConfigValue(
ServiceNameKey,
DISPLAYNAME_VALUENAME_W,
DisplayName,
NULL
);
if (RegError == ERROR_FILE_NOT_FOUND) {
*DisplayName = NULL;
return(NO_ERROR);
}
if (RegError != ERROR_SUCCESS) {
*DisplayName = NULL;
}
return(RegError);
} // ScReadDisplayName
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 {
(void) 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) DependOnService + DependSize);
DestService = DependOnService;
DestGroup = DependOnGroup;
while ((*Dependencies) != 0) {
if (*Dependencies == SC_GROUP_IDENTIFIERW) {
Dependencies++;
DependencyLength = wcslen(Dependencies) + 1;
wcscpy(DestGroup, Dependencies);
DestGroup += DependencyLength;
}
else {
DependencyLength = 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)
(LPVOID) DependOnService, // data
ScWStrArraySize(DependOnService) // byte count for data
);
if (RegError != ERROR_SUCCESS) {
#if DBG
SC_LOG1(ERROR, "ScWriteDependOnService: ScRegSetValueExW returned "
FORMAT_LONG "\n", RegError);
if (SvcctrlDebugLevel & DEBUG_ERROR) {
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)
(LPVOID) DependOnGroup, // data
ScWStrArraySize(DependOnGroup) // byte count for data
);
if (RegError != ERROR_SUCCESS) {
#if DBG
SC_LOG1(ERROR, "ScWriteDependOnGroup: ScRegSetValueExW returned "
FORMAT_LONG "\n", RegError);
if (SvcctrlDebugLevel & DEBUG_ERROR) {
ScDisplayWStrArray(DependOnGroup);
}
#endif
goto CleanExit;
}
CleanExit:
(VOID) LocalFree(DependOnService);
if (RegError != NO_ERROR) {
SC_LOG2(ERROR, "ScWriteDependencies (%ws) Error %d \n",
Dependencies,RegError);
}
return (ScWinRegErrorToApiStatus( RegError ));
} // ScWriteDependencies
DWORD
ScWriteDisplayName(
IN HKEY ServiceNameKey,
IN LPWSTR DisplayName
)
/*++
Routine Description:
This function writes the Display Name to the registry for the particular
key. If the DisplayName is a NULL pointer, we don't do anything. If
the DisplayName is an empty string, we delete the registry value for
the DisplayName.
Arguments:
Return Value:
--*/
{
LONG RegError;
SC_ASSERT( ServiceNameKey != NULL );
//
// If the DisplayName doesn't exist, then return. Nothing
// needs changing.
//
if (DisplayName == NULL) {
return(NO_ERROR);
}
if (wcslen(DisplayName) != 0) {
//
// Write the DisplayName
//
RegError = ScRegSetValueExW(
ServiceNameKey, // open handle (to section)
DISPLAYNAME_VALUENAME_W, // value name
0,
REG_SZ, // type (zero-terminated UNICODE)
(LPVOID) DisplayName, // data
WCSSIZE(DisplayName) // byte count for data
);
if (RegError != ERROR_SUCCESS) {
SC_LOG2(ERROR, "ScWriteDisplayName: ScRegSetValueExW of " FORMAT_LPWSTR
" failed " FORMAT_LONG "\n",
DisplayName, RegError);
}
return (ScWinRegErrorToApiStatus( RegError ) );
}
else {
//
// The DisplayName is specifically being cleared. So we
// want to delete the DisplayName Value.
//
RegError = ScRegDeleteValue(ServiceNameKey,DISPLAYNAME_VALUENAME_W);
if (RegError != ERROR_SUCCESS) {
SC_LOG1(TRACE, "Attempt to delete DisplayName for service failed"
"" FORMAT_LONG "\n"
"- - It may not exist, in which case this error is ok\n",
RegError);
}
return(NO_ERROR);
}
} // ScWriteDisplayName
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
(LPVOID) & 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
(LPVOID) 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 ); // BUGBUG: How do we delete?
//
// Write the group
//
RegError = ScRegSetValueExW(
ServiceNameKey, // open handle (to section)
GROUP_VALUENAME_W, // value name
0,
REG_SZ, // type (zero-terminated UNICODE)
(LPVOID) Group, // data
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)
(LPVOID) ImageFileName, // data
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
(LPVOID) & 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
(LPVOID) &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
(LPVOID) &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 ); // BUGBUG: How do we delete this?
//
// Write the StartName
//
RegError = ScRegSetValueExW(
ServiceNameKey, // open handle (to section)
STARTNAME_VALUENAME_W, // value name
0,
REG_SZ, // type (zero-terminated UNICODE)
(LPVOID) StartName, // data
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 DependOnService = NULL;
LPWSTR DependOnGroup = NULL;
LPWSTR DisplayName=NULL;
PSECURITY_DESCRIPTOR Sd = NULL;
LPSERVICE_RECORD ServiceRecord;
//
// Get the Service Type information from the registry
//
status = ScReadServiceType(ServiceNameKey, &ServiceType);
if (status != NO_ERROR) {
SC_LOG1(ERROR, "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.
//
ScDatabaseLock(SC_GET_EXCLUSIVE, "ScReadServiceConfig1");
//
// 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);
// BUGBUG! This LocalFree needs to be changed to a HeapFree from the
// service record heap.
// (void) LocalFree(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:
ScDatabaseLock(SC_RELEASE, "ScReadServiceConfig1");
if (Group != NULL) {
(void) LocalFree(Group);
}
if (Dependencies != NULL) {
(void) LocalFree(Dependencies);
}
if (DisplayName != NULL) {
(void) LocalFree(DisplayName);
}
return status;
}
DWORD
ScAllocateAndReadConfigValue(
IN HKEY Key,
IN LPWSTR 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);
}
if (TempValue != NULL) {
(void) LocalFree(TempValue);
}
return (DWORD) RegError;
}
if (ValueType != REG_EXPAND_SZ) {
*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());
(void) 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);
(void) LocalFree(*Value);
*Value = NULL;
(void) LocalFree(TempValue);
return(ERROR_NOT_ENOUGH_MEMORY); // BUGBUG: Find a better rc.
}
(void) 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 = WCSSIZE(HKeyLocalMachine)+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 LPWSTR 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_FULL_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);
//
// Allocate memory for the ValueKeyInfo
//
bufSize = *lpcbData + sizeof(KEY_VALUE_FULL_INFORMATION) + ValueName.Length
- sizeof(WCHAR); // subtract memory for 1 char because it's included
// in the sizeof(KEY_VALUE_FULL_INFORMATION).
KeyValueInfo = (PKEY_VALUE_FULL_INFORMATION)LocalAlloc(LMEM_ZEROINIT, (UINT) bufSize);
if (KeyValueInfo == NULL) {
SC_LOG0(ERROR,"ScRegQueryValueExW: LocalAlloc Failed");
return(ERROR_NOT_ENOUGH_MEMORY);
}
ntStatus = NtQueryValueKey(
hKey,
&ValueName,
KeyValueFullInformation,
(PVOID)KeyValueInfo,
(ULONG)bufSize,
(PULONG)&bufSize);
if ((NT_SUCCESS(ntStatus)
|| (ntStatus == STATUS_BUFFER_OVERFLOW))
&& ARGUMENT_PRESENT(lpcbData)) {
*lpcbData = KeyValueInfo->DataLength;
}
if (NT_SUCCESS(ntStatus)) {
if (ARGUMENT_PRESENT(lpType)) {
*lpType = KeyValueInfo->Type;
}
if (ARGUMENT_PRESENT(lpData) && (KeyValueInfo->DataLength != 0)) {
memcpy(
lpData,
(LPBYTE)KeyValueInfo + KeyValueInfo->DataOffset,
KeyValueInfo->DataLength);
}
if (!ARGUMENT_PRESENT(lpData)) {
ntStatus = STATUS_BUFFER_OVERFLOW;
}
}
LocalFree(KeyValueInfo);
return(RtlNtStatusToDosError(ntStatus));
}
DWORD
ScRegSetValueExW(
IN HKEY hKey,
IN LPWSTR lpValueName,
IN DWORD lpReserved,
IN DWORD dwType,
IN LPBYTE lpData,
IN DWORD cbData
)
/*++
Routine Description:
Arguments:
Return Value:
Note:
--*/
{
DWORD status;
NTSTATUS ntStatus;
UNICODE_STRING ValueName;
LPWSTR ScSubStrings[3];
WCHAR ScErrorCodeString[25];
UNREFERENCED_PARAMETER(lpReserved);
RtlInitUnicodeString(&ValueName,lpValueName);
ntStatus = NtSetValueKey(
hKey,
&ValueName,
0,
(ULONG)dwType,
(PVOID)lpData,
(ULONG)cbData);
status = RtlNtStatusToDosError(ntStatus);
if (status != NO_ERROR) {
ScSubStrings[0] = L"ScRegSetValueExW";
ScSubStrings[1] = lpValueName;
wcscpy(ScErrorCodeString,L"%%");
ultow(status, ScErrorCodeString+2, 10);
ScSubStrings[2] = ScErrorCodeString;
ScLogEvent(
EVENT_CALL_TO_FUNCTION_FAILED_II,
3,
ScSubStrings
);
}
return(status);
}
DWORD
ScRegDeleteValue(
IN HKEY hKey,
IN LPWSTR 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);
}
memcpy(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;
LPWSTR ScSubStrings[2];
WCHAR ScErrorCodeString[25];
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));
}
memcpy(
lpClass,
(LPBYTE)KeyInfo->Class,
KeyInfo->ClassLength);
//
// NUL terminate the class name.
//
*(lpClass + (KeyInfo->ClassLength/sizeof(WCHAR))) = UNICODE_NULL;
}
else {
//
// NtQueryKey failed
//
ScSubStrings[0] = L"ScRegQueryInfoKeyW";
wcscpy(ScErrorCodeString,L"%%");
ultow(status, ScErrorCodeString+2, 10);
ScSubStrings[1] = ScErrorCodeString;
ScLogEvent(
EVENT_CALL_TO_FUNCTION_FAILED,
2,
ScSubStrings
);
}
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 = 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;
LPWSTR ScSubStrings[2];
WCHAR ScErrorCodeString[25];
//
// 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);
ScSubStrings[0] = L"MapSDToRpcSD";
wcscpy(ScErrorCodeString,L"%%");
ultow(status, ScErrorCodeString+2, 10);
ScSubStrings[1] = ScErrorCodeString;
ScLogEvent(
EVENT_CALL_TO_FUNCTION_FAILED,
2,
ScSubStrings
);
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;
DWORD retStatus = NO_ERROR;
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);
}
//
// Allocate memory for the ValueKeyInfo
// NOTE: MAX_PATH is added to the size for the name array.
//
bufSize = *lpcbData + sizeof(KEY_VALUE_FULL_INFORMATION) + MAX_PATH;
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 {
memcpy(
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 {
memcpy(
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
ScResetGroupOrderChange(
HANDLE Event
)
/*++
Routine Description:
This function resets the event for change notification and makes
the call to NtNotifyChangeKey for the ServiceOrderGroup key.
It is called by ScProcessWatcher which is the service controller
thread that wakes up for special non-API driven events.
Arguments:
Event - Supplies the handle to the event which NtNotifyChangeKey
will set.
Return Value:
None.
--*/
{
NTSTATUS ntstatus;
//
// ServiceGroupOrder key should be kept open from the time we first
// read in the key
//
SC_ASSERT(ScSGOKey);
//
// Reset the event to a non-signalled state
//
if (! ResetEvent(Event)) {
SC_LOG1(ERROR, "Error reseting ChangeNotify group order event "
FORMAT_DWORD "\n", GetLastError());
return;
}
//
// Do a change notify asynchronously
//
ntstatus = NtNotifyChangeKey (
ScSGOKey,
Event,
NULL,
NULL,
&ScIoStatusBlock,
REG_NOTIFY_CHANGE_LAST_SET,
FALSE, // Don't watch subtree; just the key.
(PVOID) &Buffer, // For names of keys that changed
sizeof(DWORD),
TRUE // Asynchronous call
);
if (ntstatus != STATUS_SUCCESS && ntstatus != STATUS_PENDING) {
SC_LOG1(ERROR, "ScResetGroupOrderChange: NtNotifyChangeKey returned "
FORMAT_NTSTATUS "\n", ntstatus);
}
else {
SC_LOG1(DEPEND, "NtNotifyChangeKey returned " FORMAT_NTSTATUS "\n",
ntstatus);
}
}
VOID
ScHandleGroupOrderChange(
VOID
)
/*++
Routine Description:
This function reads the new value from the registry for the
ServiceGroupOrder key and is called after a change notification
has occurred by ScProcessWatcher.
Arguments:
None.
Return Value:
None.
Note:
The GroupListLock must be held exclusively prior to calling this routine.
--*/
{
LPWSTR Groups;
//
// Change notify on ServiceGroupOrder occurred
//
if (! NT_SUCCESS(ScIoStatusBlock.Status)) {
SC_LOG1(ERROR, "ScHandleGroupOrderChange: NtNotifyChangeKey failed "
FORMAT_NTSTATUS "\n", ScIoStatusBlock.Status);
return;
}
SC_LOG1(DEPEND, "ScHandleGroupOrderChange: NtNotifyChangeKey returned "
FORMAT_NTSTATUS "\n", ScIoStatusBlock.Status);
//
// Read the List value of ServiceGroupOrder
//
if (ScAllocateAndReadConfigValue(
ScSGOKey,
GROUPLIST_VALUENAME_W,
&Groups,
NULL
) != NO_ERROR) {
return;
}
//
// Modify group order list and service records as necessary
//
ScChangeGroupOrder(Groups);
(void) LocalFree(Groups);
}
VOID
ScMarkForDelete(
LPWSTR ServiceName
)
/*++
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(
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));
(void) ScRegCloseKey(hServiceKey);
if (status != NO_ERROR) {
SC_LOG1(TRACE,"ScMarkForDelete:ScRegSetValueExW failed %d\n",status);
return;
}
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 status = NO_ERROR;
//
// Read the DependOnService value
//
if (ScAllocateAndReadConfigValue(
ServiceNameKey,
DEPENDONSERVICE_VALUENAME_W,
&DependOnService,
NULL
) != NO_ERROR) {
DependOnService = NULL;
}
#if DBG
else {
SC_LOG1(CONFIG, " " FORMAT_LPWSTR " DependOnService\n", ServiceName);
if (SvcctrlDebugLevel & DEBUG_CONFIG) {
ScDisplayWStrArray(DependOnService);
}
}
#endif
//
// Read the Dependencies (BUGBUG: To be changed to DependOnGroup) value
//
if (ScAllocateAndReadConfigValue(
ServiceNameKey,
DEPENDONGROUP_VALUENAME_W,
&DependOnGroup,
NULL
) != NO_ERROR) {
DependOnGroup = NULL;
}
#if DBG
else {
SC_LOG1(CONFIG, " " FORMAT_LPWSTR " DependOnGroup\n", ServiceName);
if (SvcctrlDebugLevel & DEBUG_CONFIG) {
ScDisplayWStrArray(DependOnGroup);
}
}
#endif
//
// Concatenate the DependOnService and DependOnGroup string arrays
// to make the Dependencies array string.
//
if (DependOnService == NULL && DependOnGroup == NULL) {
*Dependencies = NULL;
}
else {
DWORD DependOnServiceSize = 0;
DWORD DependOnGroupSize = 0;
DWORD EntrySize;
LPWSTR Entry;
LPWSTR DestPtr;
if (DependOnService != NULL) {
DependOnServiceSize = ScWStrArraySize(DependOnService);
DependOnServiceSize -= sizeof(WCHAR); // subtract the NULL
// terminator
}
if (DependOnGroup != NULL) {
Entry = DependOnGroup;
while (*Entry != 0) {
EntrySize = WCSSIZE(Entry); // This entry and its null.
//
// Add extra space for the group name to be prefixed
// by SC_GROUP_IDENTIFIERW.
//
DependOnGroupSize += EntrySize + sizeof(WCHAR);
Entry = (LPWSTR) ((DWORD) Entry + EntrySize);
}
}
//
// Allocate the total amount of memory needed for DependOnService
// and DependOnGroup strings.
//
if ((*Dependencies = (LPWSTR)LocalAlloc(
LMEM_ZEROINIT,
(UINT) (DependOnServiceSize + DependOnGroupSize +
sizeof(WCHAR)) // NULL terminator
)) == NULL) {
SC_LOG1(ERROR, "ScReadServiceConfig: LocalAlloc failed " FORMAT_DWORD "\n",
GetLastError());
status = ERROR_NOT_ENOUGH_MEMORY;
goto CleanExit;
}
if (DependOnService != NULL) {
memcpy(*Dependencies, DependOnService, DependOnServiceSize);
(void) LocalFree(DependOnService);
DependOnService = NULL;
}
if (DependOnGroup != NULL) {
DestPtr = (LPWSTR) ((DWORD) *Dependencies + DependOnServiceSize);
Entry = DependOnGroup;
while (*Entry != 0) {
EntrySize = wcslen(Entry) + 1;
*DestPtr = SC_GROUP_IDENTIFIERW;
DestPtr++;
wcscpy(DestPtr, Entry);
DestPtr += EntrySize;
Entry += EntrySize;
}
(void) LocalFree(DependOnGroup);
DependOnGroup = NULL;
}
#if DBG
SC_LOG0(CONFIG, " Dependencies\n");
if (SvcctrlDebugLevel & DEBUG_CONFIG) {
ScDisplayWStrArray(*Dependencies);
}
#endif
}
CleanExit:
if (DependOnGroup != NULL) {
LocalFree(DependOnGroup);
}
if (DependOnService != NULL) {
LocalFree(DependOnService);
}
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) {
ScDatabaseLock(SC_RELEASE, "RChangeServiceConfigW 5");
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 (ScReadDependencies(
ServiceNameKey,
Dependencies,
ServiceRecord->ServiceName) != NO_ERROR) {
*Dependencies = NULL;
}
//---------------------
// LoadGroupOrder
//---------------------
if (ScAllocateAndReadConfigValue(
ServiceNameKey,
GROUP_VALUENAME_W,
LoadOrderGroup,
NULL
) != NO_ERROR) {
*LoadOrderGroup = NULL;
}
//---------------------
// DisplayName
//---------------------
ApiStatus = ScReadDisplayName(
ServiceNameKey,
DisplayName);
if (ApiStatus != NO_ERROR) {
ScRegCloseKey(ServiceNameKey);
return(ApiStatus);
}
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;
LPWSTR ScSubStrings[1];
//
// An event should be logged whenever we must resort to using this
// routine.
//
ScSubStrings[0] = pObja->ObjectName->Buffer;
ScLogEvent(
EVENT_TAKE_OWNERSHIP,
1,
ScSubStrings
);
//
// 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 = 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;
return(status);
}
//
// 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 = 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:
if (pNewDacl != NULL) {
LocalFree(pNewDacl);
}
if (pMyAce != NULL) {
LocalFree(pMyAce);
}
if (pSecurityDescriptor != NULL) {
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));
}
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,
WIN31_CLASS,
REG_OPTION_NON_VOLATILE, // options
DesiredAccess, // desired access
&SecurityAttr,
pSecurityKey,
&Disposition);
(void) 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