%%Title: Clam AntiVirusを試してみる

%%Created: Mon Aug 25 15:04:08 JST 2003
%%Updated: Mon Apr 18 19:43:07 JST 2005

以前から日に数十通か来ていたウイルス入りメールだけど、
感染の心配もないし無視できる量だった。
けど、2003/8/16の MS Blaster騒ぎのあと山のように降ってきて、
どれくらいかと言うと4時間で800通位、
1通100KB弱なので、4時間もほっとけば 70MBのスプールが溢れちゃう…
これはさすがに我慢ならないので何か対策を考えてみる。

o 何使ったらいいの?

	やりたいことは以下の4つ

		サーバ側ではねたい
		ウイルス入りメールだけをはねたい
		漏れたウイルスメールは次回はねるようにしたい
		できればソースコードが欲しい

	bogofilterを使うとウイルス入りメールじゃなくてもはねちゃうし、
	そもそもbogofilterにウイルスメールを学習させると正しくspamチェックできなくなることもあるので、
	ここは Anti-Virusアプリケーションの導入を考えてみた。

	2003/08の時点の FreeBSDの /usr/ports/security を眺めると以下のアプリケーションがあった。
		AMaViS
			MTAとエンジンのパイプ。 perlなので速度が気になる。データベースが無償じゃなくなったらしい。
		avcheck
			MTAとエンジンのパイプ。エンジンは Dr.WEBKasperskyを使う。
		Dr.WEB
			有償。お試し版があるが機能限定。
		H+BEDV
			有償。個人使用なら無料らしいけどバイナリ配布だし…
		Clam AntiVirus
			GPLライセンス。データベースは OpenAntiVirusを元にしている。

	てな分けで Clam AntiVirusを試してみる。

	Clam AntiVirusはファイルを検査してウイルスを見つけたり、ウイルスの入った
	ファイルからシグネチャを作りユーザがデータベースを更新することができる。
	圧縮してあるファイルも拡張子に対応して自動的に解凍して検査してくれる。

o どれくらい使えるか?

	どういう基準を満たせば使えるのかとか、どれくらいのパフォーマンスが
	必要なのかとか、はっきり言ってウイルスとは無縁の世界に住んでいたので、
	まったく知識がありません。

	以下のページの1章が参考になるかも。
	http://clamav-jp.sourceforge.jp/

	これによると相当使えなさそうだが、
	今回の要求は「ウイルス入りのメールを検出したい」なので十分だと思う。


o NetBSDでのインストール

	NetBSDは 2005/04/18の時点で、まだパッケージ化されていない。
	libgmp4, libcurl が必要なので、これはパッケージから突っ込む。
	パッケージを突っ込んだら、どっかで
		setenv LDFLAGS "-L/usr/pkg/lib"
		setenv CFLAGS "-I/usr/pkg/include"
	しとかないとはまる。

	% ./configure --with-dbdir=/usr/local/etc/clamav --disable-clamko --disable-clamav --enable-bigstack --disable-dependency-tracking
		:
	configure: WARNING: resolv.h: present but cannot be compiled
	configure: WARNING: resolv.h:     check for missing prerequisite headers?
	configure: WARNING: resolv.h: see the Autoconf documentation
	configure: WARNING: resolv.h:     section "Present But Cannot Be Compiled"
	configure: WARNING: resolv.h: proceeding with the preprocessor's result
	configure: WARNING: resolv.h: in the future, the compiler will take precedence
	configure: WARNING:     ## ------------------------------------------ ##
	configure: WARNING:     ## Report this to the AC_PACKAGE_NAME lists.  ##
	configure: WARNING:     ## ------------------------------------------ ##
		:

	は無視できる。

	単に
	# make install
	すると /usr/local にインストールされるので /usr/pkgが好きな人は注意。


o FreeBSDでのインストール

FreeBSDだとパッケージ化されているので、さくっとインストールできる。

インストールされるプログラムのリスト
	/usr/local/bin/clamscan
		ウイルススキャンする
	/usr/local/sbin/clamd
		ウイルススキャンのサーバ
	/usr/local/bin/clamdscan
		ウイルススキャンのクライアント
	/usr/local/bin/freshclam
		データベースを更新する
	/usr/local/bin/sigtool
		シグネチャ作成ツール

clamscan(1)だけならば非ルート権限で使うことができるが、
clamd(8)を非ルート権限で動かしたいので clamav というユーザとグループを作る。
clamscanだけを使いたい場合はユーザ/グループを作る必要はない。

