From 107d0c4c75010d14be879dd3bba8bea65bdce038 Mon Sep 17 00:00:00 2001 From: Giorgio Ravera Date: Tue, 5 Aug 2025 19:25:02 +0200 Subject: [PATCH] Updated Fritz Scripts --- update_certificate | 15 +++- update_certificate_fritz | 187 +++++++++++++++++++++++++++++++-------- 2 files changed, 163 insertions(+), 39 deletions(-) diff --git a/update_certificate b/update_certificate index b9cf405..d216f13 100755 --- a/update_certificate +++ b/update_certificate @@ -19,6 +19,7 @@ DST_TMP_CHAIN="$DST_TMP_PATH/chain.pem" DST_TMP_FULLCHAIN="$DST_TMP_PATH/fullchain.pem" DST_TMP_SERVER="$DST_TMP_PATH/server.pem" PATH="$PATH:/usr/local/bin" +SECRET_FILE=".secret" # Copy Certificate # Copy Certificate in $dst folder @@ -142,7 +143,12 @@ function update_fritz () { USER="fritz5738" ALIVE=$(ping -c 1 $HOST |grep ttl) if [ ! -z "$ALIVE" ]; then - update_certificate_fritz $HOST $USER $SRC_FULLCHAIN $SRC_KEY + if [ -f "$SECRET_FILE" ]; then + password=$(awk -F':' -v host="$HOST" '$1 == host {print $2}' "$SECRET_FILE") + update_certificate_fritz -b $HOST -u $USER -p $password -c $SRC_FULLCHAIN -k $SRC_KEY + else + echo "$SECRET_FILE not found" + fi else echo "Host $HOST not alive, skipped" fi @@ -158,7 +164,12 @@ function update_ap1 () { USER="fritz9145" ALIVE=$(ping -c 1 $HOST |grep ttl) if [ ! -z "$ALIVE" ]; then - update_certificate_fritz $HOST $USER $SRC_FULLCHAIN $SRC_KEY + if [ -f "$SECRET_FILE" ]; then + password=$(awk -F':' -v host="$HOST" '$1 == host {print $2}' "$SECRET_FILE") + update_certificate_fritz -b $HOST -u $USER -p $password -c $SRC_FULLCHAIN -k $SRC_KEY + else + echo "$SECRET_FILE not found" + fi else echo "Host $HOST not alive, skipped" fi diff --git a/update_certificate_fritz b/update_certificate_fritz index 4f71d6d..8ac0591 100755 --- a/update_certificate_fritz +++ b/update_certificate_fritz @@ -1,49 +1,162 @@ #!/bin/bash # Parameters -PASSWORD="" - -# check inputs: -if [ $# -eq 0 ]; then - echo "Wrong parameters. Please use:" - echo "- host" - echo "- username" - echo "- certificate" - echo "- key" - exit +CURL_CMD="curl" +ICONV_CMD="iconv" +OPENSSL_CMD="openssl" + +SUCCESS_MESSAGES="^ *(Das SSL-Zertifikat wurde erfolgreich importiert|Import of the SSL certificate was successful|El certificado SSL se ha importado correctamente|Le certificat SSL a été importé|Il certificato SSL è stato importato|Import certyfikatu SSL został pomyślnie zakończony)" + +DEBUG_OUTPUT=/tmp/fritzbox.debug + +function usage { + echo "Usage: $0 [-b baseurl] [-u username] [-p password] [-c fullchain certificate] [-k privkey]" >&2 + exit 64 +} + +function error { + local msg=$1 + + [ "${msg}" ] && echo "${msg}" >&2 + exit 1 +} + +md5cmd= + +for cmd in md5 md5sum; do + if which "${cmd}" >/dev/null 2>&1; then + md5cmd=${cmd} + break + fi +done + +if [ -z "${md5cmd}" ]; then + error "Missing command for calculating MD5 hash" +fi + +exit=0 + +for cmd in ${CURL_CMD} ${ICONV_CMD} ${OPENSSL_CMD}; do + if ! which "${cmd}" >/dev/null 2>&1; then + error "Please install ${cmd}" >&2 + fi +done + +[ ${exit} -ne 0 ] && exit ${exit} + +while getopts ":b:p:u:c:k:dh" opt; do + case ${opt} in + b) + baseurl=$OPTARG + ;; + p) + password=$OPTARG + ;; + u) + username=$OPTARG + ;; + c) + fullchain=$OPTARG + ;; + k) + privkey=$OPTARG + ;; + d) + debug="true" + ;; + h) + usage + ;; + \?) + echo "Invalid option: $OPTARG" >&2 + echo >&2 + usage + ;; + :) + echo "Invalid option: $OPTARG requires an argument" >&2 + echo >&2 + usage + ;; + esac +done + +shift $((OPTIND - 1)) + +exit=0 + +# strip trailing slash +baseurl="${baseurl%/}" + +if [ ! -r "${fullchain}" ] || [ ! -r "${privkey}" ]; then + error "Certpath ${certpath} must contain fullchain.pem and privkey.pem" fi -# save inputs -HOST=$1 -USERNAME=$2 -CERT=$3 -KEY=$4 +if ! ${OPENSSL_CMD} rsa -in "${privkey}" -check -noout &>/dev/null; then + error "FRITZ!OS only supports RSA private keys." +fi + +if [ -n "${debug}" ]; then + curl_opts="-v -s --stderr -" + + function process_curl_output { + grep -v '^[*{}]' | sed -e '1i\ +' | tee -a ${DEBUG_OUTPUT} + } -# make and secure a temporary file -TMP="$(mktemp -t XXXXXX)" -chmod 600 $TMP + echo "Debug output will be written to ${DEBUG_OUTPUT}" +else + curl_opts="-sS" + + function process_curl_output { + cat + } +fi + +request_file="$(mktemp -t XXXXXX)" +trap 'rm -f "${request_file}"' EXIT + +echo "----------------------------------------------------------------" >>${DEBUG_OUTPUT} +date >>${DEBUG_OUTPUT} # login to the box and get a valid SID -CHALLENGE=`wget -q -O - $HOST/login_sid.lua | sed -e 's/^.*//' -e 's/<\/Challenge>.*$//'` -HASH="`echo -n $CHALLENGE-$PASSWORD | iconv -f ASCII -t UTF16LE |md5sum|awk '{print $1}'`" -SID=`wget -q -O - "$HOST/login_sid.lua?sid=0000000000000000&username=$USERNAME&response=$CHALLENGE-$HASH"| sed -e 's/^.*//' -e 's/<\/SID>.*$//'` +# shellcheck disable=SC2086 +challenge="$(${CURL_CMD} ${curl_opts} "${baseurl}/login_sid.lua" | process_curl_output | sed -ne 's/^.*\([0-9a-f][0-9a-f]*\)<\/Challenge>.*$/\1/p')" +if [ -z "${challenge}" ]; then + error "Invalid challenge received." +fi + +md5hash="$(echo -n "${challenge}-${password}" | ${ICONV_CMD} -f ASCII -t UTF-16LE | ${md5cmd} | awk '{print $1}')" + +# shellcheck disable=SC2086 +sid="$(${CURL_CMD} ${curl_opts} "${baseurl}/login_sid.lua?username=${username}&response=${challenge}-${md5hash}" | process_curl_output | sed -ne 's/^.*\([0-9a-f][0-9a-f]*\)<\/SID>.*$/\1/p')" +if [ -z "${sid}" ] || [ "${sid}" = "0000000000000000" ]; then + error "Login failed." +fi + +certbundle=$(cat "${fullchain}" "${privkey}" | grep -v '^$') # generate our upload request -BOUNDARY="---------------------------"`date +%Y%m%d%H%M%S` -printf -- "--$BOUNDARY\r\n" >> $TMP -printf "Content-Disposition: form-data; name=\"sid\"\r\n\r\n$SID\r\n" >> $TMP -#printf -- "--$BOUNDARY\r\n" >> $TMP -#printf "Content-Disposition: form-data; name=\"BoxCertPassword\"\r\n\r\n$CERT_PASSWORD\r\n" >> $TMP -printf -- "--$BOUNDARY\r\n" >> $TMP -printf "Content-Disposition: form-data; name=\"BoxCertImportFile\"; filename=\"BoxCert.pem\"\r\n" >> $TMP -printf "Content-Type: application/octet-stream\r\n\r\n" >> $TMP -cat $KEY >> $TMP -cat $CERT >> $TMP -printf "\r\n" >> $TMP -printf -- "--$BOUNDARY--" >> $TMP +boundary="---------------------------$(date +%Y%m%d%H%M%S)" -# upload the certificate to the box -wget -q -O - $HOST/cgi-bin/firmwarecfg --header="Content-type: multipart/form-data boundary=$BOUNDARY" --post-file $TMP | grep SSL +cat <>"${request_file}" +--${boundary} +Content-Disposition: form-data; name="sid" -# clean up -rm -f $TMP +${sid} +--${boundary} +Content-Disposition: form-data; name="BoxCertImportFile"; filename="BoxCert.pem" +Content-Type: application/octet-stream + +${certbundle} +--${boundary}-- +EOD + +# upload the certificate to the box +# shellcheck disable=SC2086 +${CURL_CMD} ${curl_opts} -X POST "${baseurl}/cgi-bin/firmwarecfg" -H "Content-type: multipart/form-data boundary=${boundary}" --data-binary "@${request_file}" | process_curl_output | grep -qE "${SUCCESS_MESSAGES}" +# shellcheck disable=SC2181 +if [ $? -ne 0 ]; then + error "Could not import certificate." +else + echo "Import of the SSL certificate was successful" +fi -- 2.47.3