#!/bin/bash
# Name             : verifyPkgSign
# Description      : only use open PGP tools to verify pacakge
# Platform         : Linux
# Creation date    : 2014-12-5
# Modification log :


#==============================================================
# Global variable

export LANG=posix

cd $(dirname $0)
ABSOLUATELY_PATH=`pwd`

USER_NAME=`id -un`
SCRIPT_NAME=$(basename $0)
SCRIPT_PATH="${ABSOLUATELY_PATH}/${SCRIPT_NAME}"
cd - &>/dev/null

OPERATION_IP=`who am i | awk '{print $NF}' | sed -e 's/[()]//g' -e 's/:.*//g'`
OPERATION_IP=${OPERATION_IP:-"127.0.0.1"}

OPERATION_LEVEL="info"
OPERATION_INFO="Begin to verify package $1 with openPGP."

#==============================================================


#*************************************************************#
# Name:        fn_write_operation_Log                         #
# Description: write operation log                            #
# Parameters:  $1=log level                                   #
# Parameters:  $2=log information                             #
#*************************************************************#
function fn_write_operation_Log()
{
    local log_level="$1"
    shift
    local log_info="$@"
    local result_info
    
    if [ "${log_level}" = "err" ]
    then
        result_info=Failed
    else
        log_level=info
        result_info=Successful
    fi
    
    logger -t "${USER_NAME}" -p local0.${log_level} "${SCRIPT_PATH};${result_info};${OPERATION_IP};${log_info}"
    
    return 0
    
}    

#*************************************************************#
# Name:         fn_verify_gpg_file                            #
# Description:  verify gpg function                           #
# Parameters:   $1=package path                               #
#*************************************************************#
function fn_verify_gpg_file()
{
    #Get package path and it could not be null
    PKG_NAME="$1"
    SIGNATURE_FILE="$2"
    SIGNATURE_FILE="${SIGNATURE_FILE//\/\///}"
    
    #Get package absolute directory and package name
    echo "${PKG_NAME}" | grep / &>/dev/null
    if [ $? -ne 0 ]
    then
        PKG_DIR=`pwd`
        PKG_FILE="${PKG_NAME}"
    else
        PKG_DIR=`dirname "${PKG_NAME}"`
        cd "${PKG_DIR}" &>/dev/null
        PKG_DIR=`pwd`
        PKG_FILE=`basename "${PKG_NAME}"`
    fi

    echo "GPG: Verify package ${PKG_FILE} with gpg file."

    #Note: get the absolutly path of signature file. if SIGNATURE_FILE($2) is not given, get signature file after extracting package.
    if [ -z "${SIGNATURE_FILE}" ]
    then
        #package name must end with _pkg.tar
        echo "${PKG_FILE}" | grep "_pkg.tar$" &>/dev/null
        if [ $? -ne 0 ]
        then
            MESSAGE_INFO="Error: ${PKG_NAME} is not valid when OpenGPG verified package."
            OPERATION_LEVEL="err"
            OPERATION_INFO="${PKG_NAME} is not valid when OpenGPG verified package."
            return 1
        fi
        
        #Note:exclude the sign file,just one package
        pkgcount=$(tar -tf "${PKG_FILE}" | egrep -v ".*\.(cms|sgn|asc|crl|xml)$" | wc -l)
        if [ "${pkgcount}" -ne 1 ]
        then
            echo "GPG Error: There are more than one pacakge in the sign pacakge."
            OPERATION_LEVEL="err"
            OPERATION_INFO="There are more than one pacakge in the sign pacakge."
            return 1
        fi

        pkgcount=$(tar -tf "${PKG_FILE}" | egrep "(osprofile|\.sh|\.inc)$")
        if [ ! -z "${pkgcount}" ]
        then
            echo "GPG Error: There are (osprofile|\.sh|\.inc) file in the ${PKG_FILE} file."
            OPERATION_LEVEL="err"
            OPERATION_INFO="There are (osprofile|\.sh|\.inc) file in the ${PKG_FILE} file."
            return 1
        fi

        #Decompress package file
        echo "Begin to decompress ${PKG_FILE}"
        tar --no-same-owner -xf "${PKG_FILE}"
        if [ $? -ne 0 ]
        then
            MESSAGE_INFO="Error: decompress ${PKG_DIR}/${PKG_FILE} failed when OpenGPG verified package."
            OPERATION_LEVEL="err"
            OPERATION_INFO="Decompress ${PKG_DIR}/${PKG_FILE} failed when OpenGPG verified package."
            return 1
        fi

        PREFIX_PKG_NAME=`echo "${PKG_FILE}" | awk -F"_pkg.tar" '{print $1}'`
        OPENPGP_SIGN_FILE=`ls "${PKG_DIR}/${PREFIX_PKG_NAME}".*.asc | egrep -m1 '\.(tar\.gz|tar|7z)\.asc$'`
    else
        local signf_abs_dir=$(echo "${SIGNATURE_FILE}" | awk -F'/' '{ if (NF<2) print "'$(pwd)'"; else print "'"${SIGNATURE_FILE%/*}"'" ;}')
        if [ "${PKG_DIR}" != "${signf_abs_dir}" ]
        then
            mv "${SIGNATURE_FILE}" "${PKG_DIR}"
            if [ $? -ne 0 ]
            then
                MESSAGE_INFO="Error: move ${SIGNATURE_FILE} to ${PKG_DIR} failed when OpenGPG verified package."
                OPERATION_LEVEL="err"
                OPERATION_INFO="Move ${SIGNATURE_FILE} to ${PKG_DIR} failed when OpenGPG verified package."
                return 1
            fi
        fi
        OPENPGP_SIGN_FILE="${PKG_DIR}/${SIGNATURE_FILE##*/}"
    fi

    if [ -z "${OPENPGP_SIGN_FILE}" ] && [ ! -f "${OPENPGP_SIGN_FILE}" ] 
    then
        MESSAGE_INFO="Error: The file ${PREFIX_PKG_NAME}.*.asc  does not exist when OpenGPG verified package"
        OPERATION_LEVEL="err"
        OPERATION_INFO="The file ${PREFIX_PKG_NAME}.*.asc not exist when OpenGPG verified package."
        return 1
    fi
    
    #check if open pgp tools is installed on the system    
    gpg --version | grep "gnupg" | awk -F"." '{print $2}' | grep -w "gnupg" &>/dev/null
    if [ $? -ne 0 ]
    then
        MESSAGE_INFO="Error: The system does not install GPG tool"
        OPERATION_LEVEL="err"
        OPERATION_INFO="GPG tool not installed."
        return 1
    fi
    
    #check if the pgp public key is imported in the system    
    echo "Using the openPGP checks the file"
    gpg --list-keys | grep -w "OpenPGP signature" &>/dev/null
    if [ $? -ne 0 ]
    then
        echo "GPG Error: The public key does not exist when OpenGPG verified package."
        OPERATION_LEVEL="err"
        OPERATION_INFO="The public key does not exist when OpenGPG verified package."
        return 1
    fi
    echo "The public key exists"
    
    #verify the package file with open pgp tools
    gpg --verify "${OPENPGP_SIGN_FILE}" 2>&1 | grep -w "Good signature" &>/dev/null
    local ret=("${PIPESTATUS[@]}")
    if [ "${ret[0]}" != 0 -o "${ret[1]}" != 0 ]
    then
        MESSAGE_INFO="Error: The file ${PKG_FILE} has failed in verification with openPGP!"
        OPERATION_LEVEL="err"
        OPERATION_INFO="The file ${PKG_FILE} has failed in verification with openPGP!"
        return 1
    fi 

    #delete the package sign file
    [ -f "${OPENPGP_SIGN_FILE}" ] && rm -f "${OPENPGP_SIGN_FILE}"
    [ -f "${PREFIX_PKG_NAME}.sgn" ] && rm -f "${PREFIX_PKG_NAME}.sgn"
    
    echo ""
    echo "SignCLI Info: Successfully verified package ${PKG_DIR}/${PKG_FILE} with openPGP"   
    OPERATION_LEVEL="info"
    OPERATION_INFO="Succeed to verify package ${PKG_DIR}/${PKG_FILE} with openPGP."
    return 0
}


