I posted earlier with reports of less than stellar success in using Active 
Directory for dovecot authentication.

My approach is to using the two-step approach of
        - obtaining the user DN by a search using an authenticated bind (using 
a service account)
        - then binding as that DN, and returning the relevant user attributes

This hasn't been succesful. Dovecot's authentication process does perform the 
(first) authenticated bind successfully, it does obtain the right DN, than just 
sits there doing nothing as far as I can tell, and after a long delay concludes 
authentication failure - shortly before deciding to perform the bind with the 
user-supplied credentials, successfully.  Source inspection has not resulted in 
a glorious eureka yet.

So I thought, why not handle it myself?   And I wrote a little script, using 
the checkpassword interface.  I've enclosed it.
The script is based on <http://wiki2.dovecot.org/AuthDatabase/CheckPassword>, 
but somehow the userdb_uid and userdb_gid I've passed back in the "EXTRA" 
environment variable get lost along the way.

It syslogs, and the syslogs show that the LDAP parts working as expected:

Mar  3 14:49:09 <mail.info> ponyboy checkpassword: successful authenticated 
bind and DN(js) lookup
Mar  3 14:49:09 <mail.info> ponyboy checkpassword: DN(js) is CN=Jeroen 
Scheerder,OU=Users,OU=Netherlands,OU=ON2IT,DC=office,DC=on2it,DC=net
Mar  3 14:49:09 <mail.info> ponyboy checkpassword: js authenticated

In dovecot's log, simultaneously, I see basically a successful login, except 
that the (user_)uid and (userdb_)gid work - unless I disable prefetch, and use 
a static userdb:

Mar 03 14:49:04 auth: Debug: Loading modules from directory: 
/usr/local/lib/dovecot/auth
Mar 03 14:49:04 auth: Debug: Read auth token secret from 
/var/run/dovecot/auth-token-secret.dat
Mar 03 14:49:04 auth: Debug: auth client connected (pid=90856)
Mar 03 14:49:09 auth: Debug: client in: AUTH    1       PLAIN   service=imap    
secured session=+qFODbTzDgB/AAAB        lip=127.0.0.1   rip=127.0.0.1   
lport=143       rport=63246     resp=<hidden>
Mar 03 14:49:09 auth: Debug: checkpassword(js,127.0.0.1,<+qFODbTzDgB/AAAB>): 
execute: /usr/local/etc/dovecot/checkpassword-on2it 
/usr/local/libexec/dovecot/checkpassword-reply
Mar 03 14:49:09 auth: Debug: checkpassword(js,127.0.0.1,<+qFODbTzDgB/AAAB>): 
Received input: userdb_uid=143     userdb_gid=143
Mar 03 14:49:09 auth: Debug: checkpassword(js,127.0.0.1,<+qFODbTzDgB/AAAB>): 
exit_status=0
Mar 03 14:49:09 auth: Debug: client passdb out: OK      1       user=js
Mar 03 14:49:09 auth: Debug: master in: REQUEST 4007395329      90856   1       
29571963894e557ab643d2e51872ba55        session_pid=90899       
request_auth_token
Mar 03 14:49:09 auth: Debug: prefetch(js,127.0.0.1,<+qFODbTzDgB/AAAB>): success
Mar 03 14:49:09 auth: Debug: master userdb out: USER    4007395329      js      
uid=143 gid=143 auth_token=e2d7c2463dd4c039010e904afb4ea45214cb7de5
Mar 03 14:49:09 imap-login: Info: Login: user=<js>, method=PLAIN, 
rip=127.0.0.1, lip=127.0.0.1, mpid=90899, secured, session=<+qFODbTzDgB/AAAB>
Mar 03 14:49:09 imap: Error: user js: Mail access for users with UID 143 not 
permitted (see first_valid_uid in config file, uid from userdb lookup).
Mar 03 14:49:09 imap: Error: Invalid user settings. Refer to server log for 
more information.

With a static userdb (as shown in the config below): behold, everything works:

Mar 03 14:52:49 auth: Debug: client in: AUTH    1       PLAIN   service=imap    
secured session=R0plGrTzGAB/AAAB        lip=127.0.0.1   rip=127.0.0.1   
lport=143       rport=40984     resp=<hidden>
Mar 03 14:52:49 auth: Debug: checkpassword(js,127.0.0.1,<R0plGrTzGAB/AAAB>): 
execute: /usr/local/etc/dovecot/checkpassword-on2it 
/usr/local/libexec/dovecot/checkpassword-reply
Mar 03 14:52:49 auth: Debug: checkpassword(js,127.0.0.1,<R0plGrTzGAB/AAAB>): 
Received input: userdb_uid=143     userdb_gid=143
Mar 03 14:52:49 auth: Debug: checkpassword(js,127.0.0.1,<R0plGrTzGAB/AAAB>): 
exit_status=0
Mar 03 14:52:49 auth: Debug: client passdb out: OK      1       user=js
Mar 03 14:52:49 auth: Debug: master in: REQUEST 2818310145      90960   1       
1b6ea6c4e6b90fd49a87195c35fa34ef        session_pid=91002       
request_auth_token
Mar 03 14:52:49 auth: Debug: master userdb out: USER    2818310145      js      
uid=1000        gid=1000        home=/var/mail/on2it/js 
auth_token=21609f5f149bf80dec701dce9f288824cdf52c60
Mar 03 14:52:49 imap-login: Info: Login: user=<js>, method=PLAIN, 
rip=127.0.0.1, lip=127.0.0.1, mpid=91002, secured, session=<R0plGrTzGAB/AAAB>
Mar 03 14:53:04 imap(js): Info: Connection closed in=0 out=352

