POPFile 1.1.3 for Mac OS X 10.11 (El Capitan)

追記

POPFile のサーバが復旧しましたので、ダウンロードページより入手をお願いします。

もとの書きこみ

とりあえず作成したのでリンク(POPFile のサーバが復旧してますのでそちらにおきました)しておきます。ここしばらく POPFile のサーバ (getpopfile.org) がダウンしているので、とりいそぎ Dropbox にて共有。
内容は Yosemite 版と変わらず。インストール時のバージョンチェックを変更して El Capitan でもインストールできるようにしただけ。Yosemite からのアップグレードの場合は特になにもしなくてもそのまま動きます。

追記 4

NSS プロジェクトのページ から変更をたどると、
https://hg.mozilla.org/projects/nss/rev/d936c1e1c51e
で変更されていた。この変更は NSS_3_16_3_PLUS_BRANCH のブランチにおけるもの。この変更が mozilla-release にも反映されているようだ。
変更内容の詳細は NSIS 3.16.3 のリリースノート が詳しい。1024 ビットのルート証明書について、Web サイトの認証目的から外したということのようだ。
「nss」を指定したときにダウンロードされるものは、
https://hg.mozilla.org/projects/nss/file/a163e09dc4d5/lib/ckfw/builtins/certdata.txt
のファイルで、こちらは NSS_3_16_2_BRANCH のもの。最終のコミットを比較すると、NSS_3_16_2_BRANCH の certdata.txt は 2014-04-01 14:07 +0200、mozilla-release のものは 2014-08-06 20:22 +0200 なので、release の方が新しい。
新しいものを採用するという考え方に立てば、やはり release を使うべきということになるのか。しかし、上記の NSS プロジェクトのページを見ると、3.16.2 のブランチも更新があり、メンテナンスされているようなのでとりあえずはこれでいけそうかな。
「tip」って何なのか気になって調べてみたら、Mercurial の FAQ にいきあたった。最後に更新された changeset を指すってことか。現在は、3.16.2 のブランチの方が最後に更新されているのでそっちを見てるってことね。そうすると、3.16.3 のブランチの方に更新が入ると同じ問題が起こるってことか。んー、やはり一時的な対応にしかならないか...。

追記 2

ふと、mk-ca-bundle.pl がダウンロードしてくる certdata.txt にはいくつか種類があったなと思いだし、-d nss を指定した際に参照されるファイル を見てみたところ、上記の無印のが、SERVER_AUTH TRASTED_DELEGATOR になってることを発見。
nss と release の違いがわからないが、比較すると十数箇所違いがあった。nss と release のどちらを使うべきなのか不明だが、とりあえず nss の方を使えばエラーは回避できた。
いったんは、これか。

追記

OpenSSL でも試した。

$ openssl s_client -CAfile ca-bundle.crt -connect imap.mail.me.com:993 -showcerts
CONNECTED(00000003)
depth=2 /C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 2006 VeriSign, Inc. - For authorized use only/CN=VeriSign Class 3 Public Primary Certification Authority - G5
verify error:num=20:unable to get local issuer certificate
verify return:0
$ openssl s_client -CAfile email.crt -connect imap.mail.me.com:993 -showcerts
CONNECTED(00000003)
depth=3 /C=US/O=VeriSign, Inc./OU=Class 3 Public Primary Certification Authority
verify return:1
depth=2 /C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 2006 VeriSign, Inc. - For authorized use only/CN=VeriSign Class 3 Public Primary Certification Authority - G5
verify return:1
depth=1 /C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=Terms of use at https://www.verisign.com/rpa (c)10/CN=VeriSign Class 3 Secure Server CA - G3
verify return:1
depth=0 /C=US/ST=California/L=Cupertino/O=Apple Inc./CN=*.mail.me.com
verify return:1

となり、OpenSSL でも同じ状態なのがわかった。ここで表示されている G5 は、G5(-2021) のほうみたい。ca-bundle.crt には G3、G5(2036) しか入ってないので検証ができず、email.crt には G3、G5(2036)、無印が入っており、G5(2021) はないが、無印のがあるので検証できたったことなのかな。
そうすると、OpenSSL 自体の問題ってこと? あるいはサーバ側の問題?

続 IO::Socket::SSL で証明書の検証をする

