#! /bin/sh

# 
# Copyright 1999-2006 University of Chicago
# 
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# 
# http://www.apache.org/licenses/LICENSE-2.0
# 
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# 


openssl_cmd="/cvmfs/dirac.egi.eu/dirac/v8.0.50/Linux-x86_64/bin/openssl"

# catch the kill signal (ctrl-c) and do cleanup
trap do_trap 1 2 3 6 9 13 15

############################################################
# main code section
############################################################
prefix="${GLOBUS_LOCATION-/cvmfs/dirac.egi.eu/dirac/v8.0.50/Linux-x86_64}"
exec_prefix="${prefix}"
sbindir="${exec_prefix}/sbin"
bindir="${exec_prefix}/bin"
includedir="/cvmfs/dirac.egi.eu/dirac/v8.0.50/Linux-x86_64/include/globus"
datarootdir="${prefix}/share"
datadir="${datarootdir}"
libexecdir="/cvmfs/dirac.egi.eu/dirac/v8.0.50/Linux-x86_64/share/globus"
sysconfdir="${prefix}/etc"
sharedstatedir="${prefix}/com"
localstatedir="${prefix}/var"

PROGRAM_NAME="${0##*/}"
PROGRAM_VERSION="5.3"
PACKAGE="globus_simple_ca"
VERSION="5.3"
DIRT_TIMESTAMP="1629915172"
DIRT_BRANCH_ID="0"

CYGPATH_W="echo"
host_os="linux-gnu"

short_usage="$PROGRAM_NAME [-help] -in REQUEST -out CERTIFICATE [options ...]"

openssl_options=""

alias construct_path=echo
if test x"$CYGPATH_W" != x; then
    case $host_os in
        *mingw32*)
            alias construct_path="$CYGPATH_W"
            ;;
    esac
fi

printhelp() {
    option="${1}"
    helpstr="${2}"
    optwidth="${optwidth:-$((${COLUMNS:-80} / 3))}"
    if [ "$optwidth" -gt 30 ]; then
        optwidth=30
    fi
    helpwidth="${helpwidth:-$((${COLUMNS:-80} - $optwidth - 6))}"
    helpstrformatted="$(echo "${helpstr}" | tr -sd '\n\t' ' ' | \
            fold -sw ${helpwidth})"

    OLDIFS="$IFS"
    IFS="
"
    first=1

    for x in $helpstrformatted; do
        printf "    %-${optwidth}s %-${helpwidth}s\n" "${first:+$option}" "$x"
        first=""
    done
    if [ "$first" = "1" ]; then
        printf "    %-${optwidth}s\n" "${first:+$option}"
    fi
    IFS="$OLDIFS"
}

long_usage () {
    cat <<EOF

${short_usage}

  Sign a certificate request located in the file named by the argument to the
  -in parameter, writing the new certificate to the file named by the argument
  to the -out parameter.

  Options:
EOF
    printhelp "-help, -h, -?, -usage" "Print this usage message and exit"
    printhelp "-version" "Print $PROGRAM_NAME version number"
    printhelp "-versions" "Print detailed package version for $PACKAGE"
    printhelp "-in REQUEST" "Read the certificate request from
            the file REQUEST"
    printhelp "-out CERTIFICATE" "Write the new certificate to the path
            CERTIFICATE"
    printhelp "-force" "Sign a certificate request with subject that matches an
            already signed certificate."
    printhelp "-dir DIR" "Use the Simple CA certificate and configuration
            located in the directory DIR"
    printhelp "-openssl-help" "Print options to the openssl ca command"
    printhelp "[OPENSSL-OPTIONS]" "Any of the options allowed with the 
            openssl ca command, such as -passin pass:PASSWORD"
}

