Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

1873 lines
64 KiB

/*++
Copyright (c) 1990 Microsoft Corporation
Module Name:
regutil.c
Abstract:
This file contains support routines for accessing the registry.
Author:
Steve Wood (stevewo) 15-Apr-1992
Revision History:
--*/
#include "ntrtlp.h"
#include <ctype.h>
NTSTATUS
RtlpGetRegistryHandle(
IN ULONG RelativeTo,
IN PCWSTR KeyName,
IN BOOLEAN WriteAccess,
OUT PHANDLE Key
);
NTSTATUS
RtlpQueryRegistryDirect(
IN ULONG ValueType,
IN PVOID ValueData,
IN ULONG ValueLength,
IN OUT PVOID Destination
);
NTSTATUS
RtlpCallQueryRegistryRoutine(
IN PRTL_QUERY_REGISTRY_TABLE QueryTable,
IN PKEY_VALUE_FULL_INFORMATION KeyValueInformation,
IN OUT PULONG PKeyValueInfoLength,
IN PVOID Context,
IN PVOID Environment OPTIONAL
);
PVOID
RtlpAllocDeallocQueryBuffer(
IN OUT SIZE_T *PAllocLength OPTIONAL,
IN PVOID OldKeyValueInformation OPTIONAL,
IN SIZE_T OldAllocLength OPTIONAL,
OUT NTSTATUS *pStatus OPTIONAL
);
NTSTATUS
RtlpInitCurrentUserString(
OUT PUNICODE_STRING UserString
);
NTSTATUS
RtlpGetTimeZoneInfoHandle(
IN BOOLEAN WriteAccess,
OUT PHANDLE Key
);
#if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
#pragma alloc_text(PAGE,RtlpGetRegistryHandle)
#pragma alloc_text(PAGE,RtlpQueryRegistryDirect)
#pragma alloc_text(PAGE,RtlpCallQueryRegistryRoutine)
#pragma alloc_text(PAGE,RtlpAllocDeallocQueryBuffer)
#pragma alloc_text(PAGE,RtlQueryRegistryValues)
#pragma alloc_text(PAGE,RtlWriteRegistryValue)
#pragma alloc_text(PAGE,RtlCheckRegistryKey)
#pragma alloc_text(PAGE,RtlCreateRegistryKey)
#pragma alloc_text(PAGE,RtlDeleteRegistryValue)
#pragma alloc_text(PAGE,RtlExpandEnvironmentStrings_U)
#pragma alloc_text(PAGE,RtlFormatCurrentUserKeyPath)
#pragma alloc_text(PAGE,RtlGetNtGlobalFlags)
#pragma alloc_text(PAGE,RtlpInitCurrentUserString)
#pragma alloc_text(PAGE,RtlOpenCurrentUser)
#pragma alloc_text(PAGE,RtlpGetTimeZoneInfoHandle)
#pragma alloc_text(PAGE,RtlQueryTimeZoneInformation)
#pragma alloc_text(PAGE,RtlSetTimeZoneInformation)
#pragma alloc_text(PAGE,RtlSetActiveTimeBias)
#endif
extern const PWSTR RtlpRegistryPaths[ RTL_REGISTRY_MAXIMUM ];
NTSTATUS
RtlpGetRegistryHandle(
IN ULONG RelativeTo,
IN PCWSTR KeyName,
IN BOOLEAN WriteAccess,
OUT PHANDLE Key
)
{
NTSTATUS Status;
OBJECT_ATTRIBUTES ObjectAttributes;
WCHAR KeyPathBuffer[ MAXIMUM_FILENAME_LENGTH+6 ];
UNICODE_STRING KeyPath;
UNICODE_STRING CurrentUserKeyPath;
BOOLEAN OptionalPath;
if (RelativeTo & RTL_REGISTRY_HANDLE) {
*Key = (HANDLE)KeyName;
return STATUS_SUCCESS;
}
if (RelativeTo & RTL_REGISTRY_OPTIONAL) {
RelativeTo &= ~RTL_REGISTRY_OPTIONAL;
OptionalPath = TRUE;
} else {
OptionalPath = FALSE;
}
if (RelativeTo >= RTL_REGISTRY_MAXIMUM) {
return STATUS_INVALID_PARAMETER;
}
KeyPath.Buffer = KeyPathBuffer;
KeyPath.Length = 0;
KeyPath.MaximumLength = sizeof( KeyPathBuffer );
if (RelativeTo != RTL_REGISTRY_ABSOLUTE) {
if (RelativeTo == RTL_REGISTRY_USER &&
NT_SUCCESS( RtlFormatCurrentUserKeyPath( &CurrentUserKeyPath ) )
) {
Status = RtlAppendUnicodeStringToString( &KeyPath, &CurrentUserKeyPath );
RtlFreeUnicodeString( &CurrentUserKeyPath );
} else {
Status = RtlAppendUnicodeToString( &KeyPath, RtlpRegistryPaths[ RelativeTo ] );
}
if (!NT_SUCCESS( Status )) {
return Status;
}
Status = RtlAppendUnicodeToString( &KeyPath, L"\\" );
if (!NT_SUCCESS( Status )) {
return Status;
}
}
Status = RtlAppendUnicodeToString( &KeyPath, KeyName );
if (!NT_SUCCESS( Status )) {
return Status;
}
//
// Use a kernel-mode handle for the registry key to prevent
// malicious apps from hijacking it.
//
InitializeObjectAttributes( &ObjectAttributes,
&KeyPath,
OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE,
NULL,
NULL
);
if (WriteAccess) {
Status = ZwCreateKey( Key,
GENERIC_WRITE,
&ObjectAttributes,
0,
(PUNICODE_STRING) NULL,
0,
NULL
);
} else {
Status = ZwOpenKey( Key,
MAXIMUM_ALLOWED | GENERIC_READ,
&ObjectAttributes
);
}
return Status;
}
//
// This is the maximum MaximumLength for a UNICODE_STRING.
//
#define MAX_USTRING ( sizeof(WCHAR) * (MAXUSHORT/sizeof(WCHAR)) )
//
// This is the maximum MaximumLength for a UNICODE_STRING that still leaves
// room for a UNICODE_NULL.
//
#define MAX_NONNULL_USTRING ( MAX_USTRING - sizeof(UNICODE_NULL) )
//
// Return a registry value for RTL_QUERY_REGISTRY_DIRECT.
// For string values, ValueLength includes the UNICODE_NULL.
// Truncate string values if they don't fit within a UNICODE_STRING.
//
NTSTATUS
RtlpQueryRegistryDirect(
IN ULONG ValueType,
IN PVOID ValueData,
IN ULONG ValueLength,
IN OUT PVOID Destination
)
{
if (ValueType == REG_SZ ||
ValueType == REG_EXPAND_SZ ||
ValueType == REG_MULTI_SZ
) {
PUNICODE_STRING DestinationString;
USHORT TruncValueLength;
//
// Truncate ValueLength to be represented in a UNICODE_STRING
//
if ( ValueLength <= MAX_USTRING ) {
TruncValueLength = (USHORT)ValueLength;
} else {
TruncValueLength = MAX_USTRING;
#if DBG
DbgPrint("RtlpQueryRegistryDirect: truncating SZ Value length: %x -> %x\n",
ValueLength, TruncValueLength);
#endif //DBG
}
DestinationString = (PUNICODE_STRING)Destination;
if (DestinationString->Buffer == NULL) {
DestinationString->Buffer = RtlAllocateStringRoutine( TruncValueLength );
if (!DestinationString->Buffer) {
return STATUS_NO_MEMORY;
}
DestinationString->MaximumLength = TruncValueLength;
} else if (TruncValueLength > DestinationString->MaximumLength) {
return STATUS_BUFFER_TOO_SMALL;
}
RtlCopyMemory( DestinationString->Buffer, ValueData, TruncValueLength );
DestinationString->Length = (TruncValueLength - sizeof(UNICODE_NULL));
} else if (ValueLength <= sizeof( ULONG )) {
RtlCopyMemory( Destination, ValueData, ValueLength );
} else {
PULONG DestinationLength;
DestinationLength = (PULONG)Destination;
if ((LONG)*DestinationLength < 0) {
ULONG n = -(LONG)*DestinationLength;
if (n < ValueLength) {
return STATUS_BUFFER_TOO_SMALL;
}
RtlCopyMemory( DestinationLength, ValueData, ValueLength );
} else {
if (*DestinationLength < (2 * sizeof(*DestinationLength) + ValueLength)) {
return STATUS_BUFFER_TOO_SMALL;
}
*DestinationLength++ = ValueLength;
*DestinationLength++ = ValueType;
RtlCopyMemory( DestinationLength, ValueData, ValueLength );
}
}
return STATUS_SUCCESS;
}
#define QuadAlignPtr(P) ( \
(PVOID)((((ULONG_PTR)(P)) + 7) & (-8)) \
)
NTSTATUS
RtlpCallQueryRegistryRoutine(
IN PRTL_QUERY_REGISTRY_TABLE QueryTable,
IN PKEY_VALUE_FULL_INFORMATION KeyValueInformation,
IN OUT PULONG PKeyValueInfoLength,
IN PVOID Context,
IN PVOID Environment OPTIONAL
)
/*++
Routine Description:
This function implements the caller out the a caller specified
routine. It is reponsible for capturing the arguments for the
routine and then calling it. If not specifically disabled, this
routine will converted REG_EXPAND_SZ Registry values to REG_SZ by
calling RtlExpandEnvironmentStrings_U prior to calling the routine.
It will also converted REG_MULTI_SZ registry values into multiple
REG_SZ calls to the specified routine.
N.B. UNICODE_STRINGs cannot handle strings exceeding MAX_USTRING bytes. This creates
issues both for expansion and for returning queries. Whenever this limitation
is a encountered, we punt as best we can -- often returning an unexpanded, or perhaps
truncated stream -- since this seems to create fewer problems for our callers than
if we unexpectedly fail.
Arguments:
QueryTable - specifies the current query table entry.
KeyValueInformation - points to a buffer that contains the information
about the current registry value.
PKeyValueInfoLength - pointer to the maximum length of the KeyValueInformation
buffer. This function will use the
unused portion at the end of this buffer for storing null terminated
value name strings and the expanded version of REG_EXPAND_SZ values.
PKeyValueInfoLength returns an estimate of the space required if
STATUS_BUFFER_TOO_SMALL is returned. This estimate can be used to retry
with a larger buffer. Two retries may be required if REG_EXPAND_SZ is specified.
Context - specifies a 32-bit quantity that is passed uninterpreted to
each QueryRoutine called.
Environment - optional parameter, that if specified is the environment
used when expanding variable values in REG_EXPAND_SZ registry
values.
Return Value:
Status of the operation.
--*/
{
NTSTATUS Status;
ULONG ValueType;
PWSTR ValueName;
PVOID ValueData;
ULONG ValueLength;
PWSTR s;
PCHAR FreeMem;
PCHAR EndFreeMem;
LONG FreeMemSize;
ULONG KeyValueInfoLength;
int retries;
//
// Return 0 length unless we return STATUS_BUFFER_TOO_SMALL.
//
KeyValueInfoLength = *PKeyValueInfoLength;
*PKeyValueInfoLength = 0;
//
// the registry has signaled no data for this value
//
if( KeyValueInformation->DataOffset == (ULONG)-1 ) {
//
// Return success unless this is a required value.
//
if ( QueryTable->Flags & RTL_QUERY_REGISTRY_REQUIRED ) {
return STATUS_OBJECT_NAME_NOT_FOUND;
} else {
return STATUS_SUCCESS;
}
}
//
// Initially assume the entire KeyValueInformation buffer is unused.
//
FreeMem = (PCHAR)KeyValueInformation;
FreeMemSize = KeyValueInfoLength;
EndFreeMem = FreeMem + FreeMemSize;
if (KeyValueInformation->Type == REG_NONE ||
(KeyValueInformation->DataLength == 0 &&
KeyValueInformation->Type == QueryTable->DefaultType)
) {
//
// If there is no registry value then see if they want to default
// this value.
//
if (QueryTable->DefaultType == REG_NONE) {
//
// No default value specified. Return success unless this is
// a required value.
//
if ( QueryTable->Flags & RTL_QUERY_REGISTRY_REQUIRED ) {
return STATUS_OBJECT_NAME_NOT_FOUND;
} else {
return STATUS_SUCCESS;
}
}
//
// Default requested. Setup the value data pointers from the
// information in the table entry.
//
ValueName = QueryTable->Name,
ValueType = QueryTable->DefaultType;
ValueData = QueryTable->DefaultData;
ValueLength = QueryTable->DefaultLength;
if (ValueLength == 0) {
//
// If the length of the value is zero, then calculate the
// actual length for REG_SZ, REG_EXPAND_SZ and REG_MULTI_SZ
// value types.
//
s = (PWSTR)ValueData;
if (ValueType == REG_SZ || ValueType == REG_EXPAND_SZ) {
while (*s++ != UNICODE_NULL) {
}
ValueLength = (ULONG)((PCHAR)s - (PCHAR)ValueData);
} else if (ValueType == REG_MULTI_SZ) {
while (*s != UNICODE_NULL) {
while (*s++ != UNICODE_NULL) {
}
}
ValueLength = (ULONG)((PCHAR)s - (PCHAR)ValueData) + sizeof( UNICODE_NULL );
}
}
} else {
if (!(QueryTable->Flags & RTL_QUERY_REGISTRY_DIRECT)) {
LONG ValueSpaceNeeded;
//
// There is a registry value. Calculate a pointer to the
// free memory at the end of the value information buffer,
// and its size.
//
if (KeyValueInformation->DataLength) {
FreeMem += KeyValueInformation->DataOffset +
KeyValueInformation->DataLength;
} else {
FreeMem += FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name) +
KeyValueInformation->NameLength;
}
FreeMem = (PCHAR)QuadAlignPtr(FreeMem);
FreeMemSize = (ULONG) (EndFreeMem - FreeMem);
//
// See if there is room in the free memory area for a null
// terminated copy of the value name string. If not return
// the length we require (so far) and an error.
//
ValueSpaceNeeded = KeyValueInformation->NameLength + sizeof(UNICODE_NULL);
if ( FreeMemSize < ValueSpaceNeeded ) {
*PKeyValueInfoLength = (ULONG)(((PCHAR)FreeMem - (PCHAR)KeyValueInformation) + ValueSpaceNeeded);
return STATUS_BUFFER_TOO_SMALL;
}
//
// There is room, so copy the string, and null terminate it.
//
ValueName = (PWSTR)FreeMem;
RtlCopyMemory( ValueName,
KeyValueInformation->Name,
KeyValueInformation->NameLength
);
*(PWSTR)((PCHAR)ValueName + KeyValueInformation->NameLength) = UNICODE_NULL;
//
// Update the free memory pointer and size to reflect the space we
// just used for the null terminated value name.
//
FreeMem += ValueSpaceNeeded;
FreeMem = (PCHAR)QuadAlignPtr(FreeMem);
FreeMemSize = (LONG) (EndFreeMem - FreeMem);
} else {
ValueName = QueryTable->Name;
}
//
// Get the remaining data for the registry value.
//
ValueType = KeyValueInformation->Type;
ValueData = (PCHAR)KeyValueInformation + KeyValueInformation->DataOffset;
ValueLength = KeyValueInformation->DataLength;
}
//
// Unless specifically disabled for this table entry, preprocess
// registry values of type REG_EXPAND_SZ and REG_MULTI_SZ
//
if (!(QueryTable->Flags & RTL_QUERY_REGISTRY_NOEXPAND)) {
if (ValueType == REG_MULTI_SZ) {
PWSTR ValueEnd;
//
// For REG_MULTI_SZ value type, call the query routine once
// for each null terminated string in the registry value. Fake
// like this is multiple REG_SZ values with the same value name.
//
Status = STATUS_SUCCESS;
ValueEnd = (PWSTR)((PCHAR)ValueData + ValueLength) - sizeof(UNICODE_NULL);
s = (PWSTR)ValueData;
while (s < ValueEnd) {
while (*s++ != UNICODE_NULL) {
}
ValueLength = (ULONG)((PCHAR)s - (PCHAR)ValueData);
if (QueryTable->Flags & RTL_QUERY_REGISTRY_DIRECT) {
Status = RtlpQueryRegistryDirect( REG_SZ,
ValueData,
ValueLength,
QueryTable->EntryContext
);
(PUNICODE_STRING)(QueryTable->EntryContext) += 1;
} else {
Status = (QueryTable->QueryRoutine)( ValueName,
REG_SZ,
ValueData,
ValueLength,
Context,
QueryTable->EntryContext
);
}
//
// We ignore failures where the buffer is too small.
//
if (Status == STATUS_BUFFER_TOO_SMALL) {
Status = STATUS_SUCCESS;
}
if (!NT_SUCCESS( Status )) {
break;
}
ValueData = (PVOID)s;
}
return Status;
}
//
// If requested, expand the Value -- but only if the unexpanded value
// can be represented with a UNICODE_STRING.
//
if ((ValueType == REG_EXPAND_SZ) &&
(ValueLength >= sizeof(WCHAR)) &&
(ValueLength <= MAX_NONNULL_USTRING)) {
//
// For REG_EXPAND_SZ value type, expand any environment variable
// references in the registry value string using the Rtl function.
//
UNICODE_STRING Source;
UNICODE_STRING Destination;
PWCHAR Src;
ULONG SrcLength;
ULONG RequiredLength;
BOOLEAN PercentFound;
//
// Don't expand unless we have to since expansion doubles buffer usage.
//
PercentFound = FALSE;
SrcLength = ValueLength - sizeof(WCHAR);
Src = (PWSTR)ValueData;
while (SrcLength) {
if (*Src == L'%') {
PercentFound = TRUE;
break;
}
Src++;
SrcLength -= sizeof(WCHAR);
}
if ( PercentFound ) {
Source.Buffer = (PWSTR)ValueData;
Source.MaximumLength = (USHORT)ValueLength;
Source.Length = (USHORT)(Source.MaximumLength - sizeof(UNICODE_NULL));
Destination.Buffer = (PWSTR)FreeMem;
Destination.Length = 0;
if (FreeMemSize <= 0) {
Destination.MaximumLength = 0;
} else if (FreeMemSize <= MAX_USTRING) {
Destination.MaximumLength = (USHORT)FreeMemSize;
Destination.Buffer[FreeMemSize/sizeof(WCHAR) - 1] = UNICODE_NULL;
} else {
Destination.MaximumLength = MAX_USTRING;
Destination.Buffer[MAX_USTRING/sizeof(WCHAR) - 1] = UNICODE_NULL;
}
Status = RtlExpandEnvironmentStrings_U( Environment,
&Source,
&Destination,
&RequiredLength
);
ValueType = REG_SZ;
if ( NT_SUCCESS(Status) ) {
ValueData = Destination.Buffer;
ValueLength = Destination.Length + sizeof( UNICODE_NULL );
} else {
if (Status == STATUS_BUFFER_TOO_SMALL) {
*PKeyValueInfoLength = (ULONG)((PCHAR)FreeMem - (PCHAR)KeyValueInformation) + RequiredLength;
}
//#if DBG
if (Status == STATUS_BUFFER_TOO_SMALL) {
DbgPrint( "RTL: Expand variables for %wZ failed - Status == %lx Size %x > %x <%x>\n",
&Source, Status, *PKeyValueInfoLength, KeyValueInfoLength,
Destination.MaximumLength );
} else {
DbgPrint( "RTL: Expand variables for %wZ failed - Status == %lx\n", &Source, Status );
}
//#endif // DBG
if ( Status == STATUS_BUFFER_OVERFLOW ||
Status == STATUS_BUFFER_TOO_SMALL &&
( Destination.MaximumLength == MAX_USTRING
|| RequiredLength > MAX_NONNULL_USTRING ) ) {
// We can't do variable expansion because the required buffer can't be described
// by a UNICODE_STRING, so we silently ignore expansion.
//#if DBG
DbgPrint("RtlpCallQueryRegistryRoutine: skipping expansion. Status=%x RequiredLength=%x\n",
Status, RequiredLength);
//#endif //DBG
} else {
return Status;
}
}
}
}
//#if DBG
else if (ValueType == REG_EXPAND_SZ && ValueLength > MAX_NONNULL_USTRING) {
DbgPrint("RtlpCallQueryRegistryRoutine: skipping environment expansion. ValueLength=%x\n",
ValueLength);
}
//#endif //DBG
}
//
// No special process of the registry value required so just call
// the query routine.
//
if (QueryTable->Flags & RTL_QUERY_REGISTRY_DIRECT) {
Status = RtlpQueryRegistryDirect( ValueType,
ValueData,
ValueLength,
QueryTable->EntryContext
);
} else {
Status = (QueryTable->QueryRoutine)( ValueName,
ValueType,
ValueData,
ValueLength,
Context,
QueryTable->EntryContext
);
}
//
// At this point we fail silently if the buffer is too small.
//
if (Status == STATUS_BUFFER_TOO_SMALL) {
Status = STATUS_SUCCESS;
}
return Status;
}
//
// Most of the registry queries in the kernel are small (40-50 bytes).
// User queries use ZwAllocateVirtualMemory, so nothing less than a page will do.
//
#ifdef NTOS_KERNEL_RUNTIME
#if defined(ALLOC_DATA_PRAGMA)
#pragma const_seg("PAGECONST")
#endif
const SIZE_T RtlpRegistryQueryInitialBuffersize = 0x80 + sizeof(PVOID);
#else
const SIZE_T RtlpRegistryQueryInitialBuffersize = PAGE_SIZE;
#endif
//
// Allocate, Free, or Free/Allocate space for registry queries.
//
PVOID
RtlpAllocDeallocQueryBuffer(
IN OUT SIZE_T *PAllocLength OPTIONAL,
IN PVOID OldKeyValueInformation OPTIONAL,
IN SIZE_T OldAllocLength OPTIONAL,
OUT NTSTATUS *pStatus OPTIONAL
)
{
PVOID Ptr = NULL;
NTSTATUS Status = STATUS_SUCCESS;
#ifdef NTOS_KERNEL_RUNTIME
//
// Kernel version
//
UNREFERENCED_PARAMETER( OldAllocLength );
if ( ARGUMENT_PRESENT(OldKeyValueInformation) ) {
ExFreePool( OldKeyValueInformation );
}
if ( ARGUMENT_PRESENT(PAllocLength) ) {
Ptr = ExAllocatePoolWithTag( PagedPool, *PAllocLength, 'vrqR' );
if (Ptr == NULL) {
Status = STATUS_NO_MEMORY;
}
}
#else
//
// User version
//
if ( ARGUMENT_PRESENT(OldKeyValueInformation) ) {
Status = ZwFreeVirtualMemory( NtCurrentProcess(),
&OldKeyValueInformation,
&OldAllocLength,
MEM_RELEASE );
}
if ( ARGUMENT_PRESENT(PAllocLength) ) {
Status = ZwAllocateVirtualMemory( NtCurrentProcess(),
&Ptr,
0,
PAllocLength,
MEM_COMMIT,
PAGE_READWRITE );
if (!NT_SUCCESS(Status)) {
Ptr = NULL;
}
}
#endif
if ( ARGUMENT_PRESENT(pStatus) ) {
*pStatus = Status;
}
return Ptr;
}
NTSTATUS
RtlQueryRegistryValues(
IN ULONG RelativeTo,
IN PCWSTR Path,
IN PRTL_QUERY_REGISTRY_TABLE QueryTable,
IN PVOID Context,
IN PVOID Environment OPTIONAL
)
/*++
Routine Description:
This function allows the caller to query multiple values from the registry
sub-tree with a single call. The caller specifies an initial key path,
and a table. The table contains one or more entries that describe the
key values and subkey names the caller is interested in. This function
starts at the initial key and enumerates the entries in the table. For
each entry that specifies a value name or subkey name that exists in
the registry, this function calls the caller's query routine associated
with each table entry. The caller's query routine is passed the value
name, type, data and data length, to do with what they wish.
Arguments:
RelativeTo - specifies that the Path parameter is either an absolute
registry path, or a path relative to a predefined key path. The
following values are defined:
RTL_REGISTRY_ABSOLUTE - Path is an absolute registry path
RTL_REGISTRY_SERVICES - Path is relative to \Registry\Machine\System\CurrentControlSet\Services
RTL_REGISTRY_CONTROL - Path is relative to \Registry\Machine\System\CurrentControlSet\Control
RTL_REGISTRY_WINDOWS_NT - Path is relative to \Registry\Machine\Software\Microsoft\Windows NT\CurrentVersion
RTL_REGISTRY_DEVICEMAP - Path is relative to \Registry\Machine\Hardware\DeviceMap
RTL_REGISTRY_USER - Path is relative to \Registry\User\CurrentUser
RTL_REGISTRY_OPTIONAL - Bit that specifies the key referenced by
this parameter and the Path parameter is
optional.
RTL_REGISTRY_HANDLE - Bit that specifies that the Path parameter
is actually a registry handle to use.
optional.
Path - specifies either an absolute registry path, or a path relative to the
known location specified by the RelativeTo parameter. If the the
RTL_REGISTRY_HANDLE flag is specified, then this parameter is a
registry handle to use directly.
QueryTable - specifies a table of one or more value names and subkey names
that the caller is interested. Each table entry contains a query routine
that will be called for each value name that exists in the registry.
The table is terminated when a NULL table entry is reached. A NULL
table entry is defined as a table entry with a NULL QueryRoutine
and a NULL Name field.
QueryTable entry fields:
PRTL_QUERY_REGISTRY_ROUTINE QueryRoutine - This routine is
called with the name, type, data and data length of a
registry value. If this field is NULL, then it marks the
end of the table.
ULONG Flags - These flags control how the following fields are
interpreted. The following flags are defined:
RTL_QUERY_REGISTRY_SUBKEY - says the Name field of this
table entry is another path to a registry key and all
following table entries are for that key rather than the
key specified by the Path parameter. This change in
focus lasts until the end of the table or another
RTL_QUERY_REGISTRY_SUBKEY entry is seen or
RTL_QUERY_REGISTRY_TOPKEY entry is seen. Each such
entry must specify a path that is relative to the Path
specified on the call to this function.
RTL_QUERY_REGISTRY_TOPKEY - resets the current registry key
handle to the original one specified by the RelativeTo
and Path parameters. Useful for getting back to the
original node after descending into subkeys with the
RTL_QUERY_REGISTRY_SUBKEY flag.
RTL_QUERY_REGISTRY_REQUIRED - specifies that this value is
required and if not found then STATUS_OBJECT_NAME_NOT_FOUND
is returned. For a table entry that specifies a NULL
name so that this function will enumerate all of the
value names under a key, STATUS_OBJECT_NAME_NOT_FOUND
will be returned only if there are no value keys under
the current key.
RTL_QUERY_REGISTRY_NOVALUE - specifies that even though
there is no Name field for this table entry, all the
caller wants is a call back, it does NOT want to
enumerate all the values under the current key. The
query routine is called with NULL for ValueData,
REG_NONE for ValueType and zero for ValueLength.
RTL_QUERY_REGISTRY_NOEXPAND - specifies that if the value
type of this registry value is REG_EXPAND_SZ or
REG_MULTI_SZ, then this function is NOT to do any
preprocessing of the registry values prior to calling
the query routine. Default behavior is to expand
environment variable references in REG_EXPAND_SZ
values and to enumerate the NULL terminated strings
in a REG_MULTI_SZ value and call the query routine
once for each, making it look like multiple REG_SZ
values with the same ValueName.
RTL_QUERY_REGISTRY_DIRECT QueryRoutine field ignored.
EntryContext field points to location to store value.
For null terminated strings, EntryContext points to
UNICODE_STRING structure that that describes maximum
size of buffer. If .Buffer field is NULL then a buffer
is allocated.
RTL_QUERY_REGISTRY_DELETE Used to delete value keys after
they are queried.
PWSTR Name - This field gives the name of a Value the caller
wants to query the value of. If this field is NULL, then
the QueryRoutine specified for this table entry is called
for all values associated with the current registry key.
PVOID EntryContext - This field is an arbitrary 32-bit field
that is passed uninterpreted to each QueryRoutine called.
ULONG DefaultType
PVOID DefaultData
ULONG DefaultLength If there is no value name that matches the
name given by the Name field, and the DefaultType field is
not REG_NONE, then the QueryRoutine for this table entry is
called with the contents of the following fields as if the
value had been found in the registry. If the DefaultType is
REG_SZ, REG_EXPANDSZ or REG_MULTI_SZ and the DefaultLength
is 0 then the value of DefaultLength will be computed based
on the length of unicode string pointed to by DefaultData
Context - specifies a 32-bit quantity that is passed uninterpreted to
each QueryRoutine called.
Environment - optional parameter, that if specified is the environment
used when expanding variable values in REG_EXPAND_SZ registry
values.
Return Value:
Status of the operation.
--*/
{
NTSTATUS Status;
OBJECT_ATTRIBUTES ObjectAttributes;
UNICODE_STRING KeyPath, KeyValueName;
HANDLE Key, Key1;
PKEY_VALUE_FULL_INFORMATION KeyValueInformation;
SIZE_T KeyValueInfoLength;
ULONG ValueIndex;
SIZE_T AllocLength;
ULONG KeyResultLength;
int retries;
RTL_PAGED_CODE();
KeyValueInformation = NULL;
Status = RtlpGetRegistryHandle( RelativeTo, Path, FALSE, &Key );
if (!NT_SUCCESS( Status )) {
return Status;
}
if ((RelativeTo & RTL_REGISTRY_HANDLE) == 0) {
RtlInitUnicodeString(&KeyPath, Path);
} else {
RtlInitUnicodeString(&KeyPath, NULL);
}
AllocLength = RtlpRegistryQueryInitialBuffersize;
KeyValueInformation = RtlpAllocDeallocQueryBuffer( &AllocLength, NULL, 0, &Status );
if ( KeyValueInformation == NULL ) {
if (!(RelativeTo & RTL_REGISTRY_HANDLE)) {
ZwClose( Key );
}
return Status;
}
KeyValueInformation->DataOffset = 0;
KeyValueInfoLength = AllocLength - sizeof(UNICODE_NULL);
Key1 = Key;
while (QueryTable->QueryRoutine != NULL ||
(QueryTable->Flags & (RTL_QUERY_REGISTRY_SUBKEY | RTL_QUERY_REGISTRY_DIRECT))
) {
if ((QueryTable->Flags & RTL_QUERY_REGISTRY_DIRECT) &&
(QueryTable->Name == NULL ||
(QueryTable->Flags & RTL_QUERY_REGISTRY_SUBKEY) ||
QueryTable->QueryRoutine != NULL)
) {
Status = STATUS_INVALID_PARAMETER;
break;
}
if (QueryTable->Flags & (RTL_QUERY_REGISTRY_TOPKEY | RTL_QUERY_REGISTRY_SUBKEY)) {
if (Key1 != Key) {
NtClose( Key1 );
Key1 = Key;
}
}
if (QueryTable->Flags & RTL_QUERY_REGISTRY_SUBKEY) {
if (QueryTable->Name == NULL) {
Status = STATUS_INVALID_PARAMETER;
} else {
RtlInitUnicodeString( &KeyPath, QueryTable->Name );
//
// Use a kernel-mode handle for the registry key to prevent
// malicious apps from hijacking it.
//
InitializeObjectAttributes( &ObjectAttributes,
&KeyPath,
OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE,
Key,
NULL
);
Status = ZwOpenKey( &Key1,
MAXIMUM_ALLOWED,
&ObjectAttributes
);
if (NT_SUCCESS( Status )) {
if (QueryTable->QueryRoutine != NULL) {
goto enumvalues;
}
}
}
} else if (QueryTable->Name != NULL) {
RtlInitUnicodeString( &KeyValueName, QueryTable->Name );
retries = 0;
retryqueryvalue:
//
// A maximum of two retries is expected. If we see more we must
// have miscomputed how much is required for the query buffer.
//
if (retries++ > 4) {
//#if DBG
DbgPrint("RtlQueryRegistryValues: Miscomputed buffer size at line %d\n", __LINE__);
//#endif
break;
}
Status = ZwQueryValueKey( Key1,
&KeyValueName,
KeyValueFullInformation,
KeyValueInformation,
(ULONG) KeyValueInfoLength,
&KeyResultLength
);
//
// ZwQueryValueKey returns overflow even though the problem is that
// the specified buffer was too small, so we fix that up here so we
// can decide correctly whether to retry or not below.
//
if (Status == STATUS_BUFFER_OVERFLOW) {
Status = STATUS_BUFFER_TOO_SMALL;
}
if (!NT_SUCCESS( Status )) {
if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
KeyValueInformation->Type = REG_NONE;
KeyValueInformation->DataLength = 0;
KeyResultLength = (ULONG)KeyValueInfoLength;
Status = RtlpCallQueryRegistryRoutine( QueryTable,
KeyValueInformation,
&KeyResultLength,
Context,
Environment
);
}
if (Status == STATUS_BUFFER_TOO_SMALL) {
//
// Try to allocate a larger buffer as this is one humongous
// value.
//
AllocLength = KeyResultLength + sizeof(PVOID) + sizeof(UNICODE_NULL);
KeyValueInformation = RtlpAllocDeallocQueryBuffer( &AllocLength,
KeyValueInformation,
AllocLength,
&Status
);
if ( KeyValueInformation == NULL) {
break;
}
KeyValueInformation->DataOffset = 0;
KeyValueInfoLength = AllocLength - sizeof(UNICODE_NULL);
goto retryqueryvalue;
}
} else {
//
// KeyResultLength holds the length of the data returned by ZwQueryKeyValue.
// If this is a MULTI_SZ value, catenate a NUL.
//
if ( KeyValueInformation->Type == REG_MULTI_SZ ) {
*(PWCHAR) ((PUCHAR)KeyValueInformation + KeyResultLength) = UNICODE_NULL;
KeyValueInformation->DataLength += sizeof(UNICODE_NULL);
}
KeyResultLength = (ULONG)KeyValueInfoLength;
Status = RtlpCallQueryRegistryRoutine( QueryTable,
KeyValueInformation,
&KeyResultLength,
Context,
Environment
);
if ( Status == STATUS_BUFFER_TOO_SMALL ) {
//
// Try to allocate a larger buffer as this is one humongous
// value.
//
AllocLength = KeyResultLength + sizeof(PVOID) + sizeof(UNICODE_NULL);
KeyValueInformation = RtlpAllocDeallocQueryBuffer( &AllocLength,
KeyValueInformation,
AllocLength,
&Status
);
if ( KeyValueInformation == NULL) {
break;
}
KeyValueInformation->DataOffset = 0;
KeyValueInfoLength = AllocLength - sizeof(UNICODE_NULL);
goto retryqueryvalue;
}
//
// If requested, delete the value key after it has been successfully queried.
//
if (NT_SUCCESS( Status ) && QueryTable->Flags & RTL_QUERY_REGISTRY_DELETE) {
ZwDeleteValueKey (Key1, &KeyValueName);
}
}
} else if (QueryTable->Flags & RTL_QUERY_REGISTRY_NOVALUE) {
Status = (QueryTable->QueryRoutine)( NULL,
REG_NONE,
NULL,
0,
Context,
QueryTable->EntryContext
);
} else {
enumvalues:
retries = 0;
for (ValueIndex = 0; TRUE; ValueIndex++) {
Status = ZwEnumerateValueKey( Key1,
ValueIndex,
KeyValueFullInformation,
KeyValueInformation,
(ULONG) KeyValueInfoLength,
&KeyResultLength
);
//
// ZwEnumerateValueKey returns overflow even though the problem is that
// the specified buffer was too small, so we fix that up here so we
// can decide correctly whether to retry or not below.
//
if (Status == STATUS_BUFFER_OVERFLOW) {
Status = STATUS_BUFFER_TOO_SMALL;
}
if (Status == STATUS_NO_MORE_ENTRIES) {
if (ValueIndex == 0 && (QueryTable->Flags & RTL_QUERY_REGISTRY_REQUIRED)) {
Status = STATUS_OBJECT_NAME_NOT_FOUND;
} else {
Status = STATUS_SUCCESS;
}
break;
}
if ( NT_SUCCESS( Status ) ) {
KeyResultLength = (ULONG)KeyValueInfoLength;
Status = RtlpCallQueryRegistryRoutine( QueryTable,
KeyValueInformation,
&KeyResultLength,
Context,
Environment
);
}
if (Status == STATUS_BUFFER_TOO_SMALL) {
//
// Allocate a larger buffer and try again.
//
AllocLength = KeyResultLength + sizeof(PVOID) + sizeof(UNICODE_NULL);
KeyValueInformation = RtlpAllocDeallocQueryBuffer( &AllocLength,
KeyValueInformation,
AllocLength,
&Status
);
if (KeyValueInformation == NULL) {
break;
}
KeyValueInformation->DataOffset = 0;
KeyValueInfoLength = AllocLength - sizeof(UNICODE_NULL);
ValueIndex -= 1;
//
// A maximum of two retries is expected per loop iteration.
// If we see more we must have miscomputed
// how much is required for the query buffer.
//
if (retries++ <= 4) {
continue;
}
//#if DBG
DbgPrint("RtlQueryRegistryValues: Miscomputed buffer size at line %d\n", __LINE__);
//#endif
break;
}
if (!NT_SUCCESS( Status )) {
break;
}
retries = 0;
//
// If requested, delete the value key after it has been successfully queried.
// After deletion the current ValueIndex is for the next sub-key, so adjust it.
// KeyValueInformation->NameLength should fit in a USHORT, but we don't check since
// it only harms our caller.
//
if (QueryTable->Flags & RTL_QUERY_REGISTRY_DELETE) {
KeyValueName.Buffer = KeyValueInformation->Name;
KeyValueName.Length = (USHORT)KeyValueInformation->NameLength;
KeyValueName.MaximumLength = (USHORT)KeyValueInformation->NameLength;
Status = ZwDeleteValueKey( Key1,
&KeyValueName
);
if (NT_SUCCESS( Status )) {
ValueIndex -= 1;
}
}
}
}
if (!NT_SUCCESS( Status )) {
break;
}
QueryTable++;
}
if (Key != NULL && !(RelativeTo & RTL_REGISTRY_HANDLE)) {
ZwClose( Key );
}
if (Key1 != NULL && Key1 != Key) {
ZwClose( Key1 );
}
//
// Free any query buffer we allocated.
//
(void) RtlpAllocDeallocQueryBuffer( NULL, KeyValueInformation, AllocLength, NULL );
return Status;
}
NTSTATUS
RtlWriteRegistryValue(
IN ULONG RelativeTo,
IN PCWSTR Path,
IN PCWSTR ValueName,
IN ULONG ValueType,
IN PVOID ValueData,
IN ULONG ValueLength
)
{
NTSTATUS Status;
UNICODE_STRING KeyValueName;
HANDLE Key;
RTL_PAGED_CODE();
Status = RtlpGetRegistryHandle( RelativeTo, Path, TRUE, &Key );
if (!NT_SUCCESS( Status )) {
return Status;
}
RtlInitUnicodeString( &KeyValueName, ValueName );
Status = ZwSetValueKey( Key,
&KeyValueName,
0,
ValueType,
ValueData,
ValueLength
);
if (!(RelativeTo & RTL_REGISTRY_HANDLE)) {
ZwClose( Key );
}
return Status;
}
NTSTATUS
RtlCheckRegistryKey(
IN ULONG RelativeTo,
IN PWSTR Path
)
{
NTSTATUS Status;
HANDLE Key;
RTL_PAGED_CODE();
Status = RtlpGetRegistryHandle( RelativeTo, Path, FALSE, &Key );
if (!NT_SUCCESS( Status )) {
return Status;
}
ZwClose( Key );
return STATUS_SUCCESS;
}
NTSTATUS
RtlCreateRegistryKey(
IN ULONG RelativeTo,
IN PWSTR Path
)
{
NTSTATUS Status;
HANDLE Key;
RTL_PAGED_CODE();
Status = RtlpGetRegistryHandle( RelativeTo, Path, TRUE, &Key );
if (!NT_SUCCESS( Status )) {
return Status;
}
if (!(RelativeTo & RTL_REGISTRY_HANDLE)) {
ZwClose( Key );
}
return STATUS_SUCCESS;
}
NTSTATUS
RtlDeleteRegistryValue(
IN ULONG RelativeTo,
IN PCWSTR Path,
IN PCWSTR ValueName
)
{
NTSTATUS Status;
UNICODE_STRING KeyValueName;
HANDLE Key;
RTL_PAGED_CODE();
Status = RtlpGetRegistryHandle( RelativeTo, Path, TRUE, &Key );
if (!NT_SUCCESS( Status )) {
return Status;
}
RtlInitUnicodeString( &KeyValueName, ValueName );
Status = ZwDeleteValueKey( Key, &KeyValueName );
if (!(RelativeTo & RTL_REGISTRY_HANDLE)) {
ZwClose( Key );
}
return Status;
}
NTSTATUS
RtlExpandEnvironmentStrings_U(
IN PVOID Environment OPTIONAL,
IN PUNICODE_STRING Source,
OUT PUNICODE_STRING Destination,
OUT PULONG ReturnedLength OPTIONAL
)
{
NTSTATUS Status, Status1;
PWCHAR Src, Src1, Dst;
UNICODE_STRING VariableName, VariableValue;
ULONG SrcLength, DstLength, VarLength, RequiredLength;
RTL_PAGED_CODE();
Src = Source->Buffer;
SrcLength = Source->Length;
Dst = Destination->Buffer;
DstLength = Destination->MaximumLength;
Status = STATUS_SUCCESS;
RequiredLength = 0;
while (SrcLength >= sizeof(WCHAR)) {
if (*Src == L'%') {
Src1 = Src + 1;
VarLength = 0;
VariableName.Length = 0;
VariableName.Buffer = Src1;
while (VarLength < (SrcLength - sizeof(WCHAR))) {
if (*Src1 == L'%') {
if (VarLength) {
VariableName.Length = (USHORT)VarLength;
VariableName.MaximumLength = (USHORT)VarLength;
}
break;
}
Src1++;
VarLength += sizeof(WCHAR);
}
if (VariableName.Length) {
VariableValue.Buffer = Dst;
VariableValue.Length = 0;
VariableValue.MaximumLength = (USHORT)DstLength;
Status1 = RtlQueryEnvironmentVariable_U( Environment,
&VariableName,
&VariableValue
);
if (NT_SUCCESS( Status1 ) || Status1 == STATUS_BUFFER_TOO_SMALL) {
RequiredLength += VariableValue.Length;
Src = Src1 + 1;
SrcLength -= (VarLength + 2*sizeof(WCHAR));
if (NT_SUCCESS( Status1 )) {
DstLength -= VariableValue.Length;
Dst += VariableValue.Length / sizeof(WCHAR);
} else {
Status = Status1;
}
continue;
}
}
}
if (NT_SUCCESS( Status )) {
if (DstLength > sizeof(WCHAR)) {
DstLength -= sizeof(WCHAR);
*Dst++ = *Src;
} else {
Status = STATUS_BUFFER_TOO_SMALL;
}
}
RequiredLength += sizeof(WCHAR);
SrcLength -= sizeof(WCHAR);
Src++;
}
if (NT_SUCCESS( Status )) {
if (DstLength) {
DstLength -= sizeof(WCHAR);
*Dst = UNICODE_NULL;
} else {
Status = STATUS_BUFFER_TOO_SMALL;
}
}
RequiredLength += sizeof(WCHAR);
if (ARGUMENT_PRESENT( ReturnedLength )) {
*ReturnedLength = RequiredLength;
}
if (NT_SUCCESS( Status )) {
Destination->Length = (USHORT)(RequiredLength - sizeof(WCHAR));
}
return Status;
}
ULONG
RtlGetNtGlobalFlags( VOID )
{
#ifdef NTOS_KERNEL_RUNTIME
return NtGlobalFlag;
#else
return NtCurrentPeb()->NtGlobalFlag;
#endif
}
//
// Maximum size of TOKEN_USER information.
//
#define SIZE_OF_TOKEN_INFORMATION \
sizeof( TOKEN_USER ) \
+ sizeof( SID ) \
+ sizeof( ULONG ) * SID_MAX_SUB_AUTHORITIES
NTSTATUS
RtlFormatCurrentUserKeyPath(
OUT PUNICODE_STRING CurrentUserKeyPath
)
/*++
Routine Description:
Initialize the supplied buffer with a string representation
of the current user's SID.
Arguments:
CurrentUserKeyPath - Returns a string that represents the current
user's root key in the Registry. Caller must call
RtlFreeUnicodeString to free the buffer when done with it.
Return Value:
NTSTATUS - Returns STATUS_SUCCESS if the user string was
succesfully initialized.
--*/
{
HANDLE TokenHandle;
UCHAR TokenInformation[ SIZE_OF_TOKEN_INFORMATION ];
ULONG ReturnLength;
ULONG SidStringLength ;
UNICODE_STRING SidString ;
NTSTATUS Status;
Status = ZwOpenThreadTokenEx( NtCurrentThread(),
TOKEN_READ,
TRUE,
OBJ_KERNEL_HANDLE,
&TokenHandle
);
if ( !NT_SUCCESS( Status ) && ( Status != STATUS_NO_TOKEN ) ) {
return Status;
}
if ( !NT_SUCCESS( Status ) ) {
Status = ZwOpenProcessTokenEx( NtCurrentProcess(),
TOKEN_READ,
OBJ_KERNEL_HANDLE,
&TokenHandle
);
if ( !NT_SUCCESS( Status )) {
return Status;
}
}
Status = ZwQueryInformationToken( TokenHandle,
TokenUser,
TokenInformation,
sizeof( TokenInformation ),
&ReturnLength
);
ZwClose( TokenHandle );
if ( !NT_SUCCESS( Status )) {
return Status;
}
Status = RtlLengthSidAsUnicodeString(
((PTOKEN_USER)TokenInformation)->User.Sid,
&SidStringLength
);
if ( !NT_SUCCESS( Status ) ) {
return Status ;
}
CurrentUserKeyPath->Length = 0;
CurrentUserKeyPath->MaximumLength = (USHORT)(SidStringLength +
sizeof( L"\\REGISTRY\\USER\\" ) +
sizeof( UNICODE_NULL ));
CurrentUserKeyPath->Buffer = (RtlAllocateStringRoutine)( CurrentUserKeyPath->MaximumLength );
if (CurrentUserKeyPath->Buffer == NULL) {
return STATUS_NO_MEMORY;
}
//
// Copy "\REGISTRY\USER" to the current user string.
//
RtlAppendUnicodeToString( CurrentUserKeyPath, L"\\REGISTRY\\USER\\" );
SidString.MaximumLength = (USHORT)SidStringLength ;
SidString.Length = 0 ;
SidString.Buffer = CurrentUserKeyPath->Buffer +
(CurrentUserKeyPath->Length / sizeof(WCHAR) );
Status = RtlConvertSidToUnicodeString( &SidString,
((PTOKEN_USER)TokenInformation)->User.Sid,
FALSE
);
if ( !NT_SUCCESS( Status )) {
RtlFreeUnicodeString( CurrentUserKeyPath );
} else {
CurrentUserKeyPath->Length += SidString.Length ;
}
return Status;
}
NTSTATUS
RtlOpenCurrentUser(
IN ULONG DesiredAccess,
OUT PHANDLE CurrentUserKey
)
/*++
Routine Description:
Attempts to open the the HKEY_CURRENT_USER predefined handle.
Arguments:
DesiredAccess - Specifies the access to open the key for.
CurrentUserKey - Returns a handle to the key \REGISTRY\USER\*.
Return Value:
Returns ERROR_SUCCESS (0) for success; error-code for failure.
--*/
{
UNICODE_STRING CurrentUserKeyPath;
OBJECT_ATTRIBUTES Obja;
NTSTATUS Status;
RTL_PAGED_CODE();
//
// Format the registry path for the current user.
//
Status = RtlFormatCurrentUserKeyPath( &CurrentUserKeyPath );
if ( NT_SUCCESS(Status) ) {
InitializeObjectAttributes( &Obja,
&CurrentUserKeyPath,
OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE|OBJ_FORCE_ACCESS_CHECK,
NULL,
NULL
);
Status = ZwOpenKey( CurrentUserKey,
DesiredAccess,
&Obja
);
RtlFreeUnicodeString( &CurrentUserKeyPath );
}
if ( !NT_SUCCESS(Status) ) {
//
// Opening \REGISTRY\USER\<SID> failed, try \REGISTRY\USER\.DEFAULT
//
RtlInitUnicodeString( &CurrentUserKeyPath, RtlpRegistryPaths[ RTL_REGISTRY_USER ] );
InitializeObjectAttributes( &Obja,
&CurrentUserKeyPath,
OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE|OBJ_FORCE_ACCESS_CHECK,
NULL,
NULL
);
Status = ZwOpenKey( CurrentUserKey,
DesiredAccess,
&Obja
);
}
return Status;
}
NTSTATUS
RtlpGetTimeZoneInfoHandle(
IN BOOLEAN WriteAccess,
OUT PHANDLE Key
)
{
return RtlpGetRegistryHandle( RTL_REGISTRY_CONTROL, L"TimeZoneInformation", WriteAccess, Key );
}
extern const WCHAR szBias[];
extern const WCHAR szStandardName[];
extern const WCHAR szStandardBias[];
extern const WCHAR szStandardStart[];
extern const WCHAR szDaylightName[];
extern const WCHAR szDaylightBias[];
extern const WCHAR szDaylightStart[];
NTSTATUS
RtlQueryTimeZoneInformation(
OUT PRTL_TIME_ZONE_INFORMATION TimeZoneInformation
)
{
NTSTATUS Status;
HANDLE Key;
UNICODE_STRING StandardName, DaylightName;
RTL_QUERY_REGISTRY_TABLE RegistryConfigurationTable[ 8 ];
RTL_PAGED_CODE();
Status = RtlpGetTimeZoneInfoHandle( FALSE, &Key );
if (!NT_SUCCESS( Status )) {
return Status;
}
RtlZeroMemory( TimeZoneInformation, sizeof( *TimeZoneInformation ) );
RtlZeroMemory( RegistryConfigurationTable, sizeof( RegistryConfigurationTable ) );
RegistryConfigurationTable[ 0 ].Flags = RTL_QUERY_REGISTRY_DIRECT;
RegistryConfigurationTable[ 0 ].Name = (PWSTR)szBias;
RegistryConfigurationTable[ 0 ].EntryContext = &TimeZoneInformation->Bias;
StandardName.Buffer = TimeZoneInformation->StandardName;
StandardName.Length = 0;
StandardName.MaximumLength = sizeof( TimeZoneInformation->StandardName );
RegistryConfigurationTable[ 1 ].Flags = RTL_QUERY_REGISTRY_DIRECT;
RegistryConfigurationTable[ 1 ].Name = (PWSTR)szStandardName;
RegistryConfigurationTable[ 1 ].EntryContext = &StandardName;
RegistryConfigurationTable[ 2 ].Flags = RTL_QUERY_REGISTRY_DIRECT;
RegistryConfigurationTable[ 2 ].Name = (PWSTR)szStandardBias;
RegistryConfigurationTable[ 2 ].EntryContext = &TimeZoneInformation->StandardBias;
RegistryConfigurationTable[ 3 ].Flags = RTL_QUERY_REGISTRY_DIRECT;
RegistryConfigurationTable[ 3 ].Name = (PWSTR)szStandardStart;
RegistryConfigurationTable[ 3 ].EntryContext = &TimeZoneInformation->StandardStart;
*(PLONG)(RegistryConfigurationTable[ 3 ].EntryContext) = -(LONG)sizeof( TIME_FIELDS );
DaylightName.Buffer = TimeZoneInformation->DaylightName;
DaylightName.Length = 0;
DaylightName.MaximumLength = sizeof( TimeZoneInformation->DaylightName );
RegistryConfigurationTable[ 4 ].Flags = RTL_QUERY_REGISTRY_DIRECT;
RegistryConfigurationTable[ 4 ].Name = (PWSTR)szDaylightName;
RegistryConfigurationTable[ 4 ].EntryContext = &DaylightName;
RegistryConfigurationTable[ 5 ].Flags = RTL_QUERY_REGISTRY_DIRECT;
RegistryConfigurationTable[ 5 ].Name = (PWSTR)szDaylightBias;
RegistryConfigurationTable[ 5 ].EntryContext = &TimeZoneInformation->DaylightBias;
RegistryConfigurationTable[ 6 ].Flags = RTL_QUERY_REGISTRY_DIRECT;
RegistryConfigurationTable[ 6 ].Name = (PWSTR)szDaylightStart;
RegistryConfigurationTable[ 6 ].EntryContext = &TimeZoneInformation->DaylightStart;
*(PLONG)(RegistryConfigurationTable[ 6 ].EntryContext) = -(LONG)sizeof( TIME_FIELDS );
Status = RtlQueryRegistryValues( RTL_REGISTRY_HANDLE,
(PWSTR)Key,
RegistryConfigurationTable,
NULL,
NULL
);
ZwClose( Key );
return Status;
}
NTSTATUS
RtlSetTimeZoneInformation(
IN PRTL_TIME_ZONE_INFORMATION TimeZoneInformation
)
{
NTSTATUS Status;
HANDLE Key;
RTL_PAGED_CODE();
Status = RtlpGetTimeZoneInfoHandle( TRUE, &Key );
if (!NT_SUCCESS( Status )) {
return Status;
}
Status = RtlWriteRegistryValue( RTL_REGISTRY_HANDLE,
(PWSTR)Key,
szBias,
REG_DWORD,
&TimeZoneInformation->Bias,
sizeof( TimeZoneInformation->Bias )
);
if (NT_SUCCESS( Status )) {
Status = RtlWriteRegistryValue( RTL_REGISTRY_HANDLE,
(PWSTR)Key,
szStandardName,
REG_SZ,
TimeZoneInformation->StandardName,
(wcslen( TimeZoneInformation->StandardName ) + 1) * sizeof( WCHAR )
);
}
if (NT_SUCCESS( Status )) {
Status = RtlWriteRegistryValue( RTL_REGISTRY_HANDLE,
(PWSTR)Key,
szStandardBias,
REG_DWORD,
&TimeZoneInformation->StandardBias,
sizeof( TimeZoneInformation->StandardBias )
);
}
if (NT_SUCCESS( Status )) {
Status = RtlWriteRegistryValue( RTL_REGISTRY_HANDLE,
(PWSTR)Key,
szStandardStart,
REG_BINARY,
&TimeZoneInformation->StandardStart,
sizeof( TimeZoneInformation->StandardStart )
);
}
if (NT_SUCCESS( Status )) {
Status = RtlWriteRegistryValue( RTL_REGISTRY_HANDLE,
(PWSTR)Key,
szDaylightName,
REG_SZ,
TimeZoneInformation->DaylightName,
(wcslen( TimeZoneInformation->DaylightName ) + 1) * sizeof( WCHAR )
);
}
if (NT_SUCCESS( Status )) {
Status = RtlWriteRegistryValue( RTL_REGISTRY_HANDLE,
(PWSTR)Key,
szDaylightBias,
REG_DWORD,
&TimeZoneInformation->DaylightBias,
sizeof( TimeZoneInformation->DaylightBias )
);
}
if (NT_SUCCESS( Status )) {
Status = RtlWriteRegistryValue( RTL_REGISTRY_HANDLE,
(PWSTR)Key,
szDaylightStart,
REG_BINARY,
&TimeZoneInformation->DaylightStart,
sizeof( TimeZoneInformation->DaylightStart )
);
}
ZwClose( Key );
return Status;
}
NTSTATUS
RtlSetActiveTimeBias(
IN LONG ActiveBias
)
{
NTSTATUS Status;
HANDLE Key;
RTL_QUERY_REGISTRY_TABLE RegistryConfigurationTable[ 2 ];
LONG CurrentActiveBias;
RTL_PAGED_CODE();
Status = RtlpGetTimeZoneInfoHandle( TRUE, &Key );
if (!NT_SUCCESS( Status )) {
return Status;
}
RtlZeroMemory( RegistryConfigurationTable, sizeof( RegistryConfigurationTable ) );
RegistryConfigurationTable[ 0 ].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
RegistryConfigurationTable[ 0 ].Name = L"ActiveTimeBias";
RegistryConfigurationTable[ 0 ].EntryContext = &CurrentActiveBias;
Status = RtlQueryRegistryValues( RTL_REGISTRY_HANDLE,
(PWSTR)Key,
RegistryConfigurationTable,
NULL,
NULL
);
if ( !NT_SUCCESS(Status) || CurrentActiveBias != ActiveBias ) {
Status = RtlWriteRegistryValue( RTL_REGISTRY_HANDLE,
(PWSTR)Key,
L"ActiveTimeBias",
REG_DWORD,
&ActiveBias,
sizeof( ActiveBias )
);
}
ZwClose( Key );
return Status;
}