File: //proc/self/root/proc/self/root/scripts.20110531.215904.25158/securetmp
#!/usr/bin/perl
# cpanel - securetmp Copyright(c) 2010 cPanel, Inc.
# All rights Reserved.
# copyright@cpanel.net http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited
BEGIN { unshift @INC, '/usr/local/cpanel'; }
use strict;
use Cpanel::SafeFile ();
use Cpanel::OSSys ();
use Getopt::Long;
use Cpanel::Logger ();
use Cpanel::SafeRun::Errors ();
use Cpanel::SafeRun::Simple ();
my $logger = Cpanel::Logger->new();
$| = 1;
if ( -e '/var/cpanel/version/securetmp_disabled' ) {
print "[securetmp] Disabled per /var/cpanel/version/securetmp_disabled\n";
exit;
}
my $system = $^O;
my $has_loop_device = 0;
# do check for loopback module for Linux based VPS
if ( $system =~ m/Linux/i ) {
my @modules = Cpanel::SafeRun::Errors::saferunallerrors('lsmod');
my $loopdev = 0;
$has_loop_device = check_loop_device();
if ( !grep /loop/, @modules ) {
print "*** Notice *** No loop module detected\n"; # could be built into kernel, so don't bail out yet
print "If the loopback block device is built as a module, try running `modprobe loop` as root via ssh and running this script again.\n";
print "If the loopback block device is built into the kernel itself, you can ignore this message.\n";
}
if ( !$has_loop_device ) {
print "*** Notice *** No working loopback device files found. Try running `modprobe loop` as root via ssh and running this script again.\n";
exit(0);
}
}
my $install = 0; # Add securetmp to system startup
my $uninstall = 0; # Remove from system startup
my $auto = 0; # Secure /tmp and /var/tmp
# Get command line options
GetOptions( 'auto' => \$auto, 'install' => \$install, 'uninstall' => \$uninstall );
# Start interactive setup
if ( !$auto && !$install && !$uninstall && -t STDIN ) {
print 'Would you like to secure /tmp & /var/tmp at boot time? (y/n) ';
my $answer;
chomp( $answer = <STDIN> );
if ( $answer =~ m/^y/i ) {
$install = 1;
}
else {
print "securetmp will not be added to system startup at this time.\n";
}
undef $answer;
if ( !$install ) {
print 'Would you like to disable securetmp from the system startup? (y/n) ';
chomp( $answer = <STDIN> );
if ( $answer =~ m/^y/i ) {
$uninstall = 1;
}
else {
print "securetmp will not be removed from system startup.\n";
}
undef $answer;
}
print 'Would you like to secure /tmp & /var/tmp now? (y/n) ';
chomp( $answer = <STDIN> );
if ( $answer =~ m/^y/i ) {
$auto = 1;
}
else {
print "/tmp & /var/tmp will not be secured at this time.\n";
}
exit if ( !$install && !$auto && !$uninstall );
}
elsif ( !$auto && !$install && !$uninstall ) {
exit 1;
}
## ADD/REMOVE from startup
#-----------------------------------------------------------------
if ( !-x '/scripts/cpservice' ) {
$logger->warn("cpservice is not available. Please check its status.");
}
else {
# Remove securetmp from system startup
if ($uninstall) {
Cpanel::SafeRun::Simple::saferun( '/scripts/cpservice', 'securetmp', 'stop' );
Cpanel::SafeRun::Simple::saferun( '/scripts/cpservice', 'securetmp', 'disable', '2345' );
Cpanel::SafeRun::Simple::saferun( '/scripts/cpservice', 'securetmp', 'uninstall' );
}
# Add securetmp to system startup
if ($install) {
Cpanel::SafeRun::Simple::saferun( '/scripts/cpservice', 'securetmp', 'install' );
Cpanel::SafeRun::Simple::saferun( '/scripts/cpservice', 'securetmp', 'enable', '35' );
Cpanel::SafeRun::Simple::saferun( '/scripts/cpservice', 'securetmp', 'start' );
}
}
#-----------------------------------------------------------------
# Fork and secure if not called from console
if ( $auto && !-t STDIN ) {
$SIG{'CHLD'} = \&reaper;
print "Setting up /tmp & /var/tmp in the background\n";
exit if fork;
}
elsif ( !$auto ) {
exit;
}
print "Securing /tmp & /var/tmp\n";
# Secure PATH
$ENV{'PATH'} .= ":/sbin:/usr/sbin";
# Global Variables
my $brokenvartmp = 0;
my $freebsdversion = '';
my @vnodes = ();
my $vnodeconfig = '';
my $vnodesrch = '';
my $vnodenumber = 0;
my $tmpmnt = '';
my $vartmpmnt = '';
my $tmpopts = '';
my $vartmpopts = '';
my $mountkeyword = '';
my $cpflags = '';
my $tmpdsksize = 512000; # Must be larger than 250000
# FreeBSD Logic
if ( $system =~ m/freebsd/i ) {
$mountkeyword = 'update';
$cpflags = '-Rpf';
$freebsdversion = ( Cpanel::OSSys::uname() )[2];
@vnodes = ();
$vnodeconfig = '';
$vnodesrch = '';
# Set vnode config method
if ( $freebsdversion =~ m/^4/ ) {
$vnodeconfig = 'vnconfig';
$vnodesrch = 'vn';
}
elsif ( $freebsdversion =~ m/^(?:5|6|7|8|9)/ ) {
$vnodeconfig = 'mdconfig';
$vnodesrch = 'md';
}
else {
die "FreeBSD version $freebsdversion is not supported.";
}
open( MT, 'mount|' ) || die "Unable to execute mount: $!";
while ( my $line = <MT> ) {
if ( $line =~ m/(\S+)\s+on\s+(\S+)\s+\(([^\)]+)/ ) {
my $device = $1;
my $mount = $2;
my $opts = $3;
if ( $mount eq '/tmp' ) {
$tmpmnt = $device;
$tmpopts = $opts;
}
elsif ( $mount eq '/var/tmp' ) {
$vartmpmnt = $device;
$vartmpopts = $opts;
}
if ( $vnodeconfig eq 'vnconfig' && $device =~ m/^\/dev\/${vnodesrch}\d+/ ) {
push @vnodes, $device;
}
}
}
close(MT);
# Determine next usable vnode (vnconfig)
if (@vnodes) {
foreach my $vnode (@vnodes) {
$vnode =~ /(\d+)/;
if ( $1 > $vnodenumber ) {
$vnodenumber = $1;
}
}
$vnodenumber++;
}
}
else { # Linux Logic
$mountkeyword = 'remount';
$cpflags = '-af';
if ( open my $mounts_fh, '<', '/proc/mounts' ) {
while ( my $line = readline $mounts_fh ) {
# must detect: /dev/sda1 /var/tmp\040(deleted) ext2 rw,nosuid,noexec,usrquota 0 0
if ( $line =~ m/^(\S+)\s+([^\s\\\(]+)\S*\s+\S+\s+(\S+)/ ) {
if ( $2 eq '/tmp' ) {
$tmpmnt = $1;
$tmpopts = $3;
}
elsif ( $2 eq '/var/tmp' ) {
$vartmpmnt = $1;
$vartmpopts = $3;
}
if ( $1 =~ /^\/dev\/vn.*/ ) {
push @vnodes, $1;
}
}
if ( $line =~ m/\S+\s+\(deleted\)[^\/]*\/var\/tmp\s+/ ) {
$brokenvartmp = 1;
$vartmpmnt = '';
$vartmpopts = '';
}
}
close $mounts_fh;
}
else {
die "Unable to read /proc/mounts: $!";
}
}
# Begin securetmp actions
if ( !$tmpmnt ) {
# Check loop dev on Linux
if ( $system =~ m/Linux/i ) {
if ( !$has_loop_device ) {
print "The system does not support loop devices.\n";
if ($brokenvartmp) {
print 'Unmounting orphaned /var/tmp ...';
system 'umount', '/var/tmp';
print "Done\n";
}
exit;
}
}
if ( !-e '/usr/tmpDSK' ) {
print "No separate partition for tmp!\n";
createtmpdisk($system);
}
elsif ( -d '/usr/tmpDSK' ) {
die "/usr/tmpDSK exists as a directory. Please remove and rerun /scripts/securetmp.\n";
}
# ensure that /usr/tmpDSK is large enough
elsif ( ( -s '/usr/tmpDSK' ) < 250000000 ) {
system 'rm', '-f', '/usr/tmpDSK';
createtmpdisk($system);
}
print 'Setting up /tmp... ';
if ( -e '/usr/tmp.secure' ) {
system 'mv', '-f', '/usr/tmp.secure', '/usr/tmp.secure.cpback';
}
mkdir '/usr/tmp.secure';
archivecopy( '/tmp', '/usr/tmp.secure' );
system 'rm', '-rf', '/tmp';
mkdir '/tmp';
chmod( oct(1777), '/tmp' );
my $mountresult = mounttmpdsk( $system, $tmpopts );
archivecopy( '/usr/tmp.secure/tmp/.', '/tmp' );
chmod( oct(1777), '/tmp' );
system 'rm', '-rf', '/usr/tmp.secure';
if ($mountresult) {
die "There was a problem mounting /tmp: $mountresult";
}
print "Done\n";
}
elsif ( $tmpmnt && $tmpopts !~ m/noexec/ ) {
print 'Securing /tmp... ';
system 'mount', '-o', $mountkeyword . ',noexec,nosuid', $tmpmnt, '/tmp';
print "Done\n";
}
else {
print "/tmp is already secure\n";
}
if ( $brokenvartmp || ( $vartmpmnt && $vartmpopts !~ m/noexec/ ) ) {
print 'Unmounting insecure /var/tmp... ';
system 'umount', '/var/tmp';
$vartmpmnt = '';
$vartmpopts = '';
print "Done\n";
}
if ( !$vartmpmnt ) {
print 'Setting up /var/tmp... ';
if ( !-e '/var/tmp' ) {
mkdir '/var/tmp';
}
elsif ( !-d '/var/tmp' ) {
system 'mv', '/var/tmp', '/var/tmp.cpback';
mkdir '/var/tmp';
}
if ( $system =~ m/freebsd/i ) {
if ( $freebsdversion =~ m/^(5|6|7|8|9)/ ) {
system 'mount_nullfs', '-o', 'noexec,nosuid', '/tmp', '/var/tmp';
}
else {
system 'mount_null', '-o', 'noexec,nosuid', '/tmp', '/var/tmp';
}
}
else {
system 'mount', '-o', 'bind,noexec,nosuid', '/tmp', '/var/tmp';
}
print "Done\n";
}
else {
print "/var/tmp is already secure\n";
}
my $usingTMPDSK = 0;
if ( -e '/usr/tmpDSK' ) {
my $mount = `mount`;
if ( $mount =~ m/tmpDSK/ ) {
$usingTMPDSK = 1;
}
}
print 'Checking fstab for entries ...';
my $hastmpdsk = 0;
my $hasvartmpdsk = 0;
my $fslock = Cpanel::SafeFile::safeopen( \*FSTAB, '+<', '/etc/fstab' );
if ($fslock) {
while (<FSTAB>) {
if (/^\s*\/usr\/tmpDSK/) { $hastmpdsk = 1; }
if (/^\s*(\S+)\s*\/var\/tmp/) { $hasvartmpdsk = 1; }
}
if ( !$hastmpdsk && $usingTMPDSK ) {
print "Added fstab entry (/tmp)....";
if ( $system =~ /freebsd/i ) {
print FSTAB "/usr/tmpDSK /tmp nullfs rw,noauto 0 0\n";
}
else {
print FSTAB "/usr/tmpDSK /tmp ext3 defaults,noauto 0 0\n";
}
}
if ( !$hasvartmpdsk && $vartmpmnt ) {
print "Added fstab entry (/var/tmp)....";
if ( $system =~ /freebsd/i ) {
print FSTAB "/tmp /var/tmp nullfs rw,noauto 0 0\n";
}
else {
print FSTAB "/tmp /var/tmp ext3 defaults,bind,noauto 0 0\n";
}
}
Cpanel::SafeFile::safeclose( \*FSTAB, $fslock );
print "Done\n";
}
else {
$logger->die("Could not edit /etc/fstab");
}
my $logrotate = '/etc/cron.daily/logrotate';
if ( -e $logrotate ) {
my @logrotate_contents;
my $has_tmpdir = 0;
if ( open my $logrotate_fh, '<', $logrotate ) {
while ( my $line = readline $logrotate_fh ) {
if ( $line =~ m/TMPDIR/ && $line !~ m/^\s*#/ ) {
$has_tmpdir = 1;
last;
}
push @logrotate_contents, $line;
}
close $logrotate_fh;
if ( !$has_tmpdir ) {
my $updated_logrotate = 0;
if ( open my $logrotate_fh, '>', $logrotate ) {
foreach my $line (@logrotate_contents) {
if ( $line =~ m/^#!\/(?:usr|bin)/ ) {
print "Adding TMPDIR setting to /etc/cron.daily/logrotate\n";
print {$logrotate_fh} $line;
print {$logrotate_fh} "export TMPDIR=/var/spool/logrotate/tmp\n";
$updated_logrotate = 1;
}
else {
print {$logrotate_fh} $line;
}
}
close $logrotate_fh;
}
if ($updated_logrotate) {
if ( !-e '/var/spool/logrotate/tmp' ) {
system 'mkdir', '-p', '/var/spool/logrotate/tmp';
}
if ( !-d '/var/spool/logrotate/tmp' ) {
print <<'EOM';
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Logrotate detected and TMPDIR setting updated. The TMPDIR
directory (/var/spool/logrotate/tmp) does not exist!
Logrotate will need to use this directory for execution of
its postrotate scripts. This directory is normally /tmp, but
due to /tmp being set as non-executable an alternative
directory must be specified. Please correct this issue.
See /etc/cron.daily/logrotate to adjust the TMPDIR value for your system.
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
EOM
}
}
else {
warn "Failed to update /etc/cron.daily/logrotate! Logrotate may be corrupt.";
}
}
else {
print "Logrotate TMPDIR already configured\n";
}
}
}
print "Process Complete\n";
exit;
################################################################################
# createtmpdisk
################################################################################
sub createtmpdisk {
my $system = shift;
print "Building /usr/tmpDSK...";
if ( -e "/usr/tmpDSK" ) {
unlink("/usr/tmpDSK");
}
system( "dd", "if=/dev/zero", "of=/usr/tmpDSK", "bs=1024", "count=${tmpdsksize}" );
if ( $system !~ /freebsd/i ) {
open( MKFS, "|-" ) || exec( "/sbin/mkfs", "/usr/tmpDSK" );
print MKFS "yes\r\n";
close(MKFS);
if ( -e "/sbin/tune2fs" ) {
system( "/sbin/tune2fs", "-j", "/usr/tmpDSK" );
}
}
chmod 0600, '/usr/tmpDSK';
print "Done\n";
}
################################################################################
# archivecopy
################################################################################
sub archivecopy {
my ( $origin, $dest ) = @_;
my $cpflags = '-af';
if ( $system =~ /freebsd/i ) {
$cpflags = '-Rpf';
}
system( "cp", $cpflags, $origin, $dest );
}
################################################################################
# mounttmpdsk
################################################################################
sub mounttmpdsk {
my $system = shift;
my $currentmntopts = shift;
if ( $system =~ /freebsd/i ) {
my $results = `which $vnodeconfig`;
chomp($results);
if ( $results eq "" ) {
return (1);
}
if ( $vnodeconfig eq "vnconfig" ) {
# FreeBSD 4
print " using vnconfig\n";
if ( !-e "/dev/vn${vnodenumber}c" ) {
system("cd /dev; rm -f vn${vnodenumber}*; sh MAKEDEV vn${vnodenumber}");
}
system( "vnconfig", "-u", "vn${vnodenumber}" );
system( "vnconfig", "-s", "labels", "-c", "vn${vnodenumber}", "/usr/tmpDSK" );
system( "disklabel", "-r", "-w", "vn${vnodenumber}", "auto" );
system( "newfs", "vn${vnodenumber}c" );
system( "mount", "-o", "noexec,nosuid,rw", "/dev/vn${vnodenumber}c", "/tmp" );
}
elsif ( $vnodeconfig eq "mdconfig" ) {
# FreeBSD 5+
print " using mdconfig.\n";
$vnodenumber = `mdconfig -a -t vnode -f /usr/tmpDSK`;
chomp($vnodenumber);
if ( $vnodenumber !~ /^md\d+/ ) {
return (1);
}
# FreeBSD < 5.1 uses disklabel
my $label = "bsdlabel";
my $results = `which $label`;
chomp($results);
if ( $results eq "" ) {
$label = "disklabel";
$results = `which $label`;
chomp($results);
if ( $results eq "" ) {
return (1);
}
}
if ( $label eq "bsdlabel" ) {
system( "$label", "-w", "$vnodenumber", "auto" );
}
else {
system( "$label", "-r", "-w", "$vnodenumber", "auto" );
}
system( "newfs", "${vnodenumber}a" );
system( "mount", "-o", "noexec,nosuid,rw", "/dev/${vnodenumber}a", "/tmp" );
}
else {
return (1);
}
}
else {
if ( $currentmntopts !~ m/loop/ ) {
system( 'mount', '-o', 'loop,noexec,nosuid,rw', '/usr/tmpDSK', '/tmp' );
}
}
return (0);
}
sub add_securetmp_to_rc {
my $rc_local = -e '/etc/rc.d/rc.local' ? '/etc/rc.d/rc.local' : '/etc/rc.local'; # Defaults to /etc/rc.local
if ( open my $rc_fh, '+<', $rc_local ) {
my $has_securetmp = 0;
my @lines;
while ( my $line = readline $rc_fh ) {
chomp $line;
if ( $line !~ m/^\s*#/ && $line =~ m/\/scripts\/securetmp/ ) {
$has_securetmp = 1;
push @lines, $line;
}
elsif ( $line =~ m/securetmp/ ) {
next;
}
else {
push @lines, $line;
}
}
seek( $rc_fh, 0, 0 );
print {$rc_fh} join( "\n", @lines ) . "\n";
if ( !$has_securetmp ) {
print {$rc_fh} "# Added by /scripts/securetmp\n";
print {$rc_fh} "/scripts/securetmp --auto\n";
}
truncate( $rc_fh, tell($rc_fh) );
close $rc_fh;
chmod 0755, $rc_local;
}
else {
print "Unable to update $rc_local\n";
exit 1;
}
}
# [Intending for FreeBSD OS]
sub remove_securetmp_from_rc {
my $rc_local = -e '/etc/rc.d/rc.local' ? '/etc/rc.d/rc.local' : '/etc/rc.local'; # Defaults to /etc/rc.local
my @lines;
if ( open my $rc_fh, '+<', $rc_local ) {
while ( my $line = readline $rc_fh ) {
chomp $line;
if ( $line =~ m/securetmp/ ) {
next;
}
else {
push @lines, $line;
}
}
seek( $rc_fh, 0, 0 );
print {$rc_fh} join( "\n", @lines ) . "\n";
truncate( $rc_fh, tell($rc_fh) );
close $rc_fh;
chmod 0755, $rc_local;
}
else {
print "Unable to update $rc_local\n";
exit 1;
}
}
sub check_loop_device {
my @loop_devices = glob('/dev/loop*');
return if !@loop_devices;
local $ENV{'LANG'} = 'C';
my $loop_device;
foreach my $dev ( sort @loop_devices ) {
my $test = Cpanel::SafeRun::Errors::saferunallerrors( 'losetup', $dev );
if ( $test =~ m/^loop:/ && $test =~ m/can't\s+open\s+device/i ) {
next;
}
elsif ( $test =~ m/^loop:/ && $test =~ m/can't\s+get\s+info/i ) {
$loop_device = 1;
last;
}
elsif ( $test =~ m/^$dev:/ ) {
$loop_device = 1;
last;
}
}
return $loop_device;
}
################################################################################
# reaper
################################################################################
sub reaper {
my $thedead;
while ( ( $thedead = waitpid( -1, 1 ) ) > 0 ) {
# the dead shall do what ?
}
$SIG{CHLD} = \&reaper;
}