Compare commits
6 Commits
20243a72f2
...
463b6c6403
Author | SHA1 | Date |
---|---|---|
Dirk Wetter | 463b6c6403 | |
Dirk Wetter | 4ca02764fa | |
Jim | 6d2aef5c7d | |
Dirk | 4ededc21bc | |
Dirk | e6bdcee142 | |
Dirk | c93f06c1c6 |
|
@ -13,4 +13,4 @@ jobs:
|
|||
- uses: codespell-project/actions-codespell@master
|
||||
with:
|
||||
skip: ca_hashes.txt,tls_data.txt,*.pem,OPENSSL-LICENSE.txt,CREDITS.md,openssl.cnf
|
||||
ignore_words_list: borken,gost,ciph,ba,bloc,isnt,chello,fo,alle,anull
|
||||
ignore_words_list: borken,gost,ciph,ba,bloc,isnt,chello,fo,alle,anull,experim
|
||||
|
|
189
testssl.sh
189
testssl.sh
|
@ -2329,6 +2329,7 @@ s_client_options() {
|
|||
|
||||
# determines whether the port has an HTTP service running or not (plain TLS, no STARTTLS)
|
||||
# arg1 could be the protocol determined as "working". IIS6 needs that.
|
||||
# sets global $SERVICE
|
||||
#
|
||||
service_detection() {
|
||||
local -i was_killed
|
||||
|
@ -2404,7 +2405,7 @@ service_detection() {
|
|||
;;
|
||||
esac
|
||||
|
||||
outln "\n"
|
||||
outln
|
||||
tmpfile_handle ${FUNCNAME[0]}.txt
|
||||
return 0
|
||||
}
|
||||
|
@ -9780,40 +9781,42 @@ certificate_info() {
|
|||
must_staple "$json_postfix" "$provides_stapling" "$cert_txt"
|
||||
|
||||
out "$indent"; pr_bold " DNS CAA RR"; out " (experimental) "
|
||||
jsonID="DNS_CAArecord"
|
||||
jsonID="DNS_CAA_rrecord"
|
||||
caa_node="$NODE"
|
||||
caa=""
|
||||
while [[ -z "$caa" ]] && [[ -n "$caa_node" ]]; do
|
||||
caa="$(get_caa_rr_record $caa_node)"
|
||||
tmp=${PIPESTATUS[@]}
|
||||
[[ $DEBUG -ge 4 ]] && echo "get_caa_rr_record: $tmp"
|
||||
[[ $caa_node =~ '.'$ ]] || caa_node+="."
|
||||
caa_node=${caa_node#*.}
|
||||
done
|
||||
if [[ -n "$caa" ]]; then
|
||||
pr_svrty_good "available"; out " - please check for match with \"Issuer\" below"
|
||||
if [[ $(count_lines "$caa") -eq 1 ]]; then
|
||||
out ": "
|
||||
else
|
||||
outln; out "$spaces"
|
||||
fi
|
||||
while read caa; do
|
||||
if [[ -n "$caa" ]]; then
|
||||
all_caa+="$caa, "
|
||||
fi
|
||||
done <<< "$caa"
|
||||
all_caa=${all_caa%, } # strip trailing comma
|
||||
pr_italic "$(out_row_aligned_max_width "$all_caa" "$indent " $TERM_WIDTH)"
|
||||
fileout "${jsonID}${json_postfix}" "OK" "$all_caa"
|
||||
elif [[ -n "$NODNS" ]]; then
|
||||
if [[ -n "$NODNS" ]]; then
|
||||
out "(instructed to minimize/skip DNS queries)"
|
||||
fileout "${jsonID}${json_postfix}" "INFO" "check skipped as instructed"
|
||||
elif "$DNS_VIA_PROXY"; then
|
||||
out "(instructed to use the proxy for DNS only)"
|
||||
fileout "${jsonID}${json_postfix}" "INFO" "check skipped as instructed (proxy)"
|
||||
else
|
||||
pr_svrty_low "not offered"
|
||||
fileout "${jsonID}${json_postfix}" "LOW" "--"
|
||||
while [[ -z "$caa" ]] && [[ -n "$caa_node" ]]; do
|
||||
caa="$(get_caa_rrecord $caa_node)"
|
||||
tmp=${PIPESTATUS[@]}
|
||||
[[ $DEBUG -ge 4 ]] && echo "get_https_caa_rr_record: $tmp"
|
||||
[[ $caa_node =~ '.'$ ]] || caa_node+="."
|
||||
caa_node=${caa_node#*.}
|
||||
done
|
||||
if [[ -n "$caa" ]]; then
|
||||
pr_svrty_good "available"; out " - please check for match with \"Issuer\" below"
|
||||
if [[ $(count_lines "$caa") -eq 1 ]]; then
|
||||
out ": "
|
||||
else
|
||||
outln; out "$spaces"
|
||||
fi
|
||||
while read caa; do
|
||||
if [[ -n "$caa" ]]; then
|
||||
all_caa+="$caa, "
|
||||
fi
|
||||
done <<< "$caa"
|
||||
all_caa=${all_caa%, } # strip trailing comma
|
||||
pr_italic "$(out_row_aligned_max_width "$all_caa" "$indent " $TERM_WIDTH)"
|
||||
fileout "${jsonID}${json_postfix}" "OK" "$all_caa"
|
||||
else
|
||||
pr_svrty_low "not offered"
|
||||
fileout "${jsonID}${json_postfix}" "LOW" "--"
|
||||
fi
|
||||
fi
|
||||
outln
|
||||
|
||||
|
@ -17363,7 +17366,7 @@ run_breach() {
|
|||
detected_compression=$(sub_breach_helper "$get_command")
|
||||
case "$detected_compression" in
|
||||
warn_stalled)
|
||||
pr_warning "First request failed (HTTP header request stalled and was terminated)"
|
||||
pr_warning "First request failed (HTTP header request stalled and was terminated)\n"
|
||||
fileout "$jsonID" "WARN" "Test failed as first HTTP request stalled and was terminated" "$cve" "$cwe"
|
||||
ret=1
|
||||
;;
|
||||
|
@ -21265,7 +21268,8 @@ get_aaaa_record() {
|
|||
|
||||
# RFC6844: DNS Certification Authority Authorization (CAA) Resource Record
|
||||
# arg1: domain to check for
|
||||
get_caa_rr_record() {
|
||||
#FIXME: should be refactored, see get_https_rrecord()
|
||||
get_caa_rrecord() {
|
||||
local raw_caa=""
|
||||
local hash len line
|
||||
local -i len_caa_property
|
||||
|
@ -21341,11 +21345,102 @@ get_caa_rr_record() {
|
|||
return 1
|
||||
fi
|
||||
|
||||
# to do:
|
||||
#TODO:
|
||||
# 4: check whether $1 is a CNAME and take this
|
||||
return 0
|
||||
}
|
||||
|
||||
# See https://www.rfc-editor.org/rfc/rfc9460.html:
|
||||
# Service Binding and Parameter Specification via the DNS (SVCB and HTTPS Resource Records)
|
||||
# arg1: domain to check for
|
||||
#
|
||||
get_https_rrecord() {
|
||||
local raw_https=""
|
||||
local hash len line
|
||||
local -i len_https_property
|
||||
local https_property_name
|
||||
local https_property_value
|
||||
local saved_openssl_conf="$OPENSSL_CONF"
|
||||
local all_https=""
|
||||
local noidnout=""
|
||||
|
||||
"$HAS_DIG_NOIDNOUT" && noidnout="+noidnout"
|
||||
|
||||
[[ -n "$NODNS" ]] && return 2 # if minimum DNS lookup was instructed, leave here
|
||||
|
||||
# Ff there's a type65 record there are 2x3 output formats, mostly depending on age of distribution
|
||||
# -- roughly that's the difference between text and binary format -- and the type of DNS client
|
||||
|
||||
# for host:
|
||||
# 1) 'google.com has HTTPS record 1 . alpn="h2,h3" '
|
||||
# 2) 'google.com has TYPE65 record \# 13 0001000001000602683202683 '
|
||||
|
||||
# for drill and dig it's like
|
||||
#1) google.com. 18665 IN TYPE65 \# 13 00010000010006026832026833
|
||||
#2) google.com. 18301 IN HTTPS 1 . alpn="h2,h3"
|
||||
|
||||
# nslookup:
|
||||
# 1) dev.testssl.sh rdata_65 = 1 . alpn="h2"
|
||||
# 2) dev.testssl.sh rdata_65 = \# 10 00010000010003026832
|
||||
|
||||
# we normalize the output during the following so that's either 1) 1 . alpn="h2" or 2) \# 10 00010000010003026832 or empty
|
||||
|
||||
OPENSSL_CONF=""
|
||||
# Read either answer 1) or 2) into raw_https. Should be empty if no such record
|
||||
if "$HAS_DIG"; then
|
||||
raw_https="$(dig $DIG_R +short +search +timeout=3 +tries=3 $noidnout type65 "$1" 2>/dev/null)"
|
||||
# empty if there's no such record
|
||||
elif "$HAS_DRILL"; then
|
||||
raw_https="$(drill $1 type65 | awk '/'"^${1}"'.*TYPE65/ { print substr($0,index($0,$5)) }' )" # from 5th field onwards
|
||||
# empty if there's no such record
|
||||
elif "$HAS_HOST"; then
|
||||
raw_https="$(host -t type65 $1)"
|
||||
if grep -Ewq "has no HTTPS|has no TYPE65" <<< "$raw_https"; then
|
||||
raw_https=""
|
||||
else
|
||||
raw_https="$(sed -e 's/^.*has HTTPS record //' -e 's/^.*has TYPE65 record //' <<< "$raw_https")"
|
||||
fi
|
||||
elif "$HAS_NSLOOKUP"; then
|
||||
raw_https="$(strip_lf "$(nslookup -type=type65 $1 | awk '/'"^${1}"'.*rdata_65/ { print substr($0,index($0,$4)) }' )")"
|
||||
# empty if there's no such record
|
||||
else
|
||||
return 1
|
||||
# No dig, drill, host, or nslookup --> complaint was elsewhere already
|
||||
fi
|
||||
OPENSSL_CONF="$saved_openssl_conf" # see https://github.com/drwetter/testssl.sh/issues/134
|
||||
|
||||
if [[ -z "$raw_https" ]]; then
|
||||
:
|
||||
elif [[ -n "$raw_https" ]]; then
|
||||
safe_echo "$raw_https"
|
||||
elif [[ "$raw_https" =~ \#\ [0-9][0-9] ]]; then
|
||||
# now this is binary / hex encoded like \# 10 00010000010003026832
|
||||
#FIXME: this probably doesn't work yet and intentionally won't be reached yet
|
||||
while read hash len line ;do
|
||||
if [[ "${line:0:2}" == "00" ]]; then # probably the https flag, always 00, so we don't keep this
|
||||
len_https_property=$(printf "%0d" "$((10#${line:2:2}))") # get len and do type casting, for posteo we have 05 or 09 here as a string
|
||||
len_https_property=$((len_https_property*2)) # =>word! Now get name from 4th and value from 4th+len position...
|
||||
line="${line/ /}" # especially with iodefs there's a blank in the string which we just skip
|
||||
https_property_name="$(hex2ascii ${line:4:$len_https_property})"
|
||||
https_property_value="$(hex2ascii "${line:$((4+len_https_property)):100}")"
|
||||
# echo "${https}=${https}"
|
||||
all_https+="${https_property_name}=${https_property_value}\n"
|
||||
else
|
||||
outln "please report unknown CAA RR $line with flag @ $NODE"
|
||||
return 7
|
||||
fi
|
||||
done <<< "$raw_https"
|
||||
sort <<< "$(safe_echo "$all_https")"
|
||||
else
|
||||
echo "fixme"
|
||||
fi
|
||||
|
||||
#TODO:
|
||||
# 4: check whether $1 is a CNAME and take this
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
# arg1: domain to check for. Returned will be the MX record as a string
|
||||
get_mx_record() {
|
||||
local mx=""
|
||||
|
@ -22061,6 +22156,34 @@ determine_optimal_proto() {
|
|||
}
|
||||
|
||||
|
||||
|
||||
dns_https_rr () {
|
||||
local jsonID="DNS_HTTPS_rrecord"
|
||||
local https_rr=""
|
||||
|
||||
out "$indent"; pr_bold " DNS HTTPS RR"; out " (experim.) "
|
||||
if [[ -n "$NODNS" ]]; then
|
||||
out "(instructed to minimize/skip DNS queries)"
|
||||
fileout "${jsonID}" "INFO" "check skipped as instructed"
|
||||
elif "$DNS_VIA_PROXY"; then
|
||||
out "(instructed to use the proxy for DNS only)"
|
||||
fileout "${jsonID}" "INFO" "check skipped as instructed (proxy)"
|
||||
else
|
||||
https_rr="$(get_https_rrecord $NODE)"
|
||||
if [[ -n "$https_rr" ]]; then
|
||||
pr_svrty_good "yes" ; out " "
|
||||
prln_italic "$(out_row_aligned_max_width "$https_rr" "$indent " $TERM_WIDTH)"
|
||||
fileout "${jsonID}" "OK" "$https_rr"
|
||||
else
|
||||
outln "--"
|
||||
fileout "${jsonID}" "INFO" " no resource record found"
|
||||
fi
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
# arg1 (optional): ftp smtp, lmtp, pop3, imap, sieve, xmpp, xmpp-server, telnet, ldap, postgres, mysql, irc, nntp (maybe with trailing s)
|
||||
#
|
||||
determine_service() {
|
||||
|
@ -22099,8 +22222,11 @@ determine_service() {
|
|||
fi
|
||||
GET_REQ11="GET $URL_PATH HTTP/1.1\r\nHost: $NODE\r\nUser-Agent: $ua\r\n${basicauth_header}${reqheader}Accept-Encoding: identity\r\nAccept: */*\r\nConnection: Close\r\n\r\n"
|
||||
determine_optimal_proto
|
||||
# returns always 0:
|
||||
# returns always 0 and sets $SERVICE
|
||||
service_detection $OPTIMAL_PROTO
|
||||
if [[ $SERVICE == HTTP ]]; then
|
||||
dns_https_rr
|
||||
fi
|
||||
else # STARTTLS
|
||||
if [[ "$1" == postgres ]] || [[ "$1" == sieve ]]; then
|
||||
protocol="$1"
|
||||
|
@ -22185,7 +22311,6 @@ determine_service() {
|
|||
# It comes handy later also for STARTTLS injection to define this global. When we do banner grabbing
|
||||
# or replace service_detection() we might not need that anymore
|
||||
SERVICE=$protocol
|
||||
|
||||
fi
|
||||
|
||||
tmpfile_handle ${FUNCNAME[0]}.txt
|
||||
|
|
Loading…
Reference in New Issue