/*++

Copyright (c) 1991  Microsoft Corporation

Module Name:

    rtdmpsec.c

Abstract:

    NT level registry security test program #1, basic non-error paths.

    Dump out the security descriptors of a sub-tree of the registry.

    rtdmpsec <KeyPath>

    Will ennumerate and dump out the subkeys and values of KeyPath,
    and then apply itself recursively to each subkey it finds.

    It assumes data values are null terminated strings.

    Example:

        rtdmpsec \REGISTRY\MACHINE\TEST\bigkey

Author:

    John Vert (jvert) 24-Jan-92

        based on rtdmp.c by

    Bryan Willman (bryanwi)  10-Dec-91

        and getdacl.c by RobertRe

Revision History:

    Richard Ward (richardw)  14 April 1992   Changed ACE_HEADER

--*/

#include "cmp.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define WORK_SIZE   1024

//
//  Get a pointer to the first ace in an acl
//

#define FirstAce(Acl) ((PVOID)((PUCHAR)(Acl) + sizeof(ACL)))

//
//  Get a pointer to the following ace
//

#define NextAce(Ace) ((PVOID)((PUCHAR)(Ace) + ((PACE_HEADER)(Ace))->AceSize))

//
// Generic ACE structure, to be used for casting ACE's of known types
//

typedef struct _KNOWN_ACE {
   ACE_HEADER Header;
   ACCESS_MASK Mask;
   ULONG SidStart;
   } KNOWN_ACE, *PKNOWN_ACE;



VOID
InitVars();

VOID
PrintAcl (
    IN PACL Acl
    );

VOID
PrintAccessMask(
    IN ACCESS_MASK AccessMask
    );

void __cdecl main(int, char *);
void processargs();

void print(PUNICODE_STRING);

void
DumpSecurity(
    HANDLE  Handle
    );

void
Dump(
    HANDLE  Handle
    );

UNICODE_STRING  WorkName;
WCHAR           workbuffer[WORK_SIZE];

//
// Universal well known SIDs
//

PSID  NullSid;
PSID  WorldSid;
PSID  LocalSid;
PSID  CreatorOwnerSid;

//
// Sids defined by NT
//

PSID NtAuthoritySid;

PSID DialupSid;
PSID NetworkSid;
PSID BatchSid;
PSID InteractiveSid;
PSID LocalSystemSid;

void
__cdecl main(
    int argc,
    char *argv[]
    )
{
    NTSTATUS status;
    OBJECT_ATTRIBUTES ObjectAttributes;
    HANDLE          BaseHandle;

    InitVars();

    //
    // Process args
    //

    WorkName.MaximumLength = WORK_SIZE;
    WorkName.Length = 0L;
    WorkName.Buffer = &(workbuffer[0]);

    processargs(argc, argv);


    //
    // Set up and open KeyPath
    //

    printf("rtdmpsec: starting\n");

    InitializeObjectAttributes(
        &ObjectAttributes,
        &WorkName,
        0,
        (HANDLE)NULL,
        NULL
        );
    ObjectAttributes.Attributes |= OBJ_CASE_INSENSITIVE;

    status = NtOpenKey(
                &BaseHandle,
                MAXIMUM_ALLOWED,
                &ObjectAttributes
                );
    if (!NT_SUCCESS(status)) {
        printf("rtdmpsec: t0: %08lx\n", status);
        exit(1);
    }

    Dump(BaseHandle);
}


