#!/usr/bin/perl ### suphpfix.pl # # Author: Scott Sullivan (ssullivan@liquidweb.com) # # GENERAL: # Fixes common suPHP/FCGI/CGI (with suexec enabled) permission/ownership issues on cPanel servers. For specifics, see the help function. # This script grabs account information such as cPanel usernames, and document roots from the cPanel JSON API. /root/datastore_suphpfix/ # is used to store JSON data for saving states. Restore states read these JSON files to perform restores. # # USAGE: # See the help function. ### use strict; our $version="2.1.0.3"; use JSON; use LWP::UserAgent; use Encode; use Term::ANSIColor; use Fcntl ':mode'; use POSIX qw(strftime); use Linux::Ext2::FileAttributes; sub help { print color 'red'; print "*** Options: \n\n"; print color 'green'; print "+++ Fix common problems when converting to suPHP\n"; print color 'reset'; print color 'yellow'; print "--prep all "; print color 'blue'; print "==> "; print color 'reset'; print " This will chown all cPanel users files/directories in their public_html's to cPanelUser.cPanelUser. It will also remove group and world write from files/directories. In addition, any php directive in .htaccess such as php_flag will be commented out (unless htscanner is present). \n"; print color 'yellow'; print "--prep cPanelUser "; print color 'blue'; print "==> "; print color 'reset'; print "Same as '--prep all' but only applies fixes to the specified cPanel account.\n"; print color 'green'; print "\n+++ Saving states\n"; print color 'reset'; print color 'yellow'; print "--save-state all "; print color 'blue'; print "==> "; print color 'reset'; print "This saves the current permission/ownership settings (for later restores) for all cPanel accounts files/directories under their public_html's.\n"; print color 'yellow'; print "--save-state cPanelUser"; print color 'blue'; print " ==> "; print color 'reset'; print "Same as '--save-state all' but only for the specified cPanel account.\n"; print color 'green'; print "\n+++ Restoring states\n"; print color 'reset'; print color 'yellow'; print "--restore-state all "; print color 'blue'; print "==> "; print color 'reset'; print "This restores all saved cPanel accounts permission/ownership settings at the time --save-state all was last ran. It also uncomments any php directives in accounts .htaccess file(s).\n"; print color 'yellow'; print "--restore-state all verify"; print color 'blue'; print " ==> "; print color 'reset'; print "Same as '--restore-state all' but just outputs proposed chmods/chowns for all accounts without actually changing anything.\n"; print color 'yellow'; print "--restore-state cPanelUser"; print color 'blue'; print " ==> "; print color 'reset'; print "This restores only the specified cPanel accounts permissions/ownership settings at the time --save-state was last ran for the user. It also uncomments any php directives in the accounts .htaccess file(s).\n"; print color 'yellow'; print "--restore-state cPanelUser verify"; print color 'blue'; print " ==> "; print color 'reset'; print "Same as '--restore-state cPanelUser' but just outputs proposed chmods/chowns for the user without actually changing anything.\n"; print color 'red'; print "\n*** General: \n\n"; print color 'reset'; print color 'yellow'; print "* "; print color 'reset'; print "suphpfix uses /root/datastore_suphpfix/ to store JSON data. These JSON files allow suphpfix to store what permissions/ownerships each backed up cPanel user had at the time of the last --save-state. There are two JSON files for each cPanel account (one for files, one for directories). In this directory suphpfix uses backedupUserList.all to store what cPanel users were backed up with --save-state all. \n\n"; } sub runCmds { die "FATAL: wrong args passed to runCmds()!\n" if (scalar(@_) != 4); my $user = $_[0]; my $domain = $_[1]; my $docRoot = $_[2]; my $htscannerCheck = $_[3]; ## Do some safety checks on passed document root... if (! -d $docRoot ) { print color 'red'; print "ERROR: The discovered document root ($docRoot) for $user doesn't exist!\n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } if ( $docRoot !~ m/^\/home/ ) { print color 'red'; print "ERROR: The discovered document root ($docRoot) for $user does not start with /home*\n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } if ( $docRoot !~ m/\/public_html$/ ) { print color 'red'; print "ERROR: The discovered document root ($docRoot) for $user does not end with /public_html\n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } ## Passed document root looks ok, runCmds... print color 'reset'; print "---------------------------------", "\n"; print color 'yellow'; print "Working with: "; print color 'reset'; print $domain, "\n"; print color 'yellow'; print "Discovered document root: "; print color 'reset'; print $docRoot, "\n"; print "++ Removing group and world write in ", $docRoot, " ...\n"; system("find $docRoot -perm +022 -exec chmod go-w {} \\\;"); print color 'green'; print "* Task complete.\n"; print color 'reset'; print "++ Checking directory permissions in ", $docRoot, " ...\n"; system("find $docRoot -type d -exec chmod 755 {} \\\;"); print color 'green'; print "* Task complete.\n"; print color 'reset'; print "++ Setting ownerships to ", $user, ":", $user, " in ", $docRoot, " ...\n"; system("chown -R $user:$user $docRoot"); system("chown $user:nobody $docRoot"); print color 'green'; print "* Task complete.\n"; print color 'reset'; print "++ Ensuring files are readable in $docRoot ... \n"; system("find $docRoot -type f -exec chmod ugo+r {} \\\;"); print color 'green'; print "* Task complete.\n"; print color 'reset'; if ($htscannerCheck eq "Installed") { print color 'reset'; print color 'yellow'; print "NOTICE: htscanner found, keeping php tweaks. \n"; print color 'reset'; } elsif ($htscannerCheck eq "NotInstalled") { print "++ Commenting out php tweaks from .htaccess ...", "\n"; system("find $docRoot -name .htaccess -exec sed -i 's/^php_/#php_/g' {} \\\;"); print color 'green'; print "* Task complete.", "\n"; print color 'reset'; } print "Done with ", $domain, "!", "\n"; print "------------------------------", "\n"; } sub cleanUp { print color 'reset'; print "\nCleaning up... \n"; system("chmod -R 1777 /tmp"); system("chmod 700 /tmp/screens/S-root &> /dev/null"); } sub suphpfixMain { die "FATAL: wrong args passed to suphpfixMain()!\n" if (scalar(@_) != 1); my $param = $_[0]; print "Generating hash..."; system("QUERY_STRING=\\\"regen=1\\\" /usr/local/cpanel/whostmgr/bin/whostmgr ./setrhash &> /dev/null"); my $hashfile = '/root/.accesshash'; if (! -e $hashfile) { print color 'red'; print "ERROR: Failed to automatically generate hash! Please try logging into WHM and click `Setup Remote Access Key` and then re-run this script. \n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } my $hash = `cat /root/.accesshash`; print color 'green'; print "success!\n"; print color 'reset'; $hash =~ s/\n//g; my $auth = "WHM root:" . $hash; my $json = new JSON; my $ua = LWP::UserAgent->new; $ua->agent("suphpfix"); $ua->env_proxy(); my $request = HTTP::Request->new(POST => "http://127.0.0.1:2086/json-api/listaccts"); $request->header( Authorization => $auth ); $request->content_type('application/jsonrequest'); $request->content("{}"); #create and ensure UTF8 my $response = $ua->request($request); my $listRaw = $response->content; my $list = encode("UTF-8", $listRaw); my $accntList; eval{ $accntList = $json->allow_nonref->utf8->relaxed->decode($list); }; if ($@) { print color 'red'; print "ERROR: Didn't receive a valid JSON response from cPanel API.\n"; print "Got Error: $@ "; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } my $totalAccnts="0"; for my $each( @{$accntList->{acct}} ) { $totalAccnts++; }; print color 'reset'; print "Checking for htscanner..."; my $htscannerChk = `php -i |grep -i htscanner |grep htscanner.config_file|cut -f 1 -d "="`; my $htscannerReturnChk = ""; if (!$htscannerChk) { print color 'yellow'; print "not found! \n"; print color 'reset'; $htscannerReturnChk = "NotInstalled"; } else { print color 'green'; print "found! \n"; print color 'reset'; $htscannerReturnChk = "Installed"; } #### #### Single user #### if ( defined($param) ) { if ( $param ne "all" ) { if ($totalAccnts >= 2) { my $validUser=""; my $singleUserPartition; my $singleUserDocRoot; my $singleUserDomain; for my $userCnt( @{$accntList->{acct}} ) { if ( $userCnt->{user} eq $param ) { $validUser = "true"; $singleUserPartition = $userCnt->{partition}; $singleUserDocRoot = "/$singleUserPartition/$param/public_html"; $singleUserDomain = "$userCnt->{domain}"; } } if ( $validUser ne "true" ) { print color 'reset'; print color 'red'; print "ERROR: $param not found to be a valid user. \n"; print color 'reset'; exit(1); } if ( $validUser eq "true" ) { &runCmds("$param","$singleUserDomain","$singleUserDocRoot","$htscannerReturnChk"); &cleanUp(); &checkLogSize(); exit; } } elsif ( $totalAccnts == 1 ) { print color 'reset'; print color 'yellow'; print "NOTICE: You only have one account, no need to specify user. Please run with '--prep all'.\n"; print color 'reset'; exit(1); } else { print color 'reset'; print color 'yellow'; print "NOTICE: You do not appear to have any accounts setup yet.\n"; print color 'reset'; exit(1); } } } ### ### All users ### if ($totalAccnts == "1") { print color 'reset'; print "Discovered one account.\n"; $hash =~ s/\n//g; my $auth = "WHM root:" . $hash; my $json = new JSON; my $ua = LWP::UserAgent->new; $ua->agent("suphpfix"); $ua->env_proxy(); my $request = HTTP::Request->new(POST => "http://127.0.0.1:2086/json-api/listaccts"); $request->header( Authorization => $auth ); $request->content_type('application/jsonrequest'); $request->content("{}"); #create and ensure UTF8 my $response = $ua->request($request); my $listRaw = $response->content; my $list = encode("UTF-8", $listRaw); eval{ $accntList = $json->allow_nonref->utf8->relaxed->decode($list); }; if ($@) { print color 'red'; print "ERROR: Didn't receive a valid JSON response from cPanel API.\n"; print "Got Error: $@"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } my $user; my $domain; my $partition; for my $users( @{$accntList->{acct}} ) { $user = $users->{user}; $domain = $users->{domain}; $partition = $users->{partition}; }; my $docRoot = "/$partition/$user/public_html"; &runCmds("$user","$domain","$docRoot","$htscannerReturnChk"); &cleanUp(); &checkLogSize(); exit; } elsif ($totalAccnts >= "2") { print "Discovered multiple accounts.\n"; $hash =~ s/\n//g; my $auth = "WHM root:" . $hash; my $json = new JSON; my $ua = LWP::UserAgent->new; $ua->agent("suphpfix"); $ua->env_proxy(); my $request = HTTP::Request->new(POST => "http://127.0.0.1:2086/json-api/listaccts"); $request->header( Authorization => $auth ); $request->content_type('application/jsonrequest'); $request->content("{}"); #create and ensure UTF8 my $response = $ua->request($request); my $listRaw = $response->content; my $list = encode("UTF-8", $listRaw); eval{ $accntList = $json->allow_nonref->utf8->relaxed->decode($list); }; if ($@) { print color 'red'; print "ERROR: Didn't receive a valid JSON response from cPanel API.\n"; print "Got Error: $@"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } my ($domain,$user,$partition,$docRoot); my $count="0"; for my $userCnt( @{$accntList->{acct}} ) { $user = $userCnt->{user}; $domain = $userCnt->{domain}; $partition = $userCnt->{partition}; $docRoot = "/$partition/$user/public_html"; print color 'reset'; &runCmds("$user","$domain","$docRoot","$htscannerReturnChk"); $count++; print color 'reset'; print color 'green'; print "Completed: $count / $totalAccnts \n"; print color 'reset'; } &cleanUp(); &checkLogSize(); exit; } else { print color 'yellow'; print "NOTICE: You do not appear to have any accounts setup yet.\n"; print color 'reset'; exit(1); } } sub getCPuserDetails { die "getCPuserDetails() needs one param. Param1: cPanel user or 'all' \n" if (scalar(@_) != 1); my $arg1 = $_[0]; system("QUERY_STRING=\\\"regen=1\\\" /usr/local/cpanel/whostmgr/bin/whostmgr ./setrhash &> /dev/null"); my $hashfile = '/root/.accesshash'; if (! -e $hashfile) { print color 'red'; print "ERROR: Failed to automatically generate hash! Please try logging into WHM and click `Setup Remote Access Key` and then re-run this script. \n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } my $hash = `cat /root/.accesshash`; $hash =~ s/\n//g; my $auth = "WHM root:" . $hash; if ( $arg1 eq "all" ) { my $json = new JSON; my $ua = LWP::UserAgent->new; $ua->agent("suphpfix"); $ua->env_proxy(); my $request = HTTP::Request->new(POST => "http://127.0.0.1:2086/json-api/listaccts"); $request->header( Authorization => $auth ); $request->content_type('application/jsonrequest'); $request->content("{}"); #create and ensure UTF8 my $response = $ua->request($request); my $listRaw = $response->content; my $list = encode("UTF-8", $listRaw); my $accntList = $json->allow_nonref->utf8->relaxed->decode($list); our @cpUsersArray; my $accountCounter = 0; for my $Cnt( @{$accntList->{acct}} ) { $accountCounter++; } if ( $accountCounter == 0 ) { print color 'yellow'; print "NOTICE: You do not appear to have any accounts setup yet. \n"; print color 'reset'; exit(1); } my $saveStateProgCount = "1"; for my $userCnt( @{$accntList->{acct}} ) { my $cpUser = $userCnt->{user}; print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "Saving state for $cpUser ($saveStateProgCount/$accountCounter)\n"; print color 'reset'; my $partition = $userCnt->{partition}; my $docRoot = "/$partition/$cpUser/public_html"; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "$cpUser document root is: $docRoot \n"; push(@cpUsersArray, $cpUser); &findFiles("$cpUser","$docRoot"); &findDirs("$cpUser","$docRoot"); $saveStateProgCount++; } my $backedUpUsersSave = '/root/datastore_suphpfix/backedupUserList.all'; print color 'red'; open BAKUSERS, ">$backedUpUsersSave" or die "ERROR: Could not open $!\n"; print color 'reset'; my $endCount = "0"; for my $user ( @cpUsersArray ) { print BAKUSERS "$user\n"; $endCount++; }; print color 'bold green'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "Completed. Saved state for $endCount accounts. \n"; print color 'reset'; } else { ## Verify passed arg is valid cpUser my $json = new JSON; my $ua = LWP::UserAgent->new; $ua->agent("suphpfix"); $ua->env_proxy(); my $request = HTTP::Request->new(POST => "http://127.0.0.1:2086/json-api/listaccts"); $request->header( Authorization => $auth ); $request->content_type('application/jsonrequest'); $request->content("{}"); #create and ensure UTF8 my $response = $ua->request($request); my $listRaw = $response->content; my $list = encode("UTF-8", $listRaw); my $accntList = $json->allow_nonref->utf8->relaxed->decode($list); our @cpUsersArray; my $isValid; for my $userCnt( @{$accntList->{acct}} ) { if ( $userCnt->{user} eq $arg1 ) { my $cpUser = $userCnt->{user}; print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "Saving state for $cpUser\n"; print color 'reset'; $isValid = "true"; my $partition = $userCnt->{partition}; my $docRoot = "/$partition/$cpUser/public_html"; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "$cpUser document root is: $docRoot \n"; &findFiles("$cpUser","$docRoot"); &findDirs("$cpUser","$docRoot"); print color 'reset'; print color 'bold green'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "Completed. Saved state for 1 account.\n"; print color 'reset'; } } if ( $isValid ne "true" ) { print color 'red'; print "ERROR: $arg1 is not a valid cPanel user.\n"; print color 'reset'; exit(1); } } } sub savePermOwner { die "savePermOwner() needs four params. Param1: file to parse through. Param2: files/dirs. Param3: cPanelUser. Param4: docRoot\n" if (scalar(@_) != 4); my $file = $_[0]; our $passFrom = $_[1]; my $cpUser = $_[2]; my $docRoot = $_[3]; my $didTryInode; #remove blank lines $file =~ s/\n+/\n/g; open(FILEparse, "<$file"); my $count = "0"; our %HashOfArrayFilePerms = ( acct => { cpanelUser => [ ], file => [ ], perm => [ ], owner => [ ], group => [ ], }, ); our %HashOfArrayDirPerms = ( acct => { cpanelUser => [ ], file => [ ], perm => [ ], owner => [ ], group => [ ], }, ); my $fileLength = `wc -l $file`; my @fileColumn = split /\s+/, $fileLength; my $runCount = 0; my ($prevLine1, $firstLine, $prevLine2, $line, $fixedLine, $permissions, $user, $group); while () { if ( $didTryInode ne "yes" ) { $prevLine2 = $prevLine1; $prevLine1 = $line; ($line) = $_; chomp($line); chomp($prevLine1); chomp($prevLine2); if ( $runCount == 0 ) { $firstLine = $line; } ## If file length is 2 or fewer lines long if ( @fileColumn[0] <= 2 ) { if ( $prevLine1 !~ m/^\/home/ ) { if ( $prevLine1 ne "" ) { my $newlineChar = "?"; $prevLine1 = $newlineChar.$prevLine1; $fixedLine = $prevLine2.$prevLine1; my $bashConvert = `echo $fixedLine`; $bashConvert =~ s/\s+$//; if ((my $dev,my $ino,my $mode,my $nlink,my $uid,my $gid,my $rdev,my $size,my $atime,my $mtime,my $ctime,my $blksize,my $blocks) = lstat($bashConvert)) { my $fileTestDummy=$bashConvert; #Deal with spaces and special chars #$fileTestDummy =~ s/\s/?/g; $fileTestDummy =~ s/ /\\ /g; $fileTestDummy =~ s/'/\\'/g; $fileTestDummy =~ s/&/\\&/g; $fileTestDummy =~ s/;/\\;/g; $fileTestDummy =~ s/[(]/\\(/g; $fileTestDummy =~ s/[)]/\\)/g; $fileTestDummy =~ s/[|]/\\|/g; $fileTestDummy =~ s/"/\\"/g; $fileTestDummy =~ s/!/\\!/g; $fileTestDummy =~ s//\\>/g; $fileTestDummy =~ s/`/\\`/g; ## If a line has \n or \ in it (and has no spaces), we need to wrap the file path in ' ' $_ = $fileTestDummy; my $blanks += tr/ / /; if ( $fileTestDummy =~/\\n/ ) { if ( $blanks == 0 ) { $fileTestDummy = '\'' . $fileTestDummy . '\''; } } system("ls -ld $fileTestDummy &> /dev/null"); if ($? != 0 ) { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "WARNING: Unable to parse $fileTestDummy. Saving state for $cpUser with inode based method (this could take a while)...\n"; print color 'reset'; if ( $passFrom eq "files" ) { $didTryInode="yes"; &findFilesInode("$cpUser","$docRoot"); } elsif ( $passFrom eq "dirs" ) { $didTryInode="yes"; &findDirsInode("$cpUser","$docRoot"); } } $user = getpwuid($uid); $group = getgrgid($gid); $permissions = sprintf "%04o", S_IMODE($mode); if ( $didTryInode ne "yes" ) { if ( $passFrom eq "files" ) { ##Push the data to the hash of array push ( @{$HashOfArrayFilePerms{'acct'}{'cpanelUser'}}, "$cpUser"); push ( @{$HashOfArrayFilePerms{'acct'}{'file'}}, "$fixedLine"); push ( @{$HashOfArrayFilePerms{'acct'}{'perm'}}, "$permissions"); push ( @{$HashOfArrayFilePerms{'acct'}{'owner'}}, "$user"); push ( @{$HashOfArrayFilePerms{'acct'}{'group'}}, "$group"); } elsif ( $passFrom eq "dirs" ) { ##Push the data to the hash of array push ( @{$HashOfArrayDirPerms{'acct'}{'cpanelUser'}}, "$cpUser"); push ( @{$HashOfArrayDirPerms{'acct'}{'file'}}, "$fixedLine"); push ( @{$HashOfArrayDirPerms{'acct'}{'perm'}}, "$permissions"); push ( @{$HashOfArrayDirPerms{'acct'}{'owner'}}, "$user"); push ( @{$HashOfArrayDirPerms{'acct'}{'group'}}, "$group"); } $count++; } } else { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "WARNING: Unable to parse $bashConvert. Saving state for $cpUser with inode based method (this could take a while)...\n"; print color 'reset'; if ( $passFrom eq "files" ) { $didTryInode="yes"; &findFilesInode("$cpUser","$docRoot"); } elsif ( $passFrom eq "dirs" ) { $didTryInode="yes"; &findDirsInode("$cpUser","$docRoot"); } } } } else { if ( $prevLine1 ne "" ) { ## Make sure current line starts with /home if ( $line =~ m/^\/home/ ) { ## Since current line starts with /home , we can record prevLine1's file attribs. if ((my $dev,my $ino,my $mode,my $nlink,my $uid,my $gid,my $rdev,my $size,my $atime,my $mtime,my $ctime,my $blksize,my $blocks) = lstat($prevLine1)) { my $fileTestDummy=$prevLine1; #Deal with spaces and special chars #$fileTestDummy =~ s/\s/?/g; $fileTestDummy =~ s/ /\\ /g; $fileTestDummy =~ s/'/\\'/g; $fileTestDummy =~ s/&/\\&/g; $fileTestDummy =~ s/[(]/\\(/g; $fileTestDummy =~ s/;/\\;/g; $fileTestDummy =~ s/[)]/\\)/g; $fileTestDummy =~ s/[|]/\\|/g; $fileTestDummy =~ s/"/\\"/g; $fileTestDummy =~ s/!/\\!/g; $fileTestDummy =~ s//\\>/g; $fileTestDummy =~ s/`/\\`/g; ## If a line has \n or \ in it (and has no spaces), we need to wrap the file path in ' ' $_ = $fileTestDummy; my $blanks += tr/ / /; if ( $fileTestDummy =~/\\n/ ) { if ( $blanks == 0 ) { $fileTestDummy = '\'' . $fileTestDummy . '\''; } } system("ls -ld $fileTestDummy &> /dev/null"); if ($? != 0 ) { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "WARNING: Unable to parse $prevLine1. Saving state for $cpUser with inode based method (this could take a while)...\n"; print color 'reset'; if ( $passFrom eq "files" ) { $didTryInode="yes"; &findFilesInode("$cpUser","$docRoot"); } elsif ( $passFrom eq "dirs" ) { $didTryInode="yes"; &findDirsInode("$cpUser","$docRoot"); } } $user = getpwuid($uid); $group = getgrgid($gid); $permissions = sprintf "%04o", S_IMODE($mode); if ( $didTryInode ne "yes" ) { if ( $passFrom eq "files" ) { ##Push the data to the hash of array push ( @{$HashOfArrayFilePerms{'acct'}{'cpanelUser'}}, "$cpUser"); push ( @{$HashOfArrayFilePerms{'acct'}{'file'}}, "$prevLine1"); push ( @{$HashOfArrayFilePerms{'acct'}{'perm'}}, "$permissions"); push ( @{$HashOfArrayFilePerms{'acct'}{'owner'}}, "$user"); push ( @{$HashOfArrayFilePerms{'acct'}{'group'}}, "$group"); } elsif ( $passFrom eq "dirs" ) { ##Push the data to the hash of array push ( @{$HashOfArrayDirPerms{'acct'}{'cpanelUser'}}, "$cpUser"); push ( @{$HashOfArrayDirPerms{'acct'}{'file'}}, "$prevLine1"); push ( @{$HashOfArrayDirPerms{'acct'}{'perm'}}, "$permissions"); push ( @{$HashOfArrayDirPerms{'acct'}{'owner'}}, "$user"); push ( @{$HashOfArrayDirPerms{'acct'}{'group'}}, "$group"); } $count++; } } else { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "WARNING: Unable to parse $prevLine1. Saving state for $cpUser with inode based method (this could take a while)...\n"; print color 'reset'; if ( $passFrom eq "files" ) { $didTryInode="yes"; &findFilesInode("$cpUser","$docRoot"); } elsif ( $passFrom eq "dirs" ) { $didTryInode="yes"; &findDirsInode("$cpUser","$docRoot"); } } } } } } ## File has 3 or more lines elsif ( @fileColumn[0] >= 3 ) { if ( $prevLine1 !~ m/^\/home/ ) { if ( $prevLine1 ne "" ) { if ( $prevLine2 ne "" ) { if ( $prevLine2 !~ m/^\/home/ ) { print color 'yellow'; print "WARNING: The first line found didn't start with /home*. Saving state for $cpUser with inode based method (this could take a while)...\n"; print color 'reset'; if ( $passFrom eq "files" ) { $didTryInode="yes"; &findFilesInode("$cpUser","$docRoot"); } elsif ( $passFrom eq "dirs" ) { $didTryInode="yes"; &findDirsInode("$cpUser","$docRoot"); } } my $newlineChar = "?"; $prevLine1 = $newlineChar.$prevLine1; $fixedLine = $prevLine2.$prevLine1; my $bashConvert = `echo $fixedLine`; $bashConvert =~ s/\s+$//; if ((my $dev,my $ino,my $mode,my $nlink,my $uid,my $gid,my $rdev,my $size,my $atime,my $mtime,my $ctime,my $blksize,my $blocks) = lstat($bashConvert)) { my $fileTestDummy=$bashConvert; #Deal with spaces and special chars #$fileTestDummy =~ s/\s/?/g; $fileTestDummy =~ s/ /\\ /g; $fileTestDummy =~ s/'/\\'/g; $fileTestDummy =~ s/&/\\&/g; $fileTestDummy =~ s/;/\\;/g; $fileTestDummy =~ s/[(]/\\(/g; $fileTestDummy =~ s/[)]/\\)/g; $fileTestDummy =~ s/[|]/\\|/g; $fileTestDummy =~ s/"/\\"/g; $fileTestDummy =~ s/!/\\!/g; $fileTestDummy =~ s//\\>/g; $fileTestDummy =~ s/`/\\`/g; ## If a line has \n or \ in it (and has no spaces), we need to wrap the file path in ' ' $_ = $fileTestDummy; my $blanks += tr/ / /; if ( $fileTestDummy =~/\\n/ ) { if ( $blanks == 0 ) { $fileTestDummy = '\'' . $fileTestDummy . '\''; } } system("ls -ld $fileTestDummy &> /dev/null"); if ($? != 0 ) { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "WARNING: Unable to parse $fileTestDummy. Saving state for $cpUser with inode based method (this could take a while)...\n"; print color 'reset'; if ( $passFrom eq "files" ) { $didTryInode="yes"; &findFilesInode("$cpUser","$docRoot"); } elsif ( $passFrom eq "dirs" ) { $didTryInode="yes"; &findDirsInode("$cpUser","$docRoot"); } } $user = getpwuid($uid); $group = getgrgid($gid); $permissions = sprintf "%04o", S_IMODE($mode); if ( $didTryInode ne "yes" ) { if ( $passFrom eq "files" ) { ##Push the data to the hash of array push ( @{$HashOfArrayFilePerms{'acct'}{'cpanelUser'}}, "$cpUser"); push ( @{$HashOfArrayFilePerms{'acct'}{'file'}}, "$fixedLine"); push ( @{$HashOfArrayFilePerms{'acct'}{'perm'}}, "$permissions"); push ( @{$HashOfArrayFilePerms{'acct'}{'owner'}}, "$user"); push ( @{$HashOfArrayFilePerms{'acct'}{'group'}}, "$group"); } elsif ( $passFrom eq "dirs" ) { ##Push the data to the hash of array push ( @{$HashOfArrayDirPerms{'acct'}{'cpanelUser'}}, "$cpUser"); push ( @{$HashOfArrayDirPerms{'acct'}{'file'}}, "$fixedLine"); push ( @{$HashOfArrayDirPerms{'acct'}{'perm'}}, "$permissions"); push ( @{$HashOfArrayDirPerms{'acct'}{'owner'}}, "$user"); push ( @{$HashOfArrayDirPerms{'acct'}{'group'}}, "$group"); } $count++; } } else { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "WARNING: Unable to parse $fixedLine. Saving state for $cpUser with inode based method (this could take a while)...\n"; print color 'reset'; if ( $passFrom eq "files" ) { $didTryInode="yes"; &findFilesInode("$cpUser","$docRoot"); } elsif ( $passFrom eq "dirs" ) { $didTryInode="yes"; &findDirsInode("$cpUser","$docRoot"); } } } } } else { if ( $prevLine1 ne "" ) { if ( $prevLine2 ne "" ) { ## Make sure current line starts with /home if ( $line =~ m/^\/home/ ) { #if ( $cpUser eq "qa" ) { # if ( $passFrom eq "files" ) { # $prevLine1="/fake/file"; # } #} ## Since current line starts with /home , we can record prevLine1's file attribs. if ((my $dev,my $ino,my $mode,my $nlink,my $uid,my $gid,my $rdev,my $size,my $atime,my $mtime,my $ctime,my $blksize,my $blocks) = lstat($prevLine1)) { my $fileTestDummy=$prevLine1; #Deal with spaces and special chars #$fileTestDummy =~ s/\s/?/g; $fileTestDummy =~ s/ /\\ /g; $fileTestDummy =~ s/'/\\'/g; $fileTestDummy =~ s/&/\\&/g; $fileTestDummy =~ s/;/\\;/g; $fileTestDummy =~ s/[(]/\\(/g; $fileTestDummy =~ s/[)]/\\)/g; $fileTestDummy =~ s/[|]/\\|/g; $fileTestDummy =~ s/"/\\"/g; $fileTestDummy =~ s/!/\\!/g; $fileTestDummy =~ s//\\>/g; $fileTestDummy =~ s/`/\\`/g; ## If a line has \n or \ in it (and has no spaces), we need to wrap the file path in ' ' $_ = $fileTestDummy; my $blanks += tr/ / /; if ( $fileTestDummy =~/\\n/ ) { if ( $blanks == 0 ) { $fileTestDummy = '\'' . $fileTestDummy . '\''; } } system("ls -ld $fileTestDummy &> /dev/null"); if ($? != 0 ) { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "WARNING: Unable to parse $fileTestDummy. Saving state for $cpUser with inode based method (this could take a while)...\n"; print color 'reset'; if ( $passFrom eq "files" ) { $didTryInode="yes"; &findFilesInode("$cpUser","$docRoot"); } elsif ( $passFrom eq "dirs" ) { $didTryInode="yes"; &findDirsInode("$cpUser","$docRoot"); } } $user = getpwuid($uid); $group = getgrgid($gid); $permissions = sprintf "%04o", S_IMODE($mode); if ( $didTryInode ne "yes" ) { if ( $passFrom eq "files" ) { ##Push the data to the hash of array push ( @{$HashOfArrayFilePerms{'acct'}{'cpanelUser'}}, "$cpUser"); push ( @{$HashOfArrayFilePerms{'acct'}{'file'}}, "$prevLine1"); push ( @{$HashOfArrayFilePerms{'acct'}{'perm'}}, "$permissions"); push ( @{$HashOfArrayFilePerms{'acct'}{'owner'}}, "$user"); push ( @{$HashOfArrayFilePerms{'acct'}{'group'}}, "$group"); } elsif ( $passFrom eq "dirs" ) { ##Push the data to the hash of array push ( @{$HashOfArrayDirPerms{'acct'}{'cpanelUser'}}, "$cpUser"); push ( @{$HashOfArrayDirPerms{'acct'}{'file'}}, "$prevLine1"); push ( @{$HashOfArrayDirPerms{'acct'}{'perm'}}, "$permissions"); push ( @{$HashOfArrayDirPerms{'acct'}{'owner'}}, "$user"); push ( @{$HashOfArrayDirPerms{'acct'}{'group'}}, "$group"); } $count++; } } else { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "WARNING: Unable to parse $prevLine1. Saving state for $cpUser with inode based method (this could take a while)...\n"; print color 'reset'; if ( $passFrom eq "files" ) { $didTryInode="yes"; &findFilesInode("$cpUser","$docRoot"); } elsif ( $passFrom eq "dirs" ) { $didTryInode="yes"; &findDirsInode("$cpUser","$docRoot"); } } } } } } } $runCount++; } } if ( $didTryInode ne "yes" ) { ## Loop is done, record left behind lines from above while loop if ( @fileColumn[0] <= 2 ) { if ( $line =~ m/^\/home/ ) { if ((my $dev,my $ino,my $mode,my $nlink,my $uid,my $gid,my $rdev,my $size,my $atime,my $mtime,my $ctime,my $blksize,my $blocks) = lstat($line)) { #Deal with spaces and special chars #$line =~ s/\s/?/g; $line =~ s/ /\\ /g; $line =~ s/'/\\'/g; $line =~ s/&/\\&/g; $line =~ s/;/\\;/g; $line =~ s/[(]/\\(/g; $line =~ s/[)]/\\)/g; $line =~ s/[|]/\\|/g; $line =~ s/"/\\"/g; $line =~ s/!/\\!/g; $line =~ s//\\>/g; $line =~ s/`/\\`/g; ## If a line has \n or \ in it (and has no spaces), we need to wrap the file path in ' ' $_ = $line; my $blanks += tr/ / /; if ( $line =~/\\n/ ) { if ( $blanks == 0 ) { $line = '\'' . $line . '\''; } } system("ls -ld $line &> /dev/null"); if ($? != 0 ) { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "WARNING: Unable to parse $line. Saving state for $cpUser with inode based method (this could take a while)...\n"; print color 'reset'; if ( $passFrom eq "files" ) { $didTryInode="yes"; &findFilesInode("$cpUser","$docRoot"); } elsif ( $passFrom eq "dirs" ) { $didTryInode="yes"; &findDirsInode("$cpUser","$docRoot"); } } $user = getpwuid($uid); $group = getgrgid($gid); $permissions = sprintf "%04o", S_IMODE($mode); if ( $didTryInode ne "yes" ) { if ( $passFrom eq "files" ) { ##Push the data to the hash of array push ( @{$HashOfArrayFilePerms{'acct'}{'cpanelUser'}}, "$cpUser"); push ( @{$HashOfArrayFilePerms{'acct'}{'file'}}, "$line"); push ( @{$HashOfArrayFilePerms{'acct'}{'perm'}}, "$permissions"); push ( @{$HashOfArrayFilePerms{'acct'}{'owner'}}, "$user"); push ( @{$HashOfArrayFilePerms{'acct'}{'group'}}, "$group"); } elsif ( $passFrom eq "dirs" ) { ##Push the data to the hash of array push ( @{$HashOfArrayDirPerms{'acct'}{'cpanelUser'}}, "$cpUser"); push ( @{$HashOfArrayDirPerms{'acct'}{'file'}}, "$line"); push ( @{$HashOfArrayDirPerms{'acct'}{'perm'}}, "$permissions"); push ( @{$HashOfArrayDirPerms{'acct'}{'owner'}}, "$user"); push ( @{$HashOfArrayDirPerms{'acct'}{'group'}}, "$group"); } $count++; } } else { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "WARNING: Unable to parse $line. Saving state for $cpUser with inode based method (this could take a while)...\n"; print color 'reset'; if ( $passFrom eq "files" ) { $didTryInode="yes"; &findFilesInode("$cpUser","$docRoot"); } elsif ( $passFrom eq "dirs" ) { $didTryInode="yes"; &findDirsInode("$cpUser","$docRoot"); } } } } elsif ( @fileColumn[0] >= 3 ) { if ( $firstLine =~ m/^\/home/ ) { if ((my $dev,my $ino,my $mode,my $nlink,my $uid,my $gid,my $rdev,my $size,my $atime,my $mtime,my $ctime,my $blksize,my $blocks) = lstat($firstLine)) { #Deal with spaces and special chars #$firstLine =~ s/\s/?/g; $firstLine =~ s/ /\\ /g; $firstLine =~ s/'/\\'/g; $firstLine =~ s/&/\\&/g; $firstLine =~ s/;/\\;/g; $firstLine =~ s/[(]/\\(/g; $firstLine =~ s/[)]/\\)/g; $firstLine =~ s/[|]/\\|/g; $firstLine =~ s/"/\\"/g; $firstLine =~ s/!/\\!/g; $firstLine =~ s//\\>/g; $firstLine =~ s/`/\\`/g; ## If a line has \n or \ in it (and has no spaces), we need to wrap the file path in ' ' $_ = $firstLine; my $blanks += tr/ / /; if ( $firstLine =~/\\n/ ) { if ( $blanks == 0 ) { $firstLine = '\'' . $firstLine . '\''; } } system("ls -ld $firstLine &> /dev/null"); if ($? != 0 ) { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "WARNING: Unable to parse $firstLine. Saving state for $cpUser with inode based method (this could take a while)...\n"; print color 'reset'; if ( $passFrom eq "files" ) { $didTryInode="yes"; &findFilesInode("$cpUser","$docRoot"); } elsif ( $passFrom eq "dirs" ) { $didTryInode="yes"; &findDirsInode("$cpUser","$docRoot"); } } $user = getpwuid($uid); $group = getgrgid($gid); $permissions = sprintf "%04o", S_IMODE($mode); if ( $didTryInode ne "yes" ) { if ( $passFrom eq "files" ) { ##Push the data to the hash of array push ( @{$HashOfArrayFilePerms{'acct'}{'cpanelUser'}}, "$cpUser"); push ( @{$HashOfArrayFilePerms{'acct'}{'file'}}, "$firstLine"); push ( @{$HashOfArrayFilePerms{'acct'}{'perm'}}, "$permissions"); push ( @{$HashOfArrayFilePerms{'acct'}{'owner'}}, "$user"); push ( @{$HashOfArrayFilePerms{'acct'}{'group'}}, "$group"); } elsif ( $passFrom eq "dirs" ) { ##Push the data to the hash of array push ( @{$HashOfArrayDirPerms{'acct'}{'cpanelUser'}}, "$cpUser"); push ( @{$HashOfArrayDirPerms{'acct'}{'file'}}, "$firstLine"); push ( @{$HashOfArrayDirPerms{'acct'}{'perm'}}, "$permissions"); push ( @{$HashOfArrayDirPerms{'acct'}{'owner'}}, "$user"); push ( @{$HashOfArrayDirPerms{'acct'}{'group'}}, "$group"); } $count++; } } else { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "WARNING: Unable to parse $firstLine. Saving state for $cpUser with inode based method (this could take a while)...\n"; print color 'reset'; if ( $passFrom eq "files" ) { $didTryInode="yes"; &findFilesInode("$cpUser","$docRoot"); } elsif ( $passFrom eq "dirs" ) { $didTryInode="yes"; &findDirsInode("$cpUser","$docRoot"); } } } if ( $line =~ m/^\/home/ ) { if ((my $dev,my $ino,my $mode,my $nlink,my $uid,my $gid,my $rdev,my $size,my $atime,my $mtime,my $ctime,my $blksize,my $blocks) = lstat($line)) { #Deal with spaces and special chars #$line =~ s/\s/?/g; $line =~ s/ /\\ /g; $line =~ s/'/\\'/g; $line =~ s/&/\\&/g; $line =~ s/;/\\;/g; $line =~ s/[(]/\\(/g; $line =~ s/[)]/\\)/g; $line =~ s/[|]/\\|/g; $line =~ s/"/\\"/g; $line =~ s/!/\\!/g; $line =~ s//\\>/g; $line =~ s/`/\\`/g; ## If a line has \n or \ in it (and has no spaces), we need to wrap the file path in ' ' $_ = $line; my $blanks += tr/ / /; if ( $line =~/\\n/ ) { if ( $blanks == 0 ) { $line = '\'' . $line . '\''; } } system("ls -ld $line &> /dev/null"); if ($? != 0 ) { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "WARNING: Unable to parse $line. Saving state for $cpUser with inode based method (this could take a while)...\n"; print color 'reset'; if ( $passFrom eq "files" ) { $didTryInode="yes"; &findFilesInode("$cpUser","$docRoot"); } elsif ( $passFrom eq "dirs" ) { $didTryInode="yes"; &findDirsInode("$cpUser","$docRoot"); } } $user = getpwuid($uid); $group = getgrgid($gid); $permissions = sprintf "%04o", S_IMODE($mode); if ( $didTryInode ne "yes" ) { if ( $passFrom eq "files" ) { ##Push the data to the hash of array push ( @{$HashOfArrayFilePerms{'acct'}{'cpanelUser'}}, "$cpUser"); push ( @{$HashOfArrayFilePerms{'acct'}{'file'}}, "$line"); push ( @{$HashOfArrayFilePerms{'acct'}{'perm'}}, "$permissions"); push ( @{$HashOfArrayFilePerms{'acct'}{'owner'}}, "$user"); push ( @{$HashOfArrayFilePerms{'acct'}{'group'}}, "$group"); } elsif ( $passFrom eq "dirs" ) { ##Push the data to the hash of array push ( @{$HashOfArrayDirPerms{'acct'}{'cpanelUser'}}, "$cpUser"); push ( @{$HashOfArrayDirPerms{'acct'}{'file'}}, "$line"); push ( @{$HashOfArrayDirPerms{'acct'}{'perm'}}, "$permissions"); push ( @{$HashOfArrayDirPerms{'acct'}{'owner'}}, "$user"); push ( @{$HashOfArrayDirPerms{'acct'}{'group'}}, "$group"); } $count++; } } else { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "WARNING: Unable to parse $line. Saving state for $cpUser with inode based method (this could take a while)...\n"; print color 'reset'; if ( $passFrom eq "files" ) { $didTryInode="yes"; &findFilesInode("$cpUser","$docRoot"); } elsif ( $passFrom eq "dirs" ) { $didTryInode="yes"; &findDirsInode("$cpUser","$docRoot"); } } } } } if ( $didTryInode ne "yes" ) { my $json_text; our $HashOfArrayFilePermsRef; our $HashOfArrayDirPermsRef; if ( $passFrom eq "files" ) { $HashOfArrayFilePermsRef = \%HashOfArrayFilePerms; $json_text = JSON->new->utf8(1)->pretty(0)->allow_nonref->encode($HashOfArrayFilePermsRef); } elsif ( $passFrom eq "dirs" ) { $HashOfArrayDirPermsRef = \%HashOfArrayDirPerms; $json_text = JSON->new->utf8(1)->pretty(0)->allow_nonref->encode($HashOfArrayDirPermsRef); } my $saveDir = "/root/datastore_suphpfix"; if (! -d "$saveDir") { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "NOTICE: $saveDir not found, creating it... \n"; print color 'reset'; system("mkdir $saveDir"); } system("rm -f $file"); if ($passFrom eq "files") { print color 'green'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "++ Recorded $count files permissions/ownerships for $cpUser\n"; print color 'reset'; our $json_datastore = $saveDir . '/datastore_file_suphpfix.' . $cpUser .'.JSON'; print color 'red'; open FILE, ">$json_datastore" or die "ERROR: Could not open $json_datastore $!"; print color 'reset'; print FILE $json_text , "\n"; close(FILE); } elsif ($passFrom eq "dirs") { print color 'green'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "++ Recorded $count directories permissions/ownerships for $cpUser\n"; print color 'reset'; our $json_datastore = $saveDir . '/datastore_dir_suphpfix.' . $cpUser . '.JSON'; print color 'red'; open FILE, ">$json_datastore" or die "ERROR: Could not open $json_datastore $! \n"; print color 'reset'; print FILE $json_text , "\n"; close(FILE); } } } sub savePermOwnerInode { die "savePermOwnerInode() needs three params. Param1: files/dirs. Param2: cPanelUser Param3: docRoot. \n" if (scalar(@_) != 3); our $passFrom = $_[0]; my $cpUser = $_[1]; my $path = $_[2]; use vars qw(@fileArrayInode); use vars qw(@dirArrayInode); our %HashOfArrayFilePerms = ( acct => { cpanelUser => [ ], file => [ ], inode => [ ], perm => [ ], owner => [ ], group => [ ], docRoot => [ ], }, ); our %HashOfArrayDirPerms = ( acct => { cpanelUser => [ ], file => [ ], inode => [ ], perm => [ ], owner => [ ], group => [ ], docRoot => [ ], }, ); my $fileCount=0; my $dirCount=0; push ( @{$HashOfArrayFilePerms{'acct'}{'docRoot'}}, "$path"); push ( @{$HashOfArrayDirPerms{'acct'}{'docRoot'}}, "$path"); if ( $passFrom eq "files" ) { my ($fileName,$fileInode,$filePerm,$fileGroup,$fileOwner); for $fileInode (@fileArrayInode) { chomp($fileInode); $fileName=`find $path -xdev -inum $fileInode -printf %p`; chomp($fileName); $filePerm=`find $path -xdev -inum $fileInode -printf %m`; $fileGroup=`find $path -xdev -inum $fileInode -printf %g`; $fileOwner=`find $path -xdev -inum $fileInode -printf %u`; if ( defined($fileInode) ) { if ( defined($fileName) ) { if ( defined($filePerm) ) { if ( defined($fileGroup) ) { if ( defined($fileOwner) ) { ##Push the data to the hash of array push ( @{$HashOfArrayFilePerms{'acct'}{'cpanelUser'}}, "$cpUser"); push ( @{$HashOfArrayFilePerms{'acct'}{'file'}}, "$fileName"); push ( @{$HashOfArrayFilePerms{'acct'}{'inode'}}, "$fileInode"); push ( @{$HashOfArrayFilePerms{'acct'}{'perm'}}, "$filePerm"); push ( @{$HashOfArrayFilePerms{'acct'}{'owner'}}, "$fileOwner"); push ( @{$HashOfArrayFilePerms{'acct'}{'group'}}, "$fileGroup"); } else { print color 'red'; print "ERROR: No file owner found for $fileName (Inode: $fileInode) cPanel user: $cpUser\n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } } else { print color 'red'; print "ERROR: No file group found for $fileName (Inode: $fileInode) cPanel user: $cpUser \n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } } else { print color 'red'; print "ERROR: No file permissions found for $fileName (Inode: $fileInode) cPanel user: $cpUser\n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } } else { print color 'red'; print "ERROR: No file name found for $fileName (Inode: $fileInode) for cPanel user: $cpUser \n "; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } } else { print color 'red'; print "ERROR: No inode entry found in '\@fileArrayInode' slot $fileCount ! Slot $fileCount contents: @fileArrayInode[$fileCount] cPanel user: $cpUser\n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } $fileCount++; } } elsif ( $passFrom eq "dirs" ) { my ($dirName,$dirInode,$dirPerm,$dirGroup,$dirOwner); for $dirInode (@dirArrayInode) { chomp($dirInode); $dirName=`find $path -xdev -inum $dirInode -printf %p`; chomp($dirName); $dirPerm=`find $path -xdev -inum $dirInode -printf %m`; $dirGroup=`find $path -xdev -inum $dirInode -printf %g`; $dirOwner=`find $path -xdev -inum $dirInode -printf %u`; if ( defined($dirInode) ) { if ( defined($dirName) ) { if ( defined($dirPerm) ) { if ( defined($dirGroup) ) { if ( defined($dirOwner) ) { ##Push the data to the hash of array push ( @{$HashOfArrayDirPerms{'acct'}{'cpanelUser'}}, "$cpUser"); push ( @{$HashOfArrayDirPerms{'acct'}{'file'}}, "$dirName"); push ( @{$HashOfArrayDirPerms{'acct'}{'inode'}}, "$dirInode"); push ( @{$HashOfArrayDirPerms{'acct'}{'perm'}}, "$dirPerm"); push ( @{$HashOfArrayDirPerms{'acct'}{'owner'}}, "$dirOwner"); push ( @{$HashOfArrayDirPerms{'acct'}{'group'}}, "$dirGroup"); } else { print color 'reset'; print "ERROR: No directory owner found for $dirName (Inode: $dirInode) cPanel user: $cpUser\n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } } else { print color 'red'; print "ERROR: No directory group found for $dirName (Inode: $dirInode) cPanel user: $cpUser \n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } } else { print color 'red'; print "ERROR: No directory permissions found for $dirName (Inode: $dirInode) cPanel user: $cpUser\n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } } else { print color 'red'; print "ERROR: No directory name found for $dirName (Inode: $dirInode) for cPanel user: $cpUser \n "; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } } else { print color 'red'; print "ERROR: No inode entry found in '\@dirArrayInode' slot $dirCount ! Slot $dirCount contents: @dirArrayInode[$dirCount] cPanel user: $cpUser\n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } $dirCount++; } } ### ### Now that we have collected the details of the files/dirs , save to this data JSON. ### my $json_text; our $HashOfArrayFilePermsRef; our $HashOfArrayDirPermsRef; if ( $passFrom eq "files" ) { $HashOfArrayFilePermsRef = \%HashOfArrayFilePerms; $json_text = JSON->new->utf8(1)->pretty(0)->allow_nonref->encode($HashOfArrayFilePermsRef); } elsif ( $passFrom eq "dirs" ) { $HashOfArrayDirPermsRef = \%HashOfArrayDirPerms; $json_text = JSON->new->utf8(1)->pretty(0)->allow_nonref->encode($HashOfArrayDirPermsRef); } my $saveDir = "/root/datastore_suphpfix"; if (! -d "$saveDir") { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "NOTICE: $saveDir not found, creating it... \n"; print color 'reset'; system("mkdir $saveDir"); } if ($passFrom eq "files") { print color 'green'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "++ Recorded $fileCount files permissions/ownerships for $cpUser\n"; print color 'reset'; our $json_datastore = $saveDir . '/datastore_file_suphpfix.' . $cpUser .'.JSON'; print color 'red'; open FILE, ">$json_datastore" or die "ERROR: Could not open $json_datastore $!"; print color 'reset'; print FILE $json_text , "\n"; close(FILE); } elsif ($passFrom eq "dirs") { print color 'green'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "++ Recorded $dirCount directories permissions/ownerships for $cpUser\n"; print color 'reset'; our $json_datastore = $saveDir . '/datastore_dir_suphpfix.' . $cpUser . '.JSON'; print color 'red'; open FILE, ">$json_datastore" or die "ERROR: Could not open $json_datastore $! \n"; print color 'reset'; print FILE $json_text , "\n"; close(FILE); } } sub findFilesInode { die "findFilesInode() requires cpanel user as arg1 and wwwRoot as arg2\n" if (scalar(@_) != 2); my $cpUser = $_[0]; my $path = $_[1]; if (! -d $path ) { print color 'red'; print "ERROR: Passed document root ($path) for $cpUser does not exist!\n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "Generating file list for $path ... \n"; our @fileArrayInode; push(@fileArrayInode, `find $path -type f -printf %i'\n'`); &savePermOwnerInode("files","$cpUser","$path"); } sub findDirsInode { die "findDirsInode() requires cpanel user as arg1 and wwwRoot as arg2\n" if (scalar(@_) != 2); my $cpUser = $_[0]; my $path = $_[1]; if (! -d $path ) { print color 'red'; print "ERROR: Passed document root ($path) for $cpUser does not exist!\n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "Generating directory list for $path ... \n"; our @dirArrayInode; push(@dirArrayInode, `find $path -type d -printf %i'\n'`); &savePermOwnerInode("dirs","$cpUser","$path"); } sub findFiles { die "findFiles() requires cpanel user as arg1 and wwwRoot as arg2\n" if (scalar(@_) != 2); my $cpUser = $_[0]; my $path = $_[1]; my $saveDir = "/root/datastore_suphpfix"; if (! -d "$saveDir") { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "NOTICE: $saveDir not found, creating it... \n"; print color 'reset'; system("mkdir $saveDir"); } my $fileParseLoc = "$saveDir/suphpfixFileStore.txt"; if ( $path eq "" || !defined($path) ) { print color 'reset'; print "ERROR: Unable to locate file under account $cpUser !\n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } if ( ! -e "$path") { print color 'red'; print "ERROR: File $path doesn't exist for account $cpUser !\n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "Generating file list for $path ... \n"; system("find $path -type f > $fileParseLoc"); &savePermOwner("$fileParseLoc","files","$cpUser","$path"); } sub findDirs { die "findDirs() requires cpanel user as arg1 and wwwRoot as arg2\n" if (scalar(@_) != 2); my $cpUser = $_[0]; my $path = $_[1]; my $saveDir = "/root/datastore_suphpfix"; if (! -d "$saveDir") { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "NOTICE: $saveDir not found, creating it... \n"; print color 'reset'; system("mkdir $saveDir"); } my $dirParseLoc = "$saveDir/suphpfixDirStore.txt"; if ( $path eq "" || !defined($path) ) { print color 'red'; print "ERROR: Was unable to find the WWW_Root for $cpUser !\n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'red'; exit(1); } if ( ! -e "$path") { print color 'red'; print "ERROR: WWW_Root $path for $cpUser doesn't exist !\n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "Generating Directory list for $path ... \n"; system("find $path -type d > $dirParseLoc"); &savePermOwner("$dirParseLoc","dirs","$cpUser","$path"); } sub parseAndRestore { print color 'red'; die "parseAndRestore() requires at least one argument. Arg1 (required) RestoreType (values: all or singleAccnt). Arg2 (required only if arg1 is singleAccnt) specific cPanel user to restore. \n" if (scalar(@_) < 1); print color 'reset'; my $restoreType = $_[0]; my $accntToRestore = $_[1]; my $passedArg3 = $_[2]; my $cpUser=$accntToRestore; my ($jsonFile, $jsonDir); my $json = new JSON; if ( $restoreType eq "singleAccnt" ) { my $fileStoreLocation = "/root/datastore_suphpfix/datastore_file_suphpfix.$accntToRestore.JSON"; my $dirStoreLocation = "/root/datastore_suphpfix/datastore_dir_suphpfix.$accntToRestore.JSON"; if ( ! -e $fileStoreLocation ) { print color 'red'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "ERROR: Datastore file ($fileStoreLocation) for $accntToRestore doesn't exist. Please take another snapshot or restore the datastore files from a previous snapshot.\n"; print color 'reset'; exit(1); } if ( ! -e $dirStoreLocation ) { print color 'red'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "ERROR: Datastore file ($dirStoreLocation) for $accntToRestore doesn't exist. Please take another snapshot or restore the datastore files from a previous snapshot.\n"; print color 'reset'; exit(1); } if ( -z $fileStoreLocation ) { print color 'red'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "ERROR: Datastore file ($fileStoreLocation) for $accntToRestore exists but is empty. Please take another snapshot or restore the datastore files from a previous snapshot.\n"; print color 'reset'; exit(1); } if ( -z $dirStoreLocation ) { print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "ERROR: Datastore file ($dirStoreLocation) for $accntToRestore exists but is empty. Please take another snapshot or restore the datastore files from a previous snapshot. \n"; exit(1); } $jsonFile = `cat $fileStoreLocation`; $jsonDir = `cat $dirStoreLocation`; my ($jsonFileToParse, $jsonDirToParse); eval{ $jsonFileToParse = $json->allow_nonref->utf8->relaxed->decode($jsonFile); }; if ($@) { print color 'red'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "ERROR: Datastore file ($fileStoreLocation) is not valid JSON. Please take another snapshot or restore the datastore files from a previous snapshot. \n"; print color 'reset'; exit(1); }; eval{ $jsonDirToParse = $json->allow_nonref->utf8->relaxed->decode($jsonDir); }; if ($@) { print color 'red'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "ERROR: Datastore file ($dirStoreLocation) is not valid JSON. Please take another snapshot or restore the datastore files from a previous snapshot. \n"; print color 'reset'; exit(1); }; my (@fileInodeArray,@fileDocRootArray,@fileArray,@cpuserFileArray,@permFileArray,@ownerFileArray,@groupFileArray); my (@dirInodeArray,@dirDocRootArray,@dirArray,@cpuserDirArray,@permDirArray,@ownerDirArray,@groupDirArray); ###FILE for my $inode( @{$jsonFileToParse->{acct}->{inode}} ) { push(@fileInodeArray, $inode); }; for my $docRoot( @{$jsonFileToParse->{acct}->{docRoot}} ) { push(@fileDocRootArray, $docRoot); }; for my $cpuser( @{$jsonFileToParse->{acct}->{cpanelUser}} ) { push(@cpuserFileArray, $cpuser); }; for my $file( @{$jsonFileToParse->{acct}->{file}} ) { push(@fileArray, $file); }; for my $perm( @{$jsonFileToParse->{acct}->{perm}} ) { push(@permFileArray, $perm); }; for my $owner( @{$jsonFileToParse->{acct}->{owner}} ) { push(@ownerFileArray, $owner); }; for my $group( @{$jsonFileToParse->{acct}->{group}} ) { push(@groupFileArray, $group); }; ###DIR for my $inode( @{$jsonDirToParse->{acct}->{inode}} ) { push(@dirInodeArray, $inode); }; for my $docRoot( @{$jsonDirToParse->{acct}->{docRoot}} ) { push(@dirDocRootArray, $docRoot); }; for my $cpuser( @{$jsonDirToParse->{acct}->{cpanelUser}} ) { push(@cpuserDirArray, $cpuser); }; for my $file( @{$jsonDirToParse->{acct}->{file}} ) { push(@dirArray, $file); }; for my $perm( @{$jsonDirToParse->{acct}->{perm}} ) { push(@permDirArray, $perm); }; for my $owner( @{$jsonDirToParse->{acct}->{owner}} ) { push(@ownerDirArray, $owner); }; for my $group( @{$jsonDirToParse->{acct}->{group}} ) { push(@groupDirArray, $group); }; print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "Reverting suPHP fixes for $accntToRestore\n"; print color 'reset'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "Reverting file permissions/ownerships for $accntToRestore ...\n"; my $fileSlot = "0"; for my $cpuser( @{$jsonFileToParse->{acct}->{cpanelUser}} ) { if ( $passedArg3 eq "verify" ) { if ( defined(@fileInodeArray[0]) ) { print color 'yellow'; print "-----------------------------------------------\n"; print color 'reset'; if ( ! -e @fileArray[$fileSlot] ) { my $fileVerifyLocation=`find @fileDocRootArray[0] -xdev -inum @fileInodeArray[$fileSlot]`; if ( $fileVerifyLocation ne "" && defined($fileVerifyLocation) ) { print color 'green'; print "{FILE} Proposed chmod: "; print color 'red'; print "find @fileDocRootArray[0] -xdev -inum @fileInodeArray[$fileSlot] -exec chmod @permFileArray[$fileSlot] {} \\;"; print color 'reset'; print "\n"; print color 'green'; print "{FILE} Proposed chown: "; print color 'red'; print "find @fileDocRootArray[0] -xdev -inum @fileInodeArray[$fileSlot] -exec chown @ownerFileArray[$fileSlot]:@groupFileArray[$fileSlot] {} \\;"; print color 'reset'; print "\n"; } else { print color 'yellow'; print "WARNING: Inode @fileInodeArray[$fileSlot] no longer exists in $cpUser public_html; skipping...\n"; print color 'reset'; } } elsif ( -e @fileArray[$fileSlot] ) { print color 'green'; print "{FILE} Proposed chmod: "; print color 'red'; print "chmod @permFileArray[$fileSlot] @fileArray[$fileSlot] "; print color 'reset'; print "\n"; print color 'green'; print "{FILE} Proposed chown: "; print color 'red'; print "chown @ownerFileArray[$fileSlot]:@groupFileArray[$fileSlot] @fileArray[$fileSlot]"; print color 'reset'; print "\n"; } print color 'yellow'; print "-----------------------------------------------\n"; print color 'reset'; } else { print color 'yellow'; print "-----------------------------------------------\n"; print color 'reset'; print "{FILE} file number: $fileSlot for $cpuser...\n"; #Deal with spaces and special chars #@fileArray[$fileSlot] =~ s/\s/?/g; @fileArray[$fileSlot] =~ s/ /\\ /g; @fileArray[$fileSlot] =~ s/'/\\'/g; @fileArray[$fileSlot] =~ s/&/\\&/g; @fileArray[$fileSlot] =~ s/;/\\;/g; @fileArray[$fileSlot] =~ s/\$/\\\$/g; @fileArray[$fileSlot] =~ s/[(]/\\(/g; @fileArray[$fileSlot] =~ s/[)]/\\)/g; @fileArray[$fileSlot] =~ s/[|]/\\|/g; @fileArray[$fileSlot] =~ s/"/\\"/g; @fileArray[$fileSlot] =~ s/!/\\!/g; @fileArray[$fileSlot] =~ s//\\>/g; @fileArray[$fileSlot] =~ s/`/\\`/g; ## If a line has \n or \ in it (and has no spaces), we need to wrap the file path in ' ' $_ = @fileArray[$fileSlot]; my $blanks += tr/ / /; if ( @fileArray[$fileSlot] =~/\\n/ ) { if ( $blanks == 0 ) { @fileArray[$fileSlot] = '\'' . @fileArray[$fileSlot] . '\''; } } print color 'green'; print "{FILE} Proposed chmod: "; print color 'red'; print "chmod @permFileArray[$fileSlot] @fileArray[$fileSlot]"; print color 'reset'; print "\n"; print color 'green'; print "{FILE} Proposed chown: "; print color 'red'; print "chown @ownerFileArray[$fileSlot]:@groupFileArray[$fileSlot] @fileArray[$fileSlot]"; print color 'reset'; print "\n"; print color 'yellow'; print "-----------------------------------------------\n"; print color 'reset'; } } else { if ( -e @fileArray[$fileSlot] ) { if ( is_immutable(@fileArray[$fileSlot]) ) { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "WARNING: @fileArray[$fileSlot] is immutable, skipping...\n"; print color 'reset'; } else { #Deal with spaces and special chars #@fileArray[$fileSlot] =~ s/\s/?/g; @fileArray[$fileSlot] =~ s/ /\\ /g; @fileArray[$fileSlot] =~ s/'/\\'/g; @fileArray[$fileSlot] =~ s/&/\\&/g; @fileArray[$fileSlot] =~ s/;/\\;/g; @fileArray[$fileSlot] =~ s/\$/\\\$/g; @fileArray[$fileSlot] =~ s/[(]/\\(/g; @fileArray[$fileSlot] =~ s/[)]/\\)/g; @fileArray[$fileSlot] =~ s/[|]/\\|/g; @fileArray[$fileSlot] =~ s/"/\\"/g; @fileArray[$fileSlot] =~ s/!/\\!/g; @fileArray[$fileSlot] =~ s//\\>/g; @fileArray[$fileSlot] =~ s/`/\\`/g; ## If a line has \n or \ in it (and has no spaces), we need to wrap the file path in ' ' $_ = @fileArray[$fileSlot]; my $blanks += tr/ / /; if ( @fileArray[$fileSlot] =~/\\n/ ) { if ( $blanks == 0 ) { @fileArray[$fileSlot] = '\'' . @fileArray[$fileSlot] . '\''; } } system("chmod @permFileArray[$fileSlot] @fileArray[$fileSlot] "); ## If there's an error in the chmod, see if we made an inode save state for this account. if ( $? != 0 ) { if ( defined(@fileInodeArray[0]) ) { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "WARNING: Error parsing file for chmod! Parsing by inode directly... "; print color 'reset'; system("find @fileDocRootArray[0] -xdev -inum @fileInodeArray[$fileSlot] -exec chmod @permFileArray[$fileSlot] {} \\;"); if ( $? == 0 ) { print color 'green'; print "success\n"; print color 'reset'; } elsif ( $? != 0 ) { print color 'red'; print "failed!\n"; print "ERROR: Unable to parse inode @fileInodeArray[$fileSlot] with command: find @fileDocRootArray[0] -xdev -inum @fileInodeArray[$fileSlot] -exec chmod @permFileArray[$fileSlot] {} \\;\n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } } ## The file no longer exists, and we did not make an inode save state for this account. elsif ( ! -e @fileArray[$fileSlot] ) { print color 'yellow'; print "WARNING: @fileArray[$fileSlot] no longer exists, skipping...\n"; print color 'reset'; } ## The file does still exist, but there was some other error in the chmod on it. else { print color 'red'; print "Error in chmod command: chmod @permFileArray[$fileSlot] @fileArray[$fileSlot] \n"; print "Got Error: $!\n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } } system("chown @ownerFileArray[$fileSlot]:@groupFileArray[$fileSlot] @fileArray[$fileSlot] "); if ( $? != 0 ) { if ( defined(@fileInodeArray[0]) ) { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "WARNING: Error parsing file for chown! Parsing by inode directly... "; print color 'reset'; system("find @fileDocRootArray[0] -xdev -inum @fileInodeArray[$fileSlot] -exec chmod @permFileArray[$fileSlot] {} \\;"); if ( $? == 0 ) { print color 'green'; print "success\n"; print color 'reset'; } elsif ( $? != 0 ) { print color 'red'; print "failed!\n"; print "ERROR: Unable to parse inode @fileInodeArray[$fileSlot] with command: find @fileDocRootArray[0] -xdev -inum @fileInodeArray[$fileSlot] -exec chmod @permFileArray[$fileSlot] {} \\;\n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } } ## The file no longer exists, and we did not make an inode save state for this account. elsif ( ! -e @fileArray[$fileSlot] ) { print color 'yellow'; print "WARNING: @fileArray[$fileSlot] no longer exists, skipping...\n"; print color 'reset'; } else { print color 'red'; print "Error in chown command: chown @ownerFileArray[$fileSlot].@groupFileArray[$fileSlot] @fileArray[$fileSlot] \n"; print "Got Error: $!\n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } } } } elsif ( ! -e @fileArray[$fileSlot] ) { if ( defined(@fileInodeArray[0]) ) { my $fileVerifyLocation=`find @fileDocRootArray[0] -xdev -inum @fileInodeArray[$fileSlot]`; if ( $fileVerifyLocation ne "" && defined($fileVerifyLocation) ) { system("find @fileDocRootArray[0] -xdev -inum @fileInodeArray[$fileSlot] -exec chmod @permFileArray[$fileSlot] {} \\;"); system("find @fileDocRootArray[0] -xdev -inum @fileInodeArray[$fileSlot] -exec chown @ownerFileArray[$fileSlot]:@groupFileArray[$fileSlot] {} \\;"); } else { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "WARNING: Inode @fileInodeArray[$fileSlot] no longer exists in $cpUser public_html; skipping... \n"; print color 'reset'; } } else { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "WARNING: @fileArray[$fileSlot] no longer exists, skipping... \n"; print color 'reset'; } } } $fileSlot++; }; print color 'reset'; sleep(1); print color 'green'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "* Task complete.\n"; print color 'reset'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "Reverting directory permissions/ownerships for $accntToRestore ... \n"; my $dirSlot = "0"; for my $cpuser( @{$jsonDirToParse->{acct}->{cpanelUser}} ) { if ( $passedArg3 eq "verify" ) { if ( defined(@dirInodeArray[0]) ) { print color 'yellow'; print "-----------------------------------------------\n"; print color 'reset'; if ( ! -e @dirArray[$dirSlot] ) { my $dirVerifyLocation=`find @dirDocRootArray[0] -xdev -inum @dirInodeArray[$dirSlot]`; if ( $dirVerifyLocation ne "" && defined($dirVerifyLocation) ) { print color 'green'; print "{DIR} Proposed chmod: "; print color 'red'; print "find @dirDocRootArray[0] -xdev -inum @dirInodeArray[$dirSlot] -exec chmod @permDirArray[$dirSlot] {} \\;"; print color 'reset'; print "\n"; print color 'green'; print "{DIR} Proposed chown: "; print color 'red'; print "find @dirDocRootArray[0] -xdev -inum @dirInodeArray[$dirSlot] -exec chown @ownerDirArray[$dirSlot]:@groupDirArray[$dirSlot] {} \\;"; print color 'reset'; print "\n"; } else { print color 'yellow'; print "WARNING: Inode @dirInodeArray[$dirSlot] no longer exists in $cpUser public_html; skipping...\n"; print color 'reset'; } } elsif ( -e @dirArray[$dirSlot] ) { print color 'green'; print "{DIR} Proposed chmod: "; print color 'red'; print "chmod @permDirArray[$dirSlot] @dirArray[$dirSlot] "; print color 'reset'; print "\n"; print color 'green'; print "{DIR} Proposed chown: "; print color 'red'; print "chown @ownerDirArray[$dirSlot]:@groupDirArray[$dirSlot] @dirArray[$dirSlot]"; print color 'reset'; print "\n"; } print color 'yellow'; print "-----------------------------------------------\n"; print color 'reset'; } else { print color 'yellow'; print "-----------------------------------------------\n"; print color 'reset'; print "{DIR} directory number: $dirSlot for $cpuser...\n"; #Deal with spaces and special chars @dirArray[$dirSlot] =~ s/ /\\ /g; #@dirArray[$dirSlot] =~ s/\s/?/g; @dirArray[$dirSlot] =~ s/'/\\'/g; @dirArray[$dirSlot] =~ s/&/\\&/g; @dirArray[$dirSlot] =~ s/;/\\;/g; @dirArray[$dirSlot] =~ s/\$/\\\$/g; @dirArray[$dirSlot] =~ s/[(]/\\(/g; @dirArray[$dirSlot] =~ s/[)]/\\)/g; @dirArray[$dirSlot] =~ s/[|]/\\|/g; @dirArray[$dirSlot] =~ s/"/\\"/g; @dirArray[$dirSlot] =~ s/!/\\!/g; @dirArray[$dirSlot] =~ s//\\>/g; @dirArray[$dirSlot] =~ s/`/\\`/g; ## If a line has \n or \ in it (and has no spaces), we need to wrap the file path in ' ' $_ = @dirArray[$dirSlot]; my $blanks += tr/ / /; if ( @dirArray[$dirSlot] =~/\\n/ || @dirArray[$dirSlot] =~/\\/ ) { if ( $blanks == 0 ) { @dirArray[$dirSlot] = '\'' . @dirArray[$dirSlot] . '\''; } } print color 'green'; print "{DIR} Proposed chmod: "; print color 'red'; print "chmod @permDirArray[$dirSlot] @dirArray[$dirSlot]"; print color 'reset'; print "\n"; print color 'green'; print "{DIR} Proposed chown: "; print color 'red'; print "chown @ownerDirArray[$dirSlot]:@groupDirArray[$dirSlot] @dirArray[$dirSlot]"; print color 'reset'; print "\n"; print color 'yellow'; print "-----------------------------------------------\n"; print color 'reset'; } } else { if ( -e @dirArray[$dirSlot] ) { if ( is_immutable(@dirArray[$dirSlot]) ) { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "WARNING: @dirArray[$dirSlot] is immutable, skipping...\n"; print color 'reset'; } else { #Deal with spaces and special chars @dirArray[$dirSlot] =~ s/ /\\ /g; #@dirArray[$dirSlot] =~ s/\s/?/g; @dirArray[$dirSlot] =~ s/'/\\'/g; @dirArray[$dirSlot] =~ s/&/\\&/g; @dirArray[$dirSlot] =~ s/;/\\;/g; @dirArray[$dirSlot] =~ s/\$/\\\$/g; @dirArray[$dirSlot] =~ s/[(]/\\(/g; @dirArray[$dirSlot] =~ s/[)]/\\)/g; @dirArray[$dirSlot] =~ s/[|]/\\|/g; @dirArray[$dirSlot] =~ s/"/\\"/g; @dirArray[$dirSlot] =~ s/!/\\!/g; @dirArray[$dirSlot] =~ s//\\>/g; @dirArray[$dirSlot] =~ s/`/\\`/g; ## If a line has \n or \ in it (and has no spaces), we need to wrap the file path in ' ' $_ = @dirArray[$dirSlot]; my $blanks += tr/ / /; if ( @dirArray[$dirSlot] =~/\\n/ || @dirArray[$dirSlot] =~/\\/ ) { if ( $blanks == 0 ) { @dirArray[$dirSlot] = '\'' . @dirArray[$dirSlot] . '\''; } } system("chmod @permDirArray[$dirSlot] @dirArray[$dirSlot] &> /dev/null"); if ( $? != 0 ) { if ( defined(@dirInodeArray[0]) ) { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "WARNING: Error parsing directory for chmod! Parsing by inode directly... "; print color 'reset'; system("find @dirDocRootArray[0] -xdev -inum @dirInodeArray[$dirSlot] -exec chmod @permDirArray[$dirSlot] {} \\;"); if ( $? == 0 ) { print color 'green'; print "success\n"; print color 'reset'; } elsif ( $? != 0 ) { print color 'red'; print "failed!\n"; print "ERROR: Unable to parse inode @dirInodeArray[$dirSlot] with command: find @dirDocRootArray[0] -xdev -inum @dirInodeArray[$dirSlot] -exec chmod @permDirArray[$dirSlot] {} \\;\n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } } ## The file no longer exists, and we did not make an inode save state for this account. elsif ( ! -e @dirArray[$dirSlot] ) { print color 'yellow'; print "WARNING: @dirArray[$dirSlot] no longer exists, skipping...\n"; print color 'reset'; } else { print color 'red'; print "Error in chmod command: chmod @permDirArray[$dirSlot] @dirArray[$dirSlot] \n"; print "Got Error: $!\n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } } system("chown @ownerDirArray[$dirSlot]:@groupDirArray[$dirSlot] @dirArray[$dirSlot] &> /dev/null"); if ( $? != 0 ) { if ( defined(@dirInodeArray[0]) ) { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "WARNING: Error parsing directory for chown! Parsing by inode directly... "; print color 'reset'; system("find @dirDocRootArray[0] -xdev -inum @dirInodeArray[$dirSlot] -exec chown @ownerDirArray[$dirSlot]:@groupDirArray[$dirSlot] {} \\;"); if ( $? == 0 ) { print color 'green'; print "success\n"; print color 'reset'; } elsif ( $? != 0 ) { print color 'red'; print "failed!\n"; print "ERROR: Unable to parse inode @dirInodeArray[$dirSlot] with command: find @dirDocRootArray[0] -xdev -inum @dirInodeArray[$dirSlot] -exec chmod @permDirArray[$dirSlot] {} \\;\n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } } ## The file no longer exists, and we did not make an inode save state for this account. elsif ( ! -e @dirArray[$dirSlot] ) { print color 'yellow'; print "WARNING: @dirArray[$dirSlot] no longer exists, skipping...\n"; print color 'reset'; } else { print color 'red'; print "Error in chown command: chown @ownerDirArray[$dirSlot]:@groupDirArray[$dirSlot] @dirArray[$dirSlot] \n"; print "Got Error: $!\n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } } } } elsif ( ! -e @dirArray[$dirSlot] ) { if ( defined(@dirInodeArray[0]) ) { my $dirVerifyLocation=`find @dirDocRootArray[0] -xdev -inum @dirInodeArray[$dirSlot]`; if ( $dirVerifyLocation ne "" && defined($dirVerifyLocation) ) { system("find @dirDocRootArray[0] -xdev -inum @dirInodeArray[$dirSlot] -exec chmod @permDirArray[$dirSlot] {} \\;"); system("find @dirDocRootArray[0] -xdev -inum @dirInodeArray[$dirSlot] -exec chown @ownerDirArray[$dirSlot]:@groupDirArray[$dirSlot] {} \\;"); } else { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "WARNING: Inode @dirInodeArray[$dirSlot] no longer exists in $cpUser public_html; skipping... \n"; print color 'reset'; } } else { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "WARNING: @dirArray[$dirSlot] no longer exists, skipping... \n"; print color 'reset'; } } } $dirSlot++; }; print color 'green'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "* Task complete.\n"; print color 'reset'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "Uncommenting php tweaks for $accntToRestore ...\n"; system("find @dirArray[$dirSlot] -name .htaccess -exec sed -i 's/^#php_/php_/g' {} \\\;"); print color 'green'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "* Task complete.\n"; print color 'reset'; print color 'bold green'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "suPHP fixes reverted for $accntToRestore!\n"; print color 'reset'; } elsif ( $restoreType eq "all" ) { print color 'red'; open FD, "/root/datastore_suphpfix/backedupUserList.all" or die "ERROR: Could not open datastore file (/root/datastore_suphpfix/backedupUserList.all): $! . For more details see --help\n"; my @backedUpCPusers = ; close FD; system("QUERY_STRING=\\\"regen=1\\\" /usr/local/cpanel/whostmgr/bin/whostmgr ./setrhash &> /dev/null"); my $hashfile = '/root/.accesshash'; if (! -e $hashfile) { print color 'red'; print "ERROR: Failed to automatically generate hash! Please try logging into WHM and click `Setup Remote Access Key` and then re-run this script. \n"; print color 'reset'; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; exit(1); } my $hash = `cat /root/.accesshash`; $hash =~ s/\n//g; my $auth = "WHM root:" . $hash; my $restoreCounter = "0"; for my $user ( @backedUpCPusers ) { chomp($user); $cpUser=$user; print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "Reverting suPHP fixes for $user ... \n"; print color 'reset'; ### Let's make sure the cPanel account to be restored still exists on the server. my $json = new JSON; my $ua = LWP::UserAgent->new; $ua->agent("suphpfix"); $ua->env_proxy(); my $request = HTTP::Request->new(POST => "http://127.0.0.1:2086/json-api/listaccts"); $request->header( Authorization => $auth ); $request->content_type('application/jsonrequest'); $request->content("{}"); my $response = $ua->request($request); my $listRaw = $response->content; my $list = encode("UTF-8", $listRaw); my $accntList = $json->allow_nonref->utf8->relaxed->decode($list); my $validUser; for my $userCheck( @{$accntList->{acct}} ) { if ( $userCheck->{user} eq $user ) { $validUser = "true"; } }; if ( $validUser ne "true" ) { print color 'red'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "ERROR: $user is no longer a valid cPanel user on this system. You can either take another snapshot of all accounts, or you can restore snapshots for backed up cPanel accounts that are still on the server individually with --restore-state cPanelUser. For more details, see --help \n"; print color 'reset'; exit(1); } my $fileStoreLocation = "/root/datastore_suphpfix/datastore_file_suphpfix.$user.JSON"; my $dirStoreLocation = "/root/datastore_suphpfix/datastore_dir_suphpfix.$user.JSON"; if ( ! -e $fileStoreLocation ) { print color 'red'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "ERROR: Datastore file ($fileStoreLocation) for $user doesn't exist. Please take another snapshot or restore the datastore files from a previous snapshot.\n"; print color 'reset'; exit(1); } if ( ! -e $dirStoreLocation ) { print color 'red'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "ERROR: Datastore file ($dirStoreLocation) for $user doesn't exist. Please take another snapshot or restore the datastore files from a previous snapshot.\n"; print color 'reset'; exit(1); } if ( -z $fileStoreLocation ) { print color 'red'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "ERROR: Datastore file ($fileStoreLocation) for $user exists but is empty. Please take another snapshot or restore the datastore files from a previous snapshot.\n"; print color 'reset'; exit(1); } if ( -z $dirStoreLocation ) { print color 'red'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "ERROR: Datastore file ($dirStoreLocation) for $user exists but is empty. Please take another snapshot or restore the datastore files from a previous snapshot. \n"; print color 'reset'; exit(1); } $jsonFile = `cat $fileStoreLocation`; $jsonDir = `cat $dirStoreLocation`; my ($jsonFileToParse, $jsonDirToParse); eval{ $jsonFileToParse = $json->allow_nonref->utf8->relaxed->decode($jsonFile); }; if ($@) { print color 'red'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "ERROR: Datastore file ($fileStoreLocation) is not valid JSON. Please take another snapshot or restore the datastore files from a previous snapshot. \n"; print color 'reset'; exit(1); }; eval{ $jsonDirToParse = $json->allow_nonref->utf8->relaxed->decode($jsonDir); }; if ($@) { print color 'red'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "ERROR: Datastore file ($dirStoreLocation) is not valid JSON. Please take another snapshot or restore the datastore files from a previous snapshot. \n"; print color 'reset'; exit(1); }; my (@fileArray,@fileDocRootArray,@fileInodeArray,@cpuserFileArray,@permFileArray,@ownerFileArray,@groupFileArray); my (@dirArray,@dirDocRootArray,@dirInodeArray,@cpuserDirArray,@permDirArray,@ownerDirArray,@groupDirArray); ###FILE for my $cpuser( @{$jsonFileToParse->{acct}->{cpanelUser}} ) { push(@cpuserFileArray, $cpuser); }; for my $docRoot( @{$jsonFileToParse->{acct}->{docRoot}} ) { push(@fileDocRootArray, $docRoot); }; for my $file( @{$jsonFileToParse->{acct}->{file}} ) { push(@fileArray, $file); }; for my $perm( @{$jsonFileToParse->{acct}->{perm}} ) { push(@permFileArray, $perm); }; for my $owner( @{$jsonFileToParse->{acct}->{owner}} ) { push(@ownerFileArray, $owner); }; for my $group( @{$jsonFileToParse->{acct}->{group}} ) { push(@groupFileArray, $group); }; for my $inode( @{$jsonFileToParse->{acct}->{inode}} ) { push(@fileInodeArray, $inode); }; ###DIR for my $inode( @{$jsonDirToParse->{acct}->{inode}} ) { push(@dirInodeArray, $inode); }; for my $docRoot( @{$jsonFileToParse->{acct}->{docRoot}} ) { push(@dirDocRootArray, $docRoot); }; for my $cpuser( @{$jsonDirToParse->{acct}->{cpanelUser}} ) { push(@cpuserDirArray, $cpuser); }; for my $file( @{$jsonDirToParse->{acct}->{file}} ) { push(@dirArray, $file); }; for my $perm( @{$jsonDirToParse->{acct}->{perm}} ) { push(@permDirArray, $perm); }; for my $owner( @{$jsonDirToParse->{acct}->{owner}} ) { push(@ownerDirArray, $owner); }; for my $group( @{$jsonDirToParse->{acct}->{group}} ) { push(@groupDirArray, $group); }; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "Reverting file permissions/ownerships for $user ... \n"; my $fileSlot = "0"; for my $cpuser( @{$jsonFileToParse->{acct}->{cpanelUser}} ) { if ( $passedArg3 eq "verify" ) { if ( defined(@fileInodeArray[0]) ) { print color 'yellow'; print "-----------------------------------------------\n"; print color 'reset'; if ( ! -e @fileArray[$fileSlot] ) { my $fileVerifyLocation=`find @fileDocRootArray[0] -xdev -inum @fileInodeArray[$fileSlot]`; if ( $fileVerifyLocation ne "" && defined($fileVerifyLocation) ) { print color 'green'; print "{FILE} Proposed chmod: "; print color 'red'; print "find @fileDocRootArray[0] -xdev -inum @fileInodeArray[$fileSlot] -exec chmod @permFileArray[$fileSlot] {} \\;"; print color 'reset'; print "\n"; print color 'green'; print "{FILE} Proposed chown: "; print color 'red'; print "find @fileDocRootArray[0] -xdev -inum @fileInodeArray[$fileSlot] -exec chown @ownerFileArray[$fileSlot]:@groupFileArray[$fileSlot] {} \\;"; print color 'reset'; print "\n"; } else { print color 'yellow'; print "WARNING: Inode @fileInodeArray[$fileSlot] no longer exists in $cpUser public_html; skipping...\n"; print color 'reset'; } } elsif ( -e @fileArray[$fileSlot] ) { print color 'green'; print "{FILE} Proposed chmod: "; print color 'red'; print "chmod @permFileArray[$fileSlot] @fileArray[$fileSlot] "; print color 'reset'; print "\n"; print color 'green'; print "{FILE} Proposed chown: "; print color 'red'; print "chown @ownerFileArray[$fileSlot]:@groupFileArray[$fileSlot] @fileArray[$fileSlot]"; print color 'reset'; print "\n"; } print color 'yellow'; print "-----------------------------------------------\n"; print color 'reset'; } else { print color 'yellow'; print "-----------------------------------------------\n"; print color 'reset'; print "{FILE} file number: $fileSlot for $cpuser...\n"; #Deal with spaces and special chars @fileArray[$fileSlot] =~ s/ /\\ /g; #@fileArray[$fileSlot] =~ s/\s/?/g; @fileArray[$fileSlot] =~ s/'/\\'/g; @fileArray[$fileSlot] =~ s/&/\\&/g; @fileArray[$fileSlot] =~ s/;/\\;/g; @fileArray[$fileSlot] =~ s/\$/\\\$/g; @fileArray[$fileSlot] =~ s/[(]/\\(/g; @fileArray[$fileSlot] =~ s/[)]/\\)/g; @fileArray[$fileSlot] =~ s/[|]/\\|/g; @fileArray[$fileSlot] =~ s/"/\\"/g; @fileArray[$fileSlot] =~ s/!/\\!/g; @fileArray[$fileSlot] =~ s//\\>/g; @fileArray[$fileSlot] =~ s/`/\\`/g; ## If a line has \n or \ in it (and has no spaces), we need to wrap the file path in ' ' $_ = @fileArray[$fileSlot]; my $blanks += tr/ / /; if ( @fileArray[$fileSlot] =~/\\n/ ) { if ( $blanks == 0 ) { @fileArray[$fileSlot] = '\'' . @fileArray[$fileSlot] . '\''; } } print color 'green'; print "{FILE} Proposed chmod: "; print color 'red'; print "chmod @permFileArray[$fileSlot] @fileArray[$fileSlot]"; print color 'reset'; print "\n"; print color 'green'; print "{FILE} Proposed chown: "; print color 'red'; print "chown @ownerFileArray[$fileSlot]:@groupFileArray[$fileSlot] @fileArray[$fileSlot]"; print color 'reset'; print "\n"; print color 'yellow'; print "-----------------------------------------------\n"; print color 'reset'; } } else { if ( -e @fileArray[$fileSlot] ) { if ( is_immutable(@fileArray[$fileSlot]) ) { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] ","WARNING: @fileArray[$fileSlot] is immutable, skipping...\n"; print color 'reset'; } else { #Deal with spaces and special chars @fileArray[$fileSlot] =~ s/ /\\ /g; #@fileArray[$fileSlot] =~ s/\s/?/g; @fileArray[$fileSlot] =~ s/'/\\'/g; @fileArray[$fileSlot] =~ s/&/\\&/g; @fileArray[$fileSlot] =~ s/;/\\;/g; @fileArray[$fileSlot] =~ s/\$/\\\$/g; @fileArray[$fileSlot] =~ s/[(]/\\(/g; @fileArray[$fileSlot] =~ s/[)]/\\)/g; @fileArray[$fileSlot] =~ s/[|]/\\|/g; @fileArray[$fileSlot] =~ s/"/\\"/g; @fileArray[$fileSlot] =~ s/!/\\!/g; @fileArray[$fileSlot] =~ s//\\>/g; @fileArray[$fileSlot] =~ s/`/\\`/g; ## If a line has \n or \ in it (and has no spaces), we need to wrap the file path in ' ' $_ = @fileArray[$fileSlot]; my $blanks += tr/ / /; if ( @fileArray[$fileSlot] =~/\\n/ ) { if ( $blanks == 0 ) { @fileArray[$fileSlot] = '\'' . @fileArray[$fileSlot] . '\''; } } system("chmod @permFileArray[$fileSlot] @fileArray[$fileSlot] &> /dev/null"); if ( $? != 0 ) { if ( defined(@fileInodeArray[0]) ) { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "WARNING: Error parsing file for chmod! Parsing by inode directly... "; print color 'reset'; system("find @fileDocRootArray[0] -xdev -inum @fileInodeArray[$fileSlot] -exec chmod @permFileArray[$fileSlot] {} \\;"); if ( $? == 0 ) { print color 'green'; print "success\n"; print color 'reset'; } elsif ( $? != 0 ) { print color 'red'; print "failed!\n"; print "ERROR: Unable to parse inode @fileInodeArray[$fileSlot] with command: find @fileDocRootArray[0] -xdev -inum @fileInodeArray[$fileSlot] -exec chmod @permFileArray[$fileSlot] {} \\;\n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } } ## The file no longer exists, and we did not make an inode save state for this account. elsif ( ! -e @fileArray[$fileSlot] ) { print color 'yellow'; print "WARNING: @fileArray[$fileSlot] no longer exists, skipping...\n"; print color 'reset'; } else { print color 'red'; print "Error in chmod command: chmod @permFileArray[$fileSlot] @fileArray[$fileSlot] \n"; print "Got Error: $!\n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } } system("chown @ownerFileArray[$fileSlot]:@groupFileArray[$fileSlot] @fileArray[$fileSlot] &> /dev/null"); if ( $? != 0 ) { if ( defined(@fileInodeArray[0]) ) { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "WARNING: Error parsing file for chown! Parsing by inode directly... "; print color 'reset'; system("find @fileDocRootArray[0] -xdev -inum @fileInodeArray[$fileSlot] -exec chown @ownerFileArray[$fileSlot]:@groupFileArray[$fileSlot] {} \\;"); if ( $? == 0 ) { print color 'green'; print "success\n"; print color 'reset'; } elsif ( $? != 0 ) { print color 'red'; print "failed!\n"; print "ERROR: Unable to parse inode @fileInodeArray[$fileSlot] with command: find @fileDocRootArray[0] -xdev -inum @fileInodeArray[$fileSlot] -exec chmod @permFileArray[$fileSlot] {} \\;\n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } } ## The file no longer exists, and we did not make an inode save state for this account. elsif ( ! -e @fileArray[$fileSlot] ) { print color 'yellow'; print "WARNING: @fileArray[$fileSlot] no longer exists, skipping...\n"; print color 'reset'; } else { print color 'red'; print "Error in chown command: chown @ownerFileArray[$fileSlot]:@groupFileArray[$fileSlot] @fileArray[$fileSlot] \n"; print "Got Error: $!\n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } } } } elsif ( ! -e @fileArray[$fileSlot] ) { if ( defined(@fileInodeArray[0]) ) { my $fileVerifyLocation=`find @fileDocRootArray[0] -xdev -inum @fileInodeArray[$fileSlot]`; if ( $fileVerifyLocation ne "" && defined($fileVerifyLocation) ) { system("find @fileDocRootArray[0] -xdev -inum @fileInodeArray[$fileSlot] -exec chmod @permFileArray[$fileSlot] {} \\;"); system("find @fileDocRootArray[0] -xdev -inum @fileInodeArray[$fileSlot] -exec chown @ownerFileArray[$fileSlot]:@groupFileArray[$fileSlot] {} \\;"); } else { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "WARNING: Inode @fileInodeArray[$fileSlot] no longer exists in $cpUser public_html; skipping... \n"; print color 'reset'; } } else { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "WARNING: @fileArray[$fileSlot] no longer exists, skipping... \n"; print color 'reset'; } } } $fileSlot++; }; print color 'reset'; sleep(1); print color 'green'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "* Task complete.\n"; print color 'reset'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "Reverting directory permissions/ownerships for $user ... \n"; my $dirSlot = "0"; for my $cpuser( @{$jsonDirToParse->{acct}->{cpanelUser}} ) { if ( $passedArg3 eq "verify" ) { if ( defined(@dirInodeArray[0]) ) { print color 'yellow'; print "-----------------------------------------------\n"; print color 'reset'; if ( ! -e @dirArray[$dirSlot] ) { my $dirVerifyLocation=`find @dirDocRootArray[0] -xdev -inum @dirInodeArray[$dirSlot]`; if ( $dirVerifyLocation ne "" && defined($dirVerifyLocation) ) { print color 'green'; print "{DIR} Proposed chmod: "; print color 'red'; print "find @dirDocRootArray[0] -xdev -inum @dirInodeArray[$dirSlot] -exec chmod @permDirArray[$dirSlot] {} \\;"; print color 'reset'; print "\n"; print color 'green'; print "{DIR} Proposed chown: "; print color 'red'; print "find @dirDocRootArray[0] -xdev -inum @dirInodeArray[$dirSlot] -exec chown @ownerDirArray[$dirSlot]:@groupDirArray[$dirSlot] {} \\;"; print color 'reset'; print "\n"; } else { print color 'yellow'; print "WARNING: Inode @dirInodeArray[$dirSlot] no longer exists in $cpUser public_html; skipping...\n"; print color 'reset'; } } elsif ( -e @dirArray[$dirSlot] ) { print color 'green'; print "{DIR} Proposed chmod: "; print color 'red'; print "chmod @permDirArray[$dirSlot] @dirArray[$dirSlot] "; print color 'reset'; print "\n"; print color 'green'; print "{DIR} Proposed chown: "; print color 'red'; print "chown @ownerDirArray[$dirSlot]:@groupDirArray[$dirSlot] @dirArray[$dirSlot]"; print color 'reset'; print "\n"; } print color 'yellow'; print "-----------------------------------------------\n"; print color 'reset'; } else { print color 'yellow'; print "-----------------------------------------------\n"; print color 'reset'; print "{DIR} directory number: $dirSlot for $cpuser...\n"; #Deal with spaces and special chars @dirArray[$dirSlot] =~ s/ /\\ /g; @dirArray[$dirSlot] =~ s/'/\\'/g; @dirArray[$dirSlot] =~ s/&/\\&/g; @dirArray[$dirSlot] =~ s/;/\\;/g; @dirArray[$dirSlot] =~ s/\$/\\\$/g; @dirArray[$dirSlot] =~ s/[(]/\\(/g; @dirArray[$dirSlot] =~ s/[)]/\\)/g; @dirArray[$dirSlot] =~ s/[|]/\\|/g; @dirArray[$dirSlot] =~ s/"/\\"/g; @dirArray[$dirSlot] =~ s/!/\\!/g; @dirArray[$dirSlot] =~ s//\\>/g; @dirArray[$dirSlot] =~ s/`/\\`/g; ## If a line has \n or \ in it (and has no spaces), we need to wrap the file path in ' ' $_ = @dirArray[$dirSlot]; my $blanks += tr/ / /; if ( @dirArray[$dirSlot] =~/\\n/ || @dirArray[$dirSlot] =~/\\/ ) { if ( $blanks == 0 ) { @dirArray[$dirSlot] = '\'' . @dirArray[$dirSlot] . '\''; } } print color 'green'; print "{DIR} Proposed chmod: "; print color 'red'; print "chmod @permDirArray[$dirSlot] @dirArray[$dirSlot]"; print color 'reset'; print "\n"; print color 'green'; print "{DIR} Proposed chown: "; print color 'red'; print "chown @ownerDirArray[$dirSlot]:@groupDirArray[$dirSlot] @dirArray[$dirSlot]"; print color 'reset'; print "\n"; print color 'yellow'; print "-----------------------------------------------\n"; print color 'reset'; } } else { if ( -e @dirArray[$dirSlot] ) { if ( is_immutable(@dirArray[$dirSlot]) ) { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "WARNING: @dirArray[$dirSlot] is immutable, skipping...\n"; print color 'reset'; } else { #Deal with spaces and special chars @dirArray[$dirSlot] =~ s/ /\\ /g; #@dirArray[$dirSlot] =~ s/\s/?/g; @dirArray[$dirSlot] =~ s/'/\\'/g; @dirArray[$dirSlot] =~ s/&/\\&/g; @dirArray[$dirSlot] =~ s/;/\\;/g; @dirArray[$dirSlot] =~ s/\$/\\\$/g; @dirArray[$dirSlot] =~ s/[(]/\\(/g; @dirArray[$dirSlot] =~ s/[)]/\\)/g; @dirArray[$dirSlot] =~ s/[|]/\\|/g; @dirArray[$dirSlot] =~ s/"/\\"/g; @dirArray[$dirSlot] =~ s/!/\\!/g; @dirArray[$dirSlot] =~ s//\\>/g; @dirArray[$dirSlot] =~ s/`/\\`/g; ## If a line has \n or \ in it (and has no spaces), we need to wrap the file path in ' ' $_ = @dirArray[$dirSlot]; my $blanks += tr/ / /; if ( @dirArray[$dirSlot] =~/\\n/ || @dirArray[$dirSlot] =~/\\/ ) { if ( $blanks == 0 ) { @dirArray[$dirSlot] = '\'' . @dirArray[$dirSlot] . '\''; } } system("chmod @permDirArray[$dirSlot] @dirArray[$dirSlot] &> /dev/null"); if ( $? != 0 ) { if ( defined(@dirInodeArray[0]) ) { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "WARNING: Error parsing directory for chmod! Parsing by inode directly... "; print color 'reset'; system("find @dirDocRootArray[0] -xdev -inum @dirInodeArray[$dirSlot] -exec chmod @permDirArray[$dirSlot] {} \\;"); if ( $? == 0 ) { print color 'green'; print "success\n"; print color 'reset'; } elsif ( $? != 0 ) { print color 'red'; print "failed!\n"; print "ERROR: Unable to parse inode @dirInodeArray[$dirSlot] with command: find @dirDocRootArray[0] -xdev -inum @dirInodeArray[$dirSlot] -exec chmod @permDirArray[$dirSlot] {} \\;\n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } } ## The file no longer exists, and we did not make an inode save state for this account. elsif ( ! -e @dirArray[$dirSlot] ) { print color 'yellow'; print "WARNING: @dirArray[$dirSlot] no longer exists, skipping...\n"; print color 'reset'; } else { print color 'red'; print "Error in chmod command: chmod @permDirArray[$dirSlot] @dirArray[$dirSlot] \n"; print "Got Error: $!\n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } } system("chown @ownerDirArray[$dirSlot]:@groupDirArray[$dirSlot] @dirArray[$dirSlot] &> /dev/null"); if ( $? != 0 ) { if ( defined(@dirInodeArray[0]) ) { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "WARNING: Error parsing directory for chown! Parsing by inode directly... "; print color 'reset'; system("find @dirDocRootArray[0] -xdev -inum @dirInodeArray[$dirSlot] -exec chown @ownerDirArray[$dirSlot]:@groupDirArray[$dirSlot] {} \\;"); if ( $? == 0 ) { print color 'green'; print "success\n"; print color 'reset'; } elsif ( $? != 0 ) { print color 'red'; print "failed!\n"; print "ERROR: Unable to parse inode @dirInodeArray[$dirSlot] with command: find @dirDocRootArray[0] -xdev -inum @dirInodeArray[$dirSlot] -exec chmod @permDirArray[$dirSlot] {} \\;\n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } } ## The file no longer exists, and we did not make an inode save state for this account. elsif ( ! -e @dirArray[$dirSlot] ) { print color 'yellow'; print "WARNING: @dirArray[$dirSlot] no longer exists, skipping...\n"; print color 'reset'; } else { print color 'red'; print "Error in chown command: chown @ownerDirArray[$dirSlot]:@groupDirArray[$dirSlot] @dirArray[$dirSlot] \n"; print "Got Error: $!\n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } } } } elsif ( ! -e @dirArray[$dirSlot] ) { if ( defined(@dirInodeArray[0]) ) { my $dirVerifyLocation=`find @dirDocRootArray[0] -xdev -inum @dirInodeArray[$dirSlot]`; if ( $dirVerifyLocation ne "" && defined($dirVerifyLocation) ) { system("find @dirDocRootArray[0] -xdev -inum @dirInodeArray[$dirSlot] -exec chmod @permDirArray[$dirSlot] {} \\;"); system("find @dirDocRootArray[0] -xdev -inum @dirInodeArray[$dirSlot] -exec chown @ownerDirArray[$dirSlot]:@groupDirArray[$dirSlot] {} \\;"); } else { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "WARNING: Inode @dirInodeArray[$dirSlot] no longer exists in $cpUser public_html; skipping... \n"; print color 'reset'; } } else { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "WARNING: @dirArray[$dirSlot] no longer exists, skipping... \n"; print color 'reset'; } } } $dirSlot++; }; print color 'green'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "* Task complete.\n"; print color 'reset'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "Uncommenting php tweaks for $user ...\n"; system("find @dirArray[$dirSlot] -name .htaccess -exec sed -i 's/^#php_/php_/g' {} \\\;"); print color 'green'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "* Task complete.\n"; $restoreCounter++; print color 'reset'; sleep(1); print color 'bold green'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] ","suPHP fixes reverted for $user! {Completed: $restoreCounter/", scalar(@backedUpCPusers), "}\n"; print color 'reset'; }; print color 'green'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "suPHP fixes reverted for $restoreCounter accounts. \n"; print color 'reset'; exit; } } sub restorePermsOwners { die "restorePermsOwners() requires one argument. Arg1: cpanelUser or 'all' Optional arg2: verify\n" if (scalar(@_) < 1); my $passedArg = $_[0]; my $passedArg2 = $_[1]; if ( $passedArg eq "all" ) { if ( $passedArg2 eq "verify" ) { &parseAndRestore("all","no","$passedArg2"); } else { &parseAndRestore("all"); } } else { #Verify if passedArg is a valid cpanel user system("QUERY_STRING=\\\"regen=1\\\" /usr/local/cpanel/whostmgr/bin/whostmgr ./setrhash &> /dev/null"); my $hashfile = '/root/.accesshash'; if (! -e $hashfile) { print color 'red'; print "ERROR: Failed to automatically generate hash! Please try logging into WHM and click `Setup Remote Access Key` and then re-run this script. \n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } my $hash = `cat /root/.accesshash`; $hash =~ s/\n//g; my $auth = "WHM root:" . $hash; my $json = new JSON; my $ua = LWP::UserAgent->new; $ua->agent("suphpfix"); $ua->env_proxy(); my $request = HTTP::Request->new(POST => "http://127.0.0.1:2086/json-api/listaccts"); $request->header( Authorization => $auth ); $request->content_type('application/jsonrequest'); $request->content("{}"); #create and ensure UTF8 my $response = $ua->request($request); my $listRaw = $response->content; my $list = encode("UTF-8", $listRaw); my $accntList = $json->allow_nonref->utf8->relaxed->decode($list); our @cpUsersArray; my $isValid; for my $userCnt( @{$accntList->{acct}} ) { if ( $userCnt->{user} eq $passedArg ) { $isValid = "true"; if ( $passedArg2 eq "verify" ) { &parseAndRestore("singleAccnt","$passedArg","$passedArg2"); } else { &parseAndRestore("singleAccnt","$passedArg"); } } } if ( $isValid ne "true" ) { print color 'red'; print "ERROR: $passedArg is not a valid cPanel user.\n"; print color 'reset'; exit(1); } } } sub preChecks { if ( $< != 0 ) { print color 'reset'; print color 'red'; print "ERROR: must be root to run this.\n"; print color 'reset'; exit(1); } system("which chkconfig &> /dev/null"); if ( $? != 0 ) { print color 'red'; print "ERROR: chkconfig not found in path! \n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } my $cpanelVerify = `/sbin/chkconfig --list | grep cpanel`; if ( $cpanelVerify !~/3:on/) { print color 'reset'; print color 'red'; print "ERROR: cPanel not found (needs to be enabled in chkconfig run level 3).\n"; print color 'reset'; exit(1); } system("unalias find &> /dev/null"); system("which find &> /dev/null"); if ( $? != 0 ) { print color 'red'; print "ERROR: find not found in path! \n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } system("unalias chmod &> /dev/null"); system("which chmod &> /dev/null"); if ( $? != 0 ) { print color 'red'; print "ERROR: chmod not found in path! \n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } system("unalias chown &> /dev/null"); system("which chown &> /dev/null"); if ( $? != 0 ) { print color 'red'; print "ERROR: chown not found in path! \n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } system("unalias ls &> /dev/null"); system("which ls &> /dev/null"); if ( $? != 0 ) { print color 'red'; print "ERROR: ls not found in path! \n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } system("unalias wc &> /dev/null"); system("which wc &> /dev/null"); if ( $? != 0 ) { print color 'red'; print "ERROR: wc not found in path! \n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } system("unalias du &> /dev/null"); system("which du &> /dev/null"); if ( $? != 0 ) { print color 'red'; print "ERROR: du not found in path! \n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } system("unalias cut &> /dev/null"); system("which cut &> /dev/null"); if ( $? != 0 ) { print color 'red'; print "ERROR: cut not found in path! \n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } } sub checkLogSize { print color 'reset'; print "Checking size of logs...\n"; ## 2097152kb = 2gb my $twoGB = '2097152'; my $suphpLogFile = '/usr/local/apache/logs/suphp_log'; my $apacheErrorLog = '/usr/local/apache/logs/error_log'; if ( ! -e $apacheErrorLog ) { print color 'reset'; print color 'red'; print "ERROR: $apacheErrorLog does not exist!\n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } my $suexecLog = '/usr/local/apache/logs/suexec_log'; my $accessLog = '/usr/local/apache/logs/access_log'; if ( ! -e $accessLog ) { print color 'reset'; print color 'red'; print "ERROR: $accessLog does not exist!\n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } my $suphpLogSize = `du $suphpLogFile | cut -f 1`; my $apacheErrorLogSize = `du $apacheErrorLog | cut -f 1`; my $suexecLogSize = `du $suexecLog | cut -f 1`; my $accessLogSize = `du $accessLog | cut -f 1`; if ( $suphpLogSize >= $twoGB ) { print color 'reset'; print color 'yellow'; print "WARNING: suPHP error log ($suphpLogFile) is greater than or equal to 2GB. You should clear this log file to prevent problems serving webpages.\n"; print color 'reset'; } if ( $apacheErrorLogSize >= $twoGB ) { print color 'reset'; print color 'yellow'; print "WARNING: Apache error log ($apacheErrorLog) is greater than or equal to 2GB. You should clear this log file to prevent problems serving webpages.\n"; print color 'reset'; } if ( $suexecLogSize >= $twoGB ) { print color 'reset'; print color 'yellow'; print "WARNING: suexec log ($suexecLog) is greater than or equal to 2GB. You should clear this log file to prevent problems serving webpages.\n"; print color 'reset'; } if ( $accessLogSize >= $twoGB ) { print color 'reset'; print color 'yellow'; print "WARNING: Access log ($accessLog) is greater than or equal to 2GB. You should clear this log file to prevent problems serving webpages.\n"; print color 'reset'; } print color 'reset'; print color 'green'; print "All tasks complete.\n"; print color 'reset'; exit; } ################ #### MAIN() #### ################ &preChecks(); print "-suphpfix $version\n"; if ( $ARGV[0] eq "--prep" ) { if ( $ARGV[1] eq "all" ) { &suphpfixMain("all"); } elsif ( defined($ARGV[1]) ) { &suphpfixMain("$ARGV[1]"); } else { print "Please give a valid argument to --prep. For help, see --help.\n"; } } elsif ( $ARGV[0] eq "--save-state" ) { if ( defined($ARGV[1]) ) { if ( $ARGV[1] eq "all" ) { &getCPuserDetails("all"); } else { &getCPuserDetails("$ARGV[1]"); } } elsif ( !defined($ARGV[1]) ) { print "Please pass a valid option to --save-state . For help, see --help \n"; exit(1); } } elsif ( $ARGV[0] eq "--restore-state" ) { if ( defined($ARGV[1]) ) { if ( $ARGV[1] eq "all" ) { if ( defined($ARGV[2]) ) { if ( $ARGV[2] eq "verify" ) { print color 'yellow'; print "\nRunning restore state in verify mode. No changes will be made!\n\n"; print color 'reset'; sleep(3); &restorePermsOwners("all","verify"); } } else { &restorePermsOwners("all"); } } else { if ( $ARGV[2] eq "verify" ) { print color 'yellow'; print "\nRunning restore state in verify mode. No changes will be made!\n\n"; print color 'reset'; sleep(3); &restorePermsOwners("$ARGV[1]","verify"); } else { &restorePermsOwners("$ARGV[1]"); } } } elsif ( !defined($ARGV[1]) ) { print "Please pass a valid option to --restore-state . For help, see --help \n"; exit(1); } } elsif ( $ARGV[0] eq "--help" ) { &help(); } elsif ( $ARGV[0] eq "" ) { print color 'yellow'; print "NOTICE: suphpfix requires an option. For help, see --help\n"; print color 'reset'; } else { print "Option '$ARGV[0]' not recognized. For help, see --help \n"; }