#! /bin/sh 

# This script creates a new CA certificate and the configuration files so that
# the grid-cert-request and grid-ca-sign tools can be used with the CA.
# Unlike GT 2-5, this version of the simple CA tool does not create a setup package

set -e

openssl="/usr/bin/openssl"

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

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

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

bits=4096

##
# create_ca_directory:
# Creates the directory tree needed for a grid CA. The tree contains:
# $GRID_CA_DIR
# +- serial
# +- index.txt
# +- certs/
# +- crl/
# +- newcerts/
# +- private/
#
# On error, this function exits the shell.
#
# @param DIRECTORY
#     Path to the new CA directory
#
create_ca_directory()
{
    _ca_directory="$1"
    _ca_dir_perms="0700"

    if [ -d "${_ca_directory}/." ]; then
        if test -z "${force}"; then
            echo ""
            echo "It looks like a CA has already been setup at this location."
            printf "Do you want to overwrite this CA? (y/n) [n]: "
            read tmp_answer
            if ! expr "${tmp_answer:-n}" : '[Yy]' > /dev/null; then
                exit 1
            fi
        fi
        rm -rf "${_ca_directory}"
    fi

    mkdir -m ${_ca_dir_perms} -p "${_ca_directory}"

    if [ $? -ne 0 -o ! -d "${_ca_directory}/." ]; then
        echo "ERROR: Couldn't create directory: ${_ca_directory}"
        echo "       make sure you have valid permissions set."
        exit 1;
    fi

    # from the CA.sh script - setup the CA directory 
    for directory in certs crl newcerts private ; do
        mkdir -m ${_ca_dir_perms} "${_ca_directory}/${directory}"
        if test $? -ne 0; then
            exec 1>&2
            echo ""
            echo "ERROR: Failed to make directory: ${1}"
            echo "Check permissions of base dir"
            echo ""
            exit 1
        fi
    done

    echo "01" > ${_ca_directory}/serial
    if test $? -ne 0; then
        exec 1>&2
        echo ""
        echo "ERROR: Could not write to ${_ca_directory}/serial"
        echo "Check permissions on the dir"
        echo ""
        exit 1
    fi

    touch ${_ca_directory}/index.txt
    if test $? -ne 0; then
        exec 1>&2
        echo ""
        echo "ERROR: Could not write to ${_ca_directory}/index.txt"
        echo "Check the permissions on the dir"
        echo ""
        exit 1
    fi
}

##
# generate_unique_name: finds a unique name for the CA
#                       based on the hostname
generate_unique_name()
{
    tmp_hostname="$globus_hostname"

    if test -z "${tmp_hostname}"; then
        if [ -n "${noint}" ]; then
            exec 1>&2
        fi
        echo "" 
        echo "Cannot determine this machine's hostname for the CA name."
        echo ""



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

    echo "simpleCA-${tmp_hostname}"
    return 0
}

#
# get_ca_subject: gets the CA subject name from the user if not in command-line options
#
get_ca_subject()
{
    varname="${1}"
    _casubject="${request_subject:-cn=Globus Simple CA, ou=$(generate_unique_name), ou=GlobusTest, o=Grid}"

    _got_subject="no"

    while [ "${_got_subject}" = "no" ] ; do
        echo ""
        echo "The unique subject name for this CA is:"
        echo ""
        echo "${_casubject}"
        echo ""

        if [ -n "${noint}" -o -n "${request_subject}" ]; then
            _got_subject="yes"
        else
            printf "Do you want to keep this as the CA subject (y/n) [y]: "
            
            while [ "${_got_subject}" = "no" ]; do
                read _answer

                case "${_answer:-y}" in
                [Nn]*)
                    echo ""

                    while [ "$_got_subject" = "no" ]; do
                        printf "Enter a unique subject name for this CA: "
                        read _casubject
                        echo ""
                        if expr "${_casubject}" : "[Cc][Nn]=.*,.*=.*"  > /dev/null; then
                            _got_subject="yes"
                        else
                            echo "Invalid CA subject name. Please include a common name and at least one"
                            echo "other name component (e.g. CN=Globus, O=Test)"
                        fi
                    done
                    ;;
                [Yy]*)
                    _got_subject="yes"
                    ;;
                *)
                    echo ""
                    echo "Please answer 'y' or 'n'"
                    echo ""
                    ;;
                esac
            done
        fi
    done
    eval "$varname=\"${_casubject}\""
}

