Skip to content

Commit bc31639

Browse files
authored
Merge pull request #545 from dcooper16/cipher_order_sockets
Use sockets to determine cipher order
2 parents 2a5d56a + db4108c commit bc31639

File tree

1 file changed

+176
-54
lines changed

1 file changed

+176
-54
lines changed

testssl.sh

+176-54
Original file line numberDiff line numberDiff line change
@@ -4866,31 +4866,36 @@ run_server_preference() {
48664866
check_tls12_pref() {
48674867
local batchremoved="-CAMELLIA:-IDEA:-KRB5:-PSK:-SRP:-aNULL:-eNULL"
48684868
local batchremoved_success=false
4869-
local tested_cipher="-$1"
4870-
local order="$1"
4869+
local tested_cipher=""
4870+
local order=""
4871+
local -i nr_ciphers_found_r1=0 nr_ciphers_found_r2=0
48714872

48724873
while true; do
4873-
$OPENSSL s_client $STARTTLS -tls1_2 $BUGS -cipher "ALL:$tested_cipher:$batchremoved" -connect $NODEIP:$PORT $PROXY $SNI </dev/null 2>>$ERRFILE >$TMPFILE
4874+
$OPENSSL s_client $STARTTLS -tls1_2 $BUGS -cipher "ALL$tested_cipher:$batchremoved" -connect $NODEIP:$PORT $PROXY $SNI </dev/null 2>>$ERRFILE >$TMPFILE
48744875
if sclient_connect_successful $? $TMPFILE ; then
48754876
cipher=$(awk '/Cipher.*:/ { print $3 }' $TMPFILE)
48764877
order+=" $cipher"
48774878
tested_cipher="$tested_cipher:-$cipher"
4879+
nr_ciphers_found_r1+=1
4880+
"$FAST" && break
48784881
else
48794882
debugme outln "A: $tested_cipher"
48804883
break
48814884
fi
48824885
done
48834886
batchremoved="${batchremoved//-/}"
48844887
while true; do
4885-
# no ciphers from "ALL:$tested_cipher:$batchremoved" left
4888+
# no ciphers from "ALL$tested_cipher:$batchremoved" left
48864889
# now we check $batchremoved, and remove the minus signs first:
48874890
$OPENSSL s_client $STARTTLS -tls1_2 $BUGS -cipher "$batchremoved" -connect $NODEIP:$PORT $PROXY $SNI </dev/null 2>>$ERRFILE >$TMPFILE
48884891
if sclient_connect_successful $? $TMPFILE ; then
48894892
batchremoved_success=true # signals that we have some of those ciphers and need to put everything together later on
48904893
cipher=$(awk '/Cipher.*:/ { print $3 }' $TMPFILE)
48914894
order+=" $cipher"
48924895
batchremoved="$batchremoved:-$cipher"
4896+
nr_ciphers_found_r1+=1
48934897
debugme outln "B1: $batchremoved"
4898+
"$FAST" && break
48944899
else
48954900
debugme outln "B2: $batchremoved"
48964901
break
@@ -4900,70 +4905,66 @@ check_tls12_pref() {
49004905

49014906
if "$batchremoved_success"; then
49024907
# now we combine the two cipher sets from both while loops
4908+
[[ "${order:0:1}" == " " ]] && order="${order:1}"
49034909
combined_ciphers="${order// /:}"
4904-
$OPENSSL s_client $STARTTLS -tls1_2 $BUGS -cipher "$combined_ciphers" -connect $NODEIP:$PORT $PROXY $SNI </dev/null 2>>$ERRFILE >$TMPFILE
4905-
if sclient_connect_successful $? $TMPFILE ; then
4906-
# first cipher
4907-
cipher=$(awk '/Cipher.*:/ { print $3 }' $TMPFILE)
4908-
order="$cipher"
4909-
tested_cipher="-$cipher"
4910-
else
4911-
fixmeln "something weird happened around line $((LINENO - 6))"
4912-
return 1
4913-
fi
4910+
order="" ; tested_cipher=""
49144911
while true; do
4915-
$OPENSSL s_client $STARTTLS -tls1_2 $BUGS -cipher "$combined_ciphers:$tested_cipher" -connect $NODEIP:$PORT $PROXY $SNI </dev/null 2>>$ERRFILE >$TMPFILE
4912+
$OPENSSL s_client $STARTTLS -tls1_2 $BUGS -cipher "$combined_ciphers$tested_cipher" -connect $NODEIP:$PORT $PROXY $SNI </dev/null 2>>$ERRFILE >$TMPFILE
49164913
if sclient_connect_successful $? $TMPFILE ; then
49174914
cipher=$(awk '/Cipher.*:/ { print $3 }' $TMPFILE)
49184915
order+=" $cipher"
49194916
tested_cipher="$tested_cipher:-$cipher"
4917+
nr_ciphers_found_r2+=1
4918+
"$FAST" && break
49204919
else
49214920
# nothing left, we're done
4922-
out " $order"
49234921
break
49244922
fi
49254923
done
4926-
4927-
else
4928-
# second cipher set didn't succeed: we can just output everything
4929-
out " $order"
4924+
if "$FAST" && [[ $nr_ciphers_found_r2 -ne 1 ]]; then
4925+
fixmeln "something weird happened around line $((LINENO - 14))"
4926+
return 1
4927+
elif ! "$FAST" && [[ $nr_ciphers_found_r2 -ne $nr_ciphers_found_r1 ]]; then
4928+
fixmeln "something weird happened around line $((LINENO - 16))"
4929+
return 1
4930+
fi
49304931
fi
4932+
out "$order"
49314933

49324934
tmpfile_handle $FUNCNAME.txt
49334935
return 0
49344936
}
49354937

49364938

49374939
cipher_pref_check() {
4938-
local p proto protos npn_protos sni
4940+
local p proto proto_hex npn_protos sni
49394941
local tested_cipher cipher order
49404942
local overflow_probe_cipherlist="ALL:-ECDHE-RSA-AES256-GCM-SHA384:-AES128-SHA:-DES-CBC3-SHA"
4943+
local -i i nr_ciphers nr_nonossl_ciphers num_bundles mod_check bundle_size bundle end_of_bundle success
4944+
local hexc ciphers_to_test
4945+
local -a rfc_ciph hexcode ciphers_found ciphers_found2
4946+
local -a -i index
4947+
local using_sockets=true ciphers_found_with_sockets
4948+
4949+
"$SSL_NATIVE" && using_sockets=false
4950+
"$FAST" && using_sockets=false
4951+
[[ $TLS_NR_CIPHERS == 0 ]] && using_sockets=false
49414952

49424953
pr_bold " Cipher order"
49434954

4944-
for p in ssl2 ssl3 tls1 tls1_1 tls1_2; do
4945-
order=""
4946-
if [[ $p == ssl2 ]] && ! "$HAS_SSL2"; then
4947-
out "\n SSLv2: "; local_problem "$OPENSSL doesn't support \"s_client -ssl2\"";
4948-
continue
4949-
fi
4950-
if [[ $p == ssl3 ]] && ! "$HAS_SSL3"; then
4955+
outln " ssl3 00 SSLv3\n tls1 01 TLSv1\n tls1_1 02 TLSv1.1\n tls1_2 03 TLSv1.2"| while read p proto_hex proto; do
4956+
order=""; ciphers_found_with_sockets=false
4957+
if [[ $p == ssl3 ]] && ! "$HAS_SSL3" && ! "$using_sockets"; then
49514958
out "\n SSLv3: "; local_problem "$OPENSSL doesn't support \"s_client -ssl3\"";
49524959
continue
49534960
fi
4954-
# with the supplied binaries SNI works also for SSLv2 (+ SSLv3)
4955-
[[ "$p" =~ ssl ]] && sni="" || sni=$SNI
4956-
$OPENSSL s_client $STARTTLS -"$p" $BUGS -connect $NODEIP:$PORT $PROXY $sni </dev/null 2>$ERRFILE >$TMPFILE
4957-
if sclient_connect_successful $? $TMPFILE; then
4958-
tested_cipher=""
4959-
proto=$(awk '/Protocol/ { print $3 }' $TMPFILE)
4960-
cipher=$(awk '/Cipher *:/ { print $3 }' $TMPFILE)
4961-
[[ -z "$proto" ]] && continue # for early openssl versions sometimes needed
4962-
outln
4963-
printf " %-10s" "$proto: "
4964-
tested_cipher="-"$cipher
4965-
order="$cipher"
4966-
if [[ $p == tls1_2 ]]; then
4961+
has_server_protocol "$p" || continue
4962+
4963+
if [[ $p != ssl3 ]] || "$HAS_SSL3"; then
4964+
# with the supplied binaries SNI works also for SSLv3
4965+
[[ "$p" =~ ssl ]] && sni="" || sni=$SNI
4966+
4967+
if [[ $p == tls1_2 ]] && ! "$SERVER_SIZE_LIMIT_BUG"; then
49674968
# for some servers the ClientHello is limited to 128 ciphers or the ClientHello itself has a length restriction.
49684969
# So far, this was only observed in TLS 1.2, affected are e.g. old Cisco LBs or ASAs, see issue #189
49694970
# To check whether a workaround is needed we send a laaarge list of ciphers/big client hello. If connect fails,
@@ -4975,23 +4976,144 @@ cipher_pref_check() {
49754976
fi
49764977
fi
49774978
if [[ $p == tls1_2 ]] && "$SERVER_SIZE_LIMIT_BUG"; then
4978-
order=$(check_tls12_pref "$cipher")
4979-
out "$order"
4979+
order="$(check_tls12_pref)"
49804980
else
4981-
out " $cipher" # this is the first cipher for protocol
4982-
if ! "$FAST"; then
4983-
while true; do
4984-
$OPENSSL s_client $STARTTLS -"$p" $BUGS -cipher "ALL:$tested_cipher" -connect $NODEIP:$PORT $PROXY $sni </dev/null 2>>$ERRFILE >$TMPFILE
4985-
sclient_connect_successful $? $TMPFILE || break
4986-
cipher=$(awk '/Cipher *:/ { print $3 }' $TMPFILE)
4987-
out " $cipher"
4988-
order+=" $cipher"
4989-
tested_cipher="$tested_cipher:-$cipher"
4981+
tested_cipher=""
4982+
while true; do
4983+
$OPENSSL s_client $STARTTLS -"$p" $BUGS -cipher "ALL:COMPLEMENTOFALL$tested_cipher" -connect $NODEIP:$PORT $PROXY $sni </dev/null 2>>$ERRFILE >$TMPFILE
4984+
sclient_connect_successful $? $TMPFILE || break
4985+
cipher=$(awk '/Cipher *:/ { print $3 }' $TMPFILE)
4986+
[[ -z "$cipher" ]] && break
4987+
order+=" $cipher"
4988+
tested_cipher+=":-"$cipher
4989+
"$FAST" && break
4990+
done
4991+
fi
4992+
fi
4993+
4994+
nr_nonossl_ciphers=0
4995+
if "$using_sockets"; then
4996+
for (( i=0; i < TLS_NR_CIPHERS; i++ )); do
4997+
ciphers_found[i]=false
4998+
hexc="${TLS_CIPHER_HEXCODE[i]}"
4999+
if [[ ${#hexc} -eq 9 ]]; then
5000+
if [[ " $order " =~ " ${TLS_CIPHER_OSSL_NAME[i]} " ]]; then
5001+
ciphers_found[i]=true
5002+
else
5003+
ciphers_found2[nr_nonossl_ciphers]=false
5004+
hexcode[nr_nonossl_ciphers]="${hexc:2:2},${hexc:7:2}"
5005+
rfc_ciph[nr_nonossl_ciphers]="${TLS_CIPHER_RFC_NAME[i]}"
5006+
index[nr_nonossl_ciphers]=$i
5007+
# Only test ciphers that are relevant to the protocol.
5008+
if [[ "$p" == "tls1_3" ]]; then
5009+
[[ "${hexc:2:2}" == "13" ]] && nr_nonossl_ciphers+=1
5010+
elif [[ "$p" == "tls1_2" ]]; then
5011+
[[ "${hexc:2:2}" != "13" ]] && nr_nonossl_ciphers+=1
5012+
elif [[ ! "${TLS_CIPHER_RFC_NAME[i]}" =~ "SHA256" ]] && \
5013+
[[ ! "${TLS_CIPHER_RFC_NAME[i]}" =~ "SHA384" ]] && \
5014+
[[ "${TLS_CIPHER_RFC_NAME[i]}" != *"_CCM" ]] && \
5015+
[[ "${TLS_CIPHER_RFC_NAME[i]}" != *"_CCM_8" ]]; then
5016+
nr_nonossl_ciphers+=1
5017+
fi
5018+
fi
5019+
fi
5020+
done
5021+
fi
5022+
5023+
if [[ $nr_nonossl_ciphers -eq 0 ]]; then
5024+
num_bundles=0
5025+
elif [[ $p != tls1_2 ]] || ! "$SERVER_SIZE_LIMIT_BUG"; then
5026+
num_bundles=1
5027+
bundle_size=$nr_nonossl_ciphers
5028+
else
5029+
num_bundles=$nr_nonossl_ciphers/128
5030+
mod_check=$nr_nonossl_ciphers%128
5031+
[[ $mod_check -ne 0 ]] && num_bundles=$num_bundles+1
5032+
5033+
bundle_size=$nr_nonossl_ciphers/$num_bundles
5034+
mod_check=$nr_nonossl_ciphers%$num_bundles
5035+
[[ $mod_check -ne 0 ]] && bundle_size+=1
5036+
fi
5037+
5038+
for (( bundle=0; bundle < num_bundles; bundle++ )); do
5039+
end_of_bundle=$bundle*$bundle_size+$bundle_size
5040+
[[ $end_of_bundle -gt $nr_nonossl_ciphers ]] && end_of_bundle=$nr_nonossl_ciphers
5041+
while true; do
5042+
ciphers_to_test=""
5043+
for (( i=bundle*bundle_size; i < end_of_bundle; i++ )); do
5044+
! "${ciphers_found2[i]}" && ciphers_to_test+=", ${hexcode[i]}"
5045+
done
5046+
[[ -z "$ciphers_to_test" ]] && break
5047+
tls_sockets "$proto_hex" "${ciphers_to_test:2}, 00,ff" "ephemeralkey"
5048+
[[ $? -ne 0 ]] && break
5049+
cipher=$(awk '/Cipher *:/ { print $3 }' "$TEMPDIR/$NODEIP.parse_tls_serverhello.txt")
5050+
for (( i=bundle*bundle_size; i < end_of_bundle; i++ )); do
5051+
[[ "$cipher" == "${rfc_ciph[i]}" ]] && ciphers_found2[i]=true && break
5052+
done
5053+
i=${index[i]}
5054+
ciphers_found[i]=true
5055+
ciphers_found_with_sockets=true
5056+
if [[ $p != tls1_2 ]] || ! "$SERVER_SIZE_LIMIT_BUG"; then
5057+
# Throw out the results found so far and start over using just sockets
5058+
bundle=$num_bundles
5059+
for (( i=0; i < TLS_NR_CIPHERS; i++ )); do
5060+
ciphers_found[i]=true
49905061
done
5062+
break
49915063
fi
4992-
fi
5064+
done
5065+
done
5066+
5067+
# If additional ciphers were found using sockets and there is no
5068+
# SERVER_SIZE_LIMIT_BUG, then just use sockets to find the cipher order.
5069+
# If there is a SERVER_SIZE_LIMIT_BUG, then use sockets to find the cipher
5070+
# order, but starting with the list of ciphers supported by the server.
5071+
if "$ciphers_found_with_sockets"; then
5072+
order=""
5073+
nr_ciphers=0
5074+
for (( i=0; i < TLS_NR_CIPHERS; i++ )); do
5075+
hexc="${TLS_CIPHER_HEXCODE[i]}"
5076+
if "${ciphers_found[i]}" && [[ ${#hexc} -eq 9 ]]; then
5077+
ciphers_found2[nr_ciphers]=false
5078+
hexcode[nr_ciphers]="${hexc:2:2},${hexc:7:2}"
5079+
rfc_ciph[nr_ciphers]="${TLS_CIPHER_RFC_NAME[i]}"
5080+
if [[ "$p" == "tls1_3" ]]; then
5081+
[[ "${hexc:2:2}" == "13" ]] && nr_ciphers+=1
5082+
elif [[ "$p" == "tls1_2" ]]; then
5083+
[[ "${hexc:2:2}" != "13" ]] && nr_ciphers+=1
5084+
elif [[ ! "${TLS_CIPHER_RFC_NAME[i]}" =~ "SHA256" ]] && \
5085+
[[ ! "${TLS_CIPHER_RFC_NAME[i]}" =~ "SHA384" ]] && \
5086+
[[ "${TLS_CIPHER_RFC_NAME[i]}" != *"_CCM" ]] && \
5087+
[[ "${TLS_CIPHER_RFC_NAME[i]}" != *"_CCM_8" ]]; then
5088+
nr_ciphers+=1
5089+
fi
5090+
fi
5091+
done
5092+
while true; do
5093+
ciphers_to_test=""
5094+
for (( i=0; i < nr_ciphers; i++ )); do
5095+
! "${ciphers_found2[i]}" && ciphers_to_test+=", ${hexcode[i]}"
5096+
done
5097+
[[ -z "$ciphers_to_test" ]] && break
5098+
tls_sockets "$proto_hex" "${ciphers_to_test:2}, 00,ff" "ephemeralkey"
5099+
[[ $? -ne 0 ]] && break
5100+
cipher=$(awk '/Cipher *:/ { print $3 }' "$TEMPDIR/$NODEIP.parse_tls_serverhello.txt")
5101+
for (( i=0; i < nr_ciphers; i++ )); do
5102+
[[ "$cipher" == "${rfc_ciph[i]}" ]] && ciphers_found2[i]=true && break
5103+
done
5104+
cipher="$(rfc2openssl "$cipher")"
5105+
# If there is no OpenSSL name for the cipher, then use the RFC name
5106+
[[ -z "$cipher" ]] && cipher=$(awk '/Cipher *:/ { print $3 }' "$TEMPDIR/$NODEIP.parse_tls_serverhello.txt")
5107+
order+=" $cipher"
5108+
done
5109+
fi
5110+
5111+
if [[ -n "$order" ]]; then
5112+
outln
5113+
printf " %-10s" "$proto: "
5114+
out "$order"
5115+
fileout "order_$p" "INFO" "Default cipher order for protocol $p: $order"
49935116
fi
4994-
[[ -z "$order" ]] || fileout "order_$p" "INFO" "Default cipher order for protocol $p: $order"
49955117
done
49965118
outln
49975119

0 commit comments

Comments
 (0)