michellem has asked for the wisdom of the Perl Monks concerning the following question:

Hi folks,

I'm trying to use PAM to authenticate a web application. I tested this code on the command line, and it works just fine but for some reason it's not working in a CGI context (I'm not getting errors, but I'm getting an Authentication failure) I know there is something I'm doing wrong, but I just can't figure it out.

Here's the command line code:

#!/usr/bin/perl # # testing pam for authentication # use strict; use Authen::PAM; my $service = "passwd"; my $username = "foo"; my $passwd = "bar"; my $pamh = new Authen::PAM($service,$username, \&conv_func); ref($pamh) || die "Problems!\n"; my $res = $pamh->pam_authenticate(); print $pamh->pam_strerror($res),"\n" unless $res == PAM_SUCCESS(); print "ending...\n"; sub conv_func { my @res; while ( @_ ) { my $code = shift; my $msg = shift; my $ans = ""; $ans = $username if ($code == PAM_PROMPT_ECHO_ON() ); if ($code == PAM_PROMPT_ECHO_OFF() ) { $ans = $passwd; } push @res, (PAM_SUCCESS(),$ans); } push @res, PAM_SUCCESS(); return @res; }

Here's the web code snippets
if ((!$login_id)||($login_id ne $query{id})) { #no cookie of the right name or the cookie does not match the id s +ent in the query # make them authenticate my $username = $query{username}; my $passwd = $query{password}; # Use PAM to authenticate my $service = "passwd"; my $pamh = new Authen::PAM($service,$username,\&conv_func); # use +the conversation function # so it +doesn't have to be interactive ref($pamh) || graceful_exit("Problems with PAM authentication",$pam +h->pam_strerror($pamh),"v"); my $res = $pamh->pam_authenticate(); my $id; if (!$res == PAM_SUCCESS()) { # they aren't authentic graceful_exit("Nope. Not even close", $pamh->pam_strerror($res),"v +"); } else { # yay! they are authentic - let's look for them in our ma +pping table } } sub conv_func { my $username; my $passwd; my @res; while ( @_ ) { my $code = shift; my $msg = shift; my $ans = ""; $ans = $username if ($code == PAM_PROMPT_ECHO_ON() ); if ($code == PAM_PROMPT_ECHO_OFF() ) { $ans = $passwd; } push @res, (PAM_SUCCESS(),$ans); } push @res, PAM_SUCCESS(); return @res; }

Any suggestions? Thanks!

Replies are listed 'Best First'.
Re: Authen::PAM
by Kageneko (Scribe) on Jul 21, 2003 at 21:40 UTC
    In your command-line code, you have this:
    my $passwd = "bar";
    This declares $passwd as scoped to the module. Later on, where you have this:
    $ans = $passwd;
    in conv_func(), $passwd is just grabbing the value you declared earlier. In your web snippet, however, $passwd is scoped to the if block and conv_func doesn't have access to it (at least, it doesn't have access to the right one, if you predeclared $passwd). This is my best guess without seeing more of the code and having no idea how Authen::PAM works. If you're trying to authenticate against that $passwd, you'll need to find another way to pass it to conv_func.
      To reply to my own reply, this will also be a problem:
      my $username; my $passwd;
      Effectively, that makes it so that every time conv_func is called, $username and $passwd are blank - probably not what you want. The simplest solution is to add the my declarations to the top of the program and then never, ever, ever redeclare them :) Get rid of the line I quoted above from conv_func and get rid of the my keyword in front of $passwd and $username in the if block.
        Thanks, but now I'm really stumped. I thought this would be a scoping issue, and your suggestion made sense (I'd lost track of the blocks in the larger app). But I declared $username and $passwd early, have confirmed that the right values are getting to the subroutine, and I'm still not authenticating.