get_ca_email()
{
    _varname="${1}"
    _caemail=""
    _defaultemail="${request_email:-${globus_username}@$globus_hostname}"

    if [ -n "${request_email}" -o -n "${noint}" ]; then
        _caemail="${_defaultemail}"
    fi

    while [ -z "${_caemail}" ]; do
        echo ""
        echo "Enter the email of the CA (this is the email where certificate"
        printf "requests will be sent to be signed by the CA) [${_defaultemail}]: "
        read _caemail
        _caemail="${_caemail:-${_defaultemail}}"
    done

    eval "${_varname}=\"${_caemail}\""
}

get_ca_lifetime()
{
    _varname="${1}"

    if [ -z "${noint}" -a -z "${request_days}" ]; then
        cat <<-EOF
	    The CA certificate has an expiration date. Keep in mind that 
       	    once the CA certificate has expired, all the certificates 
       	    signed by that CA become invalid.  A CA should regenerate 
       	    the CA certificate and start re-issuing ca-setup packages 
       	    before the actual CA certificate expires.  This can be done 
       	    by re-running this setup script.  Enter the number of DAYS 
       	    the CA certificate should last before it expires.
	EOF

        printf "[default: 5 years $((365 * 5)) days]: "
        read _ca_cert_days
        _ca_cert_days="${_ca_cert_days:-$((365 * 5))}"
        echo
    else
        _ca_cert_days="${request_days:-$((365 * 5))}"
    fi

    eval "${_varname}=\"${_ca_cert_days}\""
}

grid_security_conf_template()
{
    cat <<'EOF'
#################################################################
#
# File: grid-security.conf
#
# Purpose: This file contains the configuration information
#          for the Grid Security Infrastructure
#
#################################################################

# These values are set by grid-ca-create
SETUP_GSI_HOST_BASE_DN="\\"\\\$SETUP_GSI_HOST_BASE_DN\\""
SETUP_GSI_USER_BASE_DN="\\"\\\$SETUP_GSI_USER_BASE_DN\\""
SETUP_GSI_CA_NAME="\\"\\\$SETUP_GS_CA_NAME\\""
SETUP_GSI_CA_EMAIL_ADDR="\\"\\\$SETUP_GSI_CA_EMAIL_ADDR\\""

DEFAULT_GSI_HOST_BASE_DN="\\"\$GSI_CA_BASE_DN\\""
DEFAULT_GSI_USER_BASE_DN="\\"ou=\${domain}, \$GSI_CA_BASE_DN\\""
DEFAULT_GSI_CA_NAME="\\"\$GSI_CA_NAME\\""
DEFAULT_GSI_CA_EMAIL_ADDR="\\"\$GSI_CA_EMAIL\\""

# Distinguish Name (DN) of the Host
GSI_HOST_BASE_DN="\\"\\\${SETUP_GSI_HOST_BASE_DN:-\\\${DEFAULT_GSI_HOST_BASE_DN}}\\""

# Distinguish Name (DN) of the User
GSI_USER_BASE_DN="\\"\\\${SETUP_GSI_USER_BASE_DN:-\\\${DEFAULT_GSI_USER_BASE_DN}}\\""

# CA Name for the organization
GSI_CA_NAME="\\"\\\${SETUP_GSI_CA_NAME:-\\\${DEFAULT_GSI_CA_NAME}}\\""

# CA Email address for the organization
GSI_CA_EMAIL_ADDR="\\"\\\${SETUP_GSI_CA_EMAIL_ADDR:-\\\${DEFAULT_GSI_CA_EMAIL_ADDR}}\\""

export GSI_HOST_BASE_DN
export GSI_USER_BASE_DN
export GSI_CA_NAME
export GSI_CA_EMAIL_ADDR
EOF
}