void
Dump(
    HANDLE  Handle
    )
{
    NTSTATUS    status;
    PKEY_BASIC_INFORMATION KeyInformation;
    OBJECT_ATTRIBUTES ObjectAttributes;
    ULONG   NamePos;
    ULONG   index;
    STRING  enumname;
    HANDLE  WorkHandle;
    ULONG   ResultLength;
    static  char buffer[WORK_SIZE];
    PUCHAR  p;

    KeyInformation = (PKEY_BASIC_INFORMATION)buffer;
    NamePos = WorkName.Length;

    //
    // Print name of node we are about to dump out
    //
    printf("\n");
    print(&WorkName);
    printf("::\n");

    //
    // Print out node's values
    //
    DumpSecurity(Handle);

    //
    // Enumerate node's children and apply ourselves to each one
    //

    for (index = 0; TRUE; index++) {

        RtlZeroMemory(KeyInformation, WORK_SIZE);
        status = NtEnumerateKey(
                    Handle,
                    index,
                    KeyBasicInformation,
                    KeyInformation,
                    WORK_SIZE,
                    &ResultLength
                    );

        if (status == STATUS_NO_MORE_ENTRIES) {

            WorkName.Length = NamePos;
            return;

        } else if (!NT_SUCCESS(status)) {

            printf("rtdmpsec: dump1: status = %08lx\n", status);
            exit(1);

        }

        enumname.Buffer = &(KeyInformation->Name[0]);
        enumname.Length = KeyInformation->NameLength;
        enumname.MaximumLength = KeyInformation->NameLength;

        p = WorkName.Buffer;
        p += WorkName.Length;
        *p = '\\';
        p++;
        *p = '\0';
        WorkName.Length += 2;

        RtlAppendStringToString((PSTRING)&WorkName, (PSTRING)&enumname);

        InitializeObjectAttributes(
            &ObjectAttributes,
            &enumname,
            0,
            Handle,
            NULL
            );
        ObjectAttributes.Attributes |= OBJ_CASE_INSENSITIVE;

        status = NtOpenKey(
                    &WorkHandle,
                    MAXIMUM_ALLOWED,
                    &ObjectAttributes
                    );
        if (!NT_SUCCESS(status)) {
            if (status == STATUS_ACCESS_DENIED) {
                printf("\n");
                print(&WorkName);
                printf("::\n\tAccess denied!\n");
            } else {
                printf("rtdmpsec: dump2: %08lx\n", status);
                exit(1);
            }
        } else {
            Dump(WorkHandle);
            NtClose(WorkHandle);
        }

        WorkName.Length = NamePos;
    }
}


void
DumpSecurity(
    HANDLE  Handle
    )
{
    PSECURITY_DESCRIPTOR SecurityDescriptor;
    NTSTATUS Status;
    ULONG Length;
    PACL Dacl;
    BOOLEAN DaclPresent;
    BOOLEAN DaclDefaulted;

    Status = NtQuerySecurityObject( Handle,
                                    DACL_SECURITY_INFORMATION,
                                    NULL,
                                    0,
                                    &Length );

    if (Status != STATUS_BUFFER_TOO_SMALL) {
        printf("DumpSecurity t0: NtQuerySecurityObject failed %lx\n",Status);
        exit(1);
    }

    SecurityDescriptor = malloc(Length);
    if (SecurityDescriptor == NULL) {
        printf("DumpSecurity: couldn't malloc buffer\n");
        exit(1);
    }

    Status = NtQuerySecurityObject( Handle,
                                    DACL_SECURITY_INFORMATION,
                                    SecurityDescriptor,
                                    Length,
                                    &Length );

    if (!NT_SUCCESS(Status)) {
        printf("DumpSecurity t1: NtQuerySecurityObject failed %lx\n",Status);
        exit(1);
    }

    Dacl = NULL;

    Status = RtlGetDaclSecurityDescriptor( SecurityDescriptor,
                                           &DaclPresent,
                                           &Dacl,
                                           &DaclDefaulted );
    if (!NT_SUCCESS(Status)) {
        printf("DumpSecurity t2: RtlGetDaclSecurityDescriptor failed %lx\n",Status);
    }

    if (DaclPresent) {
        PrintAcl(Dacl);
    } else {
        printf("\tAcl not present\n");
    }

}


void
print(
    PUNICODE_STRING  String
    )
{
    static  ANSI_STRING temp;
    static  char        tempbuffer[WORK_SIZE];

    temp.MaximumLength = WORK_SIZE;
    temp.Length = 0L;
    temp.Buffer = tempbuffer;

    RtlUnicodeStringToAnsiString(&temp, String, FALSE);
    printf("%s", temp.Buffer);
    return;
}


void
processargs(
    int argc,
    char *argv[]
    )
{
    ANSI_STRING temp;

    if ( (argc != 2) )
    {
        printf("Usage: %s <KeyPath>\n",
                argv[0]);
        exit(1);
    }

    RtlInitAnsiString(
        &temp,
        argv[1]
        );

    RtlAnsiStringToUnicodeString(
        &WorkName,
        &temp,
        FALSE
        );

    return;
}


BOOLEAN
SidTranslation(
    PSID Sid,
    PSTRING AccountName
    )
// AccountName is expected to have a large maximum length

