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