##
# save_ca_settings: save the settings determined from this
#                   script to grid-security.conf for this CA
#
save_ca_settings()
{
    _dest="$1"
    _subj="$2"
    _addr="$3"

    # Save stdin and stdout
    exec 3<&0
    exec 4>&1

    # Translate template to configuration file
    exec 1> "${_dest}/grid-security.conf" 

    # Template variables
    domain="$(globus-domainname)"
    GSI_CA_BASE_DN="$(expr "${_subj}" : "[Cc][Nn]=[^,]*, *\(.*\)")"
    GSI_CA_NAME="$(expr "${_subj}" : "[Cc][Nn]= *\([^,]*\)")"
    GSI_CA_EMAIL="${_addr}"

    # Replace template variables in the configuration file
    grid_security_conf_template | while read line; do
        case "$line" in
            "#"*)
                echo "$line"
                ;;
            *)
                eval echo "$line"
                ;;
        esac
    done

    # Restore stdin and stdout
    exec 0<&3
    exec 1>&4

    exec 3<&-
    exec 4>&-
}

############################################################
# create_input_file: generate the input file to be passed as
#                    stdin to the openssl req utility.
############################################################
create_input_file ()
{
    _common_name="$1"
    _config_file="$2"

    # Parse the ssleay configuration file, to determine the
    # correct default 
    exec 3<&0
    exec 0< "${_config_file}" || {
        rc=$?;
        echo 1>&2 "Error opening ${_config_file}";
        exit $rc
    }
    _skip=1

    while read _line; do
        if [ "$_line" = "# BEGIN CONFIG" ]; then
            _skip=0
            continue
        elif [ "$_line" = "# END CONFIG" ]; then
            break
        fi
        if [ "$_skip" -eq 0 ]; then
            _attr="$(expr "$_line" : "\(.*[^ ]\) *=")"
            _value="$(expr "$_line" : "[^=]*= *\(.*\)")"
            if expr "${_attr}" : ".*_default\$" > /dev/null; then
                echo "${_value}"
            fi
        fi
    done
    echo "$_common_name"

    exec 0<&3
    exec 3<&-
}

rfc2253_to_ssl_config()
{
    _type="${1}"
    _name="${2}"

    OLDIFS="$IFS"
    IFS=","
    oucount=0
    ocount=0
    dccount=0

    _reversei=""
    for i in ${_name}; do
        _reversei="${i}${_reversei:+,${_reversei}}"
    done

    for i in ${_reversei}; do
        i="${i# }"
        _component="$(echo ${i%%=*} | tr 'A-Z' 'a-z')"
        _value="${i#*=}"

        case "$_component" in
            c|countryname)
                printf "%-40s= %s\n" countryName "Country Name (2 letter code)"
                printf "%-40s= %s\n" countryName_default "${_value}"
                printf "%-40s= %s\n" countryName_min "2"
                printf "%-40s= %s\n" countryName_max "2"
                ;;
            o|organizationname)
                printf "%-40s= %s\n" "$ocount.organizationName" "Level $ocount Organization"
                printf "%-40s= %s\n" "$ocount.organizationName_default" "${_value}"
                ocount=$(($ocount+1))
                ;;
            ou|organizationalunitname)
                printf "%-40s= %s\n" "$oucount.organizationalUnitName" "Level $oucount Organizational Unit"
                printf "%-40s= %s\n" "$oucount.organizationalUnitName_default" "${_value}"
                oucount=$(($oucount+1))
                ;;
            postalcode)
                printf "%-40s= %s\n" "postalCode" "Postal Code"
                printf "%-40s= %s\n" "postalCode_default" "${_value}"
                ;;
            cn|commonname)
                if [ "$_type" = "-user" ]; then
                    printf "%-40s= %s\n" "$oucount.organizationalUnitName" "Level $oucount Organizational Unit"
                    printf "%-40s= %s\n" "$oucount.organizationalUnitName_default" "${_value}"
                fi
                printf "%-40s= %s\n" "commonName" "Name (E.g., John M. Smith)"
                printf "%-40s= %s\n" "commonName_max" "64"
                ;;
            l|localityname)
                printf "%-40s= %s\n" "localityName" "Locality Name"
                printf "%-40s= %s\n" "localityName_default" "${_value}"
                ;;
            st|stateorprovincename )
                printf "%-40s= %s\n" "stateOrProvinceName" "State or Province Name"
                printf "%-40s= %s\n" "stateOrProvinceName_default" "${_value}"
                ;;
             street|streetaddress)
                printf "%-40s= %s\n" "streetAddress" "Street Address"
                printf "%-40s= %s\n" "streetAddress_default" "${_value}"
                ;;
             dc|domaincomponent )
                printf "%-40s= %s\n" "$dccount.DC" "Domain Component"
                printf "%-40s= %s\n" "$dccount.DC_default" "${_value}"
                dccount=$(($dccount + 1))
                ;;
             uid|userid)
                printf "%-40s= %s\n" "UID" "UserId"
                printf "%-40s= %s\n" "UID_default" "${_value}"
                ;;
            *)
                echo 1>&2 "Unknown subject name component"
                exit 1
                ;;
        esac
    done
    IFS="$OLDIFS"
}

