/*
 * -----------------------------------------------------------
 *
 *   Dr.Web(R) Mail Filter for Sendmail [*]
 *
 * -----------------------------------------------------------
 */

1. Требования
2. Как обеспечить совместную работу Sendmail и drweb-smf
  2.1 Как это работает
  2.2 Изменение sendmail.mc и sendmail.cf
  2.3 Настройка drweb-smf
  2.4 Обработка сигнала SIGHUP
3. Как собрать Sendmail с поддержкой Mail Filter API
  3.1 Установка исправлений для Sendmail
  3.2 Сборка из отдельно взятых исходных текстов
  3.3 Сборка Sendmail из дистрибутива FreeBSD
  3.4 Сборка Sendmail из FreeBSD ports
4. Известные проблемы
5. Контакты


        1. Требования

- Запущенный Dr.Web Daemon (drwebd) версии не ниже 4.33.
- Фильтр drweb-smf.
- Исходные тексты Sendmail версии не ниже 8.11. С версиями 8.12.0
  Beta1-Beta9 фильтр не работает из-за ошибки в Sendmail, в последующих
  бета-версиях эта ошибка исправлена. Они нужны только, если Sendmail
  не имеет поддержки Milter API; часть дистрибьюторов (например, RedHat)
  стала поставлять Sendmail с уже включенной поддержкой Milter API.


        2. Как обеспечить совместную работу sendmail и drweb-smf

Будем исходить из того, что ваш Sendmail уже поддерживает Milter API;
если это не так, то обратитесь к пункту 3, где рассказано, как
пересобрать Sendmail с поддержкой нужного API.

   2.1 Как это работает

Итак, что же дает Sendmail Mail Filter API:

+----------+      +------------------------+      +--------+
| sendmail |<---->| milter-api | drweb-smf |<---->| drwebd |
+----------+    ^ +------------------------+    ^ +--------+
                1                               2

1. Через транспортное соединение, определяемое со стороны drweb-smf
транспортным адресом __ADDRESS__ (см. ниже), передаются внутренние
команды Milter API и сообщение (при этом сообщение передается не сразу
целиком, а по частям, в зависимости от фазы почтовой сессии: helo,
mail from:, rcpt to: и т.д.). Поэтому сообщение фильтр сохраняет во
временных файлах (см. опцию -f). Посредством Milter API drweb-smf дает
команду, что делать с данным сообщением. Milter API является
многопоточной библиотекой, то есть одновременно в процессе может
находиться несколько почтовых сессий. Таким образом, в данной схеме
взаимодействия Sendmail является клиентом, а mail filter - сервером,
поэтому в sendmail.cf и в командной строке фильтра указывается адрес
фильтра, а Sendmail для этого соединения выбирает подходящий клиентский
адрес.

2. Через другое транспортное соединение при помощи интерфейса Dr.Web
Daemon API drweb-smf передает команды (и сообщение, если опцией -l не
включен локальный режим) демону и ждет ответа. В случае положительного
ответа (сообщение "чисто") временный файл удаляется и Sendmail'у
разрешается принять это письмо. В случае нахождения вируса само
сообщение перемещается в архив инфицированных сообщений (см. опцию -a),
вместо сообщения адресатам и (опционально) отправителю (опция -x) и
администратору (опция -g) отправляется уведомление. В случае ошибок при
обработке сообщения предпринимается действие, задаваемое опцией -b. В
данном взаимодействии клиентом является уже фильтр, демон же является
сервером, поэтому в ini-файле демона и в командной строке фильтра
указывается адрес
демона.

