2014-03-06 (Thu) [長年日記]

_ [sec][obsd]コーディングスタイルと TLS とエラーと私

http://www.tedunangst.com/flak/post/thoughts-on-style-the-TLS-and-errors より。http://pastebin.com/DGpC8e6n が見づらいので転載。

やりたいわけではなかったのですが、さいきん公開された GnuTLS の脆弱性のせいで、なんだか、すり減ったタイヤに溝を付け直すようなことになっています。忙しい一週間でした。今回はマジで。

おさらい

一週間という言葉は広く捉えていただくとして、以下はすべて必読の記事です。

Apple Secure Transport が secure じゃない件。 #gotofail

OS X で Secure Transport を使わないあなたには、Apple OpenSSL 衝撃の事実があります。親切も過ぎたるは及ばざるが如しと言ったところでしょうか。(訳注: TEA を無効にしないと、trusted CA を制限できなかったり、callback がエラーにならなくなったりと独自のおせっかい動作をする)

GnuTLS がバグを“goto fail;”で直した件。(訳注: 返り値のためにgotoを二重にしたパッチ)

皆さんご存じのとおり、トリプル握手は有害と思われ。そこにはこんな一文も: 「主要な TLS 実装はどれも素数判定をしていない。」 (訳注: TLS のリジュームと再ネゴの両方が可能な善良サーバに対して、同じクライアント証明書を受け取ることのある悪サーバが MITM できる。DH の場合、pre-master secret を制御するために素数どころか偶数を使う exploit が可能で、たいていバレない)

技術的に新しい話題ではありませんが、投稿が新しいので OpenSSL の AES-GCM が ARM では定数時間じゃない件 と、その末尾の各種バグも。

断固エラー処理

以前 login_pushover の作業をしていたとき BSD auth で気づいたことがあります。それは積極的保証が必要ということ。正しく終了するだけでは不十分で、ちゃんと返答チャネルに“authorize”という文字列を書き込まなければいけないのです。もし何かのバグでうっかり exit(0) しちゃったときも、出力がなければ失敗ということにするためです。ステータス文字列と返り値で、いわば自前の二要素認証です。

これをコーディングスタイルとは呼びたくありません; むしろコーディングパラダイムと呼ぶべきでしょう。そしてこれこそ TLS が、内部実装からライブラリ API, そしてプロトコルに至るまですべての段階において固守すべき、なのに固守していないものです。断固エラーにすべき場面で「良い人」になってしまうのは、あるあるですね。

エラーの浸透

実例をもうひとつ。ちょうど signify でチェックサム検証するよう機能追加したんですが、これってかなり the unix way に反しています。コマンドをパイプラインで繋げればいいんですから。じっさい man page も最初はそう勧めていました。

$ signify -V -e -x SHA256.sig -m - | sha256 -C - bsd.rd
(SHA256) bsd.rd: OK

大丈夫そうですね。でも何か問題が起きたらどうでしょうか?

$ (echo catastrophic failure; false) | sha256 -C - bsd.rd
$ echo $?
0

無言、そして正常に終了です。

ここにおいて、ふたつ別個の問題点が露呈しています。第一に、sha256 がおめでたい奴で、ゴミでも喜んで受け取り、エラーにすべきところでエラーを出さないということです。(これは我々自身の、しょーもないミスです。OpenBSD で、-c が厳しすぎると言って -C を追加したんです。[訳注: bin/md5/md5.c rev 1.70]) しかし第二は、sh がパイプラインから返り値を取れなくしているということです。エラーがあるのに、なくなっている。一言で言えば、これは Apple の OpenSSL バグそのものです。

規約