rfc2253_to_oneline()
{
    _name="${1}"

    OLDIFS="$IFS"
    IFS=","
    _oneline=""

    for i in ${_name}; do
        i="${i# }"
        _component="$(echo ${i%%=*} | tr 'A-Z' 'a-z')"
        _value="${i#*=}"

        case "$_component" in
            c)
                _oneline="/C=${_value}${_oneline}"
                ;;
            o)
                _oneline="/O=${_value}${_oneline}"
                ;;
            ou)
                _oneline="/OU=${_value}${_oneline}"
                ;;
            cn)
                _oneline="/CN=${_value}${_oneline}"
                ;;
            l)
                _oneline="/L=${_value}${_oneline}"
                ;;
            postalcode)
                _oneline="/POSTALCODE=${_value}${_oneline}"
                ;;
            st)
                _oneline="/ST=${_value}${_oneline}"
                ;;
            street)
                _oneline="/STREET=${_value}${_oneline}"
                ;;
            dc)
                _oneline="/DC=${_value}${_oneline}"
                ;;
            uid)
                _oneline="/UID=${_value}${_oneline}"
                ;;
            *)
                echo 1>&2 "Unknown subject name component"
                exit 1
                ;;
        esac
    done
    IFS="$OLDIFS"
    echo "$_oneline"
}

ca_signing_policy_tmpl()
{
    cat <<'EOF'
# ca-signing-policy.conf, see ca-signing-policy.doc for more information
#
# This is the configuration file describing the policy for what CAs are
# allowed to sign whoses certificates.
#
# This file is parsed from start to finish with a given CA and subject
# name.
# subject names may include the following wildcard characters:
#    *    Matches any number of characters.
#    ?    Matches any single character.
#
# CA names must be specified (no wildcards). Names containing whitespaces
# must be included in single quotes, e.g. 'Certification Authority'. 
# Names must not contain new line symbols. 
# The value of condition attribute is represented as a set of regular 
# expressions. Each regular expression must be included in double quotes.  
#
# This policy file dictates the following policy:
#   -The Globus CA can sign Globus certificates
#
# Format:
#------------------------------------------------------------------------
#  token type  | def.authority |                value              
#--------------|---------------|-----------------------------------------
# EACL entry #1|

 access_id_CA      X509         '${GRID_CA_SUBJECT}'

 pos_rights        globus        CA:sign

 cond_subjects     globus       '"${GRID_CA_COND_SUBJECT}"'

# end of EACL
EOF
}

