/*++

Copyright (c) 1991  Microsoft Corporation

Module Name:

    ctreg.c

Abstract:

    Configuration Registry component test

    Needs to move from here

Author:

    Scott Birrell       (ScottBi)    June 5, 1991

Environment:

Revision History:

--*/


#include <string.h>
#include <nt.h>
#include <ntrtl.h>


#define CT_REG_INITIAL_KEY_COUNT 8L
#define CT_REG_INITIAL_LEVEL_COUNT 4L
#define CT_REG_KEY_VALUE_MAX_LENGTH 0x00000100L

//
// List of initial Registry keys to be set up.  The list must be
// kept so that moving linearly through it visits key nodes with top
// to bottom key traversal taking precedence over left-to-right.
//

typedef struct _CT_TEST_REGISTRY_KEY {
    ULONG KeyLevel;
    PUCHAR KeyName;
    ULONG KeyValueType;
    PUCHAR KeyValue;
    ULONG KeyValueLengthToQuery;
    ULONG KeyValueLengthToSet;
    NTSTATUS ExpectedStatus;
    HANDLE KeyHandle;
    HANDLE ParentKeyHandle;
} CT_TEST_REGISTRY_KEY, *PCT_TEST_REGISTRY_KEY;

CT_TEST_REGISTRY_KEY RegistryKeys[ CT_REG_INITIAL_KEY_COUNT ];

UCHAR KeyValue[CT_REG_KEY_VALUE_MAX_LENGTH];
ULONG KeyValueLengthToQuery;
ULONG KeyValueType;
LARGE_INTEGER LastWriteTime;

HANDLE ParentKeyHandle[CT_REG_INITIAL_LEVEL_COUNT + 1] =
   { NULL, NULL, NULL, NULL, NULL };

VOID
InitTestKey(
    IN ULONG KeyNumber,
    IN ULONG KeyLevel,
    IN PUCHAR KeyName,
    IN ULONG KeyNameLength,
    IN ULONG KeyValueType,
    IN PUCHAR KeyValue,
    IN ULONG KeyValueLengthToQuery,
    IN ULONG KeyValueLengthToSet,
    IN NTSTATUS ExpectedStatus
    );


VOID
CtRegExamineResult(
    IN ULONG KeyNumber,
    IN ULONG KeyValueType,
    IN PUCHAR KeyValue,
    IN ULONG KeyValueLength,
    IN NTSTATUS ReturnedStatus
    );


VOID
CtCreateSetQueryKey();

VOID
CtOpenMakeTempCloseKey();


VOID
main ()
{
    CtCreateSetQueryKey();

    CtOpenMakeTempCloseKey();

}




VOID
CtCreateSetQueryKey(
    )

/*++

Routine Description:

    This function tests the RtlpNtOpenKey RtlpNtCreateKey and NtClose API.

Arguments:

    None.

Return Value:

    None.

--*/

