Last active
September 15, 2021 07:22
-
-
Save umrysh/e26ec78bf8428ad4e279 to your computer and use it in GitHub Desktop.
Time-based One-time Passwords for Email
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Built for python 2.7 | |
import MySQLdb as mdb | |
import sys | |
host = "127.0.0.1" | |
database = "vmail" | |
username = "vmail" | |
password = "VeNUlA6xKjpfceGsN3Ull8hLmAVjc3" | |
def main(): | |
if len(sys.argv) == 2: | |
con = mdb.connect(host=host, port=3306,user=username, passwd=password, db=database) | |
cur = con.cursor() | |
cur.execute("SELECT CONCAT(mailbox.storagebasedirectory, '/', mailbox.storagenode, '/', mailbox.maildir) AS home FROM mailbox where username = '%s'" % con.escape_string(sys.argv[1])) | |
row = cur.fetchone() | |
if row is not None: | |
sys.stdout.write("%s" % row[0]) | |
else: | |
sys.stdout.write("") | |
else: | |
sys.stdout.write("") | |
sys.stdout.flush() | |
sys.exit(0) | |
if __name__ == "__main__": | |
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Built for python 2.7 | |
import onetimepass as otp | |
import sys,base64,os | |
def main(): | |
if len(sys.argv) >= 2: | |
if sys.argv[1] == "-v": | |
sys.stdout.write("%s" % otp.valid_totp(token=sys.argv[3], secret=sys.argv[2])) | |
elif sys.argv[1] == "-c": | |
try: | |
sys.stdout.write("%s" % otp.get_totp(sys.argv[2])) | |
except: | |
sys.stdout.write("") | |
elif sys.argv[1] == "-g": | |
sys.stdout.write("%s" % base64.b32encode(os.urandom(10))) | |
else: | |
sys.stdout.write("") | |
sys.stdout.flush() | |
sys.exit(0) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment