Solaris,Apache2.2.4で基本認証

SolarisとApache2.2.4で基本認証ができなくて大いに悩みました。こんな現象。

  • ディレクティブはちゃんと書いている。
  • 認証画面はちゃんと出てくる。
  • ユーザ名、パスワードはあってる。
  • でも、認証画面がでてきて次に進めない。

error_logにはこんなメッセージが。

[Wed Aug 29 15:45:46 2007] [error] [client 192.168.1.1] access to /hogehoge/ failed, reason: require directives present and no Authoritative handler.

Googleでこのメッセージで検索してみると、2つほど原因があるといっています。それぞれ検証してみる。

モジュールが有効になっていない

mod_authn_fileとか、mod_authz_userあたりが有効になっていないんじゃないかと。Debianだと、このあたりのモジュールが別のところに島流しになっていて、手動で有効にしてあげないといけないみたい。でも、Solarisでソースからインストールしたんだからstaticで有効になっているはず。確認してみよう。

$ /usr/local/apache2/bin/httpd -l | grep auth
  mod_authn_file.c
  mod_authn_default.c
  mod_authz_host.c
  mod_authz_groupfile.c
  mod_authz_user.c
  mod_authz_default.c
  mod_auth_basic.c
$

うん、ちゃんと有効になっているな。

ディレクティブが足りないんじゃないか

もう一件は AuthBasicProvider というディレクティブを追加しないといけないよというもの。httpd.confを次のように変えてみる。

<Directory "/usr/local/apache2/htdocs/hogehoge">
    AuthType    Basic
    AuthBasicProvider file
    AuthName    hogehoge
    AuthUserFile /usr/local/apache2/conf/user.password
    require hoge
</Directory>

これでhttpdを再起動。でも現象変わらず。

ソースをちょっとのぞいてみる。

仕方がないのでソースをのぞいてみる。error_logに吐き出された文字列を頼りにして探してみるとありました。modules/aaa/mod_authz_default.c の52行目から始まっているcheck_user_access()という関数。あまり大きくないのでそのまま掲載します。

static int check_user_access(request_rec *r)
{
    authz_default_config_rec *conf = ap_get_module_config(r->per_dir_config,
                                                 &authz_default_module);
    int m = r->method_number;
    int method_restricted = 0;
    register int x;
    const apr_array_header_t *reqs_arr = ap_requires(r);
    require_line *reqs;

    /* BUG FIX: tadc, 11-Nov-1995.  If there is no "requires" directive,
     * then any user will do.
     */
    if (!reqs_arr) {
        return OK;
    }
    reqs = (require_line *)reqs_arr->elts;

    for (x = 0; x < reqs_arr->nelts; x++) {
        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "reqs[x].method_mask is %d."
                          "", reqs[x].method_mask);
        if (!(reqs[x].method_mask & (AP_METHOD_BIT << m))) {
            continue;
        }
        method_restricted = 1;
        break;
    }

    if (method_restricted == 0) {
        return OK;
    }

    if (!(conf->authoritative)) {
        return DECLINED;
    }

    /* if we aren't authoritative, any require directive could be
     * considered valid even if noone groked it.  However, if we are
     * authoritative, we can warn the user they did something wrong.
     *
     * That something could be a missing "AuthAuthoritative off", but
     * more likely is a typo in the require directive.
     */
    ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
                          "access to %s failed, reason: require directives "
                          "present  and no Authoritative handler.", r->uri);

    ap_note_auth_failure(r);
    return HTTP_UNAUTHORIZED;
}

Require行があるかどうかをチェック、メソッド(GetとかPutとか)がちゃんとしたものかどうかをチェック(だと思う)、さらにAuthなんとかのディレクティブがあるかどうかもチェックして、どれかエラーだったら上記エラーメッセージをだしてエラーリターン。

どうも怪しいのがfor文中でmethod_restrictedフラグを立てているところ。これ、reqs_arr->neltsが複数で、かつ、x=0の時にif文の条件にマッチしなかったら必ずフラグ立たないか? というわけで試しにフラグを立てているところをコメントあうとしてみる。

    for (x = 0; x < reqs_arr->nelts; x++) {
        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "reqs[x].method_mask is %d."
                          "", reqs[x].method_mask);
        if (!(reqs[x].method_mask & (AP_METHOD_BIT << m))) {
            continue;
        }
/*
        method_restricted = 1;
*/
        break;
    }

これでコンパイル、リンク。をっと、ちゃんと認証してくれるじゃない。なんて乱暴な(笑)。

少し深追いしてみる

これだけじゃただの野蛮人なので、デバック文を追加して関係する値をチェックしてみると、

reqs_arr->nelts
1
reqs[0].method_mask
 -1
AP_METHOD_BIT<

だとのこと。どうもmethod_maskの値がおかしいようです。Solarisじゃなくて、Linuxで動作しているapache2.2.4ではちゃんと動作しているので、開発環境がおかしいのかもしれない。また最適化オプションが悪さしているのかな。。とりあえずは目的(基本認証させること)は達成できたようなきがするのでなにか問題がでるまで放置。