@@ -2210,7 +2210,7 @@ service_detection() {
22102210 # trying with sockets is better than not even trying.
22112211 tls_sockets "04" "$TLS13_CIPHER" "all+" "" "" false
22122212 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"')"
22142214 plaintext="${plaintext%%[!0-9A-F]*}"
22152215 send_app_data "$plaintext"
22162216 if [[ $? -eq 0 ]]; then
@@ -2225,7 +2225,7 @@ service_detection() {
22252225 fi
22262226 else
22272227 # 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 &
22292229 wait_kill $! $HEADER_MAXSLEEP
22302230 was_killed=$?
22312231 fi
@@ -2321,12 +2321,12 @@ run_http_header() {
23212321
23222322 pr_bold " HTTP Status Code "
23232323 [[ -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 &
23252325 wait_kill $! $HEADER_MAXSLEEP
23262326 if [[ $? -eq 0 ]]; then
23272327 # Issue HTTP GET again as it properly finished within $HEADER_MAXSLEEP and didn't hang.
23282328 # 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
23302330 NOW_TIME=$(date "+%s")
23312331 HTTP_TIME=$(awk -F': ' '/^date:/ { print $2 } /^Date:/ { print $2 }' $HEADERFILE)
23322332 HAD_SLEPT=0
@@ -7354,6 +7354,124 @@ tls_time() {
73547354 return 0
73557355}
73567356
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+
73577475# core function determining whether handshake succeeded or not
73587476# arg1: return value of "openssl s_client connect"
73597477# arg2: temporary file with the server hello
@@ -9475,6 +9593,7 @@ run_server_defaults() {
94759593 local -a -i success
94769594 local cn_nosni cn_sni sans_nosni sans_sni san tls_extensions
94779595 local using_sockets=true
9596+ local spaces=" "
94789597
94799598 "$SSL_NATIVE" && using_sockets=false
94809599
@@ -9821,6 +9940,9 @@ run_server_defaults() {
98219940
98229941 tls_time
98239942
9943+ sub_mta_sts "$spaces"
9944+ sub_dane "$spaces"
9945+
98249946 if [[ -n "$SNI" ]] && [[ $certs_found -ne 0 ]] && [[ ! -e $HOSTCERT.nosni ]]; then
98259947 # no cipher suites specified here. We just want the default vhost subject
98269948 if ! "$HAS_TLS13" && [[ $(has_server_protocol "tls1_3") -eq 0 ]]; then
0 commit comments