/*++ 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 // wide character c runtimes. #include // ansi character c runtimes. #include // Unicode string macros #include // ScConvertToAnsi #include // ScWaitForConnect #include "scconfig.h" // ScGetToken #include // SERVICE_TYPE_INVALID(). #include // ScDisplayWStrArray #include // ScCreateAndSetSD #include // 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