Mailing List Archive

Recognizing user in PerlFixupHandler
Hello, mod_perl users,

I have a cookies-based authentication similar to Apache2::AuthCookie,
and I have problem with setting up authentication with recognizing users
in PerlFixupHandler also for URLs accessible even without authentication
(similar to what Apache2::AuthCookie->recognize_user is supposed to do).
My httpd.conf contains something along these lines:

DocumentRoot /www

<Directory /www>
<Files *.pl>
SetHandler perl-script
PerlFixupHandler My::Auth->recognize_user
PerlResponseHandler My::Registry
</Files>
Order deny, allow
allow from all
DirectoryIndex index.pl
</Directory>

<Directory /www/auth>
AuthName "PrivateArea"
AuthType My::Auth
PerlAuthenHandler My::Auth->authenticate
require valid-user

<Files *.pl>
SetHandler perl-script
PerlResponseHandler My::Registry
</Files>
</Directory>

If I point my browser to https://my.server/auth/, the user authenticates,
and the auth info is stored in a cookie. In that case, I want subsequent
requests to Perl scripts with that cookie even outside the /auth/ area
to be recognized as authenticated, i.e. to have $r->user() nonempty.
This is what the PerlFixupHandler above is supposed to do.

It mostly works except when some URL rewriting happens:

https://my.server/index.pl works correctly (has non-empty $r->user), but
https://my.server/ without /index.pl suffix has empty $r->user, even though
I have verified that the PerlFixupHandler is also being executed and it sets
non-empty $r->user($user_from_cookie) correctly. After it returns
Apache2::Const::DECLINED, the My::Registry::handler() starts,
but it has empty $r->user, despite it being set to non-empty
in the PerlFixupHandler.

When I move the PerlFixupHandler directive outside the <Files *.pl> scope,
recognizing user works even for https://my.server/ without /index.pl,
but then the PerlFixupHandler is unnecessarily executed even for
things like static (non-Perl) data: images, Javascript files, etc.

Why does the $r->user() value disappear between PerlFixupHandler
and PerlResponseHandler calls?

Thanks,

-Yenya

--
| Jan "Yenya" Kasprzak <kas at {fi.muni.cz - work | yenya.net - private}> |
| http://www.fi.muni.cz/~kas/ GPG: 4096R/A45477D5 |
> That's why this kind of vulnerability is a concern: deploying stuff is <
> often about collecting an obscene number of .jar files and pushing them <
> up to the application server. --pboddie at LWN <
Re: Recognizing user in PerlFixupHandler [ In reply to ]
Hi Yenya!

On Monday 12 March 2018 16:15:07 Jan Kasprzak wrote:
> https://my.server/index.pl works correctly (has non-empty $r->user), but
> https://my.server/ without /index.pl suffix has empty $r->user, even though

This looks like an apache's internal redirect...

> I have verified that the PerlFixupHandler is also being executed and it sets
> non-empty $r->user($user_from_cookie) correctly. After it returns
> Apache2::Const::DECLINED, the My::Registry::handler() starts,
> but it has empty $r->user, despite it being set to non-empty
> in the PerlFixupHandler.

It is possible that in an internal redirect you get a new $r instance.

> When I move the PerlFixupHandler directive outside the <Files *.pl> scope,
> recognizing user works even for https://my.server/ without /index.pl,
> but then the PerlFixupHandler is unnecessarily executed even for
> things like static (non-Perl) data: images, Javascript files, etc.
>
> Why does the $r->user() value disappear between PerlFixupHandler
> and PerlResponseHandler calls?

And therefore it does not see $r->user as it was set in another request.
Internal redirect is a new (internal) request.

Those request objects are in some list or tree structure. Years ago I
needed to solve a similar problem, that value set in the MapToStorage
handler was not "visible" in the Authen handler. To access parent/upper
request object there is ->prev or ->main method.

You can try to set ->user on the "main" request object. And then read
->user again from the main object.

I used following code to retrieve main object, maybe it helps you.

my $r_main = $r;
$r_main = $r_main->main // $r_main->prev
until $r_main->is_initial_req;
Re: Recognizing user in PerlFixupHandler [ In reply to ]
Hi.

I do use similar (but a bit less clever) code in one of my modules :

if (my $prev = ($r->main || $r->prev)) {
$logger->warn("$pfx sub-request or internal redirect") if ($debug > 2);
# we are in a subrequest.
...
if (defined(my $uid = $prev->user)) {
$r->user( $uid );
...

but I do not recurse. I guess I should also do that, in case this is a sub-sub-request..


On 12.03.2018 16:35, pali@cpan.org wrote:
> Hi Yenya!
>
> On Monday 12 March 2018 16:15:07 Jan Kasprzak wrote:
>> https://my.server/index.pl works correctly (has non-empty $r->user), but
>> https://my.server/ without /index.pl suffix has empty $r->user, even though
>
> This looks like an apache's internal redirect...
>
>> I have verified that the PerlFixupHandler is also being executed and it sets
>> non-empty $r->user($user_from_cookie) correctly. After it returns
>> Apache2::Const::DECLINED, the My::Registry::handler() starts,
>> but it has empty $r->user, despite it being set to non-empty
>> in the PerlFixupHandler.
>
> It is possible that in an internal redirect you get a new $r instance.
>
>> When I move the PerlFixupHandler directive outside the <Files *.pl> scope,
>> recognizing user works even for https://my.server/ without /index.pl,
>> but then the PerlFixupHandler is unnecessarily executed even for
>> things like static (non-Perl) data: images, Javascript files, etc.
>>
>> Why does the $r->user() value disappear between PerlFixupHandler
>> and PerlResponseHandler calls?
>
> And therefore it does not see $r->user as it was set in another request.
> Internal redirect is a new (internal) request.
>
> Those request objects are in some list or tree structure. Years ago I
> needed to solve a similar problem, that value set in the MapToStorage
> handler was not "visible" in the Authen handler. To access parent/upper
> request object there is ->prev or ->main method.
>
> You can try to set ->user on the "main" request object. And then read
> ->user again from the main object.
>
> I used following code to retrieve main object, maybe it helps you.
>
> my $r_main = $r;
> $r_main = $r_main->main // $r_main->prev
> until $r_main->is_initial_req;
>