tarballが作られた時点のデータベースもインストールされる。
	/usr/local/share/clamav/mirrors.txt
	/usr/local/share/clamav/viruses.db
	/usr/local/share/clamav/viruses.db2
これらは freshclam(1)を使ってすぐに更新すべきである。

o テスト

clamscan(1)でファイルを検査してみる。
ウイルスが見つかれば戻り値として0以外を返す。
35997 はウイルス入りのメールである。

	% clamscan 35997
	35997: OK

	----------- SCAN SUMMARY -----------
	Known viruses: 9340
	Scanned directories: 0
	Scanned files: 1
	Infected files: 0
	Data scanned: 0.10 Mb
	I/O buffer size: 131072 bytes
	Time: 0.588 sec (0 m 0 s)

おや??? base64 encodingはサポートしてない(2003/08現在)ようなので事前に
デコードしてからプログラムに渡す。

	% b64decode < 35997 | clamscan - 
	stdin: Worm.Sobig.F FOUND

	----------- SCAN SUMMARY -----------
	Known viruses: 9340
	Scanned directories: 0
	Scanned files: 1
	Infected files: 1
	Data scanned: 0.07 Mb
	I/O buffer size: 131072 bytes
	Time: 1.426 sec (0 m 1 s)

これでいいのかな??
(少なくともclamav-0.84はサポートしています。)

o clamav.conf

o sendmail から使ってみる

少なくとも 0.75.1からは clamavに clamav-milterもパッケージされてます。
また、オプション --slient-discardが -Nに変更されています。

以下は sendmail.cfの所は使えるけど、それ以外は古い情報なので参考程度。

Milter APIを使う。FreeBSD4.7以降じゃないと libmiterが入ってないような気がする。
libmilter入りsendmailのコンパイルの方法はここを参照
http://www.sendmail.com/partner/resources/development/milter_api/api.html

- milterを使うように clamavを作り直す

configure オプションに --enable-milter を指定

