/*++ Copyright (c) 1990 Microsoft Corporation Module Name: obdir.c Abstract: Utility to obtain a directory of Object Manager Directories for NT. Author: Darryl E. Havens (DarrylH) 9-Nov-1990 Revision History: --*/ #include #include #include #include #include #include #include #include #include #define BUFFERSIZE 1024 #define Error(N,S) { \ printf(#N); \ printf(" Error %08lX\n", S); \ } typedef struct _TYPEINFO { PWSTR pszName; char * * AccessRights; DWORD NumberRights; } TYPEINFO, * PTYPEINFO; //////////////////////////////////////////////////////// // // // Internal Prototypes // // // //////////////////////////////////////////////////////// BOOLEAN EnableAllPrivileges( VOID ); VOID QueryDirectory( IN PSTRING DirectoryName ); NTSTATUS OpenObject( IN HANDLE Root, IN PWCHAR Type, IN PWCHAR Name, IN ACCESS_MASK DesiredAccess, OUT PHANDLE Object ); VOID OpenAndDisplaySacl( IN HANDLE Root, IN PWCHAR Type, IN PWCHAR Name ); VOID QueryAndDisplaySacl( IN HANDLE Object, IN PWSTR Type ); NTSTATUS DisplaySacl( PSECURITY_DESCRIPTOR SD, IN PWSTR Type ); VOID OpenAndDisplayDacl( IN HANDLE Root, IN PWCHAR Type, IN PWCHAR Name ); VOID QueryAndDisplayDacl( IN HANDLE Object, IN PWSTR Type ); VOID QueryAndDisplayOwner( IN HANDLE Object, IN PWSTR Type ); NTSTATUS DisplayDacl( PSECURITY_DESCRIPTOR SD, IN PWSTR Type ); VOID DumpAce( PACE_HEADER Ace, BOOLEAN AclIsDacl, PTYPEINFO TypeInfo ); VOID DumpStandardAceInfo( PACE_HEADER Ace, BOOLEAN AclIsDacl, PTYPEINFO TypeInfo ); VOID DisplaySid( IN PSID Sid ); VOID ConnectToLsa( VOID ); VOID Usage( VOID ); //////////////////////////////////////////////////////// // // // Global Variables // // // //////////////////////////////////////////////////////// UCHAR Buffer[BUFFERSIZE]; LSA_HANDLE LsaHandle; BOOLEAN CompoundLineOutput = FALSE; DumpDacl = FALSE; // May be changed by command parameter DumpDaclFull = FALSE; // May be changed by command parameter DumpSacl = FALSE; // May be changed by command parameter DumpSaclFull = FALSE; // May be changed by command parameter DumpOwner = FALSE; // May be changed by command parameter char * AccessMask[] = { "Delete", "ReadControl", "WriteDac", "WriteOwner", "Synch", "", "", "", "Sacl", "MaxAllowed", "", "", "GenericAll", "GenericExec", "GenericWrite", "GenericRead"}; char * TokenRights[] = {"AssignPrimary", "Duplicate", "Impersonate", "Query", "QuerySource", "AdjustPriv", "AdjustGroup", "AdjustDef" }; char * KeyRights[] = { "QueryValue", "SetValue", "CreateSubKey", "EnumSubKey", "Notify", "CreateLink", "", "" }; char * EventRights[] = {"QueryState", "ModifyState" }; char * MutantRights[]={ "QueryState" }; char * SemaphoreRights[] = { "QueryState", "ModifyState" }; char * TimerRights[] = {"QueryState", "ModifyState" }; char * ProfileRights[]={"Control"}; char * ProcessRights[]={"Terminate", "CreateThread", "", "VMOp", "VMRead", "VMWrite", "DupHandle", "CreateProcess", "SetQuota", "SetInfo", "QueryInfo", "SetPort" }; char * ThreadRights[] ={"Terminate", "Suspend", "Alert", "GetContext", "SetContext", "SetInfo", "QueryInfo", "SetToken", "Impersonate", "DirectImpersonate" }; char * SectionRights[]={"Query", "MapWrite", "MapRead", "MapExecute", "Extend"}; char * FileRights[] = { "Read/List", "Write/Add", "Append/SubDir/CreatePipe", "ReadEA", "WriteEA", "Execute/Traverse", "DelChild", "ReadAttr", "WriteAttr"}; char * PortRights[] = { "Connect" }; char * DirRights[] = { "Query", "Traverse", "Create", "CreateSubdir" }; char * SymLinkRights[]={"Query" }; char * WinstaRights[]={ "EnumDesktops", "ReadAttr", "Clipboard", "CreateDesktop", "WriteAttr", "GlobalAtom", "ExitWindows", "", "Enumerate", "ReadScreen" }; char * DesktopRights[]={"ReadObjects", "CreateWindow", "CreateMenu", "HookControl", "JournalRecord", "JournalPlayback", "Enumerate", "WriteObjects", "SwitchDesktop" }; char * CompletionRights[] = { "Query", "Modify" }; char * ChannelRights[] = { "ReadMessage", "WriteMessage", "Query", "SetInfo" }; char * JobRights[] = { "AssignProcess", "SetAttr", "Query", "Terminate", "SetSecAttr" }; #define TYPE_NONE 0 #define TYPE_EVENT 1 #define TYPE_SECTION 2 #define TYPE_FILE 3 #define TYPE_PORT 4 #define TYPE_DIRECTORY 5 #define TYPE_LINK 6 #define TYPE_MUTANT 7 #define TYPE_WINSTA 8 #define TYPE_SEM 9 #define TYPE_KEY 10 #define TYPE_TOKEN 11 #define TYPE_PROCESS 12 #define TYPE_THREAD 13 #define TYPE_DESKTOP 14 #define TYPE_COMPLETE 15 #define TYPE_CHANNEL 16 #define TYPE_TIMER 17 #define TYPE_JOB 18 #define TYPE_WPORT 19 #define TYPE_MAX 20 TYPEINFO TypeNames[TYPE_MAX] = { { L"Unknown", NULL, 0 }, { L"Event", EventRights, 2 }, { L"Section", SectionRights, 5 }, { L"File", FileRights, 9 }, { L"Port", PortRights, 1 }, { L"Directory", DirRights, 4 }, { L"SymbolicLink", SymLinkRights, 1 }, { L"Mutant", MutantRights, 2 }, { L"WindowStation", WinstaRights, 10 }, { L"Semaphore", SemaphoreRights, 2 }, { L"Key", KeyRights, 6 }, { L"Token", TokenRights, 8 }, { L"Process", ProcessRights, 12 }, { L"Thread", ThreadRights, 10 }, { L"Desktop", DesktopRights, 10 }, { L"IoCompletion", CompletionRights, 2 }, { L"Channel", ChannelRights, 4 }, { L"Timer", TimerRights, 2 }, { L"Job", JobRights, 5 }, { L"WaitablePort", PortRights, 1 } }; DWORD GetObjectTypeIndex( PWSTR TypeName ) { DWORD i; for ( i = 1 ; i < TYPE_MAX ; i++ ) { if (_wcsicmp( TypeNames[i].pszName, TypeName ) == 0 ) { return( i ); } } return( 0 ); } VOID DisplayFlags( ULONG Flags, ULONG FlagLimit, char *flagset[], ULONG Indent, ULONG LineBreak, ULONG BufferSize, UCHAR * buffer) { char * offset; char * limit ; char * linelimit ; DWORD mask, test, i, flagsize ; DWORD scratch; if ( LineBreak > BufferSize ) { strncpy( (CHAR *)buffer, "Invalid Parameter", BufferSize); return; } mask = 0; offset = (CHAR *) buffer; test = 1; limit = offset + BufferSize ; if ( LineBreak ) { linelimit = offset + LineBreak ; } else { linelimit = limit ; } if ( linelimit > limit ) { linelimit = limit ; } memset(offset, ' ', Indent); offset += Indent ; if (!Flags) { strcpy( offset, "None"); return; } for ( i = 0 ; i < FlagLimit ; i++ ) { if ( ( Flags & test ) != 0 ) { // // Found a flag set in the flag word. Try to write the text // form into the buffer // flagsize = strlen( flagset[ i ] ); if ( offset + flagsize + 2 > limit ) { return; } if ( offset + flagsize + 2 > linelimit ) { // // Need to do a linebreak: // *offset++ = '\r'; *offset++ = '\n'; if ( LineBreak ) { linelimit = offset + LineBreak ; } else { linelimit = limit ; } if ( linelimit > limit ) { linelimit = limit ; } memset(offset, ' ', Indent); offset += Indent ; if ( offset + flagsize + 2 > linelimit ) { *offset++ = '\0'; return; } } CopyMemory( offset, flagset[ i ], flagsize ); offset += flagsize ; mask |= test; if ( ( Flags & (~mask) ) != 0 ) { *offset++ = ' ' ; } } test <<= 1 ; } *offset = '\0'; } //////////////////////////////////////////////////////// // // // Routines // // // //////////////////////////////////////////////////////// VOID __cdecl main( int argc, char *argv[] ) { STRING String; int arg; char *s; BOOLEAN DirectoryNameArg; // // process any qualifiers // // All arguments are considered qualifiers until we reach a backslash ("\"). // If we reach a backslash, then that argument is accepted as the last argument // and it is expected to the the name of the directory to be listed. // DirectoryNameArg = FALSE; arg = 1; while (arg < argc) { s = argv[arg]; if (*s == '\\') { DirectoryNameArg = TRUE; break; // break out of while loop } if (*s != '/') { Usage(); return; } s++; if ((*s == 'o') || (*s == 'O')) { DumpOwner = TRUE; CompoundLineOutput = TRUE; } else if (*s == 'd') { // // Dump DACL qualifier // if (DumpDaclFull == TRUE) { printf("\n\n Conflicting qualifiers: /d and /D\n"); Usage(); return; } DumpDacl = TRUE; DumpDaclFull = FALSE; CompoundLineOutput = TRUE; } else if (*s == 'D') { // // Dump DACL qualifier // if ((DumpDacl== TRUE) && (DumpDaclFull == FALSE)) { printf("\n\n Conflicting qualifiers: /d and /D\n"); Usage(); return; } DumpDacl = TRUE; DumpDaclFull = TRUE; CompoundLineOutput = TRUE; } else if (*s == 's') { // // Dump SACL qualifier // if (DumpSaclFull == TRUE) { printf("\n\n Conflicting qualifiers: /s and /S\n"); Usage(); return; } DumpSacl = TRUE; DumpSaclFull = FALSE; CompoundLineOutput = TRUE; } else if (*s == 'S') { // // Dump SACL qualifier // if ((DumpSacl== TRUE) && (DumpSaclFull == FALSE)) { printf("\n\n Conflicting qualifiers: /s and /S\n"); Usage(); return; } DumpSacl = TRUE; DumpSaclFull = TRUE; CompoundLineOutput = TRUE; } else { Usage(); return; } arg++; } // end_while if (DumpOwner || DumpDacl || DumpSacl) { ConnectToLsa(); } // // Set up the name of the directory to list // if (!DirectoryNameArg) { RtlInitString( &String, "\\" ); } else { RtlInitString( &String, argv[arg] ); } if (EnableAllPrivileges()) { QueryDirectory( &String ); } } WCHAR LinkTargetBuffer[ 1024 ]; typedef struct _DIR_ENTRY { PWSTR Name; PWSTR Type; } DIR_ENTRY, *PDIR_ENTRY; #define MAX_DIR_ENTRIES 1024 ULONG NumberOfDirEntries; DIR_ENTRY DirEntries[ MAX_DIR_ENTRIES ]; int __cdecl CompareDirEntry( void const *p1, void const *p2 ) { return _wcsicmp( ((PDIR_ENTRY)p1)->Name, ((PDIR_ENTRY)p2)->Name ); } VOID QueryDirectory( IN PSTRING DirectoryName ) // // DumpDacl and DumpSacl are expected to be set prior to calling this routine. // { NTSTATUS Status; HANDLE DirectoryHandle, LinkHandle; ULONG Context = 0; ULONG i, ReturnedLength; UNICODE_STRING LinkTarget; POBJECT_DIRECTORY_INFORMATION DirInfo; POBJECT_NAME_INFORMATION NameInfo; OBJECT_ATTRIBUTES Attributes; UNICODE_STRING UnicodeString; ACCESS_MASK ExtraAccess = 0; UNICODE_STRING Match = { 0 }; UNICODE_STRING Separators = { 0 }; USHORT Offset ; ULONG DisplayedEntries = 0 ; BOOLEAN PrefixMatch = FALSE ; BOOLEAN SuffixMatch = FALSE ; ULONG ObjectNameLength ; BOOL PrefixMatched, SuffixMatched ; // // Perform initial setup // RtlZeroMemory( Buffer, BUFFERSIZE ); if (DumpDacl | DumpOwner) { ExtraAccess |= READ_CONTROL; } if (DumpSacl) { ExtraAccess |= ACCESS_SYSTEM_SECURITY; } // // Open the directory for list directory access // Status = RtlAnsiStringToUnicodeString( &UnicodeString, DirectoryName, TRUE ); ASSERT( NT_SUCCESS( Status ) ); InitializeObjectAttributes( &Attributes, &UnicodeString, OBJ_CASE_INSENSITIVE, NULL, NULL ); Status = NtOpenDirectoryObject( &DirectoryHandle, DIRECTORY_QUERY | ExtraAccess, &Attributes ); if ( ( Status == STATUS_OBJECT_TYPE_MISMATCH ) || ( Status == STATUS_OBJECT_NAME_NOT_FOUND ) || ( Status == STATUS_OBJECT_PATH_NOT_FOUND ) ) { RtlInitUnicodeString( &Separators, L"\\" ); Status = RtlFindCharInUnicodeString( RTL_FIND_CHAR_IN_UNICODE_STRING_START_AT_END, &UnicodeString, &Separators, &Offset ); if ( NT_SUCCESS( Status ) ) { UnicodeString.Length = Offset ; RtlInitUnicodeString( &Match, UnicodeString.Buffer + ((Offset + 2) / sizeof( WCHAR ) ) ); if ( Match.Buffer[ 0 ] == L'*' ) { Match.Buffer++ ; Match.Length -= sizeof( WCHAR ); SuffixMatch = TRUE ; } if ( Match.Buffer[ Match.Length / sizeof( WCHAR ) - 1 ] == L'*' ) { Match.Buffer[ Match.Length / sizeof( WCHAR ) - 1 ] = L'\0'; Match.Length -= sizeof( WCHAR ); PrefixMatch = TRUE ; } if ( PrefixMatch && SuffixMatch ) { printf("Too complicated a search\n" ); return; } #if DBG printf("Searching for %c%ws%c\n", (SuffixMatch ? '*' : ' '), Match.Buffer, (PrefixMatch ? '*' : ' ') ); #endif Status = NtOpenDirectoryObject( &DirectoryHandle, DIRECTORY_QUERY | ExtraAccess, &Attributes ); } } if (!NT_SUCCESS( Status )) { if (Status == STATUS_OBJECT_TYPE_MISMATCH) { printf( "%Z is not a valid Object Directory Object name\n", DirectoryName ); } else { Error( OpenDirectory, Status ); } return; } // // Get the actual name of the object directory object. // NameInfo = (POBJECT_NAME_INFORMATION) &Buffer[0]; if (!NT_SUCCESS( Status = NtQueryObject( DirectoryHandle, ObjectNameInformation, NameInfo, BUFFERSIZE, (PULONG) NULL ) )) { printf( "Unexpected error obtaining actual object directory name\n" ); printf( "Error was: %X\n", Status ); return; } // // Output initial informational message // printf( "Directory of: %wZ\n", &NameInfo->Name ); if ( Match.Length == 0 ) { if (DumpOwner) { QueryAndDisplayOwner( DirectoryHandle, L"Directory" ); } if (DumpDacl) { QueryAndDisplayDacl( DirectoryHandle, L"Directory" ); } if (DumpSacl) { QueryAndDisplaySacl( DirectoryHandle, L"Directory" ); } printf( "\n" ); } // // Query the entire directory in one sweep // NumberOfDirEntries = 0; for (Status = NtQueryDirectoryObject( DirectoryHandle, &Buffer, BUFFERSIZE, FALSE, FALSE, &Context, &ReturnedLength ); NT_SUCCESS( Status ); Status = NtQueryDirectoryObject( DirectoryHandle, &Buffer, BUFFERSIZE, FALSE, FALSE, &Context, &ReturnedLength ) ) { // // Check the status of the operation. // if (!NT_SUCCESS( Status )) { if (Status != STATUS_NO_MORE_FILES) { Error( Status, Status ); } break; } // // For every record in the buffer type out the directory information // // // Point to the first record in the buffer, we are guaranteed to have // one otherwise Status would have been No More Files // DirInfo = (POBJECT_DIRECTORY_INFORMATION) &Buffer[0]; while (TRUE) { // // Check if there is another record. If there isn't, then get out // of the loop now // if (DirInfo->Name.Length == 0) { break; } // // Print out information about the file // if (NumberOfDirEntries >= MAX_DIR_ENTRIES) { printf( "OBJDIR: Too many directory entries.\n" ); exit( 1 ); } DirEntries[ NumberOfDirEntries ].Name = RtlAllocateHeap( RtlProcessHeap(), HEAP_ZERO_MEMORY, DirInfo->Name.Length + sizeof( UNICODE_NULL ) ); if (DirEntries[ NumberOfDirEntries ].Name == NULL) { printf( "OBJDIR: Not enough memory to complete the operation.\n" ); exit( 1 ); } DirEntries[ NumberOfDirEntries ].Type = RtlAllocateHeap( RtlProcessHeap(), HEAP_ZERO_MEMORY, DirInfo->TypeName.Length + sizeof( UNICODE_NULL ) ); if (DirEntries[ NumberOfDirEntries ].Type == NULL) { printf( "OBJDIR: Not enough memory to complete the operation.\n" ); exit( 1 ); } memmove( DirEntries[ NumberOfDirEntries ].Name, DirInfo->Name.Buffer, DirInfo->Name.Length ); memmove( DirEntries[ NumberOfDirEntries ].Type, DirInfo->TypeName.Buffer, DirInfo->TypeName.Length ); NumberOfDirEntries++; // // There is another record so advance DirInfo to the next entry // DirInfo = (POBJECT_DIRECTORY_INFORMATION) (((PUCHAR) DirInfo) + sizeof( OBJECT_DIRECTORY_INFORMATION ) ); } RtlZeroMemory( Buffer, BUFFERSIZE ); } qsort( DirEntries, NumberOfDirEntries, sizeof( DIR_ENTRY ), CompareDirEntry ); for (i=0; i= Match.Length / sizeof( WCHAR ) ) { SuffixMatched = _wcsnicmp( DirEntries[ i ].Name + ( ObjectNameLength - (Match.Length / sizeof( WCHAR ) )), Match.Buffer, Match.Length / sizeof( WCHAR) ) == 0 ; } else { SuffixMatched = FALSE ; } } if ( SuffixMatch && !SuffixMatched ) { continue; } if ( PrefixMatch && !PrefixMatched ) { continue; } if ( (!SuffixMatch) && (!PrefixMatch) && _wcsicmp( Match.Buffer, DirEntries[ i ].Name ) ) { continue; } } DisplayedEntries++ ; printf( "%-32ws ", DirEntries[ i ].Name); if (CompoundLineOutput) { printf("\n "); } printf( "%ws", DirEntries[ i ].Type ); if (!wcscmp( DirEntries[ i ].Type, L"SymbolicLink" )) { RtlInitUnicodeString( &UnicodeString, DirEntries[ i ].Name ); InitializeObjectAttributes( &Attributes, &UnicodeString, OBJ_CASE_INSENSITIVE, DirectoryHandle, NULL ); Status = NtOpenSymbolicLinkObject( &LinkHandle, SYMBOLIC_LINK_QUERY, &Attributes ); if (NT_SUCCESS( Status )) { LinkTarget.Buffer = LinkTargetBuffer; LinkTarget.Length = 0; LinkTarget.MaximumLength = sizeof( LinkTargetBuffer ); Status = NtQuerySymbolicLinkObject( LinkHandle, &LinkTarget, NULL ); NtClose( LinkHandle ); } if (!NT_SUCCESS( Status )) { printf( " - unable to query link target (Status == %09X)\n", Status ); } else { printf( " - %wZ\n", &LinkTarget ); } } else { printf( "\n" ); } if (DumpOwner) { QueryAndDisplayOwner( DirectoryHandle, DirEntries[ i ].Type); } if (DumpDacl) { OpenAndDisplayDacl( DirectoryHandle, DirEntries[ i ].Type, DirEntries[ i ].Name); } if (DumpSacl) { OpenAndDisplaySacl( DirectoryHandle, DirEntries[ i ].Type, DirEntries[ i ].Name); } } // // Output final messages // if ( Match.Length != 0 ) { if ( DisplayedEntries == 0 ) { printf("not found\n" ); } else if ( DisplayedEntries == 1 ) { printf("\n1 entry\n" ); } else { printf("\n%ld entries\n", DisplayedEntries ); } } else { if (NumberOfDirEntries == 0) { printf( "no entries\n" ); } else if (NumberOfDirEntries == 1) { printf( "\n1 entry\n" ); } else { printf( "\n%ld entries\n", NumberOfDirEntries ); } } // // Now close the directory object // (VOID) NtClose( DirectoryHandle ); // // And return to our caller // return; } BOOLEAN EnableAllPrivileges( VOID ) /*++ Routine Description: This routine enables all privileges in the token. If we are being asked to dump SACLs then we will check to make sure we have SE_SECURITY_PRIVILEGE enabled too. Arguments: None. Return Value: None. --*/ { HANDLE Token; ULONG ReturnLength, Index; PTOKEN_PRIVILEGES NewState; BOOLEAN Result; LUID SecurityPrivilege; SecurityPrivilege = RtlConvertLongToLuid(SE_SECURITY_PRIVILEGE); Token = NULL; NewState = NULL; Result = (OpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &Token ) ? 1 : 0); if (Result) { ReturnLength = 4096; NewState = malloc( ReturnLength ); Result = (BOOLEAN)(NewState != NULL); if (Result) { Result = (GetTokenInformation( Token, // TokenHandle TokenPrivileges, // TokenInformationClass NewState, // TokenInformation ReturnLength, // TokenInformationLength &ReturnLength // ReturnLength ) ? 1 : 0); if (Result) { // // Set the state settings so that all privileges are enabled... // if (DumpSacl) { Result = FALSE; } if (NewState->PrivilegeCount > 0) { for (Index = 0; Index < NewState->PrivilegeCount; Index++ ) { NewState->Privileges[Index].Attributes = SE_PRIVILEGE_ENABLED; if (RtlEqualLuid(&NewState->Privileges[Index].Luid, &SecurityPrivilege )) { Result = TRUE; } } } if (!Result) { // Don't have privilege to dump SACL ASSERT(DumpSacl); printf("\n\n You do not have sufficient privilege to display SACLs.\n\n"); } else { Result = (AdjustTokenPrivileges( Token, // TokenHandle FALSE, // DisableAllPrivileges NewState, // NewState (OPTIONAL) ReturnLength, // BufferLength NULL, // PreviousState (OPTIONAL) &ReturnLength // ReturnLength ) ? 1 : 0); if (!Result) { DbgPrint( "AdjustTokenPrivileges( %lx ) failed - %u\n", Token, GetLastError() ); } } } else { DbgPrint( "GetTokenInformation( %lx ) failed - %u\n", Token, GetLastError() ); } } else { DbgPrint( "malloc( %lx ) failed - %u\n", ReturnLength, GetLastError() ); } } else { DbgPrint( "OpenProcessToken( %lx ) failed - %u\n", GetCurrentProcess(), GetLastError() ); } if (NewState != NULL) { free( NewState ); } if (Token != NULL) { CloseHandle( Token ); } return( Result ); } NTSTATUS OpenObject( IN HANDLE Root, IN PWCHAR Type, IN PWCHAR Name, IN ACCESS_MASK DesiredAccess, OUT PHANDLE Object ) { NTSTATUS Status; UNICODE_STRING UnicodeName; OBJECT_ATTRIBUTES Attributes; IO_STATUS_BLOCK Iosb; RtlInitUnicodeString( &UnicodeName, Name ); InitializeObjectAttributes( &Attributes, &UnicodeName, OBJ_CASE_INSENSITIVE, Root, NULL ); // // This is effectively a big switch statement of object types // that we know how to open... // if (!wcscmp( Type, L"SymbolicLink" )) { Status = NtOpenSymbolicLinkObject( Object, DesiredAccess, &Attributes ); } else if (!wcscmp( Type, L"Device" )) { Status = NtOpenFile( Object, DesiredAccess, &Attributes, &Iosb, 0, 0 ); } else if (!wcscmp( Type, L"Event" )) { Status = NtOpenEvent( Object, DesiredAccess, &Attributes ); } else if (!wcscmp( Type, L"EventPair" )) { Status = NtOpenEventPair( Object, DesiredAccess, &Attributes ); } else if (!wcscmp( Type, L"Mutant" )) { Status = NtOpenMutant( Object, DesiredAccess, &Attributes ); } else if (!wcscmp( Type, L"Timer" )) { Status = NtOpenTimer( Object, DesiredAccess, &Attributes ); } else if (!wcscmp( Type, L"Semaphore" )) { Status = NtOpenSemaphore( Object, DesiredAccess, &Attributes ); } else if (!wcscmp( Type, L"Section" )) { Status = NtOpenSection( Object, DesiredAccess, &Attributes ); } else if (!wcscmp( Type, L"Directory" )) { Status = NtOpenDirectoryObject( Object, DesiredAccess, &Attributes ); } else if (!wcscmp( Type, L"Process" )) { Status = NtOpenProcess( Object, DesiredAccess, &Attributes, NULL ); } else if (!wcscmp( Type, L"Job" )) { Status = NtOpenJobObject( Object, DesiredAccess, &Attributes ); } else if (!wcscmp( Type, L"WindowStation" )) { *Object = OpenWindowStationW( Name, FALSE, DesiredAccess ); if (*Object) { Status = STATUS_SUCCESS; } else { Status = STATUS_ACCESS_DENIED; } } else if (!wcscmp( Type, L"Desktop" )) { *Object = OpenDesktopW( Name, 0, FALSE, DesiredAccess ); if (*Object) { Status = STATUS_SUCCESS; } else { Status = STATUS_ACCESS_DENIED; } } else { // // this utility doesn't yet support opening this type of object // Status = STATUS_NOT_SUPPORTED; } return(Status); } VOID OpenAndDisplaySacl( IN HANDLE Root, IN PWCHAR Type, IN PWCHAR Name ) { NTSTATUS Status, IgnoreStatus; HANDLE Object; Status = OpenObject( Root, Type, Name, ACCESS_SYSTEM_SECURITY, &Object); if (NT_SUCCESS(Status)) { QueryAndDisplaySacl( Object, Type ); IgnoreStatus = NtClose( Object ); ASSERT(NT_SUCCESS(IgnoreStatus)); } if (!NT_SUCCESS(Status)) { if (Status == STATUS_NOT_SUPPORTED) { printf(" Utility doesn't yet query SACLs for this type of object.\n\n"); } else { printf(" Error attempting to query SACL: 0x%lx.\n\n", Status); } } return; } VOID QueryAndDisplaySacl( IN HANDLE Object, IN PWSTR Type ) { NTSTATUS Status; PSECURITY_DESCRIPTOR SD = NULL; ULONG LengthNeeded, TypeIndex ; Status = NtQuerySecurityObject( Object, SACL_SECURITY_INFORMATION, NULL, 0, &LengthNeeded ); ASSERT(!NT_SUCCESS(Status)); if (Status == STATUS_BUFFER_TOO_SMALL) { SD = RtlAllocateHeap( RtlProcessHeap(), 0, LengthNeeded ); if (SD == NULL) { Status = STATUS_NO_MEMORY; } else { Status = NtQuerySecurityObject( Object, SACL_SECURITY_INFORMATION, SD, LengthNeeded, &LengthNeeded ); if (NT_SUCCESS(Status)) { // // Display the SACL // Status = DisplaySacl( SD, Type ); } } } if (SD) { RtlFreeHeap( RtlProcessHeap(), 0, SD ); } return; } NTSTATUS DisplaySacl( PSECURITY_DESCRIPTOR SD, PWSTR Type ) /*++ Routine Description: This function dumps out a SACL If an error status is returned, then the caller is responsible for printing a message. --*/ { NTSTATUS Status; BOOLEAN AclPresent, AclDefaulted; PACL Acl; ACL_SIZE_INFORMATION AclInfo; ULONG i; PACE_HEADER Ace; ULONG TypeIndex ; TypeIndex = GetObjectTypeIndex( Type ); Status = RtlGetSaclSecurityDescriptor ( SD, &AclPresent, &Acl, &AclDefaulted ); if (NT_SUCCESS(Status)) { printf(" SACL - "); if (!AclPresent) { printf("No SACL present on object\n"); } else { if (AclDefaulted) { printf("SACL Defaulted flag set\n "); } if (Acl == NULL) { printf("NULL SACL - no auditing performed.\n"); } else { Status = RtlQueryInformationAcl ( Acl, &AclInfo, sizeof(ACL_SIZE_INFORMATION), AclSizeInformation); ASSERT(NT_SUCCESS(Status)); if (AclInfo.AceCount == 0) { printf("No ACEs in ACL, Auditing performed.\n"); } else { printf("\n"); for (i=0; iAceFlags & INHERIT_ONLY_ACE) != 0) { printf("Inherit Only - "); } switch (Ace->AceType) { case ACCESS_ALLOWED_ACE_TYPE: printf("Grant -"); DumpStandardAceInfo(Ace, AclIsDacl, TypeInfo); break; case ACCESS_DENIED_ACE_TYPE: printf("Deny -"); DumpStandardAceInfo(Ace, AclIsDacl, TypeInfo); break; case SYSTEM_AUDIT_ACE_TYPE: printf("Audit "); DumpStandardAceInfo(Ace, AclIsDacl, TypeInfo); break; default: printf(" ** Unknown ACE Type **"); } return; } VOID DumpStandardAceInfo( PACE_HEADER Ace, BOOLEAN AclIsDacl, PTYPEINFO TypeInfo ) /*++ Routine Description: This function dumps out the standard information for a single ACE. Arguments: Ace - Points to an ACE_HEADER. The ACE must be one of the known types. AclIsDacl - TRUE if acl is a DACL. False if acl is an SACL. Return Value: None. --*/ { PACCESS_ALLOWED_ACE Local; ACCESS_MASK Specific; // // WARNING - // // It is assumed that all the known ACE types have their ACCESS_MASK // and SID fields in the same location as the ACCESS_ALLOWED_ACE. // Local = (PACCESS_ALLOWED_ACE)(Ace); if (Ace->AceType == SYSTEM_AUDIT_ACE_TYPE) { printf("["); if (Ace->AceFlags & SUCCESSFUL_ACCESS_ACE_FLAG) { printf("S"); if (Ace->AceFlags & FAILED_ACCESS_ACE_FLAG) { printf(",F"); } } else if (Ace->AceFlags & FAILED_ACCESS_ACE_FLAG) { printf(" ,F"); } else { printf("Neither Success nor Failure flag set]"); return; } printf("] -"); } printf(" 0x%lx - ", Local->Mask ); DisplaySid( (PSID)(&Local->SidStart) ); // // Everything else is only printed for FULL displays if (AclIsDacl && !DumpDaclFull) { return; } if (!AclIsDacl && !DumpSaclFull) { return; } // // Print the inheritance // printf("\n Inherit: "); if ((Ace->AceFlags & INHERIT_ONLY_ACE) != 0) { printf("IO "); } if ((Ace->AceFlags & OBJECT_INHERIT_ACE) != 0) { printf("OI "); } if ((Ace->AceFlags & CONTAINER_INHERIT_ACE) != 0) { printf("CI "); } if ((Ace->AceFlags & NO_PROPAGATE_INHERIT_ACE) != 0) { printf("NPI"); } // // Print the accesses // Specific = (Local->Mask & 0xFFFF); printf("\n Access: 0x%04lX", Specific); if ( TypeInfo->NumberRights ) { DisplayFlags( Specific, TypeInfo->NumberRights, TypeInfo->AccessRights, 38, 80, sizeof( Buffer ), (PUCHAR) Buffer ); printf("\n%s\n ", Buffer); } if (Local->Mask != Specific) { printf(" and ("); } if ((Local->Mask & DELETE) != 0) { printf(" D"); } if ((Local->Mask & READ_CONTROL) != 0) { printf(" RCtl"); } if ((Local->Mask & WRITE_OWNER) != 0) { printf(" WOwn"); } if ((Local->Mask & WRITE_DAC) != 0) { printf(" WDacl"); } if ((Local->Mask & SYNCHRONIZE) != 0) { printf(" Synch"); } if ((Local->Mask & GENERIC_READ) != 0) { printf(" R"); } if ((Local->Mask & GENERIC_WRITE) != 0) { printf(" W"); } if ((Local->Mask & GENERIC_EXECUTE) != 0) { printf(" E"); } if ((Local->Mask & GENERIC_ALL) != 0) { printf(" ALL"); } if ((Local->Mask & ACCESS_SYSTEM_SECURITY) != 0) { printf(" ACC_SYS_SEC"); } if ((Local->Mask & MAXIMUM_ALLOWED) != 0) { printf(" MAX_ALLOWED"); } if (Local->Mask != Specific) { printf(" )"); } printf("\n"); return; } VOID DisplaySid( IN PSID Sid ) /*++ Routine Description: This function calls LSA to lookup a SID and displays the result. Arguments: Sid Return Value: None. --*/ { NTSTATUS Status; PLSA_REFERENCED_DOMAIN_LIST ReferencedDomains; PLSA_TRANSLATED_NAME SidName; ULONG DomainIndex; UNICODE_STRING SidString; if (LsaHandle == NULL) { printf("Can't call LSA to lookup sid"); return; } Status = LsaLookupSids( LsaHandle, 1, &Sid, &ReferencedDomains, &SidName ); if (!NT_SUCCESS(Status)) { RtlConvertSidToUnicodeString( &SidString, Sid, TRUE ); printf("%ws (Unable to translate)", SidString.Buffer ); RtlFreeUnicodeString( &SidString ); return; } DomainIndex = SidName[0].DomainIndex; printf("%wZ", &ReferencedDomains->Domains[DomainIndex].Name ); if (ReferencedDomains->Domains[DomainIndex].Name.Length != 0) { printf("\\"); } printf("%wZ", &SidName[0].Name ); LsaFreeMemory( ReferencedDomains ); LsaFreeMemory( SidName ); return; } VOID ConnectToLsa( VOID ) /*++ Routine Description: This function connects to LSA in preparation for expected SID lookup calls. --*/ { NTSTATUS Status; OBJECT_ATTRIBUTES ObjectAttributes; InitializeObjectAttributes( &ObjectAttributes, NULL, 0, 0, NULL ); LsaHandle = NULL; Status = LsaOpenPolicy( NULL, // SystemName &ObjectAttributes, POLICY_LOOKUP_NAMES, // DesiredAccess &LsaHandle ); if (!NT_SUCCESS(Status)) { LsaHandle = NULL; } return; } VOID Usage(VOID) { printf("\n\n" " Usage:\n" " objdir [/o | /O] [/d | /D] [/s | /S] []\n\n" " Where:\n" " /o or /O - causes the owner to be displayed.\n\n" " /d - causes DACLs to be displayed in short form.\n\n" " /D - causes DACLs to be displayed in long form.\n\n" " /s - causes SACLs to be displayed in short form.\n\n" " /S - causes SACLs to be displayed in long form.\n\n" " - is the name of the directory you\n" " would like to see displayed. Default\n" " is the root directory.\n\n" " Examples:\n" " objdir /d\n" " - displays dacls of objects in root directory\n\n" " objdir \\DosDevices\n" " - displays objects in \\DosDevices\n\n" " objdir /d \\BaseNamedObjects\n" " - displays dacls of objects in \\BaseNamedObjects\n\n" " objdir /s /d \\Windows\n" " - displays sacls and dacls of objects in \\Windows\n\n" " objdir /d \\Windows\\Windowstations\\Service*\n" " - displays dacls of all windowstations beginning with 'service'\n\n" " objdir \\Windows\\w*\n" " - displays objects starting with w in \\Windows\n\n" ); return; }