あれほどいったのに、まだわからんのくわあぁ! というほど世間は Outlook およびその親戚に満ちている。 田中・徳永研では新山が睨みをきかせているせいか、 今のところ研究室内で Outlook を使っているユーザはいない。 けれども Windows はいつもことあるごとに Outlook を インストールさせようとしてくるし、Office を入れたときや ノートパソコンが持ちこまれたときにうっかり Outlook が起動する可能性は あるのだ。そんなことで研究室がウイルスの発信元になるのはごめんである (まあ Outlook 自体、ウイルスみたいなものだが)。 そのため、政治的な対策だけでなく技術的な (レッシグ的にいえば、コードによる) 制限を加える必要がある。 しかし、世間一般からは相変わらず Outlook を使ったメールが山のように 送られてくるので、Outlook のメールを完全に中継拒否することはできない。 そこで、
ようにしたい。 どうすればできるのか、というのがこの文章のテーマである。
さて、田中・徳永研ではメールサーバに qmail + tcpserver を 使っている。研究室内はプライベートネットワークであり、 外部に送るメールは必ずメールサーバを経由しなければならない (直接外の SMTP は叩けないようになっている)。ゲートウェイ上の メールサーバは内→外および外→内のメールのどちらも中継するようになっている。 この状況で、上の要求をより細かく書くと以下のようになる:
X-Mailer: Microsoft Outlook
〜」を含むものは外部に中継しない
(つまりメールサーバが受けとりを拒否するようにする)。
これをどのように実現すればよいのか。qmail にパッチをあてることは、 できるだけしたくない。djb のソースに手を入れずに、 qmail + tcpserver の構成でどこに手を加えればこの機能が実現できるのか?
今回目をつけたのは qmail-queue
にラッパをかぶせることである。
qmail-queue
は標準入力からメッセージを読み込み qmail のキューに
溜めるもので、ゲートウェイ上のメールサーバでは
研究室に出入りするすべてのメールが必ず qmail-queue
を通るはず
だからだ。それに qmail-queue
は引数もなにもないし、単純な
インターフェイスを持っていて置きかえやすいように作られている。
実際、QMQP を使った配送など、qmail-queue
を置き換えるケースもある。
そこで qmail-queue
に一段ラッパをかぶせて、そこでヘッダの検査を
行うようにすればよい。そしてヘッダ中に「X-Mailer: Microsoft*
」を
含むメッセージは、受けとりを拒否すればよいのだ。
qmail-queue
のマニュアルをみるとこれは終了状態 31 を
返せばよい。この場合 qmail-smtpd
はエラー 554 を返して
受けとり拒否された旨を表示するため、メールはもとのユーザに
バウンスされる。検査する部分はヘッダだけでよいため、
ラッパが記憶する部分はメールのヘッダだけでよい (これは
たいした分量にはならないはずだ)。ヘッダの検査に通ったあとは
qmail-queue
を起動し、そこに今まで読みこんだヘッダと
メールの本文 (標準入力) を流してやればよい。そして qmail-queue
の
終了状態と同じ終了状態で exit するのである。
では、次に「内→外に向かうメールだけを検査の象にする」には
どうするか。ここで tcpserver
の rule を使う。
ゲートウェイ上の tcpserver
には、
内側のホストから SMTP 接続を受たときには特定の環境変数
(この場合は CHECKHEADER
とした) を設定させるようにしておく。
そして qmail-queue
のラッパには、 CHECKHEADER
が
設定されているときだけ
ヘッダを検査するようにさせる。qmail-queue
は
tcpserver
から呼び出される qmail-smtpd
の
子孫として実行されるので、まだ環境変数は継承されているはずだ。
これによって、内→外の Outlook メールだけをはじくことができる。
しかしまだ問題がある。外からきた Outlook のメールが
いったん研究室内に配送され、転送されてふたたび外に出るときは
どうすればよいのか? このような場合には出してあげるのが筋というものである。
それには、メールの Delivered-To:
ヘッダを見ればよい。
qmail では、一度あるアドレスに配送されたメールには必ずこのヘッダが
入る。そのため、ヘッダの検査は次のような規則で行う:
X-Mailer: Microsoft*
」が含まれていたら、
配送しない。
Delivered-To: *@cl.cs.titech.ac.jp
」が
含まれていたら、配送する。
そこで今回は上で述べたような機能をもち、
しかもなるべく一般的に使えるラッパ checkheader
を作った。
checkheader
がチェックするのはヘッダだけだが、
問題は上の規則をどうやって指定するかということである。
コード中に埋めこむのはやりたくない。それに
「X-Mailer: Microsoft*」のようにパターンも使いたい。
どうすればいいか?
ここで思いついたのが、multilog の規則を利用する方法である。
multilog では、どの行をログに残しどの行を残さないかを
-'pattern', +'pattern'
で指定できる。
ログのある行が 「+'pattern'
」
で指定されたパターンにマッチすればその行は記録され、
「-'pattern'
」
で指定されたパターンにマッチすればその行は記録されない。
そしてこれらのパターンはコマンドライン引数で与えられ、
左から右に走査される。
パターンは右に現れるものが一番優先順位の高い規則になる。
また、パターン中にはワイルドカード *
が使える。
同じことをメールのヘッダに対してもおこなえばよい。
ただし、この場合フィルタリングするのはメールのヘッダ 1行ではなく
メッセージ全体なので、パターンの適用範囲を 1行ではなく
メールのヘッダ全体にひろげる。このことから、checkheader
の
動作を次のように設計した:
-'pattern'
が指定された
場合、このパターンにマッチするヘッダが
存在すれば、このメールは拒否される。
+'pattern'
が指定された
場合、拒否されたメール中に
このパターンにマッチするヘッダが
存在すれば、このメールは受けとられる。
この仕様なら、先の規則を実現するには
「-'x-mailer: microsoft*' +'delivered-to:*@cl.cs.titech.ac.jp'
」
のようなコマンドラインを与えてやればよい。
qmail-queue
のラッパを、
「checkheader
を exec するシェルスクリプト」にしておけば、
お好みの規則を加えることも簡単。
実際の実装では、
標準入力からヘッダを読み込んで処理するのに、qmail についてきた
関数 headerbody
を使った。これはメールを読み込み、
それぞれの場所 (ヘッダ、ヘッダ終了、本体) に応じたコールバック関数を
呼びだす。これを使うと、めんどうなテキスト処理はほとんど必要なくなる。
ほんとうはヘッダ名の書式にはばらつきがあるため token822
や
hfield
を使う必要があるのだが、
この用途では単純な行単位のパターンマッチで十分なのでパスする。
ただし multilog に含まれていた match()
を拡張し、
大文字小文字を区別しないようにした。
あとはヘッダを一時的に保存するバッファ (stralloc 100個分 -
ヘッダが 100行以上続くことはふつうあるまい) を確保して、
ヘッダを読み込み終わったところで受けとり検査をすれば OK。
拒否なら exit(31);
し、受けとるなら
qmail-queue
を fork/exec して子プロセスにヘッダを渡す。
すべて終わったら qmail-queue
の終了を待ち、
自分も同じ終了状態で exit するようにした。
checkheader 書式:
checkheader qmail-queueのパス パターン1 パターン2 ...
/var/qmail/bin/qmail-queue
を指定する。
*
が使える:
-'pattern'
(このパターンにマッチするヘッダが含まれている
メールを拒否する)
+'pattern'
:
(このパターンにマッチするヘッダが含まれている
メールを受けとる)
終了状態は、qmail-queue
に準じる。
パターン中の大文字小文字は区別しない。
先の規則にしたがって実際に Outlook をしめだす方法は次のとおり。
checkheader
ができるので、
これを /var/qmail/bin
に手動でインストールする。
# 192.168.208.:allow,RELAYCLIENT="",CHECKHEADER="" 127.:allow,RELAYCLIENT="",CHECKHEADER="" :allow
/var/qmail/bin
以下の
qmail-queue
を qmail-queue.bin
に
リネームする。つぎに、ラッパとなるシェルスクリプトを
作ってこれを qmail-queue
と名づけ、実行可能にする:
#!/bin/sh exec /var/qmail/bin/checkheader /var/qmail/bin/qmail-queue \ -'x-mailer: microsoft*' \ +'delivered-to:*@cl.cs.titech.ac.jp'
(パターンはお好みに応じてご変更ください)
ということで、これで研究室内で Outlook を使っても意味が なくなるし、Outlook-互換のウイルスを開いても 外には送られなくなる (しかし、最近はやりの SirCam はムリらしい)。 ざまーみろ Outlook!