{
    NTSTATUS Status;

    //
    // Set up a test hierarchy of keys
    //

    ULONG KeyNumber;
    ULONG ZeroLength;
    STRING Name;
    UNICODE_STRING UnicodeName;
    OBJECT_ATTRIBUTES ObjectAttributes;

    ZeroLength = 0L;

    DbgPrint("Start of Create, Set, Query Registry Key Test\n");

    //
    // Initialize the test array.  I did it this way because a statically
    // initialized table is harder to read.
    //

    InitTestKey(
        0,
        0,
        "\\Registry\\RLM",
        sizeof ("\\Registry\\RLM"),
        0,
        "Level 0 - RLM",
        sizeof ("\\Registry\\RLM") - 1,
        sizeof ("\\Registry\\RLM") - 1,
        STATUS_SUCCESS
        );

    InitTestKey(
        1,
        1,
        "Test",
        sizeof ("Test"),
        1,
        "Keyname Test - this value too big",
        6,
        sizeof ("Keyname Test - this value too big") -1,
        STATUS_BUFFER_OVERFLOW
        );

    InitTestKey(
        2,
        2,
        "SubTestA",
        sizeof("SubTestA"),
        2,
        "Keyname SubTestA - value small",
        30,
        sizeof ("Keyname SubTestA - value small") - 1,
        STATUS_SUCCESS
        );


    InitTestKey(
        3,
        3,
        "SSTestA",
        sizeof("SSTestA"),
        3,
        "Keyname SSTestA - zero length buffer",
        30,
        sizeof("Keyname SSTestA - zero length buffer") - 1,
        STATUS_BUFFER_OVERFLOW
        );

    InitTestKey(
        4,
        3,
        "SSTestB",
        sizeof("SSTestB"),
        4,
        "Keyname SSTestB - value exact fit",
        sizeof ("Keyname SSTestB - value exact fit")  -1,
        sizeof ("Keyname SSTestB - value exact fit")  -1,
        STATUS_SUCCESS
        );

    InitTestKey(
        5,
        3,
        "SSTestC",
        sizeof("SSTestC"),
        5,
        "Keyname SSTestC - value small",
        40,
        sizeof ("Keyname SSTestC - value small")  -1,
        STATUS_SUCCESS
        );

    InitTestKey(
        6,
        3,
        "SSTestD",
        sizeof("SSTestD"),
        6,
        "Keyname SSTestD - value small",
        40,
        sizeof ("Keyname SSTestD - value small")  -1,
        STATUS_SUCCESS
        );

    InitTestKey(
        7,
        3,
        "SSTestE",
        sizeof("SSTestD"),
        0,
        "Keyname SSTestD - no value set",
        40,
        0,
        STATUS_SUCCESS
        );

    DbgPrint("Start of Registry Test\n");

    //
    // Create all of the initial test registry keys
    //


    for (KeyNumber = 0; KeyNumber < CT_REG_INITIAL_KEY_COUNT; KeyNumber++) {

        RtlInitString(
            &Name,
            RegistryKeys[KeyNumber].KeyName
            );

        Status = RtlAnsiStringToUnicodeString(
                     &UnicodeName,
                     &Name,
                     TRUE );

        if (!NT_SUCCESS(Status)) {
            DbgPrint("Security: Registry Init Ansi to Unicode failed 0x%lx\n",
                Status);
            return;
        }

        InitializeObjectAttributes(
            &ObjectAttributes,
            &UnicodeName,
            OBJ_CASE_INSENSITIVE,
            ParentKeyHandle[RegistryKeys[KeyNumber].KeyLevel],
            NULL
            );

        //
        // Remember the Parent Key handle
        //

        RegistryKeys[KeyNumber].ParentKeyHandle =
            ParentKeyHandle[RegistryKeys[KeyNumber].KeyLevel];

        //
        // Create the key if it does not already exist.  If key does exist,
        // continue trying to create all of the other keys needed (for now).
        // Store the returned key handle as the parent key handle for the
        // next higher (child) level.
        //

        Status = RtlpNtCreateKey(
                     &RegistryKeys[KeyNumber].KeyHandle,
                     (KEY_READ | KEY_WRITE),
                     &ObjectAttributes,
                     0L,			// No options
                     NULL, 			// Default provider
                     NULL
                     );

        //
        // Save the Key's handle as the next level's parent handle.
        //

        ParentKeyHandle[RegistryKeys[KeyNumber].KeyLevel + 1] =
            RegistryKeys[KeyNumber].KeyHandle;

        //
        // Free the memory allocated for the Unicode name
        //

        RtlFreeUnicodeString( &UnicodeName );

        if (NT_SUCCESS(Status) || Status == STATUS_OBJECT_NAME_COLLISION) {

            //
            // Set the key's value unless the length to set is zero.
            //

            if (RegistryKeys[KeyNumber].KeyValueLengthToSet != 0) {

                Status=RtlpNtSetValueKey(
                           ParentKeyHandle[RegistryKeys[KeyNumber].KeyLevel+1],
                           RegistryKeys[KeyNumber].KeyValueType,
                           RegistryKeys[KeyNumber].KeyValue,
                           RegistryKeys[KeyNumber].KeyValueLengthToSet
                           );
            }

            //
            // Read the key's value back
            //

            KeyValueLengthToQuery =
                RegistryKeys[KeyNumber].KeyValueLengthToQuery;

            Status = RtlpNtQueryValueKey(
                         ParentKeyHandle[RegistryKeys[KeyNumber].KeyLevel + 1],
                         &KeyValueType,
                         KeyValue,
                         &KeyValueLengthToQuery,
                         &LastWriteTime
                         );

            //
            // Verify that the expected KeyValue, KeyLength and Status
            // were returned.
            //

            CtRegExamineResult(
                KeyNumber,
                KeyValueType,
                KeyValue,
                KeyValueLengthToQuery,
                Status
                );

            //
            // Test null pointer cases don't crash NtQueryValueKey
            //

            Status = RtlpNtQueryValueKey(
                         ParentKeyHandle[RegistryKeys[KeyNumber].KeyLevel + 1],
                         NULL,   // No type
                         KeyValue,
                         &KeyValueLengthToQuery,
                         &LastWriteTime
                         );

            Status = RtlpNtQueryValueKey(
                         ParentKeyHandle[RegistryKeys[KeyNumber].KeyLevel + 1],
                         &KeyValueType,
                         NULL,   // No value
                         &KeyValueLengthToQuery,
                         &LastWriteTime
                         );

            Status = RtlpNtQueryValueKey(
                         ParentKeyHandle[RegistryKeys[KeyNumber].KeyLevel + 1],
                         &KeyValueType,
                         KeyValue,
                         NULL,    // No length
                         &LastWriteTime
                         );

            Status = RtlpNtQueryValueKey(
                         ParentKeyHandle[RegistryKeys[KeyNumber].KeyLevel + 1],
                         &KeyValueType,
                         KeyValue,
                         &ZeroLength,  // Zero length
                         &LastWriteTime
                         );

            Status = RtlpNtQueryValueKey(
                         ParentKeyHandle[RegistryKeys[KeyNumber].KeyLevel + 1],
                         &KeyValueType,
                         KeyValue,
                         &KeyValueLengthToQuery,
                         NULL   // No time
                         );
            //
            // Test null pointer cases don't crash NtSetValueKey
            //

            Status = RtlpNtSetValueKey(
                         ParentKeyHandle[RegistryKeys[KeyNumber].KeyLevel+1],
                         RegistryKeys[KeyNumber].KeyValueType,
                         NULL,  // No value, setting type only
                         RegistryKeys[KeyNumber].KeyValueLengthToSet
                         );
        } else {

            DbgPrint(
                "Key number %d creation failed 0x%lx\n",
                KeyNumber,
                Status
                );
        }
    }

    //
    // Close all the keys in the table
    //

    for (KeyNumber = 0; KeyNumber < CT_REG_INITIAL_KEY_COUNT; KeyNumber++) {

        Status = NtClose(
                     RegistryKeys[KeyNumber].KeyHandle
                     );

        if (!NT_SUCCESS(Status)) {

            DbgPrint("Closing KeyNumber %d failed 0x%lx\n", Status);
        }
    }

    DbgPrint("End of Create, Set, Query Registry Key Test\n");
}


