2008/6/10 火曜日

change_sqlpass 設定の苦悩

Filed under: プラグイン — Charlie @ 18:00:34

Squirrelmail - change_sqlpass プラグインのインストール

Squirrelmail (Web Mail) の設定をすることになった。

メールサーバは Postfix + MySQL 環境でのバーチャルマップ。 mysql_aliases.cf , mysql_virtual_alias_maps.cf , mysql_virtual_domains_maps.cf , mysql_virtual_mailbox_maps.cf の設定ファイルで Postfix が動いている環境と思ってもらいたい。

DB 接続情報
DB 名 postfix
DB 接続ユーザ名 postfixadmin_user
接続PW postfixadmin_user のパスワード
mailbox ( メアド格納用テーブル ) にあるカラム
username メールアドレス
password ※ メールパスワード
name 名前
maildir メールボックスのディレクトリ
など ほかにもいろいろ

※ なお、メールパスワードは MD5 で暗号化されているものとする。

( どこの入試試験だ )

さて、 Squirrelmail にログインしたユーザが自分でパスワードを変えることができるようにするため、change_sqlpass-3.3-1.2 というプラグインを入れた。

この設定ファイルが非常に分かりづらい。また、海外にすら情報が少ない。その程度のこと、説明も質問もするまでもないということなのだろうか。それとも Postfix + MySQL の動作環境が少ないのだろうか。

ともあれ、情報が氾濫するネットの海をさらに混沌と化すために、情報という名の一石を投じて ( 言うまでもなく、使い方を間違えている ) みることにしよう。

なお、この情報は筆者的に正確でないこともないと思っているが、 At Your OwnRisk でお願いしたい。

1. INSTALL に従ってノーマルに設定
# cp config.php.sample config.php
# vi config.php

本当にどうでもいいが、
$password_update_queries は array 型の変数であり、
$password_update_queries = array ( "ホニャララ", );
ホニャララの後にカンマが入っているのを見逃しやすい。
これは設定ファイルのサンプルがよろしくない。
ちゃんと
$password_update_queries = array ( "ホニャララ" );
にしておくこと。

PHP:
  1. // DB 接続情報
  2. $csp_dsn = 'mysql://postfixadmin_user:postfixadmin_userのパスワード@localhost/postfix';
  3.  
  4. // パスワード照合用クエリ
  5. $lookup_password_query = 'SELECT count(*) FROM mailbox WHERE username = "%1" AND password = %4';
  6.  
  7. // パスワード変更クエリ
  8. $password_update_queries = array(
  9.     'UPDATE mailbox SET password = %4 WHERE username = "%1"');
  10.  
  11. // 強制書き込み用クエリ
  12. $force_change_password_check_query = '';
  13.  
  14. // メールパスワードは MD5 で暗号化しているので
  15. $password_encryption = 'MD5CRYPT';
  16.  
  17. // MD5 の PW (実は SALT と呼ばれる部分) を拾うためのクエリ
  18. $csp_salt_static = '';
  19. $csp_salt_query = 'SELECT LEFT(password, 12) FROM mailbox WHERE username = "%1"';

# ./configure

でこのプラグインを有効にする。

2. md5crypt() エラー

こんなので動けば苦労しないわけで、当然のように Squirrelmail の挙動がおかしくなる。
Apache ログは以下のエラー。

[Sat Jun 07 00:00:00 2008] [error] [client xxx.xxx.xxx.xxx] PHP Fatal error: Call to undefined function md5crypt() in /squirrelmail/plugins/change_sqlpass/functions.
php on line 671, referer: plugins/change_sqlpass/options.php

md5crypt.php で定義されている md5crypt() が読み込めていないようだ。

力技 で修正。
( ↑力がこもっている表現 )

