windows-nt/Source/XPSP1/NT/base/screg/sc/server/cfgapi.cxx
2020-09-26 16:20:57 +08:00

3148 lines
92 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1992 Microsoft Corporation
Module Name:
CfgAPI.cxx
Abstract:
This file contains the Service Controller's Config API.
RChangeServiceConfigW
RCreateServiceW
RDeleteService
RQueryServiceConfigW
ScCanonDriverImagePath
Author:
John Rogers (JohnRo) 10-Apr-1992
Environment:
User Mode - Win32
Revision History:
26-Mar-1992 danl
Created the stubbed out version for RPC.
22-Apr-1992 JohnRo
Use SC_LOG0(), FORMAT_ equates, etc
Wrote real code for these APIs.
Split lock APIs into server/lockapi.c.
Added some assertion checks here and there. Added handle checks too.
28-Apr-1992 JohnRo
Undo all group operations (ifdef USE_GROUPS).
28-May-1992 JohnRo
Put back load order group in APIs.
Avoid compiler warnings.
02-Jun-1992 JohnRo
RAID 10709: QueryServiceConfig() has bad length check.
08-Aug-1992 Danl
RDeleteService: Add call to ScMarkForDelete so the registry entry
is marked for deletion.
25-Aug-1992 Danl
RQueryServiceConfig: Fix problem where it failed if it couldn't
read the StartName from the registry. (This should be optional).
21-Jan-1994 Danl
RChangeServiceConfigW: Fixed BUG where a unicode DisplayName was
being copied into a buffer whose size assumed ansi characters.
Also changed so that displayname is not allocated unless it is
different from the ServiceName.
24-Mar-1994 Danl
RQueryServiceConfigW: Add worse case number of bytes (3) for RPC
alignment. I removed the exact calculation because it assumed
that strings went into the buffer in a particular order. Since RPC
picks the order for unmarshalling into the users buffer, the order
may be random.
06-Jun-1994 Danl
We were allocating memory for the display name in the service record only
when it was the same. It should have been doing this only when different.
The behaviour was such that if you changed the display name to something
other than the KeyName, the new name was placed in the registry, but not
in the service record. So GetKeyName on the new name would fail.
20-Jun-1994 Danl
Added SERVICE_WIN32_INTERACTIVE support to service type.
21-Jan-1995 AnirudhS
RCreateServiceW: This was calling ScAccessValidate to check if the caller
had the desired access to the object on creation of a new service object.
Fixed this to just grant the desired access instead.
26-Feb-1996 AnirudhS
RChangeServiceConfigW: Would generate a tag only if the group name
changed. Fixed it to always generate and return a tag if one is
requested, just like RCreateServiceW.
09-Dec-1996 AnirudhS
Added SC_LOG printouts to help diagnose the annoying
ERROR_INVALID_PARAMETER and ERROR_INVALID_SERVICE_ACCOUNT return codes.
22-Oct-1997 JSchwart (after AnirudhS in _CAIRO_ 12-Apr-1995)
RChangeServiceConfigW and RCreateServiceW: Allow services that run under
accounts other than LocalSystem to share processes too.
--*/
//
// INCLUDES
//
#include "precomp.hxx"
#include <scconfig.h> // ScGetImageFileName().
#include <sclib.h> // ScImagePathsMatch(), ScIsValidImagePath(), etc.
#include <scsec.h> // ScAccessValidate().
#include <valid.h> // SERVICE_TYPE_INVALID(), etc.
#include <sccrypt.h> // ScDecryptPassword
#include "account.h" // ScValidateAndSaveAccount
#include <strarray.h> // ScWStrArraySize
#include <align.h> // ROUND_UP_COUNT
#include "smartp.h" // CHeapPtr
#include "resource.h" // IDS_SC_CONFIG_* constants
#define SC_NT_SYSTEM_ROOT L"\\SystemRoot\\"
#define SC_DOS_SYSTEM_ROOT L"%SystemRoot%\\"
#define SERVICE_TYPE_CHANGED 0x00000001
#define START_TYPE_CHANGED 0x00000002
#define ERROR_CONTROL_CHANGED 0x00000004
#define BINARY_PATH_CHANGED 0x00000008
#define REG_GROUP_CHANGED 0x00000010
#define TAG_ID_CHANGED 0x00000020
#define DEPENDENCIES_CHANGED_SR 0x00000040 // Not in NT4
#define DEPENDENCIES_CHANGED_REG 0x00000080 // Not in NT4
#define START_NAME_CHANGED 0x00000100
#define SR_GROUP_CHANGED 0x00000200
#define DISPLAY_NAME_CHANGED_SR 0x00000400
#define DISPLAY_NAME_CHANGED_REG 0x00000800
DWORD
ScCanonDriverImagePath(
IN DWORD DriverStartType,
IN LPWSTR DriverPath,
OUT LPWSTR *CanonDriverPath
);
DWORD
ScConvertToBootPathName(
LPWSTR FullQualPathName,
LPWSTR * RelativePathName
);
DWORD
ScValidateDisplayName(
LPWSTR lpDisplayName,
LPSERVICE_RECORD lpServiceRecord
);
BOOLEAN
ScIsArcName(
LPWSTR PathName
);
DWORD
RChangeServiceConfigW(
IN SC_RPC_HANDLE hService,
IN DWORD dwServiceType,
IN DWORD dwStartType,
IN DWORD dwErrorControl,
IN LPWSTR lpBinaryPathName,
IN LPWSTR lpLoadOrderGroup,
OUT LPDWORD lpdwTagId,
IN LPBYTE lpDependencies,
IN DWORD dwDependSize,
IN LPWSTR lpServiceStartName,
IN LPBYTE EncryptedPassword,
IN DWORD PasswordSize,
IN LPWSTR lpDisplayName
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
DWORD ApiStatus;
DWORD backoutStatus;
DWORD newServiceType;
LPSC_HANDLE_STRUCT serviceHandleStruct = (LPSC_HANDLE_STRUCT) hService;
HKEY ServiceNameKey = NULL;
LPSERVICE_RECORD serviceRecord;
LPWSTR CanonBinaryPath = NULL;
LPWSTR NewBinaryPath = NULL;
LPWSTR OldAccountName = NULL;
LPWSTR CanonAccountName = NULL;
DWORD CurrentServiceType = 0;
DWORD CurrentStartType = 0;
DWORD CurrentErrorControl = 0;
LPWSTR CurrentImageName = NULL;
LPWSTR CurrentDependencies = NULL;
LPWSTR CurrentDisplayName = NULL;
LPWSTR CurrentGroup = NULL;
LPWSTR CurrentStartName = NULL;
DWORD CurrentTag = 0;
DWORD Tag = 0;
DWORD Progress = 0;
LPWSTR Password = NULL;
LPWSTR OldSRDisplayName = NULL;
LPWSTR NewDisplayName = NULL;
if (ScShutdownInProgress) {
return(ERROR_SHUTDOWN_IN_PROGRESS);
}
//
// Check the handle.
//
if ( !ScIsValidServiceHandle( hService ) ) {
return(ERROR_INVALID_HANDLE);
}
SC_LOG(CONFIG_API, "RChangeServiceConfigW(%ws)\n",
serviceHandleStruct->Type.ScServiceObject.ServiceRecord->ServiceName);
//
// Do we have permission to do this?
//
if ( !RtlAreAllAccessesGranted(
serviceHandleStruct->AccessGranted,
SERVICE_CHANGE_CONFIG
)) {
return(ERROR_ACCESS_DENIED);
}
if (lpdwTagId != NULL) {
//
// Asking for new tag value but didn't specify group
//
if ((lpLoadOrderGroup == NULL) || (*lpLoadOrderGroup == 0)) {
SC_LOG0(ERROR, "Asking for new tag value but didn't specify group\n");
return(ERROR_INVALID_PARAMETER);
}
}
//
// Lock database, as we want to add stuff without other threads tripping
// on our feet. NOTE: since we may need the group list lock, we
// must get that lock first.
//
CGroupListExclusiveLock GLock;
CServiceListSharedLock LLock;
CServiceRecordExclusiveLock RLock;
//
// Find the service record for this handle.
//
serviceRecord =
serviceHandleStruct->Type.ScServiceObject.ServiceRecord;
SC_ASSERT( serviceRecord != NULL );
SC_ASSERT( serviceRecord->Signature == SERVICE_SIGNATURE );
//
// Disallow this call if record is marked for delete.
//
if (DELETE_FLAG_IS_SET(serviceRecord)) {
return(ERROR_SERVICE_MARKED_FOR_DELETE);
}
//
// If there is a DisplayName specified, check to see if it already
// exists.
//
ApiStatus = ScValidateDisplayName(lpDisplayName,serviceRecord);
if (ApiStatus != NO_ERROR) {
SC_LOG0(ERROR, "DisplayName invalid\n");
return(ApiStatus);
}
//
// Figure-out what the resulting service type will be.
// (Some other stuff below depends on this.)
//
if (dwServiceType != SERVICE_NO_CHANGE) {
if ( SERVICE_TYPE_INVALID( dwServiceType ) ) {
SC_LOG0(ERROR, "ServiceType invalid\n");
return(ERROR_INVALID_PARAMETER);
}
newServiceType = dwServiceType;
}
else {
newServiceType = serviceRecord->ServiceStatus.dwServiceType;
}
SC_ASSERT( newServiceType != SERVICE_NO_CHANGE );
//
// Validate other parameters.
//
ApiStatus = ScCheckServiceConfigParms(
TRUE, // This is a change operation
serviceRecord->ServiceName,
serviceRecord->ServiceStatus.dwServiceType, // new actual type
dwServiceType,
dwStartType,
dwErrorControl,
lpBinaryPathName,
lpLoadOrderGroup,
(LPWSTR)lpDependencies,
dwDependSize);
if (ApiStatus != NO_ERROR) {
return(ApiStatus);
}
//-----------------------------
//
// Begin Updating the Registry
//
//-----------------------------
ApiStatus = ScOpenServiceConfigKey(
serviceRecord->ServiceName,
KEY_WRITE | KEY_READ,
FALSE, // don't create if missing
& ServiceNameKey );
if (ApiStatus != NO_ERROR) {
return(ApiStatus);
}
//--------------------------------------
// (from here on we need to use Cleanup)
//
// Service Type
//--------------------------------------
if (dwServiceType != SERVICE_NO_CHANGE) {
//
// If this service is supposed to be interactive, make sure
// the service account is LocalSystem. Otherwise, it should
// fail with ERROR_INVALID_PARAMETER.
//
if (dwServiceType & SERVICE_INTERACTIVE_PROCESS) {
if (ARGUMENT_PRESENT(lpServiceStartName)) {
ApiStatus = ScCanonAccountName(lpServiceStartName, &CanonAccountName);
if (ApiStatus != NO_ERROR) {
goto Cleanup;
}
if (_wcsicmp(CanonAccountName,SC_LOCAL_SYSTEM_USER_NAME) != 0) {
SC_LOG0(ERROR, "Service must run in LocalSystem account to be interactive\n");
ApiStatus = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
}
else {
//
// Get old account name
//
ApiStatus = ScReadStartName(ServiceNameKey, &OldAccountName);
if (ApiStatus != NO_ERROR) {
goto Cleanup;
}
if (_wcsicmp(OldAccountName, SC_LOCAL_SYSTEM_USER_NAME) != 0) {
SC_LOG0(ERROR, "Service must run in LocalSystem account to be interactive\n");
ApiStatus = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
}
}
ApiStatus = ScReadServiceType( ServiceNameKey, &CurrentServiceType);
if (ApiStatus != NO_ERROR) {
goto Cleanup;
}
ApiStatus = ScWriteServiceType( ServiceNameKey, dwServiceType);
if (ApiStatus != NO_ERROR) {
goto Cleanup;
}
Progress |= SERVICE_TYPE_CHANGED;
}
else {
//
// ServiceType is not being changed.
//
CurrentServiceType = serviceRecord->ServiceStatus.dwServiceType;
//
// if the current service type contains the interactive bit, and the
// account type is being changed to something other than LocalSystem,
// then we should fail the call with ERROR_INVALID_PARAMETER.
//
if (ARGUMENT_PRESENT(lpServiceStartName)) {
if ((CurrentServiceType & SERVICE_INTERACTIVE_PROCESS) &&
(_wcsicmp(lpServiceStartName,SC_LOCAL_SYSTEM_USER_NAME) != 0)) {
SC_LOG0(ERROR, "Service must run in LocalSystem account to be interactive\n");
ApiStatus = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
}
}
//---------------------
// Start Type
//---------------------
if (dwStartType != SERVICE_NO_CHANGE) {
ApiStatus = ScReadStartType( ServiceNameKey, &CurrentStartType);
if (ApiStatus != NO_ERROR) {
goto Cleanup;
}
ApiStatus = ScWriteStartType( ServiceNameKey, dwStartType);
if (ApiStatus != NO_ERROR) {
goto Cleanup;
}
Progress |= START_TYPE_CHANGED;
//
// If they're supplying a new binary name, making it correct for the
// Start type will happen automatically. If they're keeping the
// same imagepath, we need to make sure it of the correct format,
// and if not, fix it.
//
if (lpBinaryPathName == NULL) {
//
// If the start type is changing from SERVICE_BOOT_START we need
// to turn the start path into a fully qualified NT name (BOOT
// drivers have paths relative to systemroot)
//
if (CurrentStartType == SERVICE_BOOT_START &&
dwStartType != SERVICE_BOOT_START) {
//
// Note: The following call allocates storage for the
// CurrentImageName
//
ApiStatus = ScGetImageFileName (
serviceRecord->ServiceName,
&CurrentImageName );
if (ApiStatus != NO_ERROR && ApiStatus != ERROR_PATH_NOT_FOUND) {
SC_LOG(ERROR,"RChangeServiceConfigW: ScGetImageFileName failed %d\n",
ApiStatus);
goto Cleanup;
}
//
// If there's an existing path, we need to fix
// If it is an ARC name, leave it alone
//
//
if (ApiStatus != ERROR_PATH_NOT_FOUND &&
!ScIsArcName(CurrentImageName)) {
//
// Prepend \systemroot\ to the beginning of the path
//
NewBinaryPath = (LPWSTR)LocalAlloc(LMEM_ZEROINIT,
(UINT) ((wcslen(CurrentImageName) +
wcslen(SC_NT_SYSTEM_ROOT) + 1) * sizeof(WCHAR)));
if (!NewBinaryPath) {
SC_LOG(ERROR,"RChangeServiceConfigW: LocalAlloc failed %d\n",
GetLastError());
ApiStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
wcscpy(NewBinaryPath, SC_NT_SYSTEM_ROOT);
wcscat(NewBinaryPath, CurrentImageName);
lpBinaryPathName = NewBinaryPath;
}
ApiStatus = NO_ERROR;
LocalFree(CurrentImageName);
}
//
// If the start type is changing to SERVICE_BOOT_START, we need
// to make sure the ImagePath gets canonicalized
//
else if (dwStartType == SERVICE_BOOT_START &&
CurrentStartType != SERVICE_BOOT_START) {
//
// Note: The following call allocates storage for the
// CurrentImageName
//
ApiStatus = ScGetImageFileName (
serviceRecord->ServiceName,
&CurrentImageName );
if (ApiStatus != NO_ERROR && ApiStatus != ERROR_PATH_NOT_FOUND) {
SC_LOG(ERROR,"RChangeServiceConfigW: ScGetImageFileName failed %d\n",
ApiStatus);
goto Cleanup;
}
//
// If there's an existing path and it's not an ARC name, we
// need to fix
//
if (ApiStatus != ERROR_PATH_NOT_FOUND &&
!ScIsArcName(CurrentImageName)) {
//
// Now make sure it's in the proper canonical form for a
// boot driver.
//
ApiStatus = ScConvertToBootPathName(
CurrentImageName,
&NewBinaryPath
);
if (ApiStatus != NO_ERROR) {
SC_LOG(ERROR, "ScConvertToBootPathName error %lu\n", ApiStatus);
goto Cleanup;
}
lpBinaryPathName = NewBinaryPath;
LocalFree(CurrentImageName);
}
ApiStatus = NO_ERROR;
}
}
}
//---------------------
// ErrorControl
//---------------------
if (dwErrorControl != SERVICE_NO_CHANGE) {
ApiStatus = ScReadErrorControl( ServiceNameKey, &CurrentErrorControl);
if (ApiStatus != NO_ERROR) {
goto Cleanup;
}
ApiStatus = ScWriteErrorControl( ServiceNameKey, dwErrorControl );
if (ApiStatus != NO_ERROR) {
goto Cleanup;
}
Progress |= ERROR_CONTROL_CHANGED;
}
//---------------------
// DisplayName
//---------------------
if (lpDisplayName != NULL) {
//
// UPDATE SERVICE RECORD
//
// We always update the display name in the service record - even
// if we delay in updating the rest of the config. If we don't
// do this, then we leave an opening where two services can end
// up with the same display name.
// The following scenerio can take place if we don't update
// the service record until the service is stopped:
//
// Until serviceA is stopped, the new display name only exists
// in the registry. In the mean time, another service (serviceB)
// can be given the same name. Name validation only looks in
// the service records for duplicate names. Then when serviceA is
// stopped, it takes on the new name which is the same as the
// display name for serviceB.
//
OldSRDisplayName = serviceRecord->DisplayName;
//
// If the display name is the same as the service name,
// then set the display name pointer to point to
// the service name.
//
if ((*lpDisplayName != L'\0') &&
(_wcsicmp(lpDisplayName,serviceRecord->ServiceName) != 0)) {
NewDisplayName = (LPWSTR)LocalAlloc(
LMEM_FIXED,
WCSSIZE(lpDisplayName));
if (NewDisplayName == NULL) {
SC_LOG0(ERROR,"RChangeServiceConfigW: LocalAlloc failed\n");
ApiStatus = ERROR_NOT_ENOUGH_MEMORY;
serviceRecord->DisplayName = OldSRDisplayName;
goto Cleanup;
}
//
// Copy the display name into new buffer, and free up memory
// for old name if necessary.
//
wcscpy(NewDisplayName, lpDisplayName);
}
else {
NewDisplayName = serviceRecord->ServiceName;
}
serviceRecord->DisplayName = NewDisplayName;
Progress |= DISPLAY_NAME_CHANGED_SR;
//
// UPDATE REGISTRY
//
ApiStatus = ScReadDisplayName(ServiceNameKey, &CurrentDisplayName);
if (ApiStatus != NO_ERROR) {
goto Cleanup;
}
ApiStatus = ScWriteDisplayName( ServiceNameKey, lpDisplayName);
if (ApiStatus != NO_ERROR) {
goto Cleanup;
}
Progress |= DISPLAY_NAME_CHANGED_REG;
}
//---------------------
// BinaryPathName
//---------------------
if (lpBinaryPathName != NULL) {
//
// Note: The following call allocates storage for the CurrentImageName
//
ApiStatus = ScGetImageFileName (
serviceRecord->ServiceName,
&CurrentImageName );
if (ApiStatus != NO_ERROR && ApiStatus != ERROR_PATH_NOT_FOUND) {
SC_LOG(ERROR,"RChangeServiceConfigW: ScGetImageFileName failed %d\n",
ApiStatus);
goto Cleanup;
}
if (CurrentServiceType & SERVICE_DRIVER) {
//
// Driver service
//
ApiStatus = ScCanonDriverImagePath(
dwStartType,
lpBinaryPathName,
&CanonBinaryPath
);
if (ApiStatus != NO_ERROR) {
SC_LOG(ERROR, "ScCanonDriverImagePath error %lu\n", ApiStatus);
goto Cleanup;
}
if (CurrentImageName == NULL ||
!ScImagePathsMatch(CanonBinaryPath, CurrentImageName)) {
ApiStatus = ScWriteImageFileName( ServiceNameKey, CanonBinaryPath);
if (ApiStatus != NO_ERROR) {
SC_LOG(ERROR,"RChangeServiceConfigW: ScWriteImageFileName "
"failed %d\n",ApiStatus);
goto Cleanup;
}
Progress |= BINARY_PATH_CHANGED;
}
}
else {
//
// Win32 service
//
if (CurrentImageName == NULL ||
!ScImagePathsMatch(lpBinaryPathName, CurrentImageName)) {
ApiStatus = ScWriteImageFileName( ServiceNameKey, lpBinaryPathName);
if (ApiStatus != NO_ERROR) {
SC_LOG(ERROR,"RChangeServiceConfigW: ScWriteImageFileName "
"failed %d\n",ApiStatus);
goto Cleanup;
}
Progress |= BINARY_PATH_CHANGED;
}
}
}
//---------------------
// Dependencies
//---------------------
if (lpDependencies != NULL) {
//
// Read the existing dependencies from registry so
// that we can restore it in case of failure later.
// We don't check for error here. We assume a failure means
// that there are no current dependencies.
//
ScReadDependencies(
ServiceNameKey,
&CurrentDependencies,
serviceRecord->ServiceName);
//
// Dynamically update the dependencies and check to make sure
// updating was error-free
//
ApiStatus = ScUpdateServiceRecordConfig(
serviceRecord,
SERVICE_NO_CHANGE,
SERVICE_NO_CHANGE,
SERVICE_NO_CHANGE,
NULL,
(LPBYTE)lpDependencies);
if (ApiStatus != NO_ERROR) {
goto Cleanup;
}
Progress |= DEPENDENCIES_CHANGED_SR;
//
// Update the dependencies in the registry
//
ApiStatus = ScWriteDependencies(
ServiceNameKey,
(LPWSTR) lpDependencies,
dwDependSize
);
if (ApiStatus != NO_ERROR) {
goto Cleanup;
}
Progress |= DEPENDENCIES_CHANGED_REG;
}
//---------------------
// Load order group
//---------------------
if (lpLoadOrderGroup != NULL) {
//
// Save existing group membership info so that we can restore
// it in case of error.
// Read the Current LoadOrderGroup value from Registry
//
if (ScAllocateAndReadConfigValue(
ServiceNameKey,
GROUP_VALUENAME_W,
&CurrentGroup,
NULL
) != NO_ERROR) {
CurrentGroup = NULL;
}
//
// Read current Tag Id to save in case of error.
//
CurrentTag = serviceRecord->Tag;
if ((CurrentGroup != NULL) &&
(_wcsicmp(lpLoadOrderGroup, CurrentGroup) == 0)) {
//
// The new load order group is the same as what is currently
// in the registry. This means that no group change is occuring.
//
if (lpdwTagId != NULL) {
//
// The caller requested a tag. If there isn't one, generate
// one and write it to the registry.
//
Tag = CurrentTag;
if (CurrentTag == 0) {
ScGetUniqueTag(
lpLoadOrderGroup,
&Tag
);
ApiStatus = ScWriteTag(ServiceNameKey, Tag);
if (ApiStatus != NO_ERROR) {
goto Cleanup;
}
//
// Update the service record with the tag id.
//
serviceRecord->Tag = Tag;
Progress |= TAG_ID_CHANGED;
}
}
}
else {
//
// The new load order group is different from what is currently
// stored in the registry. Save the new one in the registry.
//
ApiStatus = ScWriteGroupForThisService(
ServiceNameKey,
lpLoadOrderGroup);
if (ApiStatus != NO_ERROR) {
goto Cleanup;
}
//
// Also save it in the service controller database.
//
Progress |= REG_GROUP_CHANGED;
ScDeleteRegistryGroupPointer( serviceRecord);
ApiStatus = ScCreateRegistryGroupPointer(
serviceRecord,
lpLoadOrderGroup);
if (ApiStatus != NO_ERROR) {
goto Cleanup;
}
Progress |= SR_GROUP_CHANGED;
//
// Check to see if the LoadOrderGroup is being cleared
// (0 length string) or set to a new string. If there
// is a new string, we need to get a new unique Tag for it.
//
if (*lpLoadOrderGroup != 0) {
//
// We have a new LoadOrderGroup information. Get a unique
// Tag value.
//
if (lpdwTagId != NULL) {
ScGetUniqueTag(
lpLoadOrderGroup,
&Tag
);
}
}
//
// Write tag entry to registry if not 0. If 0, we delete the tag
// value from the registry.
//
if (Tag == 0) {
ScDeleteTag(ServiceNameKey);
}
else {
ApiStatus = ScWriteTag(ServiceNameKey, Tag);
}
if (ApiStatus != NO_ERROR) {
goto Cleanup;
}
//
// Update the service record with the tag id.
//
serviceRecord->Tag = Tag;
Progress |= TAG_ID_CHANGED;
}
}
//---------------------
// ServiceStartName
//---------------------
//
// If the service type is a DRIVER then we must interpret the
// lpServiceStartName as an NT driver object name and add it to
// the registry. If the type is WIN32, then lpServiceStartName
// must be an account name. This will be handled by
// ScUpdateServiceRecordConfig.
//
if ((newServiceType & SERVICE_DRIVER) &&
(ARGUMENT_PRESENT(lpServiceStartName))) {
//
// Read StartName to save in case of error.
//
ApiStatus = ScReadStartName( ServiceNameKey, &CurrentStartName);
if (ApiStatus != NO_ERROR) {
goto Cleanup;
}
//
// Write the driver objectname to the registry.
//
ApiStatus = ScWriteStartName(
ServiceNameKey,
lpServiceStartName
);
if (ApiStatus != NO_ERROR) {
goto Cleanup;
}
Progress |= START_NAME_CHANGED;
}
//==============================
// UPDATE Account Information
//==============================
if ((newServiceType & SERVICE_WIN32) &&
(ARGUMENT_PRESENT(lpServiceStartName) ||
ARGUMENT_PRESENT(EncryptedPassword))) {
//
// Changing the account.
//
//
// Get old account name.
// It may have already been retreived when we were handling
// ServiceType (above).
//
if (OldAccountName == NULL) {
ApiStatus = ScReadStartName(ServiceNameKey, &OldAccountName);
if (ApiStatus != NO_ERROR) {
goto Cleanup;
}
}
if (! ARGUMENT_PRESENT(lpServiceStartName)) {
//
// Account name is specified as the one saved in the registry
//
CanonAccountName = OldAccountName;
}
else {
//
// NOTE: We may have already obtained a CanonAccountName when we
// checked the INTERACTIVE service type.
//
if (CanonAccountName == NULL) {
ApiStatus = ScCanonAccountName(lpServiceStartName, &CanonAccountName);
if (ApiStatus != NO_ERROR) {
goto Cleanup;
}
}
}
//
// Decrypt the password. This function returns a pointer to
// the decrypted password that must be freed later.
//
if (ARGUMENT_PRESENT(EncryptedPassword)) {
ApiStatus = ScDecryptPassword(
hService,
EncryptedPassword,
PasswordSize,
&Password
);
if (ApiStatus != NO_ERROR) {
SC_LOG0(ERROR, "RChangeServiceConfigW: ScDecryptPassword failed\n");
goto Cleanup;
}
}
//
// NOTE: The following needs to be the last operation in the
// function. This is because there is no way to back out of this
// if something after it fails.
//
//
// Validate and update internal data structures for the new
// account, as well as write the new AccountName back to the
// registry if appropriate.
//
ApiStatus = ScValidateAndChangeAccount(
serviceRecord,
ServiceNameKey,
OldAccountName,
CanonAccountName,
Password
);
if (ApiStatus != NO_ERROR) {
SC_LOG(ERROR, "ScValidateAndChangeAccount error %lu\n", ApiStatus);
goto Cleanup;
}
}
//
// Update the service record with the new configuration if the
// service is stopped. If it is running, then set a flag to
// remind us to do it later. We don't update the dependencies
// here since it is done dynamically in RChangeServiceConfigW
//
if (serviceRecord->ServiceStatus.dwCurrentState == SERVICE_STOPPED) {
//
// Dependencies are NULL since they're updated dynamically
//
ApiStatus = ScUpdateServiceRecordConfig(
serviceRecord,
dwServiceType,
dwStartType,
dwErrorControl,
lpLoadOrderGroup,
NULL);
if (ApiStatus != NO_ERROR) {
goto Cleanup;
}
}
else {
//
// The service is running. Mark is so that we update the status
// when it stops.
//
SET_UPDATE_FLAG(serviceRecord);
}
Cleanup:
if (ApiStatus == NO_ERROR)
{
if (lpdwTagId != NULL)
{
*lpdwTagId = Tag;
}
if (Progress & DISPLAY_NAME_CHANGED_SR)
{
if (OldSRDisplayName != serviceRecord->ServiceName)
{
LocalFree(OldSRDisplayName);
}
}
}
else
{
//
// An error occured. Backout any changes that may have occured.
//
if (Progress & SERVICE_TYPE_CHANGED) {
backoutStatus = ScWriteServiceType( ServiceNameKey, CurrentServiceType);
if (backoutStatus != NO_ERROR)
{
ScLogControlEvent(NEVENT_SERVICE_CONFIG_BACKOUT_FAILED,
OldSRDisplayName ? OldSRDisplayName :
serviceRecord->DisplayName,
IDS_SC_CONFIG_SERVICE_TYPE);
}
}
if (Progress & START_TYPE_CHANGED) {
backoutStatus = ScWriteStartType( ServiceNameKey, CurrentStartType);
if (backoutStatus != NO_ERROR)
{
ScLogControlEvent(NEVENT_SERVICE_CONFIG_BACKOUT_FAILED,
OldSRDisplayName ? OldSRDisplayName :
serviceRecord->DisplayName,
IDS_SC_CONFIG_START_TYPE);
}
}
if (Progress & ERROR_CONTROL_CHANGED) {
backoutStatus = ScWriteErrorControl( ServiceNameKey, CurrentErrorControl);
if (backoutStatus != NO_ERROR)
{
ScLogControlEvent(NEVENT_SERVICE_CONFIG_BACKOUT_FAILED,
OldSRDisplayName ? OldSRDisplayName :
serviceRecord->DisplayName,
IDS_SC_CONFIG_ERROR_CONTROL);
}
}
if (Progress & DISPLAY_NAME_CHANGED_REG) {
if (CurrentDisplayName == NULL) {
backoutStatus = ScWriteDisplayName(
ServiceNameKey,
L"");
}
else {
backoutStatus = ScWriteDisplayName(
ServiceNameKey,
CurrentDisplayName);
}
if (backoutStatus != NO_ERROR)
{
ScLogControlEvent(NEVENT_SERVICE_CONFIG_BACKOUT_FAILED,
OldSRDisplayName ? OldSRDisplayName :
serviceRecord->DisplayName,
IDS_SC_CONFIG_DISPLAY_NAME);
}
}
if (Progress & DISPLAY_NAME_CHANGED_SR) {
if (serviceRecord->DisplayName != serviceRecord->ServiceName) {
LocalFree(serviceRecord->DisplayName);
serviceRecord->DisplayName = OldSRDisplayName;
}
}
if (Progress & BINARY_PATH_CHANGED) {
if (CurrentImageName == NULL) {
ScRegDeleteValue(ServiceNameKey, IMAGE_VALUENAME_W);
}
else {
backoutStatus = ScWriteImageFileName( ServiceNameKey, CurrentImageName);
if (backoutStatus != NO_ERROR)
{
ScLogControlEvent(NEVENT_SERVICE_CONFIG_BACKOUT_FAILED,
OldSRDisplayName ? OldSRDisplayName :
serviceRecord->DisplayName,
IDS_SC_CONFIG_BINARY_PATH);
}
}
}
if (Progress & DEPENDENCIES_CHANGED_REG) {
//
// ScWriteDependencies doesn't like NULL dependencies
//
LPWSTR lpTempDepend;
lpTempDepend = (CurrentDependencies ? CurrentDependencies : L"\0");
backoutStatus = ScWriteDependencies(
ServiceNameKey,
lpTempDepend,
ScWStrArraySize(lpTempDepend));
if (backoutStatus != NO_ERROR)
{
ScLogControlEvent(NEVENT_SERVICE_CONFIG_BACKOUT_FAILED,
OldSRDisplayName ? OldSRDisplayName :
serviceRecord->DisplayName,
IDS_SC_CONFIG_DEPENDENCIES);
}
}
if (Progress & DEPENDENCIES_CHANGED_SR) {
backoutStatus = ScUpdateServiceRecordConfig(
serviceRecord,
SERVICE_NO_CHANGE,
SERVICE_NO_CHANGE,
SERVICE_NO_CHANGE,
NULL,
(LPBYTE)CurrentDependencies);
if (backoutStatus != NO_ERROR)
{
ScLogControlEvent(NEVENT_SERVICE_CONFIG_BACKOUT_FAILED,
OldSRDisplayName ? OldSRDisplayName :
serviceRecord->DisplayName,
IDS_SC_CONFIG_DEPENDENCIES);
}
}
if (Progress & REG_GROUP_CHANGED) {
if (CurrentGroup != NULL) {
backoutStatus = ScWriteGroupForThisService(
ServiceNameKey, CurrentGroup);
if (backoutStatus != NO_ERROR)
{
ScLogControlEvent(NEVENT_SERVICE_CONFIG_BACKOUT_FAILED,
OldSRDisplayName ? OldSRDisplayName :
serviceRecord->DisplayName,
IDS_SC_CONFIG_GROUP);
}
}
else {
ScRegDeleteValue(ServiceNameKey, GROUP_VALUENAME_W);
}
}
if (Progress & SR_GROUP_CHANGED) {
ScDeleteRegistryGroupPointer( serviceRecord);
backoutStatus = ScCreateRegistryGroupPointer( serviceRecord, CurrentGroup);
if (backoutStatus != NO_ERROR)
{
ScLogControlEvent(NEVENT_SERVICE_CONFIG_BACKOUT_FAILED,
OldSRDisplayName ? OldSRDisplayName :
serviceRecord->DisplayName,
IDS_SC_CONFIG_GROUP);
}
}
if (Progress & TAG_ID_CHANGED)
{
if (CurrentTag == 0)
{
ScDeleteTag(ServiceNameKey);
}
else
{
backoutStatus = ScWriteTag( ServiceNameKey, CurrentTag);
if (backoutStatus != NO_ERROR)
{
ScLogControlEvent(NEVENT_SERVICE_CONFIG_BACKOUT_FAILED,
OldSRDisplayName ? OldSRDisplayName :
serviceRecord->DisplayName,
IDS_SC_CONFIG_TAG);
}
}
serviceRecord->Tag = CurrentTag;
}
if (Progress & START_NAME_CHANGED)
{
backoutStatus = ScWriteStartName( ServiceNameKey, CurrentStartName);
if (backoutStatus != NO_ERROR)
{
ScLogControlEvent(NEVENT_SERVICE_CONFIG_BACKOUT_FAILED,
OldSRDisplayName ? OldSRDisplayName :
serviceRecord->DisplayName,
IDS_SC_CONFIG_ACCOUNT);
}
}
}
if (CanonAccountName == OldAccountName) {
LocalFree(OldAccountName);
}
else {
LocalFree(OldAccountName);
LocalFree(CanonAccountName);
}
//
// Free memory resources
//
LocalFree(CurrentDisplayName);
LocalFree(CurrentImageName);
LocalFree(CurrentDependencies);
LocalFree(CurrentGroup);
LocalFree(CurrentStartName);
LocalFree(CanonBinaryPath);
LocalFree(NewBinaryPath);
LocalFree(Password);
if (ServiceNameKey != NULL) {
(VOID) ScRegFlushKey(ServiceNameKey);
(VOID) ScRegCloseKey( ServiceNameKey );
}
SC_LOG1( CONFIG_API, "RChangeServiceConfigW returning status " FORMAT_DWORD
".\n", ApiStatus );
return (ApiStatus);
}
DWORD
RCreateServiceW(
IN SC_RPC_HANDLE hSCManager,
IN LPWSTR lpServiceName,
IN LPWSTR lpDisplayName,
IN DWORD dwDesiredAccess,
IN DWORD dwServiceType,
IN DWORD dwStartType,
IN DWORD dwErrorControl,
IN LPWSTR lpBinaryPathName,
IN LPWSTR lpLoadOrderGroup,
OUT LPDWORD lpdwTagId OPTIONAL,
IN LPBYTE lpDependencies OPTIONAL,
IN DWORD dwDependSize,
IN LPWSTR lpServiceStartName OPTIONAL,
IN LPBYTE EncryptedPassword OPTIONAL,
IN DWORD PasswordSize,
IN OUT LPSC_RPC_HANDLE lpServiceHandle
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
DWORD ApiStatus;
LPSERVICE_RECORD newServiceRecord = NULL;
LPSERVICE_RECORD oldServiceRecord;
LPSC_HANDLE_STRUCT serviceHandleStruct = NULL;
HKEY serviceKey = NULL;
CHeapPtr<LPWSTR> Password;
CHeapPtr<LPWSTR> CanonAccountName;
CHeapPtr<LPWSTR> CanonBinaryPath;
DWORD Tag = 0;
SC_LOG1( CONFIG_API, "In RCreateServiceW - creating %ws\n",lpServiceName);
if (ScShutdownInProgress) {
return(ERROR_SHUTDOWN_IN_PROGRESS);
}
if ( !ScIsValidScManagerHandle( hSCManager ) ) {
ApiStatus = ERROR_INVALID_HANDLE;
return(ApiStatus);
}
if ( !RtlAreAllAccessesGranted(
((LPSC_HANDLE_STRUCT)hSCManager)->AccessGranted,
SC_MANAGER_CREATE_SERVICE
)) {
ApiStatus = ERROR_ACCESS_DENIED;
return(ApiStatus);
}
if ( !ScIsValidServiceName( lpServiceName ) ) {
ApiStatus = ERROR_INVALID_NAME;
return(ApiStatus);
}
//
// Validate other parameters.
//
ApiStatus = ScCheckServiceConfigParms(
FALSE, // no, this is not a change operation
lpServiceName,
dwServiceType, // new actual type = same one app gave us.
dwServiceType,
dwStartType,
dwErrorControl,
lpBinaryPathName,
lpLoadOrderGroup,
(LPWSTR)lpDependencies,
dwDependSize);
if (ApiStatus != NO_ERROR) {
return(ApiStatus);
}
if (lpdwTagId != NULL) {
//
// Asking for tag value but didn't specify group
//
if ((lpLoadOrderGroup == NULL) ||
((lpLoadOrderGroup != NULL) && (*lpLoadOrderGroup == 0))){
SC_LOG0(ERROR, "Asking for tag value but didn't specify group\n");
ApiStatus = ERROR_INVALID_PARAMETER;
return(ApiStatus);
}
}
if (dwServiceType & SERVICE_WIN32) {
//
// Canonicalize the StartName if it is an account name.
//
LPWSTR AccountName = SC_LOCAL_SYSTEM_USER_NAME; // Default
if (ARGUMENT_PRESENT(lpServiceStartName)) {
AccountName = lpServiceStartName;
}
ApiStatus = ScCanonAccountName(
AccountName,
&CanonAccountName
);
if (ApiStatus != NO_ERROR) {
return(ApiStatus);
}
//
// The services that are expected to be interactive MUST
// run in the LocalSystem account only.
//
if ((dwServiceType & SERVICE_INTERACTIVE_PROCESS) &&
(_wcsicmp(CanonAccountName,SC_LOCAL_SYSTEM_USER_NAME) != 0)) {
SC_LOG0(ERROR, "Service must run in LocalSystem account to be interactive\n");
ApiStatus = ERROR_INVALID_PARAMETER;
return(ApiStatus);
}
}
else if (dwServiceType & SERVICE_DRIVER) {
//
// Canonicalize the path name for drivers
//
ApiStatus = ScCanonDriverImagePath(
dwStartType,
lpBinaryPathName,
&CanonBinaryPath
);
if (ApiStatus != NO_ERROR) {
SC_LOG(ERROR, "ScCanonDriverImagePath error %lu\n", ApiStatus);
return(ApiStatus);
}
}
if (lpServiceHandle == NULL) {
SC_LOG0(ERROR, "Null lpServiceHandle\n");
ApiStatus = ERROR_INVALID_PARAMETER;
return(ApiStatus);
}
//
// Lock database, as we want to add stuff without other threads tripping
// on our feet. NOTE: since we may need the group list lock, we must
// get that lock first.
//
CGroupListExclusiveLock GLock;
CServiceListExclusiveLock LLock;
CServiceRecordExclusiveLock RLock;
//
// Look for unique tag
//
if (lpdwTagId != NULL) {
ScGetUniqueTag(
lpLoadOrderGroup,
&Tag
);
}
//
// Look for an existing service.
//
SC_LOG0( CONFIG_API, "RCreateServiceW: See if Service already exists\n");
ApiStatus = ScGetNamedServiceRecord (
lpServiceName,
& oldServiceRecord );
if (ApiStatus == NO_ERROR) {
if (DELETE_FLAG_IS_SET(oldServiceRecord)) {
ApiStatus = ERROR_SERVICE_MARKED_FOR_DELETE;
}
else {
ApiStatus = ERROR_SERVICE_EXISTS;
}
goto Cleanup;
} else if (ApiStatus != ERROR_SERVICE_DOES_NOT_EXIST) {
goto Cleanup;
}
//
// If there is a DisplayName specified, check to see if it already
// exists.
//
ApiStatus = ScValidateDisplayName(lpDisplayName,NULL);
if (ApiStatus != NO_ERROR) {
goto Cleanup;
}
//
// Allocate new service record. Put the service name in the record too.
//
SC_LOG0( CONFIG_API, "RCreateServiceW: Create a new service record\n");
ApiStatus = ScCreateServiceRecord(
lpServiceName,
& newServiceRecord );
if (ApiStatus != NO_ERROR) {
goto Cleanup;
}
SC_LOG0( CONFIG_API, "RCreateServiceW: Add Config Info to Service Record\n");
ApiStatus = ScAddConfigInfoServiceRecord(
newServiceRecord,
dwServiceType,
dwStartType,
dwErrorControl,
lpLoadOrderGroup,
Tag,
(LPWSTR) lpDependencies,
lpDisplayName,
NULL // Create new security descriptor
);
if (ApiStatus != NO_ERROR) {
SC_LOG(ERROR, "ScAddConfigInfoServiceRecord error %lu\n", ApiStatus);
goto Cleanup;
}
SC_LOG0( CONFIG_API, "RCreateServiceW: Set dependency pointers\n");
ApiStatus = ScSetDependencyPointers(
newServiceRecord
);
if (ApiStatus != NO_ERROR) {
goto Cleanup;
}
//--------------------------------------------
// Create a key in registry for this service.
//--------------------------------------------
SC_LOG0( CONFIG_API, "RCreateServiceW: Open Registry Key for service\n");
ApiStatus = ScOpenServiceConfigKey(
lpServiceName,
KEY_READ | KEY_WRITE, // desired access
TRUE, // create if it doesn't exist
& serviceKey );
if (ApiStatus != NO_ERROR) {
SC_LOG1( CONFIG_API, "RCreateServiceW: ScOpenServiceConfigKey failed %d\n",
ApiStatus);
goto Cleanup;
}
//
// Write stuff to the registry (except user name and password).
//
SC_LOG0( CONFIG_API, "RCreateServiceW: Write RegistryInfo\n");
ApiStatus = ScWriteServiceType( serviceKey, dwServiceType );
if (ApiStatus != NO_ERROR) {
goto Cleanup;
}
ApiStatus = ScWriteStartType( serviceKey, dwStartType );
if (ApiStatus != NO_ERROR) {
goto Cleanup;
}
ApiStatus = ScWriteErrorControl( serviceKey, dwErrorControl );
if (ApiStatus != NO_ERROR) {
goto Cleanup;
}
if (lpdwTagId != NULL) {
ApiStatus = ScWriteTag( serviceKey, Tag );
if (ApiStatus != NO_ERROR) {
goto Cleanup;
}
}
if (dwServiceType & SERVICE_WIN32) {
ApiStatus = ScWriteImageFileName( serviceKey, lpBinaryPathName );
}
else if (dwServiceType & SERVICE_DRIVER) {
ApiStatus = ScWriteImageFileName( serviceKey, CanonBinaryPath );
}
if (ApiStatus != NO_ERROR) {
goto Cleanup;
}
ScWriteDisplayName(serviceKey, lpDisplayName);
if ( (lpLoadOrderGroup != NULL) && ( (*lpLoadOrderGroup) != L'\0' ) ) {
ApiStatus = ScWriteGroupForThisService( serviceKey, lpLoadOrderGroup );
if (ApiStatus != NO_ERROR) {
goto Cleanup;
}
}
if ( (lpDependencies != NULL) && ( (*lpDependencies) != L'\0' ) ) {
ApiStatus = ScWriteDependencies(
serviceKey,
(LPWSTR) lpDependencies,
dwDependSize );
if (ApiStatus != NO_ERROR) {
goto Cleanup;
}
}
if (newServiceRecord != NULL && newServiceRecord->ServiceSd != NULL) {
ApiStatus = ScWriteSd(
serviceKey,
newServiceRecord->ServiceSd
);
if (ApiStatus != NO_ERROR) {
goto Cleanup;
}
}
//
// Create a handle which caller can use.
//
SC_LOG0( CONFIG_API, "RCreateServiceW: Create Service Handle\n");
ApiStatus = ScCreateServiceHandle( newServiceRecord,
& serviceHandleStruct );
if (ApiStatus != NO_ERROR) {
goto Cleanup;
}
//
// Give the handle the access that the caller requested.
//
ApiStatus = ScGrantAccess(
serviceHandleStruct,
dwDesiredAccess);
if (ApiStatus != NO_ERROR) {
goto Cleanup;
}
//
// Resolve outstanding dependency to this service.
//
SC_LOG0( CONFIG_API, "RCreateServiceW: Resolve Dependencies\n");
ApiStatus = ScResolveDependencyToService(newServiceRecord);
if (ApiStatus != NO_ERROR) {
goto Cleanup;
}
//
// Decrypt the password. This function returns a pointer to
// the decrypted password that must be freed later.
//
if (ARGUMENT_PRESENT(EncryptedPassword)) {
ApiStatus = ScDecryptPassword(
hSCManager,
EncryptedPassword,
PasswordSize,
&Password
);
if (ApiStatus != NO_ERROR) {
SC_LOG0(ERROR, "RCreateServiceW: ScDecryptPassword failed\n");
goto Cleanup;
}
}
//
// The LAST thing we must do (which might fail) is call
// ScValidateAndSaveAccount(). This must be last because there is no
// way to undo this routine's actions.
//
if (dwServiceType & SERVICE_WIN32) {
SC_LOG0( CONFIG_API, "RCreateServiceW: Validate and save account\n");
ApiStatus = ScValidateAndSaveAccount(
lpServiceName,
serviceKey,
CanonAccountName,
Password
);
if (ApiStatus != NO_ERROR) {
SC_LOG(ERROR, "ScValidateAndSaveAccount error %lu\n", ApiStatus);
goto Cleanup;
}
}
else if ((dwServiceType & SERVICE_DRIVER) &&
(ARGUMENT_PRESENT(lpServiceStartName))) {
SC_LOG0( CONFIG_API, "RCreateServiceW: Write Driver ObjectName to "
"registry\n");
//
// Write the driver objectname to the registry.
//
ApiStatus = ScWriteStartName(
serviceKey,
lpServiceStartName
);
if (ApiStatus != NO_ERROR) {
goto Cleanup;
}
}
SC_LOG0( CONFIG_API, "RCreateServiceW: Done - No Errors\n");
ApiStatus = NO_ERROR;
Cleanup:
if (serviceKey != NULL) {
(VOID) ScRegFlushKey( serviceKey);
(VOID) ScRegCloseKey( serviceKey );
}
if (ApiStatus == NO_ERROR) {
newServiceRecord->UseCount = 1;
*lpServiceHandle = (SC_RPC_HANDLE) serviceHandleStruct;
SC_LOG2(USECOUNT, "CreateService: " FORMAT_LPWSTR
" increment USECOUNT=%lu\n", newServiceRecord->ServiceName, newServiceRecord->UseCount);
if (lpdwTagId != NULL) {
*lpdwTagId = Tag;
}
}
else {
if (newServiceRecord != NULL) {
//
// Delete partially created service record.
//
SET_DELETE_FLAG(newServiceRecord);
newServiceRecord->UseCount = 1;
SC_LOG2(USECOUNT, "CreateService: " FORMAT_LPWSTR
" increment USECOUNT=%lu\n", newServiceRecord->ServiceName, newServiceRecord->UseCount);
//
// ScDecrementUseCountAndDelete deletes the service record
// and the registry entry for that service.
//
ScDecrementUseCountAndDelete(newServiceRecord);
newServiceRecord = NULL;
}
LocalFree(serviceHandleStruct);
if (lpServiceHandle != NULL) {
*lpServiceHandle = NULL;
}
}
SC_LOG2( CONFIG_API, "RCreateServiceW returning status " FORMAT_DWORD
" and handle " FORMAT_LPVOID ".\n", ApiStatus,
(LPVOID) serviceHandleStruct );
return(ApiStatus);
}
DWORD
RDeleteService(
IN SC_RPC_HANDLE hService
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
NTSTATUS status;
UNICODE_STRING Subsystem;
ULONG privileges[1];
DWORD ApiStatus;
LPSC_HANDLE_STRUCT serviceHandleStruct = (LPSC_HANDLE_STRUCT) hService;
LPSERVICE_RECORD serviceRecord;
if (ScShutdownInProgress) {
return(ERROR_SHUTDOWN_IN_PROGRESS);
}
//
// Check the handle.
//
if ( !ScIsValidServiceHandle( hService ) ) {
return(ERROR_INVALID_HANDLE);
}
//
// Do we have permission to do this?
//
if ( !RtlAreAllAccessesGranted(
serviceHandleStruct->AccessGranted,
DELETE
)) {
return(ERROR_ACCESS_DENIED);
}
//
// Lock database, as we want to change stuff without other threads tripping
// on our feet.
//
CServiceRecordExclusiveLock RLock;
//
// Find the service record
//
serviceRecord = serviceHandleStruct->Type.ScServiceObject.ServiceRecord;
SC_ASSERT( serviceRecord != NULL );
SC_ASSERT( serviceRecord->Signature == SERVICE_SIGNATURE );
//
// Check if marked for deletion (by another call to this API).
//
if (DELETE_FLAG_IS_SET(serviceRecord)) {
ApiStatus = ERROR_SERVICE_MARKED_FOR_DELETE;
goto Cleanup;
}
//
// Mark the service for deletion.
// It will actually be deleted when the last handle to it is closed.
// NOTE: Since the service itself owns a handle, the deletion is
// not complete until the service has stopped.
//
SET_DELETE_FLAG(serviceRecord);
//
// Set the start type to disabled. If we're deleting a driver that's
// started by the system, we don't want the system to start it on the
// next boot since we're going to remove the key from the registry
//
serviceRecord->StartType = SERVICE_DISABLED;
//
// Mark the registry entry for this service for deletion. If we notice
// this as being present when we go throught our initialization routine
// (during boot), the service entry in the registry will be deleted.
//
ScMarkForDelete(serviceRecord);
//
// Get Audit Privilege
//
privileges[0] = SE_AUDIT_PRIVILEGE;
status = ScGetPrivilege( 1, privileges);
if (!NT_SUCCESS(status)) {
SC_LOG1(ERROR, "RDeleteService: ScGetPrivilege (Enable) failed %#lx\n",
status);
}
//
// Generate the Audit.
//
RtlInitUnicodeString(&Subsystem, SC_MANAGER_AUDIT_NAME);
status = NtDeleteObjectAuditAlarm(
&Subsystem,
hService,
(BOOLEAN)((serviceHandleStruct->Flags
& SC_HANDLE_GENERATE_ON_CLOSE) != 0));
if (!NT_SUCCESS(status)) {
SC_LOG1(ERROR, "RDeleteService: NtDeleteObjectAuditAlarm failed %#lx\n",
status);
}
ScReleasePrivilege();
ApiStatus = NO_ERROR;
Cleanup:
SC_LOG2( CONFIG_API, "RDeleteService(%ws) returning status " FORMAT_DWORD
".\n", serviceRecord->ServiceName, ApiStatus );
return (ApiStatus);
}
DWORD
RQueryServiceConfigW(
IN SC_RPC_HANDLE hService,
OUT LPQUERY_SERVICE_CONFIGW lpServiceConfig,
IN DWORD cbBufSize,
OUT LPDWORD pcbBytesNeeded
)
/*++
Routine Description:
This function returns the service configuration information that
is currently stored in the registry.
NOTE:
When a service is running and its configuration is changed, the
change only affects the registry information as long as the service
is running. During this period (while the service is running), it is
not possible to obtain the configuration of the running service.
All that can be obtained is the configuration stored in the registry.
This is the configuration that the service will have the next time
it is run. Stopping a service causes it to get its configuration
information refreshed from the registry.
Arguments:
Return Value:
--*/
{
DWORD ApiStatus;
DWORD bytesNeeded = sizeof(QUERY_SERVICE_CONFIG); // (initial)
LPWSTR endOfVariableData;
LPWSTR fixedDataEnd;
LPSC_HANDLE_STRUCT serviceHandleStruct = (LPSC_HANDLE_STRUCT) hService;
LPSERVICE_RECORD serviceRecord = NULL;
HKEY ServiceNameKey = (HKEY) NULL;
DWORD bufSize;
DWORD dependSize=0;
LPWSTR CurrentImagePathName = NULL;
LPWSTR CurrentDependencies = NULL;
LPWSTR CurrentGroup = NULL;
LPWSTR CurrentStartName = NULL;
LPWSTR pDependString;
LPWSTR CurrentDisplayName = NULL;
LPWSTR ConvertImageName;
if (ScShutdownInProgress) {
return(ERROR_SHUTDOWN_IN_PROGRESS);
}
//
// Check the handle.
//
if ( !ScIsValidServiceHandle( hService ) ) {
return(ERROR_INVALID_HANDLE);
}
//
// Check other caller parms (except buffer too small, we need more info).
//
if (lpServiceConfig == NULL) {
return(ERROR_INVALID_PARAMETER);
} else if (pcbBytesNeeded == NULL) {
return(ERROR_INVALID_PARAMETER);
}
//
// Do we have permission to do this?
//
if ( !RtlAreAllAccessesGranted(
serviceHandleStruct->AccessGranted,
SERVICE_QUERY_CONFIG
)) {
return(ERROR_ACCESS_DENIED);
}
//
// Lock database, as we want to look at stuff without other threads changing
// fields at the same time.
//
CServiceRecordSharedLock RLock;
serviceRecord = serviceHandleStruct->Type.ScServiceObject.ServiceRecord;
SC_ASSERT( serviceRecord != NULL );
//
// Open the service name key.
//
ApiStatus = ScOpenServiceConfigKey(
serviceRecord->ServiceName,
KEY_READ,
FALSE, // Create if missing
&ServiceNameKey
);
if (ApiStatus != NO_ERROR) {
goto Cleanup;
}
//--------------------------------
// Get the BinaryPathName
//--------------------------------
SC_ASSERT( serviceRecord->ServiceName != NULL );
ApiStatus = ScGetImageFileName (
serviceRecord->ServiceName,
& CurrentImagePathName ); // alloc and set ptr.
if (ApiStatus != NO_ERROR) {
CurrentImagePathName = NULL;
}
ApiStatus = ScReadConfigFromReg(
serviceRecord,
&lpServiceConfig->dwServiceType,
&lpServiceConfig->dwStartType,
&lpServiceConfig->dwErrorControl,
&lpServiceConfig->dwTagId,
&CurrentDependencies,
&CurrentGroup,
&CurrentDisplayName);
if (ApiStatus != NO_ERROR) {
goto Cleanup;
}
//
// If the starttype is SERVICE_BOOT_START the name is a relative path
// from \SystemRoot. Fix it to be a fully qualified name, unless it is
// an ARC name, then leave it alone.
//
if (CurrentImagePathName &&
lpServiceConfig->dwStartType == SERVICE_BOOT_START &&
!ScIsArcName(CurrentImagePathName)) {
ConvertImageName = (LPWSTR) LocalAlloc(LMEM_ZEROINIT,
(UINT) ((wcslen(CurrentImagePathName) +
wcslen(SC_NT_SYSTEM_ROOT) + 1) * sizeof(WCHAR)));
if (ConvertImageName == NULL) {
SC_LOG1(ERROR, "RQueryServiceConfigW: LocalAlloc failed %lu\n",
GetLastError());
ApiStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
wcscpy(ConvertImageName, SC_NT_SYSTEM_ROOT);
wcscat(ConvertImageName, CurrentImagePathName);
LocalFree(CurrentImagePathName);
CurrentImagePathName = ConvertImageName;
}
//--------------------------------
// Get StartName
//--------------------------------
ApiStatus = ScReadStartName( ServiceNameKey, &CurrentStartName);
if (ApiStatus != NO_ERROR) {
SC_LOG1(TRACE,"StartName for %ws service does not exist\n",
serviceRecord->ServiceName);
CurrentStartName = NULL;
}
//
// Figure-out how much space we'll need for the record.
// We've already got the initial (fixed) size in bytesNeeded...
//
SC_ASSERT( bytesNeeded == sizeof(QUERY_SERVICE_CONFIG) );
//
// Add on some extra for RPC byte count alignment.
// NOTE: We don't try and solve any exact alignment problem because
// RPC will choose a random order in which to load strings into
// the buffer.
//
bytesNeeded += sizeof(ULONG_PTR) - 1; // extra for RPC alignment
if (CurrentImagePathName != NULL)
{
bytesNeeded += (DWORD) WCSSIZE(CurrentImagePathName);
}
else {
bytesNeeded += sizeof(WCHAR);
}
bytesNeeded += sizeof(ULONG_PTR) - 1; // extra for RPC alignment
//
// If the display name is not stored in the registry, the
// Service Name is returned instead.
//
if (CurrentDisplayName == NULL) {
CurrentDisplayName = serviceRecord->ServiceName;
}
bytesNeeded += (DWORD) WCSSIZE(CurrentDisplayName);
//
// Add on some extra for RPC byte count alignment.
//
bytesNeeded += sizeof(ULONG_PTR) - 1; // extra for RPC alignment
if (CurrentGroup != NULL) {
bytesNeeded += (DWORD) WCSSIZE(CurrentGroup);
}
else {
bytesNeeded += sizeof(WCHAR);
}
bytesNeeded += sizeof(ULONG_PTR) - 1; // extra for RPC alignment
if (CurrentDependencies != NULL) {
dependSize = ScWStrArraySize(CurrentDependencies);
bytesNeeded += dependSize;
}
else {
bytesNeeded += (2 * sizeof(WCHAR));
}
bytesNeeded += sizeof(ULONG_PTR) - 1; // extra for RPC alignment
if (CurrentStartName != NULL) {
bytesNeeded += (DWORD) WCSSIZE(CurrentStartName);
}
else {
bytesNeeded += sizeof(WCHAR);
}
bytesNeeded = ROUND_UP_COUNT(bytesNeeded, ALIGN_WCHAR);
//
// Make sure app gave us enough space.
//
if (bytesNeeded > cbBufSize) {
*pcbBytesNeeded = bytesNeeded;
ApiStatus = ERROR_INSUFFICIENT_BUFFER;
goto Cleanup;
}
//========================
// Fill in the strings.
//========================
fixedDataEnd = (LPWSTR) (lpServiceConfig + 1);
endOfVariableData = (LPWSTR) ((LPBYTE)lpServiceConfig + bytesNeeded);
bufSize = 0;
if (CurrentImagePathName != NULL) {
bufSize = (DWORD) wcslen( CurrentImagePathName );
}
if ( !ScCopyStringToBufferW (
CurrentImagePathName,
bufSize,
fixedDataEnd,
& endOfVariableData,
& lpServiceConfig->lpBinaryPathName,
NULL
) ) {
SC_LOG0(ERROR,
"RQueryServiceConfigW:ScCopyStringtoBufferW "
"(BinaryPathName)Failed\n");
SC_ASSERT( FALSE );
ApiStatus = ERROR_INSUFFICIENT_BUFFER;
goto Cleanup;
}
bufSize = 0;
if (CurrentGroup != NULL) {
bufSize = (DWORD) wcslen( CurrentGroup );
}
if ( !ScCopyStringToBufferW (
CurrentGroup,
bufSize,
fixedDataEnd,
&endOfVariableData,
&lpServiceConfig->lpLoadOrderGroup,
NULL
) ) {
SC_LOG0(ERROR,
"RQueryServiceConfigW:ScCopyStringtoBufferW "
"(LoadOrderGroup)Failed\n");
SC_ASSERT( FALSE );
ApiStatus = ERROR_INSUFFICIENT_BUFFER;
goto Cleanup;
}
//
// Dependencies
//
if ((CurrentDependencies == NULL) || (dependSize == 0)) {
//
// There are no dependencies so put in a double null terminated
// null string.
//
// NOTE: The seperator character '/' is inserted to allow the
// double NULL terminated string to make it across RPC.
// This is removed by the client side.
//
lpServiceConfig->lpDependencies = endOfVariableData - 2;
endOfVariableData = lpServiceConfig->lpDependencies;
lpServiceConfig->lpDependencies[0] = L'/';
lpServiceConfig->lpDependencies[1] = L'\0';
}
else {
lpServiceConfig->lpDependencies = (LPWSTR)((LPBYTE)endOfVariableData - dependSize);
pDependString = lpServiceConfig->lpDependencies;
endOfVariableData = pDependString;
RtlCopyMemory(lpServiceConfig->lpDependencies, CurrentDependencies, dependSize);
//
// Add seperator characters.
//
while ((bufSize = (DWORD) wcslen(pDependString)) != 0) {
pDependString += bufSize;
*pDependString = L'/';
pDependString++;
}
}
//
// StartName
//
bufSize = 0;
if (CurrentStartName != NULL) {
bufSize = (DWORD) wcslen(CurrentStartName);
}
if ( !ScCopyStringToBufferW (
CurrentStartName,
bufSize,
fixedDataEnd,
& endOfVariableData,
& lpServiceConfig->lpServiceStartName,
NULL
) ) {
SC_LOG0(ERROR,
"RQueryServiceConfigW:ScCopyStringtoBufferW (StartName)Failed\n");
SC_ASSERT( FALSE );
ApiStatus = ERROR_INSUFFICIENT_BUFFER;
goto Cleanup;
}
//
// DisplayName
//
bufSize = 0;
SC_ASSERT(CurrentDisplayName);
bufSize = (DWORD) wcslen(CurrentDisplayName);
if ( !ScCopyStringToBufferW (
CurrentDisplayName,
bufSize,
fixedDataEnd,
& endOfVariableData,
& lpServiceConfig->lpDisplayName,
NULL
) ) {
SC_LOG0(ERROR,
"RQueryServiceConfigW:ScCopyStringtoBufferW (DisplayName)Failed\n");
SC_ASSERT( FALSE );
ApiStatus = ERROR_INSUFFICIENT_BUFFER;
goto Cleanup;
}
//
// That's all, folks! --JR
//
ApiStatus = NO_ERROR;
Cleanup:
if (ServiceNameKey != (HKEY) NULL) {
ScRegCloseKey(ServiceNameKey);
}
LocalFree(CurrentStartName);
LocalFree(CurrentImagePathName);
if (serviceRecord != NULL &&
CurrentDisplayName != serviceRecord->DisplayName) {
(VOID) LocalFree( CurrentDisplayName );
}
LocalFree( CurrentGroup );
LocalFree( CurrentDependencies );
if (pcbBytesNeeded != NULL) {
*pcbBytesNeeded = bytesNeeded;
}
SC_LOG2( CONFIG_API, "RQueryServiceConfigW returning status " FORMAT_DWORD
" and size needed " FORMAT_DWORD ".\n", ApiStatus, bytesNeeded );
//
// If an error occurs, we set the pointers in the structure to NULL.
// This is for RPC. Since we are using the byte_count feature, the
// server marshalling code must look at the pointers. Otherwise, it
// doesn't know how to marshall the strings.
//
if (ApiStatus != NO_ERROR) {
lpServiceConfig->lpBinaryPathName = NULL;
lpServiceConfig->lpLoadOrderGroup = NULL;
lpServiceConfig->lpDependencies = NULL;
lpServiceConfig->lpServiceStartName = NULL;
lpServiceConfig->lpDisplayName = NULL;
}
return (ApiStatus);
}
DWORD
ScCanonDriverImagePath(
IN DWORD DriverStartType,
IN LPWSTR DriverPath,
OUT LPWSTR *CanonDriverPath
)
/*++
Routine Description:
This function converts the user specified DOS path name to the driver
binary file into an NT path format understood by NtLoadDriver.
Examples:
C:\nt\system32\file.sys -> \DosDevices\c:\nt\system323\file.sys
%SystemRoot%\system32\drivers\file.sys -> \SystemRoot\system32\driver\file.sys
Arguments:
DriverPath - User specified DOS path name to driver .SYS file.
CanonDriverPath - Receives a pointer to a buffer which contains the
NT path to the driver .SYS file. This buffer should be freed
with LocalFree.
Return Value:
NO_ERROR - successful.
ERROR_NOT_ENOUGH_MEMORY - no memory to allocate output buffer.
ERROR_INVALID_PARAMETER - RtlDosPathNameToNtPath_U failure.
--*/
{
UNICODE_STRING NewPath;
DWORD DriverPathLength;
DWORD NTSystemRootLength;
DWORD DOSSystemRootLength;
LPWSTR RelativeCanonPath;
DWORD Status;
SC_LOG1(DEPEND, "ScCanonDriverImagePath: Input path "
FORMAT_LPWSTR "\n", DriverPath);
DriverPathLength = (DWORD) wcslen(DriverPath);
NTSystemRootLength = (DWORD) wcslen(SC_NT_SYSTEM_ROOT);
if (DriverPathLength > NTSystemRootLength &&
(_wcsnicmp(SC_NT_SYSTEM_ROOT, DriverPath,
NTSystemRootLength) == 0)) {
//
// Path is already in NT form with \SystemRoot\ prefix.
// Just return a buffer that contains the same path as input.
//
*CanonDriverPath = (LPWSTR) LocalAlloc(LMEM_ZEROINIT,
(UINT) ((DriverPathLength + 1) * sizeof(WCHAR)));
if (*CanonDriverPath == NULL) {
SC_LOG1(ERROR, "ScCanonDriverPathName: LocalAlloc failed %lu\n",
GetLastError());
return ERROR_NOT_ENOUGH_MEMORY;
}
//
// Boot drivers need relative path names
//
if (DriverStartType == SERVICE_BOOT_START) {
wcscpy(*CanonDriverPath, DriverPath + NTSystemRootLength);
}
else {
wcscpy(*CanonDriverPath, DriverPath);
}
SC_LOG1(DEPEND, "ScCanonDriverImagePath: Canonicalized path "
FORMAT_LPWSTR "\n", *CanonDriverPath);
return NO_ERROR;
}
DOSSystemRootLength = (DWORD) wcslen(SC_DOS_SYSTEM_ROOT);
if (DriverPathLength > DOSSystemRootLength &&
(_wcsnicmp(SC_DOS_SYSTEM_ROOT, DriverPath, DOSSystemRootLength) == 0)) {
//
// DOS path has %SystemRoot%\ prefix
//
*CanonDriverPath = (LPWSTR) LocalAlloc(LMEM_ZEROINIT,
(UINT) ((DriverPathLength + 1) * sizeof(WCHAR)));
if (*CanonDriverPath == NULL) {
SC_LOG1(ERROR, "ScCanonDriverPathName: LocalAlloc failed %lu\n",
GetLastError());
return ERROR_NOT_ENOUGH_MEMORY;
}
if (DriverStartType != SERVICE_BOOT_START) {
wcscpy(*CanonDriverPath, SC_NT_SYSTEM_ROOT);
}
else {
*CanonDriverPath[0] = '\0';
}
wcscat(*CanonDriverPath, DriverPath + DOSSystemRootLength);
SC_LOG1(DEPEND, "ScCanonDriverImagePath: Canonicalized path "
FORMAT_LPWSTR "\n", *CanonDriverPath);
return NO_ERROR;
}
//
// If it's already a relative path name, leave it alone
//
if (DriverPath[0] != L'\\' && DriverPath[1] != L':') {
*CanonDriverPath = (LPWSTR) LocalAlloc(LMEM_ZEROINIT,
(UINT) ((DriverPathLength + 1) * sizeof(WCHAR)));
if (*CanonDriverPath == NULL) {
SC_LOG1(ERROR, "ScCanonDriverPathName: LocalAlloc failed %lu\n",
GetLastError());
return ERROR_NOT_ENOUGH_MEMORY;
}
wcscpy(*CanonDriverPath, DriverPath);
return(NO_ERROR);
}
//
// Convert DOS path to NT path using Rtl routine which allocates
// the Unicode string buffer.
//
if (! RtlDosPathNameToNtPathName_U(
(PCWSTR) DriverPath,
&NewPath,
NULL,
NULL
)) {
return ERROR_INVALID_PARAMETER;
}
*CanonDriverPath = (LPWSTR) LocalAlloc(
LMEM_ZEROINIT,
(UINT) NewPath.Length + sizeof(WCHAR)
);
if (*CanonDriverPath == NULL) {
SC_LOG1(ERROR, "ScCanonDriverPathName: LocalAlloc failed %lu\n",
GetLastError());
RtlFreeUnicodeString(&NewPath);
return ERROR_NOT_ENOUGH_MEMORY;
}
wcsncpy(*CanonDriverPath, NewPath.Buffer, NewPath.Length / sizeof(WCHAR));
RtlFreeUnicodeString(&NewPath);
//
// Boot drivers' imagepath must be relative to \systemroot
//
if (DriverStartType == SERVICE_BOOT_START)
{
Status = ScConvertToBootPathName(*CanonDriverPath, &RelativeCanonPath);
if (Status == NO_ERROR)
{
SC_ASSERT(RelativeCanonPath != NULL);
wcscpy(*CanonDriverPath, RelativeCanonPath + NTSystemRootLength);
LocalFree(RelativeCanonPath);
}
else
{
LocalFree(*CanonDriverPath);
*CanonDriverPath = NULL;
}
return Status;
}
SC_LOG1(DEPEND, "ScCanonDriverImagePath: Canonicalized path "
FORMAT_LPWSTR "\n", *CanonDriverPath);
return NO_ERROR;
}
DWORD
RGetServiceDisplayNameW(
SC_RPC_HANDLE hSCManager,
LPWSTR lpServiceName,
LPWSTR lpDisplayName,
LPDWORD lpcchBuffer
)
/*++
Routine Description:
This function returns the DisplayName that is associated with a
particular ServiceName. If the buffer that is to receive the
DisplayName is too small, no data is returned in the buffer. Instead,
the actual string size (in characters - not including NUL terminator)
is returned in *lpcchBuffer.
Arguments:
hSCManager - Handle to the Service Control Manager. This parameter
is the RPC handle that was used to get us to this point.
lpServiceName - This is a pointer to the service name string. This
name is the same as the registry key name for that service.
lpDisplayName - This is a pointer to the buffer where the display name
is to be placed. If this function fails, this buffer will contain
an empty string.
lpcchBuffer - This is a pointer to a DWORD that contains the size of
the buffer (in characters) upon input. On return, this DWORD
indicates how many characters (excluding the NUL terminator) are
in the DisplayName.
Return Value:
NO_ERROR - If the operation was successful.
ERROR_INSUFFICIENT_BUFFER - if the buffer is too small to contain the
whole string.
ERROR_SERVICE_DOES_NOT_EXIST - If there is no record of a service
by this name in the database.
ERROR_INVALID_NAME - if the ServiceName is invalid (NULL);
--*/
{
DWORD status;
DWORD reqSize;
LPSERVICE_RECORD ServiceRecord;
UNREFERENCED_PARAMETER(hSCManager);
//
// Find the proper service record.
//
CServiceListSharedLock LLock;
CServiceRecordSharedLock RLock;
status = ScGetNamedServiceRecord(lpServiceName, &ServiceRecord);
if (status != NO_ERROR) {
return(status);
}
//
// Get the display name and determine if it will fit in the buffer.
//
reqSize = (DWORD) wcslen(ServiceRecord->DisplayName);
if (*lpcchBuffer < (reqSize + 1))
{
*lpcchBuffer = reqSize;
return(ERROR_INSUFFICIENT_BUFFER);
}
wcscpy(lpDisplayName, ServiceRecord->DisplayName);
*lpcchBuffer = reqSize;
return(NO_ERROR);
}
DWORD
RGetServiceKeyNameW(
SC_RPC_HANDLE hSCManager,
LPWSTR lpDisplayName,
LPWSTR lpServiceName,
LPDWORD lpcchBuffer
)
/*++
Routine Description:
This function returns the ServiceName that is associated with a
particular DisplayName. If the buffer that is to receive the
ServiceName is too small, no data is returned in the buffer. Instead,
the actual string size (in characters - not including NUL terminator)
is returned in *lpcchBuffer.
Arguments:
hSCManager - Handle to the Service Control Manager. This parameter
is the RPC handle that was used to get us to this point.
lpDisplayName - This is a pointer to the service display name string.
lpServiceName - This is a pointer to the buffer where the service name
string is to be placed. If this function fails, this buffer will
contain an empty string.
lpcchBuffer - This is a pointer to a DWORD that contains the size of
the buffer (in characters) upon input. On return, this DWORD
indicates how many characters (excluding the NUL terminator) are
in the DisplayName.
Return Value:
--*/
{
DWORD status;
DWORD reqSize;
LPSERVICE_RECORD ServiceRecord;
UNREFERENCED_PARAMETER(hSCManager);
//
// Find the proper service record.
//
CServiceListSharedLock LLock;
CServiceRecordSharedLock RLock;
status = ScGetDisplayNamedServiceRecord(lpDisplayName, &ServiceRecord);
if (status != NO_ERROR)
{
return(status);
}
//
// Get the service key name and determine if it will fit in the buffer.
//
reqSize = (DWORD) wcslen(ServiceRecord->ServiceName);
if (*lpcchBuffer < (reqSize + 1)) {
*lpcchBuffer = reqSize;
return(ERROR_INSUFFICIENT_BUFFER);
}
wcscpy(lpServiceName, ServiceRecord->ServiceName);
*lpcchBuffer = reqSize;
return(NO_ERROR);
}
DWORD
ScValidateDisplayName(
LPWSTR lpDisplayName,
LPSERVICE_RECORD lpServiceRecord
)
/*++
Routine Description:
This function validates display names by checking to see if the name
string already exists in the database. The display name must not match
any other display name or another service name. The display name can
match the service name if it they both refer to the same service.
Arguments:
lpDisplayName - A pointer to the proposed DisplayName.
lpServiceRecord - A pointer to the service record to which the display
name is to be added. If this function is called from CreateService,
the lpServiceRecord pointer will be NULL.
Return Value:
NO_ERROR - If the DisplayName doesn't conflict with any other names
in the database.
ERROR_DUPLICATE_SERVICE_NAME - If the DisplayName conflicts with another name.
--*/
{
DWORD status = NO_ERROR;
LPSERVICE_RECORD displayServiceRecord;
if (lpDisplayName != NULL) {
if (wcslen(lpDisplayName) > MAX_SERVICE_NAME_LENGTH) {
return(ERROR_INVALID_NAME);
}
status = ScGetDisplayNamedServiceRecord(
lpDisplayName,
&displayServiceRecord);
if (status == NO_ERROR) {
if (displayServiceRecord != lpServiceRecord) {
//
// The display name already exists for a different
// service. Therefore we must reject it.
//
return(ERROR_DUPLICATE_SERVICE_NAME);
}
}
status = ScGetNamedServiceRecord(
lpDisplayName,
&displayServiceRecord);
if (status == NO_ERROR) {
if (displayServiceRecord != lpServiceRecord) {
//
// The display name is already used as a service name.
// Therefore we must reject it.
//
return(ERROR_DUPLICATE_SERVICE_NAME);
}
}
}
return(NO_ERROR);
}
DWORD
ScConvertToBootPathName(
LPWSTR FullQualPathName,
LPWSTR * RelativePathName
)
/*++
Routine Description:
This function takes an NT style image path name and turns it into an
NT style path name that is accessed via \systemroot. This is required
for drivers that are loaded by the boot loader.
Arguments:
FullQualPathName - The fully qualified name
RelativePathName - A pointer to the pointer for the new buffer which
contains the fully qualified path name using
\systemroot\. The caller must free this buffer using
LocalFree.
Return Value:
NO_ERROR - If the name can be converted.
ERROR_INVALID_PARAMETER - If the Name is not relative to \systemroot
--*/
{
WCHAR Dummy;
LPWSTR Prefix = &Dummy;
DWORD PrefixLength;
UNICODE_STRING NewPrefix;
DWORD NTSystemRootLength;
DWORD PathLength;
DWORD NumRequired;
DWORD CharsReturned;
OBJECT_ATTRIBUTES ObjectAttributes;
HANDLE Handle = NULL;
UNICODE_STRING FileName;
UNICODE_STRING LinkTarget = { 0, 0, NULL };
NTSTATUS Status;
DWORD dwError = NO_ERROR;
DWORD BytesRequired;
PrefixLength = (DWORD) wcslen(SC_NT_SYSTEM_ROOT);
NTSystemRootLength = PrefixLength;
PathLength = (DWORD) wcslen(FullQualPathName);
if ((PathLength > PrefixLength) &&
(_wcsnicmp(SC_NT_SYSTEM_ROOT, FullQualPathName, PrefixLength) == 0)) {
//
// Path is already in NT form with \SystemRoot\ prefix.
// Just return it
//
*RelativePathName = (LPWSTR) LocalAlloc(LMEM_ZEROINIT,
(UINT) (PathLength + 1) * sizeof(WCHAR));
if (*RelativePathName == NULL)
{
SC_LOG1(ERROR, "ScConvertToBootName: LocalAlloc failed %d\n",
GetLastError());
return(ERROR_NOT_ENOUGH_MEMORY);
}
wcscpy(*RelativePathName, FullQualPathName);
return(ERROR_SUCCESS);
}
PrefixLength = (DWORD) wcslen(SC_DOS_SYSTEM_ROOT);
if (PathLength > PrefixLength &&
(_wcsnicmp(SC_DOS_SYSTEM_ROOT, FullQualPathName, PrefixLength) == 0)) {
//
// Path is in DOS form with %SystemRoot% prefix.
// Just return it after replacing the %SystemRoot%\ prefix with
// \SystemRoot
//
*RelativePathName = (LPWSTR) LocalAlloc(LMEM_ZEROINIT,
(UINT) (PathLength - PrefixLength + NTSystemRootLength + 1)
* sizeof(WCHAR));
if (*RelativePathName == NULL) {
SC_LOG1(ERROR, "ScConvertToBootName: LocalAlloc failed %d\n",
GetLastError());
return(ERROR_NOT_ENOUGH_MEMORY);
}
wcscpy(*RelativePathName, SC_NT_SYSTEM_ROOT);
wcscat(*RelativePathName, FullQualPathName + PrefixLength);
return(ERROR_SUCCESS);
}
//
// Create a string that represents the path to systemroot that you
// would get if you started with a Dos style name
//
//
// Make the first call just to get the number of characters that
// will be returned.
//
NumRequired = ExpandEnvironmentStringsW (SC_DOS_SYSTEM_ROOT, Prefix, 1);
if (NumRequired > 1) {
Prefix = (LPWSTR)LocalAlloc(LMEM_ZEROINIT,
(UINT) ((NumRequired + 1) * sizeof(WCHAR)));
if (Prefix == NULL) {
SC_LOG2(ERROR, "ScConvertToBootName: LocalAlloc of numChar= "
FORMAT_DWORD " failed " FORMAT_DWORD "\n",
NumRequired + 1, GetLastError());
return(ERROR_NOT_ENOUGH_MEMORY);
}
//
// Now expand the string into a dos type path
//
CharsReturned = ExpandEnvironmentStringsW (
SC_DOS_SYSTEM_ROOT,
Prefix,
NumRequired);
if (CharsReturned > NumRequired) {
SC_LOG1(ERROR, "ScConvertToBootName: ExpandEnvironmentStrings "
" failed for " FORMAT_LPWSTR " \n", SC_DOS_SYSTEM_ROOT);
LocalFree(Prefix);
return(ERROR_NOT_ENOUGH_MEMORY);
}
}
else {
//
// This shouldn't ever happen
//
ASSERT(FALSE);
return ERROR_INVALID_ENVIRONMENT;
}
//
// Now convert the DOS path to an NT path
//
if (! RtlDosPathNameToNtPathName_U(
(PCWSTR) Prefix,
&NewPrefix,
NULL,
NULL
)) {
//
// This shouldn't ever happen
//
ASSERT(FALSE);
LocalFree(Prefix);
return ERROR_INVALID_ENVIRONMENT;
}
LocalFree(Prefix);
PrefixLength = NewPrefix.Length / sizeof(WCHAR);
Prefix = (LPWSTR)LocalAlloc(LMEM_ZEROINIT,
(UINT) (NewPrefix.Length + sizeof(WCHAR)));
if (Prefix == NULL)
{
SC_LOG2(ERROR, "ScConvertToBootName: LocalAlloc of numChar= "
FORMAT_DWORD " failed " FORMAT_DWORD "\n",
NewPrefix.Length + sizeof(WCHAR), GetLastError());
dwError = ERROR_NOT_ENOUGH_MEMORY;
goto ErrorExit;
}
wcsncpy(Prefix, NewPrefix.Buffer, PrefixLength);
if (PathLength > PrefixLength &&
(_wcsnicmp(Prefix, FullQualPathName, PrefixLength) == 0)) {
//
// Path is in DOS form without using %systemroot%
// Convert to \SystemRoot format
//
*RelativePathName = (LPWSTR) LocalAlloc(LMEM_ZEROINIT,
(UINT) (PathLength - PrefixLength + NTSystemRootLength + 1)
* sizeof(WCHAR));
if (*RelativePathName == NULL)
{
SC_LOG2(ERROR, "ScConvertToBootName: LocalAlloc of numChar= "
FORMAT_DWORD " failed " FORMAT_DWORD "\n",
(PathLength - PrefixLength + NTSystemRootLength + 1) *
sizeof(WCHAR), GetLastError());
dwError = ERROR_NOT_ENOUGH_MEMORY;
goto ErrorExit;
}
wcscpy(*RelativePathName, SC_NT_SYSTEM_ROOT);
wcscat(*RelativePathName, FullQualPathName + PrefixLength);
dwError = NO_ERROR;
goto CleanExit;
}
//
// Create a string that represents the path to systemroot that you
// would get if you started with a NT style name
//
//
// Make the first call just to get the number of characters that
// will be returned.
//
RtlInitUnicodeString(&FileName,L"\\SystemRoot");
InitializeObjectAttributes(
&ObjectAttributes,
&FileName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
Status = NtOpenSymbolicLinkObject(&Handle, SYMBOLIC_LINK_QUERY,
&ObjectAttributes);
if (!NT_SUCCESS(Status))
{
//
// This should never happen
//
ASSERT(FALSE);
dwError = ERROR_INVALID_ENVIRONMENT;
goto ErrorExit;
}
Status = NtQuerySymbolicLinkObject(Handle, &LinkTarget, &BytesRequired);
if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_TOO_SMALL)
{
//
// This should never happen
//
ASSERT(FALSE);
dwError = ERROR_INVALID_ENVIRONMENT;
goto ErrorExit;
}
LinkTarget.Buffer = (LPWSTR) LocalAlloc(LMEM_ZEROINIT,
BytesRequired + sizeof(WCHAR));
if (LinkTarget.Buffer == NULL)
{
SC_LOG2(ERROR, "ScConvertToBootName: LocalAlloc of numChar= "
FORMAT_DWORD " failed " FORMAT_DWORD "\n",
BytesRequired, GetLastError());
dwError = ERROR_NOT_ENOUGH_MEMORY;
goto ErrorExit;
}
LinkTarget.Length = (USHORT) BytesRequired;
LinkTarget.MaximumLength = (USHORT) (BytesRequired + sizeof(WCHAR));
Status = NtQuerySymbolicLinkObject(Handle, &LinkTarget, &BytesRequired);
if (!NT_SUCCESS(Status))
{
//
// This should never happen
//
ASSERT(FALSE);
dwError = ERROR_INVALID_ENVIRONMENT;
goto ErrorExit;
}
PrefixLength = LinkTarget.Length / sizeof(WCHAR);
if (PathLength > PrefixLength &&
(_wcsnicmp(LinkTarget.Buffer, FullQualPathName, PrefixLength) == 0)) {
//
// Path is in NT form without using \systemroot
// Convert to \SystemRoot format
//
*RelativePathName = (LPWSTR) LocalAlloc(LMEM_ZEROINIT,
(UINT) (PathLength - PrefixLength + NTSystemRootLength + 1)
* sizeof(WCHAR));
if (*RelativePathName == NULL)
{
SC_LOG2(ERROR, "ScConvertToBootName: LocalAlloc of numChar= "
FORMAT_DWORD " failed " FORMAT_DWORD "\n",
(PathLength - PrefixLength + NTSystemRootLength + 1) *
sizeof(WCHAR), GetLastError());
dwError = ERROR_NOT_ENOUGH_MEMORY;
goto ErrorExit;
}
wcscpy(*RelativePathName, SC_NT_SYSTEM_ROOT);
// Skip the \ between \SystemRoot and the relative name
wcscat(*RelativePathName, FullQualPathName + PrefixLength + 1);
dwError = NO_ERROR;
goto CleanExit;
}
//
// If we get this far, the imagepath is not relative to the systemroot.
//
dwError = ERROR_INVALID_PARAMETER;
ErrorExit:
//
// Return an error and a NULL pointer.
//
*RelativePathName = NULL;
CleanExit:
if (Handle)
{
NtClose(Handle);
}
LocalFree(LinkTarget.Buffer);
RtlFreeUnicodeString(&NewPrefix);
LocalFree(Prefix);
return dwError;
}
BOOLEAN
ScIsArcName(
LPWSTR PathName
)
/*++
Routine Description:
This function takes a driver's path name and determines if it is an
ARC style name. This is done by trying to open the file with \Arcname
prepended. There are symbolic links of this form for every valid arcname.
Arguments:
PathName - The image path
Return Value:
TRUE - If it is an arcname
FALSE - If it's not an arcname
--*/
{
#define ARC_PREFIX L"\\Arcname"
OBJECT_ATTRIBUTES ObjectAttributes;
HANDLE Handle;
IO_STATUS_BLOCK IoStatusBlock;
UNICODE_STRING FileName;
NTSTATUS Status;
LPWSTR NewString;
UINT BufferSize;
//
// Allocate the buffer for the composite string
//
BufferSize = ((DWORD) wcslen(PathName)
+ (DWORD) wcslen(ARC_PREFIX) + 1) * sizeof(WCHAR);
NewString = (LPWSTR) LocalAlloc(LMEM_ZEROINIT, BufferSize);
if (NewString == NULL)
{
SC_LOG2(ERROR, "ScIsArcName: LocalAlloc of numChar= "
FORMAT_DWORD " failed " FORMAT_DWORD "\n", BufferSize, GetLastError());
return FALSE;
}
//
// Create the composite string
//
wcscpy(NewString, ARC_PREFIX);
wcscat(NewString, PathName);
RtlInitUnicodeString(&FileName, NewString);
//
// Try to open it
//
InitializeObjectAttributes(
&ObjectAttributes,
&FileName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
Status = NtOpenFile(&Handle,
FILE_GENERIC_READ,
&ObjectAttributes,
&IoStatusBlock,
FILE_SHARE_READ,
FILE_SYNCHRONOUS_IO_NONALERT);
LocalFree(NewString);
//
// If you could open it, it's an arcname
//
if (NT_SUCCESS(Status))
{
//
// Close it, we just need the status
//
NtClose(Handle);
return TRUE;
}
else
{
return FALSE;
}
}