少なくともFreeBSD4.8では、clamav-milterのディレクトリに以下のパッチが必要。
/usr/lib/sendmailがハードコーディングされているので適当に書き換える。

	*** Makefile.in.orig	Sat Jun 21 12:07:03 2003
	--- Makefile.in	Wed Aug 27 17:15:50 2003
	***************
	*** 122,124 ****
	  
	! @HAVE_MILTER_TRUE@@USE_PTHREAD_TRUE@clamav_milter_LDADD = ../clamd/cfgfile.o ../clamd/others.o
	  
	--- 122,124 ----
	  
	! @HAVE_MILTER_TRUE@@USE_PTHREAD_TRUE@clamav_milter_LDADD = ../clamd/cfgfile.o ../clamd/others.o ../clamscan/getopt.o
	  
	***************
	*** 129,131 ****
	  LIBS = -L../libclamav -L/usr/lib/libmilter -lmilter @CLAMD_LIBS@
	! INCLUDES = -I../clamd -I../libclamav
	  EXTRA_DIST = clamav-milter.c clamd.sh clamav-milter.sh INSTALL
	--- 129,131 ----
	  LIBS = -L../libclamav -L/usr/lib/libmilter -lmilter @CLAMD_LIBS@
	! INCLUDES = -I../clamd -I../libclamav -I../clamscan
	  EXTRA_DIST = clamav-milter.c clamd.sh clamav-milter.sh INSTALL

	*** clamav-milter.c.orig	Tue May 30 08:17:00 2000
	--- clamav-milter.c	Wed Aug 27 17:17:22 2003
	***************
	*** 952,954 ****
	  
	! 		sendmail = popen("/usr/lib/sendmail -t", "w");
			if(sendmail) {
	--- 952,954 ----
	  
	! 		sendmail = popen("/usr/sbin/sendmail -t", "w");
			if(sendmail) {

clamav.conf を編集する

	# cat /var/tmp/clamav.conf 
	LocalSocket /var/run/clamd.sock
	ScanMail
	StreamSaveToDisk	※ clamav.pdf4.1にはSaveStreamToDiskとあるがはタイポっぽい

- sendmail.cf の設定

mc に以下を追加して cf をリメイク

	INPUT_MAIL_FILTER(`clmilter', `S=local:/var/run/clmilter.sock, F=, T=S:4m;R:4m')dnl
	define(`confINPUT_MAIL_FILTERS', `clmilter')

- 実験
	# /usr/local/sbin/clamd --config-file=/usr/local/etc/clamav.conf
	# /usr/local/sbin/clamav-milter -lo --config-file=/usr/local/etc/clamav.conf /var/run/clmilter.sock

	# /usr/sbin/sendmail -bd -q30m

さて、適当にウイルス入りメールを突っ込むんで /var/log/maillog を見てると

	Aug 27 18:08:27 shoichi sendmail[71113]: h7R98R60071113: from=, size=103095, class=0, nrcpts=1, msgid=<20030827180827B.sakane@tanu.org>, proto=ESMTP, daemon=MTA-v4, relay=shoichi.tanu.org [127.0.0.1]
	Aug 27 18:08:28 shoichi sendmail[71113]: h7R98R60071113: Milter: data, reject=550 5.7.1 Virus detected by ClamAV - http://clamav.elektrapro.com
	Aug 27 18:08:28 shoichi sendmail[71113]: h7R98R60071113: to=, delay=00:00:01, pri=30377, stat=Virus detected by ClamAV - http://clamav.elektrapro.com

と検出されたようだ。あぁぁ幸せ…

ただし、コンソールに

	clamfi_connect: connection from vw10.tanabe.co.jp [202.221.154.148]
	clamfi_connect: connection from orange.kame.net [0.0.0.0]

と表示されるようになった。SMTPコネクションのログっぽい。
0.0.0.0 となるのは IPv6アドレスだから。

さて動かしてると core吐いて落ちました。うぬぬ。
ウイルスが見つかったことを postmasterへ通知するためにsendmailをpopen(3)する所で
落ちていた。

	# gdb clamav-milter clamav-milter.core
	GNU gdb 4.18 (FreeBSD)
		: (snip)
	Core was generated by `clamav-milter'.
	Program terminated with signal 11, Segmentation fault.
	Reading symbols from /usr/lib/libmilter.so.2...done.
	Reading symbols from /usr/lib/libc_r.so.4...done.
	Reading symbols from /usr/lib/libc.so.4...done.
	Reading symbols from /usr/libexec/ld-elf.so.1...done.
	#0  0x280c40da in fileno () from /usr/lib/libc_r.so.4
	(gdb) info st
	#0  0x280c40da in fileno () from /usr/lib/libc_r.so.4
	#1  0x280ac4fa in popen () from /usr/lib/libc_r.so.4
	#2  0x804a4bc in clamfi_eom (ctx=0x805b280) at clamav-milter.c:961
	#3  0x28070c0f in mi_clr_macros () from /usr/lib/libmilter.so.2
	#4  0x28070100 in mi_engine () from /usr/lib/libmilter.so.2
	#5  0x2806fd79 in mi_handle_session () from /usr/lib/libmilter.so.2
	#6  0x2806f59e in mi_thread_handle_wrapper () from /usr/lib/libmilter.so.2
	#7  0x2808f0a8 in _thread_start () from /usr/lib/libc_r.so.4
	#8  0xbfa98ffc in ?? ()

どうもスレッドが絡んでそう…深そうなので追求せず。
オプションで postmasterへ通知しない(popen(3)しない)ようにして回避。
検知したことはsyslogに落ちるので必ずしも通知する必要ないはず。

	*** clamav-milter.c.orig	Thu Sep  4 09:35:45 2003
	--- clamav-milter.c	Mon Sep  1 10:58:41 2003
	***************
	*** 209,210 ****
	--- 209,211 ----
	  static	const	char	*serverIP = "127.0.0.1";
	+ static	int	quiet = 0;
	  
	***************
	*** 283,284 ****
	--- 284,288 ----
				{
	+ 				"silent-discard", 0, NULL, 'Q'
	+ 			},
	+ 			{
					"version", 0, NULL, 'V'
	***************
	*** 324,325 ****
	--- 328,332 ----
					break;
	+ 			case 'Q':	/* server running clamd */
	+ 				quiet++;
	+ 				break;
				case 'V':
	***************
	*** 952,980 ****
	  
	! #if 0
	! 		sendmail = popen("/usr/sbin/sendmail -t", "w");
	! 		if(sendmail) {
	! 			fputs("From: MAILER-DAEMON\n", sendmail);
	! 			if(bflag) {
	! 				fprintf(sendmail, "To: %s\n", privdata->from);
	! 				fputs("Cc: postmaster\n", sendmail);
	! 			} else
	! 				fputs("To: postmaster\n", sendmail);
	! 
	! 			for(to = privdata->to; *to; to++)
	! 				fprintf(sendmail, "Cc: %s\n", *to);
	! 			fputs("Subject: Virus intercepted\n\n", sendmail);
	! 
	! 			if(bflag)
	! 				fputs("A message you sent to\n\t", sendmail);
	! 			else
	! 				fprintf(sendmail, "A message sent from %s to\n\t", privdata->from);
	! 
	! 			for(to = privdata->to; *to; to++)
	! 				fprintf(sendmail, "%s\n", *to);
	! 			fputs("contained a virus and has not been delivered.\n\t", sendmail);
	! 			fputs(mess, sendmail);
	  
	! 			pclose(sendmail);
			}
	- #endif
	  
	--- 959,987 ----
	  
	! 		if (!quiet) {
	! 			sendmail = popen("/usr/local/sbin/sendmail -t", "w");
	! 			if(sendmail) {
	! 				fputs("From: MAILER-DAEMON\n", sendmail);
	! 				if(bflag) {
	! 					fprintf(sendmail, "To: %s\n", privdata->from);
	! 					fputs("Cc: postmaster\n", sendmail);
	! 				} else
	! 					fputs("To: postmaster\n", sendmail);
	! 
	! 				for(to = privdata->to; *to; to++)
	! 					fprintf(sendmail, "Cc: %s\n", *to);
	! 				fputs("Subject: Virus intercepted\n\n", sendmail);
	! 
	! 				if(bflag)
	! 					fputs("A message you sent to\n\t", sendmail);
	! 				else
	! 					fprintf(sendmail, "A message sent from %s to\n\t", privdata->from);
	! 
	! 				for(to = privdata->to; *to; to++)
	! 					fprintf(sendmail, "%s\n", *to);
	! 				fputs("contained a virus and has not been delivered.\n\t", sendmail);
	! 				fputs(mess, sendmail);
	  
	! 				pclose(sendmail);
	! 			}
			}

しばらく使ってみたが3日に1度、同じ所で落ちるみたい。
	# gdb clamav-milter /root/clamav-milter.core
	GNU gdb 4.18 (FreeBSD)
			: (snip)
	Core was generated by `clamav-milter'.
	Program terminated with signal 11, Segmentation fault.
	Reading symbols from /usr/lib/libmilter.so.2...done.
	Reading symbols from /usr/lib/libc_r.so.4...done.
	Reading symbols from /usr/lib/libc.so.4...done.
	Reading symbols from /usr/libexec/ld-elf.so.1...done.
	#0  0x804a672 in clamfi_cleanup (ctx=0x805b280) at clamav-milter.c:1040
	1040            if(privdata->dataSocket >= 0) {
	(gdb) info st
	#0  0x804a672 in clamfi_cleanup (ctx=0x805b280) at clamav-milter.c:1040
	#1  0x804a628 in clamfi_abort (ctx=0x805b280) at clamav-milter.c:1012
	#2  0x280701f0 in mi_engine () from /usr/lib/libmilter.so.2
	#3  0x2806fd79 in mi_handle_session () from /usr/lib/libmilter.so.2
	#4  0x2806f59e in mi_thread_handle_wrapper () from /usr/lib/libmilter.so.2
	#5  0x2808f0a8 in _thread_start () from /usr/lib/libc_r.so.4
	#6  0xbf9bbffc in ?? ()
	(gdb) 

うむむ、スレッド回りに虫がいるのかも…
  
o ウイルスデータベース
OpenAntiVirusのデータベースは更新されていないようだが、Clam AntiVirus側で更新し続けているようである。
ここから拾ってこれる。

freshclam(1)を使うと見て自動的にデータベースが更新される。
mirror.txt に書かれている所から MD5をチェックして取ってくる。

	# freshclam
	Current working dir is /usr/local/share/clamav
	Checking for a new database - started at Wed Aug 27 18:05:54 2003
	Connected to clamav.elektrapro.com.
	Reading md5 sum (viruses.md5): OK
	Reading md5 sum (viruses2.md5): OK
	Downloading viruses.db .........................................(snip)
	Downloading viruses.db2 ....... done
	Database updated (containing in total 9433 signatures).
	Database updated from clamav.elektrapro.com.

cron(8)で動かすと便利かもしれない。

	# cat /etc/crontab
	0 5 * * * clamav /usr/local/bin/freshclam --quiet -l /var/log/freshclam.log

freshclamはユーザclamavで実行されるので /var/log/clam-update.logに書けるようにしておく必要がある。

	# touch /var/log/freshclam.log
	# chown clamav:clamav /var/log/freshclam.log

/etc/newsyslog.conf の編集も忘れずに

	# cat /etc/newsyslog.conf
		: (snip)
	/var/log/freshclam.log  clamav:clamav   640  2     100  *     Z

sigtoolを使うと自分でデータベースのレコード(virus signature)を作れる。

ここ
からウイルスのレポートを送ることもできる。反映されるのかは不明。

o clamdscan
	clamd(8)のクライアント