CVE-2012-どさにっき 〜2012年4月中旬〜

by やまや
<< = >>

2012年4月14日(土)

BIND + SoftHSM

_ 雨ってイヤね。

_ SoftHSM をインストール。手順略。

_ openssl の pkcs#11 engineをインストール。freebsd なので ports/security/engine_pkcs11 を入れるだけ。テスト。

% openssl engine dynamic -pre SO_PATH:/usr/local/lib/engines/engine_pkcs11.so -pre ID:pkcs11 -pre LIST_ADD:1 -pre LOAD -pre MODULE_PATH:/usr/local/lib/libsofthsm.so -t
(dynamic) Dynamic engine loading support
[Success]: SO_PATH:/usr/local/lib/engines/engine_pkcs11.so
[Success]: ID:pkcs11
[Success]: LIST_ADD:1
[Success]: LOAD
[Success]: MODULE_PATH:/usr/local/lib/libsofthsm.so
Loaded: (pkcs11) pkcs11 engine
     [ available ]

_ openssl の設定ファイルを書く。

# vi /etc/ssl/openssl.cnf
openssl_conf = openssl_def
[openssl_def]
engines = engine_section
[engine_section]
pkcs11 = pkcs11_section
[pkcs11_section]
engine_id = pkcs11
dynamic_path = /usr/local/lib/engines/engine_pkcs11.so
MODULE_PATH = /usr/local/lib/libsofthsm.so
init = 0
もっかいテスト。
% openssl engine pkcs11 -t
(pkcs11) pkcs11 engine
     [ available ]

_ BIND をコンパイル。

% ./configure --with-pkcs11=/usr/local/lib/libsofthsm.so --other-options
% vi bin/pkcs11/Makefile
#LIBS =         -ldl		←コメントアウト
% make

_ SoftHSM 初期化。

# softhsm --init-token 0 --slot 0 --label softhsm
The SO PIN must have a length between 4 and 255 characters.
Enter SO PIN:
The user PIN must have a length between 4 and 255 characters.
Enter user PIN:
The token has been initialized.
今後 PIN を聞かれたら user PIN の方を答える。SO (security officer) PIN の出番はよくわからん。

_ HSM 内に鍵を生成する。

# pkcs11-keygen -l hoge -b 2048
Enter Pin:
HSM 内の鍵のリスト。
# pkcs11-list
Enter Pin:
object[0]: handle 2 class 2 label[4] 'hoge' id[0]
object[1]: handle 1 class 3 label[4] 'hoge' id[0]
鍵を消す。
# pkcs11-destroy -l hoge
label hoge
Enter Pin:
object[0]: class 2 label 'hoge' id[0]
object[1]: class 3 label 'hoge' id[0]
sleeping 5 seconds...			←なぜ5秒寝る?

_ あらためて、dnssec で使えるような鍵を作成する。

# pkcs11-keygen -l ksk -i 1000 -b 2048
Enter Pin:
# pkcs11-keygen -l zsk -i 2000 -b 1024
Enter Pin:
この鍵を dnssec-signzone で使えるようする。
# dnssec-keyfromlabel -l 0:07d0 -f KSK -a RSASHA256 example.com
Kexample.com.+008+52822
# dnssec-keyfromlabel -l 0:03e8 -a RSASHA256 example.com
Kexample.com.+008+31878
dnssec-keyfromlabel の -l で指定するラベルは <slot>:<id> の形式。slot は SoftHSM のスロットで、pkcs11-keygen の -s で指定した値(今回は指定してないのでデフォルトの 0)。id は pkcs11-keygen の -i で指定した10進数を16進数に変換したもの。pkcs11-keygen の -l で指定したラベルをそのまま指定するとエラーになる。2時間悩んだ。こんなのわかるかよ。

_ で、生成された秘密鍵。

