#  Perm.pl
#  Example 3.10:
#  ----------------------------------------
#  From "Win32 Perl Scripting: Administrators Handbook" by Dave Roth
#  Published by New Riders Publishing.
#  ISBN # 1-57870-215-1
#
#  This script displays who has permissions on specified objects.
#
print "From the book 'Win32 Perl Scripting: The Administrator's Handbook' by Dave Roth\n\n";


use Win32::Perms;

# The %PERM hash maps a permission type to its position in the
# permission string array. Notice that there is no 0 position
# since that is reserved for unhandled permission types (and
# we won't print it).
%PERM = (
  R   =>  1,
  W   =>  2,
  X   =>  3,
  D   =>  4,
  P   =>  5,
  O   =>  6,
  A   =>  7,
);

%MAP = (
  'FILE_READ_DATA'    =>  'R',
  'GENERIC_READ'      =>  'R',
  'KEY_READ'          =>  'R',
  'DIR_READ'          =>  'R',
  
  'FILE_WRITE_DATA'   =>  'W',
  'KEY_WRITE'         =>  'W',
  'GENERIC_WRITE'     =>  'W',
  'FILE_APPEND_DATA'  =>  'W',
  'DIR_ADD_SUBDIR'    =>  'W',
  'DIR_ADD_FILE'      =>  'W',
  
  'DELETE'            =>  'D',
  'FILE_DELETE_CHILD' =>  'D',
  
  'FILE_EXECUTE'      =>  'X',
  'FILE_TRAVERSE'     =>  'X',
  'GENERIC_EXECUTE'   =>  'X',
  'DIR_TRAVERSE'      =>  'X',
  'DIR_EXECUTE'       =>  'X',
  
  'CHANGE_PERMISSION' =>  'P',
  
  'TAKE_OWNERSHIP'    =>  'O',
  
  'FILE_ALL_ACCESS'   =>  'A',
  'GENERIC_ALL'       =>  'A',
  'DIR_ALL_ACCESS'    =>  'A',
  'STANDARD_RIGHTS_ALL' => 'A',
);

push( @ARGV, "*.*" ) unless( scalar @ARGV );
foreach my $Mask ( @ARGV )
{
  my( @List ) = glob( $Mask );
  if( ! scalar @List )
  {
    push( @List, $Mask );
  }
  foreach my $Path ( @List )
  {
    print "\nPermissions for '$Path':\n";
    ReportPerms( $Path );
    print "\n\n";
  }
}

sub ReportPerms
{
  my( $Path ) = @_;
  my( $Acct, @List );
  my( $Perm ) = new Win32::Perms( $Path );
  my( %PermList ) = ();
  my( $MaxAcctLength ) = 1;

  if( ! $Perm )
  {
      print "Can not obtain permissions for '$Path'\n";
      return;
  };

  printf( "  Owner: %s\n  Group: %s\n", 
          $Perm->Owner(), 
          $Perm->Group() );
  $Perm->Dump( \@List );
  foreach $Acct ( @List )
  {
    my( $PermMask );
    my( $Mask, @M, @F );
    my( $DaclType );
    my $bAllowAccess = ( "Deny" ne $Acct->{Access} );
    my $String;
    my $Account;
    
    next if( $Acct->{Entry} ne "DACL" );
    
    if( "" eq $Acct->{Account} )
    {
      $Account = $Acct->{SID};
    }
    else
    {
      $Account = "$Acct->{Domain}\\" if( "" ne $Acct->{Domain} );
      $Account .= $Acct->{Account};
    }
    if( length( $Account ) > $MaxAcctLength )
    {
      $MaxAcctLength = length( $Account )
    }
    $iTotal++;
    DecodeMask( $Acct, \@M, \@F );
    foreach $Mask ( @M )
    {
      $PermMask |= 2**$PERM{ $MAP{$Mask} };
    }
    $DaclType = $Acct->{ObjectName};
    if( 2 == $Acct->{ObjectType} )
    {
      # We have either a file or directory. Therefore we need to
      # figure out if this DACL represents an object (file) or
      # a container (dir)...
      if( $Acct->{Flag} & DIR )
      {
        $DaclType = "Directory";
      }
      else
      {
        $DaclType = "File";
      }
    }
    if( ! defined $PermList{$Account}->{$DaclType} )
    {
      # Create the permission string array. The first element in the
      # array must be blank since all unhandled permissions will default
      # to that position (and we won't print it).
      my $TempHash = [ 
                       " ", 
                       split( //, "-" x scalar( keys( %PERM ) ) ) 
                     ];
      $PermList{$Account}->{$DaclType} = $TempHash;
                                    
    }
    foreach $Mask ( keys( %PERM ) )
    {
      if( $PermMask & 2**$PERM{$Mask} )
      {
        $String = $PermList{$Account}->{$DaclType};
        # If we already have a denied permission then skip this step
        # since denied access overrides any explicitly allowed access
        if( $String->[$PERM{$Mask}] !~ /[a-z]/ )
        {
          my $TempMask = $Mask;
          $TempMask = lc $Mask if( 0 == $bAllowAccess );
          $String->[$PERM{$Mask}] = $TempMask ;
        }
      }
    }
  }
  
  if( ! $iTotal )
  {
    # There are no DACL entries therefore...
    print "\t Everyone has full permissions.\n";
  }
  else
  {
    foreach my $Permission ( sort( keys( %PermList ) ) )
    {
      foreach my $DaclType ( sort( keys( %{$PermList{$Permission}} ) ) )
      {            
        my $String = $PermList{$Permission}->{$DaclType};
        printf( "  % " . $MaxAcctLength . "s % -11s %s\n", 
                $Permission, 
                "($DaclType)", 
                join( '', @$String ) );
      }
    }
  }
}