#*************************************************************#
# Name:         fn_verify_cms_file                            #
# Description:  verify cms function                           #
# Parameters:   $1=package path                               #
#*************************************************************#
function fn_verify_cms_file()
{
    #Get package path and it could not be null
    PKG_NAME="$1"
    
    #Get package absolute directory and package name
    echo "${PKG_NAME}" | grep / &>/dev/null
    if [ $? -ne 0 ]
    then
        PKG_DIR=`pwd`
        PKG_FILE="${PKG_NAME}"
    else
        PKG_DIR=`dirname "${PKG_NAME}"`
        cd "${PKG_DIR}" &>/dev/null
        PKG_DIR=`pwd`
        PKG_FILE=`basename "${PKG_NAME}"`
    fi

    echo "CMS: Verify package ${PKG_FILE} with cms file."

    #package name must end with _pkg.tar
    echo "${PKG_FILE}" | grep "_pkg.tar$" &>/dev/null
    if [ $? -eq 0 ]
    then
        #Note:exclude the sign file,just one package
        pkgcount=$(tar -tf  "${PKG_FILE}" | egrep -v ".*\.(cms|sgn|asc|crl|xml)$" | egrep -v "^crldata.crl$" | wc -l)
        if [ ${pkgcount} -ne 1 ]
        then
            echo "CMS Error: There are more than one pacakge in the sign pacakge."
            OPERATION_LEVEL="err"
            OPERATION_INFO="There are more than one pacakge in the sign pacakge."
            return 1
        fi
        
        pkgcount=$(tar -tf "${PKG_FILE}" | egrep "(osprofile|\.sh|\.inc)$")
        if [ ! -z "${pkgcount}" ]
        then
            echo "CMS Error: There are (osprofile|\.sh|\.inc) file in the ${PKG_FILE} file."
            OPERATION_LEVEL="err"
            OPERATION_INFO="There are (osprofile|\.sh|\.inc) file in the ${PKG_FILE} file."
            return 1
        fi
        
        #Decompress package file
        tar --no-same-owner -xf "${PKG_FILE}"
        if [ $? -ne 0 ]
        then
            MESSAGE_INFO="Error: decompress ${PKG_DIR}/${PKG_FILE} failed when cms verified package."
            OPERATION_LEVEL="err"
            OPERATION_INFO="Decompress ${PKG_DIR}/${PKG_FILE} failed when cms verified package."
            return 1
        fi

        PREFIX_PKG_NAME=`echo "${PKG_FILE}" | awk -F"_pkg.tar" '{print $1}'`
        PKG_FILE=`ls "${PREFIX_PKG_NAME}".*.cms 2>/dev/null | head -n 1 | awk -F'.cms' '{print $1}'`
    fi  
    
    CRL_FILE="crldata.crl"
    if [ ! -f "${PKG_DIR}/${CRL_FILE}" ]
    then
        CRL_FILE="${PKG_FILE}.crl"
    fi
    CMS_FILE="${PKG_FILE}.cms"
    cd "${PKG_DIR}" &>/dev/null
    if [ ! -f "${CMS_FILE}" ]
    then
        MESSAGE_INFO="Error: The file ${CMS_FILE} or ${CRL_FILE} does not exist when cms verified package"
        OPERATION_LEVEL="err"
        OPERATION_INFO="The file ${CMS_FILE} or ${CRL_FILE} dose not exist when cms verified package."
        return 1
    fi
    CRL_FILE_OPTION="--crl ${CRL_FILE}"
    [ -f "${CRL_FILE}" ] || CRL_FILE_OPTION=""
    CA_FILE="/etc/ssl/certs/HuaweiRootCAsipCRLs.crl"
    RET_CONTENT=$(hwcmstool --verify --source "${PKG_FILE}" --sign "${CMS_FILE}" ${CRL_FILE_OPTION} --cacrl "${CA_FILE}")
    if [ $? -ne 0 ]
    then
        echo "Error: Verify package ${PKG_FILE} failed with CMS"
        echo "Detail error: ${RET_CONTENT}"
        OPERATION_LEVEL="err"
        OPERATION_INFO="Verify package ${PKG_FILE} failed with CMS"
        return 1
    fi
    
    echo ""
    echo "SignCLI Info: Successfully verified package ${PKG_DIR}/${PKG_FILE} with CMS"
    OPERATION_LEVEL="info"
    OPERATION_INFO="Succeed to verify package ${PKG_DIR}/${PKG_FILE} with CMS."
    return 0
}


