#############################################################################
#
# PermDump.pl - script to do intelligent permissions enumeration under Win32
#
# Author: Toby Ovod-Everett
#
#############################################################################
# Copyright 2003-2024 Toby Ovod-Everett.  All rights reserved
#
# This program is free software; you can redistribute it and/or modify it
# under the same terms as Perl itself.
#
# For comments, questions, bugs or general interest, feel free to
# contact Toby Ovod-Everett at toby@ovod-everett.org
#############################################################################

#BEGIN {
#	use Class::Prototyped;
#	my $temp = Class::Prototyped->reflect->defaultAttributes;
#	$temp->{FIELD}->{profile} = 2;
#	$temp->{METHOD}->{profile} = 2;
#	Class::Prototyped->reflect->defaultAttributes($temp);
#}

use Data::Dumper;
use File::DosGlob 'glob';
use Getopt::Long;
use Win32::Security::Recursor;

use strict;
use vars qw($counter $starttime);

$starttime = Win32::GetTickCount();

my $options = {};
GetOptions($options, qw(csv! dirsonly! file=s inherited! owner! recurse|s! help performance!)) or die "Invalid option.\n";

if (defined($options->{help})) {
	print <<ENDHELP;
PermDump.pl options:
  -c[sv]         Output in CSV format
  -d[irsonly]    Check directories only
  -f[ile]=file   Use passed filename for output in CSV or TDF format
  -i[nherited]   Display properly inherited permissions/ownership
  -o[wner]       Display ownership
  -r[ecurse]     Recurse into subdirectories
  -s             Same as -r[ecurse]
  -p[erformance] Outputs simple performance numbers
  -h[elp]        Print this message

PermDump.pl takes an optional list of files and/or directories to check.  If
no list is passed, it will display permissions for the current directory.

The Desc value displays one of these three values in the first column:
  (D)irectory  The thing in question is a directory
  (F)ile       The thing in question is a file
  (?)          Returned with ERROR_READ_FILEATTRIBS

The Desc value displays one of these nine values in the second column:
  (B)locked    Inheritance is blocked for this object
  (E)rror      There was an error on this object
  (I)nherited  The ACE is properly inherited from its container
  (J)unction   Permission enumeration did not proceed through this Junction
  (M)issing    This inheritable ACE on the container is missing from the object
  (N)ULLDACL   This object has a NULL DACL
  (O)wner      Ownership record for the object
  (W)rong      This ACE is marked as inherited but there is no corresponding
               ACE on its container
  e(X)plicit   The ACE is explicitly applied to the object

ENDHELP
	exit;
}

$| = 1;
select((select(STDERR), $|=1)[0]);

@ARGV = map {/[*?]/ ? glob($_) : $_ } @ARGV;
@ARGV = (".") unless scalar(@ARGV);

my $recursor = Win32::Security::Recursor::SE_FILE_OBJECT::PermDump->new($options, debug => 0);

$recursor->print_header();
foreach my $name (@ARGV) {
	$recursor->recurse($name);
}

$recursor->close_file_handle();

if ($options->{performance}) {
	my $elapsed = Win32::GetTickCount()-$starttime;
	print STDERR sprintf("%i in %0.2f seconds (%i/s, %0.2f ms)\n", $recursor->{payload_count},
			($elapsed)/1000, $recursor->{payload_count}*1000/($elapsed || 1),
			$elapsed/($recursor->{payload_count} || 1)
		);
	print STDERR sprintf("%i unique ACEs, %i unique ACLs\n",
			scalar(keys %{Win32::Security::ACE::SE_FILE_OBJECT->_rawAceCache()}),
			scalar(keys %{Win32::Security::ACL::SE_FILE_OBJECT->_rawAclCache()}) );
}

# my $payload_count = $recursor->{payload_count};
# foreach my $package (sort keys %{$Class::Prototyped::Mirror::PROFILE::counts}) {
	# my $slots = $Class::Prototyped::Mirror::PROFILE::counts->{$package};
	# foreach my $slotName (sort keys %{$slots}) {
		# my $slotData = $slots->{$slotName};
		# if (ref($slotData) eq 'HASH') {
			# foreach my $caller (sort keys %{$slotData}) {
				# my $call_count = $slotData->{$caller};
				# print STDERR "$package\t$slotName\t$caller\t$call_count\t".sprintf("%0.3f", $call_count/$payload_count)."\n";
			# }
		# }
		# else {
			# my $call_count = $slotData;
			# print STDERR "$package\t$slotName\t$call_count\t".sprintf("%0.3f", $call_count/$payload_count)."\n";
		# }
	# }
# }
