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.
1328 lines
38 KiB
1328 lines
38 KiB
/*++
|
|
|
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
Module Name:
|
|
|
|
util.c
|
|
|
|
Abstract:
|
|
|
|
This module contains general utility routines used by cfgmgr32 code.
|
|
|
|
INVALID_DEVINST
|
|
CopyFixedUpDeviceId
|
|
PnPUnicodeToMultiByte
|
|
PnPMultiByteToUnicode
|
|
PnPRetrieveMachineName
|
|
PnPGetVersion
|
|
PnPGetGlobalHandles
|
|
PnPEnablePrivileges
|
|
PnPRestorePrivileges
|
|
IsRemoteServiceRunning
|
|
|
|
Author:
|
|
|
|
Paula Tomlinson (paulat) 6-22-1995
|
|
|
|
Environment:
|
|
|
|
User mode only.
|
|
|
|
Revision History:
|
|
|
|
22-Jun-1995 paulat
|
|
|
|
Creation and initial implementation.
|
|
|
|
--*/
|
|
|
|
|
|
//
|
|
// includes
|
|
//
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
#include "cfgi.h"
|
|
|
|
|
|
//
|
|
// Private prototypes
|
|
//
|
|
BOOL
|
|
EnablePnPPrivileges(
|
|
VOID
|
|
);
|
|
|
|
|
|
//
|
|
// global data
|
|
//
|
|
extern PVOID hLocalStringTable; // MODIFIED by PnPGetGlobalHandles
|
|
extern PVOID hLocalBindingHandle; // MODIFIED by PnPGetGlobalHandles
|
|
extern WORD LocalServerVersion; // MODIFIED by PnPGetVersion
|
|
extern WCHAR LocalMachineNameNetBIOS[]; // NOT MODIFIED BY THIS FILE
|
|
extern CRITICAL_SECTION BindingCriticalSection; // NOT MODIFIED IN THIS FILE
|
|
extern CRITICAL_SECTION StringTableCriticalSection; // NOT MODIFIED IN THIS FILE
|
|
|
|
|
|
|
|
BOOL
|
|
INVALID_DEVINST(
|
|
PWSTR pDeviceID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine attempts a simple check whether the pDeviceID string
|
|
returned from StringTableStringFromID is valid or not. It does
|
|
this simply by dereferencing the pointer and comparing the first
|
|
character in the string against the range of characters for a valid
|
|
device id. If the string is valid but it's not an existing device id
|
|
then this error will be caught later.
|
|
|
|
Arguments:
|
|
|
|
pDeviceID Supplies a pointer to the string to be validated.
|
|
|
|
Return Value:
|
|
|
|
If it's invalid it returns TRUE, otherwise it returns FALSE.
|
|
|
|
--*/
|
|
{
|
|
BOOL Status = FALSE;
|
|
|
|
try {
|
|
|
|
if ((!ARGUMENT_PRESENT(pDeviceID)) ||
|
|
(*pDeviceID <= L' ') ||
|
|
(*pDeviceID > (WCHAR)0x7F) ||
|
|
(*pDeviceID == L',')) {
|
|
Status = TRUE;
|
|
}
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = TRUE;
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // INVALID_DEVINST
|
|
|
|
|
|
|
|
VOID
|
|
CopyFixedUpDeviceId(
|
|
OUT LPWSTR DestinationString,
|
|
IN LPCWSTR SourceString,
|
|
IN DWORD SourceStringLen
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine copies a device id, fixing it up as it does the copy.
|
|
'Fixing up' means that the string is made upper-case, and that the
|
|
following character ranges are turned into underscores (_):
|
|
|
|
c <= 0x20 (' ')
|
|
c > 0x7F
|
|
c == 0x2C (',')
|
|
|
|
(NOTE: This algorithm is also implemented in the Config Manager APIs,
|
|
and must be kept in sync with that routine. To maintain device identifier
|
|
compatibility, these routines must work the same as Win95.)
|
|
|
|
Arguments:
|
|
|
|
DestinationString - Supplies a pointer to the destination string buffer
|
|
where the fixed-up device id is to be copied. This buffer must
|
|
be large enough to hold a copy of the source string (including
|
|
terminating NULL).
|
|
|
|
SourceString - Supplies a pointer to the (null-terminated) source
|
|
string to be fixed up.
|
|
|
|
SourceStringLen - Supplies the length, in characters, of the source
|
|
string (not including terminating NULL).
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PWCHAR p;
|
|
|
|
try {
|
|
|
|
CopyMemory(DestinationString,
|
|
SourceString,
|
|
((SourceStringLen + 1) * sizeof(WCHAR)));
|
|
|
|
CharUpperBuff(DestinationString, SourceStringLen);
|
|
|
|
for(p = DestinationString; *p; p++) {
|
|
|
|
if((*p <= L' ') ||
|
|
(*p > (WCHAR)0x7F) ||
|
|
(*p == L',')) {
|
|
*p = L'_';
|
|
}
|
|
}
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
NOTHING;
|
|
}
|
|
|
|
} // CopyFixedUpDeviceId
|
|
|
|
|
|
|
|
CONFIGRET
|
|
PnPUnicodeToMultiByte(
|
|
IN PWSTR UnicodeString,
|
|
IN ULONG UnicodeStringLen,
|
|
OUT PSTR AnsiString OPTIONAL,
|
|
IN OUT PULONG AnsiStringLen
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Convert a string from unicode to ansi.
|
|
|
|
Arguments:
|
|
|
|
UnicodeString - Supplies string to be converted.
|
|
|
|
UnicodeStringLen - Specifies the size, in bytes, of the string to be
|
|
converted.
|
|
|
|
AnsiString - Optionally, supplies a buffer to receive the ANSI
|
|
string.
|
|
|
|
AnsiStringLen - Supplies the address of a variable that contains the
|
|
size, in bytes, of the buffer pointed to by AnsiString.
|
|
This API replaces the initial size with the number of
|
|
bytes of data copied to the buffer. If the variable is
|
|
initially zero, the API replaces it with the buffer size
|
|
needed to receive all the registry data. In this case,
|
|
the AnsiString parameter is ignored.
|
|
|
|
Return Value:
|
|
|
|
Returns a CONFIGRET code.
|
|
|
|
--*/
|
|
|
|
{
|
|
CONFIGRET Status = CR_SUCCESS;
|
|
NTSTATUS ntStatus;
|
|
ULONG ulAnsiStringLen = 0;
|
|
|
|
try {
|
|
//
|
|
// Validate parameters
|
|
//
|
|
if ((!ARGUMENT_PRESENT(AnsiStringLen)) ||
|
|
(!ARGUMENT_PRESENT(AnsiString)) && (*AnsiStringLen != 0)) {
|
|
Status = CR_INVALID_POINTER;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// Determine the size required for the ANSI string representation.
|
|
//
|
|
ntStatus = RtlUnicodeToMultiByteSize(&ulAnsiStringLen,
|
|
UnicodeString,
|
|
UnicodeStringLen);
|
|
if (!NT_SUCCESS(ntStatus)) {
|
|
Status = CR_FAILURE;
|
|
goto Clean0;
|
|
}
|
|
|
|
if ((!ARGUMENT_PRESENT(AnsiString)) ||
|
|
(*AnsiStringLen < ulAnsiStringLen)) {
|
|
*AnsiStringLen = ulAnsiStringLen;
|
|
Status = CR_BUFFER_SMALL;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// Perform the conversion.
|
|
//
|
|
ntStatus = RtlUnicodeToMultiByteN(AnsiString,
|
|
*AnsiStringLen,
|
|
&ulAnsiStringLen,
|
|
UnicodeString,
|
|
UnicodeStringLen);
|
|
|
|
ASSERT(NT_SUCCESS(ntStatus));
|
|
ASSERT(ulAnsiStringLen <= *AnsiStringLen);
|
|
|
|
if (!NT_SUCCESS(ntStatus)) {
|
|
Status = CR_FAILURE;
|
|
}
|
|
|
|
*AnsiStringLen = ulAnsiStringLen;
|
|
|
|
Clean0:
|
|
NOTHING;
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = CR_FAILURE;
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // PnPUnicodeToMultiByte
|
|
|
|
|
|
|
|
CONFIGRET
|
|
PnPMultiByteToUnicode(
|
|
IN PSTR AnsiString,
|
|
IN ULONG AnsiStringLen,
|
|
OUT PWSTR UnicodeString OPTIONAL,
|
|
IN OUT PULONG UnicodeStringLen
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Convert a string from ansi to unicode.
|
|
|
|
Arguments:
|
|
|
|
AnsiString - Supplies string to be converted.
|
|
|
|
AnsiStringLen - Specifies the size, in bytes, of the string to be
|
|
converted.
|
|
|
|
UnicodeString - Optionally, supplies a buffer to receive the Unicode
|
|
string.
|
|
|
|
UnicodeStringLen - Supplies the address of a variable that contains the
|
|
size, in bytes, of the buffer pointed to by UnicodeString.
|
|
This API replaces the initial size with the number of
|
|
bytes of data copied to the buffer. If the variable is
|
|
initially zero, the API replaces it with the buffer size
|
|
needed to receive all the registry data. In this case,
|
|
the UnicodeString parameter is ignored.
|
|
|
|
Return Value:
|
|
|
|
Returns a CONFIGRET code.
|
|
|
|
--*/
|
|
|
|
{
|
|
CONFIGRET Status = CR_SUCCESS;
|
|
NTSTATUS ntStatus;
|
|
ULONG ulUnicodeStringLen = 0;
|
|
|
|
try {
|
|
//
|
|
// Validate parameters
|
|
//
|
|
if ((!ARGUMENT_PRESENT(UnicodeStringLen)) ||
|
|
(!ARGUMENT_PRESENT(UnicodeString)) && (*UnicodeStringLen != 0)) {
|
|
Status = CR_INVALID_POINTER;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// Determine the size required for the ANSI string representation.
|
|
//
|
|
ntStatus = RtlMultiByteToUnicodeSize(&ulUnicodeStringLen,
|
|
AnsiString,
|
|
AnsiStringLen);
|
|
if (!NT_SUCCESS(ntStatus)) {
|
|
Status = CR_FAILURE;
|
|
goto Clean0;
|
|
}
|
|
|
|
if ((!ARGUMENT_PRESENT(UnicodeString)) ||
|
|
(*UnicodeStringLen < ulUnicodeStringLen)) {
|
|
*UnicodeStringLen = ulUnicodeStringLen;
|
|
Status = CR_BUFFER_SMALL;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// Perform the conversion.
|
|
//
|
|
ntStatus = RtlMultiByteToUnicodeN(UnicodeString,
|
|
*UnicodeStringLen,
|
|
&ulUnicodeStringLen,
|
|
AnsiString,
|
|
AnsiStringLen);
|
|
|
|
ASSERT(NT_SUCCESS(ntStatus));
|
|
ASSERT(ulUnicodeStringLen <= *UnicodeStringLen);
|
|
|
|
if (!NT_SUCCESS(ntStatus)) {
|
|
Status = CR_FAILURE;
|
|
}
|
|
|
|
*UnicodeStringLen = ulUnicodeStringLen;
|
|
|
|
Clean0:
|
|
NOTHING;
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = CR_FAILURE;
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // PnPMultiByteToUnicode
|
|
|
|
|
|
|
|
BOOL
|
|
PnPRetrieveMachineName(
|
|
IN HMACHINE hMachine,
|
|
OUT LPWSTR pszMachineName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Optimized version of PnPConnect, only returns the machine name
|
|
associated with this connection.
|
|
|
|
Arguments:
|
|
|
|
hMachine - Information about this connection
|
|
|
|
pszMachineName - Returns machine name specified when CM_Connect_Machine
|
|
was called.
|
|
|
|
** THIS BUFFER MUST BE AT LEAST (MAX_PATH + 3) CHARACTERS LONG. **
|
|
|
|
Return Value:
|
|
|
|
Return TRUE if the function succeeds and FALSE if it fails.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL Status = TRUE;
|
|
|
|
try {
|
|
|
|
if (hMachine == NULL) {
|
|
//
|
|
// local machine scenario
|
|
//
|
|
// use the global local machine name string that was filled
|
|
// when the DLL initialized.
|
|
//
|
|
if (FAILED(StringCchCopy(
|
|
pszMachineName,
|
|
MAX_PATH + 3,
|
|
LocalMachineNameNetBIOS))) {
|
|
Status = FALSE;
|
|
goto Clean0;
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// remote machine scenario
|
|
//
|
|
// validate the machine handle.
|
|
//
|
|
if (((PPNP_MACHINE)hMachine)->ulSignature != (ULONG)MACHINE_HANDLE_SIGNATURE) {
|
|
Status = FALSE;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// use information within the hMachine handle to fill in the
|
|
// machine name. The hMachine info was set on a previous call
|
|
// to CM_Connect_Machine.
|
|
//
|
|
if (FAILED(StringCchCopy(
|
|
pszMachineName,
|
|
MAX_PATH + 3,
|
|
((PPNP_MACHINE)hMachine)->szMachineName))) {
|
|
Status = FALSE;
|
|
goto Clean0;
|
|
}
|
|
}
|
|
|
|
Clean0:
|
|
NOTHING;
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = FALSE;
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // PnPRetrieveMachineName
|
|
|
|
|
|
|
|
BOOL
|
|
PnPGetVersion(
|
|
IN HMACHINE hMachine,
|
|
IN WORD * pwVersion
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine returns the internal server version for the specified machine
|
|
connection, as returned by the RPC server interface routine
|
|
PNP_GetVersionInternal. If the PNP_GetVersionInternal interface does not
|
|
exist on the specified machine, this routine returns the version as reported
|
|
by PNP_GetVersion.
|
|
|
|
Arguments:
|
|
|
|
hMachine - Information about this connection
|
|
|
|
pwVersion - Receives the internal server version.
|
|
|
|
Return Value:
|
|
|
|
Return TRUE if the function succeeds and FALSE if it fails.
|
|
|
|
Notes:
|
|
|
|
The version reported by PNP_GetVersion is defined to be constant, at 0x0400.
|
|
The version returned by PNP_GetVersionInternal may change with each release
|
|
of the product, starting with 0x0501 for Windows NT 5.1.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL Status = TRUE;
|
|
handle_t hBinding = NULL;
|
|
CONFIGRET crStatus;
|
|
WORD wVersionInternal;
|
|
|
|
try {
|
|
|
|
if (pwVersion == NULL) {
|
|
Status = FALSE;
|
|
goto Clean0;
|
|
}
|
|
|
|
if (hMachine == NULL) {
|
|
//
|
|
// local machine scenario
|
|
//
|
|
if (LocalServerVersion != 0) {
|
|
//
|
|
// local server version has already been retrieved.
|
|
//
|
|
*pwVersion = LocalServerVersion;
|
|
|
|
} else {
|
|
//
|
|
// retrieve binding handle for the local machine.
|
|
//
|
|
if (!PnPGetGlobalHandles(hMachine, NULL, &hBinding)) {
|
|
Status = FALSE;
|
|
goto Clean0;
|
|
}
|
|
|
|
ASSERT(hBinding);
|
|
|
|
//
|
|
// initialize the version supplied to the internal client
|
|
// version, in case the server wants to adjust the response
|
|
// based on the client version.
|
|
//
|
|
wVersionInternal = (WORD)CFGMGR32_VERSION_INTERNAL;
|
|
|
|
//
|
|
// No special privileges are required by the server
|
|
//
|
|
|
|
RpcTryExcept {
|
|
//
|
|
// call rpc service entry point
|
|
//
|
|
crStatus = PNP_GetVersionInternal(
|
|
hBinding, // rpc binding
|
|
&wVersionInternal); // internal server version
|
|
}
|
|
RpcExcept (I_RpcExceptionFilter(RpcExceptionCode())) {
|
|
KdPrintEx((DPFLTR_PNPMGR_ID,
|
|
DBGF_WARNINGS,
|
|
"PNP_GetVersionInternal caused an exception (%d)\n",
|
|
RpcExceptionCode()));
|
|
|
|
crStatus = MapRpcExceptionToCR(RpcExceptionCode());
|
|
}
|
|
RpcEndExcept
|
|
|
|
if (crStatus == CR_SUCCESS) {
|
|
//
|
|
// PNP_GetVersionInternal exists on NT 5.1 and later.
|
|
//
|
|
ASSERT(wVersionInternal >= (WORD)0x0501);
|
|
|
|
//
|
|
// initialize the global local server version.
|
|
//
|
|
LocalServerVersion = *pwVersion = wVersionInternal;
|
|
|
|
} else {
|
|
//
|
|
// we successfully retrieved a local binding handle, but
|
|
// PNP_GetVersionInternal failed for some reason other than
|
|
// the server not being available.
|
|
//
|
|
ASSERT(0);
|
|
|
|
//
|
|
// although we know this version of the client should match
|
|
// a version of the server where PNP_GetVersionInternal is
|
|
// available, it's technically possible (though unsupported)
|
|
// that this client is communicating with a downlevel server
|
|
// on the local machine, so we'll have to resort to calling
|
|
// PNP_GetVersion.
|
|
//
|
|
|
|
//
|
|
// No special privileges are required by the server
|
|
//
|
|
|
|
RpcTryExcept {
|
|
//
|
|
// call rpc service entry point
|
|
//
|
|
crStatus = PNP_GetVersion(
|
|
hBinding, // rpc binding
|
|
&wVersionInternal); // server version
|
|
}
|
|
RpcExcept (I_RpcExceptionFilter(RpcExceptionCode())) {
|
|
KdPrintEx((DPFLTR_PNPMGR_ID,
|
|
DBGF_WARNINGS,
|
|
"PNP_GetVersion caused an exception (%d)\n",
|
|
RpcExceptionCode()));
|
|
|
|
crStatus = MapRpcExceptionToCR(RpcExceptionCode());
|
|
}
|
|
RpcEndExcept
|
|
|
|
if (crStatus == CR_SUCCESS) {
|
|
//
|
|
// PNP_GetVersion should always return 0x0400 on all servers.
|
|
//
|
|
ASSERT(wVersionInternal == (WORD)0x0400);
|
|
|
|
//
|
|
// initialize the global local server version.
|
|
//
|
|
LocalServerVersion = *pwVersion = wVersionInternal;
|
|
|
|
} else {
|
|
//
|
|
// nothing more we can do here but fail.
|
|
//
|
|
ASSERT(0);
|
|
Status = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// remote machine scenario
|
|
//
|
|
// validate the machine handle.
|
|
//
|
|
if (((PPNP_MACHINE)hMachine)->ulSignature != (ULONG)MACHINE_HANDLE_SIGNATURE) {
|
|
Status = FALSE;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// use information within the hMachine handle to fill in the
|
|
// version. The hMachine info was set on a previous call to
|
|
// CM_Connect_Machine.
|
|
//
|
|
*pwVersion = ((PPNP_MACHINE)hMachine)->wVersion;
|
|
}
|
|
|
|
Clean0:
|
|
NOTHING;
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = FALSE;
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // PnPGetVersion
|
|
|
|
|
|
|
|
BOOL
|
|
PnPGetGlobalHandles(
|
|
IN HMACHINE hMachine,
|
|
OUT PVOID *phStringTable, OPTIONAL
|
|
OUT PVOID *phBindingHandle OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine retrieves a handle to the string table and/or the rpc binding
|
|
handle for the specified server machine connection.
|
|
|
|
Arguments:
|
|
|
|
hMachine - Specifies a server machine connection handle, as returned
|
|
by CM_Connect_Machine.
|
|
|
|
phStringTable - Optionally, specifies an address to receive a handle to
|
|
the string table for the specified server machine
|
|
connection.
|
|
|
|
phBindingHandle - Optionally, specifies an address to receive the RPC
|
|
binding handle for the specifies server machine
|
|
connection.
|
|
|
|
Return value:
|
|
|
|
Returns TRUE if successful, FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL bStatus = TRUE;
|
|
|
|
|
|
try {
|
|
|
|
if (ARGUMENT_PRESENT(phStringTable)) {
|
|
|
|
if (hMachine == NULL) {
|
|
|
|
//------------------------------------------------------
|
|
// Retrieve String Table Handle for the local machine
|
|
//-------------------------------------------------------
|
|
|
|
EnterCriticalSection(&StringTableCriticalSection);
|
|
|
|
if (hLocalStringTable != NULL) {
|
|
//
|
|
// local string table has already been created
|
|
//
|
|
*phStringTable = hLocalStringTable;
|
|
|
|
} else {
|
|
//
|
|
// first time, initialize the local string table
|
|
//
|
|
|
|
hLocalStringTable = pSetupStringTableInitialize();
|
|
|
|
if (hLocalStringTable != NULL) {
|
|
//
|
|
// No matter how the string table is implemented, I never
|
|
// want to have a string id of zero - this would generate
|
|
// an invalid devinst. So, add a small priming string just
|
|
// to be safe.
|
|
//
|
|
pSetupStringTableAddString(hLocalStringTable,
|
|
PRIMING_STRING,
|
|
STRTAB_CASE_SENSITIVE);
|
|
|
|
*phStringTable = hLocalStringTable;
|
|
|
|
} else {
|
|
KdPrintEx((DPFLTR_PNPMGR_ID,
|
|
DBGF_ERRORS,
|
|
"CFGMGR32: failed to initialize local string table\n"));
|
|
*phStringTable = NULL;
|
|
}
|
|
}
|
|
|
|
LeaveCriticalSection(&StringTableCriticalSection);
|
|
|
|
if (*phStringTable == NULL) {
|
|
bStatus = FALSE;
|
|
goto Clean0;
|
|
}
|
|
|
|
} else {
|
|
|
|
//-------------------------------------------------------
|
|
// Retrieve String Table Handle for the remote machine
|
|
//-------------------------------------------------------
|
|
|
|
//
|
|
// validate the machine handle.
|
|
//
|
|
if (((PPNP_MACHINE)hMachine)->ulSignature != (ULONG)MACHINE_HANDLE_SIGNATURE) {
|
|
bStatus = FALSE;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// use information within the hMachine handle to set the string
|
|
// table handle. The hMachine info was set on a previous call
|
|
// to CM_Connect_Machine.
|
|
//
|
|
*phStringTable = ((PPNP_MACHINE)hMachine)->hStringTable;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (ARGUMENT_PRESENT(phBindingHandle)) {
|
|
|
|
if (hMachine == NULL) {
|
|
|
|
//-------------------------------------------------------
|
|
// Retrieve Binding Handle for the local machine
|
|
//-------------------------------------------------------
|
|
|
|
EnterCriticalSection(&BindingCriticalSection);
|
|
|
|
if (hLocalBindingHandle != NULL) {
|
|
//
|
|
// local binding handle has already been set
|
|
//
|
|
*phBindingHandle = hLocalBindingHandle;
|
|
|
|
} else {
|
|
//
|
|
// first time, explicitly force binding to local machine
|
|
//
|
|
pnp_handle = PNP_HANDLE_bind(NULL); // set rpc global
|
|
|
|
if (pnp_handle != NULL) {
|
|
|
|
*phBindingHandle = hLocalBindingHandle = (PVOID)pnp_handle;
|
|
|
|
} else {
|
|
KdPrintEx((DPFLTR_PNPMGR_ID,
|
|
DBGF_ERRORS,
|
|
"CFGMGR32: failed to initialize local binding handle\n"));
|
|
*phBindingHandle = NULL;
|
|
}
|
|
}
|
|
|
|
LeaveCriticalSection(&BindingCriticalSection);
|
|
|
|
if (*phBindingHandle == NULL) {
|
|
bStatus = FALSE;
|
|
goto Clean0;
|
|
}
|
|
|
|
} else {
|
|
|
|
//-------------------------------------------------------
|
|
// Retrieve Binding Handle for the remote machine
|
|
//-------------------------------------------------------
|
|
|
|
//
|
|
// validate the machine handle.
|
|
//
|
|
if (((PPNP_MACHINE)hMachine)->ulSignature != (ULONG)MACHINE_HANDLE_SIGNATURE) {
|
|
bStatus = FALSE;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// use information within the hMachine handle to set the
|
|
// binding handle. The hMachine info was set on a previous call
|
|
// to CM_Connect_Machine.
|
|
//
|
|
*phBindingHandle = ((PPNP_MACHINE)hMachine)->hBindingHandle;
|
|
}
|
|
}
|
|
|
|
Clean0:
|
|
NOTHING;
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
bStatus = FALSE;
|
|
}
|
|
|
|
return bStatus;
|
|
|
|
} // PnpGetGlobalHandles
|
|
|
|
|
|
|
|
HANDLE
|
|
PnPEnablePrivileges(
|
|
IN PULONG Privileges,
|
|
IN ULONG PrivilegeCount
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine enables the specified privileges in the thread token for the
|
|
calling thread. If no thread exists (not impersonating), the process token
|
|
is used.
|
|
|
|
Arguments:
|
|
|
|
Privileges - Specifies a list of privileges to enable.
|
|
|
|
PrivilegeCount - Specifies the number of privileges in the list.
|
|
|
|
Return value:
|
|
|
|
If successful, returns a handle to the previous thread token (if it exists)
|
|
or NULL, to indicate that the thread did not previously have a token. If
|
|
successful, ReleasePrivileges should be called to ensure that the previous
|
|
thread token (if exists) is replaced on the calling thread and that the
|
|
handle is closed.
|
|
|
|
If unsuccessful, INVALID_HANDLE_VALUE is returned.
|
|
|
|
Notes:
|
|
|
|
This routine is intended to operate on well-known privileges only; no lookup
|
|
of privilege names is done by this routine; it assumes that the privilege
|
|
LUID value for well-known privileges can be constructed from it's
|
|
corresponding ULONG privilege value, via RtlConvertUlongToLuid.
|
|
|
|
This is true for SE_LOAD_DRIVER_PRIVILEGE and SE_UNDOCK_PRIVILEGE, which are
|
|
the only privilege values CFGMGR32 uses this routine to enable. If
|
|
additional pricileges are used where that is not the case, this routine may
|
|
be changed to receive an array of privilege names - with the corresponding
|
|
privilege LUID value lookup performed for each.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL bResult;
|
|
HANDLE hToken, hNewToken;
|
|
HANDLE hOriginalThreadToken;
|
|
PSECURITY_DESCRIPTOR pSecurityDescriptor = NULL;
|
|
SECURITY_ATTRIBUTES sa;
|
|
PTOKEN_PRIVILEGES pTokenPrivileges;
|
|
ULONG nBufferSize, i;
|
|
|
|
|
|
//
|
|
// Validate parameters
|
|
//
|
|
|
|
if ((!ARGUMENT_PRESENT(Privileges)) || (PrivilegeCount == 0)) {
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
//
|
|
// Note that TOKEN_PRIVILEGES includes a single LUID_AND_ATTRIBUTES
|
|
//
|
|
|
|
nBufferSize =
|
|
sizeof(TOKEN_PRIVILEGES) +
|
|
((PrivilegeCount - 1) * sizeof(LUID_AND_ATTRIBUTES));
|
|
|
|
pTokenPrivileges = (PTOKEN_PRIVILEGES)
|
|
pSetupMalloc(nBufferSize);
|
|
|
|
if (pTokenPrivileges == NULL) {
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
//
|
|
// Initialize the Privileges Structure
|
|
//
|
|
|
|
pTokenPrivileges->PrivilegeCount = PrivilegeCount;
|
|
for (i = 0; i < PrivilegeCount; i++) {
|
|
pTokenPrivileges->Privileges[i].Luid = RtlConvertUlongToLuid(Privileges[i]);
|
|
pTokenPrivileges->Privileges[i].Attributes = SE_PRIVILEGE_ENABLED;
|
|
}
|
|
|
|
//
|
|
// Open the thread token for TOKEN_DUPLICATE access. We also required
|
|
// READ_CONTROL access to read the security descriptor information.
|
|
//
|
|
|
|
hToken = hOriginalThreadToken = INVALID_HANDLE_VALUE;
|
|
|
|
bResult =
|
|
OpenThreadToken(
|
|
GetCurrentThread(),
|
|
TOKEN_DUPLICATE | READ_CONTROL,
|
|
FALSE,
|
|
&hToken);
|
|
|
|
if (bResult) {
|
|
|
|
//
|
|
// Remember the previous thread token
|
|
//
|
|
|
|
hOriginalThreadToken = hToken;
|
|
|
|
} else if (GetLastError() == ERROR_NO_TOKEN) {
|
|
|
|
//
|
|
// No thread token - open the process token.
|
|
//
|
|
|
|
//
|
|
// Note that if we failed to open the thread token for any other reason,
|
|
// we don't want to open the process token instead. The caller is
|
|
// impersonating, and opening the process token would defeat that.
|
|
// We'll simply not enable any privileges, and the caller will have to
|
|
// pass any required privilege checks on the merit of their existing
|
|
// thread token.
|
|
//
|
|
|
|
bResult =
|
|
OpenProcessToken(
|
|
GetCurrentProcess(),
|
|
TOKEN_DUPLICATE | READ_CONTROL,
|
|
&hToken);
|
|
}
|
|
|
|
if (bResult) {
|
|
|
|
ASSERT((hToken != NULL) && (hToken != INVALID_HANDLE_VALUE));
|
|
|
|
//
|
|
// Copy the security descriptor from whichever token we were able to
|
|
// retrieve so that we can apply it to the duplicated token.
|
|
//
|
|
// Note that if we cannot retrieve the security descriptor for the
|
|
// token, we will not continue on below to duplicate it with the default
|
|
// security descriptor, since it may be more restrictive than that of
|
|
// the original token, and may prevent the client from removing the
|
|
// impersonation token from the thread when restoring privileges.
|
|
//
|
|
|
|
bResult =
|
|
GetKernelObjectSecurity(
|
|
hToken,
|
|
DACL_SECURITY_INFORMATION,
|
|
NULL,
|
|
0,
|
|
&nBufferSize);
|
|
|
|
if ((!bResult) &&
|
|
(GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
|
|
|
|
ASSERT(nBufferSize > 0);
|
|
|
|
pSecurityDescriptor = (PSECURITY_DESCRIPTOR)
|
|
pSetupMalloc(nBufferSize);
|
|
|
|
if (pSecurityDescriptor != NULL) {
|
|
|
|
bResult =
|
|
GetKernelObjectSecurity(
|
|
hToken,
|
|
DACL_SECURITY_INFORMATION,
|
|
pSecurityDescriptor,
|
|
nBufferSize,
|
|
&nBufferSize);
|
|
}
|
|
|
|
} else {
|
|
bResult = FALSE;
|
|
}
|
|
}
|
|
|
|
if (bResult) {
|
|
|
|
ASSERT(pSecurityDescriptor != NULL);
|
|
|
|
//
|
|
// Duplicate whichever token we were able to retrieve, using the
|
|
// token's security descriptor.
|
|
//
|
|
|
|
ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
|
|
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
sa.lpSecurityDescriptor = pSecurityDescriptor;
|
|
sa.bInheritHandle = FALSE;
|
|
|
|
bResult =
|
|
DuplicateTokenEx(
|
|
hToken,
|
|
TOKEN_IMPERSONATE | TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
|
|
&sa, // PSECURITY_ATTRIBUTES
|
|
SecurityImpersonation, // SECURITY_IMPERSONATION_LEVEL
|
|
TokenImpersonation, // TokenType
|
|
&hNewToken); // Duplicate token
|
|
|
|
if (bResult) {
|
|
|
|
ASSERT((hNewToken != NULL) && (hNewToken != INVALID_HANDLE_VALUE));
|
|
|
|
//
|
|
// Adjust the privileges of the duplicated token. We don't care
|
|
// about its previous state because we still have the original
|
|
// token.
|
|
//
|
|
|
|
bResult =
|
|
AdjustTokenPrivileges(
|
|
hNewToken, // TokenHandle
|
|
FALSE, // DisableAllPrivileges
|
|
pTokenPrivileges, // NewState
|
|
0, // BufferLength
|
|
NULL, // PreviousState
|
|
NULL); // ReturnLength
|
|
|
|
if (bResult) {
|
|
//
|
|
// Begin impersonating with the new token
|
|
//
|
|
bResult =
|
|
SetThreadToken(
|
|
NULL,
|
|
hNewToken);
|
|
}
|
|
|
|
CloseHandle(hNewToken);
|
|
}
|
|
}
|
|
|
|
//
|
|
// If something failed, don't return a token
|
|
//
|
|
|
|
if (!bResult) {
|
|
hOriginalThreadToken = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
//
|
|
// Close the original token if we aren't returning it
|
|
//
|
|
|
|
if ((hOriginalThreadToken == INVALID_HANDLE_VALUE) &&
|
|
(hToken != INVALID_HANDLE_VALUE)) {
|
|
CloseHandle(hToken);
|
|
}
|
|
|
|
//
|
|
// If we succeeded, but there was no original thread token, return NULL.
|
|
// PnPRestorePrivileges will simply remove the current thread token.
|
|
//
|
|
|
|
if (bResult && (hOriginalThreadToken == INVALID_HANDLE_VALUE)) {
|
|
hOriginalThreadToken = NULL;
|
|
}
|
|
|
|
if (pSecurityDescriptor != NULL) {
|
|
pSetupFree(pSecurityDescriptor);
|
|
}
|
|
|
|
pSetupFree(pTokenPrivileges);
|
|
|
|
return hOriginalThreadToken;
|
|
|
|
} // PnPEnablePrivileges
|
|
|
|
|
|
|
|
VOID
|
|
PnPRestorePrivileges(
|
|
IN HANDLE hToken
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine restores the privileges of the calling thread to their state
|
|
prior to a corresponding call to PnPEnablePrivileges.
|
|
|
|
Arguments:
|
|
|
|
hToken - Return value from corresponding call to PnPEnablePrivileges.
|
|
|
|
Return value:
|
|
|
|
None.
|
|
|
|
Notes:
|
|
|
|
If the corresponding call to PnPEnablePrivileges returned a handle to the
|
|
previous thread token, this routine will restore it, and close the handle.
|
|
|
|
If PnPEnablePrivileges returned NULL, no thread token previously existed.
|
|
This routine will remove any existing token from the thread.
|
|
|
|
If PnPEnablePrivileges returned INVALID_HANDLE_VALUE, the attempt to enable
|
|
the specified privileges failed, but the previous state of the thread was
|
|
not modified. This routine does nothing.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL bResult;
|
|
|
|
|
|
//
|
|
// First, check if we actually need to do anything for this thread.
|
|
//
|
|
|
|
if (hToken != INVALID_HANDLE_VALUE) {
|
|
|
|
//
|
|
// Call SetThreadToken for the current thread with the specified hToken.
|
|
// If the handle value is NULL, SetThreadToken will remove the current
|
|
// thread token from the thread. Ignore the return, there's nothing we
|
|
// can do about it.
|
|
//
|
|
|
|
bResult = SetThreadToken(NULL, hToken);
|
|
|
|
if (hToken != NULL) {
|
|
//
|
|
// Close the handle to the token.
|
|
//
|
|
CloseHandle(hToken);
|
|
}
|
|
}
|
|
|
|
return;
|
|
|
|
} // PnPRestorePrivileges
|
|
|
|
|
|
|
|
CONFIGRET
|
|
IsRemoteServiceRunning(
|
|
IN LPCWSTR UNCServerName,
|
|
IN LPCWSTR ServiceName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine connects to the active service database of the Service Control
|
|
Manager (SCM) on the machine specified and returns whether or not the
|
|
specified service is running.
|
|
|
|
Arguments:
|
|
|
|
UNCServerName - Specifies the name of the remote machine.
|
|
|
|
ServiceName - Specifies the name of the service whose status is to be
|
|
queried.
|
|
|
|
Return value:
|
|
|
|
Returns TRUE if the specified service is installed on the remote machine and
|
|
is currently in the SERVICE_RUNNING state, FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
CONFIGRET Status = CR_SUCCESS;
|
|
DWORD Err;
|
|
SC_HANDLE hSCManager = NULL, hService = NULL;
|
|
SERVICE_STATUS ServiceStatus;
|
|
|
|
|
|
//
|
|
// Open the Service Control Manager
|
|
//
|
|
hSCManager = OpenSCManager(
|
|
UNCServerName, // computer name
|
|
SERVICES_ACTIVE_DATABASE, // SCM database name
|
|
SC_MANAGER_CONNECT // access type
|
|
);
|
|
|
|
if (hSCManager == NULL) {
|
|
Err = GetLastError();
|
|
KdPrintEx((DPFLTR_PNPMGR_ID,
|
|
DBGF_WARNINGS,
|
|
"CFGMGR32: OpenSCManager failed, error = %d\n",
|
|
Err));
|
|
if (Err == ERROR_ACCESS_DENIED) {
|
|
Status = CR_ACCESS_DENIED;
|
|
} else {
|
|
Status = CR_MACHINE_UNAVAILABLE;
|
|
}
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// Open the service
|
|
//
|
|
hService = OpenService(
|
|
hSCManager, // handle to SCM database
|
|
ServiceName, // service name
|
|
SERVICE_QUERY_STATUS // access type
|
|
);
|
|
|
|
if (hService == NULL) {
|
|
Err = GetLastError();
|
|
KdPrintEx((DPFLTR_PNPMGR_ID,
|
|
DBGF_WARNINGS,
|
|
"CFGMGR32: OpenService failed, error = %d\n",
|
|
Err));
|
|
if (Err == ERROR_ACCESS_DENIED) {
|
|
Status = CR_ACCESS_DENIED;
|
|
} else {
|
|
Status = CR_NO_CM_SERVICES;
|
|
}
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// Query the service status
|
|
//
|
|
if (!QueryServiceStatus(hService,
|
|
&ServiceStatus)) {
|
|
Err = GetLastError();
|
|
KdPrintEx((DPFLTR_PNPMGR_ID,
|
|
DBGF_WARNINGS,
|
|
"CFGMGR32: QueryServiceStatus failed, error = %d\n",
|
|
Err));
|
|
if (Err == ERROR_ACCESS_DENIED) {
|
|
Status = CR_ACCESS_DENIED;
|
|
} else {
|
|
Status = CR_NO_CM_SERVICES;
|
|
}
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// Check if the service is running.
|
|
//
|
|
if (ServiceStatus.dwCurrentState != SERVICE_RUNNING) {
|
|
Status = CR_NO_CM_SERVICES;
|
|
goto Clean0;
|
|
}
|
|
|
|
Clean0:
|
|
|
|
if (hService) {
|
|
CloseServiceHandle(hService);
|
|
}
|
|
|
|
if (hSCManager) {
|
|
CloseServiceHandle(hSCManager);
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // IsRemoteServiceRunning
|
|
|
|
|