File: //proc/self/root/scripts.20110531.215904.25158/mailperm
#!/usr/bin/perl
# cpanel - mailperm 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::Sys ();
use Cpanel::Usage ();
use Cpanel::SafeFind ();
use Cpanel::Hostname ();
use Cpanel::FileUtils::TouchFile ();
use Cpanel::Rlimit ();
use Cpanel::AccessIds::SetUids ();
use Cpanel::PwCache ();
use Cpanel::SafeFile ();
use Cpanel::Config::LoadCpUserFile ();
use Cpanel::Config::Users ();
use Cpanel::Config::LoadUserDomains ();
use Cpanel::SafetyBits ();
my $verbose = 0;
my $skiplocal = 0;
my $skipperm = 0;
my $checkuser;
# Argument processing
my %opts = (
'verbose' => \$verbose,
'skiplocaldomains' => \$skiplocal,
'skipserverperm' => \$skipperm,
);
Cpanel::Usage::wrap_options( \@ARGV, \&usage, \%opts );
exit 0 if ( $ENV{'DONT_RUN_MAILPERM'} );
my $maxmem = ( 256 * ( 1024 * 1024 ) );
Cpanel::Rlimit::set_rlimit($maxmem);
@ARGV = ( grep( !/^--/, @ARGV ) );
if (@ARGV) {
$checkuser = $ARGV[$#ARGV];
if ( !( Cpanel::PwCache::getpwnam($checkuser) )[0] ) {
warn "!! Specified user is not a valid system account !!\n\n";
usage();
exit 1;
}
}
my %localdomains;
my %remotedomains;
my @ensure_dir_files = ( '/etc/valiases/', '/etc/vdomainaliases/', '/etc/vfilters/' );
my $mailgid = ( getgrnam('mail') )[2];
my $hasmailnull = Cpanel::PwCache::getpwnam('mailnull') ? 1 : 0;
# Script does the same check, but a stat here is better
# than a system call to another script
if ( -x '/etc/security/msec' ) {
system '/scripts/mseclocal';
}
foreach my $dir (@ensure_dir_files) {
if ( !-e $dir ) {
mkdir $dir, 0711;
}
}
if ( !$skipperm ) {
require '/scripts/checkexim.pl';
checkeximperms();
foreach my $dir (@ensure_dir_files) {
chmod 0711, $dir;
}
}
chmod( 0666, '/dev/null' );
update_local_domains();
set_perms();
chmod( 0666, '/dev/null' );
sub update_local_domains {
Cpanel::FileUtils::TouchFile::touchfile('/etc/remotedomains');
Cpanel::FileUtils::TouchFile::touchfile('/etc/localdomains');
# Remote domains
my $rlock = Cpanel::SafeFile::safeopen( \*RD, '<', '/etc/remotedomains' ); #keep remotedomains and localdomains locked at the same time
if ($rlock) {
while ( my $line = readline( \*RD ) ) {
chomp $line;
$remotedomains{$line} = 1;
}
}
# Local domains
my $llock = Cpanel::SafeFile::safeopen( \*LD, '+<', '/etc/localdomains' ); #keep remotedomains and localdomains locked at the same time
if ($llock) {
while ( my $line = readline( \*LD ) ) {
chomp $line;
$localdomains{$line} = 1;
}
}
if ( !$skiplocal ) {
Cpanel::Config::LoadUserDomains::loaduserdomains( \%localdomains, 1 );
}
# Ensure hostname is always included
my $hostname = Cpanel::Hostname::gethostname();
$localdomains{$hostname} = 1;
my $update_failed = 0;
if ( $llock && $rlock ) {
# Update /etc/localdomains
seek( LD, 0, 0 );
foreach my $domain ( sort keys %localdomains ) {
next if $domain =~ m/^\*/; # drop wildcard domains
next if ( !Cpanel::Sys::validdomainname($domain) );
if ( !$remotedomains{$domain} ) {
print LD "$domain\n";
}
}
truncate( LD, tell(LD) );
}
else {
$update_failed = 1;
}
if ($llock) {
Cpanel::SafeFile::safeclose( \*LD, $llock );
}
if ($rlock) {
Cpanel::SafeFile::safeclose( \*RD, $rlock );
}
if ($update_failed) {
die "Unable to update /etc/localdomains because a lock could not be obtained on /etc/remotedomains and /etc/localdomains.";
}
}
sub set_perms {
my @USERS = Cpanel::Config::Users::getcpusers();
if ( !$checkuser ) { Cpanel::PwCache::init_passwdless_pwcache(); }
foreach my $cpuser (@USERS) {
next if ( $checkuser && $cpuser ne $checkuser );
my ( $useruid, $usergid, $homedir ) = ( Cpanel::PwCache::getpwnam($cpuser) )[ 2, 3, 7 ];
if ( !$useruid || !$usergid || !$homedir ) {
warn "Skipping invalid user $cpuser";
next;
}
my $cpuser_ref = Cpanel::Config::LoadCpUserFile::loadcpuserfile($cpuser);
next if ( !scalar keys %{$cpuser_ref} );
my @DOMAINS;
if ( $cpuser_ref->{'DOMAIN'} ) { push @DOMAINS, $cpuser_ref->{'DOMAIN'}; }
if ( ref $cpuser_ref->{'DOMAINS'} ) { push @DOMAINS, @{ $cpuser_ref->{'DOMAINS'} }; }
if ( -d "$homedir/mail" ) {
Cpanel::SafetyBits::safe_chmod( 0770, $useruid, $homedir . '/mail' );
if ( -e "$homedir/mail/inbox" ) {
Cpanel::SafetyBits::safe_chmod( 0660, $useruid, $homedir . '/mail/inbox' );
}
if ( !-l "$homedir/mail" ) {
# Fix perms on mbox style directories
if ( my $chmodpid = fork() ) {
waitpid( $chmodpid, 0 );
}
else {
Cpanel::AccessIds::SetUids::setuids( $useruid, $usergid ) || die "Could not setuid to $cpuser ($useruid,$usergid).";
$0 = 'mailperm - processing ' . $cpuser;
Cpanel::SafeFind::find(
{
wanted => sub {
return if ( !$File::Find::name );
next if ( -l $File::Find::name );
my ( $mode, $fuid, $fgid ) = ( stat(_) )[ 2, 4, 5 ];
$File::Find::name =~ /(.*)/;
my $safefile = $1;
if ( $fuid != $useruid || $fgid != $usergid ) {
my $changed = chown( $useruid, $usergid, $safefile );
if ($verbose) {
if ($changed) {
print "Fixed ownership of $File::Find::name : was ($fuid:$fgid), now ($useruid:$usergid)\n";
}
else {
print "Unable to fix ownership of $File::Find::name : currently ($fuid:$fgid), should be ($useruid:$usergid)\n";
}
}
}
return
if ( $File::Find::name =~
m/\/(?:courierimapkeywords|courierpop3dsizelist|dovecot-uidvalidity|dovecot-keywords|dovecot.index.log.2|dovecot.index|dovecot-uidlist|subscriptions|dovecot.index.cache|dovecot.index.log|maildirsize|courierimapuiddb)$/
|| $File::Find::name =~ m/\.cppop\.cache(?:\.msgs)?$/ );
return if -d _; # Reuse previous stat
$mode = sprintf '%04o', $mode & 07777;
if ( $mode ne '0660' ) {
my $changed = chmod( 0660, $safefile );
if ($verbose) {
if ($changed) {
print "Fixed permissions of $File::Find::name : was ($mode), now (0660)\n";
}
else {
print "Unable to fix permissions of $File::Find::name : currently ($mode), should be (0660)\n";
}
}
}
},
'follow' => 0,
'no_chdir' => 1
},
$homedir . '/mail'
);
exit 0;
}
}
else {
print "Skipping $homedir/mail. Directory is symlinked.\n";
}
}
if ($hasmailnull) {
if ( my $mailchownpid = fork() ) {
waitpid( $mailchownpid, 0 );
}
else {
my $targetuid = $useruid;
my $targetgid = ( $hasmailnull ? $mailgid : $usergid );
Cpanel::AccessIds::SetUids::setuids( $targetuid, $targetgid ) || die "Could not setuid to ($targetuid,$targetgid).";
if ( -d "$homedir/etc" ) {
chmod( 0750, "$homedir/etc" );
chown( $targetuid, $targetgid, "$homedir/etc" );
foreach my $domain (@DOMAINS) {
next if !-d "$homedir/etc/$domain";
chmod( 0750, "$homedir/etc/$domain" );
chmod( 0640, "$homedir/etc/$domain/shadow" );
chmod( 0644, "$homedir/etc/$domain/passwd" );
chown( $targetuid, $targetgid, "$homedir/etc/$domain" );
chown( $targetuid, $targetgid, "$homedir/etc/$domain/shadow" );
chown( $targetuid, $targetgid, "$homedir/etc/$domain/passwd" );
}
}
exit(0);
}
}
foreach my $domain (@DOMAINS) {
next if ( !$domain || $domain =~ /^\./ || $domain =~ /\// );
foreach my $dir (@ensure_dir_files) {
if ( !-e $dir . $domain ) {
Cpanel::FileUtils::TouchFile::touchfile( $dir . $domain );
}
chown $useruid, $mailgid, $dir . $domain; #safe root owns dir
chmod 0644, $dir . $domain; #safe root owns dir
}
}
}
}
sub usage {
print <<'EOM';
Usage: mailperm <modifier> <user>
Arguments:
<user> - Optional argument to specify the scope of
the permissions checks. The specified user must
be a valid system account.
Modifier Flags:
--skiplocaldomains - This optional argument bypasses
addition of missing domains to the /etc/localdomains
file when specified. The localdomains file specifies
to Exim that it should always accept delivery for
the listed domains. Remote domains are removed from
/etc/localdomains regardless of this flag.
--skipserverperm - This optional flag prevents modification
of the mail system files used by Exim and limits the
scope of permission modifications to the mail account
files.
--verbose - This optional flag signals the utility to
report detected permissions problems per user prior
to modifying any permissions.
--help - display this message and exit.
EOM
exit;
}