Server preference for TLS 1.3

This commit fixes #1163 which lead to the misleading output when
a TLS 1.3 enabled server had no preferences for the TLS 1.3 ciphers
but for anything below (like currently for testssl.NET).

The TLS 1.3 handshake in sockets plus the following openssl handshake
was moved to the top in run_server_preference() so that it can be better
determined whether TLS 1.3 is available. If this section's outcome is
TLS 1.3 is negotiated a single TLS 1.3 handshake with 5 ciphers only is
done forward and reverse. The resulting ciphers are later on compared
whether there's a cipher order for TLS 1.3.

Basically this section should be redone, so that all openssl handshakes
are replaced by sockets. As this would consume more time as it appears
reasonable at this point of time, this was not done yet. A starting point
for this would be tls13_list_fwd + reverse. After release of 3.0 90%
of the code will be replaced anyway.

DHE-RSA-SEED-SHA and SEED-SHA was added to the reverse and forward lists
as some old openssl versions + apache use it.

Also:

Googles ALPN_PROTO grpc-exp was added (to be reconsidered at some certain point)

Some redundant quotes in double square brackets were removed.

All "do_*" variables are now in quotes when tested w if or [[
This commit is contained in:
Dirk 2019-01-09 15:33:15 +01:00
parent e29b1f40e6
commit 53bd3bf736
1 changed files with 182 additions and 116 deletions

View File

@ -287,7 +287,7 @@ DETECTED_TLS_VERSION=""
TLS_EXTENSIONS=""
declare -r NPN_PROTOs="spdy/4a2,spdy/3,spdy/3.1,spdy/2,spdy/1,http/1.1"
# alpn_protos needs to be space-separated, not comma-seperated, including odd ones observed @ facebook and others, old ones like h2-17 omitted as they could not be found
declare -r ALPN_PROTOs="h2 spdy/3.1 http/1.1 h2-fb spdy/1 spdy/2 spdy/3 stun.turn stun.nat-discovery webrtc c-webrtc ftp"
declare -r ALPN_PROTOs="h2 spdy/3.1 http/1.1 grpc-exp h2-fb spdy/1 spdy/2 spdy/3 stun.turn stun.nat-discovery webrtc c-webrtc ftp"
declare -a SESS_RESUMPTION
TEMPDIR=""
TMPFILE=""
@ -3124,7 +3124,7 @@ openssl2rfc() {
local -i i
for (( i=0; i < TLS_NR_CIPHERS; i++ )); do
[[ "$1" == "${TLS_CIPHER_OSSL_NAME[i]}" ]] && rfcname="${TLS_CIPHER_RFC_NAME[i]}" && break
[[ "$1" == ${TLS_CIPHER_OSSL_NAME[i]} ]] && rfcname="${TLS_CIPHER_RFC_NAME[i]}" && break
done
[[ "$rfcname" == "-" ]] && rfcname=""
[[ -n "$rfcname" ]] && tm_out "$rfcname"
@ -3136,7 +3136,7 @@ rfc2openssl() {
local -i i
for (( i=0; i < TLS_NR_CIPHERS; i++ )); do
[[ "$1" == "${TLS_CIPHER_RFC_NAME[i]}" ]] && ossl_name="${TLS_CIPHER_OSSL_NAME[i]}" && break
[[ "$1" == ${TLS_CIPHER_RFC_NAME[i]} ]] && ossl_name="${TLS_CIPHER_OSSL_NAME[i]}" && break
done
[[ "$ossl_name" == "-" ]] && ossl_name=""
[[ -n "$ossl_name" ]] && tm_out "$ossl_name"
@ -3151,7 +3151,7 @@ openssl2hexcode() {
hexc="$(actually_supported_ciphers 'ALL:COMPLEMENTOFALL:@STRENGTH' 'ALL' "-V" | awk '/ '"$1"' / { print $1 }')"
else
for (( i=0; i < TLS_NR_CIPHERS; i++ )); do
[[ "$1" == "${TLS_CIPHER_OSSL_NAME[i]}" ]] && hexc="${TLS_CIPHER_HEXCODE[i]}" && break
[[ "$1" == ${TLS_CIPHER_OSSL_NAME[i]} ]] && hexc="${TLS_CIPHER_HEXCODE[i]}" && break
done
fi
[[ -z "$hexc" ]] && return 1
@ -3183,7 +3183,7 @@ show_rfc_style(){
*) return 1 ;;
esac
for (( i=0; i < TLS_NR_CIPHERS; i++ )); do
[[ "$hexcode" == "${TLS_CIPHER_HEXCODE[i]}" ]] && rfcname="${TLS_CIPHER_RFC_NAME[i]}" && break
[[ "$hexcode" == ${TLS_CIPHER_HEXCODE[i]} ]] && rfcname="${TLS_CIPHER_RFC_NAME[i]}" && break
done
[[ "$rfcname" == "-" ]] && rfcname=""
[[ -n "$rfcname" ]] && tm_out "$rfcname"
@ -3193,17 +3193,17 @@ show_rfc_style(){
neat_header(){
if [[ "$DISPLAY_CIPHERNAMES" =~ rfc ]]; then
out "$(printf -- "Hexcode Cipher Suite Name (IANA/RFC) KeyExch. Encryption Bits")"
[[ "$DISPLAY_CIPHERNAMES" != "rfc-only" ]] && out "$(printf -- " Cipher Suite Name (OpenSSL)")"
[[ "$DISPLAY_CIPHERNAMES" != rfc-only ]] && out "$(printf -- " Cipher Suite Name (OpenSSL)")"
outln
out "$(printf -- "%s------------------------------------------------------------------------------------------")"
[[ "$DISPLAY_CIPHERNAMES" != "rfc-only" ]] && out "$(printf -- "---------------------------------------")"
[[ "$DISPLAY_CIPHERNAMES" != rfc-only ]] && out "$(printf -- "---------------------------------------")"
outln
else
out "$(printf -- "Hexcode Cipher Suite Name (OpenSSL) KeyExch. Encryption Bits")"
[[ "$DISPLAY_CIPHERNAMES" != "openssl-only" ]] && out "$(printf -- " Cipher Suite Name (IANA/RFC)")"
[[ "$DISPLAY_CIPHERNAMES" != openssl-only ]] && out "$(printf -- " Cipher Suite Name (IANA/RFC)")"
outln
out "$(printf -- "%s--------------------------------------------------------------------------")"
[[ "$DISPLAY_CIPHERNAMES" != "openssl-only" ]] && out "$(printf -- "---------------------------------------------------")"
[[ "$DISPLAY_CIPHERNAMES" != openssl-only ]] && out "$(printf -- "---------------------------------------------------")"
outln
fi
}
@ -4767,7 +4767,7 @@ has_server_protocol() {
if [[ "$PROTOS_OFFERED" =~ $proto: ]]; then
for proto_val_pair in $PROTOS_OFFERED; do
if [[ $proto_val_pair =~ $proto: ]]; then
if [[ ${proto_val_pair#*:} == "yes" ]]; then
if [[ ${proto_val_pair#*:} == yes ]]; then
echo 0
return 0
else
@ -5780,16 +5780,20 @@ sub_session_resumption() {
}
run_server_preference() {
local cipher1 cipher2 prev_cipher=""
local default_cipher="" default_proto
local limitedsense supported_sslv2_ciphers
local cipher1="" cipher2="" tls13_cipher1="" tls13_cipher2="" default_proto=""
local prev_cipher="" default_cipher=""
local limitedsense="" supported_sslv2_ciphers
local -a cipher proto
local proto_ossl proto_txt proto_hex cipherlist i
local -i ret=0 j sclient_success str_len
local list_fwd="DES-CBC3-SHA:RC4-MD5:DES-CBC-SHA:RC4-SHA:AES128-SHA:AES128-SHA256:AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:ECDH-RSA-DES-CBC3-SHA:ECDH-RSA-AES128-SHA:ECDH-RSA-AES256-SHA:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:DHE-DSS-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:AES256-SHA256:ECDHE-RSA-DES-CBC3-SHA:ECDHE-RSA-AES128-SHA256:AES256-GCM-SHA384:AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-SHA256:ADH-AES256-GCM-SHA384:AECDH-AES128-SHA:ECDHE-RSA-RC4-SHA:ECDHE-ECDSA-AES128-SHA"
local list_reverse="ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-RC4-SHA:AECDH-AES128-SHA:ADH-AES256-GCM-SHA384:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-GCM-SHA256:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-DES-CBC3-SHA:AES256-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-DSS-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDH-RSA-AES256-SHA:ECDH-RSA-AES128-SHA:ECDH-RSA-DES-CBC3-SHA:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-AES128-SHA:AES256-SHA:AES128-SHA256:AES128-SHA:RC4-SHA:DES-CBC-SHA:RC4-MD5:DES-CBC3-SHA"
local list_fwd="DHE-RSA-SEED-SHA:SEED-SHA:DES-CBC3-SHA:RC4-MD5:DES-CBC-SHA:RC4-SHA:AES128-SHA:AES128-SHA256:AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:ECDH-RSA-DES-CBC3-SHA:ECDH-RSA-AES128-SHA:ECDH-RSA-AES256-SHA:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:DHE-DSS-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:AES256-SHA256:ECDHE-RSA-DES-CBC3-SHA:ECDHE-RSA-AES128-SHA256:AES256-GCM-SHA384:AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-SHA256:ADH-AES256-GCM-SHA384:AECDH-AES128-SHA:ECDHE-RSA-RC4-SHA:ECDHE-ECDSA-AES128-SHA"
local list_reverse="ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-RC4-SHA:AECDH-AES128-SHA:ADH-AES256-GCM-SHA384:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-GCM-SHA256:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-DES-CBC3-SHA:AES256-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-DSS-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDH-RSA-AES256-SHA:ECDH-RSA-AES128-SHA:ECDH-RSA-DES-CBC3-SHA:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-AES128-SHA:AES256-SHA:AES128-SHA256:AES128-SHA:RC4-SHA:DES-CBC-SHA:RC4-MD5:DES-CBC3-SHA:SEED-SHA:DHE-RSA-SEED-SHA"
tls13_list_fwd="TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256"
tls13_list_reverse="TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384"
tls_list_fwd="c0,2c, c0,30, 00,9f, cc,a9, cc,a8, cc,aa, c0,2b, c0,2f, 00,9e, c0,24, c0,28, 00,6b, c0,23, c0,27, 00,67, c0,0a, 00,04, 00,05, 00,09, 00,0a, 00,9a, 00,96,
c0,14, 00,39, c0,09, c0,13, 00,33, 00,9d, 00,9c, 13,01, 13,02, 13,03, 13,04, 13,05, 00,3d, 00,3c, 00,35, 00,2f, 00,ff"
tls_list_rev="00,2f, 00,35, 00,3c, 00,3d, 13,05, 13,04, 13,03, 13,02, 13,01, 00,9c, 00,9d, 00,33, c0,13, c0,09, 00,39, c0,14, 00,96, 00,9a, 00,0a, 00,09, 00,05, 00,04,
c0,0a, 00,67, c0,27, c0,23, 00,6b, c0,28, c0,24, 00,9e, c0,2f, c0,2b, cc,aa, cc,a8, cc,a9, 00,9f, c0,30, c0,2c, 00,ff"
local has_cipher_order=false
local addcmd="" addcmd2=""
local using_sockets=true
@ -5801,9 +5805,10 @@ run_server_preference() {
outln
pr_headlineln " Testing server preferences "
outln
outln
pr_bold " Has server cipher order? "
if [[ "$OPTIMAL_PROTO" == -ssl2 ]]; then
addcmd="$OPTIMAL_PROTO"
else
@ -5811,93 +5816,123 @@ run_server_preference() {
# and the -no_ssl2 isn't provided.
addcmd="-no_ssl2 $SNI"
fi
[[ $DEBUG -ge 4 ]] && echo -e "\n Forward: ${list_fwd}\n ${tls13_list_fwd}"
$OPENSSL s_client $(s_client_options "$STARTTLS -cipher $list_fwd -ciphersuites $tls13_list_fwd $BUGS -connect $NODEIP:$PORT $PROXY $addcmd") </dev/null 2>$ERRFILE >$TMPFILE
if ! sclient_connect_successful $? $TMPFILE && [[ -z "$STARTTLS_PROTOCOL" ]]; then
list_fwd="$(actually_supported_ciphers $list_fwd $tls13_list_fwd '-tls1')"
pr_warning "no matching cipher in this list found (pls report this): "
outln "$list_fwd . "
fileout "$jsonID" "WARN" "Could not determine server cipher order, no matching cipher in list found (pls report this): $list_fwd"
tmpfile_handle ${FUNCNAME[0]}.txt
return 1
# we assume the problem is with testing here but it could be also the server side
elif [[ -n "$STARTTLS_PROTOCOL" ]]; then
# now it still could be that we hit this bug: https://github.com/drwetter/testssl.sh/issues/188
# workaround is to connect with a protocol
debugme tm_out "(workaround #188) "
determine_optimal_proto $STARTTLS_PROTOCOL
[[ ! "$STARTTLS_OPTIMAL_PROTO" =~ ssl ]] && addcmd2="$SNI"
$OPENSSL s_client $(s_client_options "$STARTTLS $STARTTLS_OPTIMAL_PROTO -cipher $list_fwd -ciphersuites $tls13_list_fwd $BUGS -connect $NODEIP:$PORT $PROXY $addcmd2") </dev/null 2>$ERRFILE >$TMPFILE
if ! sclient_connect_successful $? $TMPFILE; then
# Determine negotiated protocol upfront
sclient_success=1
if "$using_sockets" && [[ $(has_server_protocol "tls1_3") -ne 1 ]]; then
# Send similar list of cipher suites as OpenSSL 1.1.1 does
tls_sockets "04" \
"c0,2c, c0,30, 00,9f, cc,a9, cc,a8, cc,aa, c0,2b, c0,2f, 00,9a, 00,96,
00,9e, c0,24, c0,28, 00,6b, c0,23, c0,27, 00,67, c0,0a,
c0,14, 00,39, c0,09, c0,13, 00,33, 00,9d, 00,9c, 13,02,
13,03, 13,01, 13,04, 13,05, 00,3d, 00,3c, 00,35, 00,2f, 00,ff" \
"ephemeralkey"
[[ $sclient_success -eq 2 ]] && sclient_success=0 # 2: downgraded
sclient_success=$?
if [[ $sclient_success -eq 0 ]] ; then
cp "$TEMPDIR/$NODEIP.parse_tls_serverhello.txt" $TMPFILE
cp "$TEMPDIR/$NODEIP.parse_tls_serverhello.txt" "$TEMPDIR/$NODEIP.parse_tls13_serverhello.txt"
fi
fi
if [[ $sclient_success -ne 0 ]]; then
$OPENSSL s_client $(s_client_options "$STARTTLS $BUGS -connect $NODEIP:$PORT $PROXY $addcmd") </dev/null 2>>$ERRFILE >$TMPFILE
if sclient_connect_successful $? $TMPFILE; then
cipher0=$(get_cipher $TMPFILE)
debugme tm_out "0 --> $cipher0\n"
else
# 2 second try with $OPTIMAL_PROTO especially for intolerant IIS6 servers:
$OPENSSL s_client $(s_client_options "$STARTTLS $OPTIMAL_PROTO $BUGS -connect $NODEIP:$PORT $PROXY $SNI") </dev/null 2>>$ERRFILE >$TMPFILE
if ! sclient_connect_successful $? $TMPFILE; then
pr_warning "Handshake error!"
ret=1
fi
fi
fi
[[ "$default_proto" == TLSv1.0 ]] && default_proto="TLSv1"
default_proto=$(get_protocol $TMPFILE)
# Some servers don't have a TLS 1.3 cipher order, see #1163
if [[ "$default_proto" == TLSv1.3 ]]; then
tls_sockets "04" "13,05, 13,04, 13,03, 13,02, 13,01, 00,ff"
[[ $sclient_success -ne 0 ]] && ret=1 && prln_fixme "something weird happened around line $((LINENO - 1))"
cp "$TEMPDIR/$NODEIP.parse_tls_serverhello.txt" $TMPFILE
tls13_cipher1=$(get_cipher $TMPFILE)
debugme tm_out "TLS 1.3: --> $tls13_cipher1\n"
tls_sockets "04" "13,01, 13,02, 13,03, 13,04, 13,05, 00,ff"
[[ $sclient_success -ne 0 ]] && ret=1 && prln_fixme "something weird happened around line $((LINENO - 1))"
cp "$TEMPDIR/$NODEIP.parse_tls_serverhello.txt" $TMPFILE
tls13_cipher2=$(get_cipher $TMPFILE)
debugme tm_out "TLS 1.3: --> $tls13_cipher2\n"
else
[[ $DEBUG -ge 4 ]] && echo -e "\n Forward: ${list_fwd}\n ${tls13_list_fwd}"
$OPENSSL s_client $(s_client_options "$STARTTLS -cipher $list_fwd -ciphersuites $tls13_list_fwd $BUGS -connect $NODEIP:$PORT $PROXY $addcmd") </dev/null 2>$ERRFILE >$TMPFILE
if ! sclient_connect_successful $? $TMPFILE && [[ -z "$STARTTLS_PROTOCOL" ]]; then
list_fwd="$(actually_supported_ciphers $list_fwd $tls13_list_fwd '-tls1')"
pr_warning "no matching cipher in this list found (pls report this): "
outln "$list_fwd . "
fileout "$jsonID" "WARN" "Could not determine cipher order, no matching cipher in list found (pls report this): $list_fwd"
fileout "$jsonID" "WARN" "Could not determine server cipher order, no matching cipher in list found (pls report this): $list_fwd"
tmpfile_handle ${FUNCNAME[0]}.txt
return 1
# we assume the problem is with testing here but it could be also the server side
elif [[ -n "$STARTTLS_PROTOCOL" ]]; then
# now it still could be that we hit this bug: https://github.com/drwetter/testssl.sh/issues/188
# workaround is to connect with a protocol
debugme tm_out "(workaround #188) "
determine_optimal_proto $STARTTLS_PROTOCOL
[[ ! "$STARTTLS_OPTIMAL_PROTO" =~ ssl ]] && addcmd2="$SNI"
$OPENSSL s_client $(s_client_options "$STARTTLS $STARTTLS_OPTIMAL_PROTO -cipher $list_fwd -ciphersuites $tls13_list_fwd $BUGS -connect $NODEIP:$PORT $PROXY $addcmd2") </dev/null 2>$ERRFILE >$TMPFILE
if ! sclient_connect_successful $? $TMPFILE; then
list_fwd="$(actually_supported_ciphers $list_fwd $tls13_list_fwd '-tls1')"
pr_warning "no matching cipher in this list found (pls report this): "
outln "$list_fwd . "
fileout "$jsonID" "WARN" "Could not determine cipher order, no matching cipher in list found (pls report this): $list_fwd"
tmpfile_handle ${FUNCNAME[0]}.txt
return 1
fi
fi
fi
cipher1=$(get_cipher $TMPFILE) # cipher1 from 1st serverhello
debugme tm_out "--> $cipher1\n"
cipher1=$(get_cipher $TMPFILE) # cipher1 from 1st serverhello
debugme tm_out "1 --> $cipher1\n"
if [[ -n "$STARTTLS_OPTIMAL_PROTO" ]]; then
addcmd2="$STARTTLS_OPTIMAL_PROTO $SNI"
else
if [[ "$OPTIMAL_PROTO" == -ssl2 ]]; then
addcmd2="$OPTIMAL_PROTO"
if [[ -n "$STARTTLS_OPTIMAL_PROTO" ]]; then
addcmd2="$STARTTLS_OPTIMAL_PROTO $SNI"
else
addcmd2="-no_ssl2 $SNI"
if [[ "$OPTIMAL_PROTO" == -ssl2 ]]; then
addcmd2="$OPTIMAL_PROTO"
else
addcmd2="-no_ssl2 $SNI"
fi
fi
# second client hello with reverse list
[[ $DEBUG -ge 4 ]] && echo -e "\n Reverse: ${list_reverse}\n ${tls13_list_reverse}"
$OPENSSL s_client $(s_client_options "$STARTTLS -cipher $list_reverse -ciphersuites $tls13_list_reverse $BUGS -connect $NODEIP:$PORT $PROXY $addcmd2") </dev/null 2>>$ERRFILE >$TMPFILE
# first handshake worked above so no error handling here
cipher2=$(get_cipher $TMPFILE) # cipher2 from 2nd serverhello
debugme tm_out "2 --> $cipher2\n"
fi
# second client hello with reverse list
[[ $DEBUG -ge 4 ]] && echo -e "\n Reverse: ${list_reverse}\n ${tls13_list_reverse}"
$OPENSSL s_client $(s_client_options "$STARTTLS -cipher $list_reverse -ciphersuites $tls13_list_reverse $BUGS -connect $NODEIP:$PORT $PROXY $addcmd2") </dev/null 2>>$ERRFILE >$TMPFILE
# first handshake worked above so no error handling here
cipher2=$(get_cipher $TMPFILE) # cipher2 from 2nd serverhello
debugme tm_out "--> $cipher2\n"
if [[ "$cipher1" != "$cipher2" ]]; then
if [[ "$default_proto" == TLSv1.3 ]] && [[ $tls13_cipher1 != $tls13_cipher2 ]]; then
pr_svrty_good "yes (OK)"; out " -- no TLS 1.3 cipher order"
has_cipher_order=true
fileout "$jsonID" "OK" "server -- TLS 1.3 client determined"
cipher1="$tls13_cipher1"
cipher2="$tls13_cipher2"
elif [[ "$cipher1" != $cipher2 ]]; then
# server used the different ends (ciphers) from the client hello
pr_svrty_high "nope (NOT ok)"
limitedsense=" (limited sense as client will pick)"
fileout "$jsonID" "HIGH" "NOT cipher order configured"
fileout "$jsonID" "HIGH" "NOT a cipher order configured"
else
pr_svrty_best "yes (OK)"
has_cipher_order=true
limitedsense=""
fileout "$jsonID" "OK" "server"
fi
outln
pr_bold " Negotiated protocol "
jsonID="protocol_negotiated"
sclient_success=1
if "$using_sockets" && ! "$HAS_TLS13" && [[ $(has_server_protocol "tls1_3") -ne 1 ]]; then
# Send same list of cipher suites as OpenSSL 1.1.1 sends.
tls_sockets "04" \
"c0,2c, c0,30, 00,9f, cc,a9, cc,a8, cc,aa, c0,2b, c0,2f,
00,9e, c0,24, c0,28, 00,6b, c0,23, c0,27, 00,67, c0,0a,
c0,14, 00,39, c0,09, c0,13, 00,33, 00,9d, 00,9c, 13,02,
13,03, 13,01, 00,3d, 00,3c, 00,35, 00,2f, 00,ff" \
"ephemeralkey"
sclient_success=$?
[[ $sclient_success -eq 2 ]] && sclient_success=0 # 2: downgraded
[[ $sclient_success -eq 0 ]] && cp "$TEMPDIR/$NODEIP.parse_tls_serverhello.txt" $TMPFILE
fi
if [[ $sclient_success -ne 0 ]]; then
$OPENSSL s_client $(s_client_options "$STARTTLS $BUGS -connect $NODEIP:$PORT $PROXY $addcmd") </dev/null 2>>$ERRFILE >$TMPFILE
if ! sclient_connect_successful $? $TMPFILE; then
# 2 second try with $OPTIMAL_PROTO especially for intolerant IIS6 servers:
$OPENSSL s_client $(s_client_options "$STARTTLS $OPTIMAL_PROTO $BUGS -connect $NODEIP:$PORT $PROXY $SNI") </dev/null 2>>$ERRFILE >$TMPFILE
if ! sclient_connect_successful $? $TMPFILE; then
pr_warning "Handshake error!"
ret=1
fi
fi
fi
default_proto=$(get_protocol $TMPFILE)
[[ "$default_proto" == "TLSv1.0" ]] && default_proto="TLSv1"
case "$default_proto" in
*TLSv1.3)
prln_svrty_best $default_proto
@ -5943,7 +5978,16 @@ run_server_preference() {
pr_bold " Negotiated cipher "
jsonID="cipher_negotiated"
# restore file from above
[[ "$default_proto" == TLSv1.3 ]] && cp "$TEMPDIR/$NODEIP.parse_tls13_serverhello.txt" $TMPFILE
cipher1=$(get_cipher $TMPFILE)
# Sanity check: Handshake with no ciphers and one with forward list didn't overlap
if [[ "$cipher0" != $cipher1 ]]; then
limitedsense=" (matching cipher in list missing)"
fi
if [[ "$DISPLAY_CIPHERNAMES" =~ openssl ]] && ( [[ "$cipher1" == TLS_* ]] || [[ "$cipher1" == SSL_* ]] ); then
default_cipher="$(rfc2openssl "$cipher1")"
elif [[ "$DISPLAY_CIPHERNAMES" =~ rfc ]] && [[ "$cipher1" != TLS_* ]] && [[ "$cipher1" != SSL_* ]]; then
@ -5975,7 +6019,14 @@ run_server_preference() {
;;
esac
read_dhbits_from_file "$TMPFILE"
outln "$limitedsense"
if [[ "$cipher0" != $cipher1 ]]; then
pr_warning " -- inconclusive test, matching cipher in list missing"
outln ", better see below"
#FIXME: This is ugly but the best we can do before rewrite this section
else
outln "$limitedsense"
fi
if "$has_cipher_order"; then
cipher_pref_check
@ -5997,7 +6048,7 @@ run_server_preference() {
if [[ "${TLS_CIPHER_SSLVERS[j]}" == "SSLv2" ]]; then
cipher1="${TLS_CIPHER_HEXCODE[j]}"
cipher1="$(tolower "x${cipher1:2:2}${cipher1:7:2}${cipher1:12:2}")"
if [[ "$supported_sslv2_ciphers" =~ "$cipher1" ]]; then
if [[ "$supported_sslv2_ciphers" =~ $cipher1 ]]; then
if ( [[ "$DISPLAY_CIPHERNAMES" =~ openssl ]] && [[ "${TLS_CIPHER_OSSL_NAME[j]}" != "-" ]] ) || [[ "${TLS_CIPHER_RFC_NAME[j]}" == "-" ]]; then
cipher[i]="${TLS_CIPHER_OSSL_NAME[j]}"
else
@ -6249,10 +6300,10 @@ cipher_pref_check() {
rfc_ciph[nr_nonossl_ciphers]="${TLS_CIPHER_RFC_NAME[i]}"
index[nr_nonossl_ciphers]=$i
# Only test ciphers that are relevant to the protocol.
if [[ "$p" == "tls1_3" ]]; then
if [[ "$p" == tls1_3 ]]; then
[[ "${hexc:2:2}" == "13" ]] && nr_nonossl_ciphers+=1
elif [[ "$p" == "tls1_2" ]]; then
[[ "${hexc:2:2}" != "13" ]] && nr_nonossl_ciphers+=1
elif [[ "$p" == tls1_2 ]]; then
[[ "${hexc:2:2}" != 13 ]] && nr_nonossl_ciphers+=1
elif [[ ! "${TLS_CIPHER_RFC_NAME[i]}" =~ SHA256 ]] && \
[[ ! "${TLS_CIPHER_RFC_NAME[i]}" =~ SHA384 ]] && \
[[ "${TLS_CIPHER_RFC_NAME[i]}" != *"_CCM" ]] && \
@ -6343,7 +6394,7 @@ cipher_pref_check() {
[[ $? -ne 0 ]] && break
cipher=$(get_cipher "$TEMPDIR/$NODEIP.parse_tls_serverhello.txt")
for (( i=0; i < nr_ciphers; i++ )); do
[[ "$cipher" == "${rfc_ciph[i]}" ]] && ciphers_found2[i]=true && break
[[ "$cipher" == ${rfc_ciph[i]} ]] && ciphers_found2[i]=true && break
done
if [[ "$DISPLAY_CIPHERNAMES" =~ openssl ]] && [[ $TLS_NR_CIPHERS -ne 0 ]]; then
cipher="$(rfc2openssl "$cipher")"
@ -16080,7 +16131,7 @@ prepare_arrays() {
ossl_ciph="$(awk '/'"$hexc"'/ { print $3 }' <<< "$ossl_supported_tls")"
if [[ -n "$ossl_ciph" ]]; then
TLS_CIPHER_OSSL_SUPPORTED[i]=true
[[ "$ossl_ciph" != "${TLS_CIPHER_OSSL_NAME[i]}" ]] && TLS_CIPHER_OSSL_NAME[i]="$ossl_ciph"
[[ "$ossl_ciph" != ${TLS_CIPHER_OSSL_NAME[i]} ]] && TLS_CIPHER_OSSL_NAME[i]="$ossl_ciph"
[[ "${hexc:2:2}" == 13 ]] && TLS13_OSSL_CIPHERS+=":$ossl_ciph"
fi
fi
@ -16788,7 +16839,7 @@ determine_optimal_proto() {
done
"$all_failed" && OPTIMAL_PROTO=""
debugme echo "OPTIMAL_PROTO: $OPTIMAL_PROTO"
if [[ "$OPTIMAL_PROTO" == "-ssl2" ]]; then
if [[ "$OPTIMAL_PROTO" == -ssl2 ]]; then
prln_magenta "$NODEIP:$PORT appears to only support SSLv2."
ignore_no_or_lame " Type \"yes\" to proceed and accept false negatives or positives" "yes"
[[ $? -ne 0 ]] && exit $ERR_CLUELESS
@ -17828,6 +17879,9 @@ parse_cmd_line() {
[[ $? -eq 0 ]] && shift
;;
--devel) ### this development feature will soon disappear
# arg1: SSL/TLS protocol (SSLv2=22)
# arg2: list of cipher suites / hostname/ip
# arg3: hostname/ip
HEX_CIPHER="$TLS12_CIPHER"
# DEBUG=3 ./testssl.sh --devel 03 "cc, 13, c0, 13" google.de --> TLS 1.2, old CHACHA/POLY
# DEBUG=3 ./testssl.sh --devel 03 "cc,a8, cc,a9, cc,aa, cc,ab, cc,ac" blog.cloudflare.com --> new CHACHA/POLY
@ -18124,6 +18178,7 @@ stopwatch() {
LAST_TIME=$(( new_delta + LAST_TIME ))
}
lets_roll() {
local -i ret=0
local section_number=1
@ -18147,14 +18202,25 @@ lets_roll() {
determine_service "$1" # STARTTLS service? Other will be determined here too. Returns always 0 or has already exited if fatal error occurred
# "secret" devel options --devel:
$do_tls_sockets && [[ $TLS_LOW_BYTE -eq 22 ]] && { sslv2_sockets "" "true"; echo $? ; exit $ALLOK; }
$do_tls_sockets && [[ $TLS_LOW_BYTE -ne 22 ]] && { tls_sockets "$TLS_LOW_BYTE" "$HEX_CIPHER" "all"; echo $? ; exit $ALLOK; }
$do_cipher_match && { fileout_section_header $section_number false; run_cipher_match ${single_cipher}; }
if "$do_tls_sockets"; then
if [[ "$TLS_LOW_BYTE" == 22 ]]; then
sslv2_sockets "" "true"
else
if [[ "$TLS_LOW_BYTE" == 04 ]]; then
tls_sockets "$TLS_LOW_BYTE" "$HEX_CIPHER" "ephemeralkey"
else
tls_sockets "$TLS_LOW_BYTE" "$HEX_CIPHER" "all"
fi
fi
echo $?
exit $ALLOK;
fi
"$do_cipher_match" && { fileout_section_header $section_number false; run_cipher_match ${single_cipher}; }
((section_number++))
# all top level functions now following have the prefix "run_"
fileout_section_header $section_number false && ((section_number++))
$do_protocols && {
"$do_protocols" && {
run_protocols; ret=$(($? + ret)); stopwatch run_protocols;
run_npn; ret=$(($? + ret)); stopwatch run_npn;
run_alpn; ret=$(($? + ret)); stopwatch run_alpn;
@ -18163,18 +18229,18 @@ lets_roll() {
"$do_grease" && { run_grease; ret=$(($? + ret)); stopwatch run_grease; }
fileout_section_header $section_number true && ((section_number++))
$do_cipherlists && { run_cipherlists; ret=$(($? + ret)); stopwatch run_cipherlists; }
"$do_cipherlists" && { run_cipherlists; ret=$(($? + ret)); stopwatch run_cipherlists; }
fileout_section_header $section_number true && ((section_number++))
$do_pfs && { run_pfs; ret=$(($? + ret)); stopwatch run_pfs; }
"$do_pfs" && { run_pfs; ret=$(($? + ret)); stopwatch run_pfs; }
fileout_section_header $section_number true && ((section_number++))
$do_server_preference && { run_server_preference; ret=$(($? + ret)); stopwatch run_server_preference; }
"$do_server_preference" && { run_server_preference; ret=$(($? + ret)); stopwatch run_server_preference; }
fileout_section_header $section_number true && ((section_number++))
$do_server_defaults && { run_server_defaults; ret=$(($? + ret)); stopwatch run_server_defaults; }
"$do_server_defaults" && { run_server_defaults; ret=$(($? + ret)); stopwatch run_server_defaults; }
if $do_header; then
if "$do_header"; then
#TODO: refactor this into functions
fileout_section_header $section_number true && ((section_number++))
if [[ $SERVICE == "HTTP" ]]; then
@ -18194,35 +18260,35 @@ lets_roll() {
fi
# vulnerabilities
if [[ $VULN_COUNT -gt $VULN_THRESHLD ]] || $do_vulnerabilities; then
if [[ $VULN_COUNT -gt $VULN_THRESHLD ]] || "$do_vulnerabilities"; then
outln; pr_headlineln " Testing vulnerabilities "
outln
fi
fileout_section_header $section_number true && ((section_number++))
$do_heartbleed && { run_heartbleed; ret=$(($? + ret)); stopwatch run_heartbleed; }
$do_ccs_injection && { run_ccs_injection; ret=$(($? + ret)); stopwatch run_ccs_injection; }
$do_ticketbleed && { run_ticketbleed; ret=$(($? + ret)); stopwatch run_ticketbleed; }
$do_robot && { run_robot; ret=$(($? + ret)); stopwatch run_robot; }
$do_renego && { run_renego; ret=$(($? + ret)); stopwatch run_renego; }
$do_crime && { run_crime; ret=$(($? + ret)); stopwatch run_crime; }
$do_breach && { run_breach "$URL_PATH" ; ret=$(($? + ret)); stopwatch run_breach; }
$do_ssl_poodle && { run_ssl_poodle; ret=$(($? + ret)); stopwatch run_ssl_poodle; }
$do_tls_fallback_scsv && { run_tls_fallback_scsv; ret=$(($? + ret)); stopwatch run_tls_fallback_scsv; }
$do_sweet32 && { run_sweet32; ret=$(($? + ret)); stopwatch run_sweet32; }
$do_freak && { run_freak; ret=$(($? + ret)); stopwatch run_freak; }
$do_drown && { run_drown ret=$(($? + ret)); stopwatch run_drown; }
$do_logjam && { run_logjam; ret=$(($? + ret)); stopwatch run_logjam; }
$do_beast && { run_beast; ret=$(($? + ret)); stopwatch run_beast; }
$do_lucky13 && { run_lucky13; ret=$(($? + ret)); stopwatch run_lucky13; }
$do_rc4 && { run_rc4; ret=$(($? + ret)); stopwatch run_rc4; }
"$do_heartbleed" && { run_heartbleed; ret=$(($? + ret)); stopwatch run_heartbleed; }
"$do_ccs_injection" && { run_ccs_injection; ret=$(($? + ret)); stopwatch run_ccs_injection; }
"$do_ticketbleed" && { run_ticketbleed; ret=$(($? + ret)); stopwatch run_ticketbleed; }
"$do_robot" && { run_robot; ret=$(($? + ret)); stopwatch run_robot; }
"$do_renego" && { run_renego; ret=$(($? + ret)); stopwatch run_renego; }
"$do_crime" && { run_crime; ret=$(($? + ret)); stopwatch run_crime; }
"$do_breach" && { run_breach "$URL_PATH" ; ret=$(($? + ret)); stopwatch run_breach; }
"$do_ssl_poodle" && { run_ssl_poodle; ret=$(($? + ret)); stopwatch run_ssl_poodle; }
"$do_tls_fallback_scsv" && { run_tls_fallback_scsv; ret=$(($? + ret)); stopwatch run_tls_fallback_scsv; }
"$do_sweet32" && { run_sweet32; ret=$(($? + ret)); stopwatch run_sweet32; }
"$do_freak" && { run_freak; ret=$(($? + ret)); stopwatch run_freak; }
"$do_drown" && { run_drown ret=$(($? + ret)); stopwatch run_drown; }
"$do_logjam" && { run_logjam; ret=$(($? + ret)); stopwatch run_logjam; }
"$do_beast" && { run_beast; ret=$(($? + ret)); stopwatch run_beast; }
"$do_lucky13" && { run_lucky13; ret=$(($? + ret)); stopwatch run_lucky13; }
"$do_rc4" && { run_rc4; ret=$(($? + ret)); stopwatch run_rc4; }
fileout_section_header $section_number true && ((section_number++))
$do_allciphers && { run_allciphers; ret=$(($? + ret)); stopwatch run_allciphers; }
$do_cipher_per_proto && { run_cipher_per_proto; ret=$(($? + ret)); stopwatch run_cipher_per_proto; }
"$do_allciphers" && { run_allciphers; ret=$(($? + ret)); stopwatch run_allciphers; }
"$do_cipher_per_proto" && { run_cipher_per_proto; ret=$(($? + ret)); stopwatch run_cipher_per_proto; }
fileout_section_header $section_number true && ((section_number++))
$do_client_simulation && { run_client_simulation; ret=$(($? + ret)); stopwatch run_client_simulation; }
"$do_client_simulation" && { run_client_simulation; ret=$(($? + ret)); stopwatch run_client_simulation; }
fileout_section_footer true