Server : Apache System : Linux server.lienzindia.com 4.18.0-348.7.1.el8_5.x86_64 #1 SMP Wed Dec 22 13:25:12 UTC 2021 x86_64 User : plutus ( 1007) PHP Version : 7.4.33 Disable Function : NONE Directory : /etc/exim/ |
Upload File : |
#!/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 ); } }