#!/bin/bash

# Wazuh installer
# Copyright (C) Wazuh Inc.
#
# This program is a free software; you can redistribute it
# and/or modify it under the terms of the GNU General Public
# License (version 2) as published by the FSF - Free Software
# Foundation.
indexer_config_path="/etc/wazuh-indexer"
manager_config_path="/etc/wazuh-manager"
dashboard_config_path="/etc/wazuh-dashboard"
adminpem="/etc/wazuh-indexer/certs/admin.pem"
adminkey="/etc/wazuh-indexer/certs/admin-key.pem"
manager_keystore="/var/wazuh-manager/bin/wazuh-manager-keystore"
manager_resolver="/var/wazuh-manager/bin/wazuh-manager-resolve-credentials"
base_path="$(dirname "$(readlink -f "$0")")"
readonly logfile="/var/log/wazuh-passwords-tool.log"
debug=">> ${logfile} 2>&1"
restart_manager=""
restart_dashboard=""
manager_keystore_updated=""
dashboard_keystore_updated=""
save_failed=""
indexer_accounts=("admin" "kibanaserver" "wazuh-manager")
api_accounts=("wazuh" "wazuh-internal-client")
hash_tool="/usr/share/wazuh-indexer/plugins/opensearch-security/tools/hash.sh"
rbac_control="/var/wazuh-manager/bin/rbac_control"
dashboard_keystore="/usr/share/wazuh-dashboard/bin/opensearch-dashboards-keystore"
dashboard_user="wazuh-dashboard"