So it's working for me now.  This is clearly not the way things ought to 
work... but the stock LDAP interaction seems broken to my limited mind.

So who would be so friendly as to point out the fallacies I've been pursuing?

Regards, Jeroen.

$ dovecot -n
# 2.2.10: /usr/local/etc/dovecot/dovecot.conf
# OS: FreeBSD 10.0-RELEASE amd64  ufs
auth_debug = yes
auth_mechanisms = plain login
auth_username_format = %Ln
auth_verbose = yes
first_valid_gid = 1000
first_valid_uid = 1000
imap_client_workarounds = delay-newmail
last_valid_gid = 1000
last_valid_uid = 1000
log_path = /tmp/dovecot
mail_gid = 1000
mail_location = maildir:/var/mail/on2it/%Ln
mail_uid = 1000
maildir_very_dirty_syncs = yes
namespace inbox {
  inbox = yes
  location =
  mailbox Drafts {
    special_use = \Drafts
  }
  mailbox Junk {
    special_use = \Junk
  }
  mailbox Sent {
    special_use = \Sent
  }
  mailbox "Sent Messages" {
    special_use = \Sent
  }
  mailbox Trash {
    special_use = \Trash
  }
  prefix =
}
passdb {
  args = /usr/local/etc/dovecot/checkpassword-on2it
  driver = checkpassword
}
protocols = imap
service auth-worker {
  user = root
}
service auth {
  unix_listener /var/spool/postfix/private/auth {
    mode = 0666
  }
  unix_listener auth-userdb {
    group = postfix
    mode = 0666
    user = postfix
  }
}
service imap-login {
  inet_listener imap {
    port = 143
  }
}
shutdown_clients = no
ssl = no
userdb {
  args = uid=1000 gid=1000 home=/var/mail/on2it/%Ln
  driver = static
}
valid_chroot_dirs = /var/mail/on2itn2it
#!/bin/sh

exec > /tmp/checkpassword 2>&1; printenv; set -x
CKPASSREPLY=$1

# TMP file creation and cleanup
dnresult=$(mktemp -t $(basename $0)); authresult=$(mktemp -t $(basename $0))
cleanup () { rm -f "$dnresult" "$authresult"; exit; }
trap 'cleanup' 0 1 2 3 4 6 7 8 10 11 12 14 15 24 25

cfgvalue() {
        echo $(cut -d= -f2-)
}
ldapvalue() {
        awk '{FS=": "; print $2}'
}

# Dovecot-style LDAP configuration, containing a single "hosts", a "base", "dn" 
and "dnpass"
cfg=/usr/local/etc/dovecot/on2it-ldap-users.cfg
  srv=$(grep "^hosts[[:space:]]*=[[:space:]]*" "$cfg" | cfgvalue)
  base=$(grep "^base[[:space:]]*=[[:space:]]*" "$cfg" | cfgvalue)
  dn=$(grep "^dn[[:space:]]*=[[:space:]]*" "$cfg" | cfgvalue)
  dnpass=$(grep "^dnpass[[:space:]]*=[[:space:]]*" "$cfg" | cfgvalue)
LDAPSEARCH="/usr/local/bin/ldapsearch -o ldif-wrap=no -x -LLL -E 
pr=200/noprompt -H ldap://$srv -b $base -s sub"

# Read username and pass, null-separated on fd 3
input=$(tr "\0" "\1" 0<&3)
username=$(echo $input | awk '{FS = "\1" ; print $1}')
userpass=$(echo $input | awk '{FS = "\1" ; print $2}')

# Obtain DN using authenticated bind
if ${LDAPSEARCH} -D "$dn" -w "$dnpass" 
"(&(ObjectClass=person)(sAMAccountName=$username))" DN > "$dnresult" ; then
        logger -t checkpassword -p mail.info "successful authenticated bind and 
DN($username) lookup"
else
        logger -t checkpassword -p mail.err "internal error looking up 
DN($username)"
        exit 111
fi
DN=$(grep -i '^DN:' "$dnresult" | ldapvalue)
if [ "${DN}" ] ; then
        logger -t checkpassword -p mail.info "DN($username) is $DN"
else
        logger -t checkpassword -p mail.warning "DN($username) not found"
        exit 3
fi
if ${LDAPSEARCH} -D "$DN" -w "$userpass" sAMAccountName=$username 
sAMAccountName > "$authresult" ; then
        logger -t checkpassword -p mail.info "$username authenticated"
else
        logger -t checkpassword -p mail.warning "$username authentication 
failed"
        exit 1
fi
HOME=$(grep -i '^sAMAccountName:' "$authresult" | ldapvalue)
USER=${HOME}
AUTHORIZED=2
userdb_uid=1000
userdb_gid=1000
EXTRA="userdb_uid userdb_gid"

exec ${CKPASSREPLY}

Attachment: signature.asc
Description: OpenPGP digital signature

Reply via email to