おっと wanderlust と gmail の悪口はそこまでだ

久しぶりに wanderlustgmail を読もうとしたら、

Cannot open: elmo-network-initialize-session

なんてエラーが表示され、メールが取得できなくなってしまっていた。

色々調べる内に、原因と対策が判明したので、以下に記録する。

以下のページがとても役に立った。感謝します。

[riwablo: さくらインターネットimapとopensslとwl]
http://riwablo.blogspot.com/2008/04/imapopensslwl.html

原因

imap.gmail.comSSL/TLS で接続する際、
imap.gmail.comサーバ証明書の検証に失敗するため、
wanderlust (elmo) がセッション確立の途中でエラーを吐いて終了する。

サーバ証明書の検証に失敗してしまうのは、
証明書の発行者 (issuer) である "Equifax Secure Certificate Agency" のルート証明書が、
openssl から利用できていないため。

対策(1) (推奨)

openssl 用証明書ストア (ディレクトリ) の準備

openssl が証明書を読みにいくディレクトリを用意する。

bash-3.2$ mkdir -m 744 -p ~/.w3/certs
bash-3.2$
ルート証明書の取得

openssl s_client で imap.gmail.com:993 へ接続してみると、
imap.gmail.comサーバ証明書は Equifax Secure Certificate Agency によって
発行されていることが分かる。

bash-3.2$ openssl s_client -host imap.gmail.com -port 993
CONNECTED(00000006)
depth=1 /C=US/O=Google Inc/CN=Google Internet Authority
verify error:num=20:unable to get local issuer certificate
verify return:0
---
Certificate chain
0 s:/C=US/ST=California/L=Mountain View/O=Google Inc/CN=imap.gmail.com
i:/C=US/O=Google Inc/CN=Google Internet Authority
1 s:/C=US/O=Google Inc/CN=Google Internet Authority
i:/C=US/O=Equifax/OU=Equifax Secure Certificate Authority
(以下略)

Equifax Secure Certificate Agency のルート証明書は、
firefox の証明書ストアなどに入っているので、そこから取得する。

環境設定→詳細→暗号化→証明書を表示...
で表示される証明書のリストから
"Equifax" の "Equifax Secure CA" をクリックし、
"書き出す..." ボタンをクリック。

ファイル形式は PEM を選択し、
上で作った ~/.w3/certs ディレクトリへ保存する。

シンボリックリンクの生成

openssl が適切に証明書を選択できるように、
subject のハッシュ値に ".0" を付加したシンボリックリンクを作る。
といってもやるのはコマンド c_rehash を実行するだけ。

bash-3.2$ c_rehash ~/.w3/certs
bash-3.2$

これで ~/.w3/certs 内に証明書へのシンボリックリンクが作られる。

bash-3.2$ ls -l ~/.w3/certs
total 16
lrwxr-xr-x  1 yamdan  staff    19  4  7 00:09 594f1775.0 -> EquifaxSecureCA.pem
-rw-r--r--@ 1 yamdan  staff  1162  4  7 00:06 EquifaxSecureCA.pem
.wl の編集

.wl へ以下を追記する。

;; SSL/TLS 用証明書ストアのパス
(setq ssl-certificate-directory "/Users/yamdan/.w3/certs")

ここでは必ず絶対パスを指定すること。
"~/.w3/certs" のような指定をするとディレクトリの読み込みに失敗する。

(ssl.el の内部では、openssl の実行に
start-process-shell-command ではなく start-process を利用しており、
シェルによる引数の展開 "~/.w3/certs" --> "/Users/yamdan/.w3/certs"
が行われないため)

完了

以上で imap.gmail.com への接続に成功するはず。
.wl は最終的に以下のようになる。

