Dovecot and remote SASL Client via TLS
Gerhard Wiesinger
lists at wiesinger.com
Fri Aug 8 06:05:43 UTC 2014
Hello,
Anyone interested, I solved it the following way:
imap server: dovecot <=> /bin/saslcheckpassword <=> local unix domain
socket for saslauthd <=> socat client
^
= Remote TLS connection
v
auth server: socat server <=> saslauthd <=> /etc/passwd,/etc/shadow
saslcheckpassword is based on checkpassword.sh and enhanced for SASL,
modified version attached:
https://bitbucket.org/vizovitin/dovecot-conf-examples/src/tip/checkpassword-shell/checkpassword.sh
Detailed configs below.
imap server:
touch /var/log/dovecot-saslcheckpassword.log
chown dovecot.dovecot /var/log/dovecot-saslcheckpassword.log
chmod 750 /var/log/dovecot-saslcheckpassword.log
Advantage is single authentication entity.
Works well for me for some time now.
If you have some questions feel free to ask.
Ciao,
Gerhard
================================================================================================================================================================
IMAP server, SASL client:
cat /etc/systemd/system/multi-user.target.wants/saslclient.service
[Unit]
Description=SASL remote client for authentication, provides local unix
domain socket
After=network.target
[Service]
# Must be running under permissions for the dovecot user!
ExecStart=/usr/bin/socat -4 -ly
UNIX-LISTEN:/var/run/saslauthd/mux,fork,user=dovecot,group=dovecot
'OPENSSL:myserver:1234,verify=1,method=TLSv1,cipher=DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXP:!LOW:!MD5,cafile=/etc/socat/mycert.crt'
[Install]
WantedBy=multi-user.target
================================================================================================================================================================
Auth server:
cat /etc/systemd/system/multi-user.target.wants/saslserver.service
[Unit]
Description=SASL remote server for authentication
After=network.target
[Service]
ExecStart=/usr/bin/socat -4 -ly
'OPENSSL-LISTEN:9999,reuseaddr,fork,verify=0,method=TLSv1,cipher=DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXP:!LOW:!MD5,cert=/etc/socat/mycert.pem,cafile=/etc/socat/mycert.crt'
/var/run/saslauthd/mux
[Install]
WantedBy=multi-user.target
================================================================================================================================================================
Relevant dovecot config:
userdb {
args = username_format=%n /etc/dovecot/users
driver = passwd-file
}
passdb {
args = /bin/saslcheckpassword
driver = checkpassword
}
================================================================================================================================================================
--
http://www.wiesinger.com/
On 16.07.2014 15:50, Gerhard Wiesinger wrote:
>
> Any comments?
>
> Thank you.
>
> Ciao,
> Gerhard
>
> http://www.wiesinger.com/
-------------- next part --------------
#!/bin/bash
# Example Dovecot checkpassword script that may be used as both passdb or userdb.
#
# Originally written by Nikolay Vizovitin, 2013.
# Enhanced by Gerhard Wiesinger, 2014.
# Assumes authentication DB is in /etc/dovecot/users, each line has '<user>:<password>' format.
# Place this script into /etc/dovecot/checkpassword.sh file and make executable.
# Implementation guidelines at http://wiki2.dovecot.org/AuthDatabase/CheckPassword
# The first and only argument is path to checkpassword-reply binary.
# It should be executed at the end if authentication succeeds.
CHECKPASSWORD_REPLY_BINARY="$1"
# Messages to stderr will end up in mail log (prefixed with "dovecot: auth: Error:")
#LOG=/dev/stderr
# Will be e.g. /tmp/systemd-dovecot.service-XfZAUy5/tmp/saslcheckpassword.log
#LOG=/tmp/saslcheckpassword.log
# touch /var/log/dovecot-saslcheckpassword.log
# chown dovecot.dovecot /var/log/dovecot-saslcheckpassword.log
# chmod 750 /var/log/dovecot-saslcheckpassword.log
LOG=/var/log/dovecot-saslcheckpassword.log
#export default_userdb_uid=popuser
#export default_userdb_gid=popuser
export default_userdb_uid=vmail
export default_userdb_gid=vmail
export SASL=1
# User and password will be supplied on file descriptor 3.
INPUT_FD=3
# Error return codes.
ERR_PERMFAIL=1
ERR_NOUSER=3
ERR_TEMPFAIL=111
# Make testing this script easy. To check it just run:
# printf '%s\0%s\0' <user> <password> | ./checkpassword.sh test; echo "$?"
if [ "$CHECKPASSWORD_REPLY_BINARY" = "test" ]; then
CHECKPASSWORD_REPLY_BINARY=/bin/true
INPUT_FD=0
fi
# Credentials lookup function. Given a user name it should output 'user:password' if such
# account exists or nothing if it does not. Return non-zero code in case of error.
credentials_lookup()
{
local db="$1"
local user="$2"
awk -F ':' -v USER="$user" '($1 == USER) {print}' "$db" 2>>$LOG
}
# Credentials verification function. Given a user name and password it should output non-empty
# string (this implementation outputs 'user:password') in case supplied credentials are valid
# or nothing if they are not. Return non-zero code in case of error.
credentials_verify()
{
local db="$1"
local user="$2"
local pass="$3"
awk -F ':' -v USER="$user" -v PASS="$pass" '($1 == USER && $2 == PASS) {print}' "$db" 2>>$LOG
}
credentials_verify_sasl()
{
local db="$1"
local user="$2"
local pass="$3"
mail_name="`echo \"$user\" | awk -F '@' '{ print $1 }'`"
domain_name="`echo \"$user\" | awk -F '@' '{ print $2 }'`"
#export HOME="/var/qmail/mailnames/$domain_name/$mail_name/"
export HOME="/home/vmail/${user}/"
if [ ! -z "${domain_name}" ]; then
# Don't log to stdout (otherwise authenticated=yes)!!
/usr/sbin/testsaslauthd -s smtp -r "${domain_name}" -u "${mail_name}" -p "${pass}" > /dev/null 2>&1
else
# Don't log to stdout (otherwise authenticated=yes)!!
/usr/sbin/testsaslauthd -s smtp -u "${mail_name}" -p "${pass}" > /dev/null 2>&1
fi
if [ "$?" = "0" ]; then
# Success, echo something
echo "USER=\"$user\" PASS=\"**********\""
fi
}
# Just a simple logging helper.
log_result()
{
# echo "$*; Input: $USER:$PASS Home: $HOME; Reply binary: $CHECKPASSWORD_REPLY_BINARY" >>$LOG
echo "`date "+%Y.%m.%d %H:%M:%S"` $*; Input: $USER Home: $HOME; Reply binary: $CHECKPASSWORD_REPLY_BINARY" >>$LOG
}
# Read input data. It is available from $INPUT_FD as "${USER}\0${PASS}\0".
# Password may be empty if not available (i.e. if doing credentials lookup).
read -d $'\0' -r -u $INPUT_FD USER
read -d $'\0' -r -u $INPUT_FD PASS
# Both mailbox and domain directories should be in lowercase on file system.
# So let's convert login user name to lowercase and tell Dovecot that 'user' and 'home'
# (which overrides 'mail_home' global parameter) values should be updated.
# Of course, conversion to lowercase may be done in Dovecot configuration as well.
export USER="`echo \"$USER\" | tr 'A-Z' 'a-z'`"
mail_name="`echo \"$USER\" | awk -F '@' '{ print $1 }'`"
domain_name="`echo \"$USER\" | awk -F '@' '{ print $2 }'`"
#export HOME="/var/qmail/mailnames/$domain_name/$mail_name/"
export HOME="/home/vmail/${USER}/"
# Script will be called under dovecot/dovecot user/group
#id >>$LOG
# CREDENTIALS_LOOKUP is not set in our use case!
# CREDENTIALS_LOOKUP=1 environment is set when doing non-plaintext authentication.
if [ "$CREDENTIALS_LOOKUP" = 1 ]; then
action=credentials_lookup
else
if [ ! -z "${SASL}" ]; then
action=credentials_verify_sasl
else
action=credentials_verify
fi
fi
# Perform credentials lookup/verification.
lookup_result=`$action "/etc/dovecot/users" "$USER" "$PASS"` || {
# If it failed, consider it an internal temporary error.
# This usually happens due to permission problems.
log_result "internal error (ran as `id`)"
exit $ERR_TEMPFAIL
}
if [ -n "$lookup_result" ]; then
# Dovecot calls the script with AUTHORIZED=1 environment set when performing a userdb lookup.
# The script must acknowledge this by changing the environment to AUTHORIZED=2,
# otherwise the lookup fails.
[ "$AUTHORIZED" != 1 ] || export AUTHORIZED=2
# And here's how to return extra fields from userdb/passdb lookup, e.g. 'uid' and 'gid'.
# All virtual mail users in Plesk actually run under 'popuser'.
# See also:
# http://wiki2.dovecot.org/PasswordDatabase/ExtraFields
# http://wiki2.dovecot.org/UserDatabase/ExtraFields
# http://wiki2.dovecot.org/VirtualUsers
export userdb_uid=${default_userdb_uid}
export userdb_gid=${default_userdb_gid}
export EXTRA="userdb_uid userdb_gid $EXTRA"
if [ "$CREDENTIALS_LOOKUP" = 1 ]; then
# If this is a credentials lookup, return password together with its scheme.
# The password scheme that Dovecot wants is available in SCHEME environment variable
# (e.g. SCHEME=CRAM-MD5), however 'PLAIN' scheme can be converted to anything internally
# by Dovecot, so we'll just return 'PLAIN' password.
found_password="`echo \"$lookup_result\" | awk -F ':' '{ print $2 }'`"
export password="{PLAIN}$found_password"
export EXTRA="password $EXTRA"
# log_result "credentials lookup result: '$password' [SCHEME='$SCHEME', EXTRA='$EXTRA']"
log_result "credentials lookup result: '**********' [SCHEME='$SCHEME', EXTRA='$EXTRA']"
else
log_result "lookup result: '$lookup_result'"
fi
# At the end of successful authentication execute checkpassword-reply binary.
exec $CHECKPASSWORD_REPLY_BINARY
else
# If matching credentials were not found, return proper error code depending on lookup mode.
if [ "$AUTHORIZED" = 1 -a "$CREDENTIALS_LOOKUP" = 1 ]; then
log_result "lookup failed (user not found)"
exit $ERR_NOUSER
else
log_result "lookup failed (credentials are invalid)"
exit $ERR_PERMFAIL
fi
fi
# vim:set ts=4 sts=4 sw=4 ai:
More information about the dovecot
mailing list