@@ -2210,7 +2210,7 @@ service_detection() {
2210
2210
# trying with sockets is better than not even trying.
2211
2211
tls_sockets "04" "$TLS13_CIPHER" "all+" "" "" false
2212
2212
if [[ $? -eq 0 ]]; then
2213
- plaintext="$(tm_out "$GET_REQ11" | hexdump -v -e '16/1 "%02X"')"
2213
+ plaintext="$(safe_echo "$GET_REQ11" | hexdump -v -e '16/1 "%02X"')"
2214
2214
plaintext="${plaintext%%[!0-9A-F]*}"
2215
2215
send_app_data "$plaintext"
2216
2216
if [[ $? -eq 0 ]]; then
@@ -2225,7 +2225,7 @@ service_detection() {
2225
2225
fi
2226
2226
else
2227
2227
# SNI is not standardized for !HTTPS but fortunately for other protocols s_client doesn't seem to care
2228
- tm_out "$GET_REQ11" | $OPENSSL s_client $(s_client_options "$1 -quiet $BUGS -connect $NODEIP:$PORT $PROXY $SNI") >$TMPFILE 2>$ERRFILE &
2228
+ safe_echo "$GET_REQ11" | $OPENSSL s_client $(s_client_options "$1 -quiet $BUGS -connect $NODEIP:$PORT $PROXY $SNI") >$TMPFILE 2>$ERRFILE &
2229
2229
wait_kill $! $HEADER_MAXSLEEP
2230
2230
was_killed=$?
2231
2231
fi
@@ -2321,12 +2321,12 @@ run_http_header() {
2321
2321
2322
2322
pr_bold " HTTP Status Code "
2323
2323
[[ -z "$1" ]] && url="/" || url="$1"
2324
- tm_out "$GET_REQ11" | $OPENSSL s_client $(s_client_options "$OPTIMAL_PROTO $BUGS -quiet -ign_eof -connect $NODEIP:$PORT $PROXY $SNI") >$HEADERFILE 2>$ERRFILE &
2324
+ safe_echo "$GET_REQ11" | $OPENSSL s_client $(s_client_options "$OPTIMAL_PROTO $BUGS -quiet -ign_eof -connect $NODEIP:$PORT $PROXY $SNI") >$HEADERFILE 2>$ERRFILE &
2325
2325
wait_kill $! $HEADER_MAXSLEEP
2326
2326
if [[ $? -eq 0 ]]; then
2327
2327
# Issue HTTP GET again as it properly finished within $HEADER_MAXSLEEP and didn't hang.
2328
2328
# Doing it again in the foreground to get an accurate header time
2329
- tm_out "$GET_REQ11" | $OPENSSL s_client $(s_client_options "$OPTIMAL_PROTO $BUGS -quiet -ign_eof -connect $NODEIP:$PORT $PROXY $SNI") >$HEADERFILE 2>$ERRFILE
2329
+ safe_echo "$GET_REQ11" | $OPENSSL s_client $(s_client_options "$OPTIMAL_PROTO $BUGS -quiet -ign_eof -connect $NODEIP:$PORT $PROXY $SNI") >$HEADERFILE 2>$ERRFILE
2330
2330
NOW_TIME=$(date "+%s")
2331
2331
HTTP_TIME=$(awk -F': ' '/^date:/ { print $2 } /^Date:/ { print $2 }' $HEADERFILE)
2332
2332
HAD_SLEPT=0
@@ -7354,6 +7354,124 @@ tls_time() {
7354
7354
return 0
7355
7355
}
7356
7356
7357
+ # rfc8461
7358
+ sub_mta_sts() {
7359
+ local mta_sts_record=""
7360
+ local policy=""
7361
+ local smtp_tls_record=""
7362
+ local spaces="$1"
7363
+ local useragent="$UA_STD"
7364
+ $SNEAKY && useragent="$UA_SNEAKY"
7365
+
7366
+ [[ ! "$STARTTLS_PROTOCOL" =~ smtp ]] && return 0
7367
+
7368
+ # This works currently only when the MX record is equal the domainname like with the testcase dev.testssl.sh
7369
+ # So either we must only execute this when called --mx or we must deduce the domain name from $NODE somehow.
7370
+ # For the latter we could reverse check again with get_mx_record whether the name passed later passed
7371
+ # to this function is an mx record from this domain.
7372
+ # So the plan is to chek whether $CMDLINE matches --mx. If not we check whether there is an MX record
7373
+ # for $NODE which matches the current $NODE. If not we subsequently remove the leading hostname part of
7374
+ # the $NODE and check whether this is a domainname and has a MX which matches the original node.
7375
+ # If we end up @ DOMAIN.TLD and didn't find anything we emit a message and return.
7376
+
7377
+ pr_bold " MTA-STS Policy "
7378
+
7379
+ mta_sts_record="$(get_txt_record _mta-sts.$NODE)"
7380
+ # look for exact match for 'v=STSv1'
7381
+ # look for exact match for 'id='
7382
+
7383
+ # echo "$mta_sts_record"; echo
7384
+
7385
+ 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)"
7386
+ # here also the openssl return val needs to be checked
7387
+
7388
+ #tmp="$(printf "$policy" | awk '/^$/ { p=1;next } { if(!p) { print } }')"
7389
+ # policy="$(awk '/^$/ { p=1;next } { if(!p) { print } }' <<< "$policy")"
7390
+ policy="$(print_after_blankline "$policy")"
7391
+ #echo "POLICY2: $tmp "
7392
+ # echo "$policy"; echo
7393
+
7394
+ # header needs to be stripped. Either the lower bytes which come after Content-Length in the header.
7395
+ # or starting from version or starting after blank line
7396
+
7397
+ # check policy:
7398
+ # - grep -Ew 'version|mode|mx|max_age'
7399
+ # - version.*STSv1$
7400
+ # - grep 'mode:.*testing|mode:.*enforce'
7401
+ # - grep 'max_age:.*[0-9](5-10)'
7402
+ # - max_age should be sufficient otherwise caching it is ~useless, see HSTS
7403
+ # - whether mx record matches
7404
+
7405
+ if [[ $DEBUG -ge 1 ]]; then
7406
+ echo "$mta_sts_record" >$TMPFILE/_mta-sts.$NODE.txt
7407
+ echo "$policy" >$TMPFILE/$NODE.mta-sts.well-known_mta-sts.txt
7408
+ echo "$smtp_tls_record" > $TMPFILE/_smtp._tls.$NODE
7409
+ fi
7410
+
7411
+ smtp_tls_record="$(get_txt_record _smtp._tls.$NODE)"
7412
+
7413
+ outln "valid _mta-sts TXT record \"$mta_sts_record\""
7414
+ out "$spaces"
7415
+ outln "valid enforced policy \"https://mta-sts.$NODE/.well-known/mta-sts.txt\""
7416
+ out "$spaces"
7417
+ outln "optional _smtp._tls TXT record \"$smtp_tls_record\""
7418
+
7419
+ return 0
7420
+ }
7421
+
7422
+ # e.g. for removing the HTTP header
7423
+ #
7424
+ print_after_blankline() {
7425
+ # doesn't work (oneliner with $1 instead of multiline):
7426
+ #awk '/^$/ { p=1;next } { if(p) { print } }' <<< $1
7427
+ local first=true
7428
+ local line=""
7429
+
7430
+ while read -r line; do
7431
+ if ! "$first"; then
7432
+ safe_echo "$line\n"
7433
+ else
7434
+ # ignore everything until we hit an empty line or a line with a blank or a CR / LF
7435
+ if [[ -z "$line" ]] || [[ "$line" =~ ^[[:space:]]$ ]]; then
7436
+ first=false
7437
+ continue
7438
+ fi
7439
+ fi
7440
+ done <<< $1
7441
+ set +x
7442
+ }
7443
+
7444
+ # e.g. for removing the body
7445
+ #
7446
+ print_before_blankline() {
7447
+ # doesn't work (oneliner with $1 instead of multiline):
7448
+ awk '/^$/ { p=1;next } { if(!p) { print } }' <<< $1
7449
+ }
7450
+
7451
+
7452
+ # RFC 6394
7453
+ # RFC 6698
7454
+ # RFC 7218
7455
+ # RFC 7671
7456
+ # RFC 7672
7457
+ # RFC 7673
7458
+ sub_dane() {
7459
+ local tlsa_record=""
7460
+ local rrsig_record=""
7461
+ local spaces="$1"
7462
+
7463
+ # Not yet implemeted
7464
+ return 0
7465
+
7466
+ pr_bold " DANE / DNSSEC "
7467
+
7468
+ tlsa_record="$(get_tlsa_record _$PORT._tcp.$NODE)"
7469
+ # parsing TLSA certificate usage, TLSA selector, TLSA matching type, hash
7470
+ rrsig_record="$(get_rrsig_record $NODE)"
7471
+
7472
+ # return 0
7473
+ }
7474
+
7357
7475
# core function determining whether handshake succeeded or not
7358
7476
# arg1: return value of "openssl s_client connect"
7359
7477
# arg2: temporary file with the server hello
@@ -9475,6 +9593,7 @@ run_server_defaults() {
9475
9593
local -a -i success
9476
9594
local cn_nosni cn_sni sans_nosni sans_sni san tls_extensions
9477
9595
local using_sockets=true
9596
+ local spaces=" "
9478
9597
9479
9598
"$SSL_NATIVE" && using_sockets=false
9480
9599
@@ -9821,6 +9940,9 @@ run_server_defaults() {
9821
9940
9822
9941
tls_time
9823
9942
9943
+ sub_mta_sts "$spaces"
9944
+ sub_dane "$spaces"
9945
+
9824
9946
if [[ -n "$SNI" ]] && [[ $certs_found -ne 0 ]] && [[ ! -e $HOSTCERT.nosni ]]; then
9825
9947
# no cipher suites specified here. We just want the default vhost subject
9826
9948
if ! "$HAS_TLS13" && [[ $(has_server_protocol "tls1_3") -eq 0 ]]; then
0 commit comments