VOID
CtRegExamineResult(
    IN ULONG KeyNumber,
    IN ULONG KeyValueType,
    IN PUCHAR KeyValue,
    IN ULONG KeyValueLengthReturned,
    IN NTSTATUS ReturnedStatus
    )

{
    ULONG KeyValueLengthToCompare;

    //
    // Check the status.  If bad, skip the other checks.
    //

    if (ReturnedStatus != RegistryKeys[KeyNumber].ExpectedStatus) {

        DbgPrint(
            "KeyNumber %d: RtlpNtQueryValueKey returned 0x%lx, expected 0x%lx\n",
            KeyNumber,
            ReturnedStatus,
            RegistryKeys[KeyNumber].ExpectedStatus
            );

    } else {

        //
        // Check that the Key Type is as expected.
        //


        if (KeyValueType != RegistryKeys[KeyNumber].KeyValueType) {

            DbgPrint(
                "KeyNumber %d: RtlpNtQueryValueKey returned KeyValueType 0x%lx, \
                expected 0x%lx\n",
                KeyNumber,
                KeyValueType,
                RegistryKeys[KeyNumber].KeyValueType
                );

        }

        //
        // Check that the Key Length is as expected.
        //


        if (KeyValueLengthReturned !=
            RegistryKeys[KeyNumber].KeyValueLengthToSet) {

            DbgPrint(
                "KeyNumber %d: RtlpNtQueryValueKey returned ValLength 0x%lx, \
                expected 0x%lx\n",
                KeyNumber,
                KeyValueLengthReturned,
                RegistryKeys[KeyNumber].KeyValueLengthToSet
                );

        }

        //
        // Check that the Key Value is as expected.  Distinguish between
        // Buffer truncated cases and regular cases.
        //

        if (RegistryKeys[KeyNumber].KeyValueLengthToSet != 0L) {

            //
            // Determine the length of returned key value to compare.  This is
            // the min of the set length and and the size of the return
            // buffer.
            //

            KeyValueLengthToCompare =
                RegistryKeys[KeyNumber].KeyValueLengthToSet;

            if (KeyValueLengthToCompare >
                RegistryKeys[KeyNumber].KeyValueLengthToQuery) {

                KeyValueLengthToCompare =
                     RegistryKeys[KeyNumber].KeyValueLengthToQuery;
            }


            if (strncmp(
                    KeyValue,
                    RegistryKeys[KeyNumber].KeyValue,
                    KeyValueLengthToCompare
                    ) != 0) {

                //
                // Output approriate error message.  Message contains
                // "truncated.." if key value should have been truncated
                //

                if (RegistryKeys[KeyNumber].KeyValueLengthToSet >
                    RegistryKeys[KeyNumber].KeyValueLengthToQuery) {

                    DbgPrint(
                        "KeyNumber %d: RtlpNtQueryValueKey returned KeyValue %s, \
                        expected %s truncated to %d characters\n",
                        KeyNumber,
                        KeyValue,
                        RegistryKeys[KeyNumber].KeyValue,
                        RegistryKeys[KeyNumber].KeyValueLengthToQuery
                        );

                } else {

                    DbgPrint(
                        "KeyNumber %d: RtlpNtQueryValueKey returned KeyValue %s, \
                        expected %s\n",
                        KeyNumber,
                        KeyValue,
                        RegistryKeys[KeyNumber].KeyValue
                        );
                }
            }
        }
    }
}