# cat Kexample.com.+008+52822.private
Private-key-format: v1.3
Algorithm: 8 (RSASHA256)
Modulus: 4WZPMs2QxB6lOqdDaKu1pPfmv9bFvIq2wuW6SZnYXtMg77xiqd4D2BnZV85NIfQ6yaTc518UwGVE8F1kKaPd0wb/gd1jP8jXCq57IomGV1NjEoQzbSiRLZVRowDB2W62AanFg3PgC5CAcamy0KXlTiPFC5y+tHYvFGRF+plfhXM=
PublicExponent: AQAB
Engine: cGtjczExAA==
Label: MDowN2QwAA==
Created: 20120414044747
Publish: 20120414044747
Activate: 20120414044747
鍵の本体は HSM にあるので、ふつーに dnssec-keygen で生成した場合に比べて情報が少ない。上の Engine、Label の値を base64 デコードするとそれぞれ pkcs11、0:07d0 となり、鍵といってもよーするに HSM へのポインタでしかない。

_ 署名してみる。

# dnssec-signzone -o example.com -k Kexample.com.+008+31878.private example.com Kexample.com.+008+52822.private
dnssec-signzone: fatal: cannot sign zone with non-private dnskey Kexample.com.+008+52822.private
エラーになった。引数は間違ってないはずなんだが。わけわからん。ぐぐってみると、 同じエラーに遭遇した人がいるけど解決してないっぽいな…。

_ dnssec-keyfromlabel ではなく、SoftHSM のコマンドを使って鍵を作ることも可能ではある。

# softhsm --export hoge.pem --slot 0 --id 07d0 --pin hogehoge
# softhsm-keyconv --tobind --in hoge.pem --name example.com. --ksk --algorithm RSASHA256
この場合、生成される鍵は dnssec-keygen で作るのと同じ秘密の情報が含まれたもので、これなら署名も問題なくおこなえる。が、そういうセンシティブな情報を扱いたくないから HSM でブラックボックス化するわけであって、いちいち取り出して使うのなら HSM を使う意味なんてないよね…。それじゃ単なる乱数生成器と変わらん(しかもどうせ /dev/random なので質も速度も向上しない)。

_ ちなみに、以上の手順は ARM の 4.11 節で解説されているやりかたとはまったく異なる。こっちの手順だと OpenSSL にパッチを当ててコンパイルするところから始めるんだけど、それで作った openssl ではなぜか pkcs11 エンジンが有効にならないので先に進めない。

_ そういうわけで、結論。うごきませんでした。


2012年4月15日(日)

BIND + SoftHSM つづき

_ ARM の手順で openssl にパッチを当てたもので pkcs#11 エンジンが有効にならない理由がやっとわかった。平ユーザで openssl から SoftHSM に接続できないかららしい。root でやると通る。たったこれだけのことで半日潰した。 きのうの ARM に従わない方法だとエンジンが有効かどうかの確認は root でなくても可能なんだが。たぶん libsofthsm をリンクするだけで終わりか、その後実際に SoftHSM に接続しにいくかの違いってことだと思う。いや、たしかに SoftHSM が管理してる sqlite DB の権限からして root 以外不可なのは当たり前なんだけどさ、openssl がサポートしてるエンジン種別を確認するだけのコマンドを実行して実際にデバイスにアクセスしにいくとは思わんでしょふつー。

_ ということで、もう一度 ARM の手順にしたがってやってみる。ところで ARM が何のことかわからない、どこにあるかわからないって人はいないよね?

_ openssl にパッチを当てる。

% tar xvf openssl-1.0.1.tar.gz
% tar xvf bind-9.9.0.tar.gz
% cd openssl-1.0.1
% patch -p1 < ../bind-9.9.0/bin/pkcs11/openssl-1.0.0f-patch
% find . -name '*.rej'
./crypto/engine/Makefile.rej
./util/mk1mf.pl.rej
./util/mkdef.pl.rej
./Configure.rej
いくつか失敗してるので手で修正する。どう直せばいいかは見ればすぐわかる。

_ コンパイル。

% ./Configure --prefix=/usr/local/bind99 --pk11-libname=/usr/local/lib/libsofthsm.so --pk11-flavor=sign-only -pthread BSD-x86-elf shared
% make
コケる。crypto/engine/hw_pk11.c の #define SOLARIS_AES_CTR の行をコメントアウトすると通るが、ほんとにこれであってるのかは知らない。完了したらちゃんと pkcs#11 が使えるようになってるかどうか確認する。root で。さらに、SoftHSM の初期化が済んでないとコケると思う、たぶん。
# apps/openssl engine pkcs11 -t
WARNING: can't open config file: /usr/local/bind99/ssl/openssl.cnf
(pkcs11) PKCS #11 engine support (sign only)
いやー、ここまで長かった。というか、確認の方法がまずかっただけで、できたものに問題はなかったということか。うーん。
# make install