# ------------ passwordsFunctions.sh ------------ 
function passwords_changePassword() {

    if [ -n "${changeall}" ]; then
        if [ -n "${indexer_installed}" ]; then
            eval "mkdir /etc/wazuh-indexer/backup/ ${debug}"
            eval "cp /etc/wazuh-indexer/opensearch-security/* /etc/wazuh-indexer/backup/ ${debug}"
            passwords_createBackUp
        fi

        for i in "${!passwords[@]}"; do
            if [ -n "${indexer_installed}" ] && [ -f "/etc/wazuh-indexer/backup/internal_users.yml" ]; then
                passwords_replaceHash "${users[i]}" "${hashes[i]}"
            fi

            if [ "${users[i]}" == "wazuh-manager" ]; then
                managerpass=${passwords[i]}
            elif [ "${users[i]}" == "kibanaserver" ]; then
                dashpass=${passwords[i]}
            fi
        done
    else
        if [ -z "${api}" ] && [ -n "${indexer_installed}" ]; then
            eval "mkdir /etc/wazuh-indexer/backup/ ${debug}"
            eval "cp /etc/wazuh-indexer/opensearch-security/* /etc/wazuh-indexer/backup/ ${debug}"
            passwords_createBackUp
        fi

        if [ -n "${indexer_installed}" ] && [ -f "/etc/wazuh-indexer/backup/internal_users.yml" ]; then
            passwords_replaceHash "${nuser}" "${hash}"
        fi

        if [ "${nuser}" == "wazuh-manager" ]; then
            managerpass=${password}
        elif [ "${nuser}" == "kibanaserver" ]; then
            dashpass=${password}
        fi
    fi

}
function passwords_updateKeystores() {

    local keystore_failed=""

    if [ "${nuser}" == "wazuh-manager" ] || [ -n "${changeall}" ]; then
        if [ -n "${wazuh_installed}" ]; then
            if [ -n "${managerpass}" ]; then
                if passwords_updateManagerKeystore "${managerpass}"; then
                    manager_keystore_updated=1
                    restart_manager=1
                    common_logger -w "If this is a multi-node deployment, update the keystore of every other Wazuh manager node and restart them."
                else
                    common_logger -e "The Wazuh indexer already uses the new password of user wazuh-manager, but the Wazuh manager keystore still holds the previous one. Write the new password (WAZUH_INDEXER_MANAGER_PASSWORD in ${indexer_config_path}/export/credentials.env, or the one you set with -p) with: echo 'wazuh-manager' | ${manager_keystore} -f indexer -k username; echo '<WAZUH_INDEXER_MANAGER_PASSWORD>' | ${manager_keystore} -f indexer -k password. Then restart the Wazuh manager."
                    keystore_failed=1
                fi
            else
                common_logger -w "Skipping Wazuh manager keystore update: no password available for the wazuh-manager user."
            fi
        elif [ -n "${managerpass}" ]; then
            common_logger -w "The Wazuh manager is not installed on this host. Update the indexer password in the keystore of every Wazuh manager node and restart them."
        fi
    fi

    if [ "${nuser}" == "kibanaserver" ] || [ -n "${changeall}" ]; then
        if [ -n "${dashboard_installed}" ] && [ -n "${dashpass}" ]; then
            if passwords_updateDashboardKeystore "opensearch.password" "${dashpass}"; then
                dashboard_keystore_updated=1
                restart_dashboard=1
                common_logger -w "If this is a multi-node deployment, update opensearch.password in the keystore of every other Wazuh dashboard node and restart them."
            else
                common_logger -e "The Wazuh indexer already uses the new password of user kibanaserver, but the Wazuh dashboard keystore still holds the previous one. Write the new password (WAZUH_INDEXER_KIBANASERVER_PASSWORD in ${indexer_config_path}/export/credentials.env, or the one you set with -p) with: echo '<WAZUH_INDEXER_KIBANASERVER_PASSWORD>' | OSD_PATH_CONF=${dashboard_config_path} ${dashboard_keystore} --allow-root add opensearch.password --stdin --force. Then restart the Wazuh dashboard."
                keystore_failed=1
            fi
        elif [ -n "${dashpass}" ]; then
            common_logger -w "The Wazuh dashboard is not installed on this host. Update opensearch.password in the keystore of every Wazuh dashboard node and restart them."
        fi
    fi

    if [ -n "${keystore_failed}" ]; then
        return 1
    fi

}
function passwords_changePasswordApi() {

    if [ -n "${changeall}" ]; then
        for i in "${!api_users[@]}"; do
            if ! passwords_changeApiUserPassword "${api_users[i]}" "${api_passwords[i]}" 1; then
                return 1
            fi
        done
    else
        if ! passwords_changeApiUserPassword "${nuser}" "${password}" "${autopass}"; then
            return 1
        fi
    fi

}
function passwords_changeApiUserPassword() {

    local user="${1}"
    local new_password="${2}"
    local generated="${3}"
    local rbac_output
    local rbac_status

    if [ ! -x "${rbac_control}" ]; then
        common_logger -e "Cannot find ${rbac_control}. The password of the Wazuh API user ${user} was not changed."
        return 1
    fi

    # rbac_control reads the new password from the standard input and never prints it.
    rbac_output=$(printf '%s\n' "${new_password}" | "${rbac_control}" change-password -u "${user}" -p - 2>&1)
    rbac_status=$?
    if [ "${rbac_status}" -ne 0 ] || ! grep -Eq "^[[:space:]]*${user}: UPDATED$" <<< "${rbac_output}"; then
        common_logger -e "The password of the Wazuh API user ${user} could not be changed: $(echo "${rbac_output}" | tr -s '\n\t' '  ')"
        return 1
    fi
    common_logger "The password of the Wazuh API user ${user} was changed."

    passwords_saveCredential "${user}" "${new_password}" "${generated}"

    if [ "${user}" == "wazuh-internal-client" ]; then
        if [ -n "${dashboard_installed}" ]; then
            if ! passwords_updateDashboardKeystore "wazuh_core.hosts.default.password" "${new_password}"; then
                return 1
            fi
            dashboard_keystore_updated=1
            restart_dashboard=1
            common_logger -w "If this is a multi-node deployment, update wazuh_core.hosts.default.password in the keystore of every other Wazuh dashboard node and restart them."
        else
            common_logger -w "The Wazuh dashboard is not installed on this host. Update wazuh_core.hosts.default.password in the keystore of every Wazuh dashboard node and restart them."
        fi
    fi

}
function passwords_checkUser() {

    if passwords_isInList "${nuser}" "${api_accounts[@]}"; then
        if [ -z "${wazuh_installed}" ]; then
            common_logger -e "The Wazuh manager is not installed on this host, so the password of the Wazuh API user ${nuser} cannot be changed here."
            exit 1;
        fi
        api=1
        return 0
    fi

    if passwords_isInList "${nuser}" "${indexer_accounts[@]}"; then
        if [ -z "${indexer_installed}" ]; then
            common_logger -e "The Wazuh indexer is not installed on this host, so the password of the Wazuh indexer user ${nuser} cannot be changed here."
            exit 1;
        fi
        if ! passwords_isInList "${nuser}" "${users[@]}"; then
            common_logger -e "The given user does not exist"
            exit 1;
        fi
        return 0
    fi

    common_logger -e "The user ${nuser} is not supported. Supported users: ${indexer_accounts[*]} ${api_accounts[*]}."
    exit 1;

}
function passwords_checkCredentialsFile() {

    local file env_status

    for file in "${indexer_config_path}/local.env" "${indexer_config_path}/export/credentials.env" \
        "${manager_config_path}/local.env" "${manager_config_path}/export/credentials.env" \
        "${dashboard_config_path}/local.env" $(passwords_getDeploymentFile); do
        env_status=0
        common_envGet "${file}" WAZUH_INDEXER_ADMIN_PASSWORD > /dev/null || env_status=$?
        if [ "${env_status}" -eq 2 ]; then
            common_logger -e "The credentials file ${file} cannot be used, so no password was changed. Fix the problem shown above and run the tool again."
            exit 1;
        fi
    done

}
function passwords_checkPassword() {

    local validation_error

    # Same rule as the Wazuh packages.
    if ! validation_error=$(common_passwordValidate "${1}" 2>&1); then
        common_logger -e "Invalid password: ${validation_error}."
        exit 1
    fi

}
function passwords_createBackUp() {

    if [ -z "${indexer_installed}" ] && [ -z "${dashboard_installed}" ]; then
        common_logger -e "Cannot find Wazuh indexer or Wazuh dashboard on the system."
        passwords_removeBackUp
        exit 1;
    else
        if [ -n "${indexer_installed}" ]; then
            capem=$(grep "plugins.security.ssl.transport.pemtrustedcas_filepath: " /etc/wazuh-indexer/opensearch.yml )
            rcapem="plugins.security.ssl.transport.pemtrustedcas_filepath: "
            capem="${capem//$rcapem}"
        fi
    fi

    common_logger -d "Creating passwords backup."
    if [ ! -d "/etc/wazuh-indexer/backup" ]; then
        eval "mkdir /etc/wazuh-indexer/backup ${debug}"
    fi
    eval "JAVA_HOME=/usr/share/wazuh-indexer/jdk/ OPENSEARCH_CONF_DIR=/etc/wazuh-indexer /usr/share/wazuh-indexer/plugins/opensearch-security/tools/securityadmin.sh -backup /etc/wazuh-indexer/backup -icl -p 9200 -nhnv -cacert ${capem} -cert ${adminpem} -key ${adminkey} -h ${IP} ${debug}; e_code=\${PIPESTATUS[0]}"
    if [ "${e_code}" != 0 ]; then
        common_logger -e "The backup could not be created"
        passwords_removeBackUp
        if [[ $(type -t installCommon_rollBack) == "function" ]]; then
            installCommon_rollBack
        fi
        exit 1;
    fi
    common_logger -d "Passwords backup created in /etc/wazuh-indexer/backup."

}
function passwords_removeBackUp() {

    eval "rm -rf /etc/wazuh-indexer/backup/ ${debug}"

}
function passwords_generateHash() {

    if [ -n "${changeall}" ]; then
        common_logger -d "Generating password hashes."
        hashes=()
        for i in "${!passwords[@]}"; do
            nhash=$(passwords_hashPassword "${passwords[i]}")
            if [ -z "${nhash}" ]; then
                common_logger -e "Hash generation failed."
                if [[ $(type -t installCommon_rollBack) == "function" ]]; then
                    installCommon_rollBack
                fi
                exit 1;
            fi
            hashes+=("${nhash}")
        done
        common_logger -d "Password hashes generated."
    else
        common_logger "Generating password hash"
        hash=$(passwords_hashPassword "${password}")
        if [ -z "${hash}" ]; then
            common_logger -e "Hash generation failed."
            if [[ $(type -t installCommon_rollBack) == "function" ]]; then
                installCommon_rollBack
            fi
            exit 1;
        fi
        common_logger -d "Password hash generated."
    fi

}
function passwords_generatePassword() {

    common_logger -d "Generating random password."
    if ! password=$(common_passwordGenerate); then
        common_logger -e "The password could not been generated."
        exit 1;
    fi

}
function passwords_generatePasswords() {

    common_logger -d "Generating random passwords."
    passwords=()
    api_passwords=()

    for i in "${!users[@]}"; do
        passwords_generatePassword
        passwords+=("${password}")
    done

    for i in "${!api_users[@]}"; do
        passwords_generatePassword
        api_passwords+=("${password}")
    done

    password=""

}
function passwords_getEnvKey() {

    case "${1}" in
        "admin") echo "WAZUH_INDEXER_ADMIN_PASSWORD" ;;
        "kibanaserver") echo "WAZUH_INDEXER_KIBANASERVER_PASSWORD" ;;
        "wazuh-manager") echo "WAZUH_INDEXER_MANAGER_PASSWORD" ;;
        "wazuh") echo "WAZUH_MANAGER_API_PASSWORD" ;;
        "wazuh-internal-client") echo "WAZUH_MANAGER_WUI_PASSWORD" ;;
        *) return 1 ;;
    esac

}
function passwords_getNetworkHost() {

    IP=$(grep -hr "^network.host:" /etc/wazuh-indexer/opensearch.yml)
    NH="network.host: "
    IP="${IP//$NH}"

    # Remove surrounding double quotes if present
    IP="${IP//\"}"

    #allow to find ip with an interface
    if [[ ${IP} =~ _.*_ ]]; then
        interface="${IP//_}"
        IP=$(ip -o -4 addr list "${interface}" | awk '{print $4}' | cut -d/ -f1)
    fi

    if [ "${IP}" == "0.0.0.0" ]; then
        IP="localhost"
    fi
}
function passwords_hashPassword() {

    WAZUH_PASSWORDS_TOOL_SECRET="${1}" OPENSEARCH_JAVA_HOME="/usr/share/wazuh-indexer/jdk" bash "${hash_tool}" -env WAZUH_PASSWORDS_TOOL_SECRET 2>/dev/null | grep -Eo '^\$2[aby]\$[0-9]{2}\$[./A-Za-z0-9]{53}$' | tail -n 1

}
function passwords_isInList() {

    local item="${1}"
    local element
    shift

    for element in "$@"; do
        if [ "${element}" == "${item}" ]; then
            return 0
        fi
    done
    return 1

}
function passwords_readUsers() {

    passwords_updateInternalUsers
    users=()
    for account in "${indexer_accounts[@]}"; do
        if grep -q "^${account}:$" /etc/wazuh-indexer/opensearch-security/internal_users.yml; then
            users+=("${account}")
        fi
    done

}
function passwords_isServiceActive() {

    if [ "$#" -ne 1 ]; then
        common_logger -e "passwords_isServiceActive must be called with 1 argument."
        return 1
    fi

    local service_name="${1}"

    # wazuh-manager is Type=forking with RemainAfterExit=yes and no PIDFile,
    # so systemd (and the equivalent SysV init status action) only require
    # *some* forked process from the unit's cgroup to still be alive to keep
    # reporting it as active/running -- e.g. wazuh-manager-apid on its own,
    # even if the core daemons (analysisd, remoted, etc.) already crashed.
    # Checking wazuh-manager-control status reflects each daemon's real
    # state instead.
    if [ "${service_name}" == "wazuh-manager" ] && [ -x /var/wazuh-manager/bin/wazuh-manager-control ]; then
        # Daemons enabled by default on every install, per the DAEMONS list
        # in wazuh/wazuh's src/init/wazuh-server.sh on 5.0.0. wazuh-manager-
        # clusterd and wazuh-manager-authd are optional/configurable, so they
        # are intentionally left out of this check. wazuh-manager-execd,
        # wazuh-manager-syscheckd, wazuh-manager-logcollector and
        # wazuh-manager-monitord no longer exist as separate daemons in
        # 5.0.0 (#992) -- they are not in that DAEMONS list either.
        manager_core_daemons=(wazuh-manager-db wazuh-manager-analysisd wazuh-manager-remoted wazuh-manager-modulesd wazuh-manager-apid)
        manager_status=$(/var/wazuh-manager/bin/wazuh-manager-control status 2>/dev/null)
        for manager_daemon in "${manager_core_daemons[@]}"; do
            if ! echo "${manager_status}" | grep -q "^${manager_daemon} is running"; then
                return 1
            fi
        done
        return 0
    fi

    if [[ -d /run/systemd/system ]]; then
        # Check if service is active using systemctl
        if systemctl is-active --quiet "${service_name}.service" 2>/dev/null; then
            return 0
        else
            return 1
        fi
    elif ps -p 1 -o comm= | grep "init"; then
        # Check service status for init systems
        if /etc/init.d/"${service_name}" status >/dev/null 2>&1; then
            return 0
        else
            return 1
        fi
    elif [ -x "/etc/rc.d/init.d/${service_name}" ]; then
        # Check service status for rc.d systems
        if /etc/rc.d/init.d/"${service_name}" status >/dev/null 2>&1; then
            return 0
        else
            return 1
        fi
    else
        common_logger -w "Cannot determine service status. No service manager found on the system."
        return 1
    fi

}
function passwords_readPassword() {

    local password_confirmation

    if [ -t 0 ]; then
        IFS= read -r -s -p "New password for user ${nuser}: " password
        echo >&2
        IFS= read -r -s -p "Repeat the new password: " password_confirmation
        echo >&2
        if [ "${password}" != "${password_confirmation}" ]; then
            common_logger -e "The passwords do not match."
            exit 1;
        fi
    else
        IFS= read -r password
    fi

    if [ -z "${password}" ]; then
        common_logger -e "No password was given on the standard input."
        exit 1;
    fi

}
function passwords_replaceHash() {

    WAZUH_PASSWORDS_TOOL_HASH="${2}" awk -v user="${1}:" 'prev==user{sub(/\042.*/,""); $0=$0 "\"" ENVIRON["WAZUH_PASSWORDS_TOOL_HASH"] "\""} {prev=$1} 1' /etc/wazuh-indexer/backup/internal_users.yml > internal_users.yml_tmp && mv -f internal_users.yml_tmp /etc/wazuh-indexer/backup/internal_users.yml

}
function passwords_restartPendingServices() {

    if [ -n "${restart_manager}" ]; then
        if passwords_isServiceActive "wazuh-manager"; then
            passwords_restartService "wazuh-manager"
        elif [ -n "${manager_keystore_updated}" ]; then
            common_logger -w "The Wazuh manager keystore was updated, but the wazuh-manager service is not running. The restart is pending: the new Wazuh indexer credentials will be applied when the service starts."
        else
            common_logger -w "wazuh-manager service is not running. Skipping restart."
        fi
    fi

    if [ -n "${restart_dashboard}" ]; then
        if passwords_isServiceActive "wazuh-dashboard"; then
            passwords_restartService "wazuh-dashboard"
        elif [ -n "${dashboard_keystore_updated}" ]; then
            common_logger -w "The Wazuh dashboard keystore was updated, but the wazuh-dashboard service is not running. The restart is pending: the new credentials will be applied when the service starts."
        else
            common_logger -w "wazuh-dashboard service is not running. Skipping restart."
        fi
    fi

}
function passwords_restartService() {

    common_logger -d "Restarting ${1} service..."
    if [ "$#" -ne 1 ]; then
        common_logger -e "passwords_restartService must be called with 1 argument."
        exit 1
    fi

    if [[ -d /run/systemd/system ]]; then
        eval "systemctl daemon-reload ${debug}"
        eval "systemctl restart ${1}.service ${debug}; e_code=\${PIPESTATUS[0]}"
        if [  "${e_code}" != 0  ]; then
            common_logger -e "${1} could not be started."
            if [ -n "$(command -v journalctl)" ]; then
                eval "journalctl -u ${1} >> ${logfile}"
            fi
            if [[ $(type -t installCommon_rollBack) == "function" ]]; then
                installCommon_rollBack
            fi
            exit 1;
        else
            # systemctl restart can return success immediately even if the
            # service later fails to initialize (e.g. under resource
            # pressure). Poll is-active for a short window to catch that
            # before moving on, instead of only checking the restart call
            # itself.
            restart_check_retries=12
            restart_check_delay=5
            restart_check_attempt=0
            service_is_active=""

            while [ "${restart_check_attempt}" -lt "${restart_check_retries}" ]; do
                if passwords_isServiceActive "${1}"; then
                    service_is_active="true"
                    break
                fi
                restart_check_attempt=$((restart_check_attempt+1))
                sleep "${restart_check_delay}"
            done

            if [ -z "${service_is_active}" ]; then
                common_logger -e "${1} restarted but did not stay active (checked for $((restart_check_retries * restart_check_delay))s). It may have failed to initialize, for example due to insufficient resources."
                if [ -n "$(command -v journalctl)" ]; then
                    eval "journalctl -u ${1} >> ${logfile}"
                fi
                if [[ $(type -t installCommon_rollBack) == "function" ]]; then
                    installCommon_rollBack
                fi
                exit 1;
            else
                common_logger -d "${1} started."
            fi
        fi
    elif ps -p 1 -o comm= | grep "init"; then
        eval "/etc/init.d/${1} restart ${debug}; e_code=\${PIPESTATUS[0]}"
        if [  "${e_code}" != 0  ]; then
            common_logger -e "${1} could not be started."
            if [ -n "$(command -v journalctl)" ]; then
                eval "journalctl -u ${1} >> ${logfile}"
            fi
            if [[ $(type -t installCommon_rollBack) == "function" ]]; then
                installCommon_rollBack
            fi
            exit 1;
        else
            # Same rationale as the systemd branch above: the restart command
            # can return success immediately even if the service later fails
            # to initialize. Poll the init script's own status action for a
            # short window before considering the restart actually successful.
            restart_check_retries=12
            restart_check_delay=5
            restart_check_attempt=0
            service_is_active=""

            while [ "${restart_check_attempt}" -lt "${restart_check_retries}" ]; do
                if passwords_isServiceActive "${1}"; then
                    service_is_active="true"
                    break
                fi
                restart_check_attempt=$((restart_check_attempt+1))
                sleep "${restart_check_delay}"
            done

            if [ -z "${service_is_active}" ]; then
                common_logger -e "${1} restarted but did not stay active (checked for $((restart_check_retries * restart_check_delay))s). It may have failed to initialize, for example due to insufficient resources."
                if [ -n "$(command -v journalctl)" ]; then
                    eval "journalctl -u ${1} >> ${logfile}"
                fi
                if [[ $(type -t installCommon_rollBack) == "function" ]]; then
                    installCommon_rollBack
                fi
                exit 1;
            else
                common_logger -d "${1} started."
            fi
        fi
    elif [ -x "/etc/rc.d/init.d/${1}" ] ; then
        eval "/etc/rc.d/init.d/${1} restart ${debug}; e_code=\${PIPESTATUS[0]}"
        if [  "${e_code}" != 0  ]; then
            common_logger -e "${1} could not be started."
            if [ -n "$(command -v journalctl)" ]; then
                eval "journalctl -u ${1} >> ${logfile}"
            fi
            if [[ $(type -t installCommon_rollBack) == "function" ]]; then
                installCommon_rollBack
            fi
            exit 1;
        else
            # Same rationale as the systemd branch above: poll the init
            # script's own status action for a short window before
            # considering the restart actually successful.
            restart_check_retries=12
            restart_check_delay=5
            restart_check_attempt=0
            service_is_active=""

            while [ "${restart_check_attempt}" -lt "${restart_check_retries}" ]; do
                if passwords_isServiceActive "${1}"; then
                    service_is_active="true"
                    break
                fi
                restart_check_attempt=$((restart_check_attempt+1))
                sleep "${restart_check_delay}"
            done

            if [ -z "${service_is_active}" ]; then
                common_logger -e "${1} restarted but did not stay active (checked for $((restart_check_retries * restart_check_delay))s). It may have failed to initialize, for example due to insufficient resources."
                if [ -n "$(command -v journalctl)" ]; then
                    eval "journalctl -u ${1} >> ${logfile}"
                fi
                if [[ $(type -t installCommon_rollBack) == "function" ]]; then
                    installCommon_rollBack
                fi
                exit 1;
            else
                common_logger -d "${1} started."
            fi
        fi
    else
        if [[ $(type -t installCommon_rollBack) == "function" ]]; then
            installCommon_rollBack
        fi
        common_logger -e "${1} could not start. No service manager found on the system."
        exit 1;
    fi

}
function passwords_runSecurityAdmin() {

    common_logger -d "Running security admin tool."
    if [ -z "${indexer_installed}" ] && [ -z "${dashboard_installed}" ]; then
        common_logger -e "Cannot find Wazuh indexer or Wazuh dashboard on the system."
        passwords_removeBackUp
        exit 1;
    else
        if [ -n "${indexer_installed}" ]; then
            capem=$(grep "plugins.security.ssl.transport.pemtrustedcas_filepath: " /etc/wazuh-indexer/opensearch.yml )
            rcapem="plugins.security.ssl.transport.pemtrustedcas_filepath: "
            capem="${capem//$rcapem}"
        fi
    fi

    common_logger -d "Loading new passwords changes."
    eval "OPENSEARCH_CONF_DIR=/etc/wazuh-indexer /usr/share/wazuh-indexer/plugins/opensearch-security/tools/securityadmin.sh -f /etc/wazuh-indexer/backup/internal_users.yml -t internalusers -p 9200 -nhnv -cacert ${capem} -cert ${adminpem} -key ${adminkey} -icl -h ${IP} ${debug}; e_code=\${PIPESTATUS[0]}"
    if [  "${e_code}" != 0  ]; then
        common_logger -e "Could not load the changes."
        passwords_removeBackUp
        exit 1;
    fi
    cp /etc/wazuh-indexer/backup/internal_users.yml /etc/wazuh-indexer/opensearch-security/internal_users.yml
    passwords_removeBackUp

    if [[ -n "${nuser}" ]]; then
        common_logger "The password of the Wazuh indexer user ${nuser} was changed."
    fi

    if [ -n "${changeall}" ]; then
        common_logger "The passwords of the Wazuh indexer users were changed."
    fi

}
function passwords_getCredentialFiles() {

    case "${1}" in
        "WAZUH_INDEXER_ADMIN_PASSWORD")
            echo "${indexer_config_path}/export/credentials.env ${indexer_config_path}/local.env" ;;
        "WAZUH_INDEXER_KIBANASERVER_PASSWORD")
            echo "${indexer_config_path}/export/credentials.env ${indexer_config_path}/local.env ${dashboard_config_path}/local.env" ;;
        "WAZUH_INDEXER_MANAGER_PASSWORD")
            echo "${indexer_config_path}/export/credentials.env ${indexer_config_path}/local.env ${manager_config_path}/local.env" ;;
        "WAZUH_MANAGER_API_PASSWORD")
            echo "${manager_config_path}/export/credentials.env ${manager_config_path}/local.env" ;;
        "WAZUH_MANAGER_WUI_PASSWORD")
            echo "${manager_config_path}/export/credentials.env ${manager_config_path}/local.env ${dashboard_config_path}/local.env" ;;
    esac

}
function passwords_getDeploymentFile() {

    local ca_dir

    if ca_dir=$(common_caGetDir 2>/dev/null); then
        echo "${ca_dir}/credentials.env"
    fi

}
function passwords_getOwnerFile() {

    case "${1}" in
        WAZUH_INDEXER_*) echo "${indexer_config_path}/local.env" ;;
        WAZUH_MANAGER_*) echo "${manager_config_path}/local.env" ;;
        *) return 1 ;;
    esac

}
function passwords_checkOwnerDirs() {

    local user key dir
    local -a to_change=("${users[@]}" "${api_users[@]}")

    if [ -n "${nuser}" ]; then
        to_change=("${nuser}")
    fi

    for user in "${to_change[@]}"; do
        key=$(passwords_getEnvKey "${user}") || continue
        dir=$(dirname "$(passwords_getOwnerFile "${key}")")
        if [ ! -d "${dir}" ]; then
            common_logger -e "${dir} does not exist, so the new password of user ${user} could not be saved. No password was changed."
            exit 1;
        fi
    done

}
function passwords_saveCredential() {

    local user="${1}"
    local new_password="${2}"
    local generated="${3}"
    local key file
    local -a saved=()

    if ! key=$(passwords_getEnvKey "${user}"); then
        return 0
    fi

    for file in $(passwords_getCredentialFiles "${key}") $(passwords_getDeploymentFile); do
        common_envGet "${file}" "${key}" > /dev/null 2>&1 || continue
        if ! common_envSet "${file}" "${key}" "${new_password}"; then
            common_logger -e "The new password of user ${user} was applied, but it could not be saved in ${file}. Run the tool again for this user with -p to set a password you know."
            save_failed=1
            return 1
        fi
        saved+=("${file}")
    done

    if [ "${#saved[@]}" -eq 0 ]; then
        file=$(passwords_getOwnerFile "${key}")
        if ! common_envSet "${file}" "${key}" "${new_password}"; then
            common_logger -e "The new password of user ${user} was applied, but ${file} could not be created to save it. Run the tool again for this user with -p to set a password you know."
            save_failed=1
            return 1
        fi
        saved+=("${file} (created)")
    fi

    if [ -n "${generated}" ]; then
        common_logger "The new password of user ${user} was saved as ${key} in: ${saved[*]}."
    else
        common_logger "${key} was updated in: ${saved[*]}."
    fi

}
function passwords_saveIndexerCredentials() {

    if [ -n "${changeall}" ]; then
        for i in "${!users[@]}"; do
            passwords_saveCredential "${users[i]}" "${passwords[i]}" 1
        done
    else
        passwords_saveCredential "${nuser}" "${password}" "${autopass}"
    fi

}
function passwords_updateInternalUsers() {

    common_logger "Updating the internal users."
    backup_datetime=$(date +"%Y%m%d_%H%M%S")
    internal_users_backup_path="/etc/wazuh-indexer/internalusers-backup"
    passwords_getNetworkHost
    passwords_createBackUp

    eval "mkdir -p ${internal_users_backup_path} ${debug}"
    eval "cp /etc/wazuh-indexer/backup/internal_users.yml ${internal_users_backup_path}/internal_users_${backup_datetime}.yml.bkp ${debug}"
    eval "chmod 750 ${internal_users_backup_path} ${debug}"
    eval "chmod 640 ${internal_users_backup_path}/internal_users_${backup_datetime}.yml.bkp"
    eval "chown -R wazuh-indexer:wazuh-indexer ${internal_users_backup_path} ${debug}"
    common_logger "A backup of the internal users has been saved in the /etc/wazuh-indexer/internalusers-backup folder."

    eval "cp /etc/wazuh-indexer/backup/internal_users.yml /etc/wazuh-indexer/opensearch-security/internal_users.yml ${debug}"
    eval "rm -rf /etc/wazuh-indexer/backup/ ${debug}"
    common_logger -d "The internal users have been updated before changing the passwords."

}
function passwords_updateDashboardKeystore() {

    local keystore_file="${dashboard_config_path}/opensearch_dashboards.keystore"

    if [ "$#" -ne 2 ]; then
        common_logger -e "passwords_updateDashboardKeystore must be called with 2 arguments."
        return 1
    fi

    if [ ! -x "${dashboard_keystore}" ]; then
        common_logger -e "Cannot find ${dashboard_keystore}. ${1} was not written to the Wazuh dashboard keystore."
        return 1
    fi

    if ! printf '%s' "${2}" | (cd / && OSD_PATH_CONF="${dashboard_config_path}" "${dashboard_keystore}" --allow-root add -f --stdin "${1}") > /dev/null 2>&1; then
        common_logger -e "Could not write ${1} to the Wazuh dashboard keystore."
        return 1
    fi
    chown root:"${dashboard_user}" "${keystore_file}" && chmod 640 "${keystore_file}"

}
function passwords_updateManagerKeystore() {

    if [ "$#" -ne 1 ]; then
        common_logger -e "passwords_updateManagerKeystore must be called with 1 argument."
        return 1
    fi

    printf '%s\n' "wazuh-manager" | "${manager_keystore}" -f indexer -k username
    if [  "${PIPESTATUS[1]}" != 0  ]; then
        common_logger -e "Could not write the Wazuh indexer username to the Wazuh manager keystore."
        return 1
    fi

    printf '%s\n' "${1}" | "${manager_keystore}" -f indexer -k password
    if [  "${PIPESTATUS[1]}" != 0  ]; then
        common_logger -e "Could not write the Wazuh indexer password to the Wazuh manager keystore."
        return 1
    fi

}

