aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xfiles/certreq.sh138
1 files changed, 120 insertions, 18 deletions
diff --git a/files/certreq.sh b/files/certreq.sh
index 132c049..e1638d4 100755
--- a/files/certreq.sh
+++ b/files/certreq.sh
@@ -6,25 +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
+# 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.
@@ -39,6 +40,8 @@ version ${certreqversion}
--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.
@@ -74,7 +77,7 @@ openssl_req() {
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)
@@ -150,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}"
;;
@@ -265,7 +268,7 @@ action_get_cert() {
# 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}"
@@ -284,9 +287,9 @@ action_get_cert() {
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}"
@@ -341,9 +344,9 @@ action_request() {
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}"
@@ -352,7 +355,7 @@ action_request() {
echo "DISPOSITION=${DISPOSITION}"
echo "MESSAGE=${MESSAGE}"
}
-
+
}
action_fetch() {
@@ -393,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}"
@@ -413,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() {
@@ -464,8 +476,10 @@ parseFlag() {
"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}"; }
}
@@ -558,7 +572,9 @@ 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 CERTREQ_OPENSSL_CONF defined by default. Just use the system default unless specified.
+# 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}";
@@ -586,10 +602,13 @@ 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/;' )"
-# calculate openssl_conf string if necessary
+# verify openssl_conf dependency
if test -n "${CERTREQ_OPENSSL_CONF}" ;
then
- CERTREQ_openssl_config="-config ${CERTREQ_OPENSSL_CONF}"
+ 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
@@ -605,6 +624,87 @@ fi
#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"
@@ -627,7 +727,7 @@ 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}" "${CERTREQ_OPENSSL_BIN}" "${CERTREQ_openssl_config}"
@@ -680,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}"
@@ -699,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