#!/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()