dnl dnl Проверка соответствия адреса хоста отправителя SPF записи домена отправителя dnl dnl NO - отключить поддержку SPF dnl YES - включить поддержку SPF dnl DEFER_OK - включить поддержку SPF и игнорировать невозможность dnl проверки соответствия адреса хоста отправителя SPF записи домена отправителя dnl define(`confSPF2', `NO')dnl dnl dnl варианты действий при несоответствии рилея отправителя SPF записи домена отправителя dnl описаны в файле CONFDIR/access-spf2 в виде: dnl dnl sender.domain.tld|result : действие : сообщение клиенту : сообщение в файл протокола dnl dnl где sender.dom.tld - домен отправителя, допускаются wildcards dnl result - результат проверки соответствия рилея отправителя SPF записи dnl домена отправителя (pass, fail, softfail, none, neutral, dnl err_perm, err_temp; см. exiscan-acl-spec.txt, dnl 8. Sender Policy Framework (SPF) support) dnl http://duncanthrax.net/exiscan-acl/exiscan-acl-spec.txt) dnl dnl в качестве "действия" могут выступать: dnl ok - принимать сообщения dnl warn - выдача предупреждения в лог файл и в заголовки письма dnl и добавление в заголовки сообщения поле X-Warn-Mail-MX dnl текст сообщения об ошибке может быть указан через двоеточие dnl deny или reject - отказ в приеме сообщения dnl drop или discard - отказ в приеме сообщения с обрывом соединения dnl defer - возврат клиенту временной ошибки 4xx dnl greylist:xx или greylisting:xx - добавление xx баллов к счетчику опционального greylisting'а dnl reject:yy или deny:yy - добавление yy баллов к счетчику опционального reject'а dnl delay:zz или pause:zz - задержка на zz секунд перед продолжением обработки сообщения dnl submit_mysql - занесение записи о хосте в базу MySQL dnl submit_sqlite - занесение записи о хосте в базу SQLite dnl submit_rbl - занесение записи о хосте в DNSBL dnl dnl сообщения, возвращаемое клиенту и выводимое в файл протокола, могут отсуствовать dnl dnl пример: dnl *.aol.com|fail : deny : AOL sender, but not from AOL-approved relay dnl *.aol.com|neutral : warn : AOL sender, but not from AOL-approved relay dnl aol.com|fail : deny : AOL sender, but not from AOL-approved relay dnl aol.com|neutral : deny : AOL sender, but not from AOL-approved relay dnl dnl bigmir.net|softfail : defer dnl bigmir.net|err_temp : defer dnl gala.net|softfail : deny : You can not send messages with gala.net as sender domain dnl gala.net|fail : deny : You can not send messages with gala.net as sender domain dnl rambler.ru|softfail : deny : You can not send messages with rambler.ru as sender domain dnl rambler.ru|fail : deny : You can not send messages with rambler.ru as sender domain dnl dnl *|fail : deny dnl *|softfail : defer dnl *|err_temp : defer dnl *|neutral : warn dnl dnl исключения из проверки SPF записей указываются в файле CONFDIR/access-spf2 в виде: dnl адрес_отправителя : skip : список сетей dnl dnl пример: dnl *@hotmail.com : skip : 194.183.162.130 dnl dnl необходимо внести в виде исключений адреса backup MX'ов: dnl * : skip : backup_MX_1 : backup_MX_2 dnl dnl backend для проверки соответствия адреса хоста отправителя SPF записи домена отправителя dnl LIBSPF2 - использовать libspf2 dnl SPFD - использовать spfd из состава Mail::SPF::Query dnl define(`confSPF2_BACKEND', `LIBSPF2')dnl dnl dnl путь к файлу UNIX domain сокета при использовании spfd в качестве backend для dnl проверки соответствия адреса хоста отправителя SPF записи домена отправителя dnl define(`confSPF2_SPFD_SOCKET', `/tmp/spfd')dnl dnl dnl SPF_RESULT_INVALID "(invalid)" "invalid" dnl SPF_RESULT_NEUTRAL "neutral" "neutral" dnl SPF_RESULT_PASS "pass" "pass" dnl SPF_RESULT_FAIL "fail" "fail" dnl SPF_RESULT_SOFTFAIL "softfail" "softfail" dnl SPF_RESULT_NONE "none" "none" dnl SPF_RESULT_TEMPERROR "error (temporary)" "err_temp" dnl SPF_RESULT_PERMERROR "unknown (permanent error)" "err_perm" dnl "(error: unknown result)" dnl ifelse(SECTION, `ACLS_ADDITIONAL', `dnl acl_spf_pass: accept condition = ${if eq{$acl_m_spf_pass}{}{no}{yes}} ifelse_strstr(confSPF2_BACKEND, `LIBSPF2', ` warn condition = ${if eq{$acl_m_spf_result}{}{yes}{no}} spf = pass warn condition = ${if eq{$acl_m_spf_result}{}{yes}{no}} set acl_m_spf_result = $spf_result ')dnl ifelse_strstr(confSPF2_BACKEND, `LIBSPF2', `') ifelse_strstr(confSPF2_BACKEND, `SPFD', ` warn condition = ${if eq{$acl_m_spf_result}{}{yes}{no}} set acl_m_spf = \ ${readsocket{confSPF2_SPFD_SOCKET}{\ ip=$sender_host_address\n\ helo=${if def:sender_helo_name{$sender_helo_name}{NOHELO}}\n\ sender=$sender_address\n\ result\n\ quit\n\ }{20s}{\n}{socket failure}} condition = ${if eq{$acl_m_spf_result}{}{yes}{no}} condition = ${if eq{$acl_m_spf}{socket failure}{no}{yes}} set acl_m_spf_result = ${extract{result}{$acl_m_spf}} ')dnl ifelse_strstr(confSPF2, `DEFER_OK', `dnl', `') # accept condition = ${if eq{$acl_m_spf_result}{}{yes}{no}} accept set acl_m_spf_pass = no condition = ${if eq{$acl_m_spf_result}{pass}{yes}{no}} set acl_m_spf_pass = yes accept ')dnl ifelse(SECTION, `ACL_CHECK_RCPT', `dnl warn set acl_m0 = set acl_m2 = ${lookup{$sender_address}wildlsearch{CONFDIR/access-spf2}} condition = ${if eq{$acl_m2}{}{no}{yes}} set acl_m1 = ${extract{1}{:}{$acl_m2}} acl = normalize_action condition = ${if eq{$acl_m1}{skip}{yes}{no}} set acl_m2 = ${sg{$acl_m2}{\N^\s*\S+\s*(:\s*)?\N}{}} set acl_m2 = ${if eq{$acl_m2}{}{*}{$acl_m2}} hosts = $acl_m2 set acl_m0 = skip ifelse_strstr(confSPF2_BACKEND, `LIBSPF2', ` warn condition = ${if eq{$acl_m0}{skip}{no}{yes}} spf = fail warn condition = ${if eq{$acl_m0}{skip}{no}{yes}} set acl_m_spf_result = $spf_result dnl ifelse_strstr(confSPF2, `DEFER_OK', `', `dnl dnl defer condition = ${if eq{$acl_m0}{skip}{no}{yes}} dnl condition = ${if or{\ dnl {eq{$acl_m_spf_result}{(invalid)}}\ dnl # {eq{$acl_m_spf_result}{error (temporary)}}\ dnl # {eq{$acl_m_spf_result}{unknown (permanent error)}}\ dnl }{yes}{no}} dnl message = Unable to check SPF record of sender address domain $sender_address_domain dnl log_message = Unable to check SPF record of sender address domain $sender_address_domain (SPF check result is $acl_m_spf_result) dnl ')dnl ifelse_strstr(confSPF2, `DEFER_OK', `dnl', `') warn condition = ${if eq{$acl_m0}{skip}{no}{yes}} condition = ${if eq{$spf_result}{}{no}{yes}} logwrite = SPF check result is "$spf_result" for sender host address $sender_host_address and sender address domain $sender_address_domain add_header = $spf_received warn condition = ${if eq{$acl_m0}{skip}{no}{yes}} set acl_m_spf_result = $spf_result warn condition = ${if eq{$acl_m0}{skip}{no}{yes}} condition = ${if eq{$acl_m_spf_result}{(invalid)}{yes}{no}} set acl_m_spf_result = invalid warn condition = ${if eq{$acl_m0}{skip}{no}{yes}} condition = ${if eq{$acl_m_spf_result}{error (temporary)}{yes}{no}} set acl_m_spf_result = err_temp warn condition = ${if eq{$acl_m0}{skip}{no}{yes}} condition = ${if eq{$acl_m_spf_result}{unknown (permanent error)}{yes}{no}} set acl_m_spf_result = err_perm warn set acl_m1 = condition = ${if eq{$acl_m0}{skip}{no}{yes}} condition = ${if eq{$acl_m_spf_result}{}{no}{yes}} set acl_m0 = ${lookup{$sender_address_domain|$acl_m_spf_result}wildlsearch{CONFDIR/access-spf2}} condition = ${if eq{$acl_m0}{}{no}{yes}} set acl_m1 = ${extract{1}{:}{$acl_m0}} acl = normalize_action set acl_m1 = ${sg{$acl_m1 }{\N\b([^=\s\d]+)(\s)\N}{\$1=00\$2}} # message = $acl_m2 # log_message = $acl_m0 set acl_m2 = ${sg{${extract{2}{:}{$acl_m0}}}{\N^\s+\N}{}} set acl_m2 = ${if eq{$acl_m2}{}{$spf_header_comment\n\ ${sg{\ ${sg{$spf_smtp_comment}{Please%see%}{Please see }}\ }{\N\s*: Reason: mechanism$\N}{}}\ }{$acl_m2}} set acl_m0 = ${sg{${extract{3}{:}{$acl_m0}}}{\N^\s+\N}{}} set acl_m0 = ${if eq{$acl_m0}{}{$spf_header_comment (SPF check result is $spf_result)}{$acl_m0}} ')dnl ifelse_strstr(confSPF2_BACKEND, `LIBSPF2', `') ifelse_strstr(confSPF2_BACKEND, `SPFD', ` warn set acl_m_spf = \ ${readsocket{confSPF2_SPFD_SOCKET}{\ ip=$sender_host_address\n\ helo=${if def:sender_helo_name{$sender_helo_name}{NOHELO}}\n\ sender=$sender_address\n\ result\n\ quit\n\ }{20s}{\n}{socket failure}} warn condition = ${if eq{$acl_m0}{skip}{no}{yes}} set acl_m_spf_result = condition = ${if eq{$acl_m_spf}{socket failure}{no}{yes}} set acl_m_spf_result = ${extract{result}{$acl_m_spf}} warn condition = ${if eq{$acl_m0}{skip}{no}{yes}} condition = ${if eq{$acl_m_spf_result}{}{no}{yes}} logwrite = SPF check result is "$acl_m_spf_result" for sender host address $sender_host_address and sender address domain $sender_address_domain dnl ifelse_strstr(confSPF2, `DEFER_OK', `', `dnl dnl defer condition = ${if eq{$acl_m0}{skip}{no}{yes}} dnl condition = ${if or{\ dnl {eq{$acl_m_spf_result}{(invalid)}}\ dnl # {eq{$acl_m_spf_result}{error (temporary)}}\ dnl # {eq{$acl_m_spf_result}{unknown (permanent error)}}\ dnl }{yes}{no}} dnl message = Unable to check SPF record of sender address domain $sender_address_domain dnl log_message = Unable to check SPF record of sender address domain $sender_address_domain (SPF check result is "$acl_m_spf_result") dnl defer condition = ${if eq{$acl_m0}{skip}{no}{yes}} dnl condition = ${if eq{$acl_m_spf}{socket failure}{yes}{no}} dnl message = Temporary local problem - please try later dnl log_message = Could not connect to SPFD socket confSPF2_SPFD_SOCKET dnl ', ` dnl warn condition = ${if eq{$acl_m0}{skip}{no}{yes}} dnl condition = ${if eq{$acl_m_spf}{socket failure}{yes}{no}} dnl set acl_m_spf = dnl ')dnl ifelse_strstr(confSPF2, `DEFER_OK', `dnl', `') # warn condition = ${if eq{$acl_m0}{skip}{no}{yes}} # condition = ${if eq{$acl_m_spf_result}{fail}{yes}{no}} # add_header = Received-SPF: $acl_m_spf_result ($domain: domain of $sender_address_domain does not designate $sender_address as permitted sender) client-ip=$sender_address; envelope-from=$sender_address; helo=$sender_helo_name; # warn condition = ${if eq{$acl_m0}{skip}{no}{yes}} # condition = ${if eq{$acl_m_spf_result}{pass}{yes}{no}} # add_header = Received-SPF: $acl_m_spf_result ($domain: domain of $sender_address_domain designates $sender_address as permitted sender) client-ip=$sender_address; envelope-from=$sender_address; helo=$sender_helo_name; # warn condition = ${if eq{$acl_m0}{skip}{no}{yes}} # condition = ${if eq{$acl_m_spf_result}{error (temporary)}{yes}{no}} # add_header = Received-SPF: $acl_m_spf_result ($domain: encountered temporary error during SPF processing of domain of $sender_address_domain) client-ip=$sender_address; envelope-from=$sender_address; helo=$sender_helo_name; # warn condition = ${if eq{$acl_m0}{skip}{no}{yes}} # condition = ${if eq{$acl_m_spf_result}{neutral}{yes}{no}} # add_header = Received-SPF: $acl_m_spf_result ($domain: $sender_address is neither permitted nor denied by domain of $sender_address_domain) client-ip=$sender_address; envelope-from=$sender_address; helo=$sender_helo_name; # warn condition = ${if eq{$acl_m0}{skip}{no}{yes}} # condition = ${if eq{$acl_m_spf_result}{softfail}{yes}{no}} # add_header = Received-SPF: $acl_m_spf_result ($domain: transitioning domain of $sender_address_domain does not designate $sender_address as permitted sender) client-ip=$sender_address; envelope-from=$sender_address; helo=$sender_helo_name; warn condition = ${if eq{$acl_m0}{skip}{no}{yes}} # set acl_m_spf_header_comment = ${extract{header_comment}{$acl_m_spf}} set acl_m_spf_header_comment = ${if match{$acl_m_spf}{\N(?m)^header_comment=(.+)\n\N}{$1}{}} condition = ${if eq{$acl_m_spf_header_comment}{}{no}{yes}} condition = ${if eq{$acl_m_spf_result}{none}{no}{yes}} add_header = Received-SPF: $acl_m_spf_result ($acl_m_spf_header_comment) client-ip=$sender_address; envelope-from=$sender_address; helo=$sender_helo_name; warn condition = ${if eq{$acl_m0}{skip}{no}{yes}} # set acl_m_spf_smtp_comment = ${extract{smtp_comment}{$acl_m_spf}} set acl_m_spf_smtp_comment = ${if match{$acl_m_spf}{\N(?m)^smtp_comment=(.+)\n\N}{$1}{}} warn condition = ${if eq{$acl_m0}{skip}{no}{yes}} condition = ${if eq{$acl_m_spf_result}{(invalid)}{yes}{no}} set acl_m_spf_result = invalid warn condition = ${if eq{$acl_m0}{skip}{no}{yes}} condition = ${if eq{$acl_m_spf_result}{error (temporary)}{yes}{no}} set acl_m_spf_result = err_temp warn condition = ${if eq{$acl_m0}{skip}{no}{yes}} condition = ${if eq{$acl_m_spf_result}{unknown (permanent error)}{yes}{no}} set acl_m_spf_result = err_perm warn set acl_m1 = condition = ${if eq{$acl_m0}{skip}{no}{yes}} condition = ${if eq{$acl_m_spf_result}{}{no}{yes}} set acl_m0 = ${lookup{$sender_address_domain|$acl_m_spf_result}wildlsearch{CONFDIR/access-spf2}} condition = ${if eq{$acl_m0}{}{no}{yes}} set acl_m1 = ${extract{1}{:}{$acl_m0}} acl = normalize_action set acl_m1 = ${sg{$acl_m1 }{\N\b([^=\s\d]+)(\s)\N}{\$1=00\$2}} # message = $acl_m2 # log_message = $acl_m0 set acl_m2 = ${sg{${extract{2}{:}{$acl_m0}}}{\N^\s+\N}{}} set acl_m2 = ${if eq{$acl_m2}{}{$acl_m_spf_header_comment\n\ ${sg{\ ${sg{$acl_m_spf_smtp_comment}{Please%see%}{Please see }}\ }{\N\s*: Reason: mechanism$\N}{}}\ }{$acl_m2}} set acl_m0 = ${sg{${extract{3}{:}{$acl_m0}}}{\N^\s+\N}{}} set acl_m0 = ${if eq{$acl_m0}{}{$acl_m_spf_header_comment (SPF check result is ${extract{result}{$acl_m_spf}})}{$acl_m0}} ')dnl ifelse_strstr(confSPF2_BACKEND, `SPFD', `')dnl ifdef(`confENTERPRISE_USER', `dnl warn condition = ${if match{$acl_m1}{submit_mysql}{yes}{no}} ENTERPRISE(`mysql', `submit', `spf', `blacklisted', `$sender_address', `0') warn condition = ${if match{$acl_m1}{submit_sqlite}{yes}{no}} ENTERPRISE(`sqlite', `submit', `spf', `blacklisted', `$sender_address', `0') warn condition = ${if match{$acl_m1}{submit_rbl}{yes}{no}} dnl ENTERPRISE(`rbl', `update', `mx.org.ua', `spf.rbl.mx.org.ua', `blacklisted', `$sender_address') ENTERPRISE(`rbl', `submit', `spf', `blacklisted', `$sender_address') ')dnl warn condition = ${if match{$acl_m1}{warn}{yes}{no}} log_message = $acl_m0${if eq{${extract{pause}{$acl_m1}}}{}{}{: message delayed for ${extract{pause}{$acl_m1}}s}} add_header = X-Warn-SPF: $acl_m0 warn condition = ${if eq{${extract{pause}{$acl_m1}}}{}{no}{yes}} delay = ${extract{pause}{$acl_m1}}s set acl_m15 = ${acl_m15}\t\ delay=${extract{pause}{$acl_m1}}s\t\t\ $acl_m0\n deny condition = ${if eq{${extract{reject}{$acl_m1}}}{00}{yes}{no}} log_message = $acl_m0 message = $acl_m2 defer condition = ${if match{$acl_m1}{defer}{yes}{no}} log_message = $acl_m0 message = $acl_m2 drop condition = ${if match{$acl_m1}{drop}{yes}{no}} log_message = $acl_m0 message = $acl_m2 ifelse(confGREYLIST_DBM, `OPTIONAL', `dnl warn condition = ${if eq{${extract{greylist}{$acl_m1}}}{}{no}{yes}} set acl_c8 = \ scores=${eval:${extract{scores}{$acl_c8}}+${extract{greylist}{$acl_m1}}} \ log_message="${extract{log_message}{$acl_c8}} $acl_m0;" set acl_m15 = ${acl_m15}\t\ greylist scores=${extract{greylist}{$acl_m1}}\t\ $acl_m0\n ') dnl ifelse(confGREYLIST_DBM, `OPTIONAL', `') ifdef(`confOPTIONAL_REJECT', `ifelse(confOPTIONAL_REJECT, `NO', `dnl', `dnl warn condition = ${if eq{${extract{reject}{$acl_m1}}}{}{no}{yes}} condition = ${if eq{${extract{reject}{$acl_m1}}}{00}{no}{yes}} set acl_c6 = \ scores=${eval:${extract{scores}{$acl_c6}}+${extract{reject}{$acl_m1}}} \ log_message="${extract{log_message}{$acl_c6}} $acl_m0;" set acl_m15 = ${acl_m15}\t\ reject scores=${extract{reject}{$acl_m1}}\t\t\ $acl_m0\n ')') dnl ifdef(`confOPTIONAL_REJECT', `ifelse(confOPTIONAL_REJECT, `NO', `', `')') ')dnl ifelse(SECTION, `ACL_CHECK_RCPT', `')