必要があってPHPでMicrosoft Exchange Server 2007のサーバにIMAPの接続をするような設定を行っていたのだけれど、いくらやっても接続に失敗する。最終的にはうまくいったけれど、今後のためにもメモ。
まず失敗したときのPHPコードだけど、これ自体は簡単なもので、
$imap_stream = imap_open('{example.com:143/imap/notls}INBOX', 'user', 'password'); if ($imap_stream) { echo "ok\n"; imap_close($imap_stream); } else { echo "fail\n"; }
みたいな感じ。これでたとえばphpコマンドから直接実行すると、エラーメッセージには見慣れない
PHP Notice: Unknown: Kerberos error: Unknown code krb5 195 (try running kinit) for example.com (errflg=1) in Unknown on line 0
のような文字列が出る。一般的なメールソフトやtelnetによるプロトコルの直接操作でも問題ないことは確認済み。いったいなぜなんだと悩んでいたら、認証方式が問題だった。
相手のExchangeサーバにtelnetでつないで、
? CAPABILITY
と実行すると、
* CAPABILITY IMAP4 IMAP4rev1 AUTH=NTLM AUTH=GSSAPI AUTH=PLAIN IDLE NAMESPACE LITERAL+ ? OK CAPABILITY completed.
というような結果が返ってくる。で、問題はAUTH=GSSAPIの部分。ExchangeサーバではActiveDirectoryとの連携による認証方式がサポートされている。その認証方式を表すのが、おそらくGSSAPIの部分。これがPHPのIMAPモジュールの認証方式と関係してくる。
PHPのIMAPモジュールでは、Kerberosが有効な状態でビルドされていると、GSS-API認証方式が有効になる。php_imap.cの関連部分のコードを抜き出してみると(バージョンは5.2.11)、
#ifndef PHP_WIN32 auth_link(&auth_log); /* link in the log authenticator */ auth_link(&auth_md5); /* link in the cram-md5 authenticator */ #if HAVE_IMAP_KRB && defined(HAVE_IMAP_AUTH_GSS) auth_link(&auth_gss); /* link in the gss authenticator */ #endif auth_link(&auth_pla); /* link in the plain authenticator */ #endif
のようになっている。PLAIN認証より先にGSS-API認証が来てる。さらに先ほどのExchangeサーバの応答では、LOGとCRAM-MD5での認証は使えない。とすると、PHPからはGSS-API認証を実行するようになり、その準備がきちんと行われていない場合、認証に失敗して接続ができない、ということになる。先ほどの得体のしれないエラーメッセージも、ActiveDirectoryでの認証に必要なKerberosの設定ができていないということを示していたみたいだ。
問題を解決するには、たとえばESAdminにあるような形でkerberosの設定とクライアントの認証を行って、チケットを取得しておく必要があるようだ。エラーメッセージのkinitはまさにチケット取得のためのコマンドである。調べてみたら、kinitはCentOS 5ではkrb5-workstationというパッケージに含まれていた。
でも、残念ながら今回は、自分で管理しているActiveDirectoryサーバじゃないので、Kerberosの設定に必要なRealmその他の情報が全くわからない。仕方がないのでPHPのIMAPモジュールを、Kerberosを外した状態で再コンパイルした。Kerberosを外して再コンパイルするには、CentOS 5の場合、IMAPモジュールが使うlibc-clientの設定が関係してくる。CentOS 5の標準のlibc-clientパッケージのように、もしこれがKerberosを有効な状態でコンパイルしてあると、PHPのconfigureがそれを検知して、configureのオプションにwith-kerberosをつけろと要求してくる。回避するためには、 /usr/include/imap/linkage.h の中の
extern AUTHENTICATOR auth_gss;
を削除しておかなければいけない。削除した上で、configureオプションにwithout-kerberosをつけてIMAPモジュールを作ればよい。こうしてできたIMAPモジュールをインストールしてから接続を試せば、さっきのサーバではPLAIN方式しか有効ではないので、それで認証を行ってきちんとログインできるようになる。