#!/usr/bin/env python3 import os, cgi, MySQLdb, subprocess, time # settings DB_NAME = 'vmail' DB_USER = 'vmail' DB_PW = '{{postfix.dovecot.mysql_password}}' # DB stuff db = MySQLdb.connect(user=DB_USER, passwd=DB_PW, db=DB_NAME, charset='utf8') def db_execute(query, args): cursor = db.cursor(MySQLdb.cursors.DictCursor) if len(args) == 1 and isinstance(args[0], dict): args = args[0] # einzelnes dict weiterreichen cursor.execute(query, args) return cursor def db_fetch_one_row(query, *args): cursor = db_execute(query, args) result = cursor.fetchone() if cursor.fetchone() is not None: raise Exception("Found more than one result, only one was expected") cursor.close() return result def db_run(query, *args): cursor = db_execute(query, args) cursor.close() def get_user(name): return db_fetch_one_row('SELECT * FROM users WHERE username=%s', name) def change_user_pw(name, hash): db_run('UPDATE users SET password=%s WHERE username=%s', hash, name) def change_user_active(name, active): db_run('UPDATE users SET active=%s WHERE username=%s', int(active), name) # interaction with dbadm pw def compare_pw(hash, plain): try: subprocess.check_output(["doveadm", "pw", "-t", hash, "-p", plain]) return True except subprocess.CalledProcessError: return False def gen_hash(plain): return subprocess.check_output(["doveadm", "pw", "-s", "SHA512-CRYPT", "-r", str(64*1024), "-p", plain]).decode('utf-8').strip() # the core action def act(user, oldpw, newpw1, newpw2): if user is None and oldpw is None and newpw1 is None and newpw2 is None: return if user is None or oldpw is None or newpw1 is None or newpw2 is None: return "Error: You have to fill all the fields." curdata = get_user(user) if curdata is None: return "Error: User not found." if len(newpw1) < 8: return "Error: Password must be at least 8 characters long." if newpw1 != newpw2: return "Error: New passwords do not match." # slow down brute-force attacks time.sleep(2.5) # now go on if not compare_pw(curdata['password'], oldpw): return "Error: Old PW is wrong." new_hash = gen_hash(newpw1) change_user_pw(user, new_hash) # potentially enable this user if curdata['active'] < 0: change_user_active(user, -curdata['active']) return "Password successfully changed." # print headers print("Content-Type: text/html") print() # print document header print("") print("") print(" ") print(" User PW change") print(" ") print("") # do stuff form = cgi.FieldStorage() msg = act(form.getfirst('user'), form.getfirst('oldpw'), form.getfirst('newpw1'), form.getfirst('newpw2')) if msg is not None: print("

{}

".format(msg)) # print form print("
") print(" ") print(" ") print(" ") print(" ") print(" ") print("
Username:
Old Password:
New Password:
New Password (confirmation):
") print("")