{
    if (RtlEqualSid(Sid, WorldSid)) {
        RtlInitString( AccountName, "WORLD");
        return(TRUE);
    }

    if (RtlEqualSid(Sid, LocalSid)) {
        RtlInitString( AccountName, "LOCAL");

        return(TRUE);
    }

    if (RtlEqualSid(Sid, NetworkSid)) {
        RtlInitString( AccountName, "NETWORK");

        return(TRUE);
    }

    if (RtlEqualSid(Sid, BatchSid)) {
        RtlInitString( AccountName, "BATCH");

        return(TRUE);
    }

    if (RtlEqualSid(Sid, InteractiveSid)) {
        RtlInitString( AccountName, "INTERACTIVE");
        return(TRUE);
    }

    if (RtlEqualSid(Sid, LocalSystemSid)) {
        RtlInitString( AccountName, "SYSTEM");
        return(TRUE);
    }

//
//    if (RtlEqualSid(Sid, LocalManagerSid)) {
//      RtlInitString( AccountName, "LOCAL MANAGER");
//      return(TRUE);
//  }

//  if (RtlEqualSid(Sid, LocalAdminSid)) {
//      RtlInitString( AccountName, "LOCAL ADMIN");
//      return(TRUE);
//  }

    return(FALSE);

}


VOID
DisplayAccountSid(
    PSID Sid
    )
{
    UCHAR Buffer[128];
    STRING AccountName;
    UCHAR i;
    ULONG Tmp;
    PSID_IDENTIFIER_AUTHORITY IdentifierAuthority;
    UCHAR SubAuthorityCount;

    Buffer[0] = 0;

    AccountName.MaximumLength = 127;
    AccountName.Length = 0;
    AccountName.Buffer = (PVOID)&Buffer[0];



    if (SidTranslation( (PSID)Sid, &AccountName) ) {

        printf("%s\n", AccountName.Buffer );

    } else {
        IdentifierAuthority = RtlIdentifierAuthoritySid(Sid);

        //
        // HACK! HACK!
        // The next line prints the revision of the SID.  Since there is no
        // rtl routine which gives us the SID revision, we must make due.
        // luckily, the revision field is the first field in the SID, so we
        // can just cast the pointer.
        //

        printf("S-%u-", (USHORT) *((PUCHAR) Sid) );

        if (  (IdentifierAuthority->Value[0] != 0)  ||
              (IdentifierAuthority->Value[1] != 0)     ){
            printf("0x%02hx%02hx%02hx%02hx%02hx%02hx",
                        IdentifierAuthority->Value[0],
                        IdentifierAuthority->Value[1],
                        IdentifierAuthority->Value[2],
                        IdentifierAuthority->Value[3],
                        IdentifierAuthority->Value[4],
                        IdentifierAuthority->Value[5] );
        } else {
            Tmp = IdentifierAuthority->Value[5]          +
                  (IdentifierAuthority->Value[4] <<  8)  +
                  (IdentifierAuthority->Value[3] << 16)  +
                  (IdentifierAuthority->Value[2] << 24);
            printf("%lu", Tmp);
        }

        SubAuthorityCount = *RtlSubAuthorityCountSid(Sid);
        for (i=0;i<SubAuthorityCount ;i++ ) {
            printf("-%lu", (*RtlSubAuthoritySid(Sid, i)));
        }
        printf("\n");

    }

}

