/*++ 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 "); } }