#!/usr/bin/perl -w # # Rough tool to migrate ACLs from a server you've already done a file # clone on. # use File::Find; use bytes; use strict; use Getopt::Long; my ( $UNIX_ROOT, $NT_ROOT ); GetOptions( "uroot=s" => \$UNIX_ROOT, "nroot=s" => \$NT_ROOT ) or usage(); usage() unless $UNIX_ROOT; usage() unless $NT_ROOT; $NT_ROOT =~ s/\$/\\\$/g; # Mappings for things we can't find in passwd/group my %usermap = ( "BUILTIN\\Administrators" => "root", "NT AUTHORITY\\SYSTEM" => "root", ); my %groupmap = ( "BUILTIN\\Administrators" => "Domain Admins", "NT AUTHORITY\\SYSTEM" => "Domain Admins", "BUILTIN\\Server Operators" => "Domain Admins", ); my %groups; my %users; my $verbose = 1; print "Cloning $UNIX_ROOT to $NT_ROOT\n"; find( { wanted => \&acl_frob, no_chdir => 1 }, $UNIX_ROOT ); sub acl_frob { # Fetch ACL for file my $file = $File::Find::name; $file =~ s@$UNIX_ROOT@@; $file =~ s@^/@@; return unless $file; # make filename safe $file =~ s/"/\\"/g; $file =~ s/`/\\`/g; # use double instead of single quotes around the filename because # for some reason single quotes don't seem to get escaped properly? my $cmd = "smbcacls -A /root/cred $NT_ROOT \"$file\""; my @bits = qx($cmd 2>&1) or warn "$cmd failed\n"; # back to what it used be $file = $File::Find::name; my ( $chuid, $chgid, @acls ) = ( -1, -1 ); for my $bit ( @bits ) { chomp( $bit ); my ( $acl, $setting ) = split( ':', $bit, 2 ); if ( $acl eq "REVISION" ) { next; # discard } elsif ( $acl eq "OWNER" ) { if ( defined( $usermap{$setting} )) { $setting = $usermap{$setting}; } $chuid = cached_getpwnam( $setting ); } elsif ( $acl eq "GROUP" ) { if ( defined( $groupmap{$setting} )) { $setting = $groupmap{$setting}; } $chgid = cached_getgrnam( $setting ); } elsif ( $acl eq "ACL" ) { # NT AUTHORITY\SYSTEM:ALLOWED/0/FULL my ( $user, $access ) = split( ':', $setting ); if ( defined( $usermap{$user} )) { $user = $usermap{$user}; } elsif (defined( $groupmap{$user})) { $user = $groupmap{$user}; } # parse out the access my ( $perm, $flags, $acls ) = split( '/', $access ); my $mode = 'r'; my $aclcmd = ""; if ( $acls eq 'FULL' ) { $mode = 'rwx'; } # now see if it's a user, a group, or some Damned Thing if ( defined( getpwnam( $user ))) { #print " $user is a user\n"; $aclcmd = 'setfacl -m u:\'' . $user . '\':' . $mode . " \"" . $file . "\""; } elsif ( defined( getgrnam( $user ))) { #print " $user is a group\n"; $aclcmd = 'setfacl -m g:\'' . $user . '\':' . $mode . " \"" . $file . "\""; } elsif ( $user eq "\\Everyone" ) { #print " $user is EVERYONE\n"; $aclcmd = 'setfacl -m o::' . $mode . " \"" . $file . "\""; } elsif ( $user eq "\\CREATOR OWNER" ) { #print " $user is file's owner\n"; $aclcmd = 'setfacl -m u::' . $mode . " \"" . $file . "\""; } else { print "$file\n"; print " $acl => $setting\n"; # die "What is $user, exactly?"; warn "Can't identify $user\n"; } my $code = system( $aclcmd ); warn "ACL command $aclcmd failed with $code\n" if $code; } else { if ( $bit =~ /Failed to open/ ) { print "$file appears to have vanished\n"; unlink( $file ); return; } print "Command was >$cmd<\n"; die "Can't parse output from command '$bit'\n"; } } chown( $chuid, $chgid, $file ); } sub cached_getpwnam { my $user = shift; if ( !defined( $users{$user})) { my ( $login, $pass, $uid, $gid ) = getpwnam( $user ) or warn "$user is not a valid username ($File::Find::name)"; $uid ||= 0; $users{$user} = $uid; } $users{$user}; } sub cached_getgrnam { my $group = shift; if ( !defined( $groups{$group})) { my ( $name,$passwd,$gid,$members) = getgrnam( $group ) or warn "$group is not a valid group ($File::Find::name)"; $gid ||= 0; $groups{$group} = $gid; } $groups{$group}; } sub usage { print STDERR "Usage: $0 --uroot UNIX_ROOT --nroot NT_ROOT\n"; exit(1); }