_ 上で作った openssl を使うようにして BIND をコンパイル。

% cd ../bind-9.9.0
% env CFLAGS=-DDIG_SIGCHASE=1 ./configure --prefix=/usr/local/bind99 --with-openssl=/usr/local/bind99 --with-pkcs11=/usr/local/lib/libsofthsm.so --enable-threads
% make
FreeBSD では bin/pkcs11/Makefile の LIBS=-ldl の行をコメントアウトしないと途中でコケる。DIG_SIGCHASE はいいかげんデフォルトにしてほしい。

_ HSM 内に鍵を作って、それを bind が扱える形式として取り出す。取り出すといっても、ほんとに秘密の情報は HSM に入ったまま外に出てくることはない。

# bin/pkcs11/pkcs11-keygen -l ksk -b 2048
Enter Pin:
# bin/dnssec/dnssec-keyfromlabel -l ksk -f KSK example.com
Enter PIN: 
Kexample.com.+005+31876
# bin/pkcs11/pkcs11-keygen -l zsk -b 1024
Enter Pin:
# bin/dnssec/dnssec-keyfromlabel -l zsk example.com
Enter PIN: 
Kexample.com.+005+52818
dnssec-keyfromlabel でも pkcs11-keygen で作るときに指定したラベルでちゃんと動いてくれた。きのうみたいに id をこねくりまわさなくて済む。逆に、スロット番号を指定できないので、HSM の slot 0 以外に鍵を作った場合にどうすればいいのかわからんのだが。

_ この鍵で署名してみる。

# bin/dnssec/dnssec-signzone -S -K. -o example.com example.com
Enter PIN: 
Fetching KSK 31876/RSASHA1 from key repository.
Fetching ZSK 52818/RSASHA1 from key repository.
Verifying the zone using the following algorithms: RSASHA1.
Zone signing complete:
Algorithm: RSASHA1: KSKs: 1 active, 0 stand-by, 0 revoked
                    ZSKs: 1 active, 0 stand-by, 0 revoked
example.com.signed
やっと動いたよ。長かったな。

_ ここまででとりあえず用は足りるのだが、鍵が必要になるたびに毎回 PIN を聞かれて手で入力する必要があるので自動化には向かない。ということで、これを省略できるようにする。全自動ゾーン署名(auto-dnssec) をやる場合や、cron で定期署名させる場合はこれをやっておく必要がある。

# vi /usr/local/bind99/ssl/openssl.cnf
openssl_conf = openssl_def
[ openssl_def ]
engines = engine_section
[ engine_section ]
pkcs11 = pkcs11_section
[ pkcs11_section ]
PIN = hogehoge
とーぜん、このファイルを見られると鍵が盗まれるのでそのぶんセキュリティレベルは落ちる。また、これをやっても、PIN 入力が不要になるのは dnssec-* なツールだけで、pkcs11-* なツールは手入力を求められる。こっちはコマンドライン引数で PIN を指定できるのでこれも自動化できないことはないんだが、どっちかに統一してほしいものだ。

_ ちなみにふつーにコンパイルした BIND に LD_LIBRARY_PATH や LD_PRELOAD を使ってパッチを当てた openssl をかぶせてみてもできるんじゃないかと試してみたけどダメだった。理由は追ってないのでわからん。

_ ちうことで、鍵管理に HSM を使って (でも OpenDNSSEC は使わずに) DNSSEC する話でした。SoftHSM じゃなくてちゃんとしたハードウェアの HSM を使う場合もほぼ同様の手順でできるはず。


2012年4月16日(月)

Mac こわれた。

_ トラックパッドが押し込まれたまま戻らなくなった(泣)。常時クリック/ドラッグされてる状態でまともに操作できん。

