aboutsummaryrefslogtreecommitdiff
path: root/files
diff options
context:
space:
mode:
Diffstat (limited to 'files')
-rwxr-xr-xfiles/certreq.sh171
1 files changed, 146 insertions, 25 deletions
diff --git a/files/certreq.sh b/files/certreq.sh
index 5a21205..e1638d4 100755
--- a/files/certreq.sh
+++ b/files/certreq.sh
@@ -6,24 +6,26 @@
# 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:
+# 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
# 2018-06-19 Fix get number of ca cert
# 2018-07-30 add error checking on the request and authorization
# 2018-08-16 update error checking and exit codes
+# 2018-09-10 add CERTREQ_OPENSSL_BIN and CERTREQ_OPENSSL_CONF values, and SAN support
# 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
+# subjectaltname in openssl.cnf https://bgstack15.wordpress.com/2017/05/21/generate-certificate-with-subjectaltname-attributes-in-freeipa/
# Improve:
fiversion="2017-10-10x"
-certreqversion="2018-08-16a"
+certreqversion="2018-09-10b"
usage() {
less -F >&2 <<ENDUSAGE
-usage: certreq.sh [-dhV] [-u username] [-p password] [-w tempdir] [-t template] [--cn CN] [--ca <CA hostname>] [-l|-g] [--list|--csr /path/to/file|--fetch|--request] [--no-ca] [--reqid <reqid_string>]
+usage: certreq.sh [-dhV] [-u username] [-p password] [-w tempdir] [-t template] [--cn CN] [--ca <CA hostname>] [-l|-g] [--list|--csr /path/to/file|--fetch|--request] [--no-ca] [--reqid <reqid_string>] [--openssl-bin /bin/openssl] [--openssl-conf /opt/openssl.cnf]
version ${certreqversion}
-d debug Show debugging info, including parsed variables.
-h usage Show this usage block.
@@ -36,6 +38,10 @@ version ${certreqversion}
--ca CA hostname or base URL. Example: ca2.example.com
--reqid <value> Request ID. Needed by --fetch action.
--no-ca Skip downloading the CA cert chain.
+ --openssl-bin <value> Use this binary for openssl. Default is "openssl"
+ --openssl-conf <value> Use this config for openssl. Default is none.
+ --dnssans <value> Use a pipe-delimited set of values as subjectAltName dns entries.
+ --ipsans <value> Use a pipe-delimited set of values as subjectAltName ip entries.
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.
@@ -59,7 +65,7 @@ ENDUSAGE
# DEFINE FUNCTIONS
openssl_req() {
- # call: openssl_req "${CERTREQ_CNPARAM}" "${CERTREQ_SUBJECT}" "${CERTREQ_ACTION}" "${CERTREQ_CSR}"
+ # call: openssl_req "${CERTREQ_CNPARAM}" "${CERTREQ_SUBJECT}" "${CERTREQ_ACTION}" "${CERTREQ_CSR}" "${CERTREQ_OPENSSL_BIN}" "${CERTREQ_openssl_config}"
# outputs:
# vars: ${CSR} ${DATA} ${CERTATTRIB}
# files: ${CERTREQ_WORKDIR}/${this_filename}.crt ${CERTREQ_WORKDIR}/${thisfilename}.key
@@ -69,7 +75,9 @@ openssl_req() {
local this_subject="${2}"
local this_action="${3}"
local this_csr="${4}"
-
+ local this_openssl_bin="${5}"
+ local this_openssl_config="${6}"
+
debuglev 8 && echo "Action ${this_action}"
case "${this_action}" in
*-csr)
@@ -88,7 +96,7 @@ openssl_req() {
esac
;;
*)
- openssl req -new -nodes \
+ "${this_openssl_bin}" req ${this_openssl_config} -new -nodes \
-out "${CERTREQ_WORKDIR}/${this_filename}.csr" \
-keyout "${CERTREQ_WORKDIR}/${this_filename}.key" \
-subj "${this_subject}"
@@ -145,7 +153,7 @@ submit_csr() {
-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=" )"
+ --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}"
;;
@@ -246,21 +254,21 @@ get_latest_ca_cert_chain() {
-H 'Content-Type: application/x-www-form-urlencoded' )"
# CONVERT TO PEM
- echo "${CURRENT_P7B}" | openssl pkcs7 -print_certs -out "${CERTREQ_TEMPFILE}"
+ echo "${CURRENT_P7B}" | "${CERTREQ_OPENSSL_BIN}" 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>/dev/null | sed -r -e 's/^.*CN=[A-Za-z0-9]+\.//;' )"
+ CA_DOMAIN="$( "${CERTREQ_OPENSSL_BIN}" x509 -in "${CERTREQ_TEMPFILE}" -noout -issuer 2>/dev/null | 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}"
+ # call: action_get_cert "${CERTREQ_CNPARAM}" "${CERTREQ_SUBJECT}" "${CERTREQ_USER}:${CERTREQ_PASS}" "${CERTREQ_CA}" "${CERTREQ_CAHOST}" "${CERTREQ_ACTION}" "${CERTREQ_CSR}" "${CERTREQ_OPENSSL_BIN}" "${CERTREQ_config_string}"
# outputs:
# vars: ${curloutput}
- # files: ${CHAIN_FILE} ${CERTREQ_CNPARAM}.crt and .key and
+ # files: ${CHAIN_FILE} ${CERTREQ_CNPARAM}.crt and .key and
debuglev 9 && ferror "$FUNCNAME $@"
local this_cnparam="${1}"
@@ -270,16 +278,18 @@ action_get_cert() {
local this_ca_host="${5}"
local this_action="${6}"
local this_csr="${7}"
+ local this_openssl_bin="${8}"
+ local this_openssl_config="${9}"
# GENERATE PRIVATE KEY
- openssl_req "${this_cnparam}" "${this_subject}" "${this_action}" "${this_csr}"
+ openssl_req "${this_cnparam}" "${this_subject}" "${this_action}" "${this_csr}" "${this_openssl_bin}" "${this_openssl_config}"
debuglev 8 && {
echo "CSR=${CSR}"
echo "DATA=${DATA}"
echo "CERTATTRIB=${CERTATTRIB}"
- }
+ }
- # SUBMIT CERTIFICATE SIGNING REQUEST
+ # SUBMIT CERTIFICATE SIGNING REQUEST
submit_csr "${this_user_string}" "${this_ca}" "${this_ca_host}" "${CSR}" "${CERTATTRIB}" "${this_action}"
debuglev 8 && {
echo "FULLPAGE=${FULLPAGE}"
@@ -315,7 +325,7 @@ action_get_cert() {
}
action_request() {
- # call: action_request "${CERTREQ_CNPARAM}" "${CERTREQ_SUBJECT}" "${CERTREQ_USER}:${CERTREQ_PASS}" "${CERTREQ_CA}" "${CERTREQ_CAHOST}" "${CERTREQ_ACTION}" "${CERTREQ_CSR}"
+ # call: action_request "${CERTREQ_CNPARAM}" "${CERTREQ_SUBJECT}" "${CERTREQ_USER}:${CERTREQ_PASS}" "${CERTREQ_CA}" "${CERTREQ_CAHOST}" "${CERTREQ_ACTION}" "${CERTREQ_CSR}" "${CERTREQ_OPENSSL_BIN}" "${CERTREQ_openssl_config}"
debuglev 9 && ferror "$FUNCNAME $@"
local this_cnparam="${1}"
@@ -325,16 +335,18 @@ action_request() {
local this_ca_host="${5}"
local this_action="${6}"
local this_csr="${7}"
+ local this_openssl_bin="${8}"
+ local this_openssl_config="${9}"
# GENERATE PRIVATE KEY
- openssl_req "${this_cnparam}" "${this_subject}" "${this_action}" "${this_csr}"
+ openssl_req "${this_cnparam}" "${this_subject}" "${this_action}" "${this_csr}" "${this_openssl_bin}" "${this_openssl_config}"
debuglev 8 && {
echo "CSR=${CSR}"
echo "DATA=${DATA}"
echo "CERTATTRIB=${CERTATTRIB}"
- }
+ }
- # SUBMIT CERTIFICATE SIGNING REQUEST
+ # SUBMIT CERTIFICATE SIGNING REQUEST
submit_csr "${this_user_string}" "${this_ca}" "${this_ca_host}" "${CSR}" "${CERTATTRIB}" "${this_action}"
debuglev 8 && {
echo "FULLPAGE=${FULLPAGE}"
@@ -343,7 +355,7 @@ action_request() {
echo "DISPOSITION=${DISPOSITION}"
echo "MESSAGE=${MESSAGE}"
}
-
+
}
action_fetch() {
@@ -384,7 +396,7 @@ action_fetch() {
action_list_templates() {
# call: action_list_templates "${CERTREQ_USER}:${CERTREQ_PASS}" "${CERTREQ_CA}" "${CERTREQ_CAHOST}"
debuglev 9 && ferror "$FUNCNAME $@"
-
+
local this_user_string="${1}"
local this_ca="${2}"
local this_ca_host="${3}"
@@ -404,6 +416,15 @@ action_list_templates() {
}
+file_section_has_value() {
+ # call: file_section_has_value "${thisfile}" "${sectionregex}" "${nextsectionregex}" "${valueregex}"
+ ___fshv_file="${1}"
+ ___fshv_sectionregex="${2}"
+ ___fshv_nextsectionregex="${3}"
+ ___fshv_valueregex="${4}"
+ test -n "$( sed -n -r -e "/${___fshv_sectionregex}/,/${___fshv_nextsectionregex}/p" "${___fshv_file}" 2>/dev/null | grep -q -r -e "${___fshv_valueregex}" )"
+}
+
# DEFINE TRAPS
clean_certreq() {
@@ -453,8 +474,12 @@ parseFlag() {
"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}";;
+ "openssl-bin" | "openssl" | "opensslbin" | "openssl-binary" | "opensslexec" | "openssl-exec" ) getval; CERTREQ_OPENSSL_BIN="${tempval}";;
+ "openssl-conf" | "opensslconf" | "openssl_conf" ) getval; CERTREQ_OPENSSL_CONF="${tempval}";;
+ "dnssans" | "dns-sans" | "dnssan" | "dns-san" ) getval; CERTREQ_DNSSANS="${tempval}";;
+ "ipsans" | "ip-sans" | "ipsan" | "ip-san" ) getval; CERTREQ_IPSANS="${tempval}";;
esac
-
+
debuglev 10 && { test ${hasval} -eq 1 && ferror "flag: ${flag} = ${tempval}" || ferror "flag: ${flag}"; }
}
@@ -546,6 +571,10 @@ define_if_new CERTREQ_CLEANUP_SEC 300
logfile="$( TMPDIR="${CERTREQ_WORKDIR}" mktemp -t tmp.XXXXXXXXXX )"
CERTREQ_TEMPFILE="$( TMPDIR="${CERTREQ_WORKDIR}" mktemp -t tmp.XXXXXXXXXX )"
define_if_new CERTREQ_ACTION "generate"
+define_if_new CERTREQ_OPENSSL_BIN "openssl"
+# no default CERTREQ_OPENSSL_CONF. Just use system default.
+# no default CERTREQ_DNSSANS, which is pipe-delimited.
+# no default CERTREQ_IPSANS, which is pipe-delimited.
# calculate the subject
if test -n "${CERTREQ_CNPARAM}";
@@ -573,6 +602,15 @@ define_if_new CERTREQ_CA "http://ca2.ad.example.com"
# generate cahost
CERTREQ_CAHOST="$( echo "${CERTREQ_CA}" | sed -r -e 's/https?:\/\///g' -e 's/(\.[a-z]{2,3})\/$/\1/;' )"
+# verify openssl_conf dependency
+if test -n "${CERTREQ_OPENSSL_CONF}" ;
+then
+ if ! test -r "${CERTREQ_OPENSSL_CONF}" ;
+ then
+ ferror "${scriptfile}: 1004. CERTREQ_OPENSSL_CONF file ${CERTREQ_OPENSSL_CONF} is invalid or not found. Aborted." && exit 1004
+ fi
+fi
+
## REACT TO BEING A CRONJOB
#if test ${is_cronjob} -eq 1;
#then
@@ -586,6 +624,87 @@ CERTREQ_CAHOST="$( echo "${CERTREQ_CA}" | sed -r -e 's/https?:\/\///g' -e 's/(\.
#trap "CTRLZ" 18
trap "clean_certreq" 0
+# PREPARE CUSTOM CONF FILE WITH ANY SUBJECTALTNAME ENTRIES
+if test -n "${CERTREQ_DNSSANS}${CERTREQ_IPSANS}" ;
+then
+
+ # initialize new conf file
+ CERTREQ_openssl_conf_new="$( TMPDIR="${CERTREQ_WORKDIR}" mktemp -t openssl.cnf.XXXXXXXXXX )"
+
+ # select conf file
+ if test -z "${CERTREQ_OPENSSL_CONF}" ;
+ then
+ # need to calculate the default conf file
+ CERTREQ_first_found="$( find /etc/pki/tls/openssl.cnf /usr/local/ssl/openssl.cnf 2>/dev/null | head -n1 )"
+ if ! test -r "${CERTREQ_first_found}" ;
+ then
+ ferror "${scriptfile}: 1004. Cannot determine default openssl.cnf for inserting SANs. Aborted." && exit 1004
+ else
+ CERTREQ_OPENSSL_CONF="${CERTREQ_first_found}" && unset CERTREQ_first_found
+ fi
+ fi
+
+ # copy in contents
+ /bin/cp -p "${CERTREQ_OPENSSL_CONF}" "${CERTREQ_openssl_conf_new}"
+ CERTREQ_OPENSSL_CONF="${CERTREQ_openssl_conf_new}" && unset CERTREQ_openssl_conf_new
+
+ # make modifications, add v3_req
+ #if test -z "$( sed -n -r -e '/^\s*\[ req \]/,/^\s*\[/p' "${CERTREQ_OPENSSL_CONF}" 2>/dev/null | grep -r -e '^\s*req_extensions' )" ;
+ if ! file_section_has_value "${CERTREQ_OPENSSL_CONF}" "^\s*\[ req \]" "^\s*\[" "^\s*req_extensions" ;
+ then
+ # need to add req_extensions = v3_req to [ req ]
+ # get line number of [ req ]
+ CERTREQ_line_num="$( awk '/^\s*\[ req \]/{print FNR}' "${CERTREQ_OPENSSL_CONF}" 2>/dev/null )"
+ if test -z "${CERTREQ_line_num}" ;
+ then
+ # no line containing
+ echo "[ req ]" >> "${CERTREQ_OPENSSL_CONF}"
+ CERTREQ_line_num="$( wc -l < "${CERTREQ_OPENSSL_CONF}" )"
+ fi
+ sed -i -r -e "${CERTREQ_line_num}areq_extensions = v3_req" "${CERTREQ_OPENSSL_CONF}"
+ fi
+
+ # make modifications, add SAN to [ v3_req ]
+ #if test -z "$( sed -n -r -e '/^\s*\[ v3_req \]/,/^\s*\[/p' "${CERTREQ_OPENSSL_CONF}" 2>/dev/null | grep
+ if ! file_section_has_value "${CERTREQ_OPENSSL_CONF}" "^\s*\[ v3_req \]" "^\s*\[" "^\s*subjectAltName" ;
+ then
+ # need to add it
+ CERTREQ_line_num="$( awk '/^\s*\[ v3_req \]/{print FNR}' "${CERTREQ_OPENSSL_CONF}" 2>/dev/null )"
+ if test -z "${CERTREQ_line_num}" ;
+ then
+ # need to add the section too
+ echo "[ v3_req ]" >> "${CERTREQ_OPENSSL_CONF}"
+ CERTREQ_line_num="$( wc -l < "${CERTREQ_OPENSSL_CONF}" )"
+ fi
+ sed -i -r -e "${CERTREQ_line_num}asubjectAltName = @alt_names" "${CERTREQ_OPENSSL_CONF}"
+ fi
+
+ # make modifications, add subject alt names section
+ # start by preparing the exact string to make
+ CERTREQ_san_lines="$( test -n "${CERTREQ_DNSSANS}" && echo "${CERTREQ_DNSSANS}" | tr '|' '\n' | awk 'BEGIN{a=0} {a=a+1 ; print "DNS."a" = "$0 ;}' ; test -n "${CERTREQ_IPSANS}" && echo "${CERTREQ_IPSANS}" | tr '|' '\n' | awk 'BEGIN{a=0} {a=a+1 ; print "IP."a" = "$0 ;}')"
+ # add to file
+ if ! file_section_has_value "${CERTREQ_OPENSSL_CONF}" "^\s*\[alt_names\]" "^\s*\[" "=" ;
+ then
+ # need to add the values
+ CERTREQ_line_num="$( awk '/^\s*\[alt_names\]/{print FNR}' "${CERTREQ_OPENSSL_CONF}" 2>/dev/null )"
+ if test -z "${CERTREQ_line_num}" ;
+ then
+ # need to add the section too
+ echo "[alt_names]" >> "${CERTREQ_OPENSSL_CONF}"
+ CERTREQ_line_num="$( wc -l < "${CERTREQ_OPENSSL_CONF}" )"
+ fi
+ echo "${CERTREQ_san_lines}" | while read line ; do sed -i -r -e "${CERTREQ_line_num}a${line}" "${CERTREQ_OPENSSL_CONF}" ; CERTREQ_line_num=$(( CERTREQ_line_num + 1 )) ; done
+ fi
+
+ # clean up
+ unset CERTREQ_line_num
+fi
+
+if test -n "${CERTREQ_OPENSSL_CONF}" ;
+then
+ CERTREQ_openssl_config="-config ${CERTREQ_OPENSSL_CONF}"
+fi
+
# DEBUG SIMPLECONF
debuglev 5 && {
ferror "Using values"
@@ -608,10 +727,10 @@ debuglev 5 && {
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}"
+ action_request "${CERTREQ_CNPARAM}" "${CERTREQ_SUBJECT}" "${CERTREQ_USER}:${CERTREQ_PASS}" "${CERTREQ_CA}" "${CERTREQ_CAHOST}" "${CERTREQ_ACTION}" "${CERTREQ_CSR}" "${CERTREQ_OPENSSL_BIN}" "${CERTREQ_openssl_config}"
;;
fetch)
@@ -622,14 +741,14 @@ debuglev 5 && {
*)
# 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}"
+ action_get_cert "${CERTREQ_CNPARAM}" "${CERTREQ_SUBJECT}" "${CERTREQ_USER}:${CERTREQ_PASS}" "${CERTREQ_CA}" "${CERTREQ_CAHOST}" "${CERTREQ_ACTION}" "${CERTREQ_CSR}" "${CERTREQ_OPENSSL_BIN}" "${CERTREQ_openssl_config}"
;;
esac
# CHECK EVERYTHING
failed=0 # start out with everything worked
- openssloutput="$( openssl x509 -in "${CERTREQ_WORKDIR}/${CERTREQ_CNPARAM}.crt" -noout -subject -issuer -startdate -enddate 2>/dev/null )"
+ openssloutput="$( "${CERTREQ_OPENSSL_BIN}" x509 -in "${CERTREQ_WORKDIR}/${CERTREQ_CNPARAM}.crt" -noout -subject -issuer -startdate -enddate 2>/dev/null )"
# 1 interaction with website failed: invalid login credentials or curl returned non-zero value
if echo "${MESSAGE}" | grep -qiE 'unauthorized' || test ${curloutput} -ne 0 ;
@@ -661,6 +780,7 @@ case "${CERTREQ_ACTION}" in
request)
echo "workdir: ${CERTREQ_WORKDIR}"
echo "logfile: ${logfile}"
+ test -n "${CERTREQ_OPENSSL_CONF}" && echo "openssl_conf: ${CERTREQ_OPENSSL_CONF}"
echo "csr: ${CERTREQ_WORKDIR}/${CERTREQ_CNPARAM}.csr"
echo "key: ${CERTREQ_WORKDIR}/${CERTREQ_CNPARAM}.key"
echo "reqid: ${REQUESTID}"
@@ -680,6 +800,7 @@ case "${CERTREQ_ACTION}" in
# for generate and generate-csr and everything else really
echo "workdir: ${CERTREQ_WORKDIR}"
echo "logfile: ${logfile}"
+ test -n "${CERTREQ_OPENSSL_CONF}" && echo "openssl_conf: ${CERTREQ_OPENSSL_CONF}"
echo "csr: ${CERTREQ_WORKDIR}/${CERTREQ_CNPARAM}.csr"
echo "certificate: ${CERTREQ_WORKDIR}/${CERTREQ_CNPARAM}.crt"
echo "key: ${CERTREQ_WORKDIR}/${CERTREQ_CNPARAM}.key"
bgstack15