qmail-ldap+webmail
Limitations
This solution does not take into account qmail-ldap clustering.
Also, I assume that all virtual users are under the same uid/gid and that the imap server is started from tcpserver
Ingredients
Since the imapd daemon is started from tcpserver with someting like this:
/usr/local/bin/tcpserver ... /usr/lib/courier-imap/sbin/imaplogin \ /var/qmail/bin/auth_imap \ /usr/lib/courier-imap/bin/imapd "${MAILDIRNAME-Maildir}"
We "just" have to write a new authentication program, that we will call auth_cas, which is able to understand proxy tickets as valid authentication tokens. In particular, the proxy ticket will be used as the user password, and the perl client AuthCAS will be used to validate the proxy ticket and to retrieve the username of the user trying to log-in.
We immediately see a small complication: most webmail interfaces store the user password in some form (encrypted and stored in a browser cookie, or encrypted and stored in session data). So we have to use imapproxy (see www.imapproxy.org ) to established a persisten imap connection with qmail-ldap. By using imapproxy, the webmail client passes the proxy ticket to imapproxy dameon, and this talk with the real imap daemon (which we are going to CASify).
 imapproxy
Installing and configuring imapproxy is very simple, just follow the instruction on their website. The configuration I use is the following:
server_hostname localhost listen_port 800 listen_address 127.0.0.1 server_port 843 cache_expiration_time 3600 # adjust according to your needs
So, imapproxy will be listening on 127.0.0.1:800, and will forward imap connections to 127.0.0.1:843, where qmail-ldap imap daemon is listening.
/var/qmail/bin/auth_cas.pl
This is the script that does the real work. It validates proxy tickets, and launches the imapd daemon. If the proxy ticket validates correctly, we retrieve user information from the LDAP server, and set the environment variables needed for courier-imapd.
#!/usr/bin/perl use AuthCAS; use Net::LDAP; use Net::LDAP::Util qw(ldap_error_text ldap_error_name ldap_error_desc ); $VMAIL_PATH="/vmail"; open(FH,"<&=3"); my $cas = new AuthCAS( casUrl => 'https://cas.server/url', CAFile => '/etc/ca.pem', ); while(<FH>) { chomp; if ($_ =~ m/^ST-/) { $PT=$_; my ($user, @proxies) = $cas->validatePT('{127.0.0.1:800}INBOX',$PT); ##### print LOG "User authenticated as $user via ".join(',',@proxies)."\n"; if (!$user) { exit (1); } my $ldap = Net::LDAP->new('127.0.0.1'); my $mesg =$ldap->bind(); if($mesg->code()) { return(111); } $result = $ldap->search( base=> 'dc=cilea,dc=it', filter => "(&(objectClass=qmailUser)(accountStatus=active)(uid=$user))", ); if ($result->count == 1 ) { foreach $entry ($result->all_entries) { $ENV{'MAILDIR'}=$VMAIL_PATH."/".$entry->get_value('mailMessageStore')."/Maildir/"; $ENV{'USER'}=$user; $ENV{'AUTHENTICATED'}=$user; exec @ARGV; } } else { exit(1); } } }
auth_cas.pl needs to be run as user /var/qmail/control/ldapuid and group /var/qmail/control/ldapgid. If your perl installation does not allow setuid perl script, you will have to compile a small wrapper auth_cas which is setuid and calls auth_imap.pl:
#define REAL_PATH "/usr/local/src/auth_cas_imap/auth_imap.pl" main(ac, av) char **av; { execv(REAL_PATH, av); }
 Adjust the REAL_PATH, compile with gcc, setuid the executable and use this in the run file of qmail.
Webmail integrationÂ
The final step is to integrate the webmail authentication with the casified imap daemon. If your webmail is php based, you will of course use phpCAS. For instance, for squirrelmail I use something like this: first I authenticate the users, the I build a form with username and proxy ticket as password, then this form is submitted to standard squirrelmail authentication page.
<? .... phpCAS::proxy(CAS_VERSION_2_0,'cas.server',443,'/path'); phpCAS::setCasServerCACert('/etc/ca.pem'); phpCAS::forceAuthentication(); $nomeutente=phpCAS::getUser(); phpCAS::serviceMail("{127.0.0.1:800}INBOX",0,$errore,$msg_errore,$pt); .... ?> <html> <body> <form name="logincas" action="/webmail/src/redirect.php" method="post"> <input type="hidden" name="login_username" value="<?=$nomeutente?>"> <input type="hidden" name="secretkey" value="<?=$pt?>"> </form> <script>document.forms['logincas'].submit();</script> </body> </html>