/*++

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(#S);                    \
    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
    );

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


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

LPWSTR   pszTypeNames[TYPE_MAX] = { L"None", L"Event", L"Section", L"File",
                                L"Port", L"Directory", L"SymbolicLink",
                                L"Mutant", L"WindowStation", L"Semaphore",
                                L"Key", L"Token", L"Process", L"Thread",
                                L"Desktop", L"IoCompletion", L"Channel",
                                L"Timer", L"Job", L"WaitablePort" };



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 )
   {
       strcpy( (CHAR *)buffer, "Invalid Parameter");
       
       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 == '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 (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) {
        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 (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 )
                                                                   );
            DirEntries[ NumberOfDirEntries ].Type = RtlAllocateHeap( RtlProcessHeap(),
                                                                     HEAP_ZERO_MEMORY,
                                                                     DirInfo->TypeName.Length +
                                                                         sizeof( UNICODE_NULL )
                                                                   );
            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 (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,
        IgnoreStatus;

    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;

    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 );
            }
        }
    }
    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;

    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 );
            }
        }
    }
    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
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;

    CHAR FlagBuffer[ 256 ];

    //
    // 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 [/d | /D] [/s | /S] [<dir_name>]\n\n"
           "    Where:\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 \\Windowss\n\n"
           "        objdir /d \\Windows\\Windowstations\\Service*\n"
           "                - displays dacls of all windowstations beginning with 'service'\n\n"
           "        objdir \\Global??\\w*\n"
           "                - displays objects starting with w in \\Global??\n\n"
           );
    return;
}