# ------------ passwordsMain.sh ------------ 
function getHelp() {

    echo -e ""
    echo -e "NAME"
    echo -e "        $(basename "${0}") - Manage passwords for Wazuh indexer and Wazuh API users."
    echo -e ""
    echo -e "SYNOPSIS"
    echo -e "        $(basename "${0}") [OPTIONS]"
    echo -e ""
    echo -e "DESCRIPTION"
    echo -e "        -a,  --change-all"
    echo -e "                Changes the passwords of all the Wazuh indexer and Wazuh API users installed on this host."
    echo -e "                The new passwords are generated and saved in the credential files of this host."
    echo -e ""
    echo -e "        -u,  --user <user>"
    echo -e "                Indicates the name of the user whose password will be changed."
    echo -e "                Wazuh indexer users: ${indexer_accounts[*]}."
    echo -e "                Wazuh API users: ${api_accounts[*]}."
    echo -e "                If -p|--password is not used, a random password is generated and saved in the credential files of this host."
    echo -e ""
    echo -e "        -p,  --password"
    echo -e "                Reads the new password from the standard input, must be used with option -u."
    echo -e "                The password must have between 12 and 64 characters, only these characters:"
    echo -e "                A-Z a-z 0-9 . , _ + : @ % ^ = ~ -, and at least one upper case letter, one lower"
    echo -e "                case letter, one digit and one symbol."
    echo -e ""
    echo -e "        -v,  --verbose"
    echo -e "                Shows the complete script execution output."
    echo -e ""
    echo -e "        -h,  --help"
    echo -e "                Shows help."
    echo -e ""
    echo -e "FILES"
    echo -e "        ${indexer_config_path}/export/credentials.env, ${manager_config_path}/export/credentials.env"
    echo -e "                The passwords of the Wazuh indexer and Wazuh API users, published by their package."
    echo -e "        ${indexer_config_path}/local.env, ${manager_config_path}/local.env, ${dashboard_config_path}/local.env"
    echo -e "                The passwords each package uses."
    echo -e "        $(passwords_getDeploymentFile)"
    echo -e "                The passwords that --generate-config-files keeps next to the root CA, if this host has it."
    echo -e "        A new password is saved in each of these files that exists on this host and holds that user."
    echo -e "        If none of them holds it, the local.env of the package that owns the user is created with it."
    echo -e ""
    exit "${1:-1}"

}
function main() {

    umask 177

    common_checkRoot

    if [ -n "${1}" ]; then
        while [ -n "${1}" ]
        do
            case "${1}" in
            "-v"|"--verbose")
                verboseenabled=1
                shift 1
                ;;
            "-a"|"--change-all")
                changeall=1
                shift 1
                ;;
            "-u"|"--user")
                if [ -z "${2}" ]; then
                    echo "Argument --user needs a second argument"
                    getHelp
                    exit 1
                fi
                nuser=${2}
                shift 2
                ;;
            "-p"|"--password")
                readpassword=1
                shift 1
                ;;
            "-h"|"--help")
                getHelp 0
                ;;
            *)
                getHelp
            esac
        done

        export JAVA_HOME=/usr/share/wazuh-indexer/jdk/

        if [ -n "${verboseenabled}" ]; then
            debug="2>&1 | tee -a ${logfile}"
        fi

        common_checkSystem
        common_checkInstalled

        if [ -z "${nuser}" ] && [ -z "${changeall}" ]; then
            common_logger -e "Either -u|--user or -a|--change-all is required."
            getHelp
        fi

        if [ -n "${nuser}" ] && [ -n "${changeall}" ]; then
            getHelp
        fi

        if [ -n "${readpassword}" ] && [ -z "${nuser}" ]; then
            getHelp
        fi

        if [ -n "${readpassword}" ]; then
            passwords_readPassword
            passwords_checkPassword "${password}"
        fi

        passwords_checkCredentialsFile

        if [ -n "${nuser}" ]; then
            if passwords_isInList "${nuser}" "${indexer_accounts[@]}" && [ -n "${indexer_installed}" ]; then
                passwords_readUsers
            fi
            passwords_checkUser
        fi

        if [ -n "${changeall}" ]; then
            if [ -n "${indexer_installed}" ]; then
                passwords_readUsers
            fi
            if [ -n "${wazuh_installed}" ]; then
                api_users=("${api_accounts[@]}")
            fi
            if [ "${#users[@]}" -eq 0 ] && [ "${#api_users[@]}" -eq 0 ]; then
                common_logger -e "No Wazuh indexer or Wazuh API user to change on this host."
                exit 1
            fi
            passwords_generatePasswords
        fi

        if [ -n "${nuser}" ] && [ -z "${readpassword}" ]; then
            autopass=1
            passwords_generatePassword
        fi

        passwords_checkOwnerDirs

        if [ -z "${api}" ] && [ -n "${indexer_installed}" ]; then
            passwords_getNetworkHost
            passwords_generateHash
            passwords_changePassword
            passwords_runSecurityAdmin
            # Save the passwords the Wazuh indexer already uses before writing any keystore.
            passwords_saveIndexerCredentials
            if ! passwords_updateKeystores; then
                passwords_restartPendingServices
                exit 1
            fi
        fi

        if [ -n "${api}" ] || [ "${#api_users[@]}" -gt 0 ]; then
            if ! passwords_changePasswordApi; then
                passwords_restartPendingServices
                exit 1
            fi
        fi

        passwords_restartPendingServices

        if [ -n "${save_failed}" ]; then
            exit 1
        fi

    else
        getHelp
    fi

}