readCommandLine() {

    while test -n "$1" 
    do
        case $1 in
            -\?|-h|-help|-usage|--help|--usage)
                long_usage
                exit 0
                ;;
            -force|--force)
                force_sign="true"
                shift;
                ;;
            -dir|--dir)
                shift;
                GRID_CA_DIR=$1;
                shift;
                ;;
            -in|--in)
                shift;
                INPUT_REQ_FILE=$1;
                shift;
                ;;
            -out|--out)
                shift;
                SIGNED_OUTPUT_FILE=$1;
                shift;
                ;;
            -openssl-help|--openssl-help)
                shift;
		"$openssl" ca -help
		exit;
		;;
             -version|--version)
                if [ "X${PROGRAM_NAME}" != "X" -a \
                      "X${PROGRAM_VERSION}" != "X" ]; then
                    echo "${PROGRAM_NAME}: ${PROGRAM_VERSION}"
                elif [ "X${PACKAGE}" != "X" -a \
                       "X${VERSION}" != "X" ]; then
                    echo "${PACKAGE}: ${VERSION}"
                else
                    echo "No version information available."
                fi
                exit 0
                ;;
             -versions|--versions)
                __AT='@'
                if [ -n "${PACKAGE}" -a -n "${VERSION}" -a \
                     -n "${DIRT_TIMESTAMP}" -a -n "${DIRT_BRANCH_ID}" -a \
                     "X${DIRT_TIMESTAMP}" != "X${__AT}DIRT_TIMESTAMP${__AT}" -a \
                     "X${DIRT_BRANCH_ID}" != "X${__AT}DIRT_BRANCH_ID${__AT}" ];
                then
                    echo "${PACKAGE}: ${VERSION} (${DIRT_TIMESTAMP}-${DIRT_BRANCH_ID})"
                else
                    echo "No DiRT information available."
                fi
                exit 0;
                ;;
	    *)
                openssl_options="$openssl_options $1"
                shift;
                ;;
        esac
    done
    
    if test -z "$INPUT_REQ_FILE"; then
        echo ""
        echo "ERROR: Please specify a request file using the -in option"
        echo ""
        exit 1
    else
        if test ! -r ${INPUT_REQ_FILE}; then
            echo ""
            echo "ERROR: The file: ${INPUT_REQ_FILE} is not readable"
            echo ""
            exit 1
        fi
    fi

    if test -z "$SIGNED_OUTPUT_FILE"; then
        echo ""
        echo "ERROR: Please specify a output file using the -out option"
        echo ""
        exit 1
    fi
}

do_trap() {

    echo ""
    echo "Normal program execution interrupted.  Exiting."
    echo ""
    do_cleanup
    exit 1
}

do_cleanup() {

    rm -f ${tmp_output}
}

do_password_input() {
    if test $need_password_input; then
        stty -echo
        echo ""
        echo $@
        printf "please enter the password for the CA key: "
    fi
}        

end_password_input() {
    if test $need_password_input; then
        stty echo
        echo ""
    fi
}

