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.
597 lines
17 KiB
597 lines
17 KiB
/*++
|
|
|
|
|
|
Copyright (c) 1991 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
Regqval.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the server side implementation for the Win32
|
|
Registry query value API. That is:
|
|
|
|
- BaseRegQueryValue
|
|
- BaseRegQueryMultipleValues
|
|
- BaseRegQueryMultipleValues2
|
|
|
|
Author:
|
|
|
|
David J. Gilman (davegi) 27-Nov-1991
|
|
|
|
Notes:
|
|
|
|
See the Notes in Regkey.c.
|
|
|
|
Revision History:
|
|
|
|
Dragos C. Sambotin (dragoss) 12-Nov-2001
|
|
Add new method to close DoS attack when the server is modifying ldwTotsize
|
|
to a value bigger than the actual buffer size and RPC checks the buffer on return.
|
|
|
|
--*/
|
|
|
|
#include <rpc.h>
|
|
#include "regrpc.h"
|
|
#include "localreg.h"
|
|
#include "regclass.h"
|
|
#include "regvcls.h"
|
|
|
|
|
|
#define DEFAULT_VALUE_SIZE 128
|
|
#define DEFAULT_VALUE_NAME_SIZE 64
|
|
|
|
|
|
error_status_t
|
|
BaseRegQueryValue(
|
|
IN HKEY hKey,
|
|
IN PUNICODE_STRING lpValueName,
|
|
OUT LPDWORD lpType OPTIONAL,
|
|
OUT LPBYTE lpData OPTIONAL,
|
|
OUT LPDWORD lpcbData OPTIONAL,
|
|
IN OUT LPDWORD lpcbLen OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
For an open key, given the ID of the value to query, return
|
|
the type, and value.
|
|
|
|
Arguments:
|
|
|
|
hKey - Supplies a handle to the open key. The value entries returned
|
|
are contained in the key pointed to by this key handle. Any of the
|
|
predefined reserved handles or a previously opened key handle may be
|
|
used for hKey.
|
|
|
|
lpValueName - Supplies a pointer to the name of the value.
|
|
|
|
lpType - If present, supplies a pointer to variable to receive the
|
|
type code of value entry.
|
|
|
|
lpData -If present, supplies a pointer to a buffer to receive the
|
|
data of the value entry.
|
|
|
|
lpcbData - Supplies a pointer to a variable which on input contains
|
|
the size of the buffer lpData points to. On output, the variable will
|
|
receive the number of bytes returned in lpData. It must be supplied
|
|
if lpData is, it is ignored otherwise.
|
|
|
|
lpcbLen - Return the number of bytes to transmit to the client (used
|
|
by RPC).
|
|
|
|
Return Value:
|
|
|
|
Returns ERROR_SUCCESS (0) for success; error-code for failure.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG BufferLength;
|
|
KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass;
|
|
PVOID KeyValueInformation;
|
|
ULONG ResultLength;
|
|
HKEY hkQueryKey;
|
|
|
|
BYTE PrivateKeyValueInformation[ sizeof( KEY_VALUE_PARTIAL_INFORMATION ) +
|
|
DEFAULT_VALUE_SIZE ];
|
|
#ifdef LOCAL
|
|
HKEY hkUserClasses;
|
|
HKEY hkMachineClasses;
|
|
|
|
hkUserClasses = NULL;
|
|
hkMachineClasses = NULL;
|
|
#endif LOCAL
|
|
hkQueryKey = hKey;
|
|
|
|
if( (lpValueName == NULL) ||
|
|
(!lpValueName->Length) ||
|
|
(lpValueName->Length & 1) ||
|
|
(lpcbData == NULL) ||
|
|
(lpcbLen == NULL) ||
|
|
(lpType == NULL) ) {
|
|
//
|
|
// malicious client/RPC attack
|
|
//
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// If the client gave us a bogus size, patch it.
|
|
//
|
|
if ( ARGUMENT_PRESENT( lpcbData ) && !ARGUMENT_PRESENT( lpData ) ) {
|
|
*lpcbData = 0;
|
|
}
|
|
|
|
|
|
//
|
|
// Call out to Perflib if the HKEY is HKEY_PERFOMANCE_DATA.
|
|
//
|
|
|
|
if(( hKey == HKEY_PERFORMANCE_DATA ) ||
|
|
( hKey == HKEY_PERFORMANCE_TEXT ) ||
|
|
( hKey == HKEY_PERFORMANCE_NLSTEXT)) {
|
|
error_status_t Error;
|
|
|
|
//
|
|
// Impersonate the client.
|
|
//
|
|
|
|
RPC_IMPERSONATE_CLIENT( NULL );
|
|
|
|
Error = (error_status_t)PerfRegQueryValue (
|
|
hKey,
|
|
lpValueName,
|
|
NULL,
|
|
lpType,
|
|
lpData,
|
|
lpcbData,
|
|
lpcbLen
|
|
);
|
|
RPC_REVERT_TO_SELF();
|
|
return(Error);
|
|
}
|
|
|
|
ASSERT( IsPredefinedRegistryHandle( hKey ) == FALSE );
|
|
|
|
//
|
|
// Subtract the NULL from the Length. This was added on
|
|
// the client side so that RPC would transmit it.
|
|
//
|
|
if ( lpValueName->Length > 0 ) {
|
|
lpValueName->Length -= sizeof( UNICODE_NULL );
|
|
}
|
|
|
|
//
|
|
// First we assume that the information we want will fit on
|
|
// PrivateKeyValueInformattion
|
|
//
|
|
|
|
KeyValueInformationClass = ( ARGUMENT_PRESENT( lpcbData ))?
|
|
KeyValuePartialInformation :
|
|
KeyValueBasicInformation;
|
|
|
|
|
|
KeyValueInformation = PrivateKeyValueInformation;
|
|
BufferLength = sizeof( PrivateKeyValueInformation );
|
|
|
|
//
|
|
// Query for the necessary information about the supplied value. This
|
|
// may or may not include the data depending on lpcbData as determined
|
|
// above.
|
|
//
|
|
#ifdef LOCAL
|
|
if (REG_CLASS_IS_SPECIAL_KEY(hKey)) {
|
|
|
|
Status = BaseRegGetUserAndMachineClass(
|
|
NULL,
|
|
hKey,
|
|
MAXIMUM_ALLOWED,
|
|
&hkMachineClasses,
|
|
&hkUserClasses);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
return (error_status_t) RtlNtStatusToDosError(Status);
|
|
}
|
|
}
|
|
|
|
if (hkUserClasses && hkMachineClasses) {
|
|
hkQueryKey = hkUserClasses;
|
|
}
|
|
|
|
for (;;) {
|
|
#endif
|
|
|
|
Status = NtQueryValueKey( hkQueryKey,
|
|
lpValueName,
|
|
KeyValueInformationClass,
|
|
KeyValueInformation,
|
|
BufferLength,
|
|
&ResultLength
|
|
);
|
|
#ifdef LOCAL
|
|
//
|
|
// If we don't have two classes keys to worry about,
|
|
// just continue as we normally would
|
|
//
|
|
if (!hkUserClasses || !hkMachineClasses) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If we're using machine, then we don't want to repeat
|
|
// since machine is the last resort
|
|
//
|
|
if (hkQueryKey == hkMachineClasses) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If the key doesn't exist in user, then let's try
|
|
// again in machine
|
|
//
|
|
if (STATUS_OBJECT_NAME_NOT_FOUND == Status) {
|
|
hkQueryKey = hkMachineClasses;
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// A return value of STATUS_BUFFER_TOO_SMALL would mean that there
|
|
// was not enough room for even the known (i.e. fixed length portion)
|
|
// of the structure.
|
|
//
|
|
|
|
ASSERT( Status != STATUS_BUFFER_TOO_SMALL );
|
|
|
|
if( ( Status == STATUS_BUFFER_OVERFLOW ) &&
|
|
( !ARGUMENT_PRESENT( lpData ) ) ) {
|
|
//
|
|
// STATUS_BUFFER_OVERFLOW means that the API returned all the
|
|
// information in the fixed portion of the structure
|
|
// KEY_VALUE_BASIC_INFORMATION or KEY_VALUE_PARTIAL_INFORMATION,
|
|
// but not the value name or the value data.
|
|
//
|
|
// If KeyValueInformationClass is equal to KeyValueBasicInformation
|
|
// then the API would return the value name. But since we are not
|
|
// interested in the value name (it was supplied by the client), we
|
|
// can assume that the API succeeded.
|
|
//
|
|
// If KeyValueInformationClass is equal to KeyValuePartialInformation
|
|
// then the API would return the value data. But lpData == NULL
|
|
// means that the client is not interested on the value data, but
|
|
// just on its size. For this reason, we can also assume that the
|
|
// API succeeded.
|
|
//
|
|
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
if( Status == STATUS_BUFFER_OVERFLOW ) {
|
|
//
|
|
// The buffer defined in the stack wasn't big enough to hold
|
|
// the Value information.
|
|
// If the caller's buffer is big enough to hold the value data
|
|
// then allocate a new buffer, and call the NT API again.
|
|
//
|
|
if( ( ( KeyValueInformationClass == KeyValuePartialInformation ) &&
|
|
( ARGUMENT_PRESENT( lpData ) ) &&
|
|
( *lpcbData >=
|
|
(( PKEY_VALUE_PARTIAL_INFORMATION )
|
|
KeyValueInformation )->DataLength
|
|
)
|
|
)
|
|
) {
|
|
BufferLength = ResultLength;
|
|
|
|
KeyValueInformation = RtlAllocateHeap( RtlProcessHeap( ), 0,
|
|
BufferLength
|
|
);
|
|
//
|
|
// If the memory allocation fails, return a Registry error.
|
|
//
|
|
|
|
if( ! KeyValueInformation ) {
|
|
return ERROR_OUTOFMEMORY;
|
|
}
|
|
|
|
//
|
|
// Query for the necessary information about the supplied value.
|
|
//
|
|
|
|
Status = NtQueryValueKey( hkQueryKey,
|
|
lpValueName,
|
|
KeyValueInformationClass,
|
|
KeyValueInformation,
|
|
BufferLength,
|
|
&ResultLength
|
|
);
|
|
}
|
|
}
|
|
|
|
#ifdef LOCAL
|
|
if (hkUserClasses && hkMachineClasses) {
|
|
if (hkUserClasses != hKey) {
|
|
NtClose(hkUserClasses);
|
|
} else {
|
|
NtClose(hkMachineClasses);
|
|
}
|
|
}
|
|
#endif // LOCAL
|
|
|
|
if( NT_SUCCESS( Status ) &&
|
|
ARGUMENT_PRESENT( lpData ) ) {
|
|
//
|
|
// If requested, copy the value data
|
|
//
|
|
if( *lpcbData >= (( PKEY_VALUE_PARTIAL_INFORMATION )
|
|
KeyValueInformation )->DataLength ) {
|
|
|
|
RtlMoveMemory( lpData,
|
|
(( PKEY_VALUE_PARTIAL_INFORMATION )
|
|
KeyValueInformation )->Data,
|
|
(( PKEY_VALUE_PARTIAL_INFORMATION )
|
|
KeyValueInformation )->DataLength
|
|
);
|
|
} else {
|
|
Status = STATUS_BUFFER_OVERFLOW;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Certain information is returned on success or in the case of
|
|
// NtQueryValueKey returning STATUS_BUFFER_OVERFLOW. This information
|
|
// is always available because we always pass the minimum size required for
|
|
// the NtQueryValueKey API.
|
|
//
|
|
|
|
if( NT_SUCCESS( Status ) ||
|
|
( Status == STATUS_BUFFER_OVERFLOW ) ) {
|
|
|
|
if( KeyValueInformationClass == KeyValueBasicInformation ) {
|
|
|
|
//
|
|
// If requested, return the value type.
|
|
//
|
|
|
|
if( ARGUMENT_PRESENT( lpType )) {
|
|
|
|
*lpType = (( PKEY_VALUE_BASIC_INFORMATION )
|
|
KeyValueInformation )->Type;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// If requested, return the value type.
|
|
//
|
|
|
|
if( ARGUMENT_PRESENT( lpType )) {
|
|
|
|
*lpType = (( PKEY_VALUE_PARTIAL_INFORMATION )
|
|
KeyValueInformation )->Type;
|
|
}
|
|
|
|
//
|
|
// Return the value data size
|
|
//
|
|
*lpcbData = (( PKEY_VALUE_PARTIAL_INFORMATION )
|
|
KeyValueInformation )->DataLength;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Transmit all of the data back to the client.
|
|
//
|
|
|
|
if( ARGUMENT_PRESENT( lpcbLen ) ) {
|
|
if( NT_SUCCESS( Status ) &&
|
|
ARGUMENT_PRESENT( lpData ) ) {
|
|
*lpcbLen = (( PKEY_VALUE_PARTIAL_INFORMATION )
|
|
KeyValueInformation )->DataLength;
|
|
} else {
|
|
//
|
|
// The API failed, so make sure that no data is transmitted back
|
|
// to the client. This ensures that the client stub will not
|
|
// attempt to unmarshall data that doesn't exist.
|
|
//
|
|
|
|
*lpcbLen = 0;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If memory was allocated, then free it
|
|
//
|
|
if( KeyValueInformation != PrivateKeyValueInformation ) {
|
|
|
|
RtlFreeHeap( RtlProcessHeap( ), 0, KeyValueInformation );
|
|
}
|
|
return (error_status_t)RtlNtStatusToDosError( Status );
|
|
}
|
|
|
|
error_status_t
|
|
BaseRegQueryMultipleValues(
|
|
IN HKEY hKey,
|
|
IN OUT PRVALENT val_list,
|
|
IN DWORD num_vals,
|
|
OUT LPSTR lpvalueBuf,
|
|
IN OUT LPDWORD ldwTotsize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Stub: Just calls into the new method. Never changes the
|
|
ldwTotsize value to a vallue bigger than what was passed in.
|
|
|
|
--*/
|
|
{
|
|
ULONG RequiredLength;
|
|
error_status_t Error;
|
|
|
|
if( ldwTotsize == NULL ) {
|
|
//
|
|
// malicious client/RPC attack
|
|
//
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
Error = BaseRegQueryMultipleValues2(hKey,val_list,num_vals,lpvalueBuf,ldwTotsize,&RequiredLength);
|
|
|
|
//
|
|
// only for backward compatibility
|
|
//
|
|
if( Error == ERROR_SUCCESS ) {
|
|
*ldwTotsize = RequiredLength;
|
|
} else {
|
|
//
|
|
// The API failed, so make sure that no data is transmitted back
|
|
// to the client. This ensures that the client stub will not
|
|
// attempt to unmarshall data that doesn't exist.
|
|
//
|
|
|
|
*ldwTotsize = 0;
|
|
}
|
|
|
|
|
|
return Error;
|
|
}
|
|
|
|
error_status_t
|
|
BaseRegQueryMultipleValues2(
|
|
IN HKEY hKey,
|
|
IN OUT PRVALENT val_list,
|
|
IN DWORD num_vals,
|
|
OUT LPSTR lpvalueBuf,
|
|
IN LPDWORD ldwTotsize,
|
|
OUT LPDWORD ldwRequiredSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
For an open key, atomically queries a set of values.
|
|
|
|
Arguments:
|
|
|
|
hKey - Supplies a handle to the open key. The value entries returned
|
|
are contained in the key pointed to by this key handle. Any of
|
|
the predefined reserved handles or a previously opened key handle
|
|
may be used for hKey.
|
|
|
|
val_list - Supplies a pointer to an array of RVALENT structures, one for
|
|
each value to be queried.
|
|
|
|
num_vals - Supplies the size in bytes of the val_list array.
|
|
|
|
lpValueBuf - Returns the data for each value
|
|
|
|
ldwTotsize - Supplies the length of lpValueBuf. Returns the number of bytes
|
|
written into lpValueBuf.
|
|
|
|
ldwRequiredSize - If lpValueBuf is not large enough to contain all the data,
|
|
returns the size of lpValueBuf required to return all the
|
|
requested data here. Otherwise this value is not touched.
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
Returns ERROR_SUCCESS (0) for success; error-code for failure.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
DWORD LocalTotSize;
|
|
ULONG i;
|
|
|
|
//
|
|
// Call out to Perflib if the HKEY is HKEY_PERFOMANCE_DATA.
|
|
//
|
|
|
|
if(( hKey == HKEY_PERFORMANCE_DATA ) ||
|
|
( hKey == HKEY_PERFORMANCE_TEXT ) ||
|
|
( hKey == HKEY_PERFORMANCE_NLSTEXT)) {
|
|
|
|
return((error_status_t)ERROR_CALL_NOT_IMPLEMENTED);
|
|
}
|
|
|
|
if( (ldwTotsize == NULL) ||
|
|
(ldwRequiredSize == NULL) ||
|
|
(num_vals && (val_list == NULL ))
|
|
) {
|
|
//
|
|
// malicious client/RPC attack
|
|
//
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
LocalTotSize = *ldwTotsize;
|
|
//
|
|
// Subtract the NULLs from the Length. This was added on
|
|
// the client side so that RPC would transmit it.
|
|
//
|
|
for (i=0; i<num_vals; i++) {
|
|
if( (val_list[i].rv_valuename == NULL) ||
|
|
(val_list[i].rv_valuename->Length & 1) ) {
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
if (val_list[i].rv_valuename->Length > 0) {
|
|
val_list[i].rv_valuename->Length -= sizeof( UNICODE_NULL );
|
|
}
|
|
}
|
|
|
|
#ifdef LOCAL
|
|
|
|
//
|
|
// For class keys in hkcr, we need to merge the data
|
|
//
|
|
if (REG_CLASS_IS_SPECIAL_KEY(hKey)) {
|
|
|
|
Status = BaseRegQueryMultipleClassKeyValues(
|
|
hKey,
|
|
val_list,
|
|
num_vals,
|
|
lpvalueBuf,
|
|
&LocalTotSize,
|
|
ldwRequiredSize);
|
|
|
|
} else {
|
|
|
|
#endif // LOCAL
|
|
|
|
Status = NtQueryMultipleValueKey(hKey,
|
|
(PKEY_VALUE_ENTRY)val_list,
|
|
num_vals,
|
|
lpvalueBuf,
|
|
&LocalTotSize,
|
|
ldwRequiredSize);
|
|
|
|
#ifdef LOCAL
|
|
|
|
}
|
|
|
|
#endif // LOCAL
|
|
|
|
|
|
return(error_status_t)RtlNtStatusToDosError(Status);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|