# vi function.php
PHP:
  1. 641 function get_password_encrypt_string($password)
  2.     642 {
  3.     643
  4.     644    global $password_encryption;
  5.     645
  6.     646    load_config('change_sqlpass', array('config.php'));
  7.            // これ↓を追加
  8.     647    include_once('md5crypt.php');
  9.     648
  10.     649    $salt = get_password_salt();

3. オプションメニューにパスワード変更画面が現れる

オプションメニューにパスワード変更画面が現れるようになった。大いなる前進である。

ただし、 PW を変更すると、 PW の照合をかけた直後あたりで、画面が遷移し、でもページが表示されない。当然、 PW も変更されていない。

タイミング的に、 PW 書き換えのクエリでエラーになっているようだ。

そのときのログ

[Sat Jun 07 00:00:00 2008] [error] [client xxx.xxx.xxx.xxx] PHP Fatal error: Call to undefined function includeonce() i
n /squirrelmail/plugins/change_sqlpass/functions.php on line 29, referer: https://hoge.coooooooom/squirrelmail/src/options.php

[Sat Jun 07 00:00:00 2008] [error] [client xxx.xxx.xxx.xxx] PHP Warning: include_once(../plugins/mail_fwd/setup.php) [<
a href='function.include-once'>function.include-once</a>]: failed to open stream: Permission denied in /squirrelmail/functions/plugin.php on line 29, referer: https://hoge.coooooooom/squirrelmail/srcright_main.php

どっちかだったと思うが、思い出せない・・・確か failed to open stream の方だったと思う。まあどちらでもよい。何故なら、 Apache のログでは解決するための糸口が見つからないからだ。

ここは、 change_sqlpass/config.php を $csp_debug = 1; にして、デバッグモードにしてみる。ちなみに、オリジナルはデバッグ時の情報が少なすぎるので、それぞれの値をちゃんと取得できているか、 function.php も書き換える。

# vi function.php
PHP:
  1. // debug if needed
  2.    //
  3.    if ($csp_debug)
  4.    {
  5.       // このあたりを修正したり追加したり
  6.       echo sprintf(_("<br />Debug: <br />"));
  7.       echo sprintf(_("<br />Password query result is: %s<br />Query was: %s<br /><br />"), $salt[0][0], $sql);
  8.       // 以下も追加
  9.       echo sprintf(_("<br />full_username: %s, user: %s, dom: %s"), $full_username, $user, $dom);
  10.     }
  11.  
  12.    // return salt
  13.    //
  14.    return $salt[0][0];

if の中に表示させたい変数 full_username などを入れてみた。

その結果、正しく取得できていることが分かった。デバッグモードにすると、いくつかの情報とともに、クエリも表示される。

Query was: SELECT count(*) FROM mailbox WHERE username = "test@hoge.coooooooom" AND password = "$1$$1$abcde$bbbbbbbbbbbbbbbbbbbbbb"

SALT の部分が見切れた上に区切り文字が増えて面白いことになってしまっているじゃないか・・・

ちなみに、 MD5 と SALT の関係についてちょっと寄り道。

MD5 で文字列を暗号化すると、 34 文字の結果になる。

以下のような MD5 で暗号化された文字列があったとする。
$1$abcdefgh$bbbbbbbbbbbbbbbbbbbbbb

$1$が MD5ハッシュですよという目印。

次の8文字(abcdefgh)の部分が salt と呼ばれるもの。

さらに $ で区切られて、最後の22文字(bbbbbbbbbbbbbbbbbbbbbb)は、salt とパスワードの組をハッシュした値。

原因発見

mailbox テーブルの password には
$1$abcdefgh$bbbbbbbbbbbbbbbbbbbbbb
という値が格納されているから、

$csp_salt_query = 'SELECT LEFT(password, 12) FROM mailbox WHERE username = "%1"';

で取得できた文字列は、 $1$abcdefgh$ ということになる。

でも実際に取得したパスワードの SALT は $1$$1$abcde$ になっているから、問題はここにある。

犯人はオマエだ、 $csp_salt_query よ。

なに? SALT の部分は abcdefgh なのだから、余計な文字を含んでいるだろ、設定したやつのせいだろって? じゃあこれを見てくれ。

md5crypt.php の SALT を切り出す部分
PHP:
  1. function md5crypt($pw, $salt, $magic="") {
  2.  
  3.   $MAGIC = "$1$";
  4.  
  5.   if ($magic == "") $magic = $MAGIC;
  6.  
  7.   $slist = explode("$", $salt);
  8.   if ($slist[0] == "1") $salt = $slist[1];
  9.   $salt = substr($salt, 0, 8);

$salt = substr($salt, 0, 8);

たしかに関数を追っていったわけじゃないが、 md5crypt の中を見ると SALT を切り抜く処理がしてると思うじゃないか。もはやオレを陥れようとする陰謀としか思えない。

しょうがないので、クエリを工夫して、純粋な SALT だけを取得することにした。頭が悪い (大きなお世話) ので Step by Step で。

SELECT LEFT(password, 12) で
$1$abcdefgh$bbbbbbbbbbbbbbbbbbbbb
の 12文字目までを切り取る。

つぎに、
SUBSTRING("$1$abcdefgh$", 4, 8) で 4 文字~ 8 文字目までを切り取ると abcdefgh が得られる。

つまりこうだ。

$csp_salt_query = 'SELECT LEFT(password, 12) FROM mailbox WHERE username = "%1"';

Squirrelmail で PW 変更すると OK だった。

あとはデバッグモードを OFF にしておくこと。

# vi change_sqlpass/config.php

$csp_debug = 0;

慣れない PHP のソースまで解析をして、若干精神が不安定になっている気がする。ご苦労なことだ。きっといいことあるさ>オレ。

バイチャ。

Total Hits: 71279 / Total Unique URL: 27707 / Hits today: 25
Login