#!/usr/bin/perl
# cpanel10 - install (modsecurity)           Copyright(c) 2004-2006 cPanel, Inc.
#                                                           All Rights Reserved.
# copyright@cpanel.net                                         http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited

use strict;
use warnings;
use lib '/usr/local/cpanel';
use Cpanel::Form;
use Cpanel::cPanelFunctions ();
use Cpanel::SafeRun         ();
use SafeFile;
use POSIX;
use Carp qw ( croak );

require "/scripts/regsrep.pl";

eval {
    require Cpanel::Config;
    if ( defined &Cpanel::Config::is_ea3 ) {
        if ( Cpanel::Config::is_ea3() ) {
            print "This module is now managed via easyapache.\n";
            print "To keep this message from appearing: ";
            print "Remove its entry from /var/cpanel/addonmodules\n";
            exit;
        }
    }
};

my $apache_ver = get_httpd_version();
if ( $apache_ver !~ m/^1\.3/ ) {
    print <<"EOM";


!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Unable to install the Bandwidth addon. This addon is designed specifically
for Apache 1.3. You are currently running Apache $apache_ver. This addon
has been included in the latest version of EasyApache.
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!


EOM
    exit;
}
else {
    print "Detected Apache $apache_ver\n";
}

my $time       = time;
my $installdir = `pwd`;
chomp($installdir);
my $modsecver  = "1.9.1";
my $modsecfile = "modsecurity-apache-${modsecver}.tar.gz";
my $modsecdir  = "modsecurity-apache-${modsecver}";
my $mirror     = "http://layer1.cpanel.net/buildapache/1/";
my $password   = getranddata( 12, 0 );
my $system     = ( POSIX::uname() )[0];
my $hostname   = Cpanel::cPanelFunctions::gethostname() || 'localhost';
my $dbhost     = Cpanel::cPanelFunctions::getmydbhost() || 'localhost';

if ( $dbhost eq 'localhost' ) {
    $hostname = 'localhost';
}

print "<b>Installing Mod_Security version $modsecver</b>\n";

umask(0022);

chdir '/usr/src/' or die "Unable to chdir: $!";
downloadfile( $modsecfile, $mirror, 0 );
if ( !-e $modsecfile ) {
    die "Unable to fetch sources!";
}
system 'tar', '-zxf', $modsecfile;
chdir $installdir or die "Unable to chdir: $!";

if ( !-d "/var/cpanel/apachemod" ) {
    mkdir "/var/cpanel/apachemod";
}
system "cp", "-f", "modsecurity.apache", "/var/cpanel/apachemod";
system "chmod", "0755", "/var/cpanel/apachemod/modsecurity.apache";
system "perl", "/var/cpanel/apachemod/modsecurity.apache";

print "<b>Done</b>\n";

print "<b>Editing httpd.conf</b>\n";
addinclude();
print "<b>Done</b>\n";

print "<b>Copying over default Mod_Security ruleset</b>\n";

chdir $installdir or die "Unable to chdir: $!";

system "cp", "-f", "${installdir}/modsec.conf", "/usr/local/apache/conf";
chmod 0600, '/usr/local/apache/conf/modsec.conf';

# Touch user rules
if ( open my $conf_fh, '>>', '/usr/local/apache/conf/modsec.user.conf' ) {
    close $conf_fh;
}
else {
    warn "Unable to write /usr/local/apache/conf/modsec.user.conf: $!";
}
chmod 0600, '/usr/local/apache/conf/modsec.user.conf';

# Default rules
system "cp", "-f", "${installdir}/modsec.user.conf.default", "/usr/local/apache/conf";
chmod 0600, '/usr/local/apache/conf/modsec.user.conf.default';

print "<b>Done</b>\n";

# Backup
print "<b>Backing up modsec database</b>\n";
my $dump = Cpanel::SafeRun::saferunnoerror(qw{mysqldump --no-create-db --no-create-info --skip-comments --complete-insert modsec});
if ($dump) {
    if ( open my $dump_fh, '>', $installdir . '/modsec.' . $time . '.sql' ) {
        chmod 0600, $installdir . '/modsec.' . $time . '.sql';
        print {$dump_fh} $dump;
        close $dump_fh;
    }
}
system 'mysqladmin', '-f', 'drop', 'modsec';
print "<b>Done</b>\n";

print "<b>Setting up Front End</b>\n";
chdir $installdir or die "Unable to chdir: $!";

# Protect the permissions on these files:
chmod 0600, 'modsecparse.pl'   or warn;
chmod 0600, 'modsec.sql'       or warn;
chmod 0600, 'addon_modsec.cgi' or warn;

# modsecparse.pl
regsrep( "modsecparse.pl", '__DBPASSWORD__', "my \$dbpassword = '$password';" );
regsrep( "modsecparse.pl", '__MYSQLHOST__',  "my \$dbhost     = '$dbhost';" );

# modsec.sql
regsrep( "modsec.sql", '__DBPASSWORD__',                                                                    "        password('$password')" );
regsrep( "modsec.sql", 'GRANT SELECT, INSERT, UPDATE, DELETE ON modsec.* TO \'modsec\'\@\'__HOSTNAME__\';', "GRANT SELECT, INSERT, UPDATE, DELETE ON modsec.* TO 'modsec'\@'$hostname';" );
regsrep( "modsec.sql", '        \'__HOSTNAME__\',',                                                         "        '$hostname'," );

# addon_modsec.cgi
regsrep( "addon_modsec.cgi", '__DBPASSWORD__', "    my \$dbpassword = '$password';" );
regsrep( "addon_modsec.cgi", '__MYSQLHOST__',  "    my \$dbhost = '$dbhost';" );

