Current File : //etc/exim/exim.pl |
#!/usr/bin/perl
#VERSION=31
my %user_cache = ( 'root' => 0 );
my $local_connection_uid;
my $local_connection_user;
my $check_mail_validity_data = '';
my %sender_host_address_cache;
use constant {
_SENDER_SYSTEM => '-system-',
};
my $sender_lookup;
sub convert_address_directory_to_dovecot_lda_destination_username {
my $local_part = Exim::expand_string('$local_part');
my $domain = Exim::expand_string('$domain');
$primary_hostname ||= Exim::expand_string('$primary_hostname');
my $address_file = Exim::expand_string('$address_file');
if ( $address_file !~ m{mail/\Q$domain\E} ) {
return ( getpwuid($>) )[0];
}
else {
return $local_part . '@' . $domain;
}
}
sub convert_address_directory_to_dovecot_lda_mailbox {
my $address_file = Exim::expand_string('$address_file');
my ($mailbox) = $address_file =~ m{/\.([^\/]+)};
if ($mailbox) {
return "INBOX.$mailbox";
}
return 'INBOX';
}
sub unquoted_encode_string_literal {
my $string = shift;
return if !defined $string;
$string =~ s/\\N/\\N\\\\N\\N/g; # Only use / here for perl compat
return "\\N$string\\N";
}
sub getuid {
my $user = shift;
my $uid = Exim::expand_string( '${extract{2}{:}{${lookup passwd{' .unquoted_encode_string_literal($user). '}{$value}}}}' );
return defined $uid ? $uid : '';
}
sub user2uid {
my $user = shift;
return exists $user_cache{$user} ? $user_cache{$user} : ( $user_cache{$user} = getuid($user) );
}
sub spamd_is_available {
$spamdir = '/usr/bin/spamassassin';
if (-e $spamdir) {
return 1;
}
return 0;
}
sub store_spam {
my $sender_host_address = shift;
my $spam_score = shift;
my $now = time();
open( my $spam_fh, '>>', '/var/webuzo-data/mail/spamstore' );
#uncomment to deploy
# syswrite($spam_fh, $now . ':' . $sender_host_address . ':' . $spam_score . ":.\n");
close($spam_fh);
}
sub get_suspended_shell {
my ($user) = @_;
my $passwd_file_shell = Exim::expand_string( '${extract{6}{:}{${lookup passwd{' .unquoted_encode_string_literal($user) . '}}}}' );
if ( !length($passwd_file_shell) ) {
return '';
}
if ( $passwd_file_shell ne '/bin/false' ) {
return $passwd_file_shell;
}
return '/sbin/nologin';
}
# TODO:
sub increment_max_emails_per_hour_if_needed {
return 'no';
}
# Untaint a string for exim. This is not a perl untaint
sub untaint {
return $_[0];
}
sub mailtrapheaders {
$primary_hostname ||= Exim::expand_string('$primary_hostname');
my $original_domain = Exim::expand_string('$original_domain');
my $sender_address_domain = Exim::expand_string('$sender_address_domain');
my $originator_uid = Exim::expand_string('$originator_uid');
my $originator_gid = Exim::expand_string('$originator_gid');
my $caller_uid = Exim::expand_string('$caller_uid');
my $caller_gid = Exim::expand_string('$caller_gid');
my $headers =
"X-AntiAbuse: This header was added to track abuse, please include it with any abuse report\n"
. "X-AntiAbuse: Primary Hostname - $primary_hostname\n"
. "X-AntiAbuse: Original Domain - $original_domain\n"
. "X-AntiAbuse: Originator/Caller UID/GID - [$originator_uid $originator_gid] / [$caller_uid $caller_gid]\n"
. "X-AntiAbuse: Sender Address Domain - $sender_address_domain\n";
if ( -e '/etc/eximmailtrap' ) {
my $xsource = $ENV{'X-SOURCE'};
my $xsourceargs = $ENV{'X-SOURCE-ARGS'};
my $xsourcedir = maskdir( $ENV{'X-SOURCE-DIR'} );
$headers .= "X-Source: ${xsource}\n" . "X-Source-Args: ${xsourceargs}\n" . "X-Source-Dir: ${xsourcedir}";
}
return ($headers);
}
#TODO
sub sender_domain_can_dkim_sign {
return 0;
}
sub get_sender_lookup {
return $sender_lookup || '';
}
sub check_mail_permissions_headers {
return "X-Get-Message-Sender-Via: " . ( $primary_hostname ||= Exim::expand_string('$primary_hostname') ) . ": " . get_sender_lookup_method() . "\n" . "X-Authenticated-Sender: " . ( $primary_hostname ||= Exim::expand_string('$primary_hostname') ) . ": " . get_sender_lookup();
}
sub check_mail_validity {
my $uid = int( Exim::expand_string('$originator_uid') );
my $gid = int( Exim::expand_string('$originator_gid') );
$check_mail_validity_data = ':unknown:';
my $message_exim_id = Exim::expand_string('$message_exim_id');
my $domain = Exim::expand_string('$domain');
my $sender_address_domain = Exim::expand_string('$sender_address_domain');
$domain_sender_owner = getdomainowner($sender_address_domain);
if(!$domain_sender_owner || $domain_sender_owner eq ""){
return 'no';
}
# $domain_hl = get_hourly_limit($sender_address_domain);
$user_hl = get_hourly_limit($domain_sender_owner);
#Exim::log_write("!DEBUG! running check_mail_validity ".$uid." ".$gid." user:".$domain_sender_owner." domain:".$domain." sender_domain:".$sender_address_domain." dom_cl:".$dom_cl." user_hl:".$user_hl);
#if(!$user_hl || $user_hl == 0 ){
#return 'no';
#}
# _check_tracker_dir($domain_sender_owner);
_check_tracker_dir($sender_address_domain);
$dom_cl = get_emails_per_hour_count($sender_address_domain, time());
# +1 about to send this email (not send yet)
if( ($user_hl > 0) && ( ($dom_cl + 1) > $user_hl ) ){
$check_mail_validity_data = ":fail: Domain $sender_address_domain is reached hourly send limit.";
return 'yes';
}
# Set admin max send limit on each domain
my $admin_email_hourly_limit = get_hourly_limit_domain_admin();
if ( ( $admin_email_hourly_limit > 0 ) && ( ($dom_cl + 1) > $admin_email_hourly_limit ) && !( ( $user_hl >= 0 ) || ( $user_hl >= $admin_email_hourly_limit ) ) ) {
$check_mail_validity_data = ":fail: Domain $sender_address_domain is reached hourly send limit.";
return 'yes';
}
return 'no';
}
sub check_mail_validity_results {
return $check_mail_validity_data;
}
# It only increment the email count
sub increment_email_per_hour_count_if{
my $message_exim_id = Exim::expand_string('$message_exim_id');
my $domain = Exim::expand_string('$domain');
my $sender_address_domain = Exim::expand_string('$sender_address_domain');
$domain_sender_owner = getdomainowner($sender_address_domain);
if(!$domain_sender_owner || $domain_sender_owner eq ""){
return 'no';
}
if ( $sender_address_domain && $sender_address_domain ne _SENDER_SYSTEM ) {
# Check if domain exceed warning level then warn admin
my $mail_count = get_emails_per_day_count($sender_address_domain) + 1; # +1 for we are about to send.
my $emails_to_notify = get_daily_limit_notify();
if ( ( $emails_to_notify > 0 ) && ( $mail_count > $emails_to_notify ) ) {
if ( ! -e '/var/webuzo-data/mail/notify/' . $sender_address_domain ) {
create_daily_notify_file($sender_address_domain);
Exim::log_write("increment_email_per_hour_count_if Hit daily email notify limit for domain $sender_address_domain");
}
} else {
if( -e '/var/webuzo-data/mail/notify/' . $sender_address_domain){
unlink('/var/webuzo-data/mail/notify/' . $sender_address_domain);
}
}
}
# _check_tracker_dir($sender_address_domain);
increment_mail_count($sender_address_domain, time());
return 'no';
}
sub _check_tracker_dir {
my $domain = shift;
$domain =~ s/\///g; #jic
if ( !-e '/var/webuzo-data/mail/track/' . $domain ) {
mkdir( '/var/webuzo-data/mail', 0751 );
mkdir( '/var/webuzo-data/mail/track', 0750 );
mkdir( '/var/webuzo-data/mail/track/' . $domain, 0750 );
}
}
sub _check_limit_dir {
my $domain = shift;
$domain =~ s/\///g; #jic
if ( !-e '/var/webuzo-data/mail/limit/' . $domain ) {
mkdir( '/var/webuzo-data/mail', 0751 );
mkdir( '/var/webuzo-data/mail/limit', 0750 );
mkdir( '/var/webuzo-data/mail/limit/' . $domain, 0750 );
}
}
sub _check_sendbandwidth_log {
my ($u, $t) = @_;
$u =~ s/\///g; #jic
if ( !-e '/var/webuzo-data/mail/log/' . $u .'.'.$t ) {
mkdir( '/var/webuzo-data/mail', 0751 );
mkdir( '/var/webuzo-data/mail/log', 0750 );
mkdir( '/var/webuzo-data/mail/log/' . $u.'.'.$t, 0750 );
}
}
sub log_bandwidth {
my ($u,$d, $t) = @_;
my $bytes = Exim::expand_string('$message_size');
if ($bytes == -1) { return; }
if ( open( my $mail_log, '>>', "/var/webuzo-data/mail/log/".$u.".".$t ) ) {
print $mail_log "$d&bytes=$bytes\n";
close($mail_log);
}
}
sub get_emails_per_hour_count {
( ( stat( "/var/webuzo-data/mail/track/$_[0]/" . join( '.', ( gmtime( $_[1] || time() ) )[ 2, 3, 4, 5 ] ) ) )[7] || 0 );
}
sub get_emails_per_day_count {
my $domain = shift;
$domain =~ s/\///g; #jic
return 0 if ( !-e '/var/webuzo-data/mail/track/' . $domain );
my $total_size = 0;
if ( opendir( my $domain_track_fh, '/var/webuzo-data/mail/track/' . $domain ) ) {
while ( my $domaintime = readdir($domain_track_fh) ) {
next if ( $domaintime =~ /^\.\.?$/ );
my $tracker_file_size = ( stat("/var/webuzo-data/mail/track/$domain/$domaintime") )[7];
$total_size += $tracker_file_size;
}
}
return $total_size;
}
sub get_hourly_limit {
my $domain = shift;
$domain =~ s/\///g; #jic
return 0 if ( !-e '/var/webuzo-data/mail/limit/' . $domain );
if ( open( $max_hl, '<', '/var/webuzo-data/mail/limit/' . $domain ) ){
$maxemails = readline $max_hl;
close $max_hl;
return 0 if !$maxemails || $maxemails eq 'unlimited';
return ( $maxemails ? int($maxemails) : 0 );
}
return 0;
}
sub reached_max_emails_per_hour_count {
my $domain = shift;
$domain =~ s/\///g; #jic
my $max_allowed = int( shift || 0 );
my $time = shift || time();
if ($max_allowed) {
# AKA number_of_emails_sent >= $max_allowed
if ( get_emails_per_hour_count( $domain, $time ) >= $max_allowed ) {
return 1;
}
else {
return 0;
}
}
return 0;
}
sub increment_mail_count{
my ( $d, $t ) = @_;
_check_tracker_dir($d);
$t ||= time();
if ( open( my $email_hic, '>>', "/var/webuzo-data/mail/track/$d/" . join( '.', ( gmtime($t) )[ 2, 3, 4, 5 ] ) ) ) {
print {$email_hic} '1';
close($email_hic);
}
}
sub gethomedir {
my $user = shift;
return Exim::expand_string( '${extract{5}{:}{${lookup passwd{' . unquoted_encode_string_literal($user) . '}{$value}}}}' ) || '';
}
sub getdomainowner {
my $domain = shift;
substr($domain,0,4,'') if index($domain,'www.') == 0;
return Exim::expand_string( '${lookup{' . unquoted_encode_string_literal($domain) . '}lsearch{/etc/userdomains}{$value}}' ) || '';
}
sub lookup_key_in_file {
my ( $file, $key ) = @_;
return Exim::expand_string( '${lookup{' . unquoted_encode_string_literal($key) . '}lsearch{' . $file . '}{$value}}' ) || '';
}
sub isdemo {
my $user = shift;
return if ( !$user );
return 0 if $user eq '0' || $user eq 'exim' || $user eq 'webuzo' || $user eq 'root';
if ( $user =~ /^\d+$/ ) {
return user_exists_in_db( $user, '/etc/demouids' );
}
return user_exists_in_db( $user, '/etc/demousers' );
}
sub user_exists_in_db {
my ( $user, $db ) = @_;
return 0 if !$user || $user !~ tr{ \t}{}c;
return Exim::expand_string( '${lookup{' . unquoted_encode_string_literal($user) . '}lsearch{' . $db . '}{1}{0}}' ) || '0';
}
sub get_daily_limit_notify {
# The value is the size of the file so we can avoid the open/close overhead (just a stat)
return 0 if ( !-e '/var/webuzo-data/mail/daily_notify' );
my $limit = ( stat('/var/webuzo-data/mail/daily_notify') )[7];
if ( !defined $limit ) { $limit = 0; }
return $limit;
}
sub get_hourly_limit_domain_admin {
# The value is the size of the file so we can avoid the open/close overhead (just a stat)
return 0 if ( !-e '/var/webuzo-data/mail/hourly_limit' );
my $limit = ( stat('/var/webuzo-data/mail/hourly_limit') )[7];
if ( !defined $limit ) { $limit = 0; }
return $limit;
}
sub create_daily_notify_file {
my $domain = shift;
$domain =~ s/\///g; #jic
mkdir( '/var/webuzo-data/mail/notify', 0750 ) if !-e '/var/webuzo-data/mail/notify';
if ( open( my $daily_limit_fh, '>', '/var/webuzo-data/mail/notify/' . $domain ) ) {
close $daily_limit_fh;
}
return undef;
}
sub resolve_vhost_owner {
if ( file_exists('/var/webuzo-data/mail/trust_x_php_script') ) {
if ( my $x_php_script = Exim::expand_string('$h_x-php-script:') ) {
my ( $servername, $uri ) = split( m{/}, $x_php_script, 2 );
if ( $uri =~ m/^\/?\~([^\/\s]+)/ ) {
my $http_user = $1;
my $uid = user2uid($http_user);
Exim::log_write("nobody send identification H=localhost A=127.0.0.1 U=$http_user ID=$uid B=acl_c_vhost_owner M=trust_x_php_script");
return $uid . ':' . '//' . $servername . '/' . $uri . ' ';
}
elsif ( my $http_user = getdomainowner($servername) ) {
my $uid = user2uid($http_user);
Exim::log_write("nobody send identification H=localhost A=127.0.0.1 U=$http_user ID=$uid B=acl_c_vhost_owner M=trust_x_php_script");
return $uid . ':' . '//' . $servername . '/' . $uri . ' ';
}
}
}
return;
}
sub getdomainfromaddress {
my $address = shift;
$address =~ s/\/.*$//g if $address =~ tr/\///;
if ( $address =~ tr/@+%:// ) {
unless ( $address =~ tr/@// ) {
$address =~ s/[+:%]/@/;
}
$primary_hostname ||= Exim::expand_string('$primary_hostname');
if ( $address =~ m/[@]\Q$primary_hostname\E$/ ) {
return getusersdomain( ( split( m/[@]/, $address, 2 ) )[0] ) || _SENDER_SYSTEM;
}
else {
return ( split( m/[@]/, $address, 2 ) )[1];
}
}
else {
return getusersdomain($address) || _SENDER_SYSTEM;
}
}
my %domain_to_user_cache;
sub getusersdomain {
return '' if !$_[0] || $_[0] eq 'root' || $_[0] =~ tr{/}{} || !-e "/var/webuzo/users/$_[0]";
return ( $domain_to_user_cache{ $_[0] } || ( $domain_to_user_cache{ $_[0] } = lookup_key_in_file( '/etc/domainusers', $_[0] ) ) );
}
sub get_recent_authed_mail_ips_text_entry {
my ( $sender, $domain ) = get_recent_authed_mail_ips_entry(@_);
return join( '|', ( $sender || '' ), $domain );
}
sub popbeforesmtpwarn {
if ( my @possible_users = _get_possible_users_from_recent_authed_mail_ips_users() ) {
return ( "X-PopBeforeSMTPSenders: " . join( ",", @possible_users ) );
}
return '';
}
sub get_recent_authed_mail_ips_entry {
my $log = shift;
# SENDING OVER POP B4 SMTP or NOAUTH
# case 43151, case 43150
$get_recent_authed_mail_ips_lookup_method = '';
my $sender_host_address = Exim::expand_string('$sender_host_address');
# Exim::log_write("!DEBUG! get_recent_authed_mail_ips_entry sender_host_address=[$sender_host_address] log=[$log]");
my ( $sender, $domain );
if ( exists $sender_recent_authed_mail_ips_address_cache{$sender_host_address} ) {
# Exim::log_write("!DEBUG! get_recent_authed_mail_ips_entry sender_host_address=[$sender_host_address] USING CACHE");
( $sender, $domain, $get_recent_authed_mail_ips_lookup_method ) = @{ $sender_recent_authed_mail_ips_address_cache{$sender_host_address} };
$get_recent_authed_mail_ips_lookup_method = "cached: " . $get_recent_authed_mail_ips_lookup_method;
$log = 0;
}
else {
my $recent_authed_mail_ips_users_is_up_to_date = ( stat('/etc/recent_authed_mail_ips_users') )[9] + 7200 > time() ? 1 : 0;
my $sender_address_domain;
# Exim::log_write("!DEBUG! get_recent_authed_mail_ips_entry sender_host_address=[$sender_host_address] recent_authed_mail_ips_users_is_up_to_date= $recent_authed_mail_ips_users_is_up_to_date");
# If we have a recent_authed_mail_ips_users file that is up to date, we can verify the ip matches
if ($recent_authed_mail_ips_users_is_up_to_date) {
# This is what the user has claimed as the sender
my $sender_address = Exim::expand_string('$sender_address');
my $from_h_domain = Exim::expand_string('${domain:$h_from:}');
my $from_h_localpart = Exim::expand_string('${local_part:$h_from:}');
my $from_h = "$from_h_localpart\@$from_h_domain";
# First we try to find the address in the recent_authed_mail_ips_users file (with a cached exim lookup)
if ( my @possible_users = _get_possible_users_from_recent_authed_mail_ips_users() ) {
if ( grep { tr/@// ? $from_h eq $_ : $from_h eq $_ . '@' . $primary_hostname } @possible_users ) {
$sender = $from_h;
$domain = getdomainfromaddress($from_h);
$get_recent_authed_mail_ips_lookup_method = "full match of from_h in recent_authed_mail_ips_users";
}
elsif ( grep { tr/@// ? $sender_address eq $_ : $sender_address eq $_ . '@' . $primary_hostname } @possible_users ) {
$sender = $sender_address;
$domain = getdomainfromaddress($sender_address);
$get_recent_authed_mail_ips_lookup_method = "full match of sender_address in recent_authed_mail_ips_users";
}
elsif ( ( $sender_address_domain = ( split( m/\@/, $sender_address ) )[1] ) && grep( m/\@\Q$sender_address_domain\E$/, @possible_users ) ) {
$domain = $sender_address_domain;
$sender = '-unknown-@' . $domain;
$get_recent_authed_mail_ips_lookup_method = "match of sender_address_domain in recent_authed_mail_ips_users";
}
elsif ( grep { tr/@// ? ( $from_h eq $_ ) : ( $from_h_localpart eq $_ && ( !length $from_h_domain || $from_h_domain eq $primary_hostname ) ) } @possible_users ) {
$sender = $from_h;
$domain = $from_h_domain;
$get_recent_authed_mail_ips_lookup_method = "full match of from_h in recent_authed_mail_ips_users";
}
elsif ( grep( m/\@\Q$from_h_domain\E$/, @possible_users ) ) {
$domain = $from_h_domain;
$sender = '-unknown-@' . $from_h_domain;
$get_recent_authed_mail_ips_lookup_method = "match of from_h_domain in recent_authed_mail_ips_users";
}
elsif ( $possible_users[0] && $possible_users[0] eq '-alwaysrelay-' ) {
if ($from_h_domain) {
Exim::log_write("$sender_host_address in /etc/alwaysrelay trusting from_h_domain of: $from_h_domain and from_h_localpart: $from_h_localpart");
$domain = $from_h_domain;
$sender = $from_h;
$get_recent_authed_mail_ips_lookup_method = "in alwaysrelay trusted from_h";
}
else {
Exim::log_write("$sender_host_address in /etc/alwaysrelay trusting sender_address_domain of: $sender_address_domain");
$domain = $sender_address_domain;
$sender = $sender_address;
$get_recent_authed_mail_ips_lookup_method = "in alwaysrelay trusted sender_address";
}
}
else {
# If none of them matched, we have to assume they authenticated in some we so we go with the first one
$domain = getdomainfromaddress( $possible_users[0] );
$sender = $possible_users[0];
$get_recent_authed_mail_ips_lookup_method = "in recent_authed_mail_ips_users using first address";
}
if ( $sender =~ m/^\*/ ) {
$sender =~ s/^\*/-unknown-/;
}
$sender_recent_authed_mail_ips_address_cache{$sender_host_address} = [ $sender, $domain, $get_recent_authed_mail_ips_lookup_method ];
}
}
# we need to check alwaysrelay since we don't require recentauthedmailiptracker to be enabled
if ( !$domain && -e '/etc/alwaysrelay' ) {
my $alwaysrelay_result = Exim::expand_string('${lookup{$sender_host_address}iplsearch{/etc/alwaysrelay}{$sender_host_address $value}}');
if ($alwaysrelay_result) {
my ( $alwaysrelay_ip, $alwaysrelay_user ) = split( /\s+/, $alwaysrelay_result );
if ($alwaysrelay_user) {
$domain = getdomainfromaddress($alwaysrelay_user);
$sender = $alwaysrelay_user;
$get_recent_authed_mail_ips_lookup_method = "full match in alwaysrelay with recentauthedmailiptracker disabled";
Exim::log_write("$sender_host_address in /etc/alwaysrelay using domain $domain from lookup of $alwaysrelay_user");
}
if ( !$domain ) {
$domain = $sender_address_domain = ( split( /\@/, Exim::expand_string('$sender_address') ) )[1];
$sender = "-unknown-\@$domain";
$get_recent_authed_mail_ips_lookup_method = "in alwaysrelay with recentauthedmailiptracker disabled";
Exim::log_write("$sender_host_address in /etc/alwaysrelay trusting sender_address_domain of: $sender_address_domain");
}
}
# no need to check /etc/alwaysrelay as they are automaticlly built into recent_authed_mail_ips_users
}
}
if ($domain) {
if ($log) {
my $message_exim_id = Exim::expand_string('$message_exim_id');
my $sender_host_name = Exim::expand_string('${if match_ip{$sender_host_address}{+loopback}{localhost}{$sender_host_name}}');
my $sender_host_port = Exim::expand_string('$sender_host_port');
my $recent_authed_mail_ips_local_user = getdomainowner($domain);
my $recent_authed_mail_ips_local_uid = user2uid($recent_authed_mail_ips_local_user);
Exim::log_write("SMTP connection identification H=$sender_host_name A=$sender_host_address P=$sender_host_port U=$recent_authed_mail_ips_local_user ID=$recent_authed_mail_ips_local_uid S=$sender B=get_recent_authed_mail_ips_entry");
}
return ( $sender, $domain, $get_recent_authed_mail_ips_lookup_method );
}
return ( '', '', '' );
}
sub _get_possible_users_from_recent_authed_mail_ips_users {
my $recent_authed_mail_ips_users_result = Exim::expand_string('${lookup{$sender_host_address}lsearch{/etc/recent_authed_mail_ips_users}{$value}}');
return map {
s/\/.*$//g if tr/\///;
tr/+%:/@/;
$_;
} split( m/\s*\,\s*/, $recent_authed_mail_ips_users_result );
}
sub maskdir {
my ($dir) = @_;
# Try the user first
my $maskeddir = $dir;
my ($likely_user) = ( split( m{/}, $dir ) )[2];
if ( my $likely_homedir = gethomedir($likely_user) ) {
chop $likely_homedir if substr( $likely_homedir, -1 ) eq '/';
if ( rindex( $dir, "$likely_homedir/", 0 ) == 0 ) {
substr( $maskeddir, 0, length($likely_homedir), getusersdomain($likely_user) . ":" );
return $maskeddir;
}
}
# Next try all users in /etc/passwd
if ( open my $passwd_fh, '<', "/etc/passwd" ) {
while ( readline($passwd_fh) ) {
my ( $homedir, $uid, $user ) = ( split( /:/, $_ ) )[ 0, 2, 5 ];
next if $uid < 100 || length $homedir < 3;
chop $homedir if substr( $homedir, -1 ) eq '/';
if ( rindex( $dir, "$homedir/", 0 ) == 0 ) {
substr( $maskeddir, 0, length($homedir), getusersdomain($user) . ":" );
return $maskeddir;
}
}
}
else {
warn "open(/etc/passwd): $!";
}
return $dir;
}
sub get_message_sender_domain {
my ( $uid, $gid, $log ) = @_;
$uid = int( Exim::expand_string('$originator_uid') ) if !defined $uid;
$gid = int( Exim::expand_string('$originator_gid') ) if !defined $gid;
return ( ( get_message_sender( $uid, $gid, $log ) )[1] ) || '';
}
# This must match the logic extactly for Cpanel::TailWatch::EximStats ($direction eq '<=')
sub get_message_sender {
#passes but not for production
#use strict;
my ( $uid, $gid, $log ) = @_;
my ( $authenticated_local_user, $authenticated_id, $recent_authed_mail_ips_text_entry, $domain, $counted_domain, $sender, $is_mailman, $username );
$sender_lookup_method = '';
my ( $acl_c_vhost_owner, $acl_c_vhost_owner_url ) = split( m{:}, Exim::expand_string('$acl_c_vhost_owner') || '', 2 );
my $message_exim_id = Exim::expand_string('$message_exim_id');
# SMTP AUTH
if ( $authenticated_id = Exim::expand_string('$authenticated_id') ) {
$authenticated_id =~ s/[\r\n\f]//g;
if ( $authenticated_id eq 'nobody' ) {
if ($acl_c_vhost_owner) {
$authenticated_id = uid2user($acl_c_vhost_owner);
}
$sender_lookup_method = 'uid via acl_c_vhost_owner from authenticated_id: ' . $authenticated_id . ' from ' . $acl_c_vhost_owner_url;
}
else {
$sender_lookup_method = 'authenticated_id: ' . $authenticated_id;
}
$sender = $authenticated_id;
$domain = getdomainfromaddress($authenticated_id);
# If the sender owns the domain they are sending
# from we can trust it
if ( length $sender && $sender !~ tr/\@// ) {
( $sender, $domain, $sender_lookup_method ) = resolve_authenticated_sender( $sender, $domain, $sender_lookup_method );
}
#Exim::log_write("!DEBUG! get_message_sender() got domain $domain from authenticated_id ($authenticated_id)");
}
# FROM A CONNECTION TO LOCALHOST (linux only)
elsif ( $authenticated_local_user = Exim::expand_string('${if match_ip{$sender_host_address}{+loopback}{$acl_c_authenticated_local_user}{}}') ) {
my $authenticated_local_uid = user2uid($authenticated_local_user);
my $sender_host_address = Exim::expand_string('$sender_host_address');
my $sender_host_name = Exim::expand_string('${if match_ip{$sender_host_address}{+loopback}{localhost}{$sender_host_name}}');
my $sender_host_port = Exim::expand_string('$sender_host_port');
$domain = getusersdomain($authenticated_local_user) || _SENDER_SYSTEM;
$sender = $authenticated_local_user;
$sender_lookup_method = 'acl_c_authenticated_local_user: ' . $authenticated_local_user;
if ($log) { Exim::log_write("SMTP connection identification H=$sender_host_name A=$sender_host_address P=$sender_host_port M=$message_exim_id U=$authenticated_local_user ID=$authenticated_local_uid S=$sender B=authenticated_local_user"); } #replay for tailwatchd
#Exim::log_write("!DEBUG! get_message_sender() got domain $domain from acl_c_authenticated_local_user");
}
# RELAY HOSTS
elsif ( $recent_authed_mail_ips_text_entry = Exim::expand_string('$acl_c_recent_authed_mail_ips_text_entry') ) {
#FIXME: need to get sender
( $sender, $domain ) = split( /\|/, $recent_authed_mail_ips_text_entry );
my $sender_host_address = Exim::expand_string('$sender_host_address');
my $sender_host_name = Exim::expand_string('${if match_ip{$sender_host_address}{+loopback}{localhost}{$sender_host_name}}');
my $sender_host_port = Exim::expand_string('$sender_host_port');
my $recent_authed_mail_ips_local_user = getdomainowner($domain);
my $recent_authed_mail_ips_local_uid = user2uid($recent_authed_mail_ips_local_user);
$sender_lookup_method = 'acl_c_recent_authed_mail_ips_text_entry: ' . $recent_authed_mail_ips_text_entry;
if ($log) { Exim::log_write("SMTP connection identification H=$sender_host_name A=$sender_host_address P=$sender_host_port M=$message_exim_id U=$recent_authed_mail_ips_local_user ID=$recent_authed_mail_ips_local_uid S=$sender B=recent_authed_mail_ips_domain") }
#Exim::log_write("!DEBUG! get_message_sender() got domain $domain from acl_c_recent_authed_mail_ips_text_entry");
}
elsif ( Exim::expand_string('$received_protocol') eq 'local' ) {
my $sender_ident = Exim::expand_string('$sender_ident');
$sender_ident =~ s/[\r\n\f]//g;
my $used_vhost_owner_lookup = 0;
if ( $sender_ident eq 'nobody' ) {
if ($acl_c_vhost_owner) {
$used_vhost_owner_lookup = 1;
$sender_ident = uid2user($acl_c_vhost_owner);
}
}
$sender = $sender_ident;
$domain = getusersdomain($sender_ident) || _SENDER_SYSTEM;
$sender_lookup_method = 'sender_ident via received_protocol == local: ' . $sender_ident . ( $used_vhost_owner_lookup ? ' : used vhost owner lookup from: ' . $acl_c_vhost_owner_url : '' );
# If the sender owns the domain they are sending
# from we can trust it
if ( length $sender && $sender !~ tr/\@// ) {
( $sender, $domain, $sender_lookup_method ) = resolve_authenticated_sender( $sender, $domain, $sender_lookup_method );
}
#Exim::log_write("!DEBUG! get_message_sender() got domain $domain from local user ($sender_ident)");
}
else {
$mail_gid ||= int( ( getgrnam('mail') )[2] );
#Exim::log_write("!DEBUG! mailgid=$mail_gid == gid=$gid (uid=$uid)");
if ( $gid == $mail_gid ) {
my ( $recent_authed_mail_ips_sender, $recent_authed_mail_ips_domain, $recent_authed_mail_ips_lookup_method ) = get_recent_authed_mail_ips_entry();
if ($recent_authed_mail_ips_domain) {
$sender = $recent_authed_mail_ips_sender;
$sender =~ s/[\r\n\f]//g;
$domain = $recent_authed_mail_ips_domain;
$sender_lookup_method = 'mailgid via get_recent_authed_mail_ips_entry: ' . $sender . "/$recent_authed_mail_ips_lookup_method";
#Exim::log_write("!DEBUG! get_message_sender() got domain $domain from get_recent_authed_mail_ips_entry() or sender_address_domain");
}
$primary_hostname ||= Exim::expand_string('$primary_hostname');
if ( $domain && $domain eq $primary_hostname ) {
$username = Exim::expand_string('$sender_address_local_part');
$sender = $username;
$domain = getusersdomain($username) || _SENDER_SYSTEM;
$sender_lookup_method = 'mailgid via primary_hostname' . "/$recent_authed_mail_ips_lookup_method";
}
if ( !$domain ) {
# If we cannot find the sender and it is not _SENDER_SYSTEM it is a redirected/forwarded message
my $parent_domain = Exim::expand_string('$parent_domain');
my $parent_local_part = Exim::expand_string('$parent_local_part');
my $local_part = Exim::expand_string('$local_part');
my $delivery_domain = Exim::expand_string('$domain');
$parent_domain =~ s/[^\w\.\-\/]//g;
$parent_local_part =~ s/[^\w\.\-\/]//g;
$local_part =~ s/[^\w\.\-\/]//g;
$delivery_domain =~ s/[^\w\.\-\/]//g;
# If we have a parent_domain its probably a redirect
if ( $parent_domain && ( $parent_domain ne $delivery_domain || $parent_local_part ne $local_part ) ) {
# If the parent_domain is the primary_hostname its a localuser redirect
if ( my $local_user = $parent_domain eq $primary_hostname ? $parent_local_part : getdomainowner($parent_domain) ) {
my $local_uid = user2uid($local_user);
my $redirected_domain = $parent_domain eq $primary_hostname ? getusersdomain($parent_local_part) : $parent_domain;
if ($log) { Exim::log_write("SMTP connection identification D=$redirected_domain O=$parent_local_part\@$parent_domain E=$local_part\@$delivery_domain M=$message_exim_id U=$local_user ID=$local_uid B=redirect_resolver") }
; #replay for tailwatchd
$domain = $redirected_domain;
$sender = $parent_domain eq $primary_hostname ? $local_user : "$parent_local_part\@$parent_domain";
$sender_lookup_method = "redirect/forwarder owner $parent_local_part\@$parent_domain -> $local_part\@$delivery_domain";
}
}
}
if ( !$domain ) {
$sender_lookup_method = 'mailgid no entry from get_recent_authed_mail_ips_entry';
#Exim::log_write("!DEBUG! get_message_sender() failed to get the domain. However the sender domain claims to be $sender_address_domain");
}
}
else {
# FROM A SHELL OR CGI
$username = uid2user($uid);
if ($username) {
if ( $username eq 'nobody' ) {
if ($acl_c_vhost_owner) {
$username = uid2user($acl_c_vhost_owner);
}
$sender_lookup_method = 'uid via acl_c_vhost_owner from shell cgi: ' . $username . ' from: ' . $acl_c_vhost_owner_url;
}
else {
$sender_lookup_method = 'uid via shell cgi: ' . $username;
}
$domain = getusersdomain($username) || _SENDER_SYSTEM;
$sender = $username;
}
# If the sender owns the domain they are sending
# from we can trust it
if ( length $sender && $sender !~ tr/\@// ) {
( $sender, $domain, $sender_lookup_method ) = resolve_authenticated_sender( $sender, $domain, $sender_lookup_method );
}
#Exim::log_write("!DEBUG! get_message_sender() got domain $domain from UID");
}
}
if ($domain) {
$domain =~ s/[^\w\.\-\/]//g;
$domain = lc $domain;
$counted_domain = $domain;
if ($sender) {
$sender =~ tr/+%:/@/;
$sender =~ s/[^\w\.\-\/\@]//g;
}
}
$sender_lookup = $sender;
if ( $log && $message_exim_id ) {
$username ||= ( ( $sender =~ tr{@}{} ) ? getdomainowner( ( split( m{@}, $sender ) )[1] ) : $sender );
if ($username) {
# Will log as 2017-05-26 13:42:22 1dEKBq-0007HB-6R Sender identification S=nick
Exim::log_write("Sender identification U=$username D=$domain S=$sender"); #replay for tailwatchd
}
}
return ( $sender, $domain, $counted_domain, $is_mailman );
}
sub uid2user {
my $uid = shift;
return exists $uid_cache{$uid} ? $uid_cache{$uid} : ( $uid_cache{$uid} = ( getpwuid($uid) )[0] );
}
sub user2uid {
my $user = shift;
return exists $user_cache{$user} ? $user_cache{$user} : ( $user_cache{$user} = getuid($user) );
}
sub get_sender_from_uid {
my $uid = int( Exim::expand_string('$originator_uid') );
my $user = uid2user($uid);
return getdomainfromaddress($user);
}
sub resolve_authenticated_sender {
my ( $sender, $domain, $sender_lookup_method ) = @_;
my $sender_address = Exim::expand_string('$sender_address');
my $sender_address_domain = Exim::expand_string('$sender_address_domain');
# We only want to use the sender in the from header if they have already
# authenticated with at least the permissions of the account
my ( $from_h_sender, $from_h_localpart, $from_h_domain ) = _get_from_h_sender();
$primary_hostname ||= Exim::expand_string('$primary_hostname');
# The user expects to be able to just set the From: headers
# we try to accomodate that first if they have permissions on the account
if ( $from_h_domain eq $primary_hostname ) {
$sender_lookup_method .= "/primary_hostname/system user";
}
elsif ( $sender eq getdomainowner($from_h_domain) ) {
$sender = $from_h_localpart . '@' . $from_h_domain;
$domain = $from_h_domain;
$sender_lookup_method .= "/from_h";
}
# otherwise we fallback to the sender_address_domain
elsif ( $sender eq getdomainowner($sender_address_domain) ) {
$sender = $sender_address;
$domain = $sender_address_domain;
$sender_lookup_method .= "/sender_address_domain";
}
else {
# finally we accept that we don't know who sent it besdies the
# authenticated user
$sender_lookup_method .= "/only user confirmed/virtual account not confirmed";
}
return ( $sender, $domain, $sender_lookup_method );
}
# Obtain the from header from the message
# We fallback to the envelope sender if there
# is no from header set (ie sendmail -bt or missing From header)
sub _get_from_h_sender {
my $from_h_domain = Exim::expand_string('${domain:$h_from:}');
my $from_h_local_part = Exim::expand_string('${local_part:$h_from:}');
if ( length $from_h_local_part ) {
if ( length $from_h_domain ) {
return ( $from_h_local_part . '@' . $from_h_domain, $from_h_local_part, $from_h_domain );
}
else {
$primary_hostname ||= Exim::expand_string('$primary_hostname');
return ( $from_h_local_part . '@' . $primary_hostname, $from_h_local_part, $primary_hostname );
}
}
else {
# Handle fallback to sender_address when message is missing a from header
my $sender_address_domain = Exim::expand_string('$sender_address_domain');
my $sender_address_local_part = Exim::expand_string('$sender_address_local_part');
return ( $sender_address_local_part . '@' . $sender_address_domain, $sender_address_local_part, $sender_address_domain );
}
}