# -*-Shell-script-*-
#
# cauth_functions     This file contains functions to be used by all
#                     CAuth scripts: /usr/sbin/get-{access,keys,sudoers}.sh
#


# Define global variables
ldap_url="https://cauth.yandex.net:4443"
fqdn_url="$ldap_url/check_fqdn/?"
info_url="$ldap_url/info/?color=true"
access_url="$ldap_url/access/?"
sudoers_url="$ldap_url/sudoers/?"
userkeys_url="$ldap_url/userkeys/?"
adminkeys_url="$ldap_url/adminkeys/?"
serverusers_url="$ldap_url/passwd/serverusers/?"
serveradmins_url="$ldap_url/passwd/serveradmins/?"

security_dir="/etc/security"
cauth_spool="/var/spool/cauth"

access_file="${cauth_spool}/access"
sudoers_file="${cauth_spool}/sudoers"
userkeys_file="${cauth_spool}/userkeys"
serverusers_file="${cauth_spool}/serverusers"
serveradmins_file="${cauth_spool}/serveradmins"

package_name="yandex-cauth"
root_keys_file="/root/.ssh/authorized_keys"
main_sudoers_file="/etc/sudoers.d/${package_name}"
static_access_list="/etc/security/yandex-access.conf"
main_access_list="/etc/security/yandex-access-custom.conf"

sshd_config_file="/etc/ssh/sshd_config"
sshd_executable="/usr/sbin/sshd"

checkmd5_cmd="md5sum"
package_version=$(dpkg-query -f='${Version}' -W "${package_name}")
user_agent="CAUTH/${package_name}-${package_version}"
get_cmd="curl -s --compressed --connect-timeout 60 --max-time 60 --cacert /etc/ldap/certs/cafile.pem --user-agent ${user_agent}"

cauth_conf_file="/etc/cauth/cauth.conf"


# OS specific stuff, may be different on different OSes
stat_ugp_opts="-c %U%G%a"
protect_file="chattr +i"
unprotect_file="chattr -i"


# Do we have to be silent?
test -z "$is_silent" &&  is_silent="false"


# Security stuff
PATH="/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin"


# Load cauth config
if [ -s "${cauth_conf_file}" ] ; then
    . ${cauth_conf_file}
fi


#
# create_working_dirs
#  Function creates all directories needed by yandex-cauth if not exist
#
create_working_dirs()
{
    #
    # Create our spool directory, if does not exists
    #
    if [ ! -d "${cauth_spool}" ] ; then
        install -d -o root -g root -m0755 ${cauth_spool} || \
            p_err "Cannot create ${cauth_spool} directory"
    fi

    #
    # Define ${private_tmp_dir}
    # All operations with temporary files must be there
    #
    if [ -d /var/run/ ] ; then

        if [ ! -d /var/run/cauth ] ; then
            install -d -o root -g root -m0755 /var/run/cauth || \
                p_err "Cannot create /var/run/cauth directory"
        fi

        private_tmp_dir="/var/run/cauth"

    elif [ -d "${cauth_spool}" ] ; then

        if [ ! -d "${cauth_spool}/ptmp" ] ; then
            install -d -o root -g root -m0755 ${cauth_spool}/ptmp || \
                p_err "Cannot create ${cauth_spool}/ptmp directory"
        fi

        private_tmp_dir="${cauth_spool}/ptmp"

    else
        p_err "Cannot create private temporary directory"
    fi
}


#
# get_my_fqdns
#  Function returns FQDN's of all global IP addresses detected on the host
#
get_my_fqdns()
{
    local _my_fqdns _my_ip _my_ips

    _my_fqdns=""
    _my_ips="$(hostname -I)"

    if [ ! -z "${_my_ips}" ] ; then
        for _my_ip in ${_my_ips} ; do
            _my_fqdns="${_my_fqdns} $(dig +short -x ${_my_ip} | grep -v ';;' | sed -e 's/\.$//')"
        done
    else
        return 1
    fi

    echo ${_my_fqdns}

    return 0
}


#
# check_downloaded_md5 <file>
#  Function compares md5 of <file> and "MD5:" string inside it.
#  Returns 0 if its match and 1 if its not.
#
check_downloaded_md5()
{
    local _tmp_file _real_md5 _correct_md5

    if [ -z "$1" ]; then
        return 1
    fi

    _tmp_file="$1"

    grep -q MD5: "${_tmp_file}" || return 1

    if [ -s ${_tmp_file} ] ;then
        _real_md5=$(grep -v " MD5:" ${_tmp_file} | ${checkmd5_cmd} 2>/dev/null | awk '{print $1}')
        _correct_md5=$(awk -F":" '/ MD5:/ {print $2}' ${_tmp_file})

        if [ "${_real_md5}" = "${_correct_md5}" ] ; then
            return 0
        else
            return 1
        fi
    else
        return 1
    fi
}


