CVE-2012-どさにっき 〜2012年5月下旬〜

by やまや
<< = >>

2012年5月23日(水)

timeout after DATA

_ 数日前からこんなログが出てるのに気がついた。なんだべ。

May 23 11:20:03 <mail.info> eel postfix/smtpd[16125]: timeout after DATA (0 bytes) from ***.***.co.jp[x.x.x.x]
ちゃんと再送されてきてるので、待ち構えてパケットをキャプチャしてみた。
# ngrep -q host x.x.x.x
(略)
T x.x.x.x:5000 -> 192.168.0.1:25 [AP]
  MAIL FROM:<****@***.co.jp> SIZE=2135..RCPT TO:<y@maya.st> ORCPT=rfc822;y@ma
  ya.st..DATA..

T 192.168.0.1:25 -> x.x.x.x:5000 [AP]
  250 2.1.0 Ok..250 2.1.5 Ok..354 End data with <CR><LF>.<CR><LF>..

T x.x.x.x:5000 -> 192.168.0.1:25 [AP]
  (メール本文略).....QUIT..

T 192.168.0.1:25 -> x.x.x.x:5000 [AP]
  421 4.4.2 mx.maya.st Error: timeout exceeded..
うーん。送信側はちゃんと本文の後 QUIT まで送ってきてるのに、なぜかタイムアウトしちゃってるなぁ。DATA の後で timeout するってのは MTU がおかしくてパケットが通らない、ってのが典型的な例だけど、ちゃんとパケットが届いてるのはキャプチャで確認できてるんだし、MTU が原因ならここ以外のホストとのやりとりでもひっかかりそうだけどそれは起きてないし。送信側はふつーに postfix っぽい。間にあやしげなネットワーク機器が挟まってる可能性もゼロではないけど、キャプチャを見てもあやしいところはなさげなので、その線は薄そう。向こうから QUIT が送られてきてるのは確認できてるので、それを拾って正しく応答を返せていないこちらに問題がありそうな気はするんだけど、原因がよくわからない。同じところから以前メールをもらったときは何ともなかったのに。なんだべこれ。

_ ためしに

smtpd_discard_ehlo_keywords = silent-discard pipelining
という設定で ESMTP PIPELINING を止めるようにしてみたけどこれでどうだろうか、と思ったんだけど、設定を入れてからちっとも再送されてこない。どうやら送信側のキューの滞留期限が切れてバウンスさせちゃったっぽい。このログに気がつくのが遅すぎた…。もう少し調べてみるつもりだけど、ちょっとこれはわからんなぁ。

_ というわけで、うちにメールを送っていただいた方、すみません、受け取れてません。バウンスが返っちゃったと思います。とりあえずつかまえたパケットを解読して内容は承知しました。

\? つづき

_ で、まっとうな手段ではなくパケットキャプチャから解読したメールの内容というのは、 この前の日記へのつっこみでした。要約: 基本正規表現に ? はねーよボケ

_ あれー、そうなんだっけ。基本正規表現に + とか | とかがないのは承知してたけど、? もないんだっけ。で、GNU は 0 or 1回繰り返しを意味するものがないのは不便なんで、勝手拡張として \? というものを導入してしまったらしい。sed -r は基本正規表現ではなく拡張正規表現を使え、--posix は GNU 拡張の \? を無視せよ、となるのでああいう結果になった、と(バージョンによって --posix の動作が変わるのは不可解だが)。

_ ってゆーか、拡張するにしても \? はひどくね? c が特殊記号のとき \c はその意味を打ち消す、というルールがあるのに、GNU の拡張は ? が特殊記号でないのに \? は特殊、という逆転が起きてしまってる。もちろん、GNU 拡張でなくても、基本正規表現では () はただの文字だけど \(\) は特殊記号、みたいな逆転の例がほかににいくつかあるわけだけど、それは規格化されて広く知られたルールなわけで。\? は GNU だけの勝手拡張で、ドキュメントの奥深くを読まないと見つからん場所にひっそりと書かれてるだけ。つーか、あのクソ使いづらい info にだけ書くのやめてくれマジで。ちゃんと man に書いてくれ。

_ だいたい、0 or 1回を表わす正規表現は、わざわざ \? という拡張機能を導入しなくても \{0,1\} という書き方で基本正規表現の範囲内でちゃんと記述できるんだよな。まあこれを実際使うとなると、長ったらしいうぜぇ、と文句言うことになるのは間違いないんだけど。

_ しかし、こんな初歩的なことによく今まで気づかずにいられたなオイ。


2012年5月27日(日)

unbound-1.4.17

_ ということで、うちの unbound を 1.4.17 に入れかえた。ついにラウンドロビンが実装されましたよ。

_ 該当部分のソースを見てみると、うん、 自分が以前書いたパッチとほとんど同じ。そりゃそうだわな。ただ、そのほとんど同じのわずかに違う部分が致命的で、自分のは実用にならないレベルのままずっと放置してたんだな。

_ 前のときも書いたけど、unbound はスレッドで動くから、ラウンドロビンで順番を並べかえるのにスレッドセーフでない random() は使えないんだよな。unbound 自体も query id の生成やソースポートのランダム化のために自前の乱数関数を持ってるけど、応答パケットを組み立てる関数(ラウンドロビン処理を追加する部分)ではその乱数関数の状態変数にアクセスできず、無理に取得できるようにしようとするにはえらくたいへんな修正が必要で、あるいはパフォーマンスの劣化を承知で必要となるたびに毎回乱数を初期化して使う必要があって、でもどちらもやりたくなくて投げ出した。

_ で、わずかに違う部分、すなわち今回のラウンドロビン対応ではどうやって乱数を手に入れるのかというと、こんな感じ。

        /* roundrobin offset. using query id for random number */
        rr_offset = RRSET_ROUNDROBIN?id:0;
その発想はなかった。自前で乱数を生成するのではなく、クライアントが毎回ランダムで送ってくる(はずの) query id をそのまま使わせてもらう、と。クライアントが生成した query id の乱数の質が低いととーぜんランダムの度合いも下がるけど、そもそもラウンドロビン自体やらずに常に固定順になってしまう程度のランダムさでもまったく問題ないようなものなので、この用途では乱数の品質なんてものはまったく気にしなくていいわけだ。しかも自前で乱数を生成するよりずっと修正が少なくスレッドセーフでかつ非常に軽い。柔軟な発想って大事よね。


<< = >>
やまや