VOID
InitVars()
{
    ULONG SidWithZeroSubAuthorities;
    ULONG SidWithOneSubAuthority;
    ULONG SidWithThreeSubAuthorities;
    ULONG SidWithFourSubAuthorities;

    SID_IDENTIFIER_AUTHORITY NullSidAuthority    = SECURITY_NULL_SID_AUTHORITY;
    SID_IDENTIFIER_AUTHORITY WorldSidAuthority   = SECURITY_WORLD_SID_AUTHORITY;
    SID_IDENTIFIER_AUTHORITY LocalSidAuthority   = SECURITY_LOCAL_SID_AUTHORITY;
    SID_IDENTIFIER_AUTHORITY CreatorSidAuthority = SECURITY_CREATOR_SID_AUTHORITY;

    SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;


    //
    //  The following SID sizes need to be allocated
    //

    SidWithZeroSubAuthorities  = RtlLengthRequiredSid( 0 );
    SidWithOneSubAuthority     = RtlLengthRequiredSid( 1 );
    SidWithThreeSubAuthorities = RtlLengthRequiredSid( 3 );
    SidWithFourSubAuthorities  = RtlLengthRequiredSid( 4 );

    //
    //  Allocate and initialize the universal SIDs
    //

    NullSid         = (PSID)malloc(SidWithOneSubAuthority);
    WorldSid        = (PSID)malloc(SidWithOneSubAuthority);
    LocalSid        = (PSID)malloc(SidWithOneSubAuthority);
    CreatorOwnerSid = (PSID)malloc(SidWithOneSubAuthority);

    RtlInitializeSid( NullSid,    &NullSidAuthority, 1 );
    RtlInitializeSid( WorldSid,   &WorldSidAuthority, 1 );
    RtlInitializeSid( LocalSid,   &LocalSidAuthority, 1 );
    RtlInitializeSid( CreatorOwnerSid, &CreatorSidAuthority, 1 );

    *(RtlSubAuthoritySid( NullSid, 0 ))         = SECURITY_NULL_RID;
    *(RtlSubAuthoritySid( WorldSid, 0 ))        = SECURITY_WORLD_RID;
    *(RtlSubAuthoritySid( LocalSid, 0 ))        = SECURITY_LOCAL_RID;
    *(RtlSubAuthoritySid( CreatorOwnerSid, 0 )) = SECURITY_CREATOR_OWNER_RID;

    //
    // Allocate and initialize the NT defined SIDs
    //

    NtAuthoritySid  = (PSID)malloc(SidWithZeroSubAuthorities);
    DialupSid       = (PSID)malloc(SidWithOneSubAuthority);
    NetworkSid      = (PSID)malloc(SidWithOneSubAuthority);
    BatchSid        = (PSID)malloc(SidWithOneSubAuthority);
    InteractiveSid  = (PSID)malloc(SidWithOneSubAuthority);
    LocalSystemSid  = (PSID)malloc(SidWithOneSubAuthority);

    RtlInitializeSid( NtAuthoritySid,   &NtAuthority, 0 );
    RtlInitializeSid( DialupSid,        &NtAuthority, 1 );
    RtlInitializeSid( NetworkSid,       &NtAuthority, 1 );
    RtlInitializeSid( BatchSid,         &NtAuthority, 1 );
    RtlInitializeSid( InteractiveSid,   &NtAuthority, 1 );
    RtlInitializeSid( LocalSystemSid,   &NtAuthority, 1 );

    *(RtlSubAuthoritySid( DialupSid,       0 )) = SECURITY_DIALUP_RID;
    *(RtlSubAuthoritySid( NetworkSid,      0 )) = SECURITY_NETWORK_RID;
    *(RtlSubAuthoritySid( BatchSid,        0 )) = SECURITY_BATCH_RID;
    *(RtlSubAuthoritySid( InteractiveSid,  0 )) = SECURITY_INTERACTIVE_RID;
    *(RtlSubAuthoritySid( LocalSystemSid,  0 )) = SECURITY_LOCAL_SYSTEM_RID;

    return;

}



VOID
PrintAcl (
    IN PACL Acl
    )

/*++

Routine Description:

    This routine dumps an Acl for debug purposes (via printf).  It is
    specialized to dump standard aces.

Arguments:

    Acl - Supplies the Acl to dump

Return Value:

    None

--*/


