#!/bin/sh # Filename: certreq.sh # Location: atower201:/etc/ansible/roles/certreq/files/certreq.sh # Author: bgstack15@gmail.com # Startdate: 2017-11-17 09:13:53 # Title: Script that Requests a Certificate from a Microsoft Sub-CA # Purpose: Automate host certificate generation in a domain environment # Package: ansible role certreq # History: # 2017-11-22 Add ca cert chain # 2018-04-16 Add --list and --csr options # 2018-05-07 Add actions for using a CA with manually-approved certs # Usage: in ansible role certreq # Microsoft CA cert templates have permissions on them. A user must be able to "enroll" on the template. # Reference: ftemplate.sh 2017-10-10x; framework.sh 2017-10-09a # fundamental curl statements https://stackoverflow.com/questions/31283476/submitting-base64-csr-to-a-microsoft-ca-via-curl/39722983#39722983 # Improve: fiversion="2017-10-10x" certreqversion="2018-05-07a" usage() { less -F >&2 <] [-l|-g] [--list|--csr /path/to/file|--fetch|--request] [--no-ca] [--reqid ] version ${certreqversion} -d debug Show debugging info, including parsed variables. -h usage Show this usage block. -V version Show script version number. -u username User to connect via ntlm to CA. Can be "username" or "domain\\username" -p password -w workdir Temp directory to work in. Default is \$(mktemp -d). -t template Template to request from CA. Default is "ConfigMgrLinuxClientCertificate" --cn CN to request. Default is \$( hostname -f ) --ca CA hostname or base URL. Example: ca2.example.com --reqid Request ID. Needed by --fetch action. --no-ca Skip downloading the CA cert chain. ACTIONS: --list list available templates and exit. --csr filename Provide a .csr file instead of making a new csr. Accepts "stdin" to read from standard in. --fetch Only retrieve a cert that was previously requested. Requires CERTREQ_REQID or --reqid. --request Only request a cert. Use if the cert needs to be approved manually by an admin on the server. Return values under 1000: A non-zero value is the sum of the items listed here: 0 Everything worked 1 Cert file is still a CSR 2 Cert file is html, probably due to permissions/credentials issue 4 Return code of curl statement that saves cert file is non-zero 8 Cert file does not contain whole certificate 16 Cert does not contain an issuer Return values above 1000: 1001 Help or version info displayed 1002 Count or type of flaglessvals is incorrect 1003 Incorrect OS type 1004 Unable to find dependency 1005 Not run as root or sudo 1006 Input is invalid ENDUSAGE } # DEFINE FUNCTIONS openssl_req() { # call: openssl_req "${CERTREQ_CNPARAM}" "${CERTREQ_SUBJECT}" "${CERTREQ_ACTION}" "${CERTREQ_CSR}" # outputs: # vars: ${CSR} ${DATA} ${CERTATTRIB} # files: ${CERTREQ_WORKDIR}/${this_filename}.crt ${CERTREQ_WORKDIR}/${thisfilename}.key local this_filename="${1}" local this_subject="${2}" local this_action="${3}" local this_csr="${4}" debuglev 8 && echo "Action ${this_action}" case "${this_action}" in *-csr) case "${this_csr}" in stdin) cat - > "${CERTREQ_WORKDIR}/${this_filename}.csr" ;; *) # make sure file exists if ! test -e "${this_csr}" ; then ferror "${scriptfile}: 1006. CSR file ${this_csr} is invalid or not found. Aborted." && exit 1006 fi cat "${this_csr}" > "${CERTREQ_WORKDIR}/${this_filename}.csr" ;; esac ;; *) openssl req -new -nodes \ -out "${CERTREQ_WORKDIR}/${this_filename}.csr" \ -keyout "${CERTREQ_WORKDIR}/${this_filename}.key" \ -subj "${this_subject}" ;; esac CSR="$( cat "${CERTREQ_WORKDIR}/${this_filename}.csr" | tr -d '\n\r' )" DATA="Mode=newreq&CertRequest=${CSR}&C&TargetStoreFlags=0&SaveCert=yes" CSR="$( echo ${CSR} | sed -e 's/+/%2B/g' | tr -s ' ' '+' )" CERTATTRIB="CertificateTemplate:${CERTREQ_TEMPLATE}" } submit_csr() { # call: submit_csr "${CERTREQ_USER}:${CERTREQ_PASS}" "${CERTREQ_CA}" "${CERTREQ_CAHOST}" "${CSR}" "${CERTATTRIB}" "${CERTREQ_ACTION}" # outputs: ${CERTLINK} local this_user_string="${1}" local this_ca="${2}" local this_ca_host="${3}" local this_cert="${4}" local this_cert_attrib="${5}" local this_action="${6}" case "${this_action}" in request) # request-only REQUESTID="$( curl -k -u "${this_user_string}" --ntlm \ "${this_ca}/certsrv/certfnsh.asp" \ -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' \ -H 'Accept-Encoding: gzip, deflate' \ -H 'Accept-Language: en-US,en;q=0.5' \ -H 'Connection: keep-alive' \ -H "Host: ${this_ca_host}" \ -H "Referer: ${this_ca}/certsrv/certrqxt.asp" \ -H 'User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko' \ -H 'Content-Type: application/x-www-form-urlencoded' \ --data "Mode=newreq&CertRequest=${this_cert}&CertAttrib=${this_cert_attrib}&TargetStoreFlags=0&SaveCert=yes&ThumbPrint=" | grep "' nReqId" | awk -F'= ' '{print $2}' | sed -e 's/\r//g' )" CERTLINK="${CERTREQ_CA}/certsrv/certnew.cer?ReqID=${REQUESTID}" ;; *) FULLPAGE="$( curl -k -u "${this_user_string}" --ntlm \ "${this_ca}/certsrv/certfnsh.asp" \ -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' \ -H 'Accept-Encoding: gzip, deflate' \ -H 'Accept-Language: en-US,en;q=0.5' \ -H 'Connection: keep-alive' \ -H "Host: ${this_ca_host}" \ -H "Referer: ${this_ca}/certsrv/certrqxt.asp" \ -H 'User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko' \ -H 'Content-Type: application/x-www-form-urlencoded' \ --data "Mode=newreq&CertRequest=${this_cert}&CertAttrib=${this_cert_attrib}&TargetStoreFlags=0&SaveCert=yes&ThumbPrint=" )" OUTPUTLINK="$( echo "${FULLPAGE}" | grep -A 1 'function handleGetCert() {' | tail -n 1 | cut -d '"' -f 2 )" CERTLINK="${this_ca}/certsrv/${OUTPUTLINK}" ;; esac } fetch_signed_cert() { # call: fetch_signed_cert "${CERTREQ_USER}:${CERTREQ_PASS}" "${CERTREQ_CA}" "${CERTREQ_CAHOST}" "${CERTLINK}" "${CERTREQ_CNPARAM}" "${CERTREQ_ACTION}" "${CERTREQ_REQID}" # output: # vars: ${finaloutput} # files: ${CERTREQ_WORKDIR}/${this_filename}.crt local this_user_string="${1}" local this_ca="${2}" local this_ca_host="${3}" local this_certlink="${4}" local this_filename="${5}" local this_action="${6}" local this_reqid="${7}" case "${this_action}" in fetch) if test -z "${this_reqid}" ; then ferror "${scriptfile}: 1006. For --fetch, please provide --reqid REQID. Aborted." exit 1006 fi this_certlink="${CERTREQ_CA}/certsrv/certnew.cer?ReqID=${this_reqid}" ;; *) : ;; esac curl -k -u "${this_user_string}" --ntlm "${this_certlink}" \ -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' \ -H 'Accept-Encoding: gzip, deflate' \ -H 'Accept-Language: en-US,en;q=0.5' \ -H 'Connection: keep-alive' \ -H "Host: ${this_ca_host}" \ -H "Referer: ${this_ca}/certsrv/certrqxt.asp" \ -H 'User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko' \ -H 'Content-Type: application/x-www-form-urlencoded' > "${CERTREQ_WORKDIR}/${this_filename}.crt" finaloutput=$? } get_number_of_current_ca_cert() { # call: get_number_of_current_ca_cert "${CERTREQ_USER}:${CERTREQ_PASS}" "${CERTREQ_CA}" "${CERTREQ_CAHOST}" # outputs: ${CURRENTNUM} local this_user_string="${1}" local this_ca="${2}" local this_ca_host="${3}" RESPONSE="$( curl -s -k -u "${this_user_string}" --ntlm \ "${this_ca}/certsrv/certcarc.asp" \ -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' \ -H 'Accept-Encoding: gzip, deflate' \ -H 'Accept-Language: en-US,en;q=0.5' \ -H 'Connection: keep-alive' \ -H "Host: ${this_ca_host}" \ -H "Referer: ${this_ca}/certsrv/certrqxt.asp" \ -H 'User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko' \ -H 'Content-Type: application/x-www-form-urlencoded' )" CURRENTNUM="$( echo "${RESPONSE}" | grep -cE 'Option' )" } get_latest_ca_cert_chain() { # call: get_latest_ca_cert_chain "${CERTREQ_USER}:${CERTREQ_PASS}" "${CERTREQ_CA}" "${CERTREQ_CAHOST}" "${CURRENTNUM}" # outputs: # files: ${CHAIN_FILE} local this_user_string="${1}" local this_ca="${2}" local this_ca_host="${3}" local this_num="${4}" CURRENT_P7B="$( curl -s -k -u "${this_user_string}" --ntlm \ "${this_ca}/certsrv/certnew.p7b?ReqID=CACert&Renewal=${this_num}" \ -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' \ -H 'Accept-Encoding: gzip, deflate' \ -H 'Accept-Language: en-US,en;q=0.5' \ -H 'Connection: keep-alive' \ -H "Host: ${this_ca_host}" \ -H "Referer: ${this_ca}/certsrv/certrqxt.asp" \ -H 'User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko' \ -H 'Content-Type: application/x-www-form-urlencoded' )" # CONVERT TO PEM echo "${CURRENT_P7B}" | openssl pkcs7 -print_certs -out "${CERTREQ_TEMPFILE}" # RENAME TO PROPER FILENAME # will read only the first cert, so get domain of issuer of it. CA_DOMAIN="$( openssl x509 -in "${CERTREQ_TEMPFILE}" -noout -issuer 2>&1 | sed -r -e 's/^.*CN=[A-Za-z0-9]+\.//;' )" CHAIN_FILE="chain-${CA_DOMAIN}.crt" mv -f "${CERTREQ_TEMPFILE}" "${CERTREQ_WORKDIR}/${CHAIN_FILE}" 1>/dev/null 2>&1 } action_get_cert() { # call: action_get_cert "${CERTREQ_CNPARAM}" "${CERTREQ_SUBJECT}" "${CERTREQ_USER}:${CERTREQ_PASS}" "${CERTREQ_CA}" "${CERTREQ_CAHOST}" "${CERTREQ_ACTION}" "${CERTREQ_CSR}" # outputs: # vars: ${finaloutput} # files: ${CHAIN_FILE} ${CERTREQ_CNPARAM}.crt and .key and local this_cnparam="${1}" local this_subject="${2}" local this_user_string="${3}" local this_ca="${4}" local this_ca_host="${5}" local this_action="${6}" local this_csr="${7}" # GENERATE PRIVATE KEY openssl_req "${this_cnparam}" "${this_subject}" "${this_action}" "${this_csr}" debuglev 8 && { echo "CSR=${CSR}" echo "DATA=${DATA}" echo "CERTATTRIB=${CERTATTRIB}" } # SUBMIT CERTIFICATE SIGNING REQUEST submit_csr "${this_user_string}" "${this_ca}" "${this_ca_host}" "${CSR}" "${CERTATTRIB}" "${this_action}" debuglev 8 && { echo "FULLPAGE=${FULLPAGE}" echo "OUTPUTLINK=${OUTPUTLINK}" echo "CERTLINK=${CERTLINK}" } # FETCH SIGNED CERTIFICATE fetch_signed_cert "${this_user_string}" "${this_ca}" "${this_ca_host}" "${CERTLINK}" "${this_cnparam}" "${this_action}" "REQID-not-needed-for-this-action" debuglev 8 && { echo "finaloutput=${finaloutput}" } if ! fistruthy "${CERTREQ_SKIP_CACERTS}" ; then # GET NUMBER OF CURRENT CA CERT get_number_of_current_ca_cert "${this_user_string}" "${this_ca}" "${this_ca_host}" debuglev 8 && { echo "CURRENTNUM=${CURRENTNUM}" } # GET LATEST CA CERT CHAIN get_latest_ca_cert_chain "${this_user_string}" "${this_ca}" "${this_ca_host}" "${CURRENTNUM}" debuglev 8 && { echo "CHAIN_FILE=${CHAIN_FILE}" } fi } action_request() { # call: action_request "${CERTREQ_CNPARAM}" "${CERTREQ_SUBJECT}" "${CERTREQ_USER}:${CERTREQ_PASS}" "${CERTREQ_CA}" "${CERTREQ_CAHOST}" "${CERTREQ_ACTION}" "${CERTREQ_CSR}" local this_cnparam="${1}" local this_subject="${2}" local this_user_string="${3}" local this_ca="${4}" local this_ca_host="${5}" local this_action="${6}" local this_csr="${7}" # GENERATE PRIVATE KEY openssl_req "${this_cnparam}" "${this_subject}" "${this_action}" "${this_csr}" debuglev 8 && { echo "CSR=${CSR}" echo "DATA=${DATA}" echo "CERTATTRIB=${CERTATTRIB}" } # SUBMIT CERTIFICATE SIGNING REQUEST submit_csr "${this_user_string}" "${this_ca}" "${this_ca_host}" "${CSR}" "${CERTATTRIB}" "${this_action}" debuglev 8 && { echo "FULLPAGE=${FULLPAGE}" echo "OUTPUTLINK=${OUTPUTLINK}" echo "CERTLINK=${CERTLINK}" } } action_fetch() { # call: action_fetch "${CERTREQ_CNPARAM}" "${CERTREQ_USER}:${CERTREQ_PASS}" "${CERTREQ_CA}" "${CERTREQ_CAHOST}" "${CERTREQ_ACTION}" "${CERTREQ_REQID}" local this_cnparam="${1}" local this_user_string="${2}" local this_ca="${3}" local this_ca_host="${4}" local this_action="${5}" local this_reqid="${6}" fetch_signed_cert "${this_user_string}" "${this_ca}" "${this_ca_host}" "WILL-BE-REPLACED" "${this_cnparam}" "${this_action}" "${this_reqid}" debuglev 8 && { echo "finaloutput=${finaloutput}" } if ! fistruthy "${CERTREQ_SKIP_CACERTS}" ; then # GET NUMBER OF CURRENT CA CERT get_number_of_current_ca_cert "${this_user_string}" "${this_ca}" "${this_ca_host}" debuglev 8 && { echo "CURRENTNUM=${CURRENTNUM}" } # GET LATEST CA CERT CHAIN get_latest_ca_cert_chain "${this_user_string}" "${this_ca}" "${this_ca_host}" "${CURRENTNUM}" debuglev 8 && { echo "CHAIN_FILE=${CHAIN_FILE}" } fi } action_list_templates() { # call: action_list_templates "${CERTREQ_USER}:${CERTREQ_PASS}" "${CERTREQ_CA}" "${CERTREQ_CAHOST}" local this_user_string="${1}" local this_ca="${2}" local this_ca_host="${3}" RESPONSE="$( curl -s -k -u "${this_user_string}" --ntlm \ "${this_ca}/certsrv/certrqxt.asp" \ -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' \ -H 'Accept-Encoding: gzip, deflate' \ -H 'Accept-Language: en-US,en;q=0.5' \ -H 'Connection: keep-alive' \ -H "Host: ${this_ca_host}" \ -H "Referer: ${this_ca}/certsrv/certrqus.asp" \ -H 'User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko' \ -H 'Content-Type: application/x-www-form-urlencoded' )" AVAILABLE_TEMPLATES="$( echo "${RESPONSE}" | grep -E -- "" |awk -F';' -v 'a=0' 'BEGIN{OFS=","} {a=a+1; print a,$2,$14}' | sed -r -e 's/">\s*$//;' )" # goal: set a variable of the items, probably comma-delimited } # DEFINE TRAPS clean_certreq() { # use at end of entire script if you need to clean up tmpfiles #rm -f ${tmpfile} 1>/dev/null 2>&1 if test -z "${CR_NC}"; then nohup /bin/bash </dev/null 2>&1 & sleep "${CERTREQ_CLEANUP_SEC:-300}" ; /bin/rm -r "${CERTREQ_WORKDIR:-NOTHINGTODELETE}" 2>/dev/null ; EOF fi } CTRLC() { # use with: trap "CTRLC" 2 # useful for controlling the ctrl+c keystroke : } CTRLZ() { # use with: trap "CTRLZ" 18 # useful for controlling the ctrl+z keystroke : } parseFlag() { flag="$1" hasval=0 case ${flag} in # INSERT FLAGS HERE "d" | "debug" | "DEBUG" | "dd" ) setdebug; ferror "debug level ${debug}";; "usage" | "help" | "h" ) usage; exit 1001;; "V" | "fcheck" | "version" ) ferror "${scriptfile} version ${certreqversion}"; exit 1001;; "u" | "user" | "username" ) getval; CERTREQ_USER="${tempval}";; "p" | "pass" | "password" ) getval; CERTREQ_PASS="${tempval}";; "w" | "work" | "workdir" ) getval; CERTREQ_WORKDIR="${tempval}";; "t" | "temp" | "template" ) getval; CERTREQ_TEMPLATE="${tempval}";; "cn" | "common-name" | "commonname" ) getval; CERTREQ_CNPARAM="${tempval}";; "ca" | "certauthority" | "cauthority" ) getval; CERTREQ_CAPARAM="${tempval}";; "c" | "conf" | "conffile" | "config" ) getval; conffile="${tempval}";; "nc" | "nocleanup" ) CR_NC=1;; "l" | "list" ) CERTREQ_ACTION="list";; "g" | "generate" ) CERTREQ_ACTION="generate";; "csr" ) CERTREQ_ACTION="${CERTREQ_ACTION:-generate}-csr"; getval; CERTREQ_CSR="${tempval}" ;; "fetch" | "fetch-only" ) CERTREQ_ACTION="fetch";; "request" | "request-only" ) CERTREQ_ACTION="request";; "no-ca" | "noca" ) CERTREQ_SKIP_CACERTS=1;; "req" | "reqid" | "req-id" | "request" | "requestid" | "request-id" ) getval; CERTREQ_REQID="${tempval}";; esac debuglev 10 && { test ${hasval} -eq 1 && ferror "flag: ${flag} = ${tempval}" || ferror "flag: ${flag}"; } } # DETERMINE LOCATION OF FRAMEWORK while read flocation; do if test -f ${flocation} && test "$( sh ${flocation} --fcheck )" -ge 20170608; then frameworkscript="${flocation}"; break; fi; done <&2 && exit 1004 # INITIALIZE VARIABLES # variables set in framework: # today server thistty scriptdir scriptfile scripttrim # is_cronjob stdin_piped stdout_piped stderr_piped sendsh sendopts . ${frameworkscript} || echo "$0: framework did not run properly. Continuing..." 1>&2 infile1= outfile1= #logfile=${scriptdir}/${scripttrim}.${today}.out # defined farther down define_if_new interestedparties "bgstack15@gmail.com" # SIMPLECONF define_if_new default_conffile "/tmp/certreq.conf" define_if_new defuser_conffile ~/.config/certreq/certreq.conf # REACT TO OPERATING SYSTEM TYPE case $( uname -s ) in Linux) [ ];; *) echo "${scriptfile}: 3. Indeterminate OS: $( uname -s )" 1>&2 && exit 1003;; esac ## REACT TO ROOT STATUS #case ${is_root} in # 1) # proper root # [ ] ;; # sudo) # sudo to root # [ ] ;; # "") # not root at all # #ferror "${scriptfile}: 5. Please run as root or sudo. Aborted." # #exit 1005 # [ ] # ;; #esac # SET CUSTOM SCRIPT AND VALUES #setval 1 sendsh sendopts<&2 } # MAIN LOOP { case "${CERTREQ_ACTION}" in list) action_list_templates "${CERTREQ_USER}:${CERTREQ_PASS}" "${CERTREQ_CA}" "${CERTREQ_CAHOST}" ;; request) # alias of "request-only" action_request "${CERTREQ_CNPARAM}" "${CERTREQ_SUBJECT}" "${CERTREQ_USER}:${CERTREQ_PASS}" "${CERTREQ_CA}" "${CERTREQ_CAHOST}" "${CERTREQ_ACTION}" "${CERTREQ_CSR}" ;; fetch) # alias of "fetch-only" action_fetch "${CERTREQ_CNPARAM}" "${CERTREQ_USER}:${CERTREQ_PASS}" "${CERTREQ_CA}" "${CERTREQ_CAHOST}" "${CERTREQ_ACTION}" "${CERTREQ_REQID}" ;; *) # default action="generate" # also catches "generate-csr" action_get_cert "${CERTREQ_CNPARAM}" "${CERTREQ_SUBJECT}" "${CERTREQ_USER}:${CERTREQ_PASS}" "${CERTREQ_CA}" "${CERTREQ_CAHOST}" "${CERTREQ_ACTION}" "${CERTREQ_CSR}" # CHECK EVERYTHING failed=0 openssloutput="$( openssl x509 -in "${CERTREQ_WORKDIR}/${CERTREQ_CNPARAM}.crt" -noout -subject -issuer -startdate -enddate 2>/dev/null )" grep -qE -- 'REQUEST--' "${CERTREQ_WORKDIR}/${CERTREQ_CNPARAM}.crt" && failed=$(( failed + 1 )) grep -qiE '\<\/?body\>' "${CERTREQ_WORKDIR}/${CERTREQ_CNPARAM}.crt" && failed=$(( failed + 2 )) test ${finaloutput} -ne 0 && failed=$(( failed + 4 )) grep -qE -- '--END CERTIFICATE--' "${CERTREQ_WORKDIR}/${CERTREQ_CNPARAM}.crt" || failed=$(( failed + 8 )) #echo "${openssloutput}" | grep -qE "subject.*${CERTREQ_SUBJECT}" || failed=$(( failed + 16 )) echo "${openssloutput}" | grep -qE "issuer.*" || failed=$(( failed + 16 )) ;; esac } 1> ${logfile} 2>&1 case "${CERTREQ_ACTION}" in list) # echo the variable from action_list_templates echo "${AVAILABLE_TEMPLATES}" ;; request) echo "workdir: ${CERTREQ_WORKDIR}" echo "logfile: ${logfile}" echo "csr: ${CERTREQ_WORKDIR}/${CERTREQ_CNPARAM}.csr" echo "key: ${CERTREQ_WORKDIR}/${CERTREQ_CNPARAM}.key" echo "reqid: ${REQUESTID}" ;; fetch) echo "workdir: ${CERTREQ_WORKDIR}" echo "logfile: ${logfile}" echo "certificate: ${CERTREQ_WORKDIR}/${CERTREQ_CNPARAM}.crt" ! fistruthy "${CERTREQ_SKIP_CACERTS}" && echo "chain: ${CERTREQ_WORKDIR}/${CHAIN_FILE}" ;; generate*) # for generate and generate-csr and everything else really echo "workdir: ${CERTREQ_WORKDIR}" echo "logfile: ${logfile}" echo "csr: ${CERTREQ_WORKDIR}/${CERTREQ_CNPARAM}.crt" echo "certificate: ${CERTREQ_WORKDIR}/${CERTREQ_CNPARAM}.crt" echo "key: ${CERTREQ_WORKDIR}/${CERTREQ_CNPARAM}.key" ! fistruthy "${CERTREQ_SKIP_CACERTS}" && echo "chain: ${CERTREQ_WORKDIR}/${CHAIN_FILE}" ;; esac clean_certreq exit "${failed:-0}" # EMAIL LOGFILE #${sendsh} ${sendopts} "${server} ${scriptfile} out" ${logfile} ${interestedparties}