Torsten,

Many thanks for the excellent information, I will ponder that.

More below, but one more question here :
Where does $r->internal_redirect "live" (in which package) ?
I am having trouble finding it.

Torsten Foertsch wrote:
On Sun 12 Oct 2008, André Warnier wrote:
In an attempt at being clever, I put the following code in the
handler :

     unless ($r->is_initial_req) {
         if (defined $r->prev) {
             # we are in a subrequest.  Just copy user from main
request. $r->user( $r->prev->user );
         }
         # Also disable authorization phase
         $r->set_handlers(PerlAuthzHandler => undef);
         return OK;
     }

You have to distinguish between subrequests and internal redirects. The former result from $r->lookup_uri, $r->lookup_file or similar (there are a few more such functions in the C API) and internal redirects that result from $r->internal_redirect (internal_fast_redirect() is not as the name suggests an internal redirect but simply overrides the current request). Subrequests are used for example by mod_rewrite, mod_include, mod_negotiation to look for some characteristics of a document and perhaps pull it in (run() it). Internal redirects are used in mod_cgi when the CGI output indicates a status 200 (HTTP_OK) but also contains a Location header. But the main usage of internal redirects is the ErrorDocument.

Now, is_initial_req() checks if the current $r is the result of a subrequest or the result of a internal redirect and returns false if so. prev() returns the parent request if the current $r is the result of an internal redirect and main() returns the main request if the current $r is a subrequest. So, your code checks only for internal redirects (ErrorDocument).

Now, have a look at httpd-2.x.y/server/request.c around line 170. You'll see this piece of code:

    /* Skip authn/authz if the parent or prior request passed the
     * authn/authz,
     * and that configuration didn't change (this requires
     * optimized _walk()
     * functions in map_to_storage that use the same merge results given
     * identical input.)  If the config changes, we must re-auth.
     */
    if (r->main && (r->main->per_dir_config == r->per_dir_config)) {
        r->user = r->main->user;
        r->ap_auth_type = r->main->ap_auth_type;
    }
else if (r->prev && (r->prev->per_dir_config == r->per_dir_config)) {
        r->user = r->prev->user;
        r->ap_auth_type = r->prev->ap_auth_type;
    }
    else {
        switch (ap_satisfies(r)) {
        case SATISFY_ALL:
        case SATISFY_NOSPEC:
            if ((access_status = ap_run_access_checker(r)) != 0) {
                return decl_die(access_status, "check access", r);
    ...

Ok, I get it.

I have a little question related to the above, but not very urgent : why the check on the configuration change ? what can change between a request and a sub-request (or internal redirect) ?

You see, you are not the first who had had the idea of reusing an established identity.
I did not think I would be.

If your subreq or internal redirect hits the same
Location or Directory container the AAA phases are completely skipped.
Maybe this is enough optimization if you shift a few directives around in your httpd.conf.
I don't think so, because this is a really specific authentication method, for a special case.
And I don't think that Apache will skip the mod_perl AAA phases, will it ?


If not, the code above shows you how to do it. But you must ask yourself if it really is valid to reuse the identity. I believe, you can safely inherit the identity from $r->main or $r->prev but you must not skip the other 2 A's. If you can't it would mean you have one realm of identities for the main request and another for the subreq. That, I'd say, is a configuration error.

As a first stage of the AAA, for some Locations, there is a filtering on the remote IP of the caller. Some IP's get an "automatic" user-id, which can vary according to the IP. In some cases, this is authoritative (no access unless you have the right IP), in some cases not (you get a second chance). Some Locations don't have the IP filter, they always get the second chance below. This IP filter is implemented as a PerlAccessHandler. This is the main reason for trying to optimise, because it is expensive : the IP of the caller must be compared to several ranges of IP, not necessarily matching regular subnets.

The second step is a PerlAuthenHandler, which can re-direct to a login page. Then there is a PerlAuthenzHandler to check if this user is allowed to access that resource. It also combines with SSO, with some URL rewriting, and with trying to control access to a Tomcat application behind the Apache.

The back-end for the authentication is a special DB system, whose access for that is rather heavy, but required. On the positive side, this is for a limited range of well-known applications, for a limited public and for a reasonable number of expected transactions/s.
So I am trying to wring out the optimisations I can, without going too far.
I started this module wanting to keep it "clean and lean and mean", but as I discover more and more twists, it is getting to look like the classical spaghetti bowl..

I am also, but on a separate thread, looking at tying this AAA stuff to the $r->connection (with notes()).

I'm also having fun doing this, it's interesting.

The idea being that if we are in a sub-request, there is no point in
authenticating/authorizing it again, since the main request should
already do that, right ?  Optimisation..

Now the above works very nicely, except in the case where, before
this handler gets called, there is an intervention by mod_rewrite. It
seems as if mod_rewrite makes the above fail, even when the rewrite
condition does not apply and the URL is considered as a
"pass-through".

I suspect that it is because mod_rewrite, no matter what, invoques
the original (or modified) URL as a sub-request of the original
request. This would cause the above to fail, because in such a case,
the above conditional code would be invoked, but there is no
$r->prev->user to be copied.

mod_rewrite doesn't make subrequests if not asked to. I know only of 2 ways to have mod_rewrite perform a subreq: %{LA-U:variable} and %{LA-F:variable} in a RewriteCond.


This :
http://perl.apache.org/docs/2.0/api/Apache2/RequestUtil.html#C_is_initial_req_
may be missing ".. or an internal redirect" in a couple of places.


Reply via email to