_ 2009年11月に買った MacBook Pro。もう2年半か。もう十分元は取った気がするけど、今のところとくに不満もないんだよなぁ。修理に出すか買い替えるか。外に持ち出すことはあんまりないのでクラムシェルモードで使うという手もあるにはあるけど、美しくないなぁ。MacBook Air もあるけど、こいつはディスク容量その他いろいろ足りないのでこれ1台で済ませるわけにはいかないし。外付けマウスをつないで内蔵トラックパッドを無効にできればそれでいいんだろうけど、環境設定のメニューを見てもそんな項目はなさげ。マウスをつなぐと新しい設定項目があらわれるとかかしらん。

_ これまで、Windows のノート PC は買い替えた後で自宅サーバになるのが習わしだったんだけど、 amazon でノート PC がクソ安かったので次期自宅サーバ用に買ってしまったばかりなんだよ。きのう届いて Mac が今日壊れるとかタイミング悪すぎるだろ。常識的には、じゃあこいつを代わりに使うか、ということになるんだろうけど、こいつはキーボードが史上最悪レベルで使えないので常用する気にはまったくなれない。テンキーがついてるおかげでメインキーが左側に寄ってるし、何より enter がキーボードの右端中段にないので、手元を見ながらでないとまともに入力できない(注: テンキーつきキーボードは他人の PC を一時的にさわる以外では PC98 以来という人の感想です)。テンキーをつけるならせめてメインキーと間隔を十分に開けてくれ。買うときはサーバとして使う以外のことはまったく考えてなかったからそれでもよかったんだけれど。


2012年4月17日(火)

openssl pkcs11

_ DNSSEC 用に pkcs11 対応でビルドした openssl だけど、せっかくなんで別の用途でも使ってみる。

_ 秘密鍵を SoftHSM の中に作る。SoftHSM 自身に鍵作成用ツールがない(別で作ったものをインポート/エクスポートはできる)ので BIND で遊んだときのもので。

# pkcs11-keygen -b 2048 -l hogehoge
Enter Pin:
# pkcs11-list -l hogehoge
label hogehoge
Enter Pin:
object[0]: handle 24 class 2 label[8] 'hogehoge' id[0]
object[1]: handle 23 class 3 label[8] 'hogehoge' id[0]
この鍵を利用して自己署名な SSL 証明書を作成。
# /usr/local/bind99/bin/openssl req -new -engine pkcs11 -keyform engine -key pkcs11:hogehoge -out hoge.pem -text -x509
engine "pkcs11" set.
Enter PIN:
(略)
Country Name (2 letter code) [AU]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:example.com
Email Address []:
この証明書で SSL なサーバを起動。てっとりばやく openssl 自身をサーバに。
# /usr/local/bind99/bin/openssl s_server -cert hoge.pem -engine pkcs11 -keyform engine -key pkcs11:hogehoge
engine "pkcs11" set.
Enter PIN:
Using default temp DH parameters
Using default temp ECDH parameters
ACCEPT
パラメータをとくに指定してないのでデフォルトの *:4433 で listen してる。この s_server に接続してみる。
% openssl s_client -connect localhost:4433
CONNECTED(00000003)
depth=0 /C=AU/ST=Some-State/O=Internet Widgits Pty Ltd/CN=example.com
verify error:num=18:self signed certificate
verify return:1
depth=0 /C=AU/ST=Some-State/O=Internet Widgits Pty Ltd/CN=example.com
verify return:1
---
Certificate chain
 0 s:/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd/CN=example.com
   i:/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd/CN=example.com
---
Server certificate
-----BEGIN CERTIFICATE-----
(略)
-----END CERTIFICATE-----
subject=/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd/CN=example.com
issuer=/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd/CN=example.com
---
No client certificate CA names sent
---
SSL handshake has read 1473 bytes and written 270 bytes
---
New, TLSv1/SSLv3, Cipher is DHE-RSA-AES256-SHA
Server public key is 2048 bit
Compression: NONE
Expansion: NONE
SSL-Session:
    Protocol  : TLSv1
    Cipher    : DHE-RSA-AES256-SHA
    Session-ID: 7B290D455029C51A0011022335AB3252A97B1A76E014F22F7C5552DFC664BC1B
    Session-ID-ctx:
    Master-Key: B229C8F269FA27FC79A67919472195E251DF3691DA092616BEA5CE1F8A06AD55D8BA5F00AAF69B6B3B03256BEA37CE10
    Key-Arg   : None
    Start Time: 1334629113
    Timeout   : 300 (sec)
    Verify return code: 18 (self signed certificate)