この間の日記の続き。
コメントに貴重な情報をいただいたので、少し調べてみた。まだうまくいっていないので経過報告といったところだが、ひとつ変なことに気がついたので、記しておく。

検証のために書いたコードは Gist に置いた。まず、imap.mail.me.com の証明書を検証するために必要な、「Verisign Class 3 Public Primary Certification Authority - G3」と「VeriSign Class 3 Public Primary Certification Authority - G5」の証明書だけを入れたファイルを使い、検証をさせてみた。$Net::SSLeay::trace = 3 にしても、検証過程が読みとれなかったので、コールバックを使って経過を確認してみた。
すると、最初に呼ばれた際の Issuer (正確には Issuer と Subject をつないだもののようで、二つめの「/C=US」以降は Subject と思われる)が

/C=US/O=VeriSign, Inc./OU=Class 3 Public Primary Certification Authority/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 2006 VeriSign, Inc. - For authorized use only/CN=VeriSign Class 3 Public Primary Certification Authority - G5

となっている。Issuer の OU は、「VeriSign Class 3 Public Primary Certification Authority - G5」になるべきところではないのか。なぜか「Class 3 Public Primary Certification Authority」になっている。これはおかしい(たぶん)。
エラー番号の「20」は、openssl/x509_vfy.h を見ると、「X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY」のことらしい。Issuer の証明書が見つからなかった、ってことかな。
では、ca-bundle_tiny.crt に入っている証明書がおかしいのかと思い、G5 の部分だけを抜きだしたファイルを作成し、

openssl x509 -in 05_verisign_class3_g5.crt -text

で内容を表示させてみると、

Issuer: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=(c) 2006 VeriSign, Inc. - For authorized use only, CN=VeriSign Class 3 Public Primary Certification Authority - G5

Subject: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=(c) 2006 VeriSign, Inc. - For authorized use only, CN=VeriSign Class 3 Public Primary Certification Authority - G5

と、こちらは正常っぽい? 元になった certdata.txt でも同じなので、たぶんこれは問題なさそう?

ということで、なんだか Net::SSLeay (あるいはリンクされてる OpenSSL) のバグっぽい感じもするのだけど、そもそも「Class 3 Public Primary Certification Authority」がどこからきたもの? ってのが謎。imap.gmail.com では、「Google Internet Authority G2」→「GeoTrust Global CA」→「Equifax Secure Certificate Authority」の順できれいにつながってる感じなのに。
Mac OS X だけの問題かと思って Windows 上の ActivePerl で試してみたが、同じ症状だった。IO::Socket::SSL を使わず、Net::SSLeay だけで検証させてみても同じ結果。余計にわからなくなってしまった...。

IO::Socket::SSL で証明書の検証をする

久々に更新。
Twitter でぼちぼち書いてたけど、まとめておこうかなと。