#
# fetch_file <file> <url>
#  If <file> exists, function gets md5 of it, and get data from
#  "<url>/?md5=md5" to <file>.tmp.
#  If <file>.tmp contains only "OK" string, then we quit (no new data).
#  If <file>.tmp contains data, we verify it with check_downloaded_md5(),
#  remove last line with "MD5:" substring, and copy <file>.tmp to <file>
#
fetch_file()
{
    local _file _file_md5 _file_url _tmp_file _uri

    if [ -z "$1" -o -z "$2" ] ; then
        return 1
    fi

    _file="$1"
    _file_url="$2"


    # check if ${sources} variable defined, and if yes
    # pass "sources" parameter, defined in cauth.conf, to CAuth API
    if [ ! -z "${sources+x}" ] ; then
        _uri="sources=${sources}&"
    fi

    # if file already exists, grab MD5 of it and put it to HTTP request
    if [ -f "${_file}" ] ; then
        _file_md5="$(${checkmd5_cmd} ${_file} | awk '{print $1}')"
        _uri="${_uri}md5=${_file_md5}"
    fi


    # fetch file with curl
    _tmp_file=$(mktemp ${_file}.XXXXX)
    if ! ${get_cmd} ${_file_url}${_uri} > ${_tmp_file} ; then
        clean_garbage ${_tmp_file}
        p_err "Cannot fetch ${_file_url}${_uri} to ${_tmp_file}"
    fi

    if ! tail -1 ${_tmp_file} | grep -q OK ; then
        # Compare downloaded MD5 with MD5 inside file
        if ! check_downloaded_md5 ${_tmp_file} ; then
            clean_garbage ${_tmp_file}
            p_err "Downloaded ${_file_url} with wrong md5"
        fi

        # Remove line with md5
        sed -i -e "/^#.*MD5.*/d" ${_tmp_file}

        # Save new _file
        if ! install -m0600 -o root -g root ${_tmp_file} ${_file} ; then
            clean_garbage ${_tmp_file}
            p_err "Cannot install ${_tmp_file} to ${_file}"
        fi
    fi

    rm -f ${_tmp_file}
    return 0
}


#
# is_root
#  Function checks if we are root
#  Returns 0 if our uid is 0 and 1 if not.
#
is_root()
{
    local _uid

    _uid=$(id -u)

    if [ "${_uid}" = "0" ] ; then
        return 0
    else
        return 1
    fi

    return 0
}


#
# p_err <err message>
#  Function prints error message ($1) and logs it with auth.err facility
#  then exits with error code 1
#
p_err()
{
    local _error_msg _not_fatal

    if [ "$#" -lt 1 ] ; then
        return 1
    fi

    _error_msg="$1"
    _not_fatal="$2"

    logger -t "CAuth" -p auth.error "[ERROR]: [$0]: ${_error_msg}"

    if [ "$is_silent" = "false" ] ; then
        echo "  [ERROR]: ${_error_msg}"
    fi

    [ -n "$_not_fatal" ] && return 0

    exit 1
}


# 
# p_warn <warn message>
#  Function prints warning message ($1) and logs it with auth.warn facility
#
p_warn()
{
    local _warn_msg

    if [ "$#" -lt 1 ] ; then
        return 1
    fi

    _warn_msg="$1"


    logger -t "CAuth" -p auth.warn "[WARN]: [$0]: ${_warn_msg}"

    if [ "$is_silent" = "false" ] ; then
        echo "  [WARN]: ${_warn_msg}"
    fi

    return 0
}


#
# p_info <info message>
#  Function prints info message ($1) and logs it with auth.info facility
#
p_info()
{
    local _info_msg

    if [ "$#" -lt 1 ] ; then
        return 1
    fi

    _info_msg="$1"


    logger -t "CAuth" -p auth.info "[INFO]: [$0]: ${_info_msg}"

    if [ "$is_silent" = "false" ] ; then
        echo "  [INFO]: ${_info_msg}"
    fi

    return 0
}


# clean_garbage()
#  Function removes files or directories
#
clean_garbage()
{
    local _clean_files

    if [ "$#" -lt 1 ] ; then
        return 1
    fi

    _clean_files="$1"

    if [ "${_clean_files}" = "/" -o "${_clean_files}" = "//" ] ; then
        return 1
    fi

    rm -rf "${_clean_files}"

    return 0
}