generate_signing_policy()
{
    _cadir="${1}"
    _caname="${2}"

    GRID_CA_SUBJECT="$(rfc2253_to_oneline "${_caname}")"
    rc=$?
    if [ $rc -ne 0 ]; then
        exit $rc
    fi

    GRID_CA_COND_SUBJECT="\"${GRID_CA_SUBJECT%%/CN=*}/*\""

    exec 3<&0
    exec 4>&1

    exec 1> "${_cadir}/signing-policy"

    ca_signing_policy_tmpl | while read line; do
        if expr "${line}" : ".*#" > /dev/null; then
            comment="#${line#*#}"
        else
            comment=""
        fi
        precomment="${line%%${comment}}"

        eval lineval="\"${precomment}\""
        echo "${lineval}${comment}"
    done


    exec 0<&3
    exec 1>&4

    exec 3<&-
    exec 4>&-
}

setup_grid_security_dir()
{
    _cadir="${1}"
    _destdir="${2}"
    _cahash="$("$openssl" x509 -in "$(construct_path ${_cadir}/cacert.pem)" -noout -hash)"

    printf "Installing new CA files to ${_destdir}... "

    if [ ! -d "${_destdir}" ]; then
        mkdir -m 0755 -p "${_destdir}"
    fi

    cp "${_cadir}/cacert.pem" "${_destdir}/${_cahash}.0"
    cp "${_cadir}/signing-policy" "${_destdir}/${_cahash}.signing_policy"
    cp "${_cadir}/grid-security.conf" "${_destdir}/grid-security.conf.${_cahash}"
    cp "${_cadir}/globus-user-ssl.conf" "${_destdir}/globus-user-ssl.conf.${_cahash}"
    cp "${_cadir}/globus-host-ssl.conf" "${_destdir}/globus-host-ssl.conf.${_cahash}"

    echo "done"
}

grid_ca_ssl_conf_tmpl()
{
    cat <<'EOF'
#
# SSLeay example configuration file.
# This is mostly being used for generation of certificate requests.
#

RANDFILE		= \\\$ENV::HOME/.rnd

####################################################################
[ ca ]
default_ca	= CA_default		# The default ca section

####################################################################
[ CA_default ]

dir		= $GRID_CA_DIR		# Where everything is kept
certs		= \\\$dir/certs		# Where the issued certs are kept
crl_dir		= \\\$dir/crl		# Where the issued crl are kept
database	= \\\$dir/index.txt	# database index file.
new_certs_dir	= \\\$dir/newcerts		# default place for new certs.

certificate	= \\\$dir/cacert.pem 	# The CA certificate
serial		= \\\$dir/serial 		# The current serial number
crl		= \\\$dir/crl.pem 		# The current CRL
private_key	= \\\$dir/private/cakey.pem# The private key
RANDFILE	= \\\$dir/private/.rand	# private random number file

x509_extensions	= x509v3_extensions	# The extentions to add to the cert
default_days	= 365			# how long to certify for
default_crl_days= 365 # DEE 30	# how long before next CRL
default_md	= sha1			# which md to use.
preserve	= no			# keep passed DN ordering

# A few difference way of specifying how similar the request should look
# For type CA, the listed attributes must be the same, and the optional
# and supplied fields are just that :-)
policy		= policy_match

# For the CA policy
[ policy_match ]
countryName		= optional
stateOrProvinceName	= optional
organizationName	= match
organizationalUnitName	= optional
commonName		= supplied
emailAddress		= optional

# For the 'anything' policy
# At this point in time, you must list all acceptable 'object'
# types.
[ policy_anything ]
countryName		= optional
stateOrProvinceName	= optional
localityName		= optional
organizationName	= optional
organizationalUnitName	= optional
commonName		= supplied
emailAddress		= optional

####################################################################
[ req ]
default_bits		= 2048
default_keyfile 	= privkey.pem
distinguished_name	= req_distinguished_name
x509_extensions         = v3_ca
req_extensions          = v3_req

[ req_distinguished_name ]
# BEGIN CONFIG
countryName			= Country Name (2 letter code)
countryName_default		= US
countryName_min			= 2
countryName_max			= 2

#stateOrProvinceName		= State or Province Name (full name)

#localityName			= Locality Name (e.g., city)

0.organizationName		= Main Organization
0.organizationName_default	= Not Configured

commonName			= Name (e.g., John M. Smith) 
commonName_max			= 64

#emailAddress			= Email Address
#emailAddress_max		= 40

# END CONFIG

[ v3_ca ]
basicConstraints                = critical,CA:true
subjectKeyIdentifier            = hash
nsCertType                      = sslCA,emailCA,objCA

[ v3_req ]
nsCertType                      = sslCA,emailCA,objCA

[ x509v3_extensions ]
nsCertType			= objsign,email,server,client
EOF
}