#*************************************************************#
# Name:         fn_main_file                                  #
# Description:  main function                                 #
# Parameters:   $1=package path                               #
#*************************************************************#
function fn_main()
{
    PKG_NAME="$1"
    if [ -z "${PKG_NAME}" ]
    then
        echo "Error: parameter is null, needs 1 or 2 parameters."
        OPERATION_LEVEL="err"
        OPERATION_INFO="Parameter is null, need 1 or 2 parameters."
        echo "SignCLI Info: Verify failed."
        return 1
    fi

    #Note: Determine whether the line contains a newline, a tab, a space, or a backquote
    no_blank_param=$(echo "${PKG_NAME}" | sed ':a;$!N;s/[\*\ \n\t\r\`]//g;ta')
    
    if [ "${PKG_NAME}" != "${no_blank_param}" ]
    then
        echo "Error: The param:<${PKG_NAME}> can not contain <\*|\n|\t|\r|\`>."
        OPERATION_LEVEL="err"
        OPERATION_INFO="Package name can not contain <\*|\n|\t|\r|\`>."
        return 5
    fi

    if [ ! -f "${PKG_NAME}" ]
    then
        echo "Error: ${PKG_NAME} does not exist."
        OPERATION_LEVEL="err"
        OPERATION_INFO="${PKG_NAME} does not exist."
        echo "SignCLI Info: Verify package ${PKG_NAME} failed."
        return 1
    fi

    fn_verify_cms_file "${PKG_NAME}" && return 0

    MESSAGE="${MESSAGE_INFO}"
    fn_verify_gpg_file "$@" && return 0

    echo "${MESSAGE}"
    echo "${MESSAGE_INFO}"
    echo "SignCLI Info: Verify package ${PKG_NAME} failed."
    return 1
}
#==============================================================
# main entrance

fn_write_operation_Log "${OPERATION_LEVEL}" "${OPERATION_INFO}"

fn_main "$@"
MAIN_RET=$?

fn_write_operation_Log "${OPERATION_LEVEL}" "${OPERATION_INFO}"
exit ${MAIN_RET}

#==============================================================
