#!/usr/bin/perl use CGI; use File::Path qw(mkpath rmtree); BEGIN { $SIG{__DIE__} = sub { my $msg = shift; print "Status: 500\n"; print "Content-type: text/html\n\n"; $msg =~ s/\n/\0/g; print "error: $msg\n"; CORE::die $msg; } } $| = 1; our $q = CGI->new; print "Content-type: text/html\n\n"; if ($q->param('task') eq 'detect_redirect') { print '1'; exit; } if ($q->param('task') eq 'exists_securelive_max_archive') { my $username = $q->param('user'); die('No username was provided') unless $username; die('Username is invalid') unless $username =~ /^[A-Za-z0-9]+$/; print -e "/home/$username/securelive_max.tar.bz2"; exit; } if ($q->param('task') eq 'extract_securelive_max_archive') { my $username = $q->param('user'); die('No username was provided') unless $username; die('Username is invalid') unless $username =~ /^[A-Za-z0-9]+$/; my $cmd = `tar -jxvf ./securelive_max.tar.bz2 -C /home/$username 2>&1`; print $cmd; &site_chmod("/home/$username/securelive_max", 1); exit; } if ($q->param('task') eq 'delete_securelive_max_archive') { my $username = $q->param('user'); die('No username was provided') unless $username; die('Username is invalid') unless $username =~ /^[A-Za-z0-9]+$/; my $del = unlink("/home/$username/securelive_max.tar.bz2"); die("Could not delete /home/$username/securelive_max.tar.bz2: $!") unless $del; exit; } if ($q->param('task') eq 'exists_securelive_max_directory') { my $username = $q->param('user'); die('No username was provided') unless $username; die('Username is invalid') unless $username =~ /^[A-Za-z0-9]+$/; print -e "/home/$username/securelive_max"; exit; } if ($q->param('task') eq 'delete_securelive_max_directory') { my $username = $q->param('user'); die('No username was provided') unless $username; die('Username is invalid') unless $username =~ /^[A-Za-z0-9]+$/; rmtree("/home/$username/securelive_max", {verbose => 1}); exit; } if ($q->param('task') eq 'extract_sl_admin_archive') { my $username = $q->param('user'); die('No username was provided') unless $username; die('Username is invalid') unless $username =~ /^[A-Za-z0-9]+$/; my $directory = $q->param('directory'); die('No directory was provided') unless $directory; mkpath("/home/$username/$directory", {verbose => 1}) unless -e "/home/$username/$directory"; my $cmd = `tar -jxvf ./sl_admin.tar.bz2 -C /home/$username/$directory 2>&1`; print $cmd; &disable_mod_security("/home/$username/$directory/sl_admin"); &site_chmod("/home/$username/$directory/sl_admin", 1); exit; } if ($q->param('task') eq 'delete_sl_admin_archive') { my $username = $q->param('user'); die('No username was provided') unless $username; die('Username is invalid') unless $username =~ /^[A-Za-z0-9]+$/; my $del = unlink("/home/$username/sl_admin.tar.bz2"); die("Could not delete /home/$username/sl_admin.tar.bz2: $!") unless $del; exit; } if ($q->param('task') eq 'delete_sl_admin_directory') { my $username = $q->param('user'); die('No username was provided') unless $username; die("Username ($username) is invalid") unless $username =~ /^[A-Za-z0-9]+$/; my $directory = $q->param('directory'); die('No directory was provided') unless $directory; die("directory (/home/$username/$directory) is invalid") unless -e "/home/$username/$directory"; rmtree("/home/$username/$directory/sl_admin", {verbose => 1}); exit; } if ($q->param('task') eq 'delete_self') { my $del = unlink($ENV{'SCRIPT_FILENAME'}); die("Could not delete $ENV{'SCRIPT_FILENAME'}: $!") unless $del; exit; } if ($q->param('task') eq 'configure_install') { my $directory = $q->param('directory'); my $user = $q->param('user'); my $domain = $q->param('domain'); my $unified_directory = "/home/$user/$directory"; $unified_directory =~ s/\/\//\//g; &create_authenticator($user); if (-e "$unified_directory/.htaccess") { &install_htaccess($unified_directory, $user, 1); } else { my $fh; open ($fh, '>', "$unified_directory/.htaccess") || die($!); print $fh "\n"; print $fh "\tsuPHP_ConfigPath $unified_directory\n"; print $fh "\n"; print $fh "AddHandler application/x-httpd-php5 .php5 .php4 .php .php3 .php2 .phtml\n" unless -e '/opt/hosting/VERSION'; # LPCP servers are all PHP 5 close $fh; &install_php_ini($unified_directory, $user, "$unified_directory/.htaccess"); } foreach my $entry (&find_htaccess($unified_directory)) { next unless $entry; &install_htaccess($entry, $user, 0); } exit; } if ($q->param('task') eq 'configure_delete') { my $directory = $q->param('directory'); my $user = $q->param('user'); my @exclude = $q->param('exclude_path'); my $unified_directory = "/home/$user/$directory"; $unified_directory =~ s/\/\//\//g; foreach my $entry (&find_htaccess($unified_directory)) { next unless $entry; my $safe_path = 1; foreach my $path (@exclude_path) { $safe_path = 0 if $entry =~ /$path/; } &delete_htaccess($entry, $user) if $safe_path; } exit; } if ($q->param('task') eq 'enable_domain') { my $user = $q->param('user'); my $domain = $q->param('domain'); &enable_domain($user, $domain); exit; } if ($q->param('task') eq 'delete_domain') { my $user = $q->param('user'); my $domain = $q->param('domain'); &delete_domain($user, $domain); exit; } sub disable_mod_security { my ($dir) = @_; my $fh; open ($fh, '>>', "$dir/.htaccess") || die($!); print $fh "RewriteEngine off\n"; print $fh "\n"; print $fh "\tSecFilterEngine Off\n"; print $fh "\tSecFilterScanPOST Off\n"; print $fh "\n"; close $fh; } sub site_chmod { my ($start_dir, $chmod_start) = @_; opendir(DIR, $start_dir) || die "$start_dir: $!"; my @files = grep {!-d "$start_dir\/$_"} readdir(DIR); closedir DIR; opendir(DIR, $start_dir) || die "$start_dir: $!"; my @folders = grep {-d "$start_dir\/$_"} readdir(DIR); closedir DIR; if ($chmod_start) { chmod 0755, $start_dir; } foreach my $file (sort @files) { $file =~ s/\"/\\\"/i; $file = "$start_dir\/$file"; if ($file =~ /\.pl$/ || $file =~ /\.cgi$/) { chmod 0755, $file; } else { chmod 0644, $file; } } foreach my $folder (sort @folders) { if ($folder !~ /^\.\.?$/) { $folder =~ s/\"/\\\"/i; $folder = "$start_dir\/$folder"; chmod 0755, $folder; &site_chmod($folder, 0); } } } sub find_htaccess { my ($start_dir) = @_; print "information: Scanning for additional .htaccess files\n"; my @htaccess = &dir ($start_dir); print "information: Scan for additional .htaccess files complete\n"; return @htaccess; } sub enable_domain { my ($user, $domain) = @_; my $file = "/home/$user/securelive_max/lunarpages.php"; my $fh; unless (-e $file) { &create_authenticator($user); } chmod 0644, $file; open($fh, "<$file") || die("Unable to open $file: $!"); my @lines = <$fh>; close ($fh); foreach (@lines) { next unless $_ =~ /\$domains = array\((.*?)\)/; my @domains = split(/,\s?/, $1); my $found = 0; foreach my $entry (@domains) { $entry =~ s/\"//g; $found = 1 if $entry eq $domain; $entry = "\"$entry\""; last if $found; } push (@domains, "\"$domain\"") unless $found; $_ = '$domains = array(' . join(',', sort(@domains)) . ');' . "\n"; } open($fh, ">$file") || die("Unable to open $file: $!"); print $fh join('', @lines); close($fh); } sub delete_domain { my ($user, $domain) = @_; my $file = "/home/$user/securelive_max/lunarpages.php"; my $fh; return unless -e $file; chmod 0644, $file; open($fh, "<$file") || die("Unable to open $file: $!"); my @lines = <$fh>; close ($fh); foreach (@lines) { next unless $_ =~ /\$domains = array\((.*?)\)/; my @domains = split(/,\s?/, $1); my @new_domains; foreach my $entry (@domains) { $entry =~ s/\"//g; if ($entry ne $domain) { push(@new_domains, "\"$entry\""); } } $_ = '$domains = array(' . join(',', sort(@new_domains)) . ');' . "\n"; } open($fh, ">$file") || die("Unable to open $file: $!"); print $fh join('', @lines); close($fh); } sub create_authenticator { my ($user) = @_; my $file = "/home/$user/securelive_max/lunarpages.php"; my $fh; return if -e $file; chmod 0644, $file; print "information: Creating authentication file\n"; open($fh, ">$file") || die("Unable to open $file: $!"); print $fh ''; close($fh); } sub install_htaccess { my ($directory, $user, $required_php) = @_; my $file = "$directory/.htaccess"; my $fh; print "information: Scanning .htaccess file at $file for suPHP_ConfigPath entries and PHP 5 handlers\n"; open($fh, "<$file") || die("Unable to open $file: $!"); my @lines = <$fh>; close $fh; my $php5 = 0; my $php_found = 0; my $protect_php_ini = 0; foreach (@lines) { if (/^suPHP_ConfigPath\s+(.+)/) { my $phpini = $1; $phpini =~ s/\s+$//; print "information: Found suPHP_ConfigPath entry in $file, pointing to $phpini/php.ini\n"; &install_php_ini($phpini, $user, $file); $required_php = 1 if $phpini eq $directory; $php_found = 1; } if (/^AddHandler application\/x-httpd-php5/) { print "information: Found PHP 5 handler entry\n"; $php5 = 1; } if (/^\/) { print "information: Found php.ini protection line"; $protect_php_ini = 1; } $_ = "$_\n" unless substr($_,-1) eq "\n"; # Add a newline to the end of a line that doesn't have one } $php5 = 1 if -e '/opt/hosting/VERSION'; # LPCP servers are all PHP 5 if ($required_php) { if (!$php_found) { print "information: No suPHP_ConfigPath entry found in $file, where required\n"; print "information: Adding suPHP_ConfigPath entry to $file, pointing to $directory/php.ini\n"; unshift(@lines, "\n"); unshift(@lines, "suPHP_ConfigPath $directory\n"); unshift(@lines, "\n"); &install_php_ini($directory, $user, $file); } if (!$php5) { print "information: No PHP 5 handler entry found in $file, where required\n"; print "information: Adding PHP 5 handler entry to $file\n"; unshift(@lines, "AddHandler application/x-httpd-php5 .php5 .php4 .php .php3 .php2 .phtml\n"); } if (!$protect_php_ini) { print "information: No php.ini protection found in $file, where required\n"; print "information: Adding php.ini protection entries to $file\n"; push(@lines, "\n"); push(@lines, " Order allow,deny\n"); push(@lines, " Deny from all\n"); push(@lines, "\n"); } chmod 0644, $file; open ($fh, ">$file") || die("Unable to open $file: $!"); print $fh @lines; close $fh; } print "information: Scan of .htaccess file at $file complete\n"; } sub install_php_ini { my ($directory, $user, $ref) = @_; my $file = "$directory/php.ini"; my $fh; print "information: Scanning php.ini file at $file for SecureLive auto_prepend entries\n"; my @lines; if (-e $file) { open($fh, "<$file") || die("Unable to open $file (from $ref): $!"); @lines = <$fh>; close $fh; } my $auto_append_found = 0; foreach (@lines) { if (/auto_prepend_file = \/home\/$user\/securelive_max\/lunarpages.php/) { print "information: Found SecureLive auto_prepend entry in $file\n"; $auto_append_found = 1; } elsif ( /auto_prepend_file = \/home\/$user\/securelive_max/) { print "information: Found SecureLive auto_prepend entry in $file for a different installation, skipping\n"; $auto_append_found = 1; } } if (!$auto_append_found) { print "information: No SecureLive auto_prepend entry found in $file, where required\n"; print "information: Adding SecureLive auto_prepend entry to $file\n"; chmod 0644, $file; open ($fh, ">$file") || die("Unable to open $file (from $ref): $!"); unshift(@lines, "auto_prepend_file = /home/$user/securelive_max/lunarpages.php\n"); print $fh @lines; close $fh; } print "information: Scan of php.ini file at $file complete\n"; } sub delete_htaccess { my ($directory, $user) = @_; my $file = "$directory/.htaccess"; my $fh; print "information: Scanning .htaccess file at $file for suPHP_ConfigPath entries\n"; chmod 0644, $file; open($fh, "<$file") || die("Unable to open $file: $!"); my @lines = <$fh>; close $fh; open($fh, ">$file") || die("Unable to open $file: $!"); foreach (@lines) { if (/^suPHP_ConfigPath\s+(.+)/) { my $directory = $1; print "information: Found suPHP_ConfigPath entry in $file, pointing to $1/php.ini\n"; &delete_php_ini($directory, $user); if (!-e "$directory/php.ini") { #After the edit, a php.ini file can be removed, clean up the .htaccess as well if (scalar(@lines) == 1) { print "information: Removing suPHP_ConfigPath entry as php.ini file at $1 has been removed\n"; print "information: Removing empty .htaccess $file\n"; close($fh); unlink($file); } } else { print $fh $_; } } else { print $fh $_; } } close($fh); print "information: Scan of .htaccess file at $file complete\n"; } sub delete_php_ini { my ($directory, $user) = @_; my $file = "$directory/php.ini"; my $fh; print "information: Scanning php.ini file at $file for SecureLive auto_prepend entries\n"; my @lines; if (-e $file) { open($fh, "<$file") || die($!); @lines = <$fh>; close $fh; } if (scalar(@lines) == 1 && $lines[0] =~ /auto_prepend_file = \/home\/$user\/securelive_max\/lunarpages.php/) { print "information: Found SecureLive auto_prepend entry in $file\n"; print "information: Removing SecureLive auto_prepend entry from $file\n"; print "information: Removing empty php.ini $file\n"; my $del = unlink($file); } else { chmod 0644, $file; open ($fh, ">$file") || die($!); foreach (@lines) { next unless $_; next if /^\s+$/; if (/auto_prepend_file = \/home\/$user\/securelive_max\/lunarpages.php/) { print "information: Found SecureLive auto_prepend entry in $file\n"; print "information: Removing SecureLive auto_prepend entry from $file\n"; next; } else { print $fh "$_" if $_; } } close $fh; } if ((stat($file))[7] == 0) { print "information: Removing SecureLive auto_prepend entry from $file\n"; print "information: Removing empty php.ini $file\n"; my $del = unlink($file); } print "information: Scan of php.ini file at $file complete\n"; } sub dir { my ($start_dir) = @_; return if -l $start_dir; my @results; if (opendir(DIR, $start_dir)) { my @files = grep {!-d "$start_dir\/$_"} readdir(DIR); rewinddir DIR; my @folders = grep {-d "$start_dir\/$_"} readdir(DIR); foreach my $file (sort @files) { if ($file eq '.htaccess') { print "information: Found .htaccess at $start_dir/.htaccess\n"; push(@results, $start_dir); } } foreach my $folder (sort @folders) { if ($folder !~ /^\.\.?$/) { $folder =~ s/\"/\\\"/i; push(@files, dir("$start_dir/$folder")); } } closedir DIR } return @results; } 42;