@@ -7362,12 +7362,18 @@ sub_mta_sts() {
7362
7362
local spaces="$1"
7363
7363
# we might reconsider this as booleans arent very flexible:
7364
7364
local mta_sts_record_ok=false policy_ok=false smtp_tls_record_ok=false
7365
+ local -a failreason_policy=()
7366
+ local -a failreason_mtasts_rec=()
7365
7367
local jsonID="smtp_mtasts"
7368
+ local mx_record=""
7366
7369
local useragent="$UA_STD"
7367
7370
$SNEAKY && useragent="$UA_SNEAKY"
7368
7371
7369
7372
[[ ! "$STARTTLS_PROTOCOL" =~ smtp ]] && return 0
7370
7373
7374
+ # rather move to the caller?
7375
+ mx_record=$(get_mx_record "$NODE")
7376
+
7371
7377
# This works currently only when the MX record is equal the domainname like with the testcase dev.testssl.sh
7372
7378
# So either we must only execute this when called --mx or we must deduce the domain name from $NODE somehow.
7373
7379
# For the latter we could reverse check again with get_mx_record whether the name passed later passed
@@ -7380,40 +7386,89 @@ sub_mta_sts() {
7380
7386
pr_bold " MTA-STS Policy "
7381
7387
7382
7388
mta_sts_record="$(get_txt_record _mta-sts.$NODE)"
7383
- # look for exact match for 'v=STSv1' and 'id='
7384
- if [[ "$mta_sts_record" =~ v=STSv1 ]] && [[ "$mta_sts_record" =~ id= ]] && [[ "$mta_sts_record" =~ \; ]]; then
7385
- # id check needs to improved , see sts-id in https://tools.ietf.org/html/rfc8461#section-3.1
7386
- mta_sts_record_ok=true
7387
- fi
7388
7389
# echo "$mta_sts_record"; echo
7389
7390
7391
+ mta_sts_record_ok=true
7392
+ if [[ -z "$mta_sts_record" ]]; then
7393
+ failreason_mtasts_rec+=("no record")
7394
+ mta_sts_record_ok=false
7395
+ else
7396
+ if [[ $(count_lines "$(safe_echo "$mta_sts_record" | tr ';' '\n')") -ne 2 ]]; then
7397
+ failreason_mtasts_rec+=("number of ; should be 2")
7398
+ mta_sts_record_ok=false
7399
+ fi
7400
+ IFS=';' read v id <<< "${mta_sts_record}"
7401
+ if [[ ! "$v" =~ v=STSv1 ]] ; then
7402
+ failreason_mtasts_rec+=("v seems wrong")
7403
+ mta_sts_record_ok=false
7404
+ fi
7405
+ if [[ ! "$id" =~ id= ]]; then
7406
+ failreason_mtasts_rec+=("id seems wrong")
7407
+ mta_sts_record_ok=false
7408
+ else
7409
+ id="${id#*=}" # strip key
7410
+ if [[ ! "$id" =~ ^[[:alnum:]]{1,32}$ ]]; then
7411
+ failreason_mtasts_rec+=("should be up to 32 alnum chars ")
7412
+ mta_sts_record_ok=false
7413
+ fi
7414
+ fi
7415
+ fi
7416
+
7390
7417
policy="$(safe_echo "GET /.well-known/mta-sts.txt HTTP/1.1\r\nHost: mta-sts.$NODE\r\nUser-Agent: $useragent\r\nAccept-Encoding: identity\r\nAccept: text/*\r\nConnection: Close\r\n\r\n" | $OPENSSL s_client $(s_client_options "-quiet -ign_eof -connect $NODEIP:443 $PROXY $SNI") 2>$ERRFILE)"
7391
7418
# here also the openssl return val needs to be checked
7392
7419
7393
7420
policy="$(print_after_blankline "$policy")"
7394
7421
# echo "$policy"; echo
7395
7422
7423
+ # Syntax check, keep in mind $policy appears in bash as a single line with Unix LF (0a).
7424
+ policy_ok=true
7425
+ if [[ -z "$policy" ]]; then
7426
+ failreason_policy+=("policy https://mta-sts.$NODE/.well-known/mta-sts.txt not found")
7427
+ policy_ok=false
7428
+ else
7429
+ for c in version mode mx max_age; do
7430
+ if [[ ! "$policy" =~ $c: ]] && [[ ! "$policy" =~ $c\ : ]]; then
7431
+ policy_ok=false
7432
+ failreason_policy+=("$c wrong formatted/missing")
7433
+ fi
7434
+ done
7435
+
7436
+ # we use at most 10 spaces. ToDo: look into the policy
7437
+ if "$policy_ok"; then
7438
+ if [[ ! "$policy" =~ version[\ ]{0,10}:[\ ]{0,10}STSv1 ]]; then
7439
+ failreason_policy+=("version should be STSv1 ")
7440
+ fi
7441
+ if [[ ! "$policy" =~ max_age[\ ]{0,10}:[\ ]{0,10}[0-9]{1,20} ]]; then
7442
+ failreason_policy+=("max age is not a number")
7443
+ fi
7444
+ if [[ ! "$policy" =~ mode[\ ]{0,10}:[\ ]{0,10}(enforce|testing) ]]; then
7445
+ failreason_policy+=("policy is neither testing or enforce")
7446
+ fi
7447
+ if [[ "$policy" =~ mode[\ ]{0,10}:[\ ]{0,10}testing ]]; then
7448
+ policy_mode=testing
7449
+ fi
7450
+ fi
7451
+ fi
7452
+ [[ -n "$failreason_policy" ]] && policy_ok=false
7453
+ # get max_age:
7454
+
7396
7455
# check policy:
7397
- # - grep -Ew 'version|mode|mx|max_age'
7398
- # - version.*STSv1$
7399
- # - grep 'mode:.*testing|mode:.*enforce'
7400
- # - grep 'max_age:.*[0-9](5-10)'
7401
7456
# - max_age should be sufficient otherwise caching it is ~useless, see HSTS
7402
7457
# - whether mx record matches
7403
-
7404
- # for the time being:
7405
- [[ -n "$policy" ]] && policy_ok=true
7458
+ # - check against https://tools.ietf.org/html/rfc8461#section-3.2
7406
7459
7407
7460
if [[ $DEBUG -ge 1 ]]; then
7408
- echo "$mta_sts_record" >$TMPFILE /_mta-sts.$NODE.txt
7409
- echo "$policy" >$TMPFILE /$NODE.mta-sts.well-known_mta -sts.txt
7410
- echo "$smtp_tls_record" > $TMPFILE /_smtp._tls.$NODE
7461
+ echo "$mta_sts_record" >$TEMPDIR /_mta-sts.$NODE.txt
7462
+ echo "$policy" >$TEMPDIR /$NODE.policy_mta -sts.txt
7463
+ echo "$smtp_tls_record" > $TEMPDIR /_smtp._tls.$NODE
7411
7464
fi
7412
7465
7413
7466
smtp_tls_record="$(get_txt_record _smtp._tls.$NODE)"
7414
7467
# for the time being:
7415
7468
[[ -n "$smtp_tls_record" ]] && smtp_tls_record_ok=true
7416
7469
7470
+
7471
+ # now the verdicts
7417
7472
if "$mta_sts_record_ok"; then
7418
7473
pr_svrty_good "valid"
7419
7474
fileout "${jsonID}_txtrecord" "OK" "valid _mta-sts TXT record \"$mta_sts_record\""
@@ -7425,22 +7480,28 @@ sub_mta_sts() {
7425
7480
out "$spaces"
7426
7481
7427
7482
if "$policy_ok"; then
7428
- pr_svrty_good "valid and enforced"
7429
- fileout "${jsonID}_policy" "OK" "valid and enforced policy file \"https://mta-sts.$NODE/.well-known/mta-sts.txt\""
7483
+ if [[ $policy_mode == testing ]]; then
7484
+ out "valid but not enforced"
7485
+ fileout "${jsonID}_policy" "INFO" "valid but not enforced policy \"https://mta-sts.$NODE/.well-known/mta-sts.txt\""
7486
+ else
7487
+ pr_svrty_good "valid and enforced"
7488
+ fileout "${jsonID}_policy" "OK" "valid and enforced policy \"https://mta-sts.$NODE/.well-known/mta-sts.txt\""
7489
+ fi
7490
+ outln " policy https://mta-sts.$NODE/.well-known/mta-sts.txt"
7430
7491
else
7431
7492
# missing: too short, not enforced, etc..
7432
- pr_svrty_low "invalid"
7433
- fileout "${jsonID}_policy" "LOW" "invalid policy file \"https://mta-sts.$NODE/.well-known/mta-sts.txt\""
7493
+ pr_svrty_low "invalid policy"
7494
+ fileout "${jsonID}_policy" "LOW" "invalid policy \"https://mta-sts.$NODE/.well-known/mta-sts.txt\""
7495
+ outln " \"https://mta-sts.$NODE/.well-known/mta-sts.txt\": ${failreason_policy[@]}"
7434
7496
fi
7435
- outln " policy file \"https://mta-sts.$NODE/.well-known/mta-sts.txt\""
7436
7497
out "$spaces"
7437
7498
7438
7499
if "$smtp_tls_record_ok"; then
7439
- outln "optional _smtp._tls TXT record \"$smtp_tls_record\""
7440
- fileout "${jsonID}_tlsrpt" "INFO" "optional _smtp._tls TXT record \"$smtp_tls_record\""
7500
+ outln "found ( optional) TLS RPT TXT record \"$smtp_tls_record\""
7501
+ fileout "${jsonID}_tlsrpt" "INFO" "optional TLS-RPT TXT record \"$smtp_tls_record\""
7441
7502
else
7442
7503
outln "No TLS RPT record"
7443
- fileout "${jsonID}_tlsrpt" "INFO" "no or invalid optional _smtp._tls TXT record \"$smtp_tls_record\""
7504
+ fileout "${jsonID}_tlsrpt" "INFO" "no or invalid ( optional) TLS RPT record \"$smtp_tls_record\""
7444
7505
fi
7445
7506
7446
7507
return 0
0 commit comments