/* This file is part of mailfrom filter Copyright (C) 2005, 2006 Sergey Poznyakoff This file is default configuration for mailfrom in Alt Linux. It written by Sergey Afonin. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include_once #include_once #include_once #require sa #pragma regex +extended +icase set mailfrom_address "" /* Enable MTA-specific functions. Values: sendmail - can be used meta1 - not used now postfix - not used now */ set mta "" set graylistactive 0 set msg_header "" set msg_header_last_received "" set infected_received_from "" set mail_from "" set rcpt_to "" set queue_id "" set queue_id_connect "" set helo_from_remote "" set virusmaster "" set callback_chk_exclude "\n" set massallocated_chk_exclude "\n" set graylist_exclude "\n" set explain_msg "" set spam_verification_needed 0 set spamreject 0 set spammaster "spamalert@localhost" set spamd_port "" set explain_msg_spam "" set X_Spam_Suffix "" set ma_check 2 set bad_mail_from ".*\\|.*" set good_mx "^((mx|mail|mta|relay|list|smtp|srv|server|dot).*)|(.*.(google|subscribe|yahoo|hotmail|mail)\.(com|ru))$" set not_verifiable_mx "(\.mx\.mail\.yahoo\.com|\.namaeserver\.com)$" #include_once #include_once prog connect do set queue_id_connect $i if %ma_check = 1 and not match_cidr_list(${client_addr}, %massallocated_chk_exclude) = 1 and not dbmap("/etc/mailfromd/whitelist.db", "massallocated:" $client_addr, 1) set ma_hit_num massallocated_hit(${client_ptr}, %massallocated_regexps) if %ma_hit_num > 0 echo "%queue_id_connect: client's IP [" ${client_ptr} "]. Hit to R" %ma_hit_num "." reject fi fi done prog helo do # Save the helo of host for further use set helo_from_remote $1 echo "%queue_id_connect: helo=" %helo_from_remote " client_addr=" ${client_addr} done prog envfrom do set queue_id $i if not %queue_id = %queue_id_connect echo "%queue_id_connect: new message with queue id: " %queue_id fi if %mta = "sendmail" access_db_check("/etc/mail/access.db", "connect:" $client_addr) access_db_check("/etc/mail/access.db", tolower("from:" $f)) fi if dbmap("/etc/mailfromd/whitelist.db", "global:" $client_addr, 1) or dbmap("/etc/mailfromd/whitelist.db", tolower("global:" $client_addr "-" $f), 1) or dbmap("/etc/mailfromd/whitelist.db", tolower("global:" $f), 1) accept fi if $f matches %bad_mail_from echo "%queue_id: bad mail from <$f>" reject 550 5.1.0 "bad mail from <$f>" fi set msg_header_last_received "Received: from " %helo_from_remote " (" hostname(${client_addr}) " ["${client_addr}"])\r\n\t" "by " %ehlo_domain " (" %__package__ "/" %__version__ ") id " $i ";\r\n\t" strftime('%a, %d %b %Y %T %z (%Z)', time()) set infected_received_from hostname(${client_addr}) " ["${client_addr}"]" set mail_from "<" $f ">" if %ma_check = 2 and not match_cidr_list(${client_addr}, %massallocated_chk_exclude) = 1 and not dbmap("/etc/mailfromd/whitelist.db", "massallocated:" $client_addr, 1) set ma_hit_num massallocated_hit(${client_ptr}, %massallocated_regexps) if %ma_hit_num > 0 reject 550 5.7.1 "Your reverse name is " ${client_ptr} "\nAccess from dsl/dial-up/cable/e.t.c. relays denied, client's IP [" ${client_addr} "]. Hit to R" %ma_hit_num ". " %explain_msg fi fi if hostname ${client_addr} = ${client_addr} echo "%queue_id: relay does not have a reverse name, graylistactive set to 1." set graylistactive 1 elif %helo_from_remote matches "^[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}$" or not %helo_from_remote matches "\." echo "%queue_id: bad helo, graylistactive set to 1." set graylistactive 1 fi if not hostname ${client_addr} matches %good_mx echo "%queue_id: bad reverse name for smtp relay, spam_verification_needed set to 1" set spam_verification_needed 1 fi catch failure or temp_failure do echo "%queue_id: Caught exception " $2 ", graylistactive set to 1." set graylistactive 1 done if $f = "" or dbmap("/etc/mailfromd/whitelist.db", "from:" $client_addr, 1) or dbmap("/etc/mailfromd/whitelist.db", tolower("from:" $client_addr "-" $f), 1) or dbmap("/etc/mailfromd/whitelist.db", tolower("from:" $f), 1) pass elif $f mx matches %not_verifiable_mx echo "%queue_id: mx for $f always accepts bad rcpt to, graylisted." set graylistactive 1 else if match_cidr_list(${client_addr}, %callback_chk_exclude) = 0 on poll $f do when success: pass when not_found: if %last_poll_recv = "" if not %cache_used echo "%queue_id: last_poll_recv empty, \"" $f "\" is not in cache" reject 550 5.1.0 "Sender validity for <" $f "> can not be confirm by MX" else reject 550 5.1.0 "Sender validity for <" $f "> hasn't been confirmed by MX, data has been cached for " %n_cache_time " sec." fi else reject 550 5.1.0 "Your MX reply is \"" %last_poll_recv "\"" "\nSender validity for <" $f "> not confirmed by MX \"" %last_poll_host "\"" fi when failure: echo "%queue_id: last_poll_recv = " %last_poll_recv " last_poll_host = " %last_poll_host reject 550 5.1.0 "Sender validity for <" $f "> can not be be verified, permanent error occurred" when temp_failure: if %last_poll_recv = "" tempfail 450 4.4.1 "Sender validity for <" $f "> not confirmed by MX, MX does not respond" elif %last_poll_recv = "nothing" tempfail 450 4.4.1 "Your MX has not replied a some command. Possible long timeout." "\nSender validity for <" $f "> not confirmed by MX \"" %last_poll_host "\"" else tempfail 450 4.7.1 "Your MX reply is \"" %last_poll_recv "\"" "\nSender validity for <" $f "> not confirmed by MX \"" %last_poll_host "\"" fi done fi fi if rate(tolower($f "-" ${client_addr}), interval("1 hour 30 minutes")) > 100 echo "%queue_id: WARNING: Too big rate for E-Mail:IP pair $f-${client_addr}" /* tempfail 450 4.7.0 "Mail sending rate exceeded. Try again later" */ fi done prog envrcpt do set rcpt_to %rcpt_to $1 "\r\n" set gltime interval("25 minutes") if %graylistactive = 1 and not dbmap("/etc/mailfromd/whitelist.db", "graylist:" $client_addr, 1) and not match_cidr_list(${client_addr}, %graylist_exclude) = 1 if greylist($client_addr "-" $f "-" $rcpt_addr, %gltime) if %greylist_seconds_left = %gltime tempfail 450 4.7.0 "You are greylisted for " %gltime " seconds" else tempfail 450 4.7.0 "Still greylisted for " %greylist_seconds_left " seconds" fi fi fi done func log_header_line(string hdr_line) do set idx index(%hdr_line, "\n")-1 if %idx < -1 set idx -1 fi set str1 substring(%hdr_line, 0, %idx) if index(%hdr_line, "\n") = -1 echo "%queue_id: " %str1 else echo "%queue_id: " %str1 set str2 substring(%hdr_line, index(%hdr_line, "\n")+1, -1) log_header_line(%str2) fi done prog header do set msg_header %msg_header $1 ": " $2 "\r\n" set full_header_line $1 ": " $2 log_header_line(%full_header_line) catch failure do echo "%queue_id: error decoding string of header, caught exception: $2" # reject 554 5.7.0 "error decoding string of header; input string is not RFC 2047 encoded" continue done if message_header_decode($2, "KOI-8R") matches "ò A ó ó ù ì ï þ ë é" reject 554 5.7.0 "Spam messages rejected" fi done prog eom do catch failure do echo "%queue_id: ERROR: mailfromd can not access to clamd, caught exception: $2" tempfail 451 4.3.0 "mailfromd can not access to clamd; please try again later" done if clamav(%clamd_port) set message <<- EOT Subject: Virus intercepted To: <%virusmaster> From: MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit X-Agent: %__package__ (%__version__) X-Infected-Received-From: %infected_received_from The message %queue_id that sent from %mail_from to %rcpt_to contained a virus "%clamav_virus_name" and has not been delivered. Message tested by ClamAV: http://www.clamav.net ----- The original (infected) message headers here: %msg_header_last_received %msg_header EOT echo "%queue_id: Virus founded: %clamav_virus_name from ${client_addr}" catch * do echo "%queue_id: WARNING: message from daemon was not been sent to administrator, caught exception: $2" reject 554 5.7.0 "virus %clamav_virus_name detected by ClamAV - http://www.clamav.net" done send_mail(%message, %virusmaster) reject 554 5.7.0 "virus %clamav_virus_name detected by ClamAV - http://www.clamav.net" fi if %spam_verification_needed = 1 and not %spamd_port = "" set prec 3 catch failure do echo "%queue_id: ERROR: mailfromd can not access to Spamassasin, caught exception: $2" tempfail 451 4.3.0 "mailfromd can not access to Spamassasin; please try again later" done if sa(%spamd_port,%prec,1) if %spamreject = 1 set message <<- EOT Subject: spam blocked To: <%spammaster> From: MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit X-Agent: %__package__ (%__version__) X-Infected-Received-From: %infected_received_from The message %queue_id that sent from %mail_from to %rcpt_to detected as spam and blocked. ----- The original (infected) message headers here: %msg_header_last_received %msg_header EOT catch * do echo "%queue_id: WARNING: message from daemon was not been sent to administrator, caught exception: $2" reject 554 5.7.0 "Spam detected by Spamassassin." %explain_msg_spam done send_mail(%message, %spammaster) reject 554 5.7.0 "Spam detected by Spamassassin." %explain_msg_spam else if %mta = "sendmail" header_add("X-Spamd-Flag" %X_Spam_Suffix, "YES") fi fi fi echo "%queue_id: INFO: message processed by Spamassassin, sa_score=" %sa_score " sa_threshold=" %sa_threshold if %mta = "sendmail" header_add("X-Spamd-Score" %X_Spam_Suffix, to_double_str(%sa_score, %prec)) header_add("X-Spamd-Threshold" %X_Spam_Suffix, to_double_str(%sa_threshold, %prec)) header_add("X-Spamd-Keywords" %X_Spam_Suffix, sa_format_report_header(%sa_keywords)) fi fi done