#!/usr/bin/perl

# Dovecot external auth script using SASL
# 2012-01-16
# By Frederik Teichert, http://teichert-ing.de
#
# Features: auth and isUser work, but setPass doesn't.
# Restrictions: Username or passwords may not contain some special characters: $'"` nor line breaks

# Based on the Mysql external auth script by Alejandro Grijalba (SuD) http://www.latinsud.com
# http://www.ejabberd.im/check_mysql_perl

# Changes I made in dovecot.conf:
# To have multiple client sockets, create multiple "socket liste" entries.
# 
# socket listen {
#        client {
#                path = /var/run/dovecot/ejabberd
#                mode = 0660
#                user = ejabberd
#                group = ejabberd
#        }
# }
# # The user and group called 'ejabberd' must exist!

# Troubleshooting: 
# Because debugging this auth-script is hard, try to run it as the ejabberd user from 
# the commandline:
# echo "2nauth:user:domain:password" | sudo -u ejabberd ./dovecot_auth.pl
# This helped a lot.
# Replace '2' with the length of your string if it doesn't work
#
# If authentification hangs try to relace a line in Authd.pm (e.g. /usr/local/share/perl/5.10.1/Authen/SASL/Authd.pm)
# as mentioned in http://www.mail-archive.com/dovecot@dovecot.org/msg29498.html . 
#
# my $base64 = encode_base64("\0$login\0$passwd");
# becomes
# my $base64 = encode_base64("\0$login\0$passwd", '');
# 
# This helped a lot, too.

# To use internal and external auth in ejabberd, use this two lines:
# {auth_method, [internal, external]}.
# {extauth_program, "/path/to/check_dovecot.pl"}.

# And finally:
# $ chown ejabberd:ejabberd /path/to/check_dovecot.pl
# $ chmod u+x /path/to/check_dovecot.pl

# Restart ejabberd and enjoy!

# I'm using this version:
# dovecot 1.2.15
# ejabberd 2.1.5
# perl 5.10.1



use Unix::Syslog qw(:macros :subs);
use Authen::SASL::Authd qw(auth_dovecot user_dovecot);

while(1) {
    my $buf = "";
    syslog LOG_INFO,"ejabberd-dovecot-auth: waiting for packet";
    my $nread = sysread STDIN,$buf,2;
    do { syslog LOG_INFO,"ejabberd-dovecot-auth: port closed"; exit; } unless $nread == 2;
    my $len = unpack "n",$buf;
    my $nread = sysread STDIN,$buf,$len;

    my ($op,$user,$domain,$password) = split /:/,$buf;
    
    # Filter dangerous characters
    $user =~ s/[."\n\r'\$`]//g;
    $password =~ s/[."\n\r'\$`]//g;
    
    #$user =~ s/\./\//og;
    my $result;

    syslog(LOG_INFO,"ejabberd-dovecot-auth: request ($op, \"$user\@$domain\", '****')");
    #print "ejabberd-dovecot-auth: request ($op, \"$user\@$domain\", $password)";

    SWITCH: {
        $op eq 'auth' and do {
       	       if (auth_dovecot("$user\@$domain", $password, timeout => 3, socket => '/var/run/dovecot/ejabberd')) {
                 $result = true;
                 syslog(LOG_INFO,"ejabberd-dovecot-auth: -> +OK");
               } else {
                 $result = false;
                 syslog(LOG_INFO,"ejabberd-dovecot-auth: -> -ERR");
               }
        },last SWITCH;

        $op eq 'setpass' and do {
             $result = 0;
        },last SWITCH;

        $op eq 'isuser' and do {
       	       if (user_dovecot("$user\@$domain", timeout => 3, socket => '/var/run/dovecot/ejabberd')) {
                 $result = true;
                 syslog(LOG_INFO,"ejabberd-dovecot-auth: -> +OK");
               } else {
                 $result = false;
                 syslog(LOG_INFO,"ejabberd-dovecot-auth: -> -ERR");
               }
          },last SWITCH;
    };
    my $out = pack "nn",2,$result ? 1 : 0;
    syswrite STDOUT,$out;
  }

closelog;

