ejabberdDovecotMySQLAuth/main.py

136 lines
5.0 KiB
Python

#!/usr/bin/python
#
#External auth script for ejabberd that enable auth against MySQL db with
#use of custom fields and table. It works with argon2i hashed passwords. Python 3
#Based on iltl. Contact: iltl@free.fr script.
#Released under GNU GPLv3
#Author: Humorhenker paul@roteserver.de
########################################################################
#DB Settings
########################################################################
db_name="my_db"
db_user="my_id"
db_pass="my_pass"
db_host="localhost"
db_table="my_table"
db_username_field="name"
db_password_field="pass"
db_domain_field=""
########################################################################
#Setup
########################################################################
import sys, logging, struct
import mysql.connector
from passlib.hash import argon2
import config as cfg
from struct import *
sys.stderr = open('/var/log/ejabberd/extauth_err.log', 'a')
logging.basicConfig(level=logging.INFO,
format='%(asctime)s %(levelname)s %(message)s',
filename='/var/log/ejabberd/extauth.log',
filemode='a')
try:
mysqlcnx = mysql.connector.connect(user=cfg.mysql['user'], password=cfg.mysql['password'], host='127.0.0.1', database=cfg.mysql['db'])
except:
logging.debug("Unable to initialize database, check settings!")
cursor = mysqlcnx.cursor()
logging.info('extauth script started, waiting for ejabberd requests')
class EjabberdInputError(Exception):
def __init__(self, value):
self.value = value
def __str__(self):
return repr(self.value)
########################################################################
#Declarations
########################################################################
def ejabberd_in():
logging.debug("trying to read 2 bytes from ejabberd:")
try:
input_length = sys.stdin.read(2)
except IOError:
logging.debug("ioerror")
if len(input_length) is not 2:
logging.debug("ejabberd sent us wrong things!")
raise EjabberdInputError('Wrong input from ejabberd!')
logging.debug('got 2 bytes via stdin: %s'%input_length)
(size,) = unpack('>h', input_length)
logging.debug('size of data: %i'%size)
income=sys.stdin.read(size).split(':')
logging.debug("incoming data: %s"%income)
return income
def ejabberd_out(bool):
logging.debug("Ejabberd gets: %s" % bool)
token = genanswer(bool)
logging.debug("sent bytes: %#x %#x %#x %#x" % (ord(token[0]), ord(token[1]), ord(token[2]), ord(token[3])))
sys.stdout.write(token)
sys.stdout.flush()
def genanswer(bool):
answer = 0
if bool:
answer = 1
token = pack('>hh', 2, answer)
return token
def db_entry(in_user, in_host):
ls=[None, None]
abfrage = "SELECT %(db_username_field)s, %(db_domain_field)s, %(db_password_field)s FROM %(db_table)s WHERE %(db_username_field)s LIKE %(in_user)s AND %(db_domain_field)s LIKE %(in_host)s"
abfrage_data = {'db_username_field': db_username_field, 'db_domain_field': db_domain_field, 'db_password_field': db_password_field, 'db_table': db_table,'db_username_field': db_username_field, 'in_user': in_user, 'db_domain_field': db_domain_field, 'in_host': in_host}
cursor.execute(abfrage, abfrage_data)
return cursor.fetchone()[0]
def isuser(in_user, in_host):
data=db_entry(in_user, in_host)
out=False #defaut to O preventing mistake
if data==None:
out=False
logging.debug("Wrong username: %s"%(in_user))
if in_user+"@"+in_host==data[0]+"@"+data[1]:
out=True
return out
def auth(in_user, in_host, password):
data=db_entry(in_user, in_host)
out=False #defaut to O preventing mistake
if data==None:
out=False
logging.debug("Wrong username: %s"%(in_user))
if in_user+"@"+in_host==data[0]+"@"+data[1]:
if argon2.verify(password, data[2])
out=True
else:
logging.debug("Wrong password for user: %s"%(in_user))
out=False
else:
out=False
return out
def log_result(op, in_user, bool):
if bool:
logging.info("%s successful for %s"%(op, in_user))
else:
logging.info("%s unsuccessful for %s"%(op, in_user))
########################################################################
#Main Loop
########################################################################
while True:
logging.debug("start of infinite loop")
try:
ejab_request = ejabberd_in()
except EjabberdInputError as inst:
logging.info("Exception occured: %s", inst)
break
logging.debug('operation: %s'%(ejab_request[0]))
op_result = False
if ejab_request[0] == "auth":
op_result = auth(ejab_request[1], ejab_request[2], ejab_request[3])
ejabberd_out(op_result)
log_result(ejab_request[0], ejab_request[1], op_result)
elif ejab_request[0] == "isuser":
op_result = isuser(ejab_request[1], ejab_request[2])
ejabberd_out(op_result)
log_result(ejab_request[0], ejab_request[1], op_result)
elif ejab_request[0] == "setpass":
op_result=False
ejabberd_out(op_result)
log_result(ejab_request[0], ejab_request[1], op_result)
logging.debug("end of infinite loop")
logging.info('extauth script terminating')
database.close()