Courier authlib, the authentication framework from courier projects (courier-pop, courier-imap, sqwebmail…), can be set up using authpipe mode, where authentication can be done externaly.
Protocol of exchange is described in courier-mta's website in the section "The authpipe protocol".
Libhome is a nice authentication library that emulates use of getpwnam and can talk to mysql, ldap backends. And, libhome has a perl module (Home available in the libhome sources).
So, here is a perl script which can act as an authProg for courier-authlib. It uses libhome to actually perform authentication.
It implements LOGIN, CRAM-MD5 and CRAM-SHA1 methods.
#!/usr/bin/perl -w # $Id: authProg.pl,v 1.5 2007/08/03 13:57:02 cbellot Exp $ # # Module d'authentification externe pour courier-authdaemon # en mode authpipe # # http://www.courier-mta.org/authlib/README_authlib.html # use Home; use MIME::Base64; use Digest::MD5 qw(md5 md5_hex); use Digest::SHA1 qw(sha1 sha1_hex); my %userinfo; # Retrieve data via libhome sub getuserinfo($$) { my $service = shift; my $user = shift; # Look up data for this account &Home::setpwtag($service); my ($name,$passwd,$uid,$gid, $quota,$comment,$gcos,$dir,$shell, $expire) = &Home::getpwnam($user); %userinfo = ( name => $name, passwd => $passwd, uid => $uid, gid => $gid, quota => $quota, dir => $dir ); } sub printuserinfo() { # Return retrieved data if (!defined($userinfo{'name'}) || $userinfo{'name'} eq "") { print "FAIL\n"; return; } #print "NAME=$userinfo{'name'}\n"; #print "USERNAME=$userinfo{'name'}\n"; print "ADDRESS=$userinfo{'name'}\n"; print "PASSWD2=$userinfo{'passwd'}\n"; print "UID=$userinfo{'uid'}\n"; print "GID=$userinfo{'gid'}\n"; print "HOME=$userinfo{'dir'}\n"; #print "MAILDIR=$userinfo{'dir'}/Maildir\n"; print "QUOTA=$userinfo{'quota'}S\n"; print "OPTIONS=\n"; print ".\n"; } # MAIN $|=1; print STDERR "libhome compatible authProg module starting\n"; while (<>) { if (/^PRE (\S+) (\S+) (.*)$/) { # Request type PRE # $1=. $2=service $3=uid &getuserinfo($2, $3); &printuserinfo(); } elsif (/^AUTH (\d+)$/ && read(STDIN,$buf,$1) == $1) { # Request type AUTH # AUTH method LOGIN if ($buf =~ /^(.*)\n(login)\n(.*)\n(.*)$/) { # $1=service, $2=login, $3=user, $4=password &getuserinfo($1,$3); if ($userinfo{'passwd'} ne $4) { print "FAIL\n"; next; } &printuserinfo(); next; } # AUTH method CRAM-MD5 if ($buf =~ /^(.*)\n(cram-md5)\n(.*)\n(.*)$/) { # $1=service, $2=cram-md5, $3=challenge, $4=response my $authservice = $1; my $challenge = decode_base64($3); my $response = decode_base64($4); $response =~ /(.*) (.*)/; my $username = $1; my $digest = $2; &getuserinfo($authservice,$username); my $digest2 = md5_hex(($userinfo{'passwd'} ^ "\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\").md5(($userinfo{'passwd'} ^ "6666666666666666666666666666666666666666666666666666666666666666").$challenge)); if ($digest ne $digest2) { print "FAIL\n"; next; } &printuserinfo(); next; } # AUTH method CRAM-SHA1 if ($buf =~ /^(.*)\n(cram-sha1)\n(.*)\n(.*)$/) { # $1=service, $2=cram-sha1, $3=challenge, $4=response my $authservice = $1; my $challenge = decode_base64($3); my $response = decode_base64($4); $response =~ /(.*) (.*)/; my $username = $1; my $digest = $2; &getuserinfo($authservice,$username); my $digest2 = sha1_hex(($userinfo{'passwd'} ^ "\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\").sha1(($userinfo{'passwd'} ^ "6666666666666666666666666666666666666666666666666666666666666666").$challenge)); if ($digest ne $digest2) { print "FAIL\n"; next; } &printuserinfo(); next; } # AUTH method CRAM-SHA256 } elsif (/^PASSWD (.*?)\t(.*?)\t(.*?)\t(.*?)$/) { # Request type PASSWD # Not implemented print "FAIL\n"; next; } elsif (/^ENUMERATE$/) { # Request type ENUMERATE # Not implemented print ".\n"; next; } else { print "FAIL\n"; } }