{
    ULONG i;
    PKNOWN_ACE Ace;
    BOOLEAN KnownType;
    PCHAR AceTypes[] = { "Access Allowed",
                         "Access Denied ",
                         "System Audit  ",
                         "System Alarm  "
                       };

    if (Acl == NULL) {

        printf("\tAcl == ALL ACCESS GRANTED!\n");
        return;

    }

    //
    //  Dump the Acl header
    //

    printf("\tRevision: %02x", Acl->AclRevision);
    printf(" Size: %04x", Acl->AclSize);
    printf(" AceCount: %04x\n", Acl->AceCount);

    //
    //  Now for each Ace we want do dump it
    //

    for (i = 0, Ace = FirstAce(Acl);
         i < Acl->AceCount;
         i++, Ace = NextAce(Ace) ) {

        //
        //  print out the ace header
        //

        printf("\n\tAceHeader: %08lx ", *(PULONG)Ace);

        //
        //  special case on the standard ace types
        //

        if ((Ace->Header.AceType == ACCESS_ALLOWED_ACE_TYPE) ||
            (Ace->Header.AceType == ACCESS_DENIED_ACE_TYPE) ||
            (Ace->Header.AceType == SYSTEM_AUDIT_ACE_TYPE) ||
            (Ace->Header.AceType == SYSTEM_ALARM_ACE_TYPE)) {

            //
            //  The following array is indexed by ace types and must
            //  follow the allowed, denied, audit, alarm seqeuence
            //

            PCHAR AceTypes[] = { "Access Allowed",
                                 "Access Denied ",
                                 "System Audit  ",
                                 "System Alarm  "
                               };

            printf(AceTypes[Ace->Header.AceType]);
            PrintAccessMask(Ace->Mask);
            KnownType = TRUE;

        } else {

            KnownType = FALSE;
            printf(" Unknown Ace Type\n");

        }

        printf("\n");

        printf("\tAceSize = %d\n",Ace->Header.AceSize);

        printf("\tAce Flags = ");
        if (Ace->Header.AceFlags & OBJECT_INHERIT_ACE) {
            printf("OBJECT_INHERIT_ACE\n");
            printf("                   ");
        }

        if (Ace->Header.AceFlags & CONTAINER_INHERIT_ACE) {
            printf("CONTAINER_INHERIT_ACE\n");
            printf("                   ");
        }

        if (Ace->Header.AceFlags & NO_PROPAGATE_INHERIT_ACE) {
            printf("NO_PROPAGATE_INHERIT_ACE\n");
            printf("                   ");
        }

        if (Ace->Header.AceFlags & INHERIT_ONLY_ACE) {
            printf("INHERIT_ONLY_ACE\n");
            printf("                   ");
        }

        if (Ace->Header.AceFlags & SUCCESSFUL_ACCESS_ACE_FLAG) {
            printf("SUCCESSFUL_ACCESS_ACE_FLAG\n");
            printf("            ");
        }

        if (Ace->Header.AceFlags & FAILED_ACCESS_ACE_FLAG) {
            printf("FAILED_ACCESS_ACE_FLAG\n");
            printf("            ");
        }

        printf("\n");

        printf("\tSid = ");
        DisplayAccountSid(&Ace->SidStart);
    }

}


VOID
PrintAccessMask(
    IN ACCESS_MASK AccessMask
    )
{
    printf("\n\tAccess Mask: ");

    if (AccessMask == KEY_ALL_ACCESS) {
        printf("KEY_ALL_ACCESS\n\t             ");
        return;
    }
    if (AccessMask == KEY_READ) {
        printf("KEY_READ\n\t             ");
        return;
    }
    if (AccessMask == KEY_WRITE) {
        printf("KEY_WRITE\n\t             ");
        return;
    }

    if (AccessMask & KEY_QUERY_VALUE) {
        printf("KEY_QUERY_VALUE\n\t             ");
    }
    if (AccessMask & KEY_SET_VALUE) {
        printf("KEY_SET_VALUE\n\t             ");
    }
    if (AccessMask & KEY_CREATE_SUB_KEY) {
        printf("KEY_CREATE_SUB_KEY\n\t             ");
    }
    if (AccessMask & KEY_ENUMERATE_SUB_KEYS) {
        printf("KEY_ENUMERATE_SUB_KEYS\n\t             ");
    }
    if (AccessMask & KEY_NOTIFY) {
        printf("KEY_NOTIFY\n\t             ");
    }
    if (AccessMask & KEY_CREATE_LINK) {
        printf("KEY_CREATE_LINK\n\t             ");
    }
    if (AccessMask & GENERIC_ALL) {
        printf("GENERIC_ALL\n\t             ");
    }
    if (AccessMask & GENERIC_EXECUTE) {
        printf("GENERIC_EXECUTE\n\t             ");
    }
    if (AccessMask & GENERIC_WRITE) {
        printf("GENERIC_WRITE\n\t             ");
    }
    if (AccessMask & GENERIC_READ) {
        printf("GENERIC_READ\n\t             ");
    }
    if (AccessMask & GENERIC_READ) {
        printf("GENERIC_READ\n\t             ");
    }
    if (AccessMask & MAXIMUM_ALLOWED) {
        printf("MAXIMUM_ALLOWED\n\t             ");
    }
    if (AccessMask & ACCESS_SYSTEM_SECURITY) {
        printf("ACCESS_SYSTEM_SECURITY\n\t             ");
    }
    if (AccessMask & WRITE_OWNER) {
        printf("WRITE_OWNER\n\t             ");
    }
    if (AccessMask & WRITE_DAC) {
        printf("WRITE_DAC\n\t             ");
    }
    if (AccessMask & READ_CONTROL) {
        printf("READ_CONTROL\n\t             ");
    }
    if (AccessMask & DELETE) {
        printf("DELETE\n\t             ");
    }
}