# Create database
system "mysql -f < modsec.sql";

# Restore data
if ( -e $installdir . '/modsec.' . $time . '.sql' && -s _ ) {
    system "mysql -f modsec < $installdir/modsec.$time.sql";
}

# Secure permissions of previous dump files
# And clear old database dumps
my @dump_files = glob $installdir . '/modsec.*.sql';
my $count      = 0;
foreach my $dump_file (@dump_files) {
    if ( $count > 2 ) {
        if ( unlink $dump_file ) {
            next;
        }
    }
    chmod 0600, $dump_file;
    $count++;
}

if ( $system =~ /freebsd/i ) {
    mkdir("/usr/local/cpanel/addons") if ( !-e "/usr/local/cpanel/addons" );
    system( "cp", "-f", "modsecparse.pl", "/usr/local/cpanel/addons/modsecparse.pl" );
    chmod 0750, '/usr/local/cpanel/addons/modsecparse.pl';
    my ($crontime) = "0 * * * *";
    my (@CRONTAB) = split( /\n/, `/usr/bin/crontab -l` );
    @CRONTAB = grep( !/^#/,          @CRONTAB );
    @CRONTAB = grep( !/modsecparse/, @CRONTAB );
    push( @CRONTAB, "$crontime /usr/local/cpanel/addons/modsecparse.pl > /dev/null 2>&1" );
    open( CRONTAB, "|/usr/bin/crontab -" );

    foreach (@CRONTAB) {
        s/\n//g;
        print CRONTAB;
        print CRONTAB "\n";
    }
    close(CRONTAB);
}
else {
    system( "cp", "-f", "modsecparse.pl", "/etc/cron.hourly/" );
    chmod 0750, '/etc/cron.hourly/modsecparse.pl';
}
system( "cp", "-f", "addon_modsec.cgi", "/usr/local/cpanel/whostmgr/docroot/cgi" );
chmod 0700, '/usr/local/cpanel/whostmgr/docroot/cgi/addon_modsec.cgi' or warn;
system( "touch", "/var/cpanel/cpmodsec" );
print "<b>Done\n</b>";

system("/scripts/restartsrv_httpd");

print "<b>The default audit log is located at /usr/local/apache/logs/audit_log\n";
print "All pages that are prevented from loading by mod_security will have a 406 error</b>\n";

sub addinclude {
    my $conflock  = SafeFile::safeopen( \*HC, "+<", "/usr/local/apache/conf/httpd.conf" );
    my $line      = 0;
    my $addmodule = 0;
    my (@HC);
    while (<HC>) {
        next if ( /Include/ && /modsec.conf/i );
        $line++;
        if (/^[\s\t]*AddModule/) { $addmodule = $line; }
        push( @HC, $_ );
    }
    seek( HC, 0, 0 );
    $line = 0;
    foreach (@HC) {
        $line++;
        if ( $line == ( $addmodule + 1 ) ) {
            print HC "Include \"/usr/local/apache/conf/modsec.conf\"\n";
        }
        print HC $_;
    }
    truncate( HC, tell(HC) );
    SafeFile::safeclose( \*HC, $conflock );
}

sub downloadfile {
    my $file     = shift;
    my $mirror   = shift;
    my $md5check = shift;
    my $md5checksum;
    if ( $md5check == 1 ) {
        my $md5size = 0;
        my $md5try  = 1;
        while ( $md5size == 0 ) {
            if ( $md5try == 5 ) {
                croak "ERROR: Attempted to download md5sum check file 5 times and failed! Exiting now!\n";
            }
            elsif ( $md5try == 1 ) {
                print "Downloading md5sum check file...\n";
            }
            else {
                print "\nDownload failed for md5sum check file, trying attempt # " . ( $md5try + 1 ) . "\n";
            }
            system( "wget", "-q", "-O", $file . ".md5", $mirror . $file . ".md5" );
            if ( -e "$file.md5" ) {
                $md5size = ( stat(_) )[7];
            }
            $md5try++;
        }
        open( MD5, $file . ".md5" );
        while (<MD5>) {
            $md5checksum = $_;
            chomp($md5checksum);
            unlink( "/usr/local/" . $file . ".md5" );
            if ( $md5checksum eq "" ) {
                croak "ERROR: Unable to determine the md5 checksum value for $file";
            }
        }
        close(MD5);
    }
    my $size = 0;
    my $try  = 1;
    while ( $size == 0 ) {
        if ( $try == 5 ) {
            croak "ERROR: Attempted to download $file 5 times and failed! Exiting now!\n";
        }
        elsif ( $try == 1 ) {
            print "Downloading $file...\n";
        }
        else {
            print "\nDownload failed, trying attempt # " . ( $try + 1 ) . "\n";
        }
        system( "wget", "-q", "-O", $file, $mirror . $file );
        if ( -e $file ) {
            $size = ( stat(_) )[7];
        }
        $try++;
    }
    if ( $md5check == 1 ) {
        my $realmd5sum = `md5sum $file`;
        chomp($realmd5sum);
        if ( $realmd5sum ne $md5checksum ) {
            croak "ERROR: md5sum mismatch (actual: $realmd5sum) (expected: $md5checksum)\n";
        }
    }
}

sub get_httpd_version {
    my $bin = '/usr/local/apache/bin/httpd';
    return 'UNKNOWN' if !-x $bin;
    if ( Cpanel::SafeRun::saferunallerrors( $bin, '-v' ) =~ m{Apache/(\d+\.\d+\.\d+)}m ) {
        return $1;
    }
    return 'UNKNOWN';
}

