dnl dnl Пассивная проверка операционной системы хоста отправителя dnl dnl ВНИМАНИЕ! для использования пассивной проверки операционной системы dnl хоста отправителя нужен работающий в режиме демона p0f и собранная dnl динамически загружаемая библиотека exim-p0f3-dlfunc.so (см. ниже) dnl dnl NO - не использовать пассивную проверку ОС хоста отправителя dnl YES - использовать пассивную проверку ОС хоста отправителя dnl DEBUG - выводить в файл протокола название ОС хоста отправителя dnl вне зависимости от действий в фильтре dnl define(`confCHECK_ACCESS_RELAY_OS', `NO')dnl dnl dnl в файле access-os можно указать необходимое действие для данной ОС в виде: dnl ОС : действие : сообщение : сообщение 2 dnl dnl где dnl ОС - операционная система в виде полного названия, маски или dnl регулярного выражения dnl действие - действие, применяемое к письму dnl сообщение - ответ SMTP клиенту (может отсутствовать) dnl сообщение 2 - сообщение в файл протокола или в добавляемое поле dnl заголовка (может отсутствовать) dnl dnl возможные названия ОС можно почерпнуть из файла p0f.fp из состава p0f dnl dnl возможные действия: dnl ok - исключение из проверки для указанной ОС dnl warn - вывод предупреждения в файл протокола и добавление в письмо поля заголовка dnl greylist=X - добавление X баллов к счетчику опционального грейлистинга dnl reject=X - добавление X баллов к счетчику опционального reject'а dnl delay=zz - задержка на zz секунд перед продолжением обработки сообщения dnl pause=zz - синоним delay=zz dnl dnl в полях действия и сообщений можно использовать вычисляемые выражения dnl т. о. можно возвращать разные действия для одной и той же ОС например dnl в зависимости от принадлежности хоста отправителя списку доверенных dnl сетей или от использования шифрования SMTP клиентом dnl dnl пример: dnl \N^Windows.*$\N : warn greylist=10 ${if eq{$tls_cipher}{}{pause=15 reject=1}{pause=5}} : $acl_c_sender_host_os OS detected on ${if eq{$sender_host_name}{}{}{$sender_host_name }}[$sender_host_address] dnl dnl для использования данного механизма нужно установить p0f и настроить работу в режиме демона: dnl dnl http://lcamtuf.coredump.cx/p0f.shtml dnl http://lcamtuf.coredump.cx/p0f3/releases/ dnl http://lcamtuf.coredump.cx/p0f3/releases/p0f-3.08b.tgz dnl http://lcamtuf.coredump.cx/p0f3/releases/p0f-latest.tgz dnl dnl для FreeBSD: dnl cd /usr/ports/net-mgmt/p0f && make install clean dnl dnl для RedHat/CentOS/Fedora: dnl https://dl.fedoraproject.org/pub/fedora/linux/development/rawhide/source/SRPMS/p/p0f-3.08b-2.fc23.src.rpm dnl ftp://ftp.funet.fi/pub/Linux/mirrors/fedora/linux/development/rawhide/source/SRPMS/p/p0f-3.08b-2.fc22.src.rpm dnl dnl отдельно нужно собрать exim-p0f3-dlfunc и скопировать файл exim-p0f3-dlfunc.so в каталог /usr/local/libexec/exim dnl dnl http://dist.epipe.com/exim/ dnl http://dist.epipe.com/exim/exim-p0f3-dlfunc-0.1.README.txt dnl http://dist.epipe.com/exim/exim-p0f3-dlfunc-0.1.tar.gz dnl dnl для сборки динамически загружаемой библиотеки потребутся исходные тексты exim dnl dnl пример для сборки exim из порта FreeBSD: dnl dnl fetch http://dist.epipe.com/exim/exim-p0f3-dlfunc-0.1.tar.gz dnl tar -xzf exim-p0f3-dlfunc-0.1.tar.gz dnl cd exim-p0f3-dlfunc-0.1 dnl CPPFLAGS="-I`ls -1d /usr/ports/mail/exim/work/exim-4.*/build-FreeBSD-*`" ./configure --libdir=`ls -1d /usr/ports/mail/exim/work/exim-4.*/build-FreeBSD-*`/ dnl make dnl cp -p .libs/exim-p0f3-dlfunc.so /usr/local/libexec/exim/ dnl cd .. dnl dnl пример для сборки exim из src rpm под CentOS: dnl dnl wget http://dist.epipe.com/exim/exim-p0f3-dlfunc-0.1.tar.gz dnl tar -xzf exim-p0f3-dlfunc-0.1.tar.gz dnl cd exim-p0f3-dlfunc-0.1 dnl CPPFLAGS="-I`ls -1d /usr/src/redhat/BUILD/exim-4.*/build-Linux-* | tail -n 1`" ./configure --libdir=`ls -1d /usr/src/redhat/BUILD/exim-4.*/build-Linux-* | tail -n 1`/ dnl make dnl cp -p .libs/exim-p0f3-dlfunc.so /usr/local/libexec/exim/ dnl cd .. dnl dnl путь по умолчанию к файлу сокета p0f: dnl define(`confCHECK_ACCESS_OS_P0F_SOCKET', `/var/run/p0f.sock')dnl dnl dnl т. к. p0f может слушать лишь на одном интерфейсе, то может понадобиться dnl запуск нескольких экземпляров p0f с настройкой работы на разных dnl интерфейсах и использованием разных файлов сокетов. dnl в этом случае нужно указать зависимость файла сокета от IP адреса интерфейса, на который пришло соединение от SMTP клиента: dnl define(`confCHECK_ACCESS_OS_P0F_SOCKET', `${if eq{$received_ip_address}{1.2.3.4}{/var/run/p0f_em1.sock}{/var/run/p0f.sock}}')dnl dnl dnl исключения из пассивной проверки операционной системы хоста отправителя: dnl AUTH - исключения для аутентифицированных отправителей dnl HOSTS - исключения для хостов из confCHECK_ACCESS_RELAY_OS_SKIP_HOSTS dnl define(`confCHECK_ACCESS_RELAY_OS_SKIP', `AUTH HOSTS')dnl dnl dnl исключения из пассивной проверки операционной системы хоста отправителя для хостов из списка: dnl define(`confCHECK_ACCESS_RELAY_OS_SKIP_HOSTS', `+relay_from_hosts')dnl dnl ifelse(SECTION, `ACL_CHECK_CONNECT', `dnl warn set acl_c_sender_host_os = ${dlfunc{/usr/local/libexec/exim/exim-p0f3-dlfunc.so}{p0f3_os}{confCHECK_ACCESS_OS_P0F_SOCKET}{$sender_host_address}} ifelse_strstr(confCHECK_ACCESS_RELAY_OS, `DEBUG', `dnl log_message = sender host OS $acl_c_sender_host_os detected on ${if eq{$acl_c_sender_host_name}{}{}{$sender_host_name }}[$sender_host_address] ') dnl ifelse_strstr(confCHECK_ACCESS_RELAY_OS, `DEBUG', `') ') dnl ifelse(SECTION, `ACL_CHECK_CONNECT', `') ifelse(SECTION, `ACL_CHECK_RCPT', `dnl # Пассивная проверка операционной системы хоста отправителя warn set acl_m1 = condition = ${if eq{$acl_c_sender_host_os}{}{no}{yes}} ifelse_strstr(confCHECK_ACCESS_RELAY_OS_SKIP, `AUTH', `dnl ! authenticated = * ') dnl ifelse_strstr(confCHECK_ACCESS_RELAY_OS_SKIP, `AUTH', `') ifelse_strstr(confCHECK_ACCESS_RELAY_OS_SKIP, `HOSTS', `dnl ! hosts = confCHECK_ACCESS_RELAY_OS_SKIP_HOSTS ') dnl ifelse_strstr(confCHECK_ACCESS_RELAY_OS_SKIP, `HOSTS', `') set acl_m0 = ${lookup{$acl_c_sender_host_os}wildlsearch{CONFDIR/access-relay-os}\ {${if match{$value}{\N.+(lookup |[\{\}])\N}{${expand:$value}}{$value}}}{}\ } condition = ${if eq{$acl_m0}{}{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 = ${expand:${sg{${extract{2}{:}{$acl_m0}}}{\N^\s+\N}{}}} set acl_m0 = ${expand:${sg{${extract{3}{:}{$acl_m0}}}{\N^\s+\N}{}}} set acl_m_check_relay_os_msg = ${if eq{$acl_m0}{}{}{: $acl_m0}} set acl_m0 = ${if eq{$acl_m0}{}{sender host OS $acl_c_sender_host_os detected on ${if eq{$acl_c_sender_host_name}{}{}{$sender_host_name }}[$sender_host_address]${if eq{$acl_m2}{}{}{: $acl_m2}}}{$acl_m0}} set acl_m2 = ${if eq{$acl_m2}{}{Access denied}{$acl_m2}} # warn warn condition = ${if match{$acl_m1}{warn}{yes}{no}} add_header = X-Warn-Relay-OS: sender host OS $acl_c_sender_host_os detected on ${if eq{$acl_c_sender_host_name}{}{}{$sender_host_name }}[$sender_host_address]${acl_m_check_relay_os_msg} log_message = $acl_m0${if eq{${extract{pause}{$acl_m1}}}{}{}{: message delayed for ${extract{pause}{$acl_m1}}s}} # 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 # quarantine and !reject warn \ condition = ${if eq{${extract{quarantine}{$acl_m1}}}{00}{yes}{no}} condition = ${if or{\ {eq{${extract{reject}{$acl_m1}}}{00}}\ {eq{${extract{reject_all}{$acl_m1}}}{00}}\ }{no}{yes}} add_header = X-Quarantine-Relay-OS: sender host OS $acl_c_sender_host_os detected on ${if eq{$acl_c_sender_host_name}{}{}{$sender_host_name }}[$sender_host_address]${acl_m_check_relay_os_msg} log_message = message will be quarantined: sender host OS $acl_c_sender_host_os detected on ${if eq{$acl_c_sender_host_name}{}{}{$sender_host_name }}[$sender_host_address]${acl_m_check_relay_os_msg} set acl_m_add_x_orig_rcpt = yes set acl_m_quarantined = $acl_m_quarantined envelope set acl_m_check_rcpt_and_accept = yes # quarantine and reject warn \ condition = ${if eq{${extract{quarantine}{$acl_m1}}}{00}{yes}{no}} condition = ${if or{\ {eq{${extract{reject}{$acl_m1}}}{00}}\ {eq{${extract{reject_all}{$acl_m1}}}{00}}\ }{yes}{no}} log_message = message will be quarantined and rejected: sender host OS $acl_c_sender_host_os detected on ${if eq{$acl_c_sender_host_name}{}{}{$sender_host_name }}[$sender_host_address]${acl_m_check_relay_os_msg} set acl_m_fakereject = \ message will be quarantined and rejected: sender host OS $acl_c_sender_host_os detected on ${if eq{$acl_c_sender_host_name}{}{}{$sender_host_name }}[$sender_host_address]${acl_m_check_relay_os_msg}\ |X-Quarantine-Relay-OS: sender host OS $acl_c_sender_host_os detected on ${if eq{$acl_c_sender_host_name}{}{}{$sender_host_name }}[$sender_host_address]${acl_m_check_relay_os_msg}\ |$acl_m2 set acl_m_add_x_orig_rcpt = yes set acl_m_quarantined = $acl_m_quarantined envelope set acl_m_check_rcpt_and_accept = yes # !quarantine and reject deny condition = ${if eq{$acl_m_check_rcpt_and_accept}{yes}{no}{yes}} condition = ${if or{\ {eq{${extract{reject}{$acl_m1}}}{00}}\ {eq{${extract{reject_all}{$acl_m1}}}{00}}\ }{yes}{no}} condition = ${if eq{${extract{quarantine}{$acl_m1}}}{00}{no}{yes}} log_message = $acl_m0 message = $acl_m2 defer condition = ${if eq{$acl_m_check_rcpt_and_accept}{yes}{no}{yes}} condition = ${if match{$acl_m1}{defer}{yes}{no}} log_message = $acl_m0 message = $acl_m2 drop condition = ${if eq{$acl_m_check_rcpt_and_accept}{yes}{no}{yes}} condition = ${if match{$acl_m1}{drop}{yes}{no}} log_message = $acl_m0 message = $acl_m2 discard condition = ${if eq{$acl_m_check_rcpt_and_accept}{yes}{no}{yes}} condition = ${if match{$acl_m1}{discard}{yes}{no}} log_message = $acl_m0 warn condition = ${if eq{$acl_m_check_rcpt_and_accept}{yes}{no}{yes}} condition = ${if eq{${extract{fakereject}{$acl_m1}}}{00}{yes}{no}} log_message = message will be fakerejected: sender host OS $acl_c_sender_host_os detected on ${if eq{$acl_c_sender_host_name}{}{}{$sender_host_name }}[$sender_host_address] set acl_m_fakereject = \ message will be fakerejected: $acl_m0\ |X-Fakerejected: $acl_m0\ |$acl_m2 set acl_m_add_x_orig_rcpt = yes ifelse(confGREYLIST, `OPTIONAL', `dnl warn condition = ${if eq{$acl_m_check_rcpt_and_accept}{yes}{no}{yes}} 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{$acl_m_check_rcpt_and_accept}{yes}{no}{yes}} 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', `', `')') ') dnl ifelse(SECTION, `ACL_CHECK_RCPT', `')