htpasswdで作成したMD5パスワードの検証プログラムでちょっとはまったのでメモ。
デフォルトなら簡単で、crypt()を使えば検証できる。
% htpasswd -n sakamoto New password: Re-type new password: sakamoto:ZZp3anC9LpXVk
% ruby -e 'p "test".crypt("ZZp3anC9LpXVk"); p "ZZp3anC9LpXVk"' "ZZp3anC9LpXVk" "ZZp3anC9LpXVk"
しかし、htpasswd(1)によるとcrypt()を使ったときには8文字制限がある。
When using the crypt() algorithm, note that only the first 8 characters of the password are used to form the password. If the supplied password is longer, the extra characters will be silently discarded.
というわけで、出来ればMD5を使いたい。
暗号化したパスワード("test")の生成
% htpasswd -nm sakamoto New password: Re-type new password: sakamoto:$apr1$3cZHwh7q$7DDtNJF8.j6iCI3G8TSA2.
先ほどと同様にチェック。
% ruby -e 'p "test".crypt("$apr1$3cZHwh7q$7DDtNJF8.j6iCI3G8TSA2."); p "$apr1$3cZHwh7q$7DDtNJF8.j6iCI3G8TSA2."' "$aWVSdLXZq3pQ" "$apr1$3cZHwh7q$7DDtNJF8.j6iCI3G8TSA2."
一致しない。
crypt(3)を読むと、MD5の場合は"$1$"で始まるようなので、$apr1$を$1$に変えて試してみる。
% ruby -e 'p "test".crypt("$1$3cZHwh7q$7DDtNJF8.j6iCI3G8TSA2."); p "$1$3cZHwh7q$7DDtNJF8.j6iCI3G8TSA2."' "$1$3cZHwh7q$ta3WoNKmxNLqdge3970Vy." "$1$3cZHwh7q$7DDtNJF8.j6iCI3G8TSA2."
おしい(ぜんぜんおしくねぇ)。
crypt()はあきらめてApacheのソースを読むと、apr_password_validate()という関数があった。
テストプログラム
#include "apr-1/apr_strings.h" #include "apr-1/apr_md5.h" int main (int argc, char *argv[]) { apr_status_t status; status = apr_password_validate(argv[1], argv[2]); printf("%s\n", status == APR_SUCCESS ? "ok" : "ng"); }
Apache Portable Runtimeライブラリをリンクしてコンパイル。
% gcc -I/usr/local/include -L/usr/local/lib -lapr-1 -laprutil-1 htpwd_chk.c
実行結果
% ./a.out test '$apr1$3cZHwh7q$7DDtNJF8.j6iCI3G8TSA2.' ok % ./a.out tes '$apr1$3cZHwh7q$7DDtNJF8.j6iCI3G8TSA2.' ng
というわけで、出来ました。