Таким образом, drweb-smf является простым посредником (или
преобразователем) между интерфейсом sendmail и drwebd. Все три программы
могут выполняться на разных машинах, однако оптимальным вариантом будет
установка фильтра и демона на одной машине, что позволит (при
использовании опции -l избежать повторной передачи сообщения, так как
демон будет сканировать сообщение как локальный дисковый файл.

   2.2 Изменение sendmail.mc и sendmail.cf

Если вы не хотели бы пересобирать sendmail.cf, вы можете просто вставить
или добавить (если соответствующие определения уже есть) в sendmail.cf:

Для версий 8.11:
-------------------- cut ------------------
############################
# Input mail filters
############################
O InputMailFilters=drweb-filter
############################
#      Xfilters
############################
Xdrweb-filter,  S=__ADDRESS__, F=T, T=S:5m;R:5m;E:1h
-------------------- cut ------------------

Для версий 8.12:
Если вы хотите использовать проверку для локально отправленных сообщений
(через вызов mail или sendmail), вы должны продублировать все изменения в
submit.cf (и submit.mc) тоже. Кроме этого вы должны добавить к
"O PrivacyOptions" значение nobodyreturn. Например:

-------------------- cut ------------------
# privacy flags
O PrivacyOptions=goaway,noetrn,nobodyreturn
-------------------- cut ------------------

или в {sendmail_src}/cf/cf/feature/msp.m4:

-------------------- cut ------------------
define(`confPRIVACY_FLAGS' `goaway,noetrn,nobodyreturn,restrictqrun')dnl
-------------------- cut ------------------

Теперь перейдем к изменениям в sendmail.cf:

-------------------- cut ------------------
############################
# Input mail filters
############################
O InputMailFilters=drweb-filter
O Milter.LogLevel=6
############################
#      Xfilters
############################
Xdrweb-filter,  S=__ADDRESS__, F=T, T=C:1m;S:5m;R:5m;E:1h
------------------- cut ---------------------

Сейчас можно установить следующие флаги (F=) для случая, когда фильтр
недоступен:

        R               Отказать в доставке 
        T               Временно отложить доставку
(если ни F=R, ни F=T не указаны, то сообщение пропускается без проверки),

или добавить в sendmail.mc (дополнительные опции описаны в README в
каталоге с исходными текстами библиотеки libmilter):

только для версий Sendmail 8.11:
------------------- cut ---------------------
define(`_FFR_MILTER',1)
INPUT_MAIL_FILTER(`drweb-filter', `S=__ADDRESS__, F=T, T=S:5m;R:5m;E:1h')
------------------- cut ---------------------

и для версий Sendmail 8.12 и выше:
------------------- cut ---------------------
INPUT_MAIL_FILTER(`drweb-filter', `S=__ADDRESS__, F=T, T=C:1m;S:5m;R:5m;E:1h')
define(`confMILTER_LOG_LEVEL',`6')
------------------- cut ---------------------

Замечание. Величину таймаута лучше выбирать в соответствии с величинами
таймаутов Sendmail "O Timeout.datablock=XX" (по умолчанию эта величина
равна 1 часу, XX=>1h)

После этого необходимо перекомпилировать sendmail.cf.

Замечание. __ADDRESS__ - строка, задающая транспорт для подключения
фильтра. Она имеет формат и значение, идентичные используемому в файле
конфигурации фильтра для параметра MilterAddress. Она может быть для
TCP/IP cемейства сокетов:

inet:__PORT__@__HOST__

(__PORT__ и __HOST__ должны иметь конкретные значения, например,
inet:3001@localhost)

или для UNIX-DOMAIN сокетов:

local:__SOCKPATH__

(аналогично __SOCKPATH__ должен указывать путь, доступный с теми
правами, с которыми вы будете запускать фильтр, например,
local:/var/run/drweb-smf.sock)

Тонкости настройки фильтра можно найти в документации на Sendmail
(начать можно с {sendmaildir}/libmilter/README).

После этого перезапускаем Sendmail.

ВАЖНО: Оптимален режим, когда демон находится на одной машине с фильтром
(но вовсе не обязательно с Sendmail'ом), так как при этом сообщение
можно не передавать между демоном и фильтром через сокеты, а
использовать сканирование локальных файлов. Для этого необходимо, чтобы
привилегии демона и фильтра были достаточны для доступа к общему
каталогу, а также чтобы привилегий демона хватало для чтения временных
файлов, созданных фильтром.

Для того чтобы фильтр мог в логе при выводе сообщений указывать
sendmails message ID (идентификатор этого сообщения для Sendmail),
убедитесь, что в sendmail.cf присутствует следующая строка:

------------------- cut ---------------------
O Milter.macros.envfrom=i, ... 
------------------- cut ---------------------

(многоточие означает другие параметры - их значение не важно).

Для того чтобы в заголовок Received: фильтр мог записать IP-адрес
хоста, от которого принято сообщение, убедитесь, что в sendmail.cf
присутствует следующая строка:

------------------- cut ---------------------
O Milter.macros.connect=_, ...
------------------- cut ---------------------

(многоточие означает другие параметры - их значение не важно).

Для подавления вывода в syslog сообщений вида:

------------------- cut ---------------------
X-Authentication-Warning: some.domain.com: drweb set sender to DrWeb-DAEMON@some.domain.com using -f
------------------- cut ---------------------

внесите пользователя drweb (или того, от которого работает drweb-smf)
в список trusted-users в файле submit.cf

------------------- cut ---------------------
#####################
#   Trusted users   #
#####################
Tdrweb
------------------- cut ---------------------

или добавьте в submit.mc

------------------- cut ---------------------
define(`confTRUSTED_USERS', `drweb')"
------------------- cut ---------------------


   2.3 Настройка drweb-smf

Параметры работы фильтра задаются в файле конфигурации. При старте без
параметров фильтр пытается найти файл конфигурации в стандартных (для
фильтра) каталогах, которые можно узнать, просто запустив фильтр из
консоли:

$ drweb-smf --help

Если по каким-то причинам данные пути размещения файла конфигурации не
устраивают, то фильтру можно явно задать имя файла конфигурации, указав
в Content Filtering не только путь к фильтру, но и задав параметр:

{path/to}/drweb-smf --conf={path/to/conf/file}

Внимание: {path/to/conf/file} должен содержать и имя файла тоже.

После этого необходимо отредактировать файл конфигурации и настроить
шаблоны писем-уведомлений. При старте фильтр пытается проверить свою
работоспособность в данной конфигурации. Для этого он создает и
проверяет тестовый файл в спуле.

Описание файла конфигурации фильтра, шаблонов писем-уведомлений и
списков исключаемых из проверки пользователей вы найдете в файлах
conf_file.rus.txt, notify.rus.txt и users_list.rus.txt соответственно.

Для того, чтобы узнать версию фильтра, просто запустите его со следующим
параметром:

$ drweb-smf --version

Вы можете запустить фильтр в режиме проверки конфигурационного файла,

$ drweb-smf --check_only

или
    
$ drweb-smf --check_only --check_user={USER},
        
при этом фильтр запустится (сменит уровень привилегии на {USER}),
прочитает конфигурационный файл и проверит параметры, которые возможно
проверить. О своих действиях он будет сообщать на стандартный поток
вывода (stdout).


   2.4 Обработка сигнала SIGHUP

Корректная обработка сигнала SIGHUP возможна только в двух случаях:

- вы используете фильтр, собранный производителем;
- вы используете патч для sendmail-8.12.9 и собираете из исходников.

Во всех остальных случаях фильтр завершает работу по сигналу SIGHUP.

На данный момент, фильтр произведет следующие действия при обработке
сигнала:
- перезагрузит файл с DenyList (см. секцию [Scanning] конфигурационного
  файла);
- перезагрузит файлы UnnotificableVirusesList и UnnotificableAddressesList
  (см. секцию [Actions])


        3. Как собрать Sendmail с поддержкой Mail Filter API

Вам необходимо собрать Sendmail с поддержкой Mail Filter API.
Если у вас это уже сделано, можете переходить к пункту 4, не забыв
скопировать libmilter.a и libsmutil.a для версии 8.11.x (Linux) или
libsm.a для версии 8.12.x (libsmutil или libsm нужны в Linux, во
FreeBSD не нужны) в /usr/local/lib, и mfapi.h в /usr/local/include.

   3.1 Установка исправления для Sendmail

Исправления можно не устанавливать. Всего есть три исправления:

- listener-8.11.1 - исправляет ошибку в sendmail-8.11.1, в результате
которой фильтр при завершение работы оставляет core-файл. 
Ошибка нефатальная, исправлена в следующих версиях.

- listener-8.12.0 - исправляет ошибку в sendmail-8.12.0, в результате
которой, если фильтр использует unix-сокет, то файл сокета не удаляется
при завершении работы фильтра. 
Ошибка критическая, исправлена с версии 8.12.2

$ cp listener-8.1X.Y.patch /место/где/лежат/исходные/тексты/libmilter
$ cd /место/где/лежат/исходные/тексты/libmilter
$ patch < listener-8.1X.Y.patch

- sendmail-8.12.2 - исправляет ошибку в sendmail-8.12.2, в результате
которой при использовании демо-ключа размер передаваемых сообщений
увеличивался вдвое.
Ошибка критическая, должна быть исправлена с версии 8.12.3

$ cp sendmail-8.12.2.patch /место/где/лежит/дерево/sendmail
$ cd /место/где/лежит/дерево/sendmail
$ patch < sendmail-8.12.2.patch

   3.2 Сборка из отдельно взятых исходных текстов

Далее везде считаем, что {sendmaildir} - это каталог, куда распакован
Sendmail (например /usr/local/src/sendmail-8.xx.x):

$ cd {sendmaildir}

Шаг A)
Надо указать, что вы хотите собирать с поддержкой libmilter, для этого:

$ cd {sendmaildir}/devtools/Site/

Если файла site.config.m4 нет, то создайте его, а если есть, то
добавьте в него следующие строки:

для версий Sendmail до 8.12.x:
--- cut ---
dnl Milter
APPENDDEF(`conf_sendmail_ENVDEF', `-D_FFR_MILTER')
APPENDDEF(`conf_libmilter_ENVDEF', `-D_FFR_MILTER')
--- cut ---

для версий Sendmail c 8.12.0 по 8.12.8 или, если ваша система не
поддерживает системный вызов poll:
--- cut ---
dnl Milter
APPENDDEF(`conf_sendmail_ENVDEF', `-DMILTER')
--- cut ---

для версий Sendmail c 8.12.9 и выше:
--- cut ---
dnl Milter
APPENDDEF(`conf_sendmail_ENVDEF', `-DMILTER')
APPENDDEF(`conf_libmilter_ENVDEF', `-D_FFR_USE_POLL')
--- cut ---

Если при сборке Sendmail появляются ошибки, то возможно, надо добавить
следующие строки:
--- cut ---
APPENDDEF(`confLIBSEARCH', `db db2 bind resolv 44bsd')
APPENDDEF(`confINCDIRS', `-I/usr/include/db2/ -I/usr/include/db1/')
APPENDDEF(`confMAPDEF', `-DNEWDB ')
--- cut ---

Шаг B)
Теперь собирается библиотека libmilter:

$ cd {sendmaildir}/libmilter
$ ./Build -c

Осталось собрать и установить сам Sendmail:

$ cd {sendmaildir}
$ ./Build -c

Далее необходимо установить Sendmail так, как это описано в файле
{sendmaildir}/INSTALL.

   3.3 Сборка Sendmail из дистрибутива FreeBSD (/usr/src/contrib)

Если у Вас FreeBSD, то первое, что необходимо сделать, - добавить в
make.conf (который по умолчанию находится в /etc/defaults/) следующую
строку:

для версий Sendmail до 8.12.x:
SENDMAIL_CFLAGS=-D_FFR_MILTER

для версий Sendmail c 8.12.x:
SENDMAIL_CFLAGS=-DMILTER

Предполагается, что у вас расположение каталогов стандартное для
FreeBSD 4.x. Если это не так, изменения в шагах минимальны и
достаточно прозрачны (здесь и далее $ означает shell prompt):

$ cd /usr/src/lib/libsmutil
$ make clean all
$ cd /usr/src/lib
$ mkdir libmilter
$ cd libmilter

Создаем в этом каталоге Makefile следующего содержания:

-------------------- cut --------------------------
SENDMAIL_DIR=${.CURDIR}/../../contrib/sendmail
.PATH:  ${SENDMAIL_DIR}/libmilter

CFLAGS+=-I${SENDMAIL_DIR}/src -I${SENDMAIL_DIR}/include
CFLAGS+=-DNEWDB -DNIS -DMAP_REGEX -DNOT_SENDMAIL

# User customizations to the sendmail build environment
CFLAGS+=${SENDMAIL_CFLAGS}

LIB=    milter

SRCS+=  comm.c engine.c handler.c listener.c main.c signal.c sm_gethost.c smfi.c

INTERNALLIB=        true
NOPIC=              true
INTERNALSTATICLIB=  true

.include <bsd.lib.mk>
--------------------- cut --------------------------

Далее:

$ make clean all

Пересобираем сам Sendmail:

$ cd /usr/src/usr.sbin/sendmail
$ make clean all install

   3.4 Сборка Sendmail из FreeBSD ports

Если вы собираете Sendmail (версии 8.11.5 или выше, а возможно и ниже)
из FreeBSD ports collection, вы можете просто отредактировать
/etc/make.conf и установить следующую опцию:

SENDMAIL_WITH_MILTER=Yes

и далее продолжить обычный процесс сборки Sendmail.


        4. Известные проблемы

Описание. При использовании Unix сокета для коммуникации между фильтром
и Sendmail`ом библиотека поддержки Milter API (поставляемая вместе с
Sendmail) не удаляла (до версии 8.12.2) используемый под сокет файл.

Решение. Для версии 8.12.x вы можете найти исправление
listener-8.12.0-1.patch, процесс установки которого полностью идентичен
(кроме имени файла) описанному в пункте 3.1. Для версий 8.11 этот файл
нужно удалять вручную или из скрипта, осуществляющего управлением
фильтром. Данная проблема решена в sendmail 8.12.2

Описание. При использовании "локального" сканирования и демо-ключа,
после прохождения фильтра значение размера сообщения, передаваемое
следующему серверу, увеличивается вдвое (само сообщение либо остается
неизменным, либо к нему дописывается небольшое сообщение - "баннер").

Решение. Данная проблема решена в sendmail 8.12.3 (и далее).

Описание. При работе фильтра на загруженных машинах в почтовом логе
можно наблюдать записи следующего вида:
"... Milter (drweb-filter): select(read): interrupted system call"

Решение. Данная проблема решена в sendmail 8.12.3 (и далее).

Описание. При работе фильтра на загруженных машинах в почтовом логе
можно наблюдать записи следующего вида:
"... Milter (drweb-filter): select(read): timeout before data write"
"... Milter (drweb-filter): to error state"

Решение. Проблема связана с тем, что sendmail не может установить
соединение с фильтром за заданный таймаут. В версиях 8.11 он равен
5 секундам и не может быть изменен, в версиях 8.12 этот таймаут
изменяется в описании фильтра (значение при C:)
Xdrweb-filter,  S=__ADDRESS__, F=T, T=C:1m;S:5m;R:5m;E:1h



        5. Контакты

Программа Dr.Web находится в постоянном развитии, наиболее свежую
информацию о ее обновлениях и новости можно получить на сайтах:
  http://www.drweb.com

Отдел продаж:
  http://buy.drweb.com
  e-mail: sales@drweb.com

Консультации пользователей осуществляются по следующим адресам:
  http://support.drweb.com
  e-mail: support@drweb.com

В письме желательно дать следующую дополнительную информацию, которая
поможет лучше разобраться в ситуации:

- полное название и версию дистрибутива Unix-системы;
- версию программы Dr.Web;
- версии приложений и фильтров, с которыми интегрируется Dr.Web Daemon;
- конфигурационные файлы: демона (drweb32.ini), drweb_smf.conf;
- логи: демона и почтовые (обычно maillog);
- фрагмент sendmail.cf со сведениями о фильтре
  (все строки, содержащие "drweb-filter").

-----
* - Sendmail - это товарный знак компании Sendmail, Inc.