create_ssl_config()
{
(
    _type="${1}"
    _caname="${2}"
    _cadir="${3}"

    . "${_cadir}/grid-security.conf"

    exec 3<&0

    _skip=0

    GRID_CA_DIR="${_cadir}"
    grid_ca_ssl_conf_tmpl | while read line; do
        if expr "${line}" : ".*#" > /dev/null; then
            comment="#${line#*#}"
        else
            comment=""
        fi
        precomment="${line%%${comment}}"

        if [ "$comment" = "# BEGIN CONFIG" ]; then
            _skip=1
            echo "${comment}"
            rfc2253_to_ssl_config "${_type}" "${_caname}"
        elif [ "$comment" = "# END CONFIG" ]; then
            _skip=0
        fi
        if [ $_skip -eq 1 ]; then
            continue
        fi
        case "$precomment" in
            \[* )
                echo "${precomment}${comment}"
                ;;
            *)
                eval printf "%s" "\"${precomment}\""
                echo "${comment}"
                ;;
        esac
    done

    exec 0<&3
    exec 3<&-
)
}

############################################################
# generate_ca_certificate: the meat & potatoes - calls the 
#                          openssl req utility that creates
#                          the CA certificate
############################################################
generate_ca_certificate() 
{
    _cadir="${1}"
    _caname="${2}"
    _privatedir="${_cadir}/private"
    _ca_ssl_conf="${_cadir}/grid-ca-ssl.conf"
    _user_ssl_conf="${_cadir}/globus-user-ssl.conf"
    _host_ssl_conf="${_cadir}/globus-host-ssl.conf"


    create_ssl_config -ca "${_caname}" "${_cadir}" > "${_ca_ssl_conf}"
    create_ssl_config -user "${_caname}" "${_cadir}" > "${_user_ssl_conf}"
    create_ssl_config -host "${_caname}" "${_cadir}" > "${_host_ssl_conf}"

    CA_REQ_INPUT=${_privatedir}/tmp_openssl_input.conf

    create_input_file "${GSI_CA_NAME}" "${_ca_ssl_conf}" > ${CA_REQ_INPUT}

    if test -n "${request_password}"; then
        password_option="-passout pass:${request_password} "
    elif test -n "${noint}"; then
        password_option="-passout pass:globus "
    else
        password_option=""
    fi

    # create CA certificate
    if [ -n "${verbose}" ]; then
        "$openssl" req ${openssl_options} ${password_option} -config "$(construct_path ${_ca_ssl_conf})" \
            -x509 -days ${CA_CERT_DAYS} \
            -newkey rsa:${bits} -keyout $(construct_path ${CA_KEY_FILE}) \
            -out $(construct_path ${CA_CERT_FILE}) < ${CA_REQ_INPUT}
        RET=$?
    else
        "$openssl" req ${openssl_options} ${password_option} -config "$(construct_path ${_ca_ssl_conf})" \
            -x509 -days ${CA_CERT_DAYS} \
            -newkey rsa:${bits} -keyout $(construct_path ${CA_KEY_FILE}) \
            -out $(construct_path ${CA_CERT_FILE}) < ${CA_REQ_INPUT} > openssl_req.log 2>&1
        RET=$?
    fi


    if [ "${RET}" -eq 0 -a -n "${verbose}" ]; then
        tput clear
    elif [ "${RET}" -ne 0 ]; then
        echo "Error number ${RET} was returned by openssl" 1>&2
        exit ${RET}
    fi
}


