From e7cfa31919e588d473510109392b35e4d690ac2e Mon Sep 17 00:00:00 2001 From: "B. Stack" Date: Fri, 13 Sep 2024 15:10:07 -0400 Subject: initial commit --- cepceslib.sh | 278 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 278 insertions(+) create mode 100755 cepceslib.sh (limited to 'cepceslib.sh') diff --git a/cepceslib.sh b/cepceslib.sh new file mode 100755 index 0000000..b461c1a --- /dev/null +++ b/cepceslib.sh @@ -0,0 +1,278 @@ +#!/bin/sh +# vim: set noet sts=4 sw=4 ts=4: +# File: cepceslib.sh +# Location: https://bgstack15.ddns.net/cgit/cepceslib +# Author: bgstack15, Sathvik Kolla +# SPDX-License-Identifier: GPL-3.0-only +# Startdate: 2024-08-23 08:21 +# Title: CES username enrollment +# Purpose: +# History: +# Usage: +# Reference: +# https://gist.github.com/leechristensen/28e4ddf89d77b70fe3e694684374c8a5 +# https://www.server-world.info/en/note?os=Windows_Server_2022&p=iis&f=8 +# https://stackoverflow.com/questions/3765212/an-error-occurred-when-verifying-security-for-the-message +# https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-wstep/a22e793f-2c7e-4f5e-a22c-f05c49535855 +# chatgpt for wsse:usernametoken fields and all xmlns entries +# https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-xcep/3642fda9-8de2-417a-adad-9d368ffe8fc2 +# https://medium.com/@fmcalbuquerque/python-elementtree-xml-api-with-dynamic-namespaces-171d9c9f391e +# Improve: +# use env vars for CN and SANs +# Dependencies: +# openssl, python3 +# Documentation: README.md + +gen_csr() { + # input env vars: KEYFILE, CSRFILE, TEMPLATE + _cnf="$( mktemp )" + cat >"${_cnf}" <"${CESFILE}" < + + http://schemas.microsoft.com/windows/pki/2009/01/enrollment/RST/wstep + urn:uuid:$( uuidgen ) + ${CESURL} + http://www.w3.org/2005/08/addressing/anonymous + + + ${CESUSER} + ${CESPASSWORD} + + + + + + http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3 + http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue + + ${_csr_contents} + + + + + +EOFCES +} + +submit_ces_request() { + # input env vars: CESURL, CESFILE + # -k for irony + curl --silent \ + "${CESURL}" \ + -H "Content-Type: application/soap+xml" \ + -X POST \ + --data @"${CESFILE}" \ + -k +} + +parse_ces_response() { + # input env vars: CESRESPONSEFILE + { + printf '%s\r\n' '-----BEGIN PKCS7-----' + python3 <<-EOFPYTHON +import xml.etree.ElementTree as ET, sys +rf = "${CESRESPONSEFILE}" +tree = ET.parse(rf) +print(tree.findall(".//*[@ValueType='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd#PKCS7']")[0].text) +EOFPYTHON + printf '%s\r\n' '-----END PKCS7-----' + } | grep -E '.' | openssl pkcs7 -in /dev/stdin -print_certs +} + +use_ces() { + # input env vars: KEYFILE, CSRFILE, CESURL, CESPASSWORD/CESPASSWODFILE, TEMPLATE, CERTFILE + # optional: CESFILE, CESRESPONSEFILE + unset _used_temp_crf _used_temp_cf + test -z "${KEYFILE}" && { echo "Fatal! Need KEYFILE. Aborted." 1>&2 ; return 1 ; } + test -z "${CSRFILE}" && { echo "Fatal! Need CSRFILE. Aborted." 1>&2 ; return 1 ; } + test -z "${CESURL}" && { echo "Fatal! Need CESURL. Aborted." 1>&2 ; return 1 ; } + test -z "${CESUSER}" && { echo "Fatal! Need CESUSER. Aborted." 1>&2 ; return 1 ; } + test -z "${TEMPLATE}" && { echo "Fatal! Need TEMPLATE. How about WebServerV3? Aborted." 1>&2 ; return 1 ; } + test -z "${CESPASSWORD}" && test -z "${CESPASSWORDFILE}" && { echo "Fatal! Need CESPASSWORD or CESPASSWORDFILE. Aborted." 1>&2 ; return 1 ; } + test -n "$( cat "${CESPASSWORDFILE}" 2>/dev/null )" && CESPASSWORD="$( cat "${CESPASSWORDFILE}" )" + test -z "${CESPASSWORD}" && { echo "Fatal! Need CESPASSWORD or CESPASSWORDFILE populated. Aborted." 1>&2 ; return 1 ; } + test -z "${CESRESPONSEFILE}" && { CESRESPONSEFILE="$( mktemp )" ; _used_temp_crf=1 ; echo "Using CESRESPONSEFILE=${CESRESPONSEFILE}" 1>&2 ; } + test -z "${CERTFILE}" && { echo "Fatal! Need CERTFILE. Aborted." 1>&2 ; return 1 ; } + test -z "$( cat "${KEYFILE}" 2>/dev/null )" && { + # need to generate a new key + openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:4096 -out "${KEYFILE}" + } + # so now we have KEYFILE. Lets assume we need to make CSRFILE + test -z "$( cat "${CSRFILE}" 2>/dev/null )" && { + gen_csr # will produce CSRFILE + } + test -z "${CESFILE}" && { CESFILE="$( mktemp )" ; _used_temp_cf=1 ; echo "Using CESFILE=${CESFILE}" 1>&2 ; } + test -z "$( cat "${CESFILE}" 2>/dev/null )" && { + gen_ces # will populate CESFILE + } + test -z "${SKIP_SUBMIT}" && { + submit_ces_request > "${CESRESPONSEFILE}" + } + test -n "$( cat "${CESRESPONSEFILE}" 2>/dev/null )" && { + parse_ces_response > "${CERTFILE}" + } + # CLEANUP + test -n "${_used_temp_crf}" && rm -f "${CESRESPONSEFILE:-NOTHINGTODEL}" 1>/dev/null 2>&1 + test -n "${_used_temp_cf}" && rm -f "${CESFILE:-NOTHINGTODEL}" 1>/dev/null 2>&1 + test -z "${NO_CLEAN}" && { + rm -f "${CESRESPONSEFILE}" "${CESFILE}" 1>/dev/null 2>&1 + } +} + +gen_cep() { + # input env vars: CEPFILE, CEPURL, CESUSER, CESPASSWORD + cat >"${CEPFILE}" < + + http://schemas.microsoft.com/windows/pki/2009/01/enrollmentpolicy/IPolicy/GetPolicies + urn:uuid:$( uuidgen ) + ${CEPURL} + http://www.w3.org/2005/08/addressing/anonymous + + + ${CESUSER} + ${CESPASSWORD} + + + + + + + 0001-01-01T00:00:00 + + + + + + +EOFCEP +} + +submit_cep_request() { + # input env vars: CEPURL, CEPFILE + curl --silent \ + "${CEPURL}" \ + -H "Content-Type: application/soap+xml; charset=utf-8" \ + -X POST \ + --data @"${CEPFILE}" \ + -k +} + +parse_cep_response() { + # input env vars: CEPRESPONSEFILE + # You might be tempted to use ElementTree.register_namespace(), but the author was unable to make that work here, and findall(,namespaces={"ns1":ns}) is not shorter than what is used here and was not tested. + python3 <&2 ; return 1 ; } + test -z "${CESPASSWORD}" && test -z "${CESPASSWORDFILE}" && { echo "Fatal! Need CESPASSWORD or CESPASSWORDFILE. Aborted." 1>&2 ; return 1 ; } + test -n "$( cat "${CESPASSWORDFILE}" 2>/dev/null )" && CESPASSWORD="$( cat "${CESPASSWORDFILE}" )" + test -z "${CESPASSWORD}" && { echo "Fatal! Need CESPASSWORD or CESPASSWORDFILE populated. Aborted." 1>&2 ; return 1 ; } + # process + test -z "${CEPFILE}" && { CEPFILE="$( mktemp )" ; _used_temp_cf=1 ; echo "Using CEPFILE=${CEPFILE}" 1>&2 ; } + test -z "$( cat "${CEPFILE}" 2>/dev/null )" && { + gen_cep # will populate CEPFILE + } + test -z "${CEPRESPONSEFILE}" && { CEPRESPONSEFILE="$( mktemp )" ; _used_temp_crf=1 ; echo "Using CEPRESPONSEFILE=${CEPRESPONSEFILE}" 1>&2 ; } + test -z "${SKIP_SUBMIT}" && { + submit_cep_request > "${CEPRESPONSEFILE}" + } + test -n "$( cat "${CEPRESPONSEFILE}" 2>/dev/null )" && { + parse_cep_response # will print available templates + } + # CLEANUP + test -n "${_used_temp_crf}" && rm -f "${CEPRESPONSEFILE:-NOTHINGTODEL}" 1>/dev/null 2>&1 + test -n "${_used_temp_cf}" && rm -f "${CEPFILE:-NOTHINGTODEL}" 1>/dev/null 2>&1 + test -z "${NO_CLEAN}" && { + rm -f "${CEPRESPONSEFILE}" "${CEPFILE}" 1>/dev/null 2>&1 + } +} + +# https://stackoverflow.com/questions/2683279/how-to-detect-if-a-script-is-being-sourced +# BEGIN IS-SOURCED +sourced=0 +if [ -n "$ZSH_VERSION" ]; then + case $ZSH_EVAL_CONTEXT in *:file) sourced=1;; esac +elif [ -n "$KSH_VERSION" ]; then + [ "$(cd -- "$(dirname -- "$0")" && pwd -P)/$(basename -- "$0")" != "$(cd -- "$(dirname -- "${.sh.file}")" && pwd -P)/$(basename -- "${.sh.file}")" ] && sourced=1 +elif [ -n "$BASH_VERSION" ]; then + (return 0 2>/dev/null) && sourced=1 +else # All other shells: examine $0 for known shell binary filenames. +# Detects `sh` and `dash`; add additional shell filenames as needed. + case ${0##*/} in sh|-sh|dash|-dash) sourced=1;; esac +fi +# END IS-SOURCED + +# MAIN +if test "${sourced}" = "0" ; +then + action="${action:-${1:-NONE}}" + case "${action}" in + use_cep) use_cep ;; + use_ces) use_ces ;; + *) echo "Warning: action ${action} not defined yet. Skipping..." 1>&2 ; exit 1 ; + esac +fi -- cgit