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.
2340 lines
68 KiB
2340 lines
68 KiB
/*++
|
|
|
|
File Description:
|
|
|
|
This file contains utility functions used by the
|
|
SID modifier tool.
|
|
|
|
Author:
|
|
|
|
Matt Holle (matth) Oct 1997
|
|
|
|
|
|
--*/
|
|
|
|
//
|
|
// System header files
|
|
//
|
|
#include <nt.h>
|
|
//
|
|
// Disable the DbgPrint for non-debug builds
|
|
//
|
|
#ifndef DBG
|
|
#define _DBGNT_
|
|
#endif
|
|
#include <ntrtl.h>
|
|
|
|
#include <nturtl.h>
|
|
#include <ntverp.h>
|
|
#include <wtypes.h>
|
|
|
|
//
|
|
// Private header files
|
|
//
|
|
#include "setupcl.h"
|
|
|
|
|
|
|
|
SECURITY_INFORMATION ALL_SECURITY_INFORMATION = DACL_SECURITY_INFORMATION |
|
|
SACL_SECURITY_INFORMATION |
|
|
GROUP_SECURITY_INFORMATION |
|
|
OWNER_SECURITY_INFORMATION;
|
|
|
|
|
|
//
|
|
// ISSUE-2002/02/26-brucegr,jcohen - Dead Code! Nobody calls DeleteKey!
|
|
//
|
|
NTSTATUS
|
|
DeleteKey(
|
|
PWSTR Key
|
|
);
|
|
|
|
NTSTATUS
|
|
DeleteKeyRecursive(
|
|
HANDLE hKeyRoot,
|
|
PWSTR Key
|
|
);
|
|
|
|
NTSTATUS
|
|
FileDelete(
|
|
IN WCHAR *FileName
|
|
);
|
|
|
|
NTSTATUS
|
|
FileCopy(
|
|
IN WCHAR *TargetName,
|
|
IN WCHAR *SourceName
|
|
);
|
|
|
|
NTSTATUS
|
|
SetKey(
|
|
IN WCHAR *KeyName,
|
|
IN WCHAR *SubKeyName,
|
|
IN CHAR *Data,
|
|
IN ULONG DataLength,
|
|
IN ULONG DATA_TYPE
|
|
);
|
|
|
|
NTSTATUS
|
|
ReadSetWriteKey(
|
|
IN WCHAR *ParentKeyName, OPTIONAL
|
|
IN HANDLE ParentKeyHandle, OPTIONAL
|
|
IN WCHAR *SubKeyName,
|
|
IN CHAR *OldData,
|
|
IN CHAR *NewData,
|
|
IN ULONG DataLength,
|
|
IN ULONG DATA_TYPE
|
|
);
|
|
|
|
NTSTATUS
|
|
LoadUnloadKey(
|
|
IN PWSTR KeyName,
|
|
IN PWSTR FileName
|
|
);
|
|
|
|
NTSTATUS
|
|
BackupRepairHives(
|
|
VOID
|
|
);
|
|
|
|
NTSTATUS
|
|
CleanupRepairHives(
|
|
NTSTATUS RepairHivesSuccess
|
|
);
|
|
|
|
NTSTATUS
|
|
TestSetSecurityObject(
|
|
HANDLE hKey
|
|
);
|
|
|
|
NTSTATUS
|
|
SetKeySecurityRecursive(
|
|
HANDLE hKey
|
|
);
|
|
|
|
NTSTATUS
|
|
CopyKeyRecursive(
|
|
HANDLE hKeyDst,
|
|
HANDLE hKeySrc
|
|
);
|
|
|
|
NTSTATUS
|
|
CopyRegKey(
|
|
IN WCHAR *TargetName,
|
|
IN WCHAR *SourceName,
|
|
IN HANDLE ParentKeyHandle OPTIONAL
|
|
);
|
|
|
|
//
|
|
// ISSUE-2002/02/26-brucegr,jcohen - Dead Code! Nobody calls MoveRegKey!
|
|
//
|
|
NTSTATUS
|
|
MoveRegKey(
|
|
IN WCHAR *TargetName,
|
|
IN WCHAR *SourceName
|
|
);
|
|
|
|
NTSTATUS
|
|
FindAndReplaceBlock(
|
|
IN PCHAR Block,
|
|
IN ULONG BlockLength,
|
|
IN PCHAR OldValue,
|
|
IN PCHAR NewValue,
|
|
IN ULONG ValueLength
|
|
);
|
|
|
|
NTSTATUS
|
|
SiftKeyRecursive(
|
|
HANDLE hKey,
|
|
int indent
|
|
);
|
|
|
|
NTSTATUS
|
|
SiftKey(
|
|
PWSTR KeyName
|
|
);
|
|
|
|
//
|
|
// ISSUE-2002/02/26-brucegr,jcohen - Dead Code! Nobody calls DeleteKey!
|
|
//
|
|
NTSTATUS
|
|
DeleteKey(
|
|
PWSTR KeyName
|
|
)
|
|
|
|
/*++
|
|
===============================================================================
|
|
Routine Description:
|
|
|
|
Does some overhead work, then calls out to DeleteKeyRecursive in order
|
|
to ensure that if this key has any children, they get whacked too.
|
|
|
|
Arguments:
|
|
|
|
Key: Key to delete.
|
|
|
|
Return Value:
|
|
|
|
Status is returned.
|
|
===============================================================================
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
UNICODE_STRING UnicodeString;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
WCHAR TerminateKey[MAX_PATH],
|
|
ParentName[MAX_PATH];
|
|
HANDLE hKey;
|
|
LONG i;
|
|
|
|
//
|
|
// Get the name of the parent key. Do this by replacing the last
|
|
// back-whack with a NULL.
|
|
//
|
|
i = wcslen( KeyName );
|
|
while( (KeyName[i] != '\\') &&
|
|
( i >= 0 ) ) {
|
|
i--;
|
|
}
|
|
|
|
if( i >= 0 ) {
|
|
KeyName[i] = 0;
|
|
wcscpy( ParentName, KeyName );
|
|
KeyName[i] = '\\';
|
|
} else {
|
|
return( -1 );
|
|
}
|
|
|
|
//
|
|
// Get the name of the key we're going to terminate...
|
|
//
|
|
wcscpy( TerminateKey, KeyName + i + 1 );
|
|
|
|
//
|
|
// Open the parent
|
|
//
|
|
INIT_OBJA( &ObjectAttributes, &UnicodeString, ParentName );
|
|
ObjectAttributes.RootDirectory = NULL;
|
|
Status = NtOpenKey( &hKey,
|
|
KEY_ALL_ACCESS,
|
|
&ObjectAttributes );
|
|
TEST_STATUS_RETURN( "SETUPCL: DeleteKey - Failed to open Parent key!" );
|
|
|
|
Status = DeleteKeyRecursive( hKey, TerminateKey );
|
|
TEST_STATUS( "SETUPCL: DeleteKey - Call to DeleteKeyRecursive Failed!" );
|
|
NtClose( hKey );
|
|
|
|
return( Status );
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
DeleteKeyRecursive(
|
|
HANDLE hKeyRoot,
|
|
PWSTR Key
|
|
)
|
|
/*++
|
|
===============================================================================
|
|
Routine Description:
|
|
|
|
Routine to recursively delete all subkeys under the given
|
|
key, including the key given.
|
|
|
|
Arguments:
|
|
|
|
hKeyRoot: Handle to root relative to which the key to be deleted is
|
|
specified.
|
|
|
|
Key: Root relative path of the key which is to be recursively
|
|
deleted.
|
|
|
|
Return Value:
|
|
|
|
Status is returned.
|
|
===============================================================================
|
|
--*/
|
|
{
|
|
WCHAR ValueBuffer[BASIC_INFO_BUFFER_SIZE];
|
|
ULONG ResultLength;
|
|
PKEY_BASIC_INFORMATION KeyInfo;
|
|
NTSTATUS Status;
|
|
UNICODE_STRING UnicodeString;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
PWSTR SubkeyName;
|
|
HANDLE hKey;
|
|
|
|
//
|
|
// Initialize
|
|
//
|
|
KeyInfo = (PKEY_BASIC_INFORMATION)ValueBuffer;
|
|
|
|
//
|
|
// Open the key
|
|
//
|
|
INIT_OBJA(&Obja,&UnicodeString,Key);
|
|
Obja.RootDirectory = hKeyRoot;
|
|
Status = NtOpenKey( &hKey,
|
|
KEY_ALL_ACCESS,
|
|
&Obja);
|
|
TEST_STATUS_RETURN( "SETUPCL: DeleteKeyRecursive - Failed to open key!" );
|
|
|
|
//
|
|
// Enumerate all subkeys of the current key. if any exist they should
|
|
// be deleted first. since deleting the subkey affects the subkey
|
|
// index, we always enumerate on subkeyindex 0
|
|
//
|
|
while(1) {
|
|
Status = NtEnumerateKey( hKey,
|
|
0,
|
|
KeyBasicInformation,
|
|
ValueBuffer,
|
|
sizeof(ValueBuffer),
|
|
&ResultLength );
|
|
if(!NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Zero-terminate the subkey name just in case.
|
|
//
|
|
KeyInfo->Name[KeyInfo->NameLength/sizeof(WCHAR)] = 0;
|
|
|
|
//
|
|
// Recursively call this guy with a child.
|
|
//
|
|
Status = DeleteKeyRecursive( hKey, KeyInfo->Name );
|
|
if(!NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Check the status, if the status is anything other than
|
|
// STATUS_NO_MORE_ENTRIES we failed in deleting some subkey,
|
|
// so we cannot delete this key too
|
|
//
|
|
|
|
if( Status == STATUS_NO_MORE_ENTRIES) {
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
TEST_STATUS( "SETUPCL: DeleteKeyRecursive - Failed to delete all keys!" );
|
|
|
|
Status = NtDeleteKey( hKey );
|
|
TEST_STATUS( "SETUPCL: DeleteKeyRecursive - Failed to delete key!" );
|
|
|
|
Status = NtClose( hKey );
|
|
TEST_STATUS( "SETUPCL: DeleteKeyRecursive - Failed to close key!" );
|
|
|
|
return( Status );
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
FileDelete(
|
|
IN WCHAR *FileName
|
|
)
|
|
/*++
|
|
===============================================================================
|
|
Routine Description:
|
|
|
|
This function will attempt a delete on the given file name.
|
|
|
|
Arguments:
|
|
|
|
FileName - The name of the file we're going to be deleting.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
===============================================================================
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
UNICODE_STRING UnicodeString;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
HANDLE hFile;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
WCHAR buffer[MAX_PATH];
|
|
|
|
INIT_OBJA( &ObjectAttributes,
|
|
&UnicodeString,
|
|
FileName );
|
|
|
|
Status = NtCreateFile( &hFile,
|
|
FILE_GENERIC_READ | DELETE,
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
NULL,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
0,
|
|
FILE_OPEN,
|
|
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_DELETE_ON_CLOSE,
|
|
NULL,
|
|
0 );
|
|
//
|
|
// ISSUE-2002/02/26-brucegr,jcohen - Check Status before trying to close handle!
|
|
//
|
|
NtClose( hFile );
|
|
|
|
TEST_STATUS( "SETUPCL: MyDelFile - Failed a delete." );
|
|
|
|
return( Status );
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
FileCopy(
|
|
IN WCHAR *TargetName,
|
|
IN WCHAR *SourceName
|
|
)
|
|
|
|
/*++
|
|
===============================================================================
|
|
Routine Description:
|
|
|
|
This function will attempt a copy of the given file.
|
|
|
|
Arguments:
|
|
|
|
TargetName - The name of the file we'll be writing.
|
|
SourceName - The name of the file we'll be reading.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
===============================================================================
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
UNICODE_STRING UnicodeString;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
HANDLE SourceHandle,
|
|
TargetHandle,
|
|
SectionHandle;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
ULONG FileSize,
|
|
remainingLength,
|
|
writeLength;
|
|
SIZE_T ViewSize;
|
|
LARGE_INTEGER SectionOffset,
|
|
FileOffset;
|
|
PVOID ImageBase;
|
|
PUCHAR base;
|
|
|
|
FILE_STANDARD_INFORMATION StandardInfo;
|
|
|
|
//
|
|
// Open the Source file.
|
|
//
|
|
INIT_OBJA( &ObjectAttributes,
|
|
&UnicodeString,
|
|
SourceName );
|
|
|
|
Status = NtCreateFile( &SourceHandle,
|
|
FILE_GENERIC_READ,
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
NULL,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
FILE_SHARE_READ,
|
|
FILE_OPEN,
|
|
0,
|
|
NULL,
|
|
0 );
|
|
TEST_STATUS_RETURN( "SETUPCL: FileCopy - Failed to open source." );
|
|
|
|
//
|
|
// Get the Source file size.
|
|
//
|
|
Status = NtQueryInformationFile( SourceHandle,
|
|
&IoStatusBlock,
|
|
&StandardInfo,
|
|
sizeof(StandardInfo),
|
|
FileStandardInformation );
|
|
TEST_STATUS_RETURN( "SETUPCL: FileCopy - Failed to get Source StandardInfo." );
|
|
FileSize = StandardInfo.EndOfFile.LowPart;
|
|
|
|
//
|
|
// Map the Source file.
|
|
//
|
|
ViewSize = 0;
|
|
SectionOffset.QuadPart = 0;
|
|
Status = NtCreateSection( &SectionHandle,
|
|
STANDARD_RIGHTS_REQUIRED | SECTION_QUERY | SECTION_MAP_READ,
|
|
NULL,
|
|
NULL, // entire file
|
|
PAGE_READONLY,
|
|
SEC_COMMIT,
|
|
SourceHandle );
|
|
TEST_STATUS_RETURN( "SETUPCL: FileCopy - Failed CreateSection on Source." );
|
|
|
|
ImageBase = NULL;
|
|
Status = NtMapViewOfSection( SectionHandle,
|
|
NtCurrentProcess(),
|
|
&ImageBase,
|
|
0,
|
|
0,
|
|
&SectionOffset,
|
|
&ViewSize,
|
|
ViewShare,
|
|
0,
|
|
PAGE_READONLY );
|
|
TEST_STATUS_RETURN( "SETUPCL: FileCopy - Failed MapViewOfSection on Source." );
|
|
|
|
//
|
|
// Open the Target file.
|
|
//
|
|
INIT_OBJA( &ObjectAttributes,
|
|
&UnicodeString,
|
|
TargetName );
|
|
|
|
Status = NtCreateFile( &TargetHandle,
|
|
FILE_GENERIC_WRITE,
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
NULL,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
0,
|
|
FILE_OVERWRITE_IF,
|
|
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_SEQUENTIAL_ONLY,
|
|
NULL,
|
|
0 );
|
|
TEST_STATUS_RETURN( "SETUPCL: FileCopy - Failed to open target." );
|
|
|
|
//
|
|
// Write him. We guard him with a try/except because if there is an
|
|
// i/o error, memory management will raise an in-page exception.
|
|
//
|
|
FileOffset.QuadPart = 0;
|
|
base = ImageBase;
|
|
remainingLength = FileSize;
|
|
try {
|
|
while( remainingLength != 0 ) {
|
|
writeLength = 60 * 1024;
|
|
if( writeLength > remainingLength ) {
|
|
writeLength = remainingLength;
|
|
}
|
|
Status = NtWriteFile( TargetHandle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
base,
|
|
writeLength,
|
|
&FileOffset,
|
|
NULL );
|
|
base += writeLength;
|
|
FileOffset.LowPart += writeLength;
|
|
remainingLength -= writeLength;
|
|
|
|
if( !NT_SUCCESS( Status ) ) {
|
|
break;
|
|
}
|
|
}
|
|
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
Status = STATUS_IN_PAGE_ERROR;
|
|
}
|
|
|
|
NtClose( TargetHandle );
|
|
|
|
TEST_STATUS_RETURN( "SETUPCL: FileCopy - Failed to write target." );
|
|
|
|
//
|
|
// Unmap the source file.
|
|
//
|
|
Status = NtUnmapViewOfSection( NtCurrentProcess(), ImageBase );
|
|
TEST_STATUS( "SETUPCL: FileCopy - Failed to UnMapSection on source." );
|
|
|
|
Status = NtClose( SectionHandle );
|
|
TEST_STATUS( "SETUPCL: FileCopy - Failed to close sectionhandle on source." );
|
|
|
|
NtClose( SourceHandle );
|
|
|
|
return( Status );
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SetKey(
|
|
IN WCHAR *KeyName,
|
|
IN WCHAR *SubKeyName,
|
|
IN CHAR *Data,
|
|
IN ULONG DataLength,
|
|
IN ULONG DATA_TYPE
|
|
)
|
|
/*++
|
|
===============================================================================
|
|
Routine Description:
|
|
|
|
This function will set the specified key to the specified value.
|
|
|
|
Arguments:
|
|
|
|
KeyName - The name of the key we're going to be setting.
|
|
SubKeyName - The name of the Value key we're setting
|
|
Data - The value we'll be setting him to.
|
|
DataLength - How much data are we writing?
|
|
DATA_TYPE - Type of the registry entry
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
===============================================================================
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
UNICODE_STRING UnicodeString,
|
|
ValueName;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
HANDLE hKey;
|
|
|
|
//
|
|
// Open the parent key.
|
|
//
|
|
INIT_OBJA( &ObjectAttributes,
|
|
&UnicodeString,
|
|
KeyName );
|
|
Status = NtOpenKey( &hKey,
|
|
KEY_ALL_ACCESS,
|
|
&ObjectAttributes );
|
|
|
|
|
|
if( !NT_SUCCESS( Status ) ) {
|
|
DbgPrint( "SETUPCL: SetKey - Failed to open %ws (%lx)\n", KeyName, Status );
|
|
return( Status );
|
|
}
|
|
|
|
//
|
|
// Now write the target key.
|
|
//
|
|
RtlInitUnicodeString(&ValueName, SubKeyName );
|
|
Status = NtSetValueKey( hKey,
|
|
&ValueName, // SubKeyName
|
|
0, // TitleIndex
|
|
DATA_TYPE, // Type
|
|
Data, // value
|
|
DataLength );
|
|
if( !NT_SUCCESS( Status ) ) {
|
|
DbgPrint( "SETUPCL: SetKey - Failed to Set %ws\\%ws (%lx)\n", KeyName, SubKeyName, Status );
|
|
//
|
|
// ISSUE-2002/02/26-brucegr,jcohen - If NtSetValueKey fails, hKey is leaked!
|
|
//
|
|
return( Status );
|
|
}
|
|
|
|
|
|
NtFlushKey( hKey );
|
|
NtClose( hKey );
|
|
|
|
return( Status );
|
|
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
ReadSetWriteKey(
|
|
IN WCHAR *ParentKeyName, OPTIONAL
|
|
IN HANDLE ParentKeyHandle, OPTIONAL
|
|
IN WCHAR *SubKeyName,
|
|
IN CHAR *OldData,
|
|
IN CHAR *NewData,
|
|
IN ULONG DataLength,
|
|
IN ULONG DATA_TYPE
|
|
)
|
|
/*++
|
|
===============================================================================
|
|
Routine Description:
|
|
|
|
This function will read a value from a key, surgically replace some bits
|
|
in it, then write it back out.
|
|
|
|
Arguments:
|
|
|
|
ParentKeyName - Parent name of the key we're going to be setting.
|
|
ParentKeyHandle - Parent handle of the key we're going to be setting.
|
|
SubKeyName - The name of the Value key we're setting
|
|
Data - The value we'll be setting him to.
|
|
DataLength - How much data are we writing?
|
|
DATA_TYPE - Type of the registry entry
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
===============================================================================
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
UNICODE_STRING UnicodeString,
|
|
ValueName;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
HANDLE hKey;
|
|
PKEY_VALUE_PARTIAL_INFORMATION KeyValueInfo = NULL;
|
|
ULONG KeyValueLength, LengthNeeded;
|
|
|
|
if( ParentKeyHandle == NULL ) {
|
|
//
|
|
// Open the parent key.
|
|
//
|
|
INIT_OBJA( &ObjectAttributes,
|
|
&UnicodeString,
|
|
ParentKeyName );
|
|
Status = NtOpenKey( &hKey,
|
|
KEY_ALL_ACCESS,
|
|
&ObjectAttributes );
|
|
|
|
|
|
if( !NT_SUCCESS( Status ) ) {
|
|
DbgPrint( "SETUPCL: ReadSetWriteKey - Failed to open %ws (%lx)\n", ParentKeyName, Status );
|
|
return( Status );
|
|
}
|
|
} else {
|
|
hKey = ParentKeyHandle;
|
|
}
|
|
|
|
//
|
|
// Get his data.
|
|
//
|
|
|
|
|
|
RtlInitUnicodeString( &UnicodeString, SubKeyName );
|
|
|
|
|
|
//
|
|
// How big is his buffer?
|
|
//
|
|
Status = NtQueryValueKey( hKey,
|
|
&UnicodeString,
|
|
KeyValuePartialInformation,
|
|
NULL,
|
|
0,
|
|
&LengthNeeded );
|
|
//
|
|
// ISSUE-2002/02/26-brucegr,jcohen - Check for STATUS_SUCCESS, not assume success on STATUS_OBJECT_NAME_NOT_FOUND
|
|
//
|
|
if( Status == STATUS_OBJECT_NAME_NOT_FOUND ) {
|
|
DbgPrint( "SETUPCL: ReadSetWriteKey - Unable to query subkey %ws size. Error (%lx)\n", SubKeyName, Status );
|
|
//
|
|
// ISSUE-2002/02/26-brucegr,jcohen - Leaks key if ParentKeyHandle == NULL
|
|
//
|
|
return( Status );
|
|
} else {
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Allocate a block.
|
|
//
|
|
LengthNeeded += 0x10;
|
|
KeyValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)RtlAllocateHeap( RtlProcessHeap(),
|
|
0,
|
|
LengthNeeded );
|
|
|
|
if( KeyValueInfo == NULL ) {
|
|
DbgPrint( "SETUPCL: ReadSetWriteKey - Unable to allocate buffer\n" );
|
|
//
|
|
// ISSUE-2002/02/26-brucegr,jcohen - Leaks key if ParentKeyHandle == NULL
|
|
//
|
|
return( Status );
|
|
}
|
|
|
|
//
|
|
// Get the data.
|
|
//
|
|
Status = NtQueryValueKey( hKey,
|
|
&UnicodeString,
|
|
KeyValuePartialInformation,
|
|
(PVOID)KeyValueInfo,
|
|
LengthNeeded,
|
|
&KeyValueLength );
|
|
if( !NT_SUCCESS( Status ) ) {
|
|
DbgPrint( "SETUPCL: ReadSetWriteKey - Failed to query subkey %ws (%lx)\n", SubKeyName, Status );
|
|
//
|
|
// ISSUE-2002/02/26-brucegr,jcohen - Leaks key if ParentKeyHandle == NULL
|
|
// ISSUE-2002/02/26-brucegr,jcohen - Leaks KeyValueInfo
|
|
//
|
|
return( Status );
|
|
}
|
|
|
|
|
|
//
|
|
// We got it. Now we need to implant our new Sid into KeyValueInfo and
|
|
// write him back out. This is really gross...
|
|
//
|
|
// We're not going to rely on this structure being constant. We'll
|
|
// brute force the replacement via a call to FindAndReplaceBlock. This
|
|
// should insulate us from changes to this structure.
|
|
//
|
|
if( DATA_TYPE == REG_SZ ) {
|
|
//
|
|
// ISSUE - 2002/03/01-brucegr,acosma: We should handle REG_MULTI_SZ like a string instead of binary data
|
|
//
|
|
|
|
//
|
|
// The user sent us a set of strings. Use StringSwitchString
|
|
// and ignore the DataLength he sent us.
|
|
//
|
|
Status = StringSwitchString( (PWCHAR)&KeyValueInfo->Data,
|
|
(DWORD) ( LengthNeeded - FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data) ) / sizeof(WCHAR),
|
|
(PWCHAR)(OldData),
|
|
(PWCHAR)(NewData) );
|
|
//
|
|
// Need to update KeyValueInfo->DataLength because SID string may have changed size!
|
|
//
|
|
if ( NT_SUCCESS( Status ) )
|
|
{
|
|
//
|
|
// StringSwitchString may have changed the length. Make sure we're in sync.
|
|
//
|
|
KeyValueInfo->DataLength = (wcslen((PWCHAR) KeyValueInfo->Data) + 1) * sizeof(WCHAR);
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// Treat it as a some non-string.
|
|
//
|
|
Status = FindAndReplaceBlock( (PUCHAR)&KeyValueInfo->Data,
|
|
KeyValueInfo->DataLength,
|
|
(PUCHAR)(OldData),
|
|
(PUCHAR)(NewData),
|
|
DataLength );
|
|
}
|
|
|
|
if( NT_SUCCESS( Status ) ) {
|
|
|
|
|
|
//
|
|
// Now write the structure back into the registry key.
|
|
//
|
|
Status = NtSetValueKey( hKey,
|
|
&UnicodeString,
|
|
0,
|
|
DATA_TYPE,
|
|
(PVOID)KeyValueInfo->Data,
|
|
KeyValueInfo->DataLength );
|
|
|
|
if( !NT_SUCCESS( Status ) ) {
|
|
DbgPrint( "SETUPCL: ReadSetWriteKey - Failed to set subkey %ws (%lx)\n", SubKeyName, Status );
|
|
} else {
|
|
#if 0
|
|
DbgPrint( "SETUPCL: ReadSetWriteKey - We updated key %ws\\%ws.\n", ParentKeyName, SubKeyName );
|
|
#endif
|
|
}
|
|
|
|
NtFlushKey( hKey );
|
|
}
|
|
|
|
//
|
|
// Clean up.
|
|
//
|
|
RtlFreeHeap( RtlProcessHeap(),
|
|
0,
|
|
KeyValueInfo );
|
|
|
|
//
|
|
// Yuck. Don't close him if he came in as the ParentKeyHandle.
|
|
//
|
|
if( !ParentKeyHandle ) {
|
|
NtClose( hKey );
|
|
}
|
|
|
|
return( Status );
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LoadUnloadHive(
|
|
IN PWSTR KeyName,
|
|
IN PWSTR FileName
|
|
)
|
|
|
|
/*++
|
|
===============================================================================
|
|
Routine Description:
|
|
|
|
This function will load a hive from a file.
|
|
|
|
Arguments:
|
|
|
|
KeyName - The name of the hive we'll be loading/saving.
|
|
FileName - The name of the file we'll be loading/saving.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
===============================================================================
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
UNICODE_STRING KeyNameUString,
|
|
FileNameUString;
|
|
OBJECT_ATTRIBUTES ObjectAttributesKey,
|
|
ObjectAttributesFile;
|
|
|
|
|
|
INIT_OBJA( &ObjectAttributesKey,
|
|
&KeyNameUString,
|
|
KeyName );
|
|
|
|
ObjectAttributesKey.RootDirectory = NULL;
|
|
|
|
if( FileName == NULL ) {
|
|
|
|
//
|
|
// Delete the key
|
|
//
|
|
Status = NtUnloadKey( &ObjectAttributesKey );
|
|
TEST_STATUS( "SETUPCL: LoadUnloadHive - Failed to unload the key." );
|
|
} else {
|
|
|
|
//
|
|
// Load the key from a file.
|
|
//
|
|
INIT_OBJA( &ObjectAttributesFile, &FileNameUString, FileName );
|
|
ObjectAttributesFile.RootDirectory = NULL;
|
|
Status = NtLoadKey( &ObjectAttributesKey, &ObjectAttributesFile );
|
|
TEST_STATUS( "SETUPCL: LoadUnloadHive - Failed to load the key." );
|
|
}
|
|
|
|
return( Status );
|
|
}
|
|
|
|
NTSTATUS
|
|
BackupRepairHives(
|
|
)
|
|
|
|
/*++
|
|
===============================================================================
|
|
Routine Description:
|
|
|
|
Make a double-secret copy of the repair hives in case we
|
|
get halfway through operating on them and something goes wrong.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
===============================================================================
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// We'd like to make a backup copy of the repair hives before we mess with
|
|
// them. This way, if something goes wrong, we can restore the originals
|
|
// and leave the repair hives with the old SID.
|
|
//
|
|
|
|
DbgPrint( "\nAbout to copy repair SAM hive.\n" );
|
|
Status = FileCopy( TEXT( BACKUP_REPAIR_SAM_HIVE ),
|
|
TEXT( REPAIR_SAM_HIVE ) );
|
|
TEST_STATUS_RETURN( "SETUPCL: BackupRepairHives - Failed to save backup repair SAM hive." );
|
|
|
|
|
|
DbgPrint( "About to copy repair SECURITY hive.\n" );
|
|
Status = FileCopy( TEXT( BACKUP_REPAIR_SECURITY_HIVE ),
|
|
TEXT( REPAIR_SECURITY_HIVE ) );
|
|
TEST_STATUS_RETURN( "SETUPCL: BackupRepairHives - Failed to save backup repair SECURITY hive." );
|
|
|
|
|
|
DbgPrint( "About to copy repair SOFTWARE hive.\n" );
|
|
Status = FileCopy( TEXT( BACKUP_REPAIR_SOFTWARE_HIVE ),
|
|
TEXT( REPAIR_SOFTWARE_HIVE ) );
|
|
TEST_STATUS_RETURN( "SETUPCL: BackupRepairHives - Failed to save backup repair SOFTWARE hive." );
|
|
|
|
|
|
DbgPrint( "About to copy repair SYSTEM hive.\n" );
|
|
Status = FileCopy( TEXT( BACKUP_REPAIR_SYSTEM_HIVE ),
|
|
TEXT( REPAIR_SYSTEM_HIVE ) );
|
|
TEST_STATUS_RETURN( "SETUPCL: BackupRepairHives - Failed to save backup repair SYSTEM hive." );
|
|
|
|
return( Status );
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
CleanupRepairHives(
|
|
NTSTATUS RepairHivesSuccess
|
|
)
|
|
|
|
/*++
|
|
===============================================================================
|
|
Routine Description:
|
|
|
|
Decide whether or not to restore the repair hives from the backups
|
|
we made.
|
|
|
|
Delete the backups we made.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
===============================================================================
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// See if we need to restore the repair hives from the backups
|
|
// we made.
|
|
//
|
|
if( !NT_SUCCESS(RepairHivesSuccess) ) {
|
|
|
|
//
|
|
// Replace the repair hives with the backups. This will "undo"
|
|
// any problems that happened while we were trying to update
|
|
// the domain SID in the repair hives.
|
|
//
|
|
DbgPrint( "About to restore from backup repair hives.\n" );
|
|
|
|
Status = FileCopy( TEXT( REPAIR_SAM_HIVE ),
|
|
TEXT( BACKUP_REPAIR_SAM_HIVE ) );
|
|
TEST_STATUS_RETURN( "SETUPCL: CleanupRepairHives - Failed to restore SAM hive from backup." );
|
|
|
|
|
|
Status = FileCopy( TEXT( REPAIR_SECURITY_HIVE ),
|
|
TEXT( BACKUP_REPAIR_SECURITY_HIVE ) );
|
|
TEST_STATUS_RETURN( "SETUPCL: CleanupRepairHives - Failed to restore SECURITY hive from backup." );
|
|
|
|
|
|
Status = FileCopy( TEXT( REPAIR_SOFTWARE_HIVE ),
|
|
TEXT( BACKUP_REPAIR_SOFTWARE_HIVE ) );
|
|
TEST_STATUS_RETURN( "SETUPCL: CleanupRepairHives - Failed to restore SOFTWARE hive from backup." );
|
|
|
|
|
|
Status = FileCopy( TEXT( REPAIR_SYSTEM_HIVE ),
|
|
TEXT( BACKUP_REPAIR_SYSTEM_HIVE ) );
|
|
TEST_STATUS_RETURN( "SETUPCL: CleanupRepairHives - Failed to restore SYSTEM hive from backup." );
|
|
|
|
}
|
|
|
|
//
|
|
// Delete the backups of the repair hives.
|
|
//
|
|
DbgPrint( "About to delete backup repair hives.\n" );
|
|
|
|
Status = FileDelete( TEXT( BACKUP_REPAIR_SAM_HIVE ) );
|
|
TEST_STATUS( "SETUPCL: CleanupRepairHives - Failed to delete backup repair SAM hive." );
|
|
|
|
Status = FileDelete( TEXT( BACKUP_REPAIR_SECURITY_HIVE ) );
|
|
TEST_STATUS( "SETUPCL: CleanupRepairHives - Failed to delete backup repair SECURITY hive." );
|
|
|
|
Status = FileDelete( TEXT( BACKUP_REPAIR_SOFTWARE_HIVE ) );
|
|
TEST_STATUS( "SETUPCL: CleanupRepairHives - Failed to delete backup repair SOFTWARE hive." );
|
|
|
|
Status = FileDelete( TEXT( BACKUP_REPAIR_SYSTEM_HIVE ) );
|
|
TEST_STATUS( "SETUPCL: CleanupRepairHives - Failed to delete backup repair SYSTEM hive." );
|
|
|
|
return( Status );
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
TestSetSecurityObject(
|
|
HANDLE handle
|
|
)
|
|
/*++
|
|
===============================================================================
|
|
Routine Description:
|
|
|
|
This function will read security information from the object. It
|
|
will then see if that security info contains any instances of the
|
|
old SID. If we find any, we'll replace them with the new SID.
|
|
|
|
Arguments:
|
|
|
|
hKey - Handle of the object we're operating on.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
===============================================================================
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PSECURITY_DESCRIPTOR pSD;
|
|
ULONG ResultLength,
|
|
ShadowLength;
|
|
INT i;
|
|
|
|
//
|
|
// Find out how big the descriptor is.
|
|
//
|
|
Status = NtQuerySecurityObject( handle,
|
|
ALL_SECURITY_INFORMATION,
|
|
NULL,
|
|
0,
|
|
&ShadowLength );
|
|
|
|
if (Status != STATUS_BUFFER_TOO_SMALL) {
|
|
DbgPrint( "SETUPCL: TestSetSecurityObject - Failed to query object security for size (%lx)\n", Status);
|
|
return( Status );
|
|
}
|
|
|
|
//
|
|
// Allocate our buffer.
|
|
//
|
|
pSD = (PSECURITY_DESCRIPTOR)RtlAllocateHeap( RtlProcessHeap(),
|
|
0,
|
|
ShadowLength + 0x10 );
|
|
if( !pSD ) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
//
|
|
// Load the security info, whack it, and write it back out.
|
|
//
|
|
Status = NtQuerySecurityObject( handle,
|
|
ALL_SECURITY_INFORMATION,
|
|
pSD,
|
|
ShadowLength + 0x10,
|
|
&ResultLength );
|
|
TEST_STATUS( "SETUPCL: TestSetSecurityObject - Failed to query security info." );
|
|
|
|
Status = FindAndReplaceBlock( (PUCHAR)pSD,
|
|
ShadowLength,
|
|
(PUCHAR)G_OldSid + (SID_SIZE - 0xC),
|
|
(PUCHAR)G_NewSid + (SID_SIZE - 0xC),
|
|
0xC );
|
|
if( NT_SUCCESS( Status ) ) {
|
|
|
|
//
|
|
// We hit. Write out the new security info.
|
|
//
|
|
Status = NtSetSecurityObject( handle,
|
|
ALL_SECURITY_INFORMATION,
|
|
pSD );
|
|
TEST_STATUS( "SETUPCL: TestSetSecurityObject - Failed to set security info." );
|
|
}
|
|
|
|
//
|
|
// Clean up.
|
|
//
|
|
RtlFreeHeap( RtlProcessHeap(),
|
|
0,
|
|
pSD );
|
|
|
|
return( STATUS_SUCCESS );
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SetKeySecurityRecursive(
|
|
HANDLE hKey
|
|
)
|
|
/*++
|
|
===============================================================================
|
|
Routine Description:
|
|
|
|
Set security on a registry tree.
|
|
|
|
Arguments:
|
|
|
|
hKey - Handle of the key we're operating on.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
===============================================================================
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PKEY_FULL_INFORMATION FullKeyInfo;
|
|
PKEY_BASIC_INFORMATION BasicKeyInfo;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
UNICODE_STRING UnicodeString;
|
|
HANDLE hKeyChild;
|
|
ULONG ResultLength;
|
|
DWORD dwNumSubKeys;
|
|
|
|
Status = TestSetSecurityObject( hKey );
|
|
TEST_STATUS( "SETUPCL: SetKeySecurityRecursive - Failed call out to TestSetSecurityObject()." );
|
|
|
|
//
|
|
// Alocate a buffer for KEY_FULL_INFORMATION
|
|
//
|
|
FullKeyInfo = (PKEY_FULL_INFORMATION)RtlAllocateHeap( RtlProcessHeap(),
|
|
0,
|
|
FULL_INFO_BUFFER_SIZE );
|
|
if (FullKeyInfo)
|
|
{
|
|
|
|
Status = NtQueryKey( hKey,
|
|
KeyFullInformation,
|
|
FullKeyInfo,
|
|
FULL_INFO_BUFFER_SIZE,
|
|
&ResultLength);
|
|
|
|
//
|
|
// Even if the call above failed this will be caught below.
|
|
//
|
|
dwNumSubKeys = FullKeyInfo->SubKeys;
|
|
|
|
//
|
|
// Free the memory right away after getting the number of subkeys.
|
|
//
|
|
RtlFreeHeap( RtlProcessHeap(),
|
|
0,
|
|
FullKeyInfo );
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
//
|
|
// Alocate a buffer for KEY_BASIC_INFORMATION
|
|
//
|
|
BasicKeyInfo = (PKEY_BASIC_INFORMATION)RtlAllocateHeap( RtlProcessHeap(),
|
|
0,
|
|
BASIC_INFO_BUFFER_SIZE );
|
|
if (BasicKeyInfo)
|
|
{
|
|
DWORD dwSubKeyCount;
|
|
for ( dwSubKeyCount = 0; dwSubKeyCount < dwNumSubKeys; dwSubKeyCount++ )
|
|
{
|
|
Status = NtEnumerateKey( hKey,
|
|
dwSubKeyCount,
|
|
KeyBasicInformation,
|
|
BasicKeyInfo,
|
|
BASIC_INFO_BUFFER_SIZE,
|
|
&ResultLength );
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
//
|
|
// Zero-terminate the subkey name just in case.
|
|
//
|
|
BasicKeyInfo->Name[BasicKeyInfo->NameLength/sizeof(WCHAR)] = 0;
|
|
|
|
//
|
|
// Generate a handle for this child key and call ourselves again.
|
|
//
|
|
INIT_OBJA( &Obja, &UnicodeString, BasicKeyInfo->Name );
|
|
Obja.RootDirectory = hKey;
|
|
Status = NtOpenKey( &hKeyChild,
|
|
KEY_ALL_ACCESS | ACCESS_SYSTEM_SECURITY,
|
|
&Obja );
|
|
|
|
if ( NT_SUCCESS(Status) )
|
|
{
|
|
Status = SetKeySecurityRecursive( hKeyChild );
|
|
NtClose( hKeyChild );
|
|
}
|
|
else
|
|
{
|
|
TEST_STATUS( "SETUPCL: SetKeySecurityRecursive - Failed to open child key." );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TEST_STATUS( "SETUPCL: SetKeySecurityRecursive - Failed to enumerate key." );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Free the memory held for the children
|
|
//
|
|
RtlFreeHeap( RtlProcessHeap(),
|
|
0,
|
|
BasicKeyInfo );
|
|
}
|
|
else
|
|
{
|
|
TEST_STATUS( "SETUPCL: SetKeySecurityRecursive - Out of memory when allocating BasicKeyInfo." );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TEST_STATUS( "SETUPCL: SetKeySecurityRecursive - Failed to query full key information." );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TEST_STATUS( "SETUPCL: SetKeySecurityRecursive - Out of memory when allocating FullKeyInfo." );
|
|
}
|
|
|
|
return( Status );
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CopyKeyRecursive(
|
|
HANDLE hKeyDst,
|
|
HANDLE hKeySrc
|
|
)
|
|
/*++
|
|
===============================================================================
|
|
Routine Description:
|
|
|
|
Copy a registry key (and all its subkeys) to a new key.
|
|
Arguments:
|
|
|
|
hKeyDst - Handle of the new key we're going to create.
|
|
hKeySrc - Handle of the key we're copying.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
===============================================================================
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
OBJECT_ATTRIBUTES ObjaSrc,
|
|
ObjaDst;
|
|
UNICODE_STRING UnicodeStringSrc,
|
|
UnicodeStringDst,
|
|
UnicodeStringValue;
|
|
HANDLE hKeySrcChild = NULL,
|
|
hKeyDstChild = NULL;
|
|
ULONG ResultLength,
|
|
BufferLength,
|
|
Index;
|
|
WCHAR ValueBuffer[FULL_INFO_BUFFER_SIZE],
|
|
TmpChar;
|
|
PSECURITY_DESCRIPTOR pSD = NULL;
|
|
|
|
PKEY_BASIC_INFORMATION KeyInfo;
|
|
PKEY_VALUE_FULL_INFORMATION ValueInfo;
|
|
|
|
|
|
//
|
|
// Enumerate all keys in the source key and recursively create
|
|
// all the subkeys
|
|
//
|
|
KeyInfo = (PKEY_BASIC_INFORMATION) ValueBuffer;
|
|
|
|
for( Index = 0; ; Index++ )
|
|
{
|
|
Status = NtEnumerateKey( hKeySrc, Index, KeyBasicInformation, ValueBuffer, sizeof( ValueBuffer ), &ResultLength );
|
|
|
|
if ( !NT_SUCCESS(Status) )
|
|
{
|
|
if( Status == STATUS_NO_MORE_ENTRIES)
|
|
{
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
PRINT_STATUS( "SETUPCL: CopyKeyRecursive - failed to enumerate src keys." );
|
|
}
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Zero-terminate the subkey name just in case.
|
|
//
|
|
KeyInfo->Name[KeyInfo->NameLength/sizeof(WCHAR)] = 0;
|
|
|
|
//
|
|
// Generate key handles for these children and call ourselves again.
|
|
//
|
|
INIT_OBJA( &ObjaSrc, &UnicodeStringSrc, KeyInfo->Name );
|
|
ObjaSrc.RootDirectory = hKeySrc;
|
|
|
|
Status = NtOpenKey( &hKeySrcChild, KEY_ALL_ACCESS | ACCESS_SYSTEM_SECURITY, &ObjaSrc );
|
|
|
|
if ( NT_SUCCESS(Status) )
|
|
{
|
|
Status = NtQuerySecurityObject( hKeySrcChild, ALL_SECURITY_INFORMATION, NULL, 0, &BufferLength );
|
|
|
|
if ( Status == STATUS_BUFFER_TOO_SMALL )
|
|
{
|
|
//
|
|
// Allocate our buffer.
|
|
//
|
|
if ( pSD = (PSECURITY_DESCRIPTOR)RtlAllocateHeap( RtlProcessHeap(), 0, BufferLength ) )
|
|
{
|
|
//
|
|
// Load the security info from the sources key.
|
|
//
|
|
Status = NtQuerySecurityObject( hKeySrcChild,
|
|
ALL_SECURITY_INFORMATION,
|
|
pSD,
|
|
BufferLength,
|
|
&ResultLength );
|
|
}
|
|
else
|
|
{
|
|
Status = STATUS_NO_MEMORY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TEST_STATUS( "SETUPCL: CopyKeyRecursive - Failed to query object security for size.");
|
|
}
|
|
|
|
if ( NT_SUCCESS(Status) )
|
|
{
|
|
INIT_OBJA( &ObjaDst, &UnicodeStringDst, KeyInfo->Name );
|
|
|
|
ObjaDst.RootDirectory = hKeyDst;
|
|
ObjaDst.SecurityDescriptor = pSD;
|
|
|
|
Status = NtCreateKey( &hKeyDstChild,
|
|
KEY_ALL_ACCESS | ACCESS_SYSTEM_SECURITY,
|
|
&ObjaDst,
|
|
0,
|
|
NULL,
|
|
REG_OPTION_NON_VOLATILE,
|
|
NULL );
|
|
|
|
if ( NT_SUCCESS(Status) )
|
|
{
|
|
// Call ourselves to copy the child keys.
|
|
//
|
|
Status = CopyKeyRecursive( hKeyDstChild, hKeySrcChild );
|
|
TEST_STATUS("SETUPCL: CopyKeyRecursive - Recursive call failed.");
|
|
|
|
NtClose( hKeyDstChild );
|
|
}
|
|
else
|
|
{
|
|
PRINT_STATUS( "SETUPCL: CopyKeyRecursive - Failed to create destination child key." );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PRINT_STATUS( "SETUPCL: CopyKeyRecursive - Failed to get key security descriptor." );
|
|
}
|
|
|
|
// If we allocated a buffer for the security descriptor, free it now.
|
|
//
|
|
if ( pSD )
|
|
{
|
|
RtlFreeHeap( RtlProcessHeap(), 0, pSD );
|
|
pSD = NULL;
|
|
}
|
|
|
|
NtClose( hKeySrcChild );
|
|
}
|
|
else
|
|
{
|
|
PRINT_STATUS( "SETUPCL: CopyKeyRecursive - Failed to open source child key." );
|
|
}
|
|
}
|
|
|
|
//
|
|
// We don't really care about the return value here since even if something above failed
|
|
// for some reason, we should still do the copy for the values in this key.
|
|
//
|
|
|
|
//
|
|
// Enumerate all values in the source key and create all the values
|
|
// in the destination key
|
|
//
|
|
ValueInfo = (PKEY_VALUE_FULL_INFORMATION) ValueBuffer;
|
|
|
|
for( Index = 0; ; Index++ )
|
|
{
|
|
// Zero the buffer in between each iteration.
|
|
//
|
|
RtlZeroMemory( ValueBuffer, sizeof(ValueBuffer) );
|
|
|
|
Status = NtEnumerateValueKey( hKeySrc, Index, KeyValueFullInformation, ValueBuffer, sizeof( ValueBuffer ), &ResultLength );
|
|
|
|
if ( !NT_SUCCESS(Status) )
|
|
{
|
|
if ( Status == STATUS_NO_MORE_ENTRIES )
|
|
{
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
PRINT_STATUS( "SETUPCL: CopyKeyRecursive - failed to enumerate src value keys." );
|
|
}
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Zero-terminate the subkey name just in case, init the
|
|
// unicode string and restore the wchar we whacked.
|
|
//
|
|
|
|
//
|
|
// ISSUE-2002/03/11-acosma - This is really funky. If we have a non-NULL terminated
|
|
// string we will end up with a UnicodeString that is not NULL terminated and has an extra
|
|
// character at the end. Maybe we should just increase the size of it by one to use the
|
|
// space where NULL should be as part of the string.
|
|
//
|
|
TmpChar = ValueInfo->Name[ValueInfo->NameLength/sizeof(WCHAR)];
|
|
ValueInfo->Name[ValueInfo->NameLength/sizeof(WCHAR)] = 0;
|
|
RtlInitUnicodeString( &UnicodeStringValue, ValueInfo->Name );
|
|
ValueInfo->Name[ValueInfo->NameLength/sizeof(WCHAR)] = TmpChar;
|
|
|
|
//
|
|
// Create the destination value.
|
|
//
|
|
Status = NtSetValueKey( hKeyDst,
|
|
&UnicodeStringValue,
|
|
ValueInfo->TitleIndex,
|
|
ValueInfo->Type,
|
|
(PBYTE)ValueInfo + ValueInfo->DataOffset,
|
|
ValueInfo->DataLength );
|
|
if( !NT_SUCCESS(Status) ) {
|
|
PRINT_STATUS( "SETUPCL: CopyKeyRecursive - failed to set destination value key." );
|
|
break;
|
|
}
|
|
}
|
|
|
|
return( Status );
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CopyRegKey(
|
|
IN WCHAR *TargetName,
|
|
IN WCHAR *SourceName,
|
|
IN HANDLE hParentKey OPTIONAL
|
|
)
|
|
/*++
|
|
===============================================================================
|
|
Routine Description:
|
|
|
|
Copy a registry key (and all its subkeys) to a new key.
|
|
Arguments:
|
|
|
|
TargetName - The name of the new key we're going to create.
|
|
SourceName - The name of the key we're copying.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
===============================================================================
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
HANDLE hKeySrc,
|
|
hKeyDst;
|
|
UNICODE_STRING UnicodeString;
|
|
OBJECT_ATTRIBUTES ObjaSrc,
|
|
ObjaDst;
|
|
ULONG BufferLength,
|
|
ResultLength;
|
|
PSECURITY_DESCRIPTOR pSD = NULL;
|
|
|
|
// Generate key handle for source key.
|
|
//
|
|
INIT_OBJA( &ObjaSrc, &UnicodeString, SourceName );
|
|
|
|
if ( hParentKey )
|
|
{
|
|
ObjaSrc.RootDirectory = hParentKey;
|
|
}
|
|
Status = NtOpenKey( &hKeySrc, KEY_ALL_ACCESS | ACCESS_SYSTEM_SECURITY, &ObjaSrc );
|
|
|
|
if ( NT_SUCCESS( Status ) )
|
|
{
|
|
// Find out how big the descriptor is.
|
|
//
|
|
Status = NtQuerySecurityObject( hKeySrc, ALL_SECURITY_INFORMATION, NULL, 0, &BufferLength );
|
|
|
|
if ( Status == STATUS_BUFFER_TOO_SMALL )
|
|
{
|
|
// Allocate the buffer for the security descriptor.
|
|
//
|
|
if ( pSD = (PSECURITY_DESCRIPTOR)RtlAllocateHeap( RtlProcessHeap(), 0, BufferLength ) )
|
|
{
|
|
// Load the security info into the buffer.
|
|
//
|
|
Status = NtQuerySecurityObject( hKeySrc, ALL_SECURITY_INFORMATION, pSD, BufferLength, &ResultLength );
|
|
}
|
|
else
|
|
{
|
|
Status = STATUS_NO_MEMORY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TEST_STATUS( "SETUPCL: CopyRegKey - Failed to query object security for size.");
|
|
}
|
|
|
|
if ( NT_SUCCESS(Status) )
|
|
{
|
|
INIT_OBJA( &ObjaDst, &UnicodeString, TargetName );
|
|
ObjaDst.SecurityDescriptor = pSD;
|
|
|
|
if ( hParentKey )
|
|
{
|
|
ObjaDst.RootDirectory = hParentKey;
|
|
}
|
|
|
|
// Create the destination key.
|
|
//
|
|
Status = NtCreateKey( &hKeyDst,
|
|
KEY_ALL_ACCESS | ACCESS_SYSTEM_SECURITY,
|
|
&ObjaDst,
|
|
0,
|
|
NULL,
|
|
REG_OPTION_NON_VOLATILE,
|
|
NULL );
|
|
|
|
if ( NT_SUCCESS(Status) )
|
|
{
|
|
Status = CopyKeyRecursive( hKeyDst, hKeySrc );
|
|
// Close the destination key.
|
|
//
|
|
NtClose( hKeyDst );
|
|
}
|
|
else
|
|
{
|
|
PRINT_STATUS( "SETUPCL: CopyRegKey - Failed to create destination key.");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PRINT_STATUS("SETUPCL: CopyRegKey - Failed to get key security descriptor.");
|
|
}
|
|
|
|
// If we allocated a buffer for the security descriptor, free it now.
|
|
//
|
|
if ( pSD )
|
|
{
|
|
RtlFreeHeap( RtlProcessHeap(), 0, pSD );
|
|
pSD = NULL;
|
|
}
|
|
|
|
// Close the source key.
|
|
//
|
|
NtClose( hKeySrc );
|
|
}
|
|
else
|
|
{
|
|
PRINT_STATUS( "SETUPCL: CopyRegKey - Failed to open source key." );
|
|
}
|
|
|
|
return( Status );
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// ISSUE-2002/02/26-brucegr,jcohen - Dead Code! Nobody calls MoveRegKey!
|
|
//
|
|
NTSTATUS
|
|
MoveRegKey(
|
|
IN WCHAR *TargetName,
|
|
IN WCHAR *SourceName
|
|
)
|
|
/*++
|
|
===============================================================================
|
|
Routine Description:
|
|
|
|
Move a registry key (and all its subkeys) to a new key.
|
|
Arguments:
|
|
|
|
TargetName - The name of the new key we're going to create.
|
|
SourceName - The name of the key we're copying.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
===============================================================================
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// Copy the original..
|
|
//
|
|
Status = CopyRegKey( TargetName, SourceName, NULL );
|
|
TEST_STATUS_RETURN( "SETUPCL: MoveRegKey - CopyRegKey failed!" );
|
|
|
|
//
|
|
// Delete the original key.
|
|
//
|
|
Status = DeleteKey( SourceName );
|
|
TEST_STATUS( "SETUPCL: MoveRegKey - DeleteKey failed!" );
|
|
|
|
|
|
return( Status );
|
|
}
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
FindAndReplaceBlock(
|
|
IN PCHAR Block,
|
|
IN ULONG BlockLength,
|
|
IN PCHAR OldValue,
|
|
IN PCHAR NewValue,
|
|
IN ULONG ValueLength
|
|
)
|
|
|
|
/*++
|
|
===============================================================================
|
|
Routine Description:
|
|
|
|
This function will go search Block for any and all instances of OldValue.
|
|
If he finds one, he'll replace that section with NewValue.
|
|
|
|
Arguments:
|
|
|
|
Block - A block of memory that we'll be searching
|
|
BlockLength - How big is Block
|
|
OldValue - What value are we looking for?
|
|
NewValue - What's the new value we'll be inserting?
|
|
ValueLength - How long are the Old and New values?
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
===============================================================================
|
|
--*/
|
|
|
|
{
|
|
ULONG i;
|
|
BOOLEAN We_Hit = FALSE;
|
|
|
|
//
|
|
// Make sure the lengths make sense... If not, we're done.
|
|
//
|
|
if( BlockLength < ValueLength ) {
|
|
#if 0
|
|
DbgPrint( "SETUPCL: FindAndReplaceBlock - Mismatched data lengths!\n\tBlockLength: (%lx)\n\tValueLength: (%lx)\n", BlockLength, ValueLength );
|
|
#endif
|
|
return( STATUS_UNSUCCESSFUL );
|
|
}
|
|
|
|
//
|
|
// We start at the beginning and search for any instances of OldValue.
|
|
//
|
|
i = 0;
|
|
while( i <= (BlockLength - ValueLength) ) {
|
|
if( !memcmp( (Block + i), OldValue, ValueLength ) ) {
|
|
|
|
//
|
|
// Record that we hit at least once.
|
|
//
|
|
We_Hit = TRUE;
|
|
|
|
//
|
|
// We got a hit. Insert NewValue.
|
|
//
|
|
memcpy( (Block + i), NewValue, ValueLength );
|
|
|
|
//
|
|
// Let's skip checking this block. We're asking for trouble
|
|
// if we don't.
|
|
//
|
|
i = i + ValueLength;
|
|
} else {
|
|
i++;
|
|
}
|
|
}
|
|
|
|
if( !We_Hit ) {
|
|
//
|
|
// We didn't find a match. It's likely non-fatal,
|
|
// but we need to tell our caller.
|
|
//
|
|
#if 0
|
|
DbgPrint( "SETUPCL: FindAndReplaceBlock - We didn't find any hits in this data block.\n" );
|
|
#endif
|
|
return( STATUS_UNSUCCESSFUL );
|
|
} else {
|
|
#if 0
|
|
DbgPrint( "SETUPCL: FindAndReplaceBlock - We hit in this data block.\n" );
|
|
#endif
|
|
return( STATUS_SUCCESS );
|
|
}
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
StringSwitchString(
|
|
PWSTR BaseString,
|
|
DWORD cBaseStringLen,
|
|
PWSTR OldSubString,
|
|
PWSTR NewSubString
|
|
)
|
|
|
|
/*++
|
|
===============================================================================
|
|
Routine Description:
|
|
|
|
This function search BaseString for any instance of OldSubString. If
|
|
found, he'll replace that instance with NewSubString. Note that
|
|
OldSubString and NewSubString can be different lengths.
|
|
|
|
Arguments:
|
|
|
|
BaseString - This is the string we'll be operating on.
|
|
OldSubString - String we're looking for
|
|
NewSubString - String we'll be inserting
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
===============================================================================
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PWSTR Index;
|
|
WCHAR New_String[MAX_PATH] = {0};
|
|
WCHAR TmpChar;
|
|
|
|
Index = wcsstr( BaseString, OldSubString );
|
|
|
|
if( !Index ) {
|
|
//
|
|
// OldSubString isn't present.
|
|
//
|
|
return( STATUS_UNSUCCESSFUL );
|
|
}
|
|
|
|
//
|
|
// Copy the first part of the original string into New_String.
|
|
//
|
|
TmpChar = *Index;
|
|
*Index = 0;
|
|
wcsncpy( New_String, BaseString, AS(New_String) - 1 );
|
|
|
|
//
|
|
// Now concatenate the new sub string...
|
|
//
|
|
wcsncpy( New_String + wcslen(New_String), NewSubString, AS(New_String) - wcslen(New_String) - 1 );
|
|
|
|
//
|
|
// Jump past the OldSubString, and cat the remaining BaseString
|
|
// onto the end.
|
|
//
|
|
Index = Index + wcslen( OldSubString );
|
|
|
|
wcsncpy( New_String + wcslen(New_String), Index, AS(New_String) - wcslen(New_String) - 1);
|
|
|
|
memset( BaseString, 0, cBaseStringLen * sizeof(WCHAR) );
|
|
wcsncpy( BaseString, New_String, cBaseStringLen - 1 );
|
|
|
|
return( STATUS_SUCCESS );
|
|
}
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
SiftKeyRecursive(
|
|
HANDLE hKey,
|
|
int indent
|
|
)
|
|
|
|
/*++
|
|
===============================================================================
|
|
Routine Description:
|
|
|
|
This function check all of the subkeys and any valuekeys for:
|
|
- keys with the old SID name
|
|
In this case, we rename the key with the appropriate new SID name.
|
|
|
|
- value keys with the old SID value
|
|
In this case, we substitute the new SID values for the old SID values.
|
|
|
|
Arguments:
|
|
|
|
hKey - Handle to the key we're about to recurse into.
|
|
indent - For debug. Number of spaces to indent any messages.
|
|
This helps us determine which recurse we're in.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
===============================================================================
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
UNICODE_STRING UnicodeString;
|
|
HANDLE hKeyChild;
|
|
ULONG ResultLength, Index;
|
|
PKEY_BASIC_INFORMATION KeyInfo;
|
|
PKEY_VALUE_BASIC_INFORMATION ValueInfo;
|
|
int i;
|
|
|
|
|
|
// DisplayUI
|
|
//
|
|
DisplayUI();
|
|
|
|
//
|
|
// Enumerate all keys in the source key and recursively create
|
|
// all the subkeys
|
|
//
|
|
KeyInfo = (PKEY_BASIC_INFORMATION)RtlAllocateHeap( RtlProcessHeap(),
|
|
0,
|
|
BASIC_INFO_BUFFER_SIZE );
|
|
if( KeyInfo == NULL ) {
|
|
#if I_AM_MATTH
|
|
DbgPrint( "SETUPCL: SiftKeyRecursive - Call to RtlAllocateHeap failed!\n" );
|
|
#endif
|
|
return( STATUS_NO_MEMORY );
|
|
}
|
|
|
|
Index = 0;
|
|
while( 1 ) {
|
|
Status = NtEnumerateKey( hKey,
|
|
Index,
|
|
KeyBasicInformation,
|
|
KeyInfo,
|
|
BASIC_INFO_BUFFER_SIZE,
|
|
&ResultLength );
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
if(Status == STATUS_NO_MORE_ENTRIES) {
|
|
Status = STATUS_SUCCESS;
|
|
} else {
|
|
TEST_STATUS( "SETUPCL: SiftKeyRecursive - Failure during enumeration of subkeys." );
|
|
}
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Zero-terminate the subkey name just in case.
|
|
//
|
|
KeyInfo->Name[KeyInfo->NameLength/sizeof(WCHAR)] = 0;
|
|
|
|
memset( TmpBuffer, 0, sizeof(TmpBuffer) );
|
|
wcsncpy( TmpBuffer, KeyInfo->Name, AS(TmpBuffer) - 1 );
|
|
Status = StringSwitchString( TmpBuffer,
|
|
AS( TmpBuffer ),
|
|
G_OldSidSubString,
|
|
G_NewSidSubString );
|
|
|
|
if( NT_SUCCESS( Status ) ) {
|
|
//
|
|
// We need to rename this key. First do the
|
|
// copy, then a delete.
|
|
//
|
|
#if I_AM_MATTH
|
|
for( i = 0; i < indent; i++ )
|
|
DbgPrint( " " );
|
|
DbgPrint( "SETUPCL: SiftKeyRecursive - About to copy subkey:\n" );
|
|
DbgPrint( "\t%ws\n\tto\n\t%ws\n", KeyInfo->Name, TmpBuffer );
|
|
#endif
|
|
Status = CopyRegKey( TmpBuffer,
|
|
KeyInfo->Name,
|
|
hKey );
|
|
if( !NT_SUCCESS( Status ) ) {
|
|
TEST_STATUS( "SETUPCL: SiftKeyRecursive - failed call to CopyRegKey." );
|
|
break;
|
|
}
|
|
DeleteKeyRecursive( hKey, KeyInfo->Name );
|
|
|
|
// Flush the key to make sure that everything gets written out to disk.
|
|
//
|
|
NtFlushKey(hKey);
|
|
//
|
|
// Now reset our index since we've just changed the ordering
|
|
// of keys.
|
|
//
|
|
Index = 0;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// We didn't rename him, so let's recursively call ourselves
|
|
// on the subkey key.
|
|
//
|
|
#if I_AM_MATTH
|
|
for( i = 0; i < indent; i++ )
|
|
DbgPrint( " " );
|
|
DbgPrint( "SETUPCL: SiftKeyRecursive - About to check subkey: %ws\n",
|
|
KeyInfo->Name );
|
|
#endif
|
|
|
|
//
|
|
// Generate a handle for this child key and call ourselves again.
|
|
//
|
|
INIT_OBJA( &Obja, &UnicodeString, KeyInfo->Name );
|
|
Obja.RootDirectory = hKey;
|
|
Status = NtOpenKey( &hKeyChild,
|
|
KEY_ALL_ACCESS | ACCESS_SYSTEM_SECURITY,
|
|
&Obja );
|
|
TEST_STATUS_RETURN( "SETUPCL: SiftKeyRecursive - Failed to open child key." );
|
|
|
|
Status = SiftKeyRecursive( hKeyChild, indent + 1 );
|
|
|
|
NtClose( hKeyChild );
|
|
|
|
Index++;
|
|
}
|
|
|
|
//
|
|
// Enumerate all values in the key and search for instances
|
|
// of the old SID.
|
|
//
|
|
ValueInfo = (PKEY_VALUE_BASIC_INFORMATION)KeyInfo;
|
|
for( Index = 0; ; Index++ ) {
|
|
Status = NtEnumerateValueKey( hKey,
|
|
Index,
|
|
KeyValueBasicInformation,
|
|
ValueInfo,
|
|
BASIC_INFO_BUFFER_SIZE,
|
|
&ResultLength );
|
|
if(!NT_SUCCESS(Status)) {
|
|
if(Status == STATUS_NO_MORE_ENTRIES) {
|
|
Status = STATUS_SUCCESS;
|
|
} else {
|
|
TEST_STATUS( "SETUPCL: SiftKeyRecursive - Failure during enumeration of value keys." );
|
|
}
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Zero-terminate the subkey name just in case.
|
|
//
|
|
ValueInfo->Name[ValueInfo->NameLength/sizeof(WCHAR)] = 0;
|
|
|
|
//
|
|
// ISSUE - 2002/03/01-brucegr,acosma: We don't handle value names containing the old SID.
|
|
//
|
|
|
|
//
|
|
// We'll probably fail this call because the key probably
|
|
// doesn't contain any SID info. For that reason, don't
|
|
// treat failures here as fatal...
|
|
//
|
|
if( ValueInfo->Type == REG_SZ ) {
|
|
//
|
|
// ISSUE - 2002/03/01-brucegr,acosma: We should handle REG_MULTI_SZ like a string instead of binary data
|
|
//
|
|
Status = ReadSetWriteKey( NULL, // No parent Name.
|
|
hKey, // Parent handle.
|
|
ValueInfo->Name, // SubKey name
|
|
(PUCHAR)G_OldSidSubString,
|
|
(PUCHAR)G_NewSidSubString,
|
|
0xC,
|
|
ValueInfo->Type );
|
|
|
|
} else {
|
|
Status = ReadSetWriteKey( NULL, // No parent Name.
|
|
hKey, // Parent handle.
|
|
ValueInfo->Name, // SubKey name
|
|
(PUCHAR)G_OldSid + (SID_SIZE - 0xC),
|
|
(PUCHAR)G_NewSid + (SID_SIZE - 0xC),
|
|
0xC,
|
|
ValueInfo->Type );
|
|
}
|
|
|
|
#if I_AM_MATTH
|
|
if( NT_SUCCESS( Status ) ) {
|
|
for( i = 0; i < indent; i++ )
|
|
DbgPrint( " " );
|
|
DbgPrint( "SETUPCL: SiftKeyRecursive - updated subkey: %ws\n",
|
|
ValueInfo->Name );
|
|
#if 0
|
|
} else {
|
|
for( i = 0; i < indent; i++ )
|
|
DbgPrint( " " );
|
|
DbgPrint( "SETUPCL: SiftKeyRecursive - did not update subkey: %ws\n",
|
|
ValueInfo->Name );
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
RtlFreeHeap( RtlProcessHeap(),
|
|
0,
|
|
KeyInfo );
|
|
|
|
return( Status );
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SiftKey(
|
|
PWSTR KeyName
|
|
)
|
|
|
|
/*++
|
|
===============================================================================
|
|
Routine Description:
|
|
|
|
This function opens a handle to the key specified in KeyName, the
|
|
calls SiftKeyRecursive with it.
|
|
|
|
Arguments:
|
|
|
|
KeyName - Name of the key we're about to operate on.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
===============================================================================
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
HANDLE hKey;
|
|
UNICODE_STRING UnicodeString;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
|
|
//
|
|
// Open the key.
|
|
//
|
|
INIT_OBJA( &Obja, &UnicodeString, KeyName );
|
|
Status = NtOpenKey( &hKey,
|
|
KEY_ALL_ACCESS | ACCESS_SYSTEM_SECURITY,
|
|
&Obja );
|
|
TEST_STATUS( "SETUPCL: SiftKey - Failed to open key." );
|
|
|
|
//
|
|
// Fix all instances of the SID in this key and all
|
|
// it's children.
|
|
//
|
|
Status = SiftKeyRecursive( hKey, 0 );
|
|
|
|
//
|
|
// Now fix ACLs on this key and all its children.
|
|
//
|
|
SetKeySecurityRecursive( hKey );
|
|
|
|
NtClose( hKey );
|
|
|
|
return( Status );
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
DriveLetterToNTPath(
|
|
IN WCHAR DriveLetter,
|
|
IN OUT PWSTR NTPath,
|
|
IN DWORD cNTPathLen
|
|
)
|
|
|
|
/*++
|
|
===============================================================================
|
|
Routine Description:
|
|
|
|
This function will convert a driveletter to an NT path.
|
|
|
|
Arguments:
|
|
|
|
DriveLetter - DriveLetter.
|
|
|
|
NTPath - The ntpath that corresponds to the given drive letter.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
===============================================================================
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
HANDLE DosDevicesDir,
|
|
Handle;
|
|
CHAR DirInfoBuffer[1024],
|
|
LinkTargetBuffer[1024];
|
|
POBJECT_DIRECTORY_INFORMATION DirInfo;
|
|
UNICODE_STRING UnicodeString,
|
|
LinkTarget,
|
|
DesiredPrefix1,
|
|
DesiredPrefix2,
|
|
LinkTypeName;
|
|
ULONG Context,
|
|
Length;
|
|
|
|
|
|
//
|
|
// Open \DosDevices
|
|
//
|
|
RtlInitUnicodeString(&UnicodeString,L"\\DosDevices");
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
&UnicodeString,
|
|
OBJ_CASE_INSENSITIVE | OBJ_OPENIF | OBJ_PERMANENT,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
Status = NtOpenDirectoryObject(&DosDevicesDir,DIRECTORY_QUERY,&ObjectAttributes);
|
|
TEST_STATUS_RETURN( "SETUPCL: DriveLetterToNTPath - Failed to open DosDevices." );
|
|
|
|
LinkTarget.Buffer = (PVOID)LinkTargetBuffer;
|
|
RtlInitUnicodeString(&LinkTypeName,L"SymbolicLink");
|
|
|
|
DirInfo = (POBJECT_DIRECTORY_INFORMATION)DirInfoBuffer;
|
|
|
|
//
|
|
// Query first object in \DosDevices directory
|
|
//
|
|
Status = NtQueryDirectoryObject( DosDevicesDir,
|
|
DirInfo,
|
|
sizeof(DirInfoBuffer),
|
|
TRUE,
|
|
TRUE,
|
|
&Context,
|
|
&Length );
|
|
|
|
while(NT_SUCCESS(Status)) {
|
|
//
|
|
// Terminate these guys just in case...
|
|
//
|
|
DirInfo->Name.Buffer[DirInfo->Name.Length/sizeof(WCHAR)] = 0;
|
|
DirInfo->TypeName.Buffer[DirInfo->TypeName.Length/sizeof(WCHAR)] = 0;
|
|
|
|
|
|
// DbgPrint( "SETUPCL: DriveLetterToNTPath - About to examine an object: %ws\n", DirInfo->Name.Buffer );
|
|
|
|
//
|
|
// Make sure he's a drive letter.
|
|
// Make sure he's our drive letter.
|
|
// Make sure he's a symbolic link.
|
|
//
|
|
if( (DirInfo->Name.Buffer[1] == L':') &&
|
|
(DirInfo->Name.Buffer[0] == DriveLetter) &&
|
|
(RtlEqualUnicodeString(&LinkTypeName,&DirInfo->TypeName,TRUE)) ) {
|
|
|
|
// DbgPrint( "\tSETUPCL: DriveLetterToNTPath - Object: %ws is a symbolic link\n", DirInfo->Name.Buffer );
|
|
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
&DirInfo->Name,
|
|
OBJ_CASE_INSENSITIVE,
|
|
DosDevicesDir,
|
|
NULL
|
|
);
|
|
|
|
Status = NtOpenSymbolicLinkObject( &Handle,
|
|
SYMBOLIC_LINK_ALL_ACCESS,
|
|
&ObjectAttributes );
|
|
if(NT_SUCCESS(Status)) {
|
|
|
|
LinkTarget.Length = 0;
|
|
LinkTarget.MaximumLength = sizeof(LinkTargetBuffer);
|
|
|
|
Status = NtQuerySymbolicLinkObject( Handle,
|
|
&LinkTarget,
|
|
NULL );
|
|
NtClose(Handle);
|
|
|
|
TEST_STATUS( "\tSETUPCL: DriveLetterToNTPath - We failed to queried him.\n" );
|
|
|
|
LinkTarget.Buffer[LinkTarget.Length/sizeof(WCHAR)] = 0;
|
|
// DbgPrint( "\tSETUPCL: DriveLetterToNTPath - We queried him and his name is %ws.\n", LinkTarget.Buffer );
|
|
|
|
//
|
|
// Copy the buffer into out our path and break from the loop.
|
|
//
|
|
//
|
|
// NTRAID#NTBUG9-545988-2002/02/26-brucegr,jcohen - Buffer overrun
|
|
//
|
|
memset( NTPath, 0, cNTPathLen * sizeof(WCHAR) );
|
|
wcsncpy( NTPath, LinkTarget.Buffer, cNTPathLen - 1 );
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Query next object in \DosDevices directory
|
|
//
|
|
Status = NtQueryDirectoryObject( DosDevicesDir,
|
|
DirInfo,
|
|
sizeof(DirInfoBuffer),
|
|
TRUE,
|
|
FALSE,
|
|
&Context,
|
|
&Length );
|
|
}
|
|
|
|
NtClose(DosDevicesDir);
|
|
|
|
return( STATUS_SUCCESS );
|
|
}
|
|
|
|
|
|
// If there are any problems with the Japanese build see nt\base\fs\utils\ulib\src\basesys.cxx, around line 150
|
|
// to see what they did.
|
|
|
|
BOOL LoadStringResource(
|
|
PUNICODE_STRING pUnicodeString,
|
|
INT MsgId
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is a simple implementation of LoadString().
|
|
|
|
Arguments:
|
|
|
|
usString - Returns the resource string.
|
|
MsgId - Supplies the message id of the resource string.
|
|
|
|
Return Value:
|
|
|
|
FALSE - Failure.
|
|
TRUE - Success.
|
|
|
|
--*/
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
PMESSAGE_RESOURCE_ENTRY MessageEntry;
|
|
ANSI_STRING AnsiString;
|
|
|
|
Status = RtlFindMessage( NtCurrentPeb()->ImageBaseAddress,
|
|
(ULONG_PTR) RT_MESSAGETABLE,
|
|
0,
|
|
(ULONG)MsgId,
|
|
&MessageEntry
|
|
);
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (!(MessageEntry->Flags & MESSAGE_RESOURCE_UNICODE)) {
|
|
RtlInitAnsiString( &AnsiString, (PCSZ)&MessageEntry->Text[ 0 ] );
|
|
Status = RtlAnsiStringToUnicodeString( pUnicodeString, &AnsiString, TRUE );
|
|
if (!NT_SUCCESS( Status )) {
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
//
|
|
// ISSUE-2002/02/26-brucegr,jcohen - Doesn't check return code from RtlCreateUnicodeString
|
|
//
|
|
RtlCreateUnicodeString(pUnicodeString, (PWSTR)MessageEntry->Text);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|