ということで、example.com な自己署名なサーバに SSL でつながった。

_ ふつーの SSL と違いがわかりにくいんだけど、何が違うかというと SSL の秘密鍵がファイルではなくセキュリティデバイスの中に入ってるということ。鍵を盗むが困難になるのでセキュリティレベルが上がる。

_ Apache でやる場合は

SSLCryptoDevice pkcs11
SSLCertificateKeyFile pkcs11:hogehoge
って設定すればいいのかしら。んー、でもドキュメントを読むかぎり、SSLCryptoDevice は SSL アクセラレータとして使うためのものであって、秘密鍵の保管場所として使うことは意図してなさげな感じがする。SoftHSM がアクセラレータとして使えるかどうかは知らんが、仮に使えたとして遅くなることはあっても速くなることはありえない。SSLCertificateKeyFile にファイル以外のものを指定できるかどうかも怪しいし。試してないけど、もしこれで動いてしまったらラッキー、てな程度かな。postfix はドキュメントを見ても外部エンジンを使う設定項目がなさげなのでたぶん使えないと思う。


2012年4月18日(水)

Apache CVE-2012-0883

_ 今回リリースされたのは 2.4.2だけだけど、LD_LIBRARY_PATH にカレントディレクトリが含まれてしまうという 静寂性脆弱性は 2.2 にも存在してる。それにしても起動スクリプトの脆弱性とはずいぶん珍しい。しかもわし的にかなり懐しいバグだ。

_ ずっと前、今の会社に移る前の会社での仕事で、外注してたスクリプトを見たら先頭がこんな感じになってた。

#!/bin/sh

PATH1=/bin:/usr/bin
PATH2=/sbin:/usr/sbin
PATH3=/opt/hoge/bin
PATH4=
PATH5=

PATH=$PATH1:$PATH2:$PATH3:$PATH4:$PATH5:$PATH6:$PATH7:$PATH8:$PATH9
単に変数に値をつっこんでるだけに見えるけど、実はこれ、セキュリティホールが存在する。外注先にすぐ直させた。

_ 上のスクリプトは : を区切りとして空文字列を連結してしまって、最終的に PATH=/bin:/usr/bin:/sbin:/usr/sbin:/opt/hoge/bin:::::: になる。で、man sh してみるとこう書いてある。これは FreeBSD の ash の man だけど、それ以外の sh でも同様のことが書いてあるはず。

   Path Search
     When locating a command, the shell first looks to see if it has a shell
     function by that name.  Then it looks for a built-in command by that
     name.  If a built-in command is not found, one of two things happen:

     1.   Command names containing a slash are simply executed without per-
          forming any searches.

     2.   The shell searches each entry in the PATH environment variable in
          turn for the command.  The value of the PATH variable should be a
          series of entries separated by colons.  Each entry consists of a
          directory name.  The current directory may be indicated implicitly
          by an empty directory name, or explicitly by a single period.
最後の行が重要。「$PATH で空ディレクトリはカレントディレクトリとみなす」。つまり、上の PATH 設定の状態で
#!/bin/sh
rm -rf /
なんてのを /tmp/sl とかの名前で置いておくと、/tmp にいるときに ls と間違えて sl を実行したときに rm -rf / が実行されてシステムが破壊されてしまう。PATH にカレントディレクトリを入れてはいけないし、カレントディレクトリと同義である空文字列も入れてはいけない。個人的には空文字列をカレントディレクトリとみなすのは仕様バグだと思ってる。

_ で、今日初めて知ったんだけど、PATH だけでなくて LD_LIBRARY_PATH も同様に空文字列をカレントディレクトリとみなす仕様だったのね。man ld.so しても書いてないみたいだけど。今回の apache の脆弱性はまさにこれ。起動スクリプトで LD_LIBARARY_PATH="/usr/local/apache/lib:$LD_LIBARARY_PATH" としてしまってるので、LD_LIBRARY_PATH が元々設定されていない場合は LD_LIBARARY_PATH に新たに空ディレクトリ(=カレントディレクトリ)を追加してしまって意図せぬライブラリをリンクしてしまう危険を抱えこんでいる。ちなみに関係ないが MANPATH はそういう動作はしないみたいだった。

