dnl dnl Проверка соответствия HELO и PTR записи хоста отправителя dnl dnl данная проверка может использовать в случаях, когда в HELO указано dnl имя хоста из определенного домена, и при этом PTR запись хоста dnl отправителя должна быть из этого же (или другого, но определенного) dnl домена dnl NO - не проводить проверку dnl YES - проводить проверку dnl define(`confCHECK_HELO_FORGED', `NO')dnl dnl dnl соответствие HELO и PTR записей хостов отправителей описано в файле confCONFDIR/access-helo-forged в виде: dnl : : : dnl dnl helo_regexp - регулярное выражение, применяемое к HELO dnl hostname_regexp - регулярное выражение, которому должна dnl соответствовать PTR запись хоста отправителя при dnl соответствии HELO первому регуляному выражению dnl action - действие в случае несоответствия PTR записи dnl хоста отправителя второму регуляному выражению dnl возможные значения: dnl drop - отвергнуть сообщение и оборвать соединение dnl deny - отвергнуть сообщение dnl reject - синоним deny dnl defer - вернуть временную ошибку dnl warn - выдать предупреждение в лог файл dnl quarantine- принять письмо с сохранением в карантин dnl без доставки получателям dnl pause=XX- пауза XX секунд dnl greylistXX- добавить XX баллов к счетчику dnl опционального greylisting'а dnl reject=XX- добавить XX баллов к счетчику dnl опционального reject'а dnl action_tempfail - действие в случае проблем с резолвингом PTR dnl записи хоста отправителя, возможные значения: dnl defer - вернуть клиенту 4xx dnl pass - принять сообщение dnl warn - выдать предупреждение в лог файл dnl hostname_regexp - регулярное выражение, которому должна dnl соответстовать PTR запись хоста отправителя, dnl если HELO соответствует первому регулярному dnl выражению dnl регулярное выражение обязательно указыватеся без dnl в начале и в конце dnl dnl пример: dnl \N^(.+\.)*compuserve\.com$\N : deny : defer : ^(.+\.)*compuserve\.com$ dnl \N^(.+\.)*hotmail\.com$\N : deny : defer : ^(.+\.)*hotmail\.com$ dnl \N^(.+\.)*galaradio\.com$\N : warn : pass : ^(.+\.)*galaradio\.com$ dnl dnl исключения указываются в файле confCONFDIR/skip_helo_forged_check в виде: dnl <адрес_хоста_отправителя> : dnl dnl в качестве адреса хоста отправителя может быть указана A запись хоста dnl или сеть в виде CIDR dnl в качестве regexp указывается регулярное выражение, которому должно dnl соответствовать HELO сообщений данного хоста отправителя dnl регулярное выражение обязательно указыватеся без \N в начале и в конце dnl dnl пример: dnl 66.218.64.0/19 : ^(.+\.)*yahoo\.com$ dnl 213.180.200.0/24 : ^(.+\.)*yandex\.ru$ dnl 66.220.144.0/20 : ^mx-out\.facebook\.com$ dnl # Проверка соответствия HELO и PTR записи хоста отправителя warn set acl_m0 = ${lookup{$sender_host_address}iplsearch{CONFDIR/access-helo-forged}{${lc:$value}}{}} # Проверка соответствия HELO и PTR записи рилея # ищем HELO в списке проверяемых warn condition = ${if eq{$acl_m0}{}{yes}{no}} set acl_m0 = ${lookup{$sender_helo_name}wildlsearch{CONFDIR/access-helo-forged}\ {${if eq{$value}{}{$sender_helo_name}{$value}}}{skip}} # если HELO найден в списке проверяемых, # при этом PTR запись рилея не отрезолвлена # именно из-за проблем резолвинга (не обязательно из-за отсутствия записи в реверсной зоне) # и в настройках указан pass для данного HELO warn condition = ${if eq{$acl_m0}{skip}{no}{yes}} condition = ${if eq{$sender_host_name}{}{yes}{no}} condition = ${if eq{$host_lookup_failed}{1}{no}{yes}} condition = ${if match{${extract{2}{:}{$acl_m0}}}\ {\N^(?i)\s*pass\s*$\N}{yes}{no}} set acl_m0 = skip # если HELO найден в списке проверяемых, # при этом PTR запись рилея не отрезолвлена # именно из-за проблем резолвинга (не обязательно из-за отсутствия записи в реверсной зоне) # проверяем, не указан ли IP адрес рилея или сеть в файле исключений; # пропускаем дальнейшие проверки, если HELO соответствует регулярному выражению, # указанному в файле исключений warn condition = ${if eq{$acl_m0}{skip}{no}{yes}} condition = ${if eq{$sender_host_name}{}{yes}{no}} condition = ${if eq{$host_lookup_failed}{1}{no}{yes}} set acl_m1 = ${lookup{$sender_host_address}\ iplsearch{CONFDIR/skip_helo_forged_check}} set acl_m0 = ${if and{\ {!eq{$acl_m1}{}}\ {match{$sender_helo_name}{$acl_m1}}\ }{skip}{$acl_m0}} # если HELO найден в списке проверяемых, # при этом PTR запись рилея не отрезолвлена # именно из-за проблем резолвинга (не обязательно из-за отсутствия записи в реверсной зоне) # и IP адрес рилея не находится в списке исключений для данного HELO # и в настройках указан defer для данного HELO defer condition = ${if eq{$acl_m0}{skip}{no}{yes}} condition = ${if eq{$sender_host_name}{}{yes}{no}} condition = ${if eq{$host_lookup_failed}{1}{no}{yes}} condition = ${if match{${extract{2}{:}{$acl_m0}}}\ {\N^(?i)\s*defer\s*$\N}{yes}{no}} log_message = Could not resolve ${if eq{$acl_c_RR}{}{PTR record for $sender_host_address}{A record for $acl_c_RR}} for helo forged check message = Access temporarily denied. Could not resolve ${if eq{$acl_c_RR}{}{PTR record for $sender_host_address}{A record for $acl_c_RR}} # если HELO найден в списке проверяемых # при этом PTR запись рилея не отрезолвлена # именно из-за проблем резолвинга (не обязательно из-за отсутствия записи в реверсной зоне) # и IP адрес рилея не находится в списке исключений для данного HELO # и в настройках указан warn для данного HELO warn condition = ${if eq{$acl_m0}{skip}{no}{yes}} condition = ${if eq{$sender_host_name}{}{yes}{no}} condition = ${if eq{$host_lookup_failed}{1}{no}{yes}} condition = ${if match{${extract{2}{:}{$acl_m0}}}\ {\N^(?i)\s*warn\s*$\N}{yes}{no}} log_message = Could not resolve ${if eq{$acl_c_RR}{}{PTR record for $sender_host_address}{A record for $acl_c_RR}} for helo forged check add_header = X-Warn-HELO-Forged: Could not resolve ${if eq{$acl_c_RR}{}{PTR record for $sender_host_address}{A record for $acl_c_RR}} set acl_m0 = skip # если HELO находится в списке проверяемых # выделяем регулярное выражение для PTR записи рилея в отдельную переменную # проверяем PTR запись хоста отправителя на соответствие регулярному выржению # если PTR запись не прошла проверку, нормализуем действие warn set acl_m_regex = set acl_m1 = condition = ${if eq{$acl_m0}{skip}{no}{yes}} set acl_m_regex = ${if match{$acl_m0}{\N^[^:]+:[^:]+:\s*(.+$)\N}{$1}{}} condition = ${if eq{$acl_m_regex}{}{no}{yes}} condition = ${if match{$sender_host_name}{$acl_m_regex}{no}{yes}} acl = acl_normalize_action "${extract{1}{:}{$acl_m0}}" set acl_m1 = ${sg{$acl_m_normalize_action_result }{\N\b([^=\s\d]+)(\s)\N}{\$1=00\$2}} # message = $acl_m2 # log_message = $acl_m0 set acl_m2 = Invalid greeting used set acl_m0 = Forged HELO used (HELO $sender_helo_name does not correspond to relay${if eq{$sender_host_name}{}{}{ hostname $sender_host_name}} [$sender_host_address]) # pause warn condition = ${if eq{${extract{pause}{$acl_m1}}}{}{no}{yes}} delay = ${extract{pause}{$acl_m1}}s set acl_m_spam_action = ${acl_m_spam_action}\t\ delay=${extract{pause}{$acl_m1}}s\t\t\ $acl_m0\n log_message = $acl_m0; message delayed for ${extract{pause}{$acl_m1}}s # warning warn condition = ${if match{$acl_m1}{warn}{yes}{no}} add_header = X-Warn-HELO-Forged: $acl_m0 # quarantine and reject accept condition = ${if eq{${extract{quarantine}{$acl_m1}}}{00}{yes}{no}} condition = ${if eq{${extract{reject}{$acl_m1}}}{00}{yes}{no}} log_message = $acl_m0 set acl_m_fakereject = \ message will be quarantined and rejected: $acl_m0\ |X-Quarantine-HELO-Forged: $acl_m0\ |$acl_m2 set acl_m_add_x_orig_rcpt = yes set acl_m_quarantined = $acl_m_quarantined envelope # quarantine and !reject warn condition = ${if eq{${extract{quarantine}{$acl_m1}}}{00}{yes}{no}} condition = ${if eq{${extract{reject}{$acl_m1}}}{00}{no}{yes}} log_message = message will be quarantined: $acl_m0 add_header = X-Quarantine-HELO-Forged: $acl_m0 set acl_m_add_x_orig_rcpt = yes set acl_m_quarantined = $acl_m_quarantined envelope accept condition = ${if eq{${extract{quarantine}{$acl_m1}}}{00}{yes}{no}} condition = ${if eq{${extract{reject}{$acl_m1}}}{00}{no}{yes}} # !quarantine and reject deny condition = ${if eq{${extract{reject}{$acl_m1}}}{00}{yes}{no}} message = ${if eq{$acl_m2}{}{Invalid greeting used}{$acl_m2}} log_message = $acl_m0 # defer defer condition = ${if match{$acl_m1}{defer}{yes}{no}} message = ${if eq{$acl_m2}{}{Invalid greeting used}{$acl_m2}} log_message = $acl_m0 # drop drop condition = ${if match{$acl_m1}{drop}{yes}{no}} message = ${if eq{$acl_m2}{}{Invalid greeting used}{$acl_m2}} log_message = $acl_m0 # warning warn condition = ${if match{$acl_m1}{warn}{yes}{no}} condition = ${if eq{${extract{pause}{$acl_m1}}}{}{yes}{no}} log_message = $acl_m0 ifelse(confGREYLIST, `OPTIONAL', `dnl warn condition = ${if eq{${extract{greylist}{$acl_m1}}}{}{no}{yes}} set acl_m_optional_greylist = \ scores=${eval:${extract{scores}{$acl_m_optional_greylist}}+${extract{greylist}{$acl_m1}}} \ log_message="${extract{log_message}{$acl_m_optional_greylist}} $acl_m0;" set acl_m_spam_action = ${acl_m_spam_action}\t\ greylist scores=${extract{greylist}{$acl_m1}}\t\ $acl_m0\n ') dnl ifelse(confGREYLIST, `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_m_optional_reject = \ scores=${eval:${extract{scores}{$acl_m_optional_reject}}+${extract{reject}{$acl_m1}}} \ log_message="${extract{log_message}{$acl_m_optional_reject}} $acl_m0;" set acl_m_spam_action = ${acl_m_spam_action}\t\ reject scores=${extract{reject}{$acl_m1}}\t\t\ $acl_m0\n ')') dnl ifdef(`confOPTIONAL_REJECT', `ifelse(confOPTIONAL_REJECT, `NO', `', `')')