#!/usr/bin/perl -w
#  
#  authPAM.pl.  Version 1.2.
#
#  A simple external authentication program for CommuniGate Pro   
#  that uses PAM (Pluggable Authentication Modules) library.
#
#  Warning: On some systems this program will work properly only if
#  you are running as 'root'.
#
#  Please mail your comments to <support@communigatepro.ru>
#
#  References:
#  [1] CGPro External Authentication
#        <http://communigatepro.ru/CommuniGatePro/Security.html#External>
#  [2] The Linux-PAM System Administrators' Guide
#        <http://linux.ssc.nsu.ru/pam/html/pam.html>
#  [3] The Linux-PAM Application Developers' Guide
#        <http://linux.ssc.nsu.ru/pam/html/pam_appl.html>
#  [4] Perl interface for PAM.
#        <http://search.cpan.org/search?module=Authen::PAM>
#
#  History:
#  1.0 - first release
#  1.1 - quitCounter implemented to help to avoid memory leak problems on some systems
#  1.2 - using parse_line instead of split

use Authen::PAM;
use Text::ParseWords;

# authenticate this number of times (if >0) and then quit
# make sure the helper is auto-restarted by CGPro
my $quitCounter=1000; 

$| = 1; #force STDOUT autoflush after each write

my ($seq,$vrfy,$mode,$user,$passw);

sub pam_conv_func {
  my @res;
  while ( @_ ) {
    my $msg_type = shift;
    my $msg = shift;
    #print $msg;
    push @res, 0;
    push @res, $passw;
  }
  push @res, PAM_SUCCESS;
  return @res;
}

print "* authPAM 1.2 external authenticator started.\n";
while(<STDIN>) {
  chomp;
  ($seq,$cmd,$mode,$user,$passw) = parse_line('\s+', 0, $_);
  if(!defined($cmd)) {
      print "$seq ERROR Wrong arguments\n";
  }elsif($cmd eq 'VRFY') {
    unless($mode =~ /^\(.*\)$/) {
      $passw=$user; $user=$mode;  
    }
  
    unless($seq && $user && $passw) {
      print "$seq ERROR Wrong arguments\n";
    } else {
      my ($res,$pamh);
      if($user =~/(.+)\@.+/) {$user=$1;}  #strip the domain name if necessary
      if(($res=pam_start("login", $user, \&pam_conv_func, $pamh))!=PAM_SUCCESS ||
         ($res=pam_authenticate($pamh, 0))!=PAM_SUCCESS ||
         ($res=pam_end($pamh, 0))!=PAM_SUCCESS) {
        print "$seq ERROR ".pam_strerror($pamh,$res)."\n";
      } else {
        print "$seq OK \n";
      }   
    }
    if($quitCounter>0) {
      if(--$quitCounter==0) {
        print "* time to quit and to be restarted\n";
        last;
      }
    }
  }elsif($cmd eq 'QUIT') {
    last;
  }elsif($cmd eq 'INTF') {
    print "$seq INTF 1\n";
  }elsif($cmd =~ /^SASL/) {
    print "$seq ERROR only plain text passwords supported\n";
  }else {
    print "$seq ERROR unknown command\n";
  }
}
print "* authPAM external authenticator stopped.\n";

__END__ 
