#!/opt/imh-python/bin/python3
"""Scans a cPanel account for WordPress and Joomla! configuration files, then
uses whmapi1 to reset database passwords to match what was found in them.
This script logs to /var/log/messages"""
from argparse import ArgumentParser
from logging.handlers import SysLogHandler
from cpapis import whmapi1, CpAPIError
WP_CONF = re.compile(r"DB_(USER|PASSWORD).*['\"](.+)['\"]")
JM_CONF = re.compile(r"\b(user|password) = '(.+)'")
def setup_logging() -> logging.Logger:
logger = logging.getLogger('cmspass.py')
logger.setLevel(logging.DEBUG)
out_fmt = logging.Formatter(fmt='%(levelname)s: %(message)s')
log_fmt = logging.Formatter(fmt='cmspass.py: %(levelname)s: %(message)s')
stdout = logging.StreamHandler(stream=sys.stdout)
stdout.setFormatter(out_fmt)
stdout.setLevel(logging.DEBUG)
logger.addHandler(stdout)
syslog = SysLogHandler(address='/dev/log')
syslog.setFormatter(log_fmt)
syslog.setLevel(logging.WARNING)
logger.addHandler(syslog)
regex: re.Pattern, path: Path
) -> tuple[Union[str, None], Union[str, None]]:
"""Will parse db conf variables"""
db_user, db_pass = None, None
with open(path, encoding='utf-8') as file:
if match := regex.search(line):
key, val = match.groups()
if key.lower() == 'user':
elif key.lower() == 'password':
def set_pass(logger: logging.Logger, cpuser: str, db_user: str, db_pass: str):
"""Will pass the reset variables to the cPanel API"""
if db_user is None or db_pass is None:
args={'cpuser': cpuser, 'user': db_user, 'password': db_pass},
except CpAPIError as exc:
logger.error("Failed to reset password for %s: %s", db_user, exc)
logger.warning("Reset password for %s", db_user)
"""Will parse input for user and ensure the user exists"""
parser = ArgumentParser(description=__doc__)
parser.add_argument('-u', '--user', required=True, help='cPanel username')
user = parser.parse_args().user
if not rads.cpuser_safe(user):
sys.exit(f"{user} does not exist or is restricted")
def find_files(homedir: str) -> list[Path]:
"""Find all config files"""
'find', homedir, '-not', '(', '-path', f"{homedir}/mail", '-prune', ')',
'(', '-name', 'wp-config.php', '-o', '-name', 'configuration.php', ')',
cmd, stdout=subprocess.PIPE, encoding='utf-8', check=False
return [Path(x) for x in ret.stdout.split('\0') if x]
def conf_is_joomla(path: Path):
if path.name != 'configuration.php':
with open(path, encoding='utf-8') as conf:
if 'class JConfig' in line:
homedir = rads.get_homedir(user)
except rads.CpuserError as exc:
for path in find_files(homedir):
db_user, db_pass = None, None
if path.name == 'wp-config.php':
logger.debug('Scanning %s', path)
db_user, db_pass = conf_parse(WP_CONF, path)
elif conf_is_joomla(path):
logger.debug('Scanning %s', path)
db_user, db_pass = conf_parse(JM_CONF, path)
set_pass(logger, user, db_user, db_pass)
if __name__ == "__main__":