IO::Socket::SSL には、接続先の証明書を検証する機能があるが、検索してもあまりその使い方が出てこない。いつかのバージョンで、「SSL_verify_mode」を設定せずに使おうとすると警告が出るようになったため、「とりあえず SSL_VERIFY_NONE に設定したら動くよ」っていう情報があちこち見つかるのだが、「SSL_VERIFY_PEER」を使うのが推奨されているにもかかわらず、そうした場合に証明書をどうやって検証するかっていう情報がなぜかあまり出てこない。
証明書の検証をするためには、SSL_ca_file あるいは SSL_ca_path で CA の証明書のファイルが入っている場所を指定すればよい(ドキュメント)ということなのだが、どこにどういう形式で保存されているかは OS によって異なる。また、Web ブラウザとかもそれぞれ持っていたりする。Perl で使うのだから、OS による違いを気にしなくてよいような方法を採りたいところ。
探してみると、Mozilla::CA というモジュールを見つけた。モジュールには Mozilla が使用している CA の証明書のファイルを変換したものが含まれておりそれを、IO::Socket::SSL の SSL_ca_file に指定できる(Mozilla::CA::SSL_ca_file() で)ようになっている。これならば OS 間の違いを気にすることなく動くではないか。すばらしい。モジュール自体のバージョンはちょっと古いが、証明書のファイルを更新するためのスクリプトも含まれており、モジュールのバージョンアップが止まっても更新していくことができそうだ。
と、一旦はうまい方法を見つけたと思ったのだが、Mozilla::CA のバグ情報に気になる情報を発見。Mozilla::CA に含まれている更新用のスクリプトが古く、参照しているファイルがずっと更新されていないというではないか。
どうも、この更新用スクリプトは curl に含まれているもののようで、curl の最新バージョンから持ってきたものを使えば新しいファイルを入手することができるらしい。ということで試してみると、ちゃんと更新され、うまくいった、かに見えた。というか、最初に試した imap.gmail.com に対しては問題なく動いた。
これで一安心かと思ったが、imap.mail.me.com で試すと、エラー。原因はわからなかったが、IO::Socket::SSL のバージョンを落とすとエラーが起らなくなったためモジュールが悪いのかと考えた。いくつかのバージョンを試していくと、1.974 までは大丈夫で、 1.975 からエラーが起こることがわかった。
1.974 と 1.975 の違いを見ると、Mac OS X だけのために修正された部分を見つけた。そこに示されたリンクを辿ると、Apple が OpenSSL に独自のパッチをあてており、指定された証明書ファイルで検証ができないばあいに、OS が持っているものを使って検証するようになっている、ということらしい(たぶん)。で、1.974 まではその機能が有効になっていたが、1.975 ではそれが働かないようになった、ということのようだ。要は、バージョンが変わったことが原因ではなく、新しいバージョンの mk-ca-bundle.pl で作成したファイルでは、証明書の検証ができていなかったのだ。
その後、OpenSSL とともにインストールされたと思われる「/usr/local/etc/openssl/cert.pem」を指定した場合には問題ないことがわかり、さらに、Mozilla::CA に最初から入っていたものでも問題ないことがわかった。そこから先がなかなかわからなかったのだが、本日、mk-ca-bundle.pl を読んでいたところ、「-p」オプションで証明書の利用目的とレベルの指定ができることがわかった。試しに、すべてが含まれる、「-p ALL:ALL」を指定すると、問題なく動いた。いろいろとオプションを試していくと、「-p EMAIL_PROTECTION:TRUSTED_DELEGATOR」の指定でエラーなく検証できることを発見。デフォルトの「SERVER_AUTH:TRUSTED_DELEGATOR」ではだめなのが納得いかないが、まあ、とりあえず。
では、どの部分が原因だったのか。「openssl s_client -showcerts -connect imap.mail.me.com:993」を実行してみると、「*.mail.me.com」の issuer は「VeriSign Class 3 Secure Server CA - G3」となっており、そこから、「VeriSign Class 3 Public Primary Certification Authority - G5」、「Class 3 Public Primary Certification Authority」という順で検証されているらしい。ここで、mk-ca-bundle.pl のデフォルトで作ったファイルを見ると、「G3」と「G5」はあるが、素のものはない。その元になっている「certdata.txt」を見ると、

CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST

となっている。EMAIL_PROTECTION については TRUSTED_DELEGATOR だが、SERVER_AUTH では MUST_VERIFY_TRUST だそうだ。んで、MUST_VERIFY_TRUST ってのはどういう意味よ、と検索してみたのだが、結局よくわからなかった。信頼してもよいかどうか検証しろよってことなのかしら?
なんだか釈然としないが、現状では上に書いたように EMAIL_PROTECTION の方を使えばうまくいくので他の方法が見つかるまではこれでいくしかないのかな。というか、Perl で書かれていてマルチプラットフォームで動くものならみんな同じ条件のはずだけど、全然情報が見つからないのはなぜなんだろう。証明書の検証なんて別に、、、ってことなの? 「IO::Socket::SSL VERIFY_PEER」でぐぐると、トップは CPAN の IO::Socket::SSL のページで、次が私が POPFile のフォーラムに書いた書き込みなんだよね。。。
他のモジュールではどうしているんだろうと、LWP を見てみたら、LWP::Protocol::https にその処理が入っているらしい。内容はというと、SSL_ca_file または SSL_ca_path オプションが指定された場合はそれを、指定がないばあいは Mozilla::CA を使うというもの。Mozilla::CA に含まれているファイルが古いだなんてことはまったく気にされていないよう。んー、それでいいのか?? まあ、それがいやな人は SSL_ca_file とかのオプションを使えばってことなのかね。


ということで、Perl で IO::Socket::SSL を使っていて、マルチプラットフォームで動く証明書の検証方法のベストプラクティスを誰か教えてください。。。