blackadder has asked for the wisdom of the Perl Monks concerning the following question:

I REALLY do have a problem
I ma requested to write a script that does the following :
ON a given machine, the script will check every directory and sub directory (from root directory and downwards) for access permission levels.
An example
We have a stand a lone server called srv1 with 1 drive C: On C drive I have 3 folders. I right click on the C drive from my computer Icon and go to security tab and add 1 user, 1 local group and 1 global group.
The script should produce the following out put (or similar
Server : srv1, user: samant001, local_group: lo_grp, global_group : gl_grp
The script that I have written (Please see below) doesn?t work properly
My problem is
1- Admin::Misc::GetGroups("server",GROUP_TYPE_LOCAL/GLOBAL) Will only return what you get in UserManager rather than the users or local/global groups I have added by right-clicking and selecting my choices
2-use Win32::FileSecurity qw ( MakeMask Set Get EnumerateRights) will dump users and groups (local/global) without differentiating them.
I really do need to know the item that I am examining if its a single user or a group and if its a group then is it a local or global group.
I thank you very much in advance.
please ignore the OLE and Excel bits
# Declare Libraries used use OLE; use Win32; use Getopt::Long; #use Win32::Perms; use Win32::NetAdmin; use Win32::AdminMisc; use Win32::FileSecurity qw ( MakeMask Set Get EnumerateRights); my $srv_lst; #Clear Screen system ( CLS ); sub configure { my ( $config ) = @_; Getopt::Long::Configure ( "prefix_pattern=(-|\/)" ); my $result = GetOptions ( $config, qw ( path|p=s help|?)); $config->{help}=1 if (! $result); } sub syntax { my( $script ) = (Win32::GetLongPathName( $0 ) =~ /([^\\\/]*?)$/ ); my( $line ) = "-" x length ( $script ); print <<EOT; $script $line User requested help Syntax: $script [ path to server list ] or [ No argument supplied for loca +l host ] [ path to server list ]........................path to servers lis +t [ Nothing ]....................................for local host only -help | -?......................................Display this help +message. Exampl $script c:\\my_dir\servers_list.txt <return> or $script <return> Note : path to the servers list can be remote of local. FOR FURTHER HELP CONTACT Tim) EOT } #Display used server list configure (\ my %config ); if ($config{help}) { syntax ( ); exit ( ); } if (! @ARGV) { print "\nLocal Host will be used\n"; my ($host ) = Win32::AdminMisc::GetComputerName ( ); push ( @srv_lst, $host ); } else { #Open server list file open ( LST, "@ARGV" ) || die "\n$! : @ARGV\n"; #Input server names into an array chomp ( @srv_lst = <LST> ); print "\n\nUsing server source '@ARGV'\n"; } Win32::MsgBox ( "Using server source @ARGV",32,"NT Server Access Contr +ol List" ); #Start Microsoft Excel, make it visible and add a workbook my $xls = CreateObject OLE "Excel.Application" || die "CreateObject: $ +!"; $xls -> { Visible } = 1; $xls -> Workbooks -> Add ( ); #Prepare Column width and title $xls -> Worksheets ( "Sheet1" ) -> Columns ( "A:A" ) -> { ColumnWidth +} =15; $xls -> Worksheets ( "Sheet1" ) -> Columns ( "B:B" ) -> { ColumnWidth +} =6; $xls -> Worksheets ( "Sheet1" ) -> Columns ( "C:C" ) -> { ColumnWidth +} =50; $xls -> Worksheets ( "Sheet1" ) -> Columns ( "D:D" ) -> { ColumnWidth +} =30; $xls -> Worksheets ( "Sheet1" ) -> Columns ( "E:E" ) -> { ColumnWidth +} =40; $xls -> Worksheets ( "Sheet1" ) -> Columns ( "F:F" ) -> { ColumnWidth +} =30; # Write the titles to Excel $xls -> Worksheets ( "Sheet1" ) -> Cells (1,1) -> Font -> { FontStyle +} = 'bold'; $xls -> Worksheets ( "Sheet1" ) -> Cells (1,1) -> { Value } = "Server +Name"; $xls -> Worksheets ( "Sheet1" ) -> Cells (1,2) -> Font -> { FontStyle +} = 'bold'; $xls -> Worksheets ( "Sheet1" ) -> Cells (1,2) -> { Value } = "Drive"; $xls -> Worksheets ( "Sheet1" ) -> Cells (1,3) -> Font -> { FontStyle +} = 'bold'; $xls -> Worksheets ( "Sheet1" ) -> Cells (1,3) -> { Value } = "Directo +ry/Sub-Directory Scanned"; $xls -> Worksheets ( "Sheet1" ) -> Cells (1,4) -> Font -> { FontStyle +} = 'bold'; $xls -> Worksheets ( "Sheet1" ) -> Cells (1,4) -> { Value } = "Local G +roups"; $xls -> Worksheets ( "Sheet1" ) -> Cells (1,5) -> Font -> { FontStyle +} = 'bold'; $xls -> Worksheets ( "Sheet1" ) -> Cells (1,5) -> { Value } = "Users P +ermissioned"; $xls -> Worksheets ( "Sheet1" ) -> Cells (1,6) -> Font -> { FontStyle +} = 'bold'; $xls -> Worksheets ( "Sheet1" ) -> Cells (1,6) -> { Value } = "Access +Type"; #Main program body - Go through each server in the array my $row = 2; foreach my $srv ( @srv_lst ) { my $col = 1; #Inform of what you are inttending to do print "\n\nObtaining drive information on server '$srv'\n"; $xls -> Worksheets ( "Sheet1" ) -> Cells ( $row, $col ) -> { Value + } = "$srv"; #Get server drives, and input drive letters into an array if ( Win32::NetAdmin::GetServerDisks ( "$srv", \ my @disks ) ) { foreach my $disk ( @disks ) { $col=2; next if $disk =~ /a|A|z|Z|c|C/; $disk =~ s/:/\$/; print"\nDrive $disk : "; $xls -> Worksheets ( "Sheet1" ) -> Cells ( $row, $col ) -> + { Value } = "$disk"; my $unc_path = "\\\\" . $srv . "\\" . $disk . "\\"; + my ( $size, $free ) = ( Win32::AdminMisc::GetDriveSpace ( +"$unc_path" ) ); if (Win32::AdminMisc::GetGroups("$srv", GROUP_TYPE_LOCAL, +\ @group )) { print "\nDone\n"; $col = 4; foreach $g (@group) { $xls -> Worksheets ( "Sheet1" ) -> Cells ( $row, $ +col) -> { Value } = "@group"; print "\n$g\n"; } } if ( $size != 0 ) { $col = 3; $size = int ( $size/1024/1024 ); $free = int ( $free/1024/1024 ); print "size = $size, Free = $free\n"; #Go through each drive and display directories and sub +-ditrectories only my $file_counter = 1; &print_dir ( "$unc_path"); sub print_dir { $col = 3; my ( $dir_name ) = @_; my ( $file, $sub_dir, $file_var ); $file_var = "CONST" . $file_counter++; opendir ( $file_var, $dir_name ) || die "\n$! : Ca +nnot Open Directory : '$dir_name'\n"; print "\n$dir_name :\n"; $xls -> Worksheets ( "Sheet1" ) -> Cells ( $row, $ +col ) -> { Value } = "$dir_name"; #The permissions check. foreach (@_) { next unless -e $_; if ( Get ( $_, \ %hash ) ) { while ( ( $name, $mask ) = each %hash ) { $col = 5; print "\n$name : \n \t "; + $xls -> Worksheets ( "Sheet1" ) -> Cel +ls ( $row, $col++ ) -> { Value } = "$name - ". &get_f_name; EnumerateRights ( $mask, \ @happy ); print join ( "\t\n" , @happy ), "\n"; $xls -> Worksheets ( "Sheet1" ) -> Cel +ls ( $row++, $col) -> { Value } = "@happy"; } } else { print ( "Error #", int ( $! ), " : $!" ); } } while ( $file = readdir ( $file_var ) ) { next if ( $file eq "." || $file eq ".." ); } rewinddir ( $file_var ); while ( $sub_dir = readdir ( $file_var ) ) { next unless ( -d ( $dir_name . "\\" . $sub_dir + ) ); next if ( $sub_dir eq "." || $sub_dir eq ".." +); &print_dir ( $dir_name . "\\". $sub_dir ); } closedir ( $file_var ); } } else { print "'CD-Rom Drive'\n"; #$xls -> Worksheets ( "Sheet1" ) -> Cells ( $row++, $c +ol ) -> { Value } = "$dir_name : CD-Rom Drive"; } } } } sub get_f_name { my $data = @_; my $usr = 'USER_FULL_NAME'; my %attrib = (); ($attrib{$usr}) if (Win32::AdminMisc::UserGetMiscAttributes("$unc_ +path",$name,\%attrib));

Edit kudra, 2002-06-10 Added readmore, removed double spacing

Replies are listed 'Best First'.
Win32 ACLs: Local vs Global groups
by Util (Priest) on Jun 10, 2002 at 16:33 UTC

    I would be remiss in my monk duties if I did not begin with these cautions:

    1. You have a long, large Perl program in which you neither:
      1. use strict, nor
      2. use warnings (-w).
      In Perl programming, this is the equivalent of keeping a loaded gun pointed at your anesthetized foot. You *will* shoot yourself in the foot one day, and you may not even know it at the time it happens. *Please* take the time to adopt the strict and warnings habits, and to retrofit your code to work cleanly under their yoke. Most monks would be very happy to help teach you this.
    2. You posted large sample code, much of which was not relevant to your problem. To better understand why this is not in your own best interest, please read the "Post Only Relevant Code" section of How (Not) To Ask A Question.

    Here is the first piece of the puzzle: From the Win32::AdminMisc FAQ, in the section on GetGroups:

    If the 3rd parameter is an array reference then upon success the user names populate the array. If it is a hash reference then it is populated with the group name, comment, type (local or global) and if the it is a global group then the groups flags.

    I wrote and ran this test code:
    #!/usr/bin/perl -w use strict; use warnings 'all'; use Win32::AdminMisc; my $server = ''; # Use local machine. my %groups; Win32::AdminMisc::GetGroups($server, GROUP_TYPE_ALL, \%groups) or warn "Failed: $!, $^E"; foreach my $group (keys %groups) { printf "%s\t%s\n", $groups{$group}{type}, $group; } # Uncomment next two lines to see all data from %group hash. #use Data::Dumper; #print Dumper \%group;
    I received this output:
    global  None
    local   Replicator
    local   Users
    local   Backup Operators
    local   Administrators
    local   Guests
    local   Power Users
    

    What do you get when you run this code on a server that you "right-clicked and selected" on? How does it differ from what you expected?

    I can add this code:
    use Win32::FileSecurity; my $filename = 'C:/WINNT/twain_32'; my %hash; Win32::FileSecurity::Get($filename,\%hash) or warn "Get failed: $!"; while ( my ( $ACL_owner, $mask ) = each %hash ) { $ACL_owner =~ s{.+\\}{}; my @perms; Win32::FileSecurity::EnumerateRights($mask, \@perms) or warn "Enumerate failed: $!"; my $ACL_owner_type = $groups{$ACL_owner}{type} || 'User'; print "$ACL_owner ($ACL_owner_type):\n"; @perms = $perms[0]; # only print one permission while testing. print "\t\t\t$_\n" foreach @perms; }
    and receive this output:
    Administrators (local):
                            DELETE
    CREATOR OWNER (User):
                            GENERIC_ALL
    Power Users (local):
                            DELETE
    SYSTEM (User):
                            DELETE
    Users (local):
                            READ_CONTROL
    

    I think that solves part 2 of your question. Let me know if any of my code needs further explanation.

    If you will answer the question I posed above (right after the first block of output), then perhaps the answer to part 1 will be clearer to myself or another monk.

      Brother Kudra and all monks whom are helping me: I JUST CANNOT THANK YOU ENOUGH, your reply to me is like a ray of sunshine...I will get on with your recommendations in moment, thank you indeed (and I hope to be a Monk as well) I will get back to you all with my findins as soon as. GodBlessYou Bros
      All worked nicely, however I couldn’t use strict with the recursive routine that accesses directories and sub directories (but this is the least of my worries at the moment. I can use File::Find module I suppose but I will continue with the recursive access for the time being)
      Finally and most importantly is: if I wanted to remove the 'everyone' account from accessing certain directories or sub directories without having to remove the account from the PDC? is this possible?
      I tried using Win32::NetAdmin::UserDelete, but this did not work
      Can someone help me please?
      I have marked the bit that dosen't in my code below
      use Win32; use warnings 'all'; use Win32::FileSecurity; use Win32::NetAdmin; use Win32::AdminMisc; my $unc_path = shift @ARGV; my $accnt = pop @ARGV; $unc_path = "\\\\" . $unc_path; print "\n\nPath to scan=>\t'$unc_path'\t\t Account to remove=>\t'$accn +t'\n\n"; my %groups; Win32::AdminMisc::GetGroups($unc_path, GROUP_TYPE_ALL, \%groups) || wa +rn "\nFailed : $!, $^E\n"; foreach my $group (keys %groups) { printf "%s\t%s\n", $groups{$group}{type}, $group; } print "\n\nUser permissioned\n"; my %hash; Win32::FileSecurity::Get("$unc_path", \ %hash ) || warn "Get failed : +$!\n"; while (my ($acl_owner, $mask)=each %hash) { $acl_owner =~ s{.+\\}{}; my @perms; Win32::FileSecurity::EnumerateRights( $mask, \@perms) || warn "Enu +merateRights failed : $!\n"; my $acl_owner_type = $groups{$acl_owner}{type} || 'user'; print "$acl_owner ($acl_owner_type): "; if ( $acl_owner =~ /everyone/i ) { print "This account is about to be deleted\n"; #THIS BIT DOES NOT WORK Win32::NetAdmin::UserDelete($unc_path, $acl_owner); } @perms = $perms[0]; foreach (@perms) { print "\t\t\t$_\n" } }