############################################################
# do_trap:  catches any abortive signals and does cleanup
############################################################
do_trap() {

    echo ""
    echo ""
    echo "Normal program execution interrupted.  You will"
    echo "need to rerun the script:"
    echo ""
    echo "\${GLOBUS_LOCATION}/setup/globus/setup-simple-ca"
    echo ""
    echo "to setup the simple CA."
    echo ""

    exit 1
}


############################################################
# main code section
############################################################

prefix="${GLOBUS_LOCATION-/usr}"
exec_prefix="${prefix}"
sbindir="${exec_prefix}/sbin"
bindir="${exec_prefix}/bin"
includedir="/usr/include/globus"
datarootdir="${prefix}/share"
datadir="${datarootdir}"
libexecdir="/usr/share/globus"
sysconfdir="/etc"
sharedstatedir="${prefix}/com"
localstatedir="/var"

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

short_usage="$PROGRAM_NAME [-help] [ options ...] [ openssl options ...]"

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
    IFS="$OLDIFS"
}

globus_hostname="$(globus-hostname)"
globus_username="$(id -un)"

long_usage () {
    cat <<EOF

${short_usage}

  Note: Many of the following options can be used instead of allowing
        the script to interactively request configration info.  If
        its not clear what to do, let the interactive prompts guide you.

  Options:
EOF
    printhelp "-help, -?, -h, -usage" "shows this help message"
    printhelp "-verbose" "Show verbose output [unset]"
    printhelp "-force" "Overwite existing CA if one exists [unset]"
    printhelp "-noint" "Run in non-interactive mode. This will choose
            defaults for parameters or those specified on the command line
            without prompting. This option also implies -force. [unset]"
    printhelp "-dir DIRECTORY" "Create the SimpleCA in DIRECTORY.
            [$( ([ -w "${localstatedir}/lib/." ] &&  \
                    echo \${localstatedir}/lib/globus/simple_ca ) \
                    || echo \${HOME}/.globus/simpleCA)]"
    printhelp "-subject SUBJECT" "Create CA with the subject name SUBJECT 
           [cn=Globus Simple CA, ou=$(generate_unique_name),
           ou=GlobusTest, o=Grid]"
    printhelp "-email ADDRESS" "Include instructions to send certificate
            requests to ADDRESS [${globus_username}@${globus_hostname}]"
    printhelp "-days DAYS" "Create a CA certificate that lasts for DAYS days
            [$((365 * 5))]"
    printhelp "-bits BITS" "Create a CA certificate with a BITS long RSA key [4096]"
    printhelp "-pass PASSWORD" "Set the password for the CA's private key
            to PASSWORD.  Since the password is visible to utilities, this
            should only be used where security is not important. [globus]"
    printhelp "-nobuild" "Don't create a package for distributing
            configuration files for this CA. These can be created later by
            using the grid-ca-package utility [unset]"
    printhelp "-g" "Create a GPT binary package containing the CA and
            configuration files [unset]"
    printhelp "-b" "Create a GPT binary package containing the CA and
            configuration files compatible with GPT 3.2 and GT 4 and 5 [unset]"
    printhelp "-openssl-help" "Show help text for openssl"
    printhelp "[OPENSSL-OPTIONS]" "Specify additional options to pass to the 
            openssl command.  Use with caution, some options will conflict 
            with this script."
}

