diff --git a/Scripts/Billing/dkim-verify.sh b/Scripts/Billing/dkim-verify.sh new file mode 100755 index 0000000..d6e6d60 --- /dev/null +++ b/Scripts/Billing/dkim-verify.sh @@ -0,0 +1,258 @@ +#!/bin/bash + +PROGRAM_NAME=`basename $0` +INPUT=$1 + +mkdir -p tmp + +cat << EOF > tmp/parameters.sh +DKIM_VERSION=0 +DKIM_ALGORITHM=rsa-sha256 +DKIM_CANONICALIZATION=relaxed/relaxed +DKIM_DOMAIN=domain +DKIM_SUBDOMAIN=sub +DKIM_TIMESTAMP=0 +DKIM_BODY_HASH=0 +DKIM_HEADERS=0 +DKIM_BASE64_SIGNATURE=0 +DKIM_SIGNATURE=0 +EOF + +# Split email text in to 2 parts, the email headers and the email body +cat $INPUT | tr -d '\r' | ( +# cat $INPUT | ( + echo -n "" > tmp/headers.txt + while IFS= read -r line + do + echo "$line" >> tmp/headers.txt + if [ "$line" == '' ] + then + break + fi + done + echo -n "" > tmp/body.txt + while IFS= read -r line + do + echo "$line" >> tmp/body.txt + done +) + +# Handles a single trailing empty line +# Perhaps this needs to remove all trailing empty lines? +LAST_LINE=`tail -n 1 tmp/body.txt` +if [ "$LAST_LINE" == '' ] +then + sed -i '$ d' tmp/body.txt +fi + +# Assuming dkim 'relaxed' canonicalization +cat tmp/body.txt | sed 's/[ \t][ \t]*/ /g' | sed 's/[ \t]$//g' | unix2dos > tmp/cbody.txt +cat tmp/headers.txt | sed -e 's/\(.*\)[ \t]*:[ \t]\(.*\)/\L\1:\E\2/' | sed ':a;N;$!ba;s/[ \t]*\n[ \t][ \t]*/ /g' > tmp/cheaders.txt + +echo -n "dkim-signature:" > tmp/signature.txt +cat tmp/cheaders.txt | while read LINE +do + HEADER_FIELD=`echo "$LINE" | cut -d ':' -f 1` + HEADER_VALUE=`echo "$LINE" | cut -d ':' -f 2-` + # echo " field = $HEADER_FIELD" + if [ "dkim-signature" == "$HEADER_FIELD" ] + then + # echo " $HEADER_FIELD = $HEADER_VALUE" + echo "$HEADER_VALUE" | sed 's/\;/\;\n/g' | while read ATTRIB_LINE + do + ATTRIB=`echo "$ATTRIB_LINE" | tr -d ' '` + if [ "$ATTRIB" != "" ] + then + ATTRIB_NAME=`echo "$ATTRIB" | cut -d '=' -f 1` + ATTRIB_VALUE=`echo "$ATTRIB" | cut -d '=' -f 2-` + # echo " ATTRIB: $ATTRIB" + # echo " -$ATTRIB_NAME- = -$ATTRIB_VALUE-" + [ "$ATTRIB_NAME" != "b" ] && echo -n "$ATTRIB_LINE " >> tmp/signature.txt + [ "$ATTRIB_NAME" == "b" ] && echo -n "$ATTRIB_LINE" | sed 's/b=[^;]*\(.*\)/b=\1/g' >> tmp/signature.txt + [ "$ATTRIB_NAME" == "v" ] && echo "DKIM_VERSION=$ATTRIB_VALUE" >> tmp/parameters.sh + [ "$ATTRIB_NAME" == "a" ] && echo "DKIM_ALGORITHM=$ATTRIB_VALUE" >> tmp/parameters.sh + [ "$ATTRIB_NAME" == "c" ] && echo "DKIM_CANONICALIZATION=$ATTRIB_VALUE" >> tmp/parameters.sh + [ "$ATTRIB_NAME" == "d" ] && echo "DKIM_DOMAIN=$ATTRIB_VALUE" >> tmp/parameters.sh + [ "$ATTRIB_NAME" == "s" ] && echo "DKIM_SUBDOMAIN=$ATTRIB_VALUE" >> tmp/parameters.sh + [ "$ATTRIB_NAME" == "t" ] && echo "DKIM_TIMESTAMP=$ATTRIB_VALUE" >> tmp/parameters.sh + [ "$ATTRIB_NAME" == "bh" ] && echo "DKIM_BODY_HASH=$ATTRIB_VALUE" >> tmp/parameters.sh + [ "$ATTRIB_NAME" == "h" ] && echo "DKIM_HEADERS=$ATTRIB_VALUE" >> tmp/parameters.sh + [ "$ATTRIB_NAME" == "b" ] && echo "DKIM_BASE64_SIGNATURE=$ATTRIB_VALUE" >> tmp/parameters.sh + fi + done + fi +done + +if [ "`cat tmp/signature.txt`" == "dkim-signature:" ] +then + echo "$PROGRAM_NAME: No DKIM-Signature found" + exit -1 +fi + +# echo -n "b=" >> tmp/signature.txt + +. ./tmp/parameters.sh + +if [ "$DKIM_VERSION" != "1" ] +then + echo "$PROGRAM_NAME: Unsupported DKIM version: $DKIM_VERSION" + exit -1 +fi + +BODY_HASH=0 + +if [ "$DKIM_ALGORITHM" == "rsa-sha256" ] +then + BODY_HASH=`cat tmp/cbody.txt | openssl dgst -sha256 -binary | openssl base64` +elif [ "$DKIM_ALGORITHM" == "rsa-sha1" ] +then + BODY_HASH=`cat tmp/cbody.txt | openssl dgst -sha1 -binary | openssl base64` +else + echo "$PROGRAM_NAME: Unsupported DKIM signing algorithm: $DKIM_ALGORITHM" + exit -1 +fi + +if [ "$DKIM_BODY_HASH" != "$BODY_HASH" ] +then + echo "$PROGRAM_NAME: DKIM body hash incorrect ($DKIM_BODY_HASH != $BODY_HASH), possibly email body has been tampered with" + # echo " DKIM body hash field: $DKIM_BODY_HASH" + # echo " Calculated body hash: $BODY_HASH" + # echo "Continuing" + exit -1 +fi +# rm tmp/body.txt + +# Lookup the public key for the domain +# PUBLIC_KEY=`dig ${DKIM_SUBDOMAIN}._domainkey.${DKIM_DOMAIN} TXT | grep rsa | cut -d '"' -f 2 | cut -d '=' -f 4` +DOMAIN_KEY_DNS_RECORD=`host -t txt ${DKIM_SUBDOMAIN}._domainkey.${DKIM_DOMAIN}` +RECORD_NOT_FOUND=`echo "$DOMAIN_KEY_DNS_RECORD" | grep -c -i "not found"` +if [ "$RECORD_NOT_FOUND" != "0" ] +then + echo "$PROGRAM_NAME: DKIM domain key not found in DNS" + # echo "Continuing" +# if TESTING + exit -1 +fi +# echo "Copying over file" +# rm tmp/key.pub +# cp misc/key.pub tmp/key.pub +# echo "Copied over file" +#else # !TESTING + +# echo "DOMAIN_KEY_DNS_RECORD= -$DOMAIN_KEY_DNS_RECORD-" +# | tr -d '["\\ ]' | sed 's/.*;p=\(.*\)/\1/g' | cut -d ';' -f 1 + +# echo "DOMAIN_KEY_DNS_RECORD= -`echo "$DOMAIN_KEY_DNS_RECORD" | sed 's/.*[\073 \t\$]p=\(.*\)[\073 \t\$].*/\1/g' | tr -d ' \"'`-" +PUBLIC_KEY=`echo "$DOMAIN_KEY_DNS_RECORD" | tr -d '["\\ ]' | sed 's/.*;p=\(.*\)/\1/g' | cut -d ';' -f 1` +# | cut -d '"' -f 2,4 | tr -d '[" ]' | tr ';' '\n' | grep "p=" | cut -d '=' -f 2` + +# TODO: Need to see if the 'test' flag is set eg: 't=y' is in the TXT of the DNS record + +# echo PUBLIC_KEY=$PUBLIC_KEY + +# echo " DOMAIN_KEY_DNS_RECORD = $DOMAIN_KEY_DNS_RECORD " + +LAST_CHAR=`echo $PUBLIC_KEY | tail -c 2` +if [ "$LAST_CHAR" == "\\" ] +then + # echo "fixing key" + PUBLIC_KEY=`echo $PUBLIC_KEY | head -c -2` +fi + +# Convert to a format that openssl can understand +echo -----BEGIN PUBLIC KEY----- > tmp/key.pub +echo $PUBLIC_KEY | fold -w 64 >> tmp/key.pub +echo -----END PUBLIC KEY----- >> tmp/key.pub +# fi # End TESTING + +# Dump the contents of the public key +# echo -n "PUBLIC_MODULUS=" > secret.sh +# openssl asn1parse -in key.pub -strparse 19 -offset 4 | cut -d ':' -f 4 | head -n 1 >> secret.sh +# echo -n "PUBLIC_EXPONENT=" >> secret.sh +# openssl asn1parse -in key.pub -strparse 19 -offset 4 | cut -d ':' -f 4 | tail -n 1 >> secret.sh + + +echo -n "" > tmp/dkim-headers.txt + +HEADER_INDEX=1 + +# echo HEADERS = "$DKIM_HEADERS" +while true +do + CURRENT_HEADER=`echo "$DKIM_HEADERS :" | cut -d ':' -f $HEADER_INDEX | tr -d '[ \t]' | sed 's/\(.*\)/\L\1/g'` + if [ "$CURRENT_HEADER" == "" ] + then + break + fi + # echo " header[$HEADER_INDEX] = -$CURRENT_HEADER-" + cat tmp/cheaders.txt | while read LINE + do + HEADER_FIELD=`echo "$LINE" | cut -d ':' -f 1` + # echo "$HEADER_FIELD" + if [ "$CURRENT_HEADER" == "$HEADER_FIELD" ] + then + echo "$LINE" >> tmp/dkim-headers.txt + # echo "$LINE" + break + fi + done + + HEADER_INDEX=$((HEADER_INDEX + 1)) +done + +cat tmp/signature.txt >> tmp/dkim-headers.txt + +# cat tmp/dkim-headers.txt + + +LAST_CHAR=`echo $DKIM_BASE64_SIGNATURE | tail -c 2` +if [ "$LAST_CHAR" == " " ] +then + # echo "Fixing sig" + DKIM_BASE64_SIGNATURE=`echo $DKIM_BASE64_SIGNATURE | head -c -2` +fi + +# echo " DKIM_BASE64_SIGNATURE=$DKIM_BASE64_SIGNATURE" + +echo "$DKIM_BASE64_SIGNATURE" | fold -w 64 | base64 -d > tmp/sign.bin +# cat tmp/sign.txt | base64 -d > tmp/sign.bin +# cat tmp/dkim-headers.txt | head -c -1 | unix2dos > tmp/dkim-headers.dos +uniq tmp/dkim-headers.txt | head -c -1 | unix2dos > tmp/dkim-headers.dos + +if [ "$DKIM_ALGORITHM" == "rsa-sha256" ] +then + echo -n "$PROGRAM_NAME: " + cat tmp/dkim-headers.dos | openssl dgst -keyform pem -sha256 -verify tmp/key.pub -signature tmp/sign.bin + exit $? + # echo "Ret: -$?-" + # if verified ok, returns 0, else returns 1 + + # echo "Hash DOS line endings" + # # Convert raw hash to DER encoding by pre-pending something + # (echo '3031300d060960864801650304020105000420' ; cat tmp/dkim-headers.dos | sha256sum) | xxd -r -p | base64 + # echo "Verified Hash Method 1" + # cat tmp/sign.bin | openssl rsautl -verify -pkcs -pubin -inkey tmp/key.pub | base64 + # + # CALCULATED_HASH=`cat tmp/dkim-headers.dos | sha256sum | tr [a-z] [A-Z] | tr -d ' -'` + # DECRYPTED_HASH=`cat tmp/sign.bin | openssl rsautl -verify -pkcs -pubin -inkey tmp/key.pub | openssl asn1parse -inform der -offset 17 | cut -d ':' -f 4` + # if [ "$CALCULATED_HASH" != "$DECRYPTED_HASH" ] + # then + # echo "$PROGRAM_NAME: Failure, mismatched hashes" + # echo " CALCULATED_HASH = -$CALCULATED_HASH-" + # echo " DECRYPTED_HASH = -$DECRYPTED_HASH-" + # fi + +elif [ "$DKIM_ALGORITHM" == "rsa-sha1" ] +then + echo -n "$PROGRAM_NAME: " + cat tmp/dkim-headers.dos | openssl dgst -keyform pem -sha1 -verify tmp/key.pub -signature tmp/sign.bin + # echo "Ret: -$?-" + # if verified ok, returns 0, else returns 1 + exit $? +else + echo "$PROGRAM_NAME: Unsupported DKIM signing algorithm: $DKIM_ALGORITHM" + exit -1 +fi + + +