2063 lines
49 KiB
C
2063 lines
49 KiB
C
/*++
|
||
|
||
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;
|
||
}
|