readCommandLine () {
  # Expects $@ from the shell invocation

  while test -n "$1"; do
    case $1 in
      -\?|-h|-help|-usage|--help|--usage)
         long_usage
         exit 0
         ;;
     -g)
         gpt_package=1
         shift
         ;;
     -b)
         gpt_package=1
         backward_compatible=1
         shift
         ;;
     -bits|--bits)
        shift
        bits="$1"
        shift
        ;;
     -dir|--dir)
         tmp_ca_dir="$2"
         if test -z "${tmp_ca_dir}"; then
            echo "ERROR: the -dir option expects a directory."
            exit 1;
         fi
         if test "${tmp_ca_dir##/*}" = ""; then
             GRID_CA_DIR="${tmp_ca_dir}"
         else
             GRID_CA_DIR="$PWD/${tmp_ca_dir}"
         fi
         
         shift ; shift
         ;;
     -force|--force)
         force="yes"
         shift
         ;;
     -subject|--subject)
         shift
         request_subject="${1}"
         shift
         if ! expr "${request_subject}" : "[Cc][Nn]=.*,.*=.*"  > /dev/null; then
             exec 1>&2
             echo "Invalid CA subject name. Please include a common name and at least one"
             echo "other name component (e.g. CN=Globus, O=Test)"
             exit 1
         fi
         ;;
     -email|--email)
         request_email="${2}"
         shift ; shift
         ;;
     -days|--days)
         request_days="${2}"
         shift ; shift
         ;;
     -pass|--pass)
         request_password="${2}"
         shift ; shift
         ;;
     -nobuild|--nobuild)
         nobuild="yes"
         shift
         ;;
     -noint|--noint)
         noint="yes"
         force="yes"
         shift
         ;;
     -verbose|--verbose)
         verbose="yes"
         shift
         ;;
     -openssl-help)
         shift;
         "$openssl" req -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
}

# MAIN
readCommandLine "$@"

# setup variables used by the script
if test -z "${GRID_CA_DIR}"; then
    if [ -d "${localstatedir}/lib/." -a -w "${localstatedir}/lib/." ]; then
        GRID_CA_DIR="$localstatedir/lib/globus/simple_ca"
        default_loc="yes"
    else
        GRID_CA_DIR="${HOME}/.globus/simpleCA"
        default_loc="yes"
    fi
fi


CA_KEY_FILE="${GRID_CA_DIR}/private/cakey.pem"
CA_CERT_FILE="${GRID_CA_DIR}/cacert.pem"

cat <<EOF
 

    C e r t i f i c a t e    A u t h o r i t y    S e t u p

This script will setup a Certificate Authority for signing Globus
users certificates.  It will also generate a simple CA package
that can be distributed to the users of the CA.

The CA information about the certificates it distributes will
be kept in:

${GRID_CA_DIR}
EOF

create_ca_directory "${GRID_CA_DIR}"

# These functions assign a value to the variable named by their first parameter so that
# they can prompt for input
get_ca_subject grid_ca_subject
get_ca_email grid_ca_email
get_ca_lifetime CA_CERT_DAYS

save_ca_settings "${GRID_CA_DIR}" "$grid_ca_subject" "$grid_ca_email"
generate_ca_certificate "${GRID_CA_DIR}" "${grid_ca_subject}"
generate_signing_policy "${GRID_CA_DIR}" "${grid_ca_subject}"
mkdir -m 0755 -p "${sysconfdir}/grid-security/certificates/" 2> /dev/null || :
if [ -w "${sysconfdir}/grid-security/certificates/." ]; then
    setup_grid_security_dir "${GRID_CA_DIR}" "${sysconfdir}/grid-security/certificates"
elif [ -w "${datadir}/certificates" ]; then
    setup_grid_security_dir "${GRID_CA_DIR}" "${datadir}/certificates"
else
    echo "Insufficient permissions to install CA into the trusted certifiicate"
    echo "directory (tried \${sysconfdir}/grid-security/certificates and"
    echo "\${datadir}/certificates)"
fi

cahash="$("$openssl" x509 -in "$(construct_path ${_cadir}/cacert.pem)" -noout -hash)"

if [ -z "$nobuild" ]; then
    grid-ca-package ${backward_compatible:+-b} ${gpt_package:+-g} -cadir "${GRID_CA_DIR}"
fi

exit 0