必然的に、議論はこの種のバグを防ぐコーディング規格へと向かいます。おそらく最も有名な工業規格は MISRA C ですが、私は好きじゃありません。

  1. 三項演算子の禁止。ええ、すばらしいルールです。けど最近いつ、三項演算子を使ったせいでセキュリティ問題が起きたという話を聞きました?
  2. まぎらわしい変数名の禁止。tls と t1s は同じに見えるので規格違反ですよ、と。ええ、すばらしいルールです。でもこんなこと、誰がやるんです? OCC (obfuscated code contest) 以外では誰も見たことありませんよ。見たことがあるのは inputLen と outputLen をごっちゃにする人たちです。太陽のように明らかな変数名だというのに。
  3. ソースファイルは改行で終わらねばならない。ファッ!? 歴史上どこを見ても、C ファイルの最後に改行を足すパッチで修正されたセキュリティバグなんてありませんが。

お役所仕事っていうのはこういうものです。「C ファイルはすべて改行で終わっていますか?」ええ、たぶん。「でもどうしてそう言えるんです? 遵守のための手順は? その手順をテストするためにいつ改行のないファイルをチェックインしましたか?」

まあ、これほど常軌を逸しているとは言いませんが、はっきり言って、if に必ず {} を付けるルールも近いところ行ってると思います。それはなぜか。コンピューティングにおいてムーアの法則に従っていないリソースがひとつあるとすれば、それは垂直解像度だからです。使える行数に余裕なんてありません。

Go でプログラミングするときには私も要求どおりの括弧スタイルを使いますよ。彼らは C のひねくれたところを減らしたいのですね、分かります。でも私、気づいてしまったんです。Go 設計者は自分たちのやったことを後から理解したんですよきっと。だってこんなスタイルを唱道して行数を縮めさせているんですから。

if rv, err := foo(bar); err != nil {
    return err;
}

私はまだ慣れなくて、これがどうもゴミの詰め合わせに見えるんですよね。

いっさい goto を使うべきでないと主張し戦う人たちがいます。関数は return をひとつしか持つべきでないという人たちもいます。私の心からの願いは、このふたつの団体が一緒にならないことです。そんなことになったら、彼らのコードはどれほど複雑に絡み合った冗長な条件分岐のかたまりと化すことでしょう……。

base rate fallacy (基準率の誤謬 [訳注: ある病気 D は、ホモが三倍かかりやすい 病気だとする。ホモに占める D 病患者の割合が、ヘテロに占める D 病の割合の三倍ということだ。Pat という人が D 病と診断されたとすると、Pat がホモである確率はいくらだろうか? 75% と答えるのがこの誤謬。実際にはパラメータが足りない。ホモが人口の 1 割、そのうち 3 割が D 病だとすると Pat がホモである確率はわずか 25% になる]) に注意するようお勧めします。括弧のない if を使っているコードが何億行あるでしょうか? もっと重要なこととして、そのコードへ無思慮に括弧を突っ込んだらバグがいくつ発生するでしょうか? (そうです、こういうコードはあまりに多く存在しており、括弧を追加するとすれば無思慮にやるしかないのです。)

さて、「新米プログラマがヘマをしやすいのは if/goto と strcpy のどっち?」と訊かれたなら、私は迷わず strcpy に賭けます。しかし GNU Linux のプログラマは全員「真のプログラマというのはミスしないものだ」と言って、strlcpy の恩恵なしで苦行を続けています。陰謀論がお好きなら、ここ掘れワンワンですよ。

陰謀論

私が陰謀論を信じていたら、「GnuTLS プロジェクトは

+    goto fail;

などというパッチで皮肉な笑いを取ろうと企んでいるんだよ! (Ω ΩΩ<な、なんだってー)」と言うところです。

問題はどこですか。突如、ありあまるほど明白になったのは、だれも TLS スタックをテストしていないということ、そしてそれが、ちゃんとしたテストスイートをだれも持っていないからだということです。こ れ は あ や し い。でもいつから自分の使っている TLS スタックはよくテストされていると錯覚していました? それに関して自分で、情強市民であるあなたは、何をしてきましたか? 今週末 Futurama: Into the Wild Green Yonder を観ましたが、こう言っていましたよ。「スローガンを連呼する道もあるし、行動を起こす道もある!」「前はどうしたんだったっけ?」


«前の日記(2014-02-23 (Sun)) 最新 次の日記(2014-04-13 (Sun))»