@@ -0,0 +1,186 @@
#! /bin/bash
# Example Dovecot checkpassword script that may be used as both passdb or userdb.
#
# Originally written by Nikolay Vizovitin, 2013.
# 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
# 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 "
local cached=" $4 "
local ipfile=" $5 "
local ip=" $TCPREMOTEIP "
# local ip="$TCPLOCALIP"
# local ip="192.168.149.100"
local timestamp=" $( date +%s) "
# local defaultTime=300
local defaultTime=10
local expire=" "
if [ -f " $cached " ]; then
expire=` awk -F ' :' -v USER=" $user " -v IP=" $ip " -v PASS=" $pass " ' ($1 == USER && $2 == IP && $3 == PASS) {print $4}' " $cached " `
fi
if [ ! -z " $expire " ]; then
if [ " $timestamp " -gt " $expire " ]; then
# Remove from cache
sed -i " /$user :$ip :$pass /d" " $cached "
# echo "cached is old. fail log in"
log_result_basic " cached is old. fail log in"
else
# echo "cache is current. allow log on"
log_result_basic " cache is current. allow log on"
echo " true"
fi
else
if python2.7 /etc/dovecot/getTOTP.py -v ` awk -F ' :' -v USER=" $user " ' ($1 == USER) {print $2}' " $db " ` " $pass " | grep ' True' ;
then
# echo "allow log on"
log_result_basic " allow log on"
if [ -f " $ipfile " ]; then
timeforip=` awk -F ' :' -v IP=" $ip " ' ($1 == IP) {print $2}' " $ipfile " `
if [ -z " $timeforip " ]; then
# Use default time cache
# echo "Using default time"
log_result_basic " Using default time"
echo " $user :$ip :$pass :$(( $timestamp + $defaultTime )) " >> " $cached "
else
# echo "Using stored time"
log_result_basic " Using stored time"
echo " $user :$ip :$pass :$(( $timestamp + $timeforip )) " >> " $cached "
fi
else
# Use default time cache
# echo "Using default time"
log_result_basic " Using default time"
echo " $user :$ip :$pass :$(( $timestamp + $defaultTime )) " >> " $cached "
fi
echo " true"
fi
fi
}
# Just a simple logging helper.
log_result ()
{
echo " $* ; Input: $USER :$PASS ; Home: $HOME ; Reply binary: $CHECKPASSWORD_REPLY_BINARY " >> $LOG
}
# Just a simpler logging helper.
log_result_basic ()
{
echo " $* ; Input: $USER :$PASS " >> $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=` python2.7 /etc/dovecot/getHome.py $USER `
# CREDENTIALS_LOOKUP=1 environment is set when doing non-plaintext authentication.
if [ " $CREDENTIALS_LOOKUP " = 1 ]; then
action=credentials_lookup
else
action=credentials_verify
fi
# Perform credentials lookup/verification.
lookup_result=` $action " /etc/dovecot/users" " $USER " " $PASS " " /etc/dovecot/cached" " /etc/dovecot/ipfile" ` || {
# 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=vmail
export userdb_gid=vmail
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 ']"
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