;; IMAP サーバの設定
(setq elmo-imap4-default-server "imap.gmail.com")
(setq elmo-imap4-default-user "xxxxxxxxxxxx@gmail.com")
(setq elmo-imap4-default-authenticate-type 'clear)
(setq elmo-imap4-default-port '993)
(setq elmo-imap4-default-stream-type 'ssl)
(setq elmo-imap4-use-modified-utf7 t)

;; SMTP サーバの設定
(setq wl-smtp-connection-type 'starttls)
(setq wl-smtp-posting-port 587)
(setq wl-smtp-authenticate-type "plain")
(setq wl-smtp-posting-user "xxxxxxxxxxxxx")
(setq wl-smtp-posting-server "smtp.gmail.com")

;; SSL/TLS 用証明書ストアのパス
(setq ssl-certificate-directory "/Users/yamdan/.w3/certs")

対策(2) (非推奨)

上の手順の代わりに、

(setq ssl-certificate-verification-policy 1)

のように、ssl-certificate-verification-policy の値に 0 以外を与えても、エラーが抑制される。
証明書の検証に失敗しているのには変わらないはずなんだが、
この挙動の違いはなんなのだろう?

ssl.el の中を見ると、ssl-certificate-verification-policy の値は、
openssl s_client の -verify オプションの値として利用されるようだ。

そしてどうも openssl s_client は
「-verify 0 オプションを付加したときだけ」
証明書検証失敗時の出力が変化するようだ。

bash-3.2$ openssl s_client -host imap.gmail.com -port 993 -verify 0 -CApath ~/.w3/certs
verify depth is 0
CONNECTED(00000003)
depth=1 /C=US/O=Google Inc/CN=Google Internet Authority
verify error:num=20:unable to get local issuer certificate
verify return:0
49965:error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed:s3_clnt.c:983:

-verify 0 オプションを付加したときだけは、
openssl s_client は error を吐いて途中終了する。
この error が ssl.el 経由で elmo へ伝わり、

Cannot open: elmo-network-initialize-session

のエラー出力へ繋がっていたようだ。

一方、-verify オプションを付加しない場合や、付加しても 0 以外の値を与えた場合には、
証明書検証に失敗しようが問答無用で、セッションの確立までは進んでしまう。

bash-3.2$ openssl s_client -host imap.gmail.com -port 993 -verify 1 -CApath ~/.w3/certs
verify depth is 1
CONNECTED(00000003)
depth=1 /C=US/O=Google Inc/CN=Google Internet Authority
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=1 /C=US/O=Google Inc/CN=Google Internet Authority
verify error:num=27:certificate not trusted
verify return:1
depth=0 /C=US/ST=California/L=Mountain View/O=Google Inc/CN=imap.gmail.com
verify return:1
---
Certificate chain
 0 s:/C=US/ST=California/L=Mountain View/O=Google Inc/CN=imap.gmail.com
   i:/C=US/O=Google Inc/CN=Google Internet Authority
 1 s:/C=US/O=Google Inc/CN=Google Internet Authority
   i:/C=US/O=Equifax/OU=Equifax Secure Certificate Authority
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIDLzCCApigAwIBAgIKKNVg8QAAAAAHfzANBgkqhkiG9w0BAQUFADBGMQswCQYD
VQQGEwJVUzETMBEGA1UEChMKR29vZ2xlIEluYzEiMCAGA1UEAxMZR29vZ2xlIElu
dGVybmV0IEF1dGhvcml0eTAeFw0wOTAzMTIyMDIzNDFaFw0xMDAzMTIyMDMzNDFa
MGgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1N
b3VudGFpbiBWaWV3MRMwEQYDVQQKEwpHb29nbGUgSW5jMRcwFQYDVQQDEw5pbWFw
LmdtYWlsLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAo1cDhDhgSN3M
9Do6D459nSNxZW1fTPJV5rmJctt12zZwDTBbHqWlZsK1ZvcV1ngj1nYgZ7RXZmgC
sxEUSK/sw+/5Otl6BpK3OLFZI5U/FmMUTssG588Hsl21SBxeK7RoAgxxM2vB2Jgi
3KH+E13mlgUXlCyrehnbbTlwEosURyUCAwEAAaOCAQAwgf0wHQYDVR0OBBYEFNiX
w7Ps6PWzF80VrxYbz63ucVArMB8GA1UdIwQYMBaAFL/AMOv1QxE+Z7qekfv8atrj
axIkMEYGA1UdHwQ/MD0wO6A5oDeGNWh0dHA6Ly9jcmwuZXh0Lmdvb2dsZS5jb20v
R29vZ2xlSW50ZXJuZXRBdXRob3JpdHkuY3JsMFAGCCsGAQUFBwEBBEQwQjBABggr
BgEFBQcwAoY0aHR0cDovL2NhLmV4dC5nb29nbGUuY29tL0dvb2dsZUludGVybmV0
QXV0aG9yaXR5LmNydDAhBgkrBgEEAYI3FAIEFB4SAFcAZQBiAFMAZQByAHYAZQBy
MA0GCSqGSIb3DQEBBQUAA4GBALo7fCzccb+0OeU+iGPJ/CEynTIegiqt/OkZPP4P
aVy7a0MjwK7mDDYpMMu10r4oYSM8iI1BN2iIXRFDkuogp48yeh8P3In1A332XdW3
UoaoIhI8DunLXgfBEr2/TPUHtXDfJumu+wCojdjT2ywwud1hY8LSRR2zv27FWEAD
OaRh
-----END CERTIFICATE-----
subject=/C=US/ST=California/L=Mountain View/O=Google Inc/CN=imap.gmail.com
issuer=/C=US/O=Google Inc/CN=Google Internet Authority
---
No client certificate CA names sent
---
SSL handshake has read 1660 bytes and written 306 bytes
---
New, TLSv1/SSLv3, Cipher is RC4-MD5
Server public key is 1024 bit
Compression: NONE
Expansion: NONE
SSL-Session:
    Protocol  : TLSv1
    Cipher    : RC4-MD5
    Session-ID: xxxxxxxx
    Session-ID-ctx: 
    Master-Key: xxxxxxxx
    Key-Arg   : None
    Start Time: 1241109692
    Timeout   : 300 (sec)
    Verify return code: 27 (certificate not trusted)
---
* OK Gimap ready for requests from xxx.xxx.xxx.xxx

途中で verify error とか certificate not trusted とか出てるけど、
セッションは確立する。
elmo や wanderlust はこれをエラーと認識できないようだ。

ちなみに openssl のバージョンは 0.9.8i です。

bash-3.2$ openssl version
OpenSSL 0.9.8i 15 Sep 2008

結論?

openssl の動きが把握できていないため確信が持てない部分があるものの、
とりあえず上の対策(1)の通り、
ssl-certificate-verification-policy-verify はデフォルト値 (0) のままで、
~/.w3/certs 内に Equifax Secure Certificate Authority のルート証明書
を入れておく方法が安全策ではないだろうか。