do_sign() {
 
do_password_input "To sign the request"
tmp_output=/tmp/tmp_output.$$
"${openssl_cmd}" ca $openssl_options -batch -config $(construct_path ${grid_ca_conf}) \
                  -in $(construct_path ${INPUT_REQ_FILE}) \
                  -out $(construct_path ${SIGNED_OUTPUT_FILE}) \
                  > $tmp_output 2>&1

openssl_result=$?
end_password_input
 
# check to see if an error occurred while signing
if test ${openssl_result} != 0; then

    # check that a certificate with the same subject has not
    # already been signed
    already_signed=`grep \
                        "ERROR:There is already a certificate" \
                        ${tmp_output}`
    already_signed2=`grep \
                        "unique_subject = \"yes\"" \
                        ${tmp_output}`
    already_signed3=`grep \
                        "TXT_DB error number 2" \
                        ${tmp_output}`
    if test -n "${already_signed}" || test -n "${already_signed2}" \
                                   || test -n "${already_signed3}"; then

        subj_tmp_output=/tmp/tmp_output.$$
        "${openssl_cmd}" req -noout -in $(construct_path ${INPUT_REQ_FILE}) \
                           -subject -nameopt sep_multiline | \
                           sed -e '/^subject=/d' -e 's!^\s*!/!' | tr -d '\n' \
                           > ${subj_tmp_output} 2>&1
        res=$?
        if test $res != 0; then
            echo ""
            echo "ERROR: Failed to get subject of request ${INPUT_REQ_FILE}"
            echo ""
            do_cleanup
            exit 1
        fi
        
        req_subj=`grep "^subject=" < ${subj_tmp_output} \
                             | sed -e "s|subject= *|/|" -e "s|,|/|g"` 
        # find signed cert
        for p in ${GRID_CA_DIR}/newcerts/*.pem; do
            subj_tmp_output=/tmp/tmp_output.$$
            "${openssl_cmd}" x509 -noout -in $(construct_path ${p}) \
                               -subject -nameopt sep_multiline | \
                               sed -e '/^subject=/d' -e 's!^\s*!/!' | tr -d '\n' \
                               > ${subj_tmp_output} 2>&1
            res=$?
            if test $res != 0; then
                echo ""
                echo "ERROR: Failed to get subject of signed cert at: ${p}"
                echo ""
            fi

            signed_subj="$(sed -e "s|subject= *|/|" -e "s|,|/|g" < "${subj_tmp_output}")"

            if test "${signed_subj}" = "${req_subj}"; then
                SIGNED_CERT=${p}
            fi
        done

        if test -z "${SIGNED_CERT}"; then
            echo ""
            echo "ERROR: Failed to find signed cert in CA cert store with subject:"
            echo "       ${req_subj}"
            echo ""
            do_cleanup
            exit 1
        fi

        if test ! -f "${SIGNED_CERT}"; then
            echo ""
            echo "ERROR: Could not find cert file to revoke in certificate store:"
            echo "       ${SIGNED_CERT}"
            echo ""
            do_cleanup
            exit 1
        fi

        SIGNED_SUBJECT="${req_subj}"
        expired=$("${openssl_cmd}" x509 -in $(construct_path ${SIGNED_CERT}) -checkend 0 > /dev/null; echo $?)

        if test -n "${force_sign}" || test "${expired}" = "0"; then

            echo ""
            echo "Revoking previous certificate"

            tmp_revoke_output=/tmp/tmp_revoke_out.$$

            do_password_input "To revoke the current certificate"
            "${openssl_cmd}" ca $openssl_options -batch -config $(construct_path ${grid_ca_conf}) \
                    -revoke $(construct_path ${SIGNED_CERT}) \
                    2> ${tmp_revoke_output}
	    res=$?
	    end_password_input
            if test $res != 0; then
                echo ""
	        echo "ERROR: Failed to revoke previous certificate with subject:"
		echo "       ${SIGNED_SUBJECT}"
                echo ""
    		echo "========== ERROR MESSAGES FROM OPENSSL =========="
    		cat ${tmp_revoke_output} 1>&2
    		echo "================================================="
	        echo ""
                rm -f ${tmp_revoke_output}
                do_cleanup
                exit 1
            else
                
                rm -f ${tmp_revoke_output}
                echo ""
                echo "Signing new certificate"
                do_sign;
                return;
            fi

        else
            echo ""
            echo "There is already a valid certificate that matches this subject at:"
            echo
            echo "${SIGNED_CERT}"
            echo
            echo "You can use the -force option to overwrite"
            echo "this certificate and create a new one."
            echo ""

            rm -f ${subj_tmp_output}
            
            do_cleanup
            exit 1
        fi
    fi

    echo ""
    echo "ERROR running command:"
    echo ""
    echo " ${openssl_cmd} ca $openssl_options \\"
    echo "    -batch -config $(construct_path ${grid_ca_conf}) \\"
    echo "    -in $(construct_path ${INPUT_REQ_FILE}) -out $(construct_path ${SIGNED_OUTPUT_FILE})"
    echo ""
    echo "========== ERROR MESSAGES FROM OPENSSL =========="
    cat ${tmp_output} 1>&2
    echo "================================================="

    # check if the error was with 
    no_config_file=`grep "error loading the config file" ${tmp_output}`
    if test -n "${no_config_file}"; then

        echo ""
        echo "ERROR: No CA config file found."
        echo "Either simple CA setup package is not installed,"
        echo "or the config file is not located at:"
        echo ""
        echo "  ${grid_ca_conf}"
        echo ""
    fi

    # check if the CA's private key could be loaded
    wrong_password=`grep "unable to load CA private key" ${tmp_output}`
    if test -n "${wrong_password}"; then
    
        echo ""
        echo "ERROR: The CA private key could not be loaded."
        echo "Possibly an incorrect password for the CA key was used."
        echo ""
    fi

    # check that the cert request matches the CA cert
    wrong_org=`grep "field needed to be the same" ${tmp_output}`
    if test -n "${wrong_org}"; then
    
        echo "" 
        echo "ERROR: The cert request does not match CA cert"
        echo "Check that the correct security config files are"
        echo "set during grid-cert-request"
        echo ""
        echo "The default configuration can be set using either"
        echo "the command:  grid-default-ca, or via the -ca option"
        echo "to grid-cert-request."
        echo ""
    fi

    rm -f ${tmp_output}

    echo ""
    do_cleanup
    exit 1
fi

}

readCommandLine "$@"

if test -z "$GRID_CA_DIR"; then
    if test -w "${localstatedir}/lib/globus/simple_ca/."; then
        GRID_CA_DIR="${localstatedir}/lib/globus/simple_ca" 
    elif test -d "${HOME}/.globus/simpleCA/."; then
        GRID_CA_DIR="${HOME}/.globus/simpleCA"
    else
        exec 1>&2
        echo ""
        echo "ERROR: No usable Simple CA directory found at \${HOME}/.globus/simpleCA or "
        echo "\${localstatedir}/lib/globus/simple_ca"
        echo ""
        echo "Either specify a directory with -dir, or run"
        echo "grid-ca-create to create a CA"
        echo ""
        exit 1
    fi
fi

if test ! -f ${GRID_CA_DIR}/cacert.pem; then
    exec 1>&2
    echo ""
    echo "ERROR: No CA certificate found at ${GRID_CA_DIR}/cacert.pem"
    echo "The CA certificate is missing!"
    echo "Please run setup-simple-ca before"
    echo "signing certificates."
    echo ""
    exit 1
fi

grid_ca_conf=${GRID_CA_DIR}/grid-ca-ssl.conf

echo "${openssl_options}" | grep -- -passin > /dev/null
res1=$?
if test ! $res1 = 0; then
    echo ${openssl_options} | grep -- -key > /dev/null
    res1=$?
fi
if test ! $res1 = 0; then
    if test -f ${GRID_CA_DIR}/passwd; then
        openssl_options="${openssl_options} -passin file:$(construct_path ${GRID_CA_DIR}/passwd)"
        res1=0
    fi
fi

if test ! $res1 = 0; then
    need_password_input=1
    openssl_options="${openssl_options} -passin stdin"
fi

do_sign;

if test ! -f ${SIGNED_OUTPUT_FILE}; then
        exec 1>&2
	echo "ERROR: ${SIGNED_OUTPUT_FILE} does not exist.  Invalid internal state, exiting..."
	echo ""
	exit 1
fi

NEW_SERIAL="$("${openssl_cmd}" x509 -in $(construct_path ${SIGNED_OUTPUT_FILE}) -noout -serial)"
res=$?
if test $res != 0; then
        exec 1>&2
	echo ""
	echo "ERROR: Failed to get serial number of newly signed cert at: ${SIGNED_OUTPUT_FILE}"
	echo ""
fi

NEW_SERIAL="${NEW_SERIAL##serial=}"

echo ""
echo "The new signed certificate is at: ${GRID_CA_DIR}/newcerts/${NEW_SERIAL}.pem"
echo ""

do_cleanup
exit