# ------------ passwordsVariables.sh ------------ 

function common_checkAptLock() {

    attempt=0
    seconds=30
    max_attempts=10

    while fuser "${apt_lockfile}" >/dev/null 2>&1 && [ "${attempt}" -lt "${max_attempts}" ]; do
        attempt=$((attempt+1))
        common_logger "Another process is using APT. Waiting for it to release the lock. Next retry in ${seconds} seconds (${attempt}/${max_attempts})"
        sleep "${seconds}"
    done

}
function common_logger() {

    now=$(date +'%d/%m/%Y %H:%M:%S')
    mtype="INFO:"
    debugLogger=
    nolog=
    if [ -n "${1}" ]; then
        while [ -n "${1}" ]; do
            case ${1} in
                "-e")
                    mtype="ERROR:"
                    shift 1
                    ;;
                "-w")
                    mtype="WARNING:"
                    shift 1
                    ;;
                "-d")
                    debugLogger=1
                    mtype="DEBUG:"
                    shift 1
                    ;;
                "-nl")
                    nolog=1
                    shift 1
                    ;;
                *)
                    message="${1}"
                    shift 1
                    ;;
            esac
        done
    fi

    if [ -z "${debugLogger}" ] || { [ -n "${debugLogger}" ] && [ -n "${debugEnabled}" ]; }; then
        if [ -z "${nolog}" ] && { [ "$EUID" -eq 0 ] || [[ -n "${cert_tool_script_name}" && "$(basename "$0")" =~ ${cert_tool_script_name} ]]; }; then
            printf "%s\n" "${now} ${mtype} ${message}" | tee -a ${logfile}
        else
            printf "%b\n" "${now} ${mtype} ${message}"
        fi
    fi

}
function common_checkRoot() {

    common_logger -d "Checking root permissions."
    if [ "$EUID" -ne 0 ]; then
        echo "This script must be run as root."
        exit 1;
    fi

}
function common_checkInstalled() {

    common_logger -d "Checking Wazuh installation."
    wazuh_installed=""
    indexer_installed=""
    dashboard_installed=""

    if [ "${sys_type}" == "yum" ]; then
        rpm -q wazuh-manager --quiet && wazuh_installed=1
    elif [ "${sys_type}" == "apt-get" ]; then
        common_isDebInstalled wazuh-manager && wazuh_installed=1
    fi

    if [ -d "/var/wazuh-manager" ]; then
        common_logger -d "There are Wazuh remaining files."
        wazuh_remaining_files=1
    fi

    if [ "${sys_type}" == "yum" ]; then
        rpm -q wazuh-indexer --quiet && indexer_installed=1

    elif [ "${sys_type}" == "apt-get" ]; then
        common_isDebInstalled wazuh-indexer && indexer_installed=1
    fi

    if [ -d "/var/lib/wazuh-indexer/" ] || [ -d "/usr/share/wazuh-indexer" ] || [ -d "/etc/wazuh-indexer" ] || [ -f "${base_path}/search-guard-tlstool*" ]; then
        common_logger -d "There are Wazuh indexer remaining files."
        indexer_remaining_files=1
    fi

    if [ "${sys_type}" == "yum" ]; then
        eval "rpm -q wazuh-dashboard --quiet && dashboard_installed=1"
    elif [ "${sys_type}" == "apt-get" ]; then
        common_isDebInstalled wazuh-dashboard && dashboard_installed=1
    fi

    if [ -d "/var/lib/wazuh-dashboard/" ] || [ -d "/usr/share/wazuh-dashboard" ] || [ -d "/etc/wazuh-dashboard" ] || [ -d "/run/wazuh-dashboard/" ]; then
        common_logger -d "There are Wazuh dashboard remaining files."
        dashboard_remaining_files=1
    fi

}
function common_isDebInstalled() {

    local status
    status=$(dpkg-query -W -f='${db:Status-Status}' "${1}" 2>/dev/null)
    [ -n "${status}" ] && [ "${status}" != "not-installed" ] && [ "${status}" != "config-files" ]

}
function common_checkSystem() {

    if [ -n "$(command -v yum)" ]; then
        sys_type="yum"
        sep="-"
        common_logger -d "YUM package manager will be used."
    elif [ -n "$(command -v apt-get)" ]; then
        sys_type="apt-get"
        sep="="
        common_logger -d "APT package manager will be used."
    else
        common_logger -e "Couldn't find YUM or APT package manager. Try installing the one corresponding to your operating system and then, launch the installation assistant again."
        exit 1
    fi

}
function common_checkWazuhConfigYaml() {

    common_logger -d "Checking Wazuh YAML configuration file."
    filecorrect=$(cert_parseYaml "${config_file}" | grep -Ev '^#|^\s*$' | grep -Pzc "\A(\s*(nodes_indexer__name|nodes_indexer__ip|nodes_manager__name|nodes_manager__ip|nodes_manager__node_type|nodes_dashboard__name|nodes_dashboard__ip)=.*?)+\Z")
    if [[ "${filecorrect}" -ne 1 ]]; then
        common_logger -e "The configuration file ${config_file} does not have a correct format."
        exit 1
    fi

}
function common_curl() {

    if [ -n "${curl_has_connrefused}" ]; then
        eval "curl --retry-connrefused $@; e_code=\${PIPESTATUS[0]}"
    else
        retries=0
        eval "curl $@; e_code=\${PIPESTATUS[0]}"
        while [ "${e_code}" -eq 7 ] && [ "${retries}" -ne 12 ]; do
            retries=$((retries+1))
            sleep 5
            eval "curl $@; e_code=\${PIPESTATUS[0]}"
        done
    fi
    return "${e_code}"

}
function common_checkYumLock() {

    attempt=0
    seconds=30
    max_attempts=10

    while [ -f "${yum_lockfile}" ] && [ "${attempt}" -lt "${max_attempts}" ]; do
        attempt=$((attempt+1))
        common_logger "Another process is using YUM. Waiting for it to release the lock. Next retry in ${seconds} seconds (${attempt}/${max_attempts})"
        sleep "${seconds}"
    done

}
function common_envGet() {

    local file="${1}" key="${2}" value

    if [ ! -e "${file}" ] && [ ! -L "${file}" ]; then
        return 1
    fi
    if [ -L "${file}" ] || [ ! -f "${file}" ] || [ "$(stat -c %u "${file}")" != "0" ]; then
        echo "${file} must be a regular file owned by root." >&2
        return 2
    fi

    value=$(sed -nE "s/^[[:space:]]*${key}[[:space:]]*=[[:space:]]*//p" "${file}" | tail -n 1)
    if ! grep -qE "^[[:space:]]*${key}[[:space:]]*=" "${file}"; then
        return 1
    fi
    value="${value%"${value##*[![:space:]]}"}"
    if [[ "${value}" =~ ^\"(.*)\"$ ]] || [[ "${value}" =~ ^\'(.*)\'$ ]]; then
        value="${BASH_REMATCH[1]}"
    fi
    printf '%s\n' "${value}"

}
function common_envSet() {

    local file="${1}" key="${2}" value="${3}" tmp

    if [ -L "${file}" ] || { [ -e "${file}" ] && [ ! -f "${file}" ]; }; then
        echo "${file} must be a regular file." >&2
        return 1
    fi
    if ! tmp=$(mktemp "${file}.XXXXXX"); then
        return 1
    fi
    if [ -f "${file}" ] && grep -qE "^[[:space:]]*${key}[[:space:]]*=" "${file}"; then
        KEY="${key}" VALUE="${value}" awk '
            $0 ~ "^[[:space:]]*" ENVIRON["KEY"] "[[:space:]]*=" { if (!done) print ENVIRON["KEY"] "=" ENVIRON["VALUE"]; done = 1; next }
            { print }
        ' "${file}" > "${tmp}"
    else
        [ -f "${file}" ] && cat "${file}" > "${tmp}"
        printf '%s=%s\n' "${key}" "${value}" >> "${tmp}"
    fi
    if ! chown root:root "${tmp}" || ! chmod 600 "${tmp}" || ! mv -f "${tmp}" "${file}"; then
        rm -f "${tmp}"
        return 1
    fi

}
function common_passwordGenerate() {

    local password=""

    until common_passwordValidate "${password}" 2>/dev/null; do
        password=$(LC_ALL=C tr -dc 'A-Za-z0-9.,_+:@%^=~-' < /dev/urandom 2>/dev/null | head -c 32)
    done
    printf '%s\n' "${password}"

}
function common_passwordValidate() {

    local password="${1}"

    if [[ "${password}" =~ [^ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.,_+:@%^=~-] ]]; then
        echo "password must only contain characters from A-Z a-z 0-9 . , _ + : @ % ^ = ~ -" >&2
        return 1
    fi
    if [ "${#password}" -lt 12 ] || [ "${#password}" -gt 64 ]; then
        echo "password must contain between 12 and 64 characters" >&2
        return 1
    fi
    if [[ ! "${password}" =~ [abcdefghijklmnopqrstuvwxyz] ]]; then
        echo "password must contain at least one lowercase letter" >&2
        return 1
    fi
    if [[ ! "${password}" =~ [ABCDEFGHIJKLMNOPQRSTUVWXYZ] ]]; then
        echo "password must contain at least one uppercase letter" >&2
        return 1
    fi
    if [[ ! "${password}" =~ [0123456789] ]]; then
        echo "password must contain at least one digit" >&2
        return 1
    fi
    if [[ ! "${password}" =~ [.,_+:@%^=~-] ]]; then
        echo "password must contain at least one symbol from . , _ + : @ % ^ = ~ -" >&2
        return 1
    fi

}
function common_caGetDir() {

    local dir="${WAZUH_CA_DIR:-${base_path}/wazuh-ca}"

    if [[ "${dir}" != /* ]]; then
        echo "WAZUH_CA_DIR must be an absolute path." >&2
        return 1
    fi
    printf '%s\n' "${dir}"

}
function common_caValidate() {

    local dir="${1}"
    local cert="${dir}/root-ca.pem" key="${dir}/root-ca.key"

    if [ -L "${dir}" ] || [ ! -d "${dir}" ]; then
        echo "${dir} is not a directory." >&2
        return 1
    fi
    if [ ! -f "${cert}" ]; then
        echo "There is no root-ca.pem in ${dir}." >&2
        return 1
    fi
    if ! openssl x509 -in "${cert}" -noout -checkend 0 > /dev/null 2>&1; then
        echo "${cert} is not a valid or current certificate." >&2
        return 1
    fi
    if ! openssl x509 -in "${cert}" -noout -text 2>/dev/null | grep -q 'CA:TRUE'; then
        echo "${cert} is not a CA certificate." >&2
        return 1
    fi
    if ! openssl verify -CAfile "${cert}" "${cert}" > /dev/null 2>&1; then
        echo "${cert} is not a self-signed CA." >&2
        return 1
    fi
    if [ ! -e "${key}" ]; then
        return 0
    fi
    if [ "$(openssl x509 -in "${cert}" -noout -pubkey 2>/dev/null)" != "$(openssl pkey -in "${key}" -pubout 2>/dev/null)" ]; then
        echo "root-ca.key does not match root-ca.pem in ${dir}." >&2
        return 1
    fi

}
function common_caEnsure() {

    local dir="${1}" tmp

    if [ -e "${dir}/root-ca.pem" ] || [ -e "${dir}/root-ca.key" ]; then
        if [ ! -e "${dir}/root-ca.pem" ]; then
            echo "root-ca.key exists without root-ca.pem in ${dir}." >&2
            return 1
        fi
        common_caValidate "${dir}"
        return $?
    fi

    if ! (umask 077 && mkdir -p "${dir}") || [ -L "${dir}" ]; then
        echo "Could not create ${dir}." >&2
        return 1
    fi
    chown root:root "${dir}" && chmod 700 "${dir}" || return 1

    tmp=$(mktemp -d "${dir}/.root-ca.XXXXXX") || return 1
    printf '%s\n' '[req]' 'distinguished_name = dn' 'x509_extensions = v3_ca' 'prompt = no' \
        '[dn]' 'OU = Wazuh' 'O = Wazuh' 'L = California' \
        '[v3_ca]' 'basicConstraints = critical, CA:TRUE' 'keyUsage = critical, keyCertSign, cRLSign' \
        'subjectKeyIdentifier = hash' 'authorityKeyIdentifier = keyid:always' > "${tmp}/openssl.cnf"
    if ! (umask 077 && openssl req -x509 -new -nodes -newkey rsa:2048 -sha256 -days 3650 -batch \
        -config "${tmp}/openssl.cnf" -keyout "${tmp}/root-ca.key" -out "${tmp}/root-ca.pem" > /dev/null 2>&1); then
        rm -rf "${tmp}"
        echo "OpenSSL could not create the root CA." >&2
        return 1
    fi
    chmod 400 "${tmp}/root-ca.key" && chmod 644 "${tmp}/root-ca.pem" \
        && mv "${tmp}/root-ca.key" "${dir}/root-ca.key" && mv "${tmp}/root-ca.pem" "${dir}/root-ca.pem"
    local e_code=$?
    rm -rf "${tmp}"
    return "${e_code}"

}

main "$@"
