Windows-Server-2003/sdktools/objdir/objdir.c

2063 lines
49 KiB
C
Raw Permalink Normal View History

2024-08-04 01:28:15 +02:00
/*++
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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <malloc.h>
#include <ntlsa.h>
#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<NumberOfDirEntries; i++) {
if ( Match.Length )
{
ObjectNameLength = wcslen( DirEntries[ i ].Name );
if ( PrefixMatch )
{
PrefixMatched = _wcsnicmp( DirEntries[ i ].Name,
Match.Buffer,
Match.Length / sizeof( WCHAR ) ) == 0 ;
}
if ( SuffixMatch )
{
if ( ObjectNameLength >= 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; i<AclInfo.AceCount; i++) {
Status = RtlGetAce( Acl, i, &Ace );
ASSERT(NT_SUCCESS(Status));
printf(" Ace[%2d] - ", i);
DumpAce( Ace, FALSE, &TypeNames[ TypeIndex ] );
printf("\n");
}
}
}
}
}
printf("\n");
return(Status);
}
VOID
OpenAndDisplayDacl(
IN HANDLE Root,
IN PWCHAR Type,
IN PWCHAR Name
)
{
NTSTATUS
Status,
IgnoreStatus;
HANDLE
Object;
ULONG TypeIndex ;
Status = OpenObject( Root, Type, Name, READ_CONTROL, &Object);
if (NT_SUCCESS(Status)) {
QueryAndDisplayDacl( Object, Type );
IgnoreStatus = NtClose( Object );
ASSERT(NT_SUCCESS(IgnoreStatus));
}
if (!NT_SUCCESS(Status)) {
if (Status == STATUS_ACCESS_DENIED) {
printf(" Protection on object prevented querying the DACL.\n\n");
} else if (Status == STATUS_NOT_SUPPORTED) {
printf(" Utility doesn't yet query DACLs for this type of object.\n\n");
} else {
printf(" Error attempting to query DACL: 0x%lx.\n\n", Status);
}
}
return;
}
VOID
QueryAndDisplayDacl(
IN HANDLE Object,
IN PWSTR Type
)
{
NTSTATUS
Status;
PSECURITY_DESCRIPTOR
SD = NULL;
ULONG
LengthNeeded;
Status = NtQuerySecurityObject( Object,
DACL_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,
DACL_SECURITY_INFORMATION,
SD,
LengthNeeded,
&LengthNeeded
);
if (NT_SUCCESS(Status)) {
//
// Display the DACL
//
Status = DisplayDacl( SD, Type );
}
}
}
if (SD) {
RtlFreeHeap( RtlProcessHeap(), 0, SD );
}
return;
}
NTSTATUS
DisplayDacl(
PSECURITY_DESCRIPTOR SD,
PWSTR Type
)
/*++
Routine Description:
This function dumps out a DACL
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 = RtlGetDaclSecurityDescriptor ( SD, &AclPresent, &Acl, &AclDefaulted );
if (NT_SUCCESS(Status)) {
printf(" DACL - ");
if (!AclPresent) {
printf("No DACL present on object\n");
} else {
if (AclDefaulted) {
printf("DACL Defaulted flag set\n ");
}
if (Acl == NULL) {
printf("NULL DACL - grants all access to Everyone\n");
} else {
Status = RtlQueryInformationAcl ( Acl,
&AclInfo,
sizeof(ACL_SIZE_INFORMATION),
AclSizeInformation);
ASSERT(NT_SUCCESS(Status));
if (AclInfo.AceCount == 0) {
printf("No ACEs in ACL, Denies all access to everyone\n");
} else {
printf("\n");
for (i=0; i<AclInfo.AceCount; i++) {
Status = RtlGetAce( Acl, i, &Ace );
ASSERT(NT_SUCCESS(Status));
printf(" Ace[%2d] - ", i);
DumpAce( Ace, TRUE, &TypeNames[ TypeIndex ] );
printf("\n");
}
}
}
}
}
printf("\n");
return(Status);
}
VOID
QueryAndDisplayOwner(
IN HANDLE Object,
IN PWSTR Type
)
{
NTSTATUS
Status;
PSECURITY_DESCRIPTOR
SD = NULL;
ULONG
LengthNeeded;
Status = NtQuerySecurityObject( Object,
OWNER_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,
OWNER_SECURITY_INFORMATION,
SD,
LengthNeeded,
&LengthNeeded
);
if (NT_SUCCESS(Status)) {
PSID Owner;
BOOLEAN OwnerDefaulted;
Status = RtlGetOwnerSecurityDescriptor ( SD, &Owner, &OwnerDefaulted );
if (NT_SUCCESS(Status)) {
printf(" Owner - ");
DisplaySid(Owner);
}
printf("\n");
}
}
}
if (SD) {
RtlFreeHeap( RtlProcessHeap(), 0, SD );
}
return;
}
VOID
DumpAce(
PACE_HEADER Ace,
BOOLEAN AclIsDacl,
PTYPEINFO TypeInfo
)
/*++
Routine Description:
This function displays a single ACE
Arguments:
Ace - Points to an ACE.
AclIsDacl - TRUE if acl is a DACL. False if acl is an SACL.
Return Value:
None.
--*/
{
if ((Ace->AceFlags & 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] [<dir_name>]\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"
" <dir_name> - 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;
}