_ 修正版はまだ出てないけど同じバグは 2.2.x にもあって、かつ起動スクリプトの修正だけで再コンパイル不要なんで、修正版を待たずにとっとと入れ替えておくとべきかと。対象は apachectl から読まれる envvars というファイル。ふつーにインストールした場合は apachectl と同じディレクトリにあるはず。このファイルの

LD_LIBARARY_PATH="/path/to/apache/lib/:$LD_LIBARARY_PATH"
になってるところを
if test "x$LD_LIBRARY_PATH" != "x" ; then
  LD_LIBRARY_PATH="/path/to/apache/lib:$LD_LIBRARY_PATH"
else
  LD_LIBRARY_PATH="/path/to/apache/lib"
fi
に直せばおしまい。ちなみにこの envvars というファイルは設定ファイルの一種という扱いなので、既存の apache が存在してるところに新しいバージョンを上書きインストールしても、古いバージョンのものが優先されて更新されない。つまり、 上書き再インストールではこの脆弱性は修正できない。必ず手で書き換える必要がある。修正版リリースを待たなくていいというのはこれが理由。まあ、リモートから攻撃できる穴ではないので環境によってはそれほど急ぐ必要もないかもしれんが。

_ なお、redhat や centos では envvars がやってる環境変数の設定のようなお仕事は /etc/sysconfig/ 以下のファイルでおこなうのが流儀なので、RPM では envvars なんてものはそもそも使わないようにパッチが当たっている。なのでこの穴は存在せず、修正も不要。


2012年4月19日(木)

ネトゲの IPv6 化

_ ネトゲ視点からの v6 fallback 問題の記事→ 格ゲーもFPSもカックカクになっちゃうの?

_ …えーと。サーバ側が v4 のみならまったく関係ないですよ?

_ 素朴な疑問なんですが、今度の World IPv6 Launch から v6 化する予定、あるいはそれを待たずにすでに v6 化しているゲーム屋さんってあるんでしょうか? あったら教えてほしい。そんなのあるわけねーだろ、という反語ではなく、純粋な疑問。もし対応したところがあるのならば話を聞いてみたい。

_ ところで、ネトゲやらんから知らんのですが、こういうのって L4 は TCP なんでしょうか UDP なんでしょうか。v6 → v4 へのフォールバックを短縮するための NTT 側の処理(TCP RST)はその文字からもわかるように TCP にしか効かないので、もし UDP だとすると v6 化されるとフレッツ網からは「初回の接続」がかなり待たされるだろうなー、と。ICMPv6 で dst unreach を返してはくれないのかしら。

_ あくまで、初回の接続だけ。まともな実装ならばサーバに接続に行くたびに v6 → v4 へのフォールバックをするとは思えんので、初回の接続で v6 がダメと判断されれば以後は v4 でのみ通信するでしょう。なので、ゲーム開始直後の最初の接続は時間がかかったとしても、その後のゲーム進行に問題は起きない、つまりカックカクにはならないんじゃないかと。タコな実装ならば毎回フォールバックすることもありえなくもないけど、ディレイを気にするようなネトゲならそのへんはちゃんとしてるんじゃないかな。これは TCP も UDP も同じ。

_ ゲーム屋さんが W6L を契機に v6 化しないとしても、いつまでも v4 のみってわけにもいかないだろうから、いずれこの問題に直面することになるのは避けられない。でもゲームなんて特定のクライアントプログラムから特定のホストにしか接続しないわけで、組み合わせなんてたかがしれてる。ぶっちゃけ、起動時に v4 と v6 で接続テストして回線品質のよさそうな方を使うように実装するだけだよね。対応はほかのアプリケーションよりもずっと楽そうに思える。

_ ゲーム業界のことはほとんど知らんので的はずれなことを書いてたらごめんなさい。


<< = >>
やまや