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=XX - добавление XX баллов к счетчику опционального грейлистинга dnl reject=XX - добавление XX баллов к счетчику опционального reject'а dnl delay=XX - задержка на XX секунд перед продолжением обработки сообщения dnl pause=XX - синоним delay=XX 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 https://github.com/p0f/p0f dnl https://github.com/p0f/p0f.git dnl dnl для FreeBSD: dnl cd /usr/ports/net-mgmt/p0f && make install clean dnl wget http://mta.org.ua/p0f/freebsd/usr/local/etc/rc.d/p0f -O/usr/local/etc/rc.d/p0f dnl chmod +x /usr/local/etc/rc.d/p0f dnl echo 'p0f_enable="YES"' >> /etc/rc.conf dnl echo 'p0f_iface="em0"' >> /etc/rc.conf dnl service p0f start dnl dnl для RHEL/Oracle Linux/CentOS/Fedora: dnl # https://dl.fedoraproject.org/pub/fedora/linux/development/rawhide/Everything/source/tree/Packages/p/p0f-3.09b-1.fc25.src.rpm dnl # ftp://ftp.funet.fi/pub/Linux/mirrors/fedora/linux/development/rawhide/Everything/source/tree/Packages/p/p0f-3.09b-1.fc25.src.rpm dnl wget ftp://ftp.funet.fi/pub/Linux/mirrors/fedora/linux/development/rawhide/source/SRPMS/p/p0f-3.08b-1.fc22.src.rpm dnl rpmbuild --rebuild p0f-3.08b-1.fc22.src.rpm dnl rpm -Uvh /usr/src/redhat/RPMS/i386/p0f-3.08b-1.el6.i386.rpm dnl wget http://mta.org.ua/p0f/redhat/etc/rc.d/init.d/p0f -O/etc/rc.d/init.d/p0f dnl chmod +x /etc/rc.d/init.d/p0f dnl chkconfig --level 35 p0f on dnl echo 'P0F_IFACE="eth0"' > /etc/sysconfig/p0f dnl #echo 'P0F_SOCKET="/var/run/p0f.sock"' >> /etc/sysconfig/p0f dnl #echo 'P0F_FINGERPRINT_DATABASE="/etc/p0f/p0f.fp"' >> /etc/sysconfig/p0f dnl service p0f start dnl dnl для Ubuntu 16: dnl apt-get update dnl apt-get install -y libpcap-dev dnl wget http://lcamtuf.coredump.cx/p0f3/releases/p0f-latest.tgz dnl tar -xvzf p0f-latest.tgz dnl cd p0f* dnl make all && ( cp -p p0f /usr/local/sbin/ ; cp -p p0f.fp /usr/local/etc/ ; mkdir -p /usr/local/share/doc/p0f ; cp -p docs/* /usr/local/share/doc/p0f/ ) dnl cd tools dnl make all && cp -p p0f-client p0f-sendsyn p0f-sendsyn6 /usr/local/bin/ dnl cd ../.. dnl echo 'P0F_IFACE="eth0"' > /etc/default/p0f dnl #echo 'P0F_SOCKET="/var/run/p0f.sock"' >> /etc/default/p0f dnl #echo 'P0F_FINGERPRINT_DATABASE="/usr/local/etc/p0f.fp"' >> /etc/default/p0f dnl wget https://mta.org.ua/p0f/ubuntu-16/etc/init.d/p0f -O/etc/init.d/p0f dnl chmod +x /etc/init.d/p0f dnl systemctl enable p0f dnl systemctl start p0f dnl dnl отдельно нужно собрать exim-p0f3-dlfunc и скопировать файл exim-p0f3-dlfunc.so в каталог /usr/local/libexec/exim/ dnl dnl https://lists.exim.org/lurker/message/20120707.161423.79af567d.en.html dnl https://dist.epipe.com/exim/ dnl https://dist.epipe.com/exim/exim-p0f3-dlfunc-0.1.README.txt dnl https://dist.epipe.com/exim/exim-p0f3-dlfunc-0.1.tar.gz dnl https://github.com/snabb/exim-p0f3-dlfunc dnl https://github.com/snabb/exim-p0f3-dlfunc.git 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 под RHEL/Oracle Linux/CentOS/Fedora: 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 пример для сборки exim из deb-src под Debian/Ubuntu: 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 /tmp/pbuild/exim4/exim4-4.*/build-tree/build-exim4-daemon-custom /tmp/pbuild/exim4/exim4-4.*/b-exim4-daemon-custom/build-* /tmp/pbuild/exim4/exim4-4.*/b-exim4-daemon-light/build-* | tail -n 1`" ./configure --libdir=`ls -1d /tmp/pbuild/exim4/exim4-4.*/build-tree/build-exim4-daemon-custom /tmp/pbuild/exim4/exim4-4.*/b-exim4-daemon-custom/build-* /tmp/pbuild/exim4/exim4-4.*/b-exim4-daemon-light/build-* | 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]`'_GEO_DATA_INFO_ ') 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]`'_GEO_DATA_INFO_`'${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]`'_GEO_DATA_INFO_`'${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]`'_GEO_DATA_INFO_`'${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]`'_GEO_DATA_INFO_`'${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]`'_GEO_DATA_INFO_`'${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]`'_GEO_DATA_INFO_`'${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 }}`'_GEO_DATA_INFO_`'[$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', `')