VOID
CtOpenMakeTempCloseKey()

/*++

Routine Description:

    This function tests NtDeleteKey by deleting the CT configuration

Arguments:

    None.

Return Value:

    None

--*/

{
    ULONG KeyNumber;
    ULONG KeyLevel;
    STRING Name;
    UNICODE_STRING UnicodeName;
    NTSTATUS Status;
    OBJECT_ATTRIBUTES ObjectAttributes;

    //
    // Open all of the initial test registry keys for write and delete
    // access, set, query and delete each key.
    //

    DbgPrint("Start of Open Make Temp and Close Delete Registry Key Test\n");

    //
    // First, set all of the Parent handles to NULL
    //

    for (KeyNumber = 0; KeyNumber < CT_REG_INITIAL_KEY_COUNT; KeyNumber++) {

        RegistryKeys[KeyNumber].ParentKeyHandle = NULL;
    }

    for (KeyLevel = 0; KeyLevel < CT_REG_INITIAL_LEVEL_COUNT; KeyLevel++) {

        ParentKeyHandle[KeyLevel] = NULL;
    }

    for (KeyNumber = 0; KeyNumber < CT_REG_INITIAL_KEY_COUNT; KeyNumber++) {

        RtlInitString(
            &Name,
            RegistryKeys[KeyNumber].KeyName
            );

        Status = RtlAnsiStringToUnicodeString(
                     &UnicodeName,
                     &Name,
                     TRUE );

        if (!NT_SUCCESS(Status)) {
            DbgPrint("Security: Registry Init Ansi to Unicode failed 0x%lx\n",
                Status);
            return;
        }

        InitializeObjectAttributes(
            &ObjectAttributes,
            &UnicodeName,
            OBJ_CASE_INSENSITIVE,
            ParentKeyHandle[RegistryKeys[KeyNumber].KeyLevel],
            NULL
            );

        //
        // Open the key and store the returned key handle as the parent key
        // handle for the next higher (child) level.
        //

        Status = RtlpNtOpenKey(
                     &RegistryKeys[KeyNumber].KeyHandle,
                     (KEY_READ | KEY_WRITE | DELETE),
                     &ObjectAttributes,
                     0L   			// No options
                     );

        if (!NT_SUCCESS(Status)) {

            DbgPrint(
                "NtOpenKey - KeyNumber %d failed 0x%lx\n",
                KeyNumber,
                Status
                );
        }

        //
        // Save the Key's handle as the next level's parent handle.
        //

        ParentKeyHandle[RegistryKeys[KeyNumber].KeyLevel + 1] =
            RegistryKeys[KeyNumber].KeyHandle;

        //
        // Free the memory allocated for the Unicode name
        //

        RtlFreeUnicodeString( &UnicodeName );

        if (NT_SUCCESS(Status) || Status == STATUS_OBJECT_NAME_COLLISION) {

            //
            // Set the key's value unless the length to set is zero.
            //

            if (RegistryKeys[KeyNumber].KeyValueLengthToSet != 0) {

                Status=RtlpNtSetValueKey(
                           ParentKeyHandle[RegistryKeys[KeyNumber].KeyLevel+1],
                           RegistryKeys[KeyNumber].KeyValueType,
                           RegistryKeys[KeyNumber].KeyValue,
                           RegistryKeys[KeyNumber].KeyValueLengthToSet
                           );
            }

            //
            // Read the key's value back
            //

            KeyValueLengthToQuery =
                RegistryKeys[KeyNumber].KeyValueLengthToQuery;

            Status = RtlpNtQueryValueKey(
                         ParentKeyHandle[RegistryKeys[KeyNumber].KeyLevel + 1],
                         &KeyValueType,
                         KeyValue,
                         &KeyValueLengthToQuery,
                         &LastWriteTime
                         );

            //
            // Verify that the expected KeyValue, KeyLength and Status
            // were returned.
            //

            CtRegExamineResult(
                KeyNumber,
                KeyValueType,
                KeyValue,
                KeyValueLengthToQuery,
                Status
                );

        } else {

            DbgPrint(
                "Key number %d open failed 0x%lx\n",
                KeyNumber,
                Status
                );
        }
    }

    //
    // Make Temporary and Close all the keys in the table
    //

    for (KeyNumber = CT_REG_INITIAL_KEY_COUNT-1; KeyNumber != 0L; KeyNumber--) {

        Status = RtlpNtMakeTemporaryKey( RegistryKeys[KeyNumber].KeyHandle );

        if (!NT_SUCCESS(Status)) {

            DbgPrint(
                "Making Temporary KeyNumber %d failed 0x%lx\n",
                KeyNumber,
                Status
                );
        }

        Status = NtClose(
                     RegistryKeys[KeyNumber].KeyHandle
                     );

        if (!NT_SUCCESS(Status)) {

            DbgPrint(
                "Closing KeyNumber %d failed 0x%lx\n",
                KeyNumber,
                Status
                );
        }
    }

    DbgPrint("End of Open Mk Temp and Close Registry Key Test\n");
}


VOID
InitTestKey(
    IN ULONG KeyNumber,
    IN ULONG KeyLevel,
    IN PUCHAR KeyName,
    IN ULONG KeyNameLength,
    IN ULONG KeyValueType,
    IN PUCHAR KeyValue,
    IN ULONG KeyValueLengthToQuery,
    IN ULONG KeyValueLengthToSet,
    IN NTSTATUS ExpectedStatus
    )

/*++

Routine Description:

    This function initializes an entry in the array of test keys

Arguments:

    TBS.

Return Value:

--*/

{
     RegistryKeys[KeyNumber].KeyLevel = KeyLevel;
     RegistryKeys[KeyNumber].KeyName = KeyName;
     RegistryKeys[KeyNumber].KeyValueType = KeyValueType;
     RegistryKeys[KeyNumber].KeyValue = KeyValue;
     RegistryKeys[KeyNumber].KeyValueLengthToSet = KeyValueLengthToSet;
     RegistryKeys[KeyNumber].KeyValueLengthToQuery = KeyValueLengthToQuery;
     RegistryKeys[KeyNumber].ExpectedStatus = ExpectedStatus;
     RegistryKeys[KeyNumber].KeyHandle = NULL;
     RegistryKeys[KeyNumber].ParentKeyHandle = NULL;


     DBG_UNREFERENCED_PARAMETER (KeyNameLength);
}