From 8c7dcbbc3bb6dbc65792eb4ab6c18f68a509ada6 Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Tue, 21 Apr 2020 19:22:16 +0200 Subject: [PATCH 01/19] Fix misleading phrasing in run of standard ciphers see #1571. Bit size doesn't matter. It only matters to the user which ciphers they are. Additionally phrased the output better (FS + strong enc) and do less indentation. Renamed average_ciphers -> obsoleted_ciphers to refect what's on the output. --- testssl.sh | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/testssl.sh b/testssl.sh index d5d7ee7..0fd691b 100755 --- a/testssl.sh +++ b/testssl.sh @@ -5574,7 +5574,7 @@ sub_cipherlists() { local cve="${9}" local cwe="${10}" - pr_bold "$3 " + pr_bold "$3 " [[ "$OPTIMAL_PROTO" == -ssl2 ]] && proto="$OPTIMAL_PROTO" jsonID="${jsonID}_$5" @@ -5743,7 +5743,7 @@ run_cipherlists() { local ossl_exp_ciphers exp_ciphers sslv2_exp_ciphers local ossl_low_ciphers low_ciphers sslv2_low_ciphers local ossl_tdes_ciphers tdes_ciphers sslv2_tdes_cipher - local ossl_average_ciphers average_ciphers + local ossl_obsoleted_ciphers obsoleted_ciphers local strong_ciphers local cwe="CWE-327" local cwe2="CWE-310" @@ -5780,13 +5780,14 @@ run_cipherlists() { tdes_ciphers="00,07, 00,0A, 00,0D, 00,10, 00,13, 00,16, 00,1F, 00,21, 00,8B, 00,8F, 00,93, C0,03, C0,08, C0,0D, C0,12, C0,1A, C0,1B, C0,1C, C0,34, FE,FF, FF,E0, 00,FF" sslv2_tdes_ciphers="07,01,c0" - ossl_average_ciphers='HIGH:MEDIUM:AES:CAMELLIA:ARIA:!IDEA:!CHACHA20:!3DES:!RC2:!RC4:!AESCCM8:!AESCCM:!AESGCM:!ARIAGCM:!aNULL:!MD5' + # # Now all AES, CAMELLIA, ARIA and SEED CBC ciphers plus GOST + ossl_obsoleted_ciphers='HIGH:MEDIUM:AES:CAMELLIA:ARIA:!IDEA:!CHACHA20:!3DES:!RC2:!RC4:!AESCCM8:!AESCCM:!AESGCM:!ARIAGCM:!aNULL:!MD5' # egrep -w "256|128" etc/cipher-mapping.txt | egrep -v "Au=None|AEAD|RC2|RC4|IDEA|MD5" - average_ciphers="00,2F, 00,30, 00,31, 00,32, 00,33, 00,35, 00,36, 00,37, 00,38, 00,39, 00,3C, 00,3D, 00,3E, 00,3F, 00,40, 00,41, 00,42, 00,43, 00,44, 00,45, 00,67, 00,68, 00,69, 00,6A, 00,6B, 00,84, 00,85, 00,86, 00,87, 00,88, 00,8C, 00,8D, 00,90, 00,91, 00,94, 00,95, 00,96, 00,97, 00,98, 00,99, 00,9A, 00,AE, 00,AF, 00,B2, 00,B3, 00,B6, 00,B7, 00,BA, 00,BB, 00,BC, 00,BD, 00,BE, 00,C0, 00,C1, 00,C2, 00,C3, 00,C4, C0,04, C0,05, C0,09, C0,0A, C0,0E, C0,0F, C0,13, C0,14, C0,1D, C0,1E, C0,1F, C0,20, C0,21, C0,22, C0,23, C0,24, C0,25, C0,26, C0,27, C0,28, C0,29, C0,2A, C0,35, C0,36, C0,37, C0,38, C0,3C, C0,3D, C0,3E, C0,3F, C0,40, C0,41, C0,42, C0,43, C0,44, C0,45, C0,48, C0,49, C0,4A, C0,4B, C0,4C, C0,4D, C0,4E, C0,4F, C0,64, C0,65, C0,66, C0,67, C0,68, C0,69, C0,70, C0,71, C0,72, C0,73, C0,74, C0,75, C0,76, C0,77, C0,78, C0,79, C0,94, C0,95, C0,96, C0,97, C0,98, C0,99, C0,9A, C0,9B" + obsoleted_ciphers="00,2F, 00,30, 00,31, 00,32, 00,33, 00,35, 00,36, 00,37, 00,38, 00,39, 00,3C, 00,3D, 00,3E, 00,3F, 00,40, 00,41, 00,42, 00,43, 00,44, 00,45, 00,67, 00,68, 00,69, 00,6A, 00,6B, 00,84, 00,85, 00,86, 00,87, 00,88, 00,8C, 00,8D, 00,90, 00,91, 00,94, 00,95, 00,96, 00,97, 00,98, 00,99, 00,9A, 00,AE, 00,AF, 00,B2, 00,B3, 00,B6, 00,B7, 00,BA, 00,BB, 00,BC, 00,BD, 00,BE, 00,C0, 00,C1, 00,C2, 00,C3, 00,C4, C0,04, C0,05, C0,09, C0,0A, C0,0E, C0,0F, C0,13, C0,14, C0,1D, C0,1E, C0,1F, C0,20, C0,21, C0,22, C0,23, C0,24, C0,25, C0,26, C0,27, C0,28, C0,29, C0,2A, C0,35, C0,36, C0,37, C0,38, C0,3C, C0,3D, C0,3E, C0,3F, C0,40, C0,41, C0,42, C0,43, C0,44, C0,45, C0,48, C0,49, C0,4A, C0,4B, C0,4C, C0,4D, C0,4E, C0,4F, C0,64, C0,65, C0,66, C0,67, C0,68, C0,69, C0,70, C0,71, C0,72, C0,73, C0,74, C0,75, C0,76, C0,77, C0,78, C0,79, C0,94, C0,95, C0,96, C0,97, C0,98, C0,99, C0,9A, C0,9B" # Workaround: If we use sockets and in order not to hit 131+1 ciphers we omit the GOST ciphers if SERVER_SIZE_LIMIT_BUG is true. # This won't be supported by Cisco ACE anyway. - "$SERVER_SIZE_LIMIT_BUG" || average_ciphers="${average_ciphers}, 00,80, 00,81, FF,01, FF,02, FF,03, FF,85" - average_ciphers="${average_ciphers}, 00,FF" + "$SERVER_SIZE_LIMIT_BUG" || obsoleted_ciphers="${obsoleted_ciphers}, 00,80, 00,81, FF,01, FF,02, FF,03, FF,85" + obsoleted_ciphers="${obsoleted_ciphers}, 00,FF" ossl_good_ciphers='AESGCM:CHACHA20:CamelliaGCM:AESCCM:ARIAGCM:!kEECDH:!kEDH:!kDHE:!kDHEPSK:!kECDHEPSK:!aNULL' # grep AEAD etc/cipher-mapping.txt | egrep -v 'Au=None|TLS_ECDHE|TLS_DHE|TLS_PSK_DHE|TLSv1.3' @@ -5807,21 +5808,21 @@ run_cipherlists() { # argv[9]: CVE # argv[10]: CWE - sub_cipherlists "$ossl_null_ciphers" "" " NULL ciphers (no encryption) " 1 "NULL" "$null_ciphers" "$sslv2_null_ciphers" "$using_sockets" "$cve" "$cwe" + sub_cipherlists "$ossl_null_ciphers" "" " NULL ciphers (no encryption) " 1 "NULL" "$null_ciphers" "$sslv2_null_ciphers" "$using_sockets" "$cve" "$cwe" ret=$? - sub_cipherlists "$ossl_anon_ciphers" "" " Anonymous NULL Ciphers (no authentication) " 1 "aNULL" "$anon_ciphers" "$sslv2_anon_ciphers" "$using_sockets" "$cve" "$cwe" + sub_cipherlists "$ossl_anon_ciphers" "" " Anonymous NULL Ciphers (no authentication) " 1 "aNULL" "$anon_ciphers" "$sslv2_anon_ciphers" "$using_sockets" "$cve" "$cwe" ret=$((ret + $?)) - sub_cipherlists "$ossl_exp_ciphers" "" " Export ciphers (w/o ADH+NULL) " 1 "EXPORT" "$exp_ciphers" "$sslv2_exp_ciphers" "$using_sockets" "$cve" "$cwe" + sub_cipherlists "$ossl_exp_ciphers" "" " Export ciphers (w/o ADH+NULL) " 1 "EXPORT" "$exp_ciphers" "$sslv2_exp_ciphers" "$using_sockets" "$cve" "$cwe" ret=$((ret + $?)) - sub_cipherlists "$ossl_low_ciphers" "" " LOW: 64 Bit + DES, RC[2,4], MD5 (w/o export) " 2 "LOW" "$low_ciphers" "$sslv2_low_ciphers" "$using_sockets" "$cve" "$cwe" + sub_cipherlists "$ossl_low_ciphers" "" " LOW: 64 Bit + DES, RC[2,4], MD5 (w/o export) " 2 "LOW" "$low_ciphers" "$sslv2_low_ciphers" "$using_sockets" "$cve" "$cwe" ret=$((ret + $?)) - sub_cipherlists "$ossl_tdes_ciphers" "" " Triple DES Ciphers / IDEA " 3 "3DES_IDEA" "$tdes_ciphers" "$sslv2_tdes_ciphers" "$using_sockets" "$cve" "$cwe2" + sub_cipherlists "$ossl_tdes_ciphers" "" " Triple DES Ciphers / IDEA " 3 "3DES_IDEA" "$tdes_ciphers" "$sslv2_tdes_ciphers" "$using_sockets" "$cve" "$cwe2" ret=$((ret + $?)) - sub_cipherlists "$ossl_average_ciphers" "" " Obsolete: SEED + 128+256 Bit CBC cipher " 4 "AVERAGE" "$average_ciphers" "" "$using_sockets" "$cve" "$cwe2" + sub_cipherlists "$ossl_obsoleted_ciphers" "" " Obsoleted CBC ciphers (AES, ARIA etc.) " 4 "AVERAGE" "$obsoleted_ciphers" "" "$using_sockets" "$cve" "$cwe2" ret=$((ret + $?)) - sub_cipherlists "$ossl_good_ciphers" "" " non-FS Strong encryption (AEAD ciphers) " 6 "GOOD" "$good_ciphers" "" "$using_sockets" "" "" + sub_cipherlists "$ossl_good_ciphers" "" " Strong encryption (AEAD ciphers) with no FS " 6 "GOOD" "$good_ciphers" "" "$using_sockets" "" "" ret=$((ret + $?)) - sub_cipherlists "$ossl_strong_ciphers" 'ALL' " Forward Secure Strong encryption (AEAD ciphers)" 7 "STRONG" "$strong_ciphers" "" "$using_sockets" "" "" + sub_cipherlists "$ossl_strong_ciphers" 'ALL' " Forward Secrecy strong encryption (AEAD ciphers)" 7 "STRONG" "$strong_ciphers" "" "$using_sockets" "" "" ret=$((ret + $?)) outln From f5aa20ceb1fd90839933fe9e2e34d2e9b14bd896 Mon Sep 17 00:00:00 2001 From: David Cooper Date: Wed, 22 Apr 2020 12:31:45 -0400 Subject: [PATCH 02/19] Extended run_server_preference() This commit extends run_server_preference() to list every cipher supported by each protocol even in cases in which the server does not enforce a preference order. For protocols where the server enforces a cipher order the list of supported ciphers is ordered by server preference (as now). For protocols where the server does not enforce a cipher order, the ciphers are listed by encryption strength (as run_cipher_per_proto() does). In order to implement this, ciphers_by_strength() was extended to offer a non-wide mode. --- testssl.sh | 214 ++++++++++++++++++----------------------------------- 1 file changed, 72 insertions(+), 142 deletions(-) diff --git a/testssl.sh b/testssl.sh index 0fd691b..771f8a3 100755 --- a/testssl.sh +++ b/testssl.sh @@ -3970,7 +3970,7 @@ run_allciphers() { # are good or bad) and list them in order to encryption strength. ciphers_by_strength() { local proto="$1" proto_hex="$2" proto_text="$3" - local using_sockets="$4" + local using_sockets="$4" wide="$5" local ossl_ciphers_proto local -i nr_ciphers nr_ossl_ciphers nr_nonossl_ciphers success local n sslvers auth mac hexc sslv2_ciphers="" cipher @@ -3983,13 +3983,12 @@ ciphers_by_strength() { local id local has_dh_bits="$HAS_DH_BITS" - pr_underline "$(printf -- "%b" "$proto_text")" # for local problem if it happens out " " if ! "$using_sockets" && ! locally_supported "$proto"; then return 0 fi - outln + "$wide" && outln [[ $(has_server_protocol "${proto:1}") -eq 1 ]] && return 0 @@ -4006,7 +4005,7 @@ ciphers_by_strength() { ciphers_found[nr_ciphers]=false sigalg[nr_ciphers]="" ossl_supported[nr_ciphers]=${TLS_CIPHER_OSSL_SUPPORTED[i]} - if "$using_sockets" && ! "$has_dh_bits" && ( [[ ${kx[nr_ciphers]} == "Kx=ECDH" ]] || [[ ${kx[nr_ciphers]} == "Kx=DH" ]] || [[ ${kx[nr_ciphers]} == "Kx=EDH" ]] ); then + if "$using_sockets" && "$wide" && ! "$has_dh_bits" && ( [[ ${kx[nr_ciphers]} == "Kx=ECDH" ]] || [[ ${kx[nr_ciphers]} == "Kx=DH" ]] || [[ ${kx[nr_ciphers]} == "Kx=EDH" ]] ); then ossl_supported[nr_ciphers]=false fi if [[ ${#hexc} -eq 9 ]]; then @@ -4022,16 +4021,16 @@ ciphers_by_strength() { normalized_hexcode[nr_ciphers]="x${hexc:2:2}${hexc:7:2}${hexc:12:2}" fi if ( "$using_sockets" || "${TLS_CIPHER_OSSL_SUPPORTED[i]}" ); then - if [[ ${#hexc} -eq 9 ]] && [[ "$proto_text" != SSLv2 ]]; then - if [[ "$proto_text" == TLS\ 1.3 ]]; then + if [[ ${#hexc} -eq 9 ]] && [[ "$proto" != -ssl2 ]]; then + if [[ "$proto" == -tls1_3 ]]; then [[ "${hexc:2:2}" == 13 ]] && nr_ciphers+=1 - elif [[ "$proto_text" == TLS\ 1.2 ]]; then + elif [[ "$proto" == -tls1_2 ]]; then [[ "${hexc:2:2}" != 13 ]] && nr_ciphers+=1 elif [[ ! "${TLS_CIPHER_RFC_NAME[i]}" =~ SHA256 ]] && [[ ! "${TLS_CIPHER_RFC_NAME[i]}" =~ SHA384 ]] && \ [[ "${TLS_CIPHER_RFC_NAME[i]}" != *_CCM ]] && [[ "${TLS_CIPHER_RFC_NAME[i]}" != *_CCM_8 ]]; then nr_ciphers+=1 fi - elif [[ ${#hexc} -eq 14 ]] && [[ "$proto_text" == SSLv2 ]]; then + elif [[ ${#hexc} -eq 14 ]] && [[ "$proto" == -ssl2 ]]; then sslv2_ciphers+=", ${hexcode[nr_ciphers]}" nr_ciphers+=1 fi @@ -4049,9 +4048,9 @@ ciphers_by_strength() { ossl_ciphers_proto="-tls1" fi while read hexc n ciph[nr_ciphers] sslvers kx[nr_ciphers] auth enc[nr_ciphers] mac export2[nr_ciphers]; do - if [[ "$proto_text" == TLS\ 1.3 ]]; then + if [[ "$proto" == -tls1_3 ]]; then [[ "${ciph[nr_ciphers]}" == TLS13* ]] || [[ "${ciph[nr_ciphers]}" == TLS_* ]] || continue - elif [[ "$proto_text" == "TLS 1.2" ]]; then + elif [[ "$proto" == -tls1_2 ]]; then if [[ "${ciph[nr_ciphers]}" == TLS13* ]] || [[ "${ciph[nr_ciphers]}" == TLS_* ]]; then continue fi @@ -4073,11 +4072,11 @@ ciphers_by_strength() { sslv2_sockets "${sslv2_ciphers:2}" "true" if [[ $? -eq 3 ]] && [[ "$V2_HELLO_CIPHERSPEC_LENGTH" -ne 0 ]]; then supported_sslv2_ciphers="$(grep "Supported cipher: " "$TEMPDIR/$NODEIP.parse_sslv2_serverhello.txt")" - "$SHOW_SIGALGO" && s="$(read_sigalg_from_file "$HOSTCERT")" + "$wide" && "$SHOW_SIGALGO" && s="$(read_sigalg_from_file "$HOSTCERT")" for (( i=0 ; i>$ERRFILE >$TMPFILE - if sclient_connect_successful $? $TMPFILE; then - offered_proto[i]=$(get_protocol $TMPFILE) - offered_cipher[i]=$(get_cipher $TMPFILE) - [[ ${offered_cipher[i]} == "0000" ]] && offered_cipher[i]="" # Hack! - if [[ "$DISPLAY_CIPHERNAMES" =~ rfc ]] && [[ -n "${offered_cipher[i]}" ]]; then - offered_cipher[i]="$(openssl2rfc "${offered_cipher[i]}")" - [[ -z "${offered_cipher[i]}" ]] && offered_cipher[i]=$(get_cipher $TMPFILE) - fi - [[ $DEBUG -ge 2 ]] && tmln_out "Default cipher for ${offered_proto[i]}: ${offered_cipher[i]}" - else - offered_proto[i]="" - offered_cipher[i]="" - fi + out " (server order)" + cipher_pref_check "$proto_ossl" "$proto_hex" "$proto_txt" "$using_sockets" fi - [[ -n "${offered_cipher[i]}" ]] && add_tls_offered "$proto_ossl" yes - i=$((i + 1)) - done + else + if [[ "$proto_ossl" == ssl2 ]] || ( [[ "$proto_ossl" != tls1_3 ]] && ! "$has_cipher_order" ]] ) || \ + ( [[ "$proto_ossl" == tls1_3 ]] && ! "$has_tls13_cipher_order" ]] ); then + ciphers_by_strength "-$proto_ossl" "$proto_hex" "$proto_txt" "$using_sockets" "$WIDE" + else + cipher_pref_check "$proto_ossl" "$proto_hex" "$proto_txt" "$using_sockets" + fi + fi + done <<< "$(tm_out " ssl2 22 SSLv2\n ssl3 00 SSLv3\n tls1 01 TLSv1\n tls1_1 02 TLSv1.1\n tls1_2 03 TLSv1.2\n tls1_3 04 TLSv1.3\n")" + outln + outln - for i in 1 2 3 4 5 6; do - if [[ -n "${offered_cipher[i]}" ]]; then # cipher not empty - if [[ -z "$prev_cipher" ]] || [[ "$prev_cipher" != "${offered_cipher[i]}" ]]; then - [[ -n "$prev_cipher" ]] && outln - str_len=${#offered_cipher[i]} - out " " - if [[ "$COLOR" -le 2 ]]; then - out "${offered_cipher[i]}" - else - pr_cipher_quality "${offered_cipher[i]}" - fi - out ":" - if [[ "$DISPLAY_CIPHERNAMES" =~ openssl ]]; then - for (( 1; str_len < 30; str_len++ )); do - out " " - done - else - for (( 1; str_len < 51; str_len++ )); do - out " " - done - fi - else - out ", " # same cipher --> only print out protocol behind it - fi - out "${offered_proto[i]}" - prev_cipher="${offered_cipher[i]}" - fi - fileout "cipher_order_${offered_proto[i]}" "INFO" "${offered_cipher[i]} at ${offered_proto[i]} $limitedsense" - done - outln "\n No further cipher order check has been done as order is determined by the client" - outln - fi return $ret } From a9ab2bcd91fcd8164cd497f153c1a2d8c0c885bc Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Thu, 23 Apr 2020 11:20:46 +0200 Subject: [PATCH 03/19] Update documentation (ADDITIONAL_CA_FILES -> ADDTL_CA_FILES) which happened in d44a643fab6be6755a917f85ed491c38990d15ae in testssl.sh . This fixes it in the related files. See also #1581 --- Coding_Convention.md | 2 +- doc/testssl.1 | 4 ++-- doc/testssl.1.html | 4 ++-- doc/testssl.1.md | 4 ++-- etc/README.md | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Coding_Convention.md b/Coding_Convention.md index 933b30c..03122d1 100644 --- a/Coding_Convention.md +++ b/Coding_Convention.md @@ -65,7 +65,7 @@ Bash is actually quite powerful -- not only with respect to sockets. It's not as ### Misc -* If you're implementing a new feature a cmd line switch, there has to be also a global ENV variable which can be used without the switch (see e.g. `SNEAKY`, `ASSUME_HTTP` or `ADDITIONAL_CA_FILES`) +* If you're implementing a new feature a cmd line switch, there has to be also a global ENV variable which can be used without the switch (see e.g. `SNEAKY`, `ASSUME_HTTP` or `ADDTL_CA_FILES`) * Test before doing a PR! Best if you check with two bad and two good examples which should then work as expected. Maybe compare results e.g. with SSLlabs. * Unit tests are done automatically done with Perl using Travis. The trigger is `~/.travis.yml`. The general documentation for [Test::More](https://perldoc.perl.org/Test/More.html) is a good start. You are encouraged to write own checks. You can use e.g. `t/20_baseline_ipv4_http.t` as an example. * If it's an OpenSSL feature you want to use and it could be not available for older OpenSSL versions testssl.sh needs to find out whether OpenSSL has that feature. Best do this with OpenSSL itself and not by checking the version as some vendors do backports. See the examples for `HAS_SSL2` or proxy option check of OpenSSL in `check_proxy()`. diff --git a/doc/testssl.1 b/doc/testssl.1 index 9c0f684..a8e8626 100644 --- a/doc/testssl.1 +++ b/doc/testssl.1 @@ -176,7 +176,7 @@ Please note that \fBfname\fR has to be in Unix format\. DOS carriage returns won \fB\-\-phone\-out\fR Checking for revoked certificates via CRL and OCSP is not done per default\. This switch instructs testssl\.sh to query external \-\- in a sense of the current run \-\- URIs\. By using this switch you acknowledge that the check might have privacy issues, a download of several megabytes (CRL file) may happen and there may be network connectivity problems while contacting the endpoint which testssl\.sh doesn\'t handle\. PHONE_OUT is the environment variable for this which needs to be set to true if you want this\. . .P -\fB\-\-add\-ca \fR enables you to add your own CA(s) for trust chain checks\. \fBcafile\fR can be a single path or multiple paths as a comma separated list of root CA files\. Internally they will be added during runtime to all CA stores\. This is (only) useful for internal hosts whose certificates is issued by internal CAs\. Alternatively ADDITIONAL_CA_FILES is the environment variable for this\. +\fB\-\-add\-ca \fR enables you to add your own CA(s) for trust chain checks\. \fBcafile\fR can be a single path or multiple paths as a comma separated list of root CA files\. Internally they will be added during runtime to all CA stores\. This is (only) useful for internal hosts whose certificates is issued by internal CAs\. Alternatively ADDTL_CA_FILES is the environment variable for this\. . .SS "SINGLE CHECK OPTIONS" Any single check switch supplied as an argument prevents testssl\.sh from doing a default run\. It just takes this and if supplied other options and runs them \- in the order they would also appear in the default run\. @@ -282,7 +282,7 @@ Certificate Transparency info (if provided by server)\. .IP "" 0 . .P -For the trust chain check 5 certificate stores are provided\. If the test against one of the trust stores failed, the one is being identified and the reason for the failure is displayed \- in addition the ones which succeeded are displayed too\. You can configure your own CA via ADDITIONAL_CA_FILES, see section \fBFILES\fR below\. If the server provides no matching record in Subject Alternative Name (SAN) but in Common Name (CN), it will be indicated as this is deprecated\. Also for multiple server certificates are being checked for as well as for the certificate reply to a non\-SNI (Server Name Indication) client hello to the IP address\. Regarding the TLS clock skew: it displays the time difference to the client\. Only a few TLS stacks nowadays still support this and return the local clock \fBgmt_unix_time\fR, e\.g\. IIS, openssl < 1\.0\.1f\. In addition to the HTTP date you could e\.g\. derive that there are different hosts where your TLS and your HTTP request ended \-\- if the time deltas differ significantly\. +For the trust chain check 5 certificate stores are provided\. If the test against one of the trust stores failed, the one is being identified and the reason for the failure is displayed \- in addition the ones which succeeded are displayed too\. You can configure your own CA via ADDTL_CA_FILES, see section \fBFILES\fR below\. If the server provides no matching record in Subject Alternative Name (SAN) but in Common Name (CN), it will be indicated as this is deprecated\. Also for multiple server certificates are being checked for as well as for the certificate reply to a non\-SNI (Server Name Indication) client hello to the IP address\. Regarding the TLS clock skew: it displays the time difference to the client\. Only a few TLS stacks nowadays still support this and return the local clock \fBgmt_unix_time\fR, e\.g\. IIS, openssl < 1\.0\.1f\. In addition to the HTTP date you could e\.g\. derive that there are different hosts where your TLS and your HTTP request ended \-\- if the time deltas differ significantly\. . .P \fB\-x , \-\-single\-cipher \fR tests matched \fBpattern\fR of ciphers against a server\. Patterns are similar to \fB\-V pattern , \-\-local pattern\fR, see above about matching\. diff --git a/doc/testssl.1.html b/doc/testssl.1.html index e7f3c34..5a6c392 100644 --- a/doc/testssl.1.html +++ b/doc/testssl.1.html @@ -221,7 +221,7 @@ in /etc/hosts. The use of the switch is only useful if you either

--phone-out Checking for revoked certificates via CRL and OCSP is not done per default. This switch instructs testssl.sh to query external -- in a sense of the current run -- URIs. By using this switch you acknowledge that the check might have privacy issues, a download of several megabytes (CRL file) may happen and there may be network connectivity problems while contacting the endpoint which testssl.sh doesn't handle. PHONE_OUT is the environment variable for this which needs to be set to true if you want this.

--add-ca <cafile> enables you to add your own CA(s) for trust chain checks. cafile can be a single path or multiple paths as a comma separated list of root CA files. Internally they will be added during runtime to all CA stores. This is (only) useful for internal hosts whose certificates is issued by internal CAs. Alternatively -ADDITIONAL_CA_FILES is the environment variable for this.

+ADDTL_CA_FILES is the environment variable for this.

SINGLE CHECK OPTIONS

@@ -278,7 +278,7 @@ ADDITIONAL_CA_FILES is the environment variable for this.

For the trust chain check 5 certificate stores are provided. If the test against one of the trust stores failed, the one is being identified and the reason for the failure is displayed - in addition the ones which succeeded are displayed too. -You can configure your own CA via ADDITIONAL_CA_FILES, see section FILES below. If the server provides no matching record in Subject Alternative Name (SAN) but in Common Name (CN), it will be indicated as this is deprecated. +You can configure your own CA via ADDTL_CA_FILES, see section FILES below. If the server provides no matching record in Subject Alternative Name (SAN) but in Common Name (CN), it will be indicated as this is deprecated. Also for multiple server certificates are being checked for as well as for the certificate reply to a non-SNI (Server Name Indication) client hello to the IP address. Regarding the TLS clock skew: it displays the time difference to the client. Only a few TLS stacks nowadays still support this and return the local clock gmt_unix_time, e.g. IIS, openssl < 1.0.1f. In addition to the HTTP date you could e.g. derive that there are different hosts where your TLS and your HTTP request ended -- if the time deltas differ significantly.

-x <pattern>, --single-cipher <pattern> tests matched pattern of ciphers against a server. Patterns are similar to -V pattern , --local pattern, see above about matching.

diff --git a/doc/testssl.1.md b/doc/testssl.1.md index f347c20..8f29a92 100644 --- a/doc/testssl.1.md +++ b/doc/testssl.1.md @@ -143,7 +143,7 @@ in `/etc/hosts`. The use of the switch is only useful if you either can't or ar `--phone-out` Checking for revoked certificates via CRL and OCSP is not done per default. This switch instructs testssl.sh to query external -- in a sense of the current run -- URIs. By using this switch you acknowledge that the check might have privacy issues, a download of several megabytes (CRL file) may happen and there may be network connectivity problems while contacting the endpoint which testssl.sh doesn't handle. PHONE_OUT is the environment variable for this which needs to be set to true if you want this. `--add-ca ` enables you to add your own CA(s) for trust chain checks. `cafile` can be a single path or multiple paths as a comma separated list of root CA files. Internally they will be added during runtime to all CA stores. This is (only) useful for internal hosts whose certificates is issued by internal CAs. Alternatively -ADDITIONAL_CA_FILES is the environment variable for this. +ADDTL_CA_FILES is the environment variable for this. ### SINGLE CHECK OPTIONS @@ -191,7 +191,7 @@ Any single check switch supplied as an argument prevents testssl.sh from doing a - Certificate Transparency info (if provided by server). For the trust chain check 5 certificate stores are provided. If the test against one of the trust stores failed, the one is being identified and the reason for the failure is displayed - in addition the ones which succeeded are displayed too. -You can configure your own CA via ADDITIONAL_CA_FILES, see section `FILES` below. If the server provides no matching record in Subject Alternative Name (SAN) but in Common Name (CN), it will be indicated as this is deprecated. +You can configure your own CA via ADDTL_CA_FILES, see section `FILES` below. If the server provides no matching record in Subject Alternative Name (SAN) but in Common Name (CN), it will be indicated as this is deprecated. Also for multiple server certificates are being checked for as well as for the certificate reply to a non-SNI (Server Name Indication) client hello to the IP address. Regarding the TLS clock skew: it displays the time difference to the client. Only a few TLS stacks nowadays still support this and return the local clock `gmt_unix_time`, e.g. IIS, openssl < 1.0.1f. In addition to the HTTP date you could e.g. derive that there are different hosts where your TLS and your HTTP request ended -- if the time deltas differ significantly. `-x , --single-cipher ` tests matched `pattern` of ciphers against a server. Patterns are similar to `-V pattern , --local pattern`, see above about matching. diff --git a/etc/README.md b/etc/README.md index 3437195..fc619cc 100644 --- a/etc/README.md +++ b/etc/README.md @@ -18,7 +18,7 @@ The certificate trust stores were retrieved from Google Chromium uses basically the trust stores above, see https://www.chromium.org/Home/chromium-security/root-ca-policy. -If you want to check trust against e.g. a company internal CA you need to use ``./testssl.sh --add-ca companyCA1.pem,companyCA2.pem `` or ``ADDITIONAL_CA_FILES=companyCA1.pem,companyCA2.pem ./testssl.sh ``. +If you want to check trust against e.g. a company internal CA you need to use ``./testssl.sh --add-ca companyCA1.pem,companyCA2.pem `` or ``ADDTL_CA_FILES=companyCA1.pem,companyCA2.pem ./testssl.sh ``. #### Further files From a86ccb6968f116ffc50c19f06b01a9d3021252f0 Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Thu, 23 Apr 2020 14:11:33 +0200 Subject: [PATCH 04/19] First round of polish of David's PR to extend run_server_preference() See #1580. This commit brings: * If there's no cipher for a protocol it adds a "\n - \n" (also for run_cipher_per_proto() ) * further output improvements * Cipher order --> Cipher listing per protocol * make some conditional statement easier to read (at least for me) New open points: - cipher_pref_check() doesn't save to PROTOS_OFFERED (was there before) (just stumbled over this but how about we also use get_protocol() / parse_tls_serverhello() - do we want run_allciphers() to be started by default? - $WIDE per default for run_cipher_per_proto() ? - probably better not to display text in round square brackets when there's no cipher: Hexcode Cipher Suite Name (OpenSSL) KeyExch. Encryption Bits Cipher Suite Name (IANA/RFC) ----------------------------------------------------------------------------------------------------------------------------- SSLv2 (listed by strength) SSLv3 (server order) TLSv1 (server order) TLSv1.1 (server order) TLSv1.2 (server order) xc02c ECDHE-ECDSA-AES256-GCM-SHA384 ECDH 256 AESGCM 256 TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 [..] - when a server has no preference at all it shows in wide mode: Has server cipher order? no (NOT ok) -- only for TLS 1.3 Negotiated protocol TLSv1.3 Negotiated cipher TLS_AES_256_GCM_SHA384, 253 bit ECDH (X25519) Cipher listing per protocol Hexcode Cipher Suite Name (OpenSSL) KeyExch. Encryption Bits Cipher Suite Name (IANA/RFC) ----------------------------------------------------------------------------------------------------------------------------- SSLv2 - SSLv3 - TLSv1 (no server order, thus listed by strength) xc014 ECDHE-RSA-AES256-SHA ECDH 521 AES 256 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA [..] e.g. dev.testssl.sh --- testssl.sh | 44 ++++++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/testssl.sh b/testssl.sh index 771f8a3..37c4e57 100755 --- a/testssl.sh +++ b/testssl.sh @@ -3984,13 +3984,17 @@ ciphers_by_strength() { local has_dh_bits="$HAS_DH_BITS" # for local problem if it happens - out " " + "$wide" || out " " if ! "$using_sockets" && ! locally_supported "$proto"; then + pr_local_problem "Your $OPENSSL does not support $proto" + "$wide" && outln return 0 fi - "$wide" && outln - [[ $(has_server_protocol "${proto:1}") -eq 1 ]] && return 0 + if [[ $(has_server_protocol "${proto:1}") -eq 1 ]]; then + "$wide" && outln " - " + return 0 + fi # get a list of all the cipher suites to test nr_ciphers=0 @@ -4005,7 +4009,8 @@ ciphers_by_strength() { ciphers_found[nr_ciphers]=false sigalg[nr_ciphers]="" ossl_supported[nr_ciphers]=${TLS_CIPHER_OSSL_SUPPORTED[i]} - if "$using_sockets" && "$wide" && ! "$has_dh_bits" && ( [[ ${kx[nr_ciphers]} == "Kx=ECDH" ]] || [[ ${kx[nr_ciphers]} == "Kx=DH" ]] || [[ ${kx[nr_ciphers]} == "Kx=EDH" ]] ); then + if "$using_sockets" && "$wide" && ! "$has_dh_bits" && \ + ( [[ ${kx[nr_ciphers]} == "Kx=ECDH" ]] || [[ ${kx[nr_ciphers]} == "Kx=DH" ]] || [[ ${kx[nr_ciphers]} == "Kx=EDH" ]] ); then ossl_supported[nr_ciphers]=false fi if [[ ${#hexc} -eq 9 ]]; then @@ -4289,7 +4294,7 @@ run_cipher_per_proto() { outln neat_header while read proto proto_hex proto_text; do - pr_underline "$(printf -- "%b" "$proto_text")" + prln_underline "$(printf -- "%b" "$proto_text")" ciphers_by_strength "$proto" "$proto_hex" "$proto_text" "$using_sockets" "true" done <<< "$(tm_out " -ssl2 22 SSLv2\n -ssl3 00 SSLv3\n -tls1 01 TLS 1\n -tls1_1 02 TLS 1.1\n -tls1_2 03 TLS 1.2\n -tls1_3 04 TLS 1.3")" return 0 @@ -6527,23 +6532,32 @@ run_server_preference() { # TODO: Shouldn't say "Cipher order" if the server does not always impose an order. # TODO: In non-wide mode, need to distinguish between those order by server preference and those ordered by encryption strength. - pr_bold " Cipher order" - "$WIDE" && outln && neat_header + pr_bold " Cipher listing per protocol" + "$WIDE" && outln "\n" && neat_header while read proto_ossl proto_hex proto_txt; do if "$WIDE"; then pr_underline "$(printf -- "%b" "$proto_txt")" - if [[ "$proto_ossl" == ssl2 ]] || ( [[ "$proto_ossl" != tls1_3 ]] && ! "$has_cipher_order" ]] ) || \ - ( [[ "$proto_ossl" == tls1_3 ]] && ! "$has_tls13_cipher_order" ]] ); then + # TODO: If there's no cipher we should consider not displaying the text in the round brackets) + # the following takes care of that but only if we know the protocol is not supported + if [[ $(has_server_protocol "$proto_ossl") -eq 1 ]]; then + "$WIDE" && outln "\n - " + continue + fi + # TODO: Also the fact that a protocol is not supported seems not to be saved by cipher_pref_check() + # (./testssl.sh --wide -p -P -E vs ./testssl.sh --wide -P -E ) + if [[ "$proto_ossl" == ssl2 ]] || \ + ( [[ "$proto_ossl" != tls1_3 ]] && ! "$has_cipher_order" ]] ) || \ + ( [[ "$proto_ossl" == tls1_3 ]] && ! "$has_tls13_cipher_order" ]] ); then if [[ "$proto_ossl" == ssl2 ]]; then - out " (listed by strength)" + outln " (listed by strength)" elif [[ "$proto_ossl" == tls1_3 ]]; then - out " (no server order, thus listed by strength)" + outln " (no server order, thus listed by strength)" else - pr_svrty_high " (no server order, thus listed by strength)" + prln_svrty_high " (no server order, thus listed by strength)" fi ciphers_by_strength "-$proto_ossl" "$proto_hex" "$proto_txt" "$using_sockets" "$WIDE" else - out " (server order)" + outln " (server order)" cipher_pref_check "$proto_ossl" "$proto_hex" "$proto_txt" "$using_sockets" fi else @@ -6559,6 +6573,7 @@ run_server_preference() { outln return $ret + # end of run_server_preference() } # arg1: true if the list that is returned does not need to be ordered by preference. @@ -6663,8 +6678,6 @@ cipher_pref_check() { return 0 fi - [[ $(has_server_protocol "$p") -eq 1 ]] && return 0 - if ( [[ $p != tls1_3 ]] || "$HAS_TLS13" ) && ( [[ $p != ssl3 ]] || "$HAS_SSL3" ); then if [[ $p == tls1_2 ]] && "$SERVER_SIZE_LIMIT_BUG"; then order="$(check_tls12_pref "$WIDE")" @@ -6869,7 +6882,6 @@ cipher_pref_check() { order="$rfc_order" fi - "$WIDE" && outln if [[ -n "$order" ]]; then add_tls_offered "$p" yes if "$WIDE"; then From bb1c649513dbb8952dc3d64bf14a87ddca978c02 Mon Sep 17 00:00:00 2001 From: David Cooper Date: Thu, 23 Apr 2020 14:52:14 -0400 Subject: [PATCH 05/19] Fix run_logjam() in --ssl-native mode This commit fixes a problem with run_logjam() when run in --ssl-native mode. If $OPENSSL does not support any DH export ciphers, then no test for such cipher is performed. However, the results of "test" is still checked, leading to testssl.sh incorrectly reporting that the server supports DH EXPORT ciphers. --- testssl.sh | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/testssl.sh b/testssl.sh index 0fd691b..8c1e395 100755 --- a/testssl.sh +++ b/testssl.sh @@ -16413,15 +16413,13 @@ run_logjam() { tls_sockets "03" "$exportdh_cipher_list_hex, 00,ff" sclient_success=$? [[ $sclient_success -eq 2 ]] && sclient_success=0 + [[ $sclient_success -eq 0 ]] && vuln_exportdh_ciphers=true elif [[ $nr_supported_ciphers -ne 0 ]]; then $OPENSSL s_client $(s_client_options "$STARTTLS $BUGS -cipher $exportdh_cipher_list -connect $NODEIP:$PORT $PROXY $SNI") >$TMPFILE 2>$ERRFILE Date: Thu, 23 Apr 2020 15:04:06 -0400 Subject: [PATCH 06/19] Improve compatibility with OpenSSL 3.0 This commit fixes a couple of issues related to the use of testssl.sh with OpenSSL 3.0.0-alpha1. First, when the command line includes an unknown option (e.g., -ssl2), OpenSSL 3.0.0-alpha responds with "Unknown option: -ssl2" rather than "Option unknown option -ssl2". This commit addresses this by making the check for "unknown option" case insensitve. Second, the printing a DH key, OpenSSL 3.0.0-alpha1 labels the prime and the generator using "prime P:" and "generator G:" rather than just "prime:" and "generator:". This commit by changing testssl.sh to match on either string. --- testssl.sh | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/testssl.sh b/testssl.sh index 0fd691b..2ea71a7 100755 --- a/testssl.sh +++ b/testssl.sh @@ -4794,7 +4794,7 @@ run_client_simulation() { # locally_supported() { [[ -n "$2" ]] && out "$2 " - if $OPENSSL s_client "$1" -connect invalid. 2>&1 | grep -aq "unknown option"; then + if $OPENSSL s_client "$1" -connect invalid. 2>&1 | grep -aiq "unknown option"; then prln_local_problem "$OPENSSL doesn't support \"s_client $1\"" return 7 fi @@ -4816,7 +4816,7 @@ run_prototest_openssl() { local protos proto # check whether the protocol being tested is supported by $OPENSSL - $OPENSSL s_client "$1" -connect invalid. 2>&1 | grep -aq "unknown option" && return 7 + $OPENSSL s_client "$1" -connect invalid. 2>&1 | grep -aiq "unknown option" && return 7 case "$1" in -ssl2) protos="-ssl2" ;; -ssl3) protos="-ssl3" ;; @@ -13684,8 +13684,8 @@ parse_tls_serverhello() { esac [[ -z "$key_bitstring" ]] && named_curve=0 && named_curve_str="" if "$HAS_PKEY" && [[ $named_curve -ne 0 ]] && [[ "${TLS13_KEY_SHARES[named_curve]}" =~ BEGIN ]]; then - ephemeral_param="$($OPENSSL pkey -pubin -text -noout 2>>$ERRFILE <<< "$key_bitstring" | grep -A 1000 "prime:")" - rfc7919_param="$($OPENSSL pkey -text -noout 2>>$ERRFILE <<< "${TLS13_KEY_SHARES[named_curve]}" | grep -A 1000 "prime:")" + ephemeral_param="$($OPENSSL pkey -pubin -text -noout 2>>$ERRFILE <<< "$key_bitstring" | grep -EA 1000 "prime:|prime P:")" + rfc7919_param="$($OPENSSL pkey -text -noout 2>>$ERRFILE <<< "${TLS13_KEY_SHARES[named_curve]}" | grep -EA 1000 "prime:|prime P:")" [[ "$ephemeral_param" != "$rfc7919_param" ]] && named_curve_str="" fi @@ -16302,7 +16302,7 @@ get_common_prime() { local -i lineno_matched=0 "$HAS_PKEY" || return 2 - dh_p="$($OPENSSL pkey -pubin -text -noout 2>>$ERRFILE <<< "$key_bitstring" | awk '/prime:/,/generator:/' | grep -Ev "prime|generator")" + dh_p="$($OPENSSL pkey -pubin -text -noout 2>>$ERRFILE <<< "$key_bitstring" | awk '/prime:|prime P:/,/generator:|generator G:/' | grep -Ev "prime|generator")" dh_p="$(strip_spaces "$(colon_to_spaces "$(newline_to_spaces "$dh_p")")")" [[ "${dh_p:0:2}" == "00" ]] && dh_p="${dh_p:2}" DH_GROUP_LEN_P="$((4*${#dh_p}))" @@ -18094,18 +18094,18 @@ find_openssl_binary() { HAS_AES256_GCM=false HAS_ZLIB=false - $OPENSSL ciphers -s 2>&1 | grep -aq "unknown option" || \ + $OPENSSL ciphers -s 2>&1 | grep -aiq "unknown option" || \ OSSL_CIPHERS_S="-s" # This and all other occurences we do a little trick using "invalid." to avoid plain and # link level DNS lookups. See issue #1418 and https://tools.ietf.org/html/rfc6761#section-6.4 - $OPENSSL s_client -ssl2 -connect invalid. 2>&1 | grep -aq "unknown option" || \ + $OPENSSL s_client -ssl2 -connect invalid. 2>&1 | grep -aiq "unknown option" || \ HAS_SSL2=true - $OPENSSL s_client -ssl3 -connect invalid. 2>&1 | grep -aq "unknown option" || \ + $OPENSSL s_client -ssl3 -connect invalid. 2>&1 | grep -aiq "unknown option" || \ HAS_SSL3=true - $OPENSSL s_client -tls1_3 -connect invalid. 2>&1 | grep -aq "unknown option" || \ + $OPENSSL s_client -tls1_3 -connect invalid. 2>&1 | grep -aiq "unknown option" || \ HAS_TLS13=true $OPENSSL genpkey -algorithm X448 -out - 2>&1 | grep -aq "not found" || \ @@ -18114,19 +18114,19 @@ find_openssl_binary() { $OPENSSL genpkey -algorithm X25519 -out - 2>&1 | grep -aq "not found" || \ HAS_X25519=true - $OPENSSL s_client -no_ssl2 -connect invalid. 2>&1 | grep -aq "unknown option" || \ + $OPENSSL s_client -no_ssl2 -connect invalid. 2>&1 | grep -aiq "unknown option" || \ HAS_NO_SSL2=true - $OPENSSL s_client -noservername -connect invalid. 2>&1 | grep -aq "unknown option" || \ + $OPENSSL s_client -noservername -connect invalid. 2>&1 | grep -aiq "unknown option" || \ HAS_NOSERVERNAME=true - $OPENSSL s_client -ciphersuites -connect invalid. 2>&1 | grep -aq "unknown option" || \ + $OPENSSL s_client -ciphersuites -connect invalid. 2>&1 | grep -aiq "unknown option" || \ HAS_CIPHERSUITES=true - $OPENSSL s_client -comp -connect invalid. 2>&1 | grep -aq "unknown option" || \ + $OPENSSL s_client -comp -connect invalid. 2>&1 | grep -aiq "unknown option" || \ HAS_COMP=true - $OPENSSL s_client -no_comp -connect invalid. 2>&1 | grep -aq "unknown option" || \ + $OPENSSL s_client -no_comp -connect invalid. 2>&1 | grep -aiq "unknown option" || \ HAS_NO_COMP=true OPENSSL_NR_CIPHERS=$(count_ciphers "$(actually_supported_osslciphers 'ALL:COMPLEMENTOFALL' 'ALL')") @@ -19551,7 +19551,7 @@ determine_optimal_proto() { elif "$all_failed" && ! "$ALL_FAILED_SOCKETS"; then if ! "$HAS_TLS13" && "$TLS13_ONLY"; then pr_magenta " $NODE:$PORT appears to support TLS 1.3 ONLY. You better use --openssl=" - if ! "$OSSL_SHORTCUT" || [[ ! -x /usr/bin/openssl ]] || /usr/bin/openssl s_client -tls1_3 -connect invalid. 2>&1 | grep -aq "unknown option"; then + if ! "$OSSL_SHORTCUT" || [[ ! -x /usr/bin/openssl ]] || /usr/bin/openssl s_client -tls1_3 -connect invalid. 2>&1 | grep -aiq "unknown option"; then outln ignore_no_or_lame " Type \"yes\" to proceed and accept all scan problems" "yes" [[ $? -ne 0 ]] && exit $ERR_CLUELESS From 3e54f4e4cd158c11975222798316776b19add659 Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Fri, 24 Apr 2020 13:32:26 +0200 Subject: [PATCH 07/19] Further changes to run_server_preference() In order not to provide redundant information run_allciphers() is now not being run via default (1). Therefore run_server_preference() runs always in wide mode. In order to archieve that cipher_pref_check() was modified to accept a fifth argument whether it'll run in wide mode. As of now cipher_pref_check() is only called by run_server_preference(), so the code referring to non-wide mode in cipher_pref_check() may also be deleted in the future. To provide a better view the run_fs() section is now being run after run_server_preference(). (1) saves also 5-6 seconds --- testssl.sh | 75 +++++++++++++++++++++++------------------------------- 1 file changed, 32 insertions(+), 43 deletions(-) diff --git a/testssl.sh b/testssl.sh index 37c4e57..90c655c 100755 --- a/testssl.sh +++ b/testssl.sh @@ -6530,47 +6530,35 @@ run_server_preference() { "$FAST" && using_sockets=false [[ $TLS_NR_CIPHERS == 0 ]] && using_sockets=false - # TODO: Shouldn't say "Cipher order" if the server does not always impose an order. - # TODO: In non-wide mode, need to distinguish between those order by server preference and those ordered by encryption strength. - pr_bold " Cipher listing per protocol" - "$WIDE" && outln "\n" && neat_header + pr_bold " Cipher per protocol" + outln "\n" && neat_header while read proto_ossl proto_hex proto_txt; do - if "$WIDE"; then - pr_underline "$(printf -- "%b" "$proto_txt")" - # TODO: If there's no cipher we should consider not displaying the text in the round brackets) - # the following takes care of that but only if we know the protocol is not supported - if [[ $(has_server_protocol "$proto_ossl") -eq 1 ]]; then - "$WIDE" && outln "\n - " - continue - fi - # TODO: Also the fact that a protocol is not supported seems not to be saved by cipher_pref_check() - # (./testssl.sh --wide -p -P -E vs ./testssl.sh --wide -P -E ) - if [[ "$proto_ossl" == ssl2 ]] || \ - ( [[ "$proto_ossl" != tls1_3 ]] && ! "$has_cipher_order" ]] ) || \ - ( [[ "$proto_ossl" == tls1_3 ]] && ! "$has_tls13_cipher_order" ]] ); then - if [[ "$proto_ossl" == ssl2 ]]; then - outln " (listed by strength)" - elif [[ "$proto_ossl" == tls1_3 ]]; then - outln " (no server order, thus listed by strength)" - else - prln_svrty_high " (no server order, thus listed by strength)" - fi - ciphers_by_strength "-$proto_ossl" "$proto_hex" "$proto_txt" "$using_sockets" "$WIDE" + pr_underline "$(printf -- "%b" "$proto_txt")" + # TODO: If there's no cipher we should consider not displaying the text in the round brackets) + # the following takes care of that but only if we know the protocol is not supported + if [[ $(has_server_protocol "$proto_ossl") -eq 1 ]]; then + outln "\n - " + continue + fi + # TODO: Also the fact that a protocol is not supported seems not to be saved by cipher_pref_check() + # (./testssl.sh --wide -p -P -E vs ./testssl.sh --wide -P -E ) + if [[ "$proto_ossl" == ssl2 ]] || \ + ( [[ "$proto_ossl" != tls1_3 ]] && ! "$has_cipher_order" ]] ) || \ + ( [[ "$proto_ossl" == tls1_3 ]] && ! "$has_tls13_cipher_order" ]] ); then + if [[ "$proto_ossl" == ssl2 ]]; then + outln " (listed by strength)" + elif [[ "$proto_ossl" == tls1_3 ]]; then + outln " (no server order, thus listed by strength)" else - outln " (server order)" - cipher_pref_check "$proto_ossl" "$proto_hex" "$proto_txt" "$using_sockets" + prln_svrty_high " (no server order, thus listed by strength)" fi + ciphers_by_strength "-$proto_ossl" "$proto_hex" "$proto_txt" "$using_sockets" "true" else - if [[ "$proto_ossl" == ssl2 ]] || ( [[ "$proto_ossl" != tls1_3 ]] && ! "$has_cipher_order" ]] ) || \ - ( [[ "$proto_ossl" == tls1_3 ]] && ! "$has_tls13_cipher_order" ]] ); then - ciphers_by_strength "-$proto_ossl" "$proto_hex" "$proto_txt" "$using_sockets" "$WIDE" - else - cipher_pref_check "$proto_ossl" "$proto_hex" "$proto_txt" "$using_sockets" - fi + outln " (server order)" + cipher_pref_check "$proto_ossl" "$proto_hex" "$proto_txt" "$using_sockets" "true" fi done <<< "$(tm_out " ssl2 22 SSLv2\n ssl3 00 SSLv3\n tls1 01 TLSv1\n tls1_1 02 TLSv1.1\n tls1_2 03 TLSv1.2\n tls1_3 04 TLSv1.3\n")" outln - outln return $ret # end of run_server_preference() @@ -6658,6 +6646,7 @@ check_tls12_pref() { cipher_pref_check() { local p="$1" proto_hex="$2" proto="$3" local using_sockets="$4" + local wide="$5" # at the moment this is called ALWAYS via run_server_preference and ALWAYS w true local tested_cipher cipher order rfc_cipher rfc_order local overflow_probe_cipherlist="ALL:-ECDHE-RSA-AES256-GCM-SHA384:-AES128-SHA:-DES-CBC3-SHA" local -i i nr_ciphers nr_nonossl_ciphers num_bundles bundle_size bundle end_of_bundle success @@ -6680,11 +6669,11 @@ cipher_pref_check() { if ( [[ $p != tls1_3 ]] || "$HAS_TLS13" ) && ( [[ $p != ssl3 ]] || "$HAS_SSL3" ); then if [[ $p == tls1_2 ]] && "$SERVER_SIZE_LIMIT_BUG"; then - order="$(check_tls12_pref "$WIDE")" + order="$(check_tls12_pref "$wide")" [[ "${order:0:1}" == \ ]] && order="${order:1}" ciphers_found="$order" fi - if "$WIDE" || [[ -z "$order" ]]; then + if "$wide" || [[ -z "$order" ]]; then tested_cipher=""; order=""; nr_ciphers_found=0 while true; do if [[ $p != tls1_3 ]]; then @@ -6713,7 +6702,7 @@ cipher_pref_check() { order+="$cipher " tested_cipher+=":-"$cipher "$FAST" && break - if "$WIDE"; then + if "$wide"; then for (( i=0; i < TLS_NR_CIPHERS; i++ )); do [[ "$cipher" == ${TLS_CIPHER_OSSL_NAME[i]} ]] && break done @@ -6843,7 +6832,7 @@ cipher_pref_check() { for (( i=0; i < nr_ciphers; i++ )); do [[ "$cipher" == ${rfc_ciph[i]} ]] && ciphers_found2[i]=true && break done - if "$WIDE"; then + if "$wide"; then for (( i=0; i < TLS_NR_CIPHERS; i++ )); do [[ "$cipher" == ${TLS_CIPHER_RFC_NAME[i]} ]] && break done @@ -6884,7 +6873,7 @@ cipher_pref_check() { if [[ -n "$order" ]]; then add_tls_offered "$p" yes - if "$WIDE"; then + if "$wide"; then for (( i=0 ; i Date: Sat, 25 Apr 2020 11:12:36 +0200 Subject: [PATCH 08/19] Negotiated protocol showed no warning for TLS 1.1/1.0 .. whereas the protocol section did that. This fixes the inconsistency. --- testssl.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/testssl.sh b/testssl.sh index 0fd691b..eedbb3a 100755 --- a/testssl.sh +++ b/testssl.sh @@ -6419,12 +6419,12 @@ run_server_preference() { fileout "$jsonID" "OK" "Default protocol TLS1.2" ;; *TLSv1.1) - prln_svrty_good $default_proto - fileout "$jsonID" "OK" "Default protocol TLS1.1" + prln_svrty_low $default_proto + fileout "$jsonID" "LOW" "Default protocol TLS1.1" ;; *TLSv1) - outln $default_proto - fileout "$jsonID" "INFO" "Default protocol TLS1.0" + prln_svrty_low $default_proto + fileout "$jsonID" "LOW" "Default protocol TLS1.0" ;; *SSLv2) prln_svrty_critical $default_proto From 0a859d7b986291ba8aca055ff33375d47f5c51f0 Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Mon, 27 Apr 2020 15:32:43 +0200 Subject: [PATCH 09/19] rename $p --> $proto_ossl in cipher_pref_check() plus remove redundant quotes for that --- testssl.sh | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/testssl.sh b/testssl.sh index 90c655c..f9567af 100755 --- a/testssl.sh +++ b/testssl.sh @@ -6282,7 +6282,7 @@ run_server_preference() { "$SSL_NATIVE" && using_sockets=false outln - pr_headlineln " Testing server preferences " + pr_headlineln " Testing server's cipher preferences " outln pr_bold " Has server cipher order? " @@ -6644,7 +6644,7 @@ check_tls12_pref() { cipher_pref_check() { - local p="$1" proto_hex="$2" proto="$3" + local proto_ossl="$1" proto_hex="$2" proto="$3" local using_sockets="$4" local wide="$5" # at the moment this is called ALWAYS via run_server_preference and ALWAYS w true local tested_cipher cipher order rfc_cipher rfc_order @@ -6658,17 +6658,17 @@ cipher_pref_check() { local ciphers_found_with_sockets order=""; ciphers_found_with_sockets=false - if [[ $p == ssl3 ]] && ! "$HAS_SSL3" && ! "$using_sockets"; then + if [[ $proto_ossl == ssl3 ]] && ! "$HAS_SSL3" && ! "$using_sockets"; then out "\n SSLv3: "; pr_local_problem "$OPENSSL doesn't support \"s_client -ssl3\""; return 0 fi - if [[ $p == tls1_3 ]] && ! "$HAS_TLS13" && ! "$using_sockets"; then + if [[ $proto_ossl == tls1_3 ]] && ! "$HAS_TLS13" && ! "$using_sockets"; then out "\n TLSv1.3 "; pr_local_problem "$OPENSSL doesn't support \"s_client -tls1_3\""; return 0 fi - if ( [[ $p != tls1_3 ]] || "$HAS_TLS13" ) && ( [[ $p != ssl3 ]] || "$HAS_SSL3" ); then - if [[ $p == tls1_2 ]] && "$SERVER_SIZE_LIMIT_BUG"; then + if ( [[ $proto_ossl != tls1_3 ]] || "$HAS_TLS13" ) && ( [[ $proto_ossl != ssl3 ]] || "$HAS_SSL3" ); then + if [[ $proto_ossl == tls1_2 ]] && "$SERVER_SIZE_LIMIT_BUG"; then order="$(check_tls12_pref "$wide")" [[ "${order:0:1}" == \ ]] && order="${order:1}" ciphers_found="$order" @@ -6676,7 +6676,7 @@ cipher_pref_check() { if "$wide" || [[ -z "$order" ]]; then tested_cipher=""; order=""; nr_ciphers_found=0 while true; do - if [[ $p != tls1_3 ]]; then + if [[ $proto_ossl != tls1_3 ]]; then if [[ -n "$ciphers_found" ]]; then ciphers_to_test="" for cipher in $ciphers_found; do @@ -6695,7 +6695,7 @@ cipher_pref_check() { [[ -z "$ciphers_to_test" ]] && break ciphers_to_test="-ciphersuites ${ciphers_to_test:1}" fi - $OPENSSL s_client $(s_client_options "$STARTTLS -"$p" $BUGS $ciphers_to_test -connect $NODEIP:$PORT $PROXY $SNI") >$ERRFILE >$TMPFILE + $OPENSSL s_client $(s_client_options "$STARTTLS -"$proto_ossl" $BUGS $ciphers_to_test -connect $NODEIP:$PORT $PROXY $SNI") >$ERRFILE >$TMPFILE sclient_connect_successful $? $TMPFILE || break cipher=$(get_cipher $TMPFILE) [[ -z "$cipher" ]] && break @@ -6710,7 +6710,7 @@ cipher_pref_check() { normalized_hexcode[nr_ciphers_found]="$(normalize_ciphercode "${TLS_CIPHER_HEXCODE[i]}")" ciph[nr_ciphers_found]="${TLS_CIPHER_OSSL_NAME[i]}" kx[nr_ciphers_found]="${TLS_CIPHER_KX[i]}" - [[ "$p" == tls1_3 ]] && kx[nr_ciphers_found]="$(read_dhtype_from_file $TMPFILE)" + [[ $proto_ossl == tls1_3 ]] && kx[nr_ciphers_found]="$(read_dhtype_from_file $TMPFILE)" if ( [[ ${kx[nr_ciphers_found]} == Kx=ECDH ]] || [[ ${kx[nr_ciphers_found]} == Kx=DH ]] || [[ ${kx[nr_ciphers_found]} == Kx=EDH ]] ); then kx[nr_ciphers_found]+=" $(read_dhbits_from_file "$TMPFILE" quiet)" fi @@ -6739,9 +6739,9 @@ 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 - [[ "${hexc:2:2}" == "13" ]] && nr_nonossl_ciphers+=1 - elif [[ "$p" == tls1_2 ]]; then + if [[ $proto_ossl == tls1_3 ]]; then + [[ "${hexc:2:2}" == 13 ]] && nr_nonossl_ciphers+=1 + elif [[ $proto_ossl == tls1_2 ]]; then [[ "${hexc:2:2}" != 13 ]] && nr_nonossl_ciphers+=1 elif [[ ! "${TLS_CIPHER_RFC_NAME[i]}" =~ SHA256 ]] && \ [[ ! "${TLS_CIPHER_RFC_NAME[i]}" =~ SHA384 ]] && \ @@ -6756,7 +6756,7 @@ cipher_pref_check() { if [[ $nr_nonossl_ciphers -eq 0 ]]; then num_bundles=0 - elif [[ $p != tls1_2 ]] || ! "$SERVER_SIZE_LIMIT_BUG"; then + elif [[ $proto_ossl != tls1_2 ]] || ! "$SERVER_SIZE_LIMIT_BUG"; then num_bundles=1 bundle_size=$nr_nonossl_ciphers else @@ -6785,7 +6785,7 @@ cipher_pref_check() { i=${index[i]} ciphers_found[i]=true ciphers_found_with_sockets=true - if [[ $p != tls1_2 ]] || ! "$SERVER_SIZE_LIMIT_BUG"; then + if [[ $proto_ossl != tls1_2 ]] || ! "$SERVER_SIZE_LIMIT_BUG"; then # Throw out the results found so far and start over using just sockets bundle=$num_bundles for (( i=0; i < TLS_NR_CIPHERS; i++ )); do @@ -6808,9 +6808,9 @@ cipher_pref_check() { ciphers_found2[nr_ciphers]=false hexcode[nr_ciphers]="${hexc:2:2},${hexc:7:2}" rfc_ciph[nr_ciphers]="${TLS_CIPHER_RFC_NAME[i]}" - if [[ "$p" == "tls1_3" ]]; then + if [[ $proto_ossl == "tls1_3" ]]; then [[ "${hexc:2:2}" == "13" ]] && nr_ciphers+=1 - elif [[ "$p" == "tls1_2" ]]; then + elif [[ $proto_ossl == "tls1_2" ]]; then [[ "${hexc:2:2}" != "13" ]] && nr_ciphers+=1 elif [[ ! "${TLS_CIPHER_RFC_NAME[i]}" =~ SHA256 ]] && \ [[ ! "${TLS_CIPHER_RFC_NAME[i]}" =~ SHA384 ]] && \ @@ -6840,7 +6840,7 @@ cipher_pref_check() { normalized_hexcode[nr_ciphers_found]="$(normalize_ciphercode "${TLS_CIPHER_HEXCODE[i]}")" ciph[nr_ciphers_found]="${TLS_CIPHER_OSSL_NAME[i]}" kx[nr_ciphers_found]="${TLS_CIPHER_KX[i]}" - [[ "$p" == tls1_3 ]] && kx[nr_ciphers_found]="$(read_dhtype_from_file "$TEMPDIR/$NODEIP.parse_tls_serverhello.txt")" + [[ $proto_ossl == tls1_3 ]] && kx[nr_ciphers_found]="$(read_dhtype_from_file "$TEMPDIR/$NODEIP.parse_tls_serverhello.txt")" if ( [[ ${kx[nr_ciphers_found]} == Kx=ECDH ]] || [[ ${kx[nr_ciphers_found]} == Kx=DH ]] || [[ ${kx[nr_ciphers_found]} == Kx=EDH ]] ); then kx[nr_ciphers_found]+=" $(read_dhbits_from_file "$TEMPDIR/$NODEIP.parse_tls_serverhello.txt" quiet)" fi @@ -6872,12 +6872,12 @@ cipher_pref_check() { fi if [[ -n "$order" ]]; then - add_tls_offered "$p" yes + add_tls_offered "$proto_ossl" yes if "$wide"; then for (( i=0 ; i Date: Mon, 27 Apr 2020 16:51:45 +0200 Subject: [PATCH 10/19] Remember better protocol settings in ciphers_by_strength() / cipher_pref_check() ... in cases where the protcol section has not been run before. Also add " -\n" on the screen/html if protocol is not supported. Also for SSLv2 which can be supported but at the same time not offer any ciphers mention there will be an output on the screen. --- testssl.sh | 63 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 45 insertions(+), 18 deletions(-) diff --git a/testssl.sh b/testssl.sh index f9567af..e4c5ab0 100755 --- a/testssl.sh +++ b/testssl.sh @@ -259,6 +259,7 @@ APP_TRAF_KEY_INFO="" # Information about the application traf TLS13_ONLY=false # Does the server support TLS 1.3 ONLY? OSSL_SHORTCUT=${OSSL_SHORTCUT:-false} # Hack: if during the scan turns out the OpenSSL binary suports TLS 1.3 would be a better choice, this enables it. TLS_EXTENSIONS="" +V2_HELLO_CIPHERSPEC_LENGTH=0 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 grpc-exp h2-fb spdy/1 spdy/2 spdy/3 stun.turn stun.nat-discovery webrtc c-webrtc ftp" @@ -4045,7 +4046,7 @@ ciphers_by_strength() { # The OpenSSL ciphers function, prior to version 1.1.0, could only understand -ssl2, -ssl3, and -tls1. if [[ "$OSSL_NAME" =~ LibreSSL ]]; then ossl_ciphers_proto="" - elif [[ "$proto" == -ssl2 ]] || [[ "$proto" == -ssl3 ]] || \ + elif [[ $proto == -ssl2 ]] || [[ $proto == -ssl3 ]] || \ [[ $OSSL_VER_MAJOR.$OSSL_VER_MINOR == 1.1.0* ]] || [[ $OSSL_VER_MAJOR.$OSSL_VER_MINOR == 1.1.1* ]] || \ [[ $OSSL_VER_MAJOR.$OSSL_VER_MINOR == 3.0.0* ]]; then ossl_ciphers_proto="$proto" @@ -4072,23 +4073,32 @@ ciphers_by_strength() { done < <(actually_supported_osslciphers 'ALL:COMPLEMENTOFALL:@STRENGTH' 'ALL' "$ossl_ciphers_proto -V") fi - if [[ "$proto" == -ssl2 ]]; then + if [[ $proto == -ssl2 ]]; then if "$using_sockets"; then sslv2_sockets "${sslv2_ciphers:2}" "true" - if [[ $? -eq 3 ]] && [[ "$V2_HELLO_CIPHERSPEC_LENGTH" -ne 0 ]]; then - supported_sslv2_ciphers="$(grep "Supported cipher: " "$TEMPDIR/$NODEIP.parse_sslv2_serverhello.txt")" - "$wide" && "$SHOW_SIGALGO" && s="$(read_sigalg_from_file "$HOSTCERT")" - for (( i=0 ; i$TMPFILE 2>$ERRFILE Date: Mon, 27 Apr 2020 17:08:43 +0200 Subject: [PATCH 11/19] Renaming proto variables in cipher_pref_check() ... to be consistent with ciphers_by_strength: - proto --> proto_text - proto_ossl --> proto --- testssl.sh | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/testssl.sh b/testssl.sh index e4c5ab0..fe29a31 100755 --- a/testssl.sh +++ b/testssl.sh @@ -4088,7 +4088,7 @@ ciphers_by_strength() { fi done else - outln " protocol support with no cipher " + outln " protocol supported with no cipher " fi else add_tls_offered ssl2 no @@ -6667,7 +6667,7 @@ check_tls12_pref() { # At the moment only called from run_server_preference() cipher_pref_check() { - local proto_ossl="$1" proto_hex="$2" proto="$3" + local proto="$1" proto_hex="$2" proto_text="$3" local using_sockets="$4" local wide="$5" # at the moment always = true local tested_cipher cipher order rfc_cipher rfc_order @@ -6681,17 +6681,17 @@ cipher_pref_check() { local ciphers_found_with_sockets order=""; ciphers_found_with_sockets=false - if [[ $proto_ossl == ssl3 ]] && ! "$HAS_SSL3" && ! "$using_sockets"; then + if [[ $proto == ssl3 ]] && ! "$HAS_SSL3" && ! "$using_sockets"; then out "\n SSLv3: "; pr_local_problem "$OPENSSL doesn't support \"s_client -ssl3\""; return 0 fi - if [[ $proto_ossl == tls1_3 ]] && ! "$HAS_TLS13" && ! "$using_sockets"; then + if [[ $proto == tls1_3 ]] && ! "$HAS_TLS13" && ! "$using_sockets"; then out "\n TLSv1.3 "; pr_local_problem "$OPENSSL doesn't support \"s_client -tls1_3\""; return 0 fi - if ( [[ $proto_ossl != tls1_3 ]] || "$HAS_TLS13" ) && ( [[ $proto_ossl != ssl3 ]] || "$HAS_SSL3" ); then - if [[ $proto_ossl == tls1_2 ]] && "$SERVER_SIZE_LIMIT_BUG"; then + if ( [[ $proto != tls1_3 ]] || "$HAS_TLS13" ) && ( [[ $proto != ssl3 ]] || "$HAS_SSL3" ); then + if [[ $proto == tls1_2 ]] && "$SERVER_SIZE_LIMIT_BUG"; then order="$(check_tls12_pref "$wide")" [[ "${order:0:1}" == \ ]] && order="${order:1}" ciphers_found="$order" @@ -6699,7 +6699,7 @@ cipher_pref_check() { if "$wide" || [[ -z "$order" ]]; then tested_cipher=""; order=""; nr_ciphers_found=0 while true; do - if [[ $proto_ossl != tls1_3 ]]; then + if [[ $proto != tls1_3 ]]; then if [[ -n "$ciphers_found" ]]; then ciphers_to_test="" for cipher in $ciphers_found; do @@ -6718,7 +6718,7 @@ cipher_pref_check() { [[ -z "$ciphers_to_test" ]] && break ciphers_to_test="-ciphersuites ${ciphers_to_test:1}" fi - $OPENSSL s_client $(s_client_options "$STARTTLS -"$proto_ossl" $BUGS $ciphers_to_test -connect $NODEIP:$PORT $PROXY $SNI") >$ERRFILE >$TMPFILE + $OPENSSL s_client $(s_client_options "$STARTTLS -"$proto" $BUGS $ciphers_to_test -connect $NODEIP:$PORT $PROXY $SNI") >$ERRFILE >$TMPFILE sclient_connect_successful $? $TMPFILE || break cipher=$(get_cipher $TMPFILE) [[ -z "$cipher" ]] && break @@ -6733,7 +6733,7 @@ cipher_pref_check() { normalized_hexcode[nr_ciphers_found]="$(normalize_ciphercode "${TLS_CIPHER_HEXCODE[i]}")" ciph[nr_ciphers_found]="${TLS_CIPHER_OSSL_NAME[i]}" kx[nr_ciphers_found]="${TLS_CIPHER_KX[i]}" - [[ $proto_ossl == tls1_3 ]] && kx[nr_ciphers_found]="$(read_dhtype_from_file $TMPFILE)" + [[ $proto == tls1_3 ]] && kx[nr_ciphers_found]="$(read_dhtype_from_file $TMPFILE)" if ( [[ ${kx[nr_ciphers_found]} == Kx=ECDH ]] || [[ ${kx[nr_ciphers_found]} == Kx=DH ]] || [[ ${kx[nr_ciphers_found]} == Kx=EDH ]] ); then kx[nr_ciphers_found]+=" $(read_dhbits_from_file "$TMPFILE" quiet)" fi @@ -6762,9 +6762,9 @@ 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 [[ $proto_ossl == tls1_3 ]]; then + if [[ $proto == tls1_3 ]]; then [[ "${hexc:2:2}" == 13 ]] && nr_nonossl_ciphers+=1 - elif [[ $proto_ossl == tls1_2 ]]; then + elif [[ $proto == tls1_2 ]]; then [[ "${hexc:2:2}" != 13 ]] && nr_nonossl_ciphers+=1 elif [[ ! "${TLS_CIPHER_RFC_NAME[i]}" =~ SHA256 ]] && \ [[ ! "${TLS_CIPHER_RFC_NAME[i]}" =~ SHA384 ]] && \ @@ -6779,7 +6779,7 @@ cipher_pref_check() { if [[ $nr_nonossl_ciphers -eq 0 ]]; then num_bundles=0 - elif [[ $proto_ossl != tls1_2 ]] || ! "$SERVER_SIZE_LIMIT_BUG"; then + elif [[ $proto != tls1_2 ]] || ! "$SERVER_SIZE_LIMIT_BUG"; then num_bundles=1 bundle_size=$nr_nonossl_ciphers else @@ -6808,7 +6808,7 @@ cipher_pref_check() { i=${index[i]} ciphers_found[i]=true ciphers_found_with_sockets=true - if [[ $proto_ossl != tls1_2 ]] || ! "$SERVER_SIZE_LIMIT_BUG"; then + if [[ $proto != tls1_2 ]] || ! "$SERVER_SIZE_LIMIT_BUG"; then # Throw out the results found so far and start over using just sockets bundle=$num_bundles for (( i=0; i < TLS_NR_CIPHERS; i++ )); do @@ -6831,9 +6831,9 @@ cipher_pref_check() { ciphers_found2[nr_ciphers]=false hexcode[nr_ciphers]="${hexc:2:2},${hexc:7:2}" rfc_ciph[nr_ciphers]="${TLS_CIPHER_RFC_NAME[i]}" - if [[ $proto_ossl == "tls1_3" ]]; then + if [[ $proto == tls1_3 ]]; then [[ "${hexc:2:2}" == "13" ]] && nr_ciphers+=1 - elif [[ $proto_ossl == "tls1_2" ]]; then + elif [[ $proto == tls1_2 ]]; then [[ "${hexc:2:2}" != "13" ]] && nr_ciphers+=1 elif [[ ! "${TLS_CIPHER_RFC_NAME[i]}" =~ SHA256 ]] && \ [[ ! "${TLS_CIPHER_RFC_NAME[i]}" =~ SHA384 ]] && \ @@ -6863,7 +6863,7 @@ cipher_pref_check() { normalized_hexcode[nr_ciphers_found]="$(normalize_ciphercode "${TLS_CIPHER_HEXCODE[i]}")" ciph[nr_ciphers_found]="${TLS_CIPHER_OSSL_NAME[i]}" kx[nr_ciphers_found]="${TLS_CIPHER_KX[i]}" - [[ $proto_ossl == tls1_3 ]] && kx[nr_ciphers_found]="$(read_dhtype_from_file "$TEMPDIR/$NODEIP.parse_tls_serverhello.txt")" + [[ $proto == tls1_3 ]] && kx[nr_ciphers_found]="$(read_dhtype_from_file "$TEMPDIR/$NODEIP.parse_tls_serverhello.txt")" if ( [[ ${kx[nr_ciphers_found]} == Kx=ECDH ]] || [[ ${kx[nr_ciphers_found]} == Kx=DH ]] || [[ ${kx[nr_ciphers_found]} == Kx=EDH ]] ); then kx[nr_ciphers_found]+=" $(read_dhbits_from_file "$TEMPDIR/$NODEIP.parse_tls_serverhello.txt" quiet)" fi @@ -6895,31 +6895,31 @@ cipher_pref_check() { fi if [[ -n "$order" ]]; then - add_tls_offered "$proto_ossl" yes + add_tls_offered "$proto" yes if "$wide"; then for (( i=0 ; i Date: Mon, 27 Apr 2020 17:12:25 +0200 Subject: [PATCH 12/19] Rename add_tls_offered --> add_proto_offered ... last but not least SSLv2 and SSLv3 are no TLS protocols --- testssl.sh | 188 ++++++++++++++++++++++++++--------------------------- 1 file changed, 94 insertions(+), 94 deletions(-) diff --git a/testssl.sh b/testssl.sh index fe29a31..a85be9d 100755 --- a/testssl.sh +++ b/testssl.sh @@ -4077,7 +4077,7 @@ ciphers_by_strength() { if "$using_sockets"; then sslv2_sockets "${sslv2_ciphers:2}" "true" if [[ $? -eq 3 ]] ; then - add_tls_offered ssl2 yes + add_proto_offered ssl2 yes if [[ "$V2_HELLO_CIPHERSPEC_LENGTH" -ne 0 ]]; then supported_sslv2_ciphers="$(grep "Supported cipher: " "$TEMPDIR/$NODEIP.parse_sslv2_serverhello.txt")" "$wide" && "$SHOW_SIGALGO" && s="$(read_sigalg_from_file "$HOSTCERT")" @@ -4091,14 +4091,14 @@ ciphers_by_strength() { outln " protocol supported with no cipher " fi else - add_tls_offered ssl2 no + add_proto_offered ssl2 no "$wide" && outln " - " fi else $OPENSSL s_client $STARTTLS $BUGS -connect $NODEIP:$PORT $PROXY -ssl2 >$TMPFILE 2>$ERRFILE comes from run_prototest_openssl fileout "$jsonID" "HIGH" "$supported_no_ciph1" - add_tls_offered ssl3 yes + add_proto_offered ssl3 yes ;; 7) if "$using_sockets" ; then # can only happen in debug mode @@ -5153,10 +5153,10 @@ run_protocols() { fileout "$jsonID" "LOW" "offered (deprecated)" latest_supported="0301" latest_supported_string="TLSv1.0" - add_tls_offered tls1 yes + add_proto_offered tls1 yes ;; # nothing wrong with it -- per se 1) out "not offered" - add_tls_offered tls1 no + add_proto_offered tls1 no if [[ -z $latest_supported ]]; then outln fileout "$jsonID" "INFO" "not offered" # neither good or bad @@ -5166,7 +5166,7 @@ run_protocols() { fi ;; 2) pr_svrty_medium "not offered" - add_tls_offered tls1 no + add_proto_offered tls1 no if [[ "$DETECTED_TLS_VERSION" == 0300 ]]; then [[ $DEBUG -ge 1 ]] && tm_out " -- downgraded" outln @@ -5187,19 +5187,19 @@ run_protocols() { ;; 3) out "not offered, " fileout "$jsonID" "OK" "not offered" - add_tls_offered tls1 no + add_proto_offered tls1 no pr_warning "TLS downgraded to STARTTLS plaintext"; outln fileout "$jsonID" "WARN" "TLS downgraded to STARTTLS plaintext" ;; 4) out "likely not offered, " fileout "$jsonID" "INFO" "likely not offered" - add_tls_offered tls1 no + add_proto_offered tls1 no pr_warning "received 4xx/5xx after STARTTLS handshake"; outln "$debug_recomm" fileout "$jsonID" "WARN" "received 4xx/5xx after STARTTLS handshake${debug_recomm}" ;; 5) outln "$supported_no_ciph1" # protocol detected but no cipher --> comes from run_prototest_openssl fileout "$jsonID" "INFO" "$supported_no_ciph1" - add_tls_offered tls1 yes + add_proto_offered tls1 yes ;; 7) if "$using_sockets" ; then # can only happen in debug mode @@ -5231,10 +5231,10 @@ run_protocols() { fileout "$jsonID" "LOW" "offered (deprecated)" latest_supported="0302" latest_supported_string="TLSv1.1" - add_tls_offered tls1_1 yes + add_proto_offered tls1_1 yes ;; # nothing wrong with it 1) out "not offered" - add_tls_offered tls1_1 no + add_proto_offered tls1_1 no if [[ -z $latest_supported ]]; then outln fileout "$jsonID" "INFO" "is not offered" # neither good or bad @@ -5244,7 +5244,7 @@ run_protocols() { fi ;; 2) out "not offered" - add_tls_offered tls1_1 no + add_proto_offered tls1_1 no if [[ "$DETECTED_TLS_VERSION" == "$latest_supported" ]]; then [[ $DEBUG -ge 1 ]] && tm_out " -- downgraded" outln @@ -5268,19 +5268,19 @@ run_protocols() { ;; 3) out "not offered, " fileout "$jsonID" "OK" "not offered" - add_tls_offered tls1_1 no + add_proto_offered tls1_1 no pr_warning "TLS downgraded to STARTTLS plaintext"; outln fileout "$jsonID" "WARN" "TLS downgraded to STARTTLS plaintext" ;; 4) out "likely not offered, " fileout "$jsonID" "INFO" "is not offered" - add_tls_offered tls1_1 no + add_proto_offered tls1_1 no pr_warning "received 4xx/5xx after STARTTLS handshake"; outln "$debug_recomm" fileout "$jsonID" "WARN" "received 4xx/5xx after STARTTLS handshake${debug_recomm}" ;; 5) outln "$supported_no_ciph1" # protocol detected but no cipher --> comes from run_prototest_openssl fileout "$jsonID" "INFO" "$supported_no_ciph1" - add_tls_offered tls1_1 yes + add_proto_offered tls1_1 yes ;; 7) if "$using_sockets" ; then # can only happen in debug mode @@ -5342,9 +5342,9 @@ run_protocols() { fileout "$jsonID" "OK" "offered" latest_supported="0303" latest_supported_string="TLSv1.2" - add_tls_offered tls1_2 yes + add_proto_offered tls1_2 yes ;; # GCM cipher in TLS 1.2: very good! - 1) add_tls_offered tls1_2 no + 1) add_proto_offered tls1_2 no if "$offers_tls13"; then out "not offered" else @@ -5362,7 +5362,7 @@ run_protocols() { fileout "$jsonID" "CRITICAL" "connection failed rather than downgrading to $latest_supported_string" fi ;; - 2) add_tls_offered tls1_2 no + 2) add_proto_offered tls1_2 no pr_svrty_medium "not offered and downgraded to a weaker protocol" if [[ "$tls12_detected_version" == 0300 ]]; then detected_version_string="SSLv3" @@ -5390,19 +5390,19 @@ run_protocols() { ;; 3) out "not offered, " fileout "$jsonID" "INFO" "not offered" - add_tls_offered tls1_2 no + add_proto_offered tls1_2 no pr_warning "TLS downgraded to STARTTLS plaintext"; outln fileout "$jsonID" "WARN" "TLS downgraded to STARTTLS plaintext" ;; 4) out "likely "; pr_svrty_medium "not offered, " fileout "$jsonID" "MEDIUM" "not offered" - add_tls_offered tls1_2 no + add_proto_offered tls1_2 no pr_warning "received 4xx/5xx after STARTTLS handshake"; outln "$debug_recomm" fileout "$jsonID" "WARN" "received 4xx/5xx after STARTTLS handshake${debug_recomm}" ;; 5) outln "$supported_no_ciph1" # protocol detected, but no cipher --> comes from run_prototest_openssl fileout "$jsonID" "INFO" "$supported_no_ciph1" - add_tls_offered tls1_2 yes + add_proto_offered tls1_2 yes ;; 7) if "$using_sockets" ; then # can only happen in debug mode @@ -5496,7 +5496,7 @@ run_protocols() { fi latest_supported="0304" latest_supported_string="TLSv1.3" - add_tls_offered tls1_3 yes + add_proto_offered tls1_3 yes ;; 1) pr_svrty_low "not offered" if [[ -z $latest_supported ]]; then @@ -5506,7 +5506,7 @@ run_protocols() { prln_svrty_critical " -- connection failed rather than downgrading to $latest_supported_string" fileout "$jsonID" "CRITICAL" "connection failed rather than downgrading to $latest_supported_string" fi - add_tls_offered tls1_3 no + add_proto_offered tls1_3 no ;; 2) if [[ "$DETECTED_TLS_VERSION" == 0300 ]]; then detected_version_string="SSLv3" @@ -5529,23 +5529,23 @@ run_protocols() { prln_svrty_critical " -- server responded with version number ${DETECTED_TLS_VERSION:0:2}.${DETECTED_TLS_VERSION:2:2}" fileout "$jsonID" "CRITICAL" "server responded with version number ${DETECTED_TLS_VERSION:0:2}.${DETECTED_TLS_VERSION:2:2}" fi - add_tls_offered tls1_3 no + add_proto_offered tls1_3 no ;; 3) out "not offered " fileout "$jsonID" "INFO" "not offered" - add_tls_offered tls1_3 no + add_proto_offered tls1_3 no pr_warning "TLS downgraded to STARTTLS plaintext"; outln fileout "$jsonID" "WARN" "TLS downgraded to STARTTLS plaintext" ;; 4) out "likely not offered, " fileout "$jsonID" "INFO" "not offered" - add_tls_offered tls1_3 no + add_proto_offered tls1_3 no pr_warning "received 4xx/5xx after STARTTLS handshake"; outln "$debug_recomm" fileout "$jsonID" "WARN" "received 4xx/5xx after STARTTLS handshake${debug_recomm}" ;; 5) outln "$supported_no_ciph1" # protocol detected but no cipher --> comes from run_prototest_openssl fileout "$jsonID" "INFO" "$supported_no_ciph1" - add_tls_offered tls1_3 yes + add_proto_offered tls1_3 yes ;; 7) if "$using_sockets" ; then # can only happen in debug mode @@ -6330,14 +6330,14 @@ run_server_preference() { "ephemeralkey" sclient_success=$? if [[ $sclient_success -eq 0 ]]; then - add_tls_offered tls1_3 yes + add_proto_offered tls1_3 yes elif [[ $sclient_success -eq 2 ]]; then sclient_success=0 # 2: downgraded case $DETECTED_TLS_VERSION in - 0303) add_tls_offered tls1_2 yes ;; - 0302) add_tls_offered tls1_1 yes ;; - 0301) add_tls_offered tls1 yes ;; - 0300) add_tls_offered ssl3 yes ;; + 0303) add_proto_offered tls1_2 yes ;; + 0302) add_proto_offered tls1_1 yes ;; + 0301) add_proto_offered tls1 yes ;; + 0300) add_proto_offered ssl3 yes ;; esac fi if [[ $sclient_success -eq 0 ]] ; then @@ -6895,7 +6895,7 @@ cipher_pref_check() { fi if [[ -n "$order" ]]; then - add_tls_offered "$proto" yes + add_proto_offered "$proto" yes if "$wide"; then for (( i=0 ; i$TMPFILE 2>$ERRFILE $TMPFILE 2>$ERRFILE $TMPFILE 2>$ERRFILE /dev/null)") debugme tm_out " ($lines lines) " - add_tls_offered ssl2 yes + add_proto_offered ssl2 yes if [[ "$lines" -gt 1 ]]; then nr_ciphers_detected=$((V2_HELLO_CIPHERSPEC_LENGTH / 3)) if [[ 0 -eq "$nr_ciphers_detected" ]]; then @@ -16684,7 +16684,7 @@ run_beast(){ $OPENSSL s_client $(s_client_options "-state -"${proto}" $STARTTLS $BUGS -connect $NODEIP:$PORT $PROXY $SNI") 2>>$ERRFILE >$TMPFILE $TMPFILE 2>>$ERRFILE if sclient_auth $? $TMPFILE; then all_failed=false - add_tls_offered "${proto/-/}" yes + add_proto_offered "${proto/-/}" yes break fi done @@ -19479,11 +19479,11 @@ determine_optimal_proto() { tmp=${tmp/\./_} tmp=${tmp/v/} tmp="$(tolower $tmp)" - add_tls_offered "${tmp}" yes + add_proto_offered "${tmp}" yes debugme echo "one proto determined: $tmp" OPTIMAL_PROTO="" else - add_tls_offered "${proto/-/}" yes + add_proto_offered "${proto/-/}" yes OPTIMAL_PROTO="$proto" fi all_failed=false From 680aff48e4c999a859c2af4862968537f646c632 Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Mon, 27 Apr 2020 17:19:30 +0200 Subject: [PATCH 13/19] Update documentation related to extended run_server_preference() --- doc/testssl.1 | 9 +++------ doc/testssl.1.html | 8 +++----- doc/testssl.1.md | 8 +++----- 3 files changed, 9 insertions(+), 16 deletions(-) diff --git a/doc/testssl.1 b/doc/testssl.1 index a8e8626..460996d 100644 --- a/doc/testssl.1 +++ b/doc/testssl.1 @@ -46,10 +46,10 @@ Any OpenSSL or LibreSSL version is needed as a helper\. Unlike previous versions 2) standard cipher categories to give you upfront an idea for the ciphers supported . .P -3) checks forward secrecy: ciphers and elliptical curves +3) server's cipher preferences (server order) . .P -4) server preferences (server order) +4) forward secrecy: ciphers and elliptical curves . .P 5) server defaults (certificate info, TLS extensions, session information) @@ -61,10 +61,7 @@ Any OpenSSL or LibreSSL version is needed as a helper\. Unlike previous versions 7) vulnerabilities . .P -8) testing each of 370 preconfigured ciphers -. -.P -9) client simulation +8) client simulation . .SH "OPTIONS AND PARAMETERS" Options are either short or long options\. Any long or short option requiring a value can be called with or without an equal sign\. E\.g\. \fBtestssl\.sh \-t=smtp \-\-wide \-\-openssl=/usr/bin/openssl \fR (short options with equal sign) is equivalent to \fBtestssl\.sh \-\-starttls smtp \-\-wide \-\-openssl /usr/bin/openssl \fR (long option without equal sign)\. Some command line options can also be preset via ENV variables\. \fBWIDE=true OPENSSL=/usr/bin/openssl testssl\.sh \-\-starttls=smtp \fR would be the equivalent to the aforementioned examples\. Preference has the command line over any environment variables\. diff --git a/doc/testssl.1.html b/doc/testssl.1.html index 5a6c392..4746b25 100644 --- a/doc/testssl.1.html +++ b/doc/testssl.1.html @@ -123,9 +123,9 @@ linked OpenSSL binaries for major operating systems are supplied in ./bin/

2) standard cipher categories to give you upfront an idea for the ciphers supported

-

3) checks forward secrecy: ciphers and elliptical curves

+

3) server's cipher preferences (server order)

-

4) server preferences (server order)

+

4) forward secrecy: ciphers and elliptical curves

5) server defaults (certificate info, TLS extensions, session information)

@@ -133,9 +133,7 @@ linked OpenSSL binaries for major operating systems are supplied in ./bin/

7) vulnerabilities

-

8) testing each of 370 preconfigured ciphers

- -

9) client simulation

+

8) client simulation

OPTIONS AND PARAMETERS

diff --git a/doc/testssl.1.md b/doc/testssl.1.md index 8f29a92..ac54ec4 100644 --- a/doc/testssl.1.md +++ b/doc/testssl.1.md @@ -42,9 +42,9 @@ linked OpenSSL binaries for major operating systems are supplied in `./bin/`. 2) standard cipher categories to give you upfront an idea for the ciphers supported -3) checks forward secrecy: ciphers and elliptical curves +3) server's cipher preferences (server order?) -4) server preferences (server order) +4) forward secrecy: ciphers and elliptical curves 5) server defaults (certificate info, TLS extensions, session information) @@ -52,9 +52,7 @@ linked OpenSSL binaries for major operating systems are supplied in `./bin/`. 7) vulnerabilities -8) testing each of 370 preconfigured ciphers - -9) client simulation +8) client simulation ## OPTIONS AND PARAMETERS From 50d10d00f7afabc81ec142d1a56d9a0bcecea244 Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Mon, 27 Apr 2020 19:19:19 +0200 Subject: [PATCH 14/19] Add latest changes including the one since 3.0 --- CHANGELOG.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 289f81a..675f757 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,20 @@ ## Change Log +### Features implemented / improvements in 3.1dev + +* Extend Server (cipher) preference: always now in wide mode instead of running all ciphers in the end (per default) +* Improved compatibility with OpenSSL 3.0 +* Renamed PFS/perfect forward secrecy --> FS/forward secrecy +* Improved mass testing +* Align better colors of ciphers with standard cipherlists +* Added several ciphers to colored ciphers +* Percent output char problem fixed +* Several display/output fixes +* Security fix: DNS input +* Don't use external pwd anymore +* Rating (pending) + ### Features implemented / improvements in 3.0 * Full support of TLS 1.3, shows also drafts supported From 88c04f534525685da43f6d301e0be2f1a030274c Mon Sep 17 00:00:00 2001 From: Dirk Date: Tue, 28 Apr 2020 10:06:29 +0200 Subject: [PATCH 15/19] Relax the possible GPL license contradiction ... see also #1590 --- Readme.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Readme.md b/Readme.md index 7454f79..071828e 100644 --- a/Readme.md +++ b/Readme.md @@ -30,9 +30,12 @@ cryptographic flaws. ### License This software is free. You can use it under the terms of GPLv2, see LICENSE. -In addition starting from version 3.0rc1 if you're offering a scanner based on testssl.sh -as a public and / or paid service in the internet you need to mention to your audience that you're using -this program and where to get this program from. + +Attribution is important for the future of this project -- also in the +internet. Thus if you're offering a scanner based on testssl.sh as a public and/or +paid service in the internet you are strongly encouraged to mention to your audience +that you're using this program and where to get this program from. That helps us +to get bugfixes, other feedback and more contributions. ### Compatibility From 3db9d74c2162f334023ab49b7401e83204faefa9 Mon Sep 17 00:00:00 2001 From: David Cooper Date: Wed, 29 Apr 2020 10:13:22 -0400 Subject: [PATCH 16/19] Ticketbleed and TLS 1.3 run_ticketbleed() and sub_session_ticket_tls() each include one call to "$OPENSSL s_client". For each of these calls the expected response is a TLS 1.2 or earlier ServerHello. However, if $OPENSSL supports TLS 1.3, then a TLS 1.3 ClientHello will be sent. This commit fixes this problem in two ways. For the call in run_ticketbleed(), "-no_tls1_3" is added to the command line if "$OPENSSL" supports TLS 1.3. For the call in sub_session_ticket_tls(), this commit changes the function so that the same ClientHello version is sent as will sent by run_ticketbleed() via sockets. --- testssl.sh | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/testssl.sh b/testssl.sh index 995196f..b7f60ee 100755 --- a/testssl.sh +++ b/testssl.sh @@ -15134,13 +15134,14 @@ run_ccs_injection(){ } sub_session_ticket_tls() { + local tls_proto="$1" local sessticket_tls="" #FIXME: we likely have done this already before (either @ run_server_defaults() or at least the output # from a previous handshake) --> would save 1x connect. We have TLS_TICKET but not yet the ticket itself #FIXME #ATTENTION: we DO NOT use SNI here as we assume ticketbleed is a vulnerability of the TLS stack. If we'd do SNI here, we'd also need # it in the ClientHello of run_ticketbleed() otherwise the ticket will be different and the whole thing won't work! # - sessticket_tls="$($OPENSSL s_client $(s_client_options "$BUGS $OPTIMAL_PROTO $PROXY -connect $NODEIP:$PORT") $ERRFILE | awk '/TLS session ticket:/,/^$/' | awk '!/TLS session ticket/')" + sessticket_tls="$($OPENSSL s_client $(s_client_options "$BUGS $tls_proto $PROXY -connect $NODEIP:$PORT") $ERRFILE | awk '/TLS session ticket:/,/^$/' | awk '!/TLS session ticket/')" sessticket_tls="$(sed -e 's/^.* - /x/g' -e 's/ .*$//g' <<< "$sessticket_tls" | tr '\n' ',')" sed -e 's/ /,x/g' -e 's/-/,x/g' <<< "$sessticket_tls" @@ -15149,6 +15150,7 @@ sub_session_ticket_tls() { # see https://blog.filippo.io/finding-ticketbleed/ | https://filippo.io/ticketbleed/ run_ticketbleed() { + local tls_hexcode tls_proto="" local session_tckt_tls="" local -i len_ch=300 # fixed len of prepared clienthello below local sid="x00,x0B,xAD,xC0,xDE,x00," # some abitratry bytes @@ -15186,25 +15188,26 @@ run_ticketbleed() { fi if [[ 0 -eq $(has_server_protocol tls1) ]]; then - tls_hexcode="x03, x01" + tls_hexcode="x03, x01"; tls_proto="-tls1" elif [[ 0 -eq $(has_server_protocol tls1_1) ]]; then - tls_hexcode="x03, x02" + tls_hexcode="x03, x02"; tls_proto="-tls1_1" elif [[ 0 -eq $(has_server_protocol tls1_2) ]]; then - tls_hexcode="x03, x03" + tls_hexcode="x03, x03"; tls_proto="-tls1_2" elif [[ 0 -eq $(has_server_protocol ssl3) ]]; then - tls_hexcode="x03, x00" + tls_hexcode="x03, x00"; tls_proto="-ssl3" else # no protocol for some reason defined, determine TLS versions offered with a new handshake - $OPENSSL s_client $(s_client_options "$STARTTLS $BUGS -connect $NODEIP:$PORT $PROXY") >$TMPFILE 2>$ERRFILE $TMPFILE 2>$ERRFILE Date: Thu, 30 Apr 2020 10:26:56 -0400 Subject: [PATCH 17/19] Use $HAS_X25519 and $HAS_X448 generate_key_share_extension() and prepare_tls_clienthello() currently check the $OPENSSL version number to determine whether X25519 and X448 are supported. The commit changes these functions to use $HAS_X25519 and $HAS_X448. --- testssl.sh | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/testssl.sh b/testssl.sh index b7f60ee..a8a4bf4 100755 --- a/testssl.sh +++ b/testssl.sh @@ -13835,26 +13835,13 @@ generate_key_share_extension() { # with X25519 keys, so don't include the X25519 key share # if the server's response needs to be decrypted and an # older version of OpenSSL is being used. - if [[ $i -gt 12 ]] && [[ $group -eq 29 ]] && [[ "$2" == all ]]; then - [[ "$OSSL_NAME" =~ LibreSSL ]] && continue - if [[ $OSSL_VER_MAJOR.$OSSL_VER_MINOR != 1.1.0* ]] && \ - [[ $OSSL_VER_MAJOR.$OSSL_VER_MINOR != 1.1.1* ]] && \ - [[ $OSSL_VER_MAJOR.$OSSL_VER_MINOR != 3.0.0* ]]; then - continue - fi - fi + [[ $i -gt 12 ]] && [[ $group -eq 29 ]] && [[ "$2" == all ]] && ! "$HAS_X25519" && continue # Versions of OpenSSL prior to 1.1.1 cannot perform operations # with X448 keys, so don't include the X448 key share # if the server's response needs to be decrypted and an # older version of OpenSSL is being used. - if [[ $i -gt 12 ]] && [[ $group -eq 30 ]] && [[ "$2" == all ]]; then - [[ "$OSSL_NAME" =~ LibreSSL ]] && continue - if [[ $OSSL_VER_MAJOR.$OSSL_VER_MINOR != 1.1.1* ]] && \ - [[ $OSSL_VER_MAJOR.$OSSL_VER_MINOR != 3.0.0* ]]; then - continue - fi - fi + [[ $i -gt 12 ]] && [[ $group -eq 30 ]] && [[ "$2" == all ]] && ! "$HAS_X448" && continue # NOTE: The public keys could be extracted from the private keys # (TLS13_KEY_SHARES) using $OPENSSL, but only OpenSSL 1.1.0 and newer can @@ -14016,9 +14003,7 @@ prepare_tls_clienthello() { 00, 01, 00, 02, 00, 03, 00, 0f, 00, 10, 00, 11" elif [[ 0x$tls_low_byte -gt 0x03 ]]; then # Supported Groups Extension - if [[ ! "$process_full" =~ all ]] || ( [[ ! "$OSSL_NAME" =~ LibreSSL ]] && \ - ( [[ $OSSL_VER_MAJOR.$OSSL_VER_MINOR == 1.1.1* ]] || \ - [[ $OSSL_VER_MAJOR.$OSSL_VER_MINOR == 3.0.0* ]] ) ); then + if [[ ! "$process_full" =~ all ]] || ( "$HAS_X25519" && "$HAS_X448" ); then extension_supported_groups=" 00,0a, # Type: Supported Groups, see RFC 8446 00,10, 00,0e, # lengths @@ -14027,13 +14012,13 @@ prepare_tls_clienthello() { # OpenSSL prior to 1.1.1 does not support X448, so list it as the least # preferred option if the response needs to be decrypted, and do not # list it at all if the response MUST be decrypted. - elif [[ $OSSL_VER_MAJOR.$OSSL_VER_MINOR == 1.1.0* ]] && [[ "$process_full" == all+ ]]; then + elif "$HAS_X25519" && [[ "$process_full" == all+ ]]; then extension_supported_groups=" 00,0a, # Type: Supported Groups, see RFC 8446 00,0e, 00,0c, # lengths 00,1d, 00,17, 00,18, 00,19, 01,00, 01,01" - elif [[ $OSSL_VER_MAJOR.$OSSL_VER_MINOR == "1.1.0"* ]]; then + elif "$HAS_X25519"; then extension_supported_groups=" 00,0a, # Type: Supported Groups, see RFC 8446 00,10, 00,0e, # lengths From cb67d9141786656002682d1780122c221e1aa4b6 Mon Sep 17 00:00:00 2001 From: David Cooper Date: Thu, 30 Apr 2020 10:37:12 -0400 Subject: [PATCH 18/19] Improve compatibility with LibreSSL 3.0.2 and earlier This commit addresses two compatibility issues with LibreSSL. First, with LibreSSL, "$OPENSSL s_client" does not support the "-curves" option, so the "-groups" option needs to be used instead. Note that with LibreSSL, the command line "$OPENSSL s_client -groups $curve -connect invalid." will not work, as it will complain "no port defined," but will not indicate whether the specified curve is supported. Adding a port number fixes that problem. (There does not seem to be a need to include a port number for other tests, such as whether the "-curves" option itself is supported.) Second, including "-out -" in the command line for "$OPENSSL genpkey" causes LibreSSL to create a file with the name "-" if the algorithm is supported. This is not an issue at the moment, since LibreSSL's genpkey does not support X25519 or X448. However, both genpkey with both OpenSSL and LibreSSL uses stdout as the default output if no "-out" is specified, so the "-out -" is not necessary. --- testssl.sh | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/testssl.sh b/testssl.sh index b7f60ee..3125688 100755 --- a/testssl.sh +++ b/testssl.sh @@ -297,6 +297,7 @@ OSSL_VER_MINOR=0 OSSL_VER_APPENDIX="none" CLIENT_PROB_NO=1 HAS_DH_BITS=${HAS_DH_BITS:-false} # initialize openssl variables +HAS_CURVES=false OSSL_SUPPORTED_CURVES="" HAS_SSL2=false HAS_SSL3=false @@ -2034,6 +2035,7 @@ s_client_options() { # (e.g. client simulations) we replace it with the name which OpenSSL understands # This shouldn't be needed. We have this here as a last resort if [[ "$1" =~ " -curves " ]]; then + ! "$HAS_CURVES" && options="${options// -curves / -groups }" [[ "$1" =~ secp192r1 ]] && options="${options//secp192r1/prime192v1}" [[ "$1" =~ secp256r1 ]] && options="${options//secp256r1/prime256v1}" fi @@ -18035,6 +18037,7 @@ find_openssl_binary() { HAS_CIPHERSUITES=false HAS_COMP=false HAS_NO_COMP=false + HAS_CURVES=false OSSL_SUPPORTED_CURVES="" HAS_PKEY=false HAS_PKUTIL=false @@ -18067,10 +18070,10 @@ find_openssl_binary() { $OPENSSL s_client -tls1_3 -connect invalid. 2>&1 | grep -aiq "unknown option" || \ HAS_TLS13=true - $OPENSSL genpkey -algorithm X448 -out - 2>&1 | grep -aq "not found" || \ + $OPENSSL genpkey -algorithm X448 2>&1 | grep -aq "not found" || \ HAS_X448=true - $OPENSSL genpkey -algorithm X25519 -out - 2>&1 | grep -aq "not found" || \ + $OPENSSL genpkey -algorithm X25519 2>&1 | grep -aq "not found" || \ HAS_X25519=true $OPENSSL s_client -no_ssl2 -connect invalid. 2>&1 | grep -aiq "unknown option" || \ @@ -18090,10 +18093,18 @@ find_openssl_binary() { OPENSSL_NR_CIPHERS=$(count_ciphers "$(actually_supported_osslciphers 'ALL:COMPLEMENTOFALL' 'ALL')") - for curve in "${curves_ossl[@]}"; do - $OPENSSL s_client -curves $curve -connect invalid. 2>&1 | grep -Eiaq "Error with command|unknown option" - [[ $? -ne 0 ]] && OSSL_SUPPORTED_CURVES+=" $curve " - done + if $OPENSSL s_client -curves "${curves_ossl[0]}" -connect invalid. 2>&1 | grep -aiq "unknown option"; then + for curve in "${curves_ossl[@]}"; do + $OPENSSL s_client -groups $curve -connect invalid.:8443 2>&1 | grep -Eiaq "Error with command|unknown option|Failed to set groups" + [[ $? -ne 0 ]] && OSSL_SUPPORTED_CURVES+=" $curve " + done + else + HAS_CURVES=true + for curve in "${curves_ossl[@]}"; do + $OPENSSL s_client -curves $curve -connect invalid. 2>&1 | grep -Eiaq "Error with command|unknown option" + [[ $? -ne 0 ]] && OSSL_SUPPORTED_CURVES+=" $curve " + done + fi $OPENSSL pkey -help 2>&1 | grep -q Error || \ HAS_PKEY=true @@ -18423,6 +18434,7 @@ OSSL_VER_PLATFORM: $OSSL_VER_PLATFORM OPENSSL_NR_CIPHERS: $OPENSSL_NR_CIPHERS OPENSSL_CONF: $OPENSSL_CONF +HAS_CURVES: $HAS_CURVES OSSL_SUPPORTED_CURVES: $OSSL_SUPPORTED_CURVES HAS_IPv6: $HAS_IPv6 From a5a28d2457b6aa9ab2944057dec8adf2beded14a Mon Sep 17 00:00:00 2001 From: David Cooper Date: Thu, 30 Apr 2020 10:54:41 -0400 Subject: [PATCH 19/19] Improve LibreSSL 3.1.0 compatibility This commit addresses two compatibility issues with LibreSSL 3.1.0, which has added client support for TLS 1.3. The first issue is that LibreSSL has named the TLS 1.3 ciphers that it supports AEAD-AES256-GCM-SHA384, AEAD-CHACHA20-POLY1305-SHA256, and AEAD-AES128-GCM-SHA256, rather than using the OpenSSL names, which are TLS_AES_256_GCM_SHA384, TLS_CHACHA20_POLY1305_SHA256, and TLS_AES_128_GCM_SHA256. (Draft versions of OpenSSL 1.1.1 names these ciphers TLS13-AES-256-GCM-SHA384, TLS13-CHACHA20-POLY1305-SHA256, TLS13-AES-128-GCM-SHA256.) There are several places where testssl.sh checks whether a cipher suite is a TLS 1.3 cipher by checking whether its OpenSSL name begins with "TLS_" (or "TLS13"). In order to work with LibreSSL 3.1.0, these checks also need to consider names that begin with "AEAD-" to be TLS 1.3 ciphers. Second, in sub_session_resumption() there is code that adds "-no_ssl2" to the "$OPENSSL s_client" command line if that option is supported. If "-no_ssl2" is not supported, then other protocol information is added to the command line. I believe this code was written with the assumption that any version of OpenSSL that supports "-no_ssl2" does not support TLS 1.3. However, LibreSSL 3.1.0 supports both. So, this commit changes the code to add the "-no_ssl2" option only if TLS 1.3 is not supported. --- testssl.sh | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/testssl.sh b/testssl.sh index b7f60ee..05515fa 100755 --- a/testssl.sh +++ b/testssl.sh @@ -3575,7 +3575,7 @@ run_cipher_match(){ tls13_ciphers_to_test="" for (( i=bundle*bundle_size; i < end_of_bundle; i++ )); do if ! "${ciphers_found2[i]}"; then - if [[ "${ciph2[i]}" == TLS13* ]] || [[ "${ciph2[i]}" == TLS_* ]]; then + if [[ "${ciph2[i]}" == TLS13* ]] || [[ "${ciph2[i]}" == TLS_* ]] || [[ "${ciph2[i]}" == AEAD-* ]]; then tls13_ciphers_to_test+=":${ciph2[i]}" else ciphers_to_test+=":${ciph2[i]}" @@ -3593,7 +3593,7 @@ run_cipher_match(){ [[ $i -eq $end_of_bundle ]] && break i=${index[i]} ciphers_found[i]=true - if [[ "$cipher" == TLS13* ]] || [[ "$cipher" == TLS_* ]]; then + if [[ "$cipher" == TLS13* ]] || [[ "$cipher" == TLS_* ]] || [[ "$cipher" == AEAD-* ]]; then kx[i]="$(read_dhtype_from_file $TMPFILE)" fi if [[ ${kx[i]} == "Kx=ECDH" ]] || [[ ${kx[i]} == "Kx=DH" ]] || [[ ${kx[i]} == "Kx=EDH" ]]; then @@ -3849,7 +3849,7 @@ run_allciphers() { tls13_ciphers_to_test="" for (( i=bundle*bundle_size; i < end_of_bundle; i++ )); do if ! "${ciphers_found2[i]}"; then - if [[ "${ciph2[i]}" == TLS13* ]] || [[ "${ciph2[i]}" == TLS_* ]]; then + if [[ "${ciph2[i]}" == TLS13* ]] || [[ "${ciph2[i]}" == TLS_* ]] || [[ "${ciph2[i]}" == AEAD-* ]]; then tls13_ciphers_to_test+=":${ciph2[i]}" else ciphers_to_test+=":${ciph2[i]}" @@ -3867,7 +3867,7 @@ run_allciphers() { [[ $i -eq $end_of_bundle ]] && break i=${index[i]} ciphers_found[i]=true - if [[ "$cipher" == TLS13* ]] || [[ "$cipher" == TLS_* ]]; then + if [[ "$cipher" == TLS13* ]] || [[ "$cipher" == TLS_* ]] || [[ "$cipher" == AEAD-* ]]; then kx[i]="$(read_dhtype_from_file $TMPFILE)" fi if [[ ${kx[i]} == Kx=ECDH ]] || [[ ${kx[i]} == Kx=DH ]] || [[ ${kx[i]} == Kx=EDH ]]; then @@ -4055,9 +4055,9 @@ ciphers_by_strength() { fi while read hexc n ciph[nr_ciphers] sslvers kx[nr_ciphers] auth enc[nr_ciphers] mac export2[nr_ciphers]; do if [[ "$proto" == -tls1_3 ]]; then - [[ "${ciph[nr_ciphers]}" == TLS13* ]] || [[ "${ciph[nr_ciphers]}" == TLS_* ]] || continue + [[ "${ciph[nr_ciphers]}" == TLS13* ]] || [[ "${ciph[nr_ciphers]}" == TLS_* ]] || [[ "${ciph[nr_ciphers]}" == AEAD-* ]] || continue elif [[ "$proto" == -tls1_2 ]]; then - if [[ "${ciph[nr_ciphers]}" == TLS13* ]] || [[ "${ciph[nr_ciphers]}" == TLS_* ]]; then + if [[ "${ciph[nr_ciphers]}" == TLS13* ]] || [[ "${ciph[nr_ciphers]}" == TLS_* ]] || [[ "${ciph[nr_ciphers]}" == AEAD-* ]]; then continue fi elif [[ "${ciph[nr_ciphers]}" == *-SHA256 ]] || [[ "${ciph[nr_ciphers]}" == *-SHA384 ]] || \ @@ -6203,7 +6203,7 @@ sub_session_resumption() { fi fi "$CLIENT_AUTH" && return 6 - if "$HAS_NO_SSL2"; then + if ! "$HAS_TLS13" && "$HAS_NO_SSL2"; then addcmd+=" -no_ssl2" else protocol=${protocol/\./_} @@ -9586,7 +9586,7 @@ run_fs() { tls13_ciphers_to_test="" for (( i=0; i < nr_supported_ciphers; i++ )); do if ! "${ciphers_found[i]}" && "${ossl_supported[i]}"; then - if [[ "${ciph[i]}" == TLS13* ]] || [[ "${ciph[i]}" == TLS_* ]]; then + if [[ "${ciph[i]}" == TLS13* ]] || [[ "${ciph[i]}" == TLS_* ]] || [[ "${ciph[i]}" == AEAD-* ]]; then tls13_ciphers_to_test+=":${ciph[i]}" else ciphers_to_test+=":${ciph[i]}" @@ -9603,7 +9603,7 @@ run_fs() { done [[ $i -eq $nr_supported_ciphers ]] && break ciphers_found[i]=true - if [[ "$fs_cipher" == TLS13* ]] || [[ "$fs_cipher" == TLS_* ]]; then + if [[ "$fs_cipher" == TLS13* ]] || [[ "$fs_cipher" == TLS_* ]] || [[ "$fs_cipher" == AEAD-* ]]; then fs_tls13_offered=true "$WIDE" && kx[i]="$(read_dhtype_from_file $TMPFILE)" fi @@ -9662,11 +9662,12 @@ run_fs() { fi fs_ciphers+="$fs_cipher " - if [[ "${ciph[i]}" == ECDHE-* ]] || [[ "${ciph[i]}" == TLS13* ]] || [[ "${ciph[i]}" == TLS_* ]] || ( "$using_sockets" && [[ "${rfc_ciph[i]}" == TLS_ECDHE_* ]] ); then + if [[ "${ciph[i]}" == ECDHE-* ]] || [[ "${ciph[i]}" == TLS13* ]] || [[ "${ciph[i]}" == TLS_* ]] || \ + [[ "${ciph[i]}" == AEAD-* ]] || ( "$using_sockets" && [[ "${rfc_ciph[i]}" == TLS_ECDHE_* ]] ); then ecdhe_offered=true ecdhe_cipher_list_hex+=", ${hexcode[i]}" if [[ "${ciph[i]}" != "-" ]]; then - if [[ "${ciph[i]}" == TLS13* ]] || [[ "${ciph[i]}" == TLS_* ]]; then + if [[ "${ciph[i]}" == TLS13* ]] || [[ "${ciph[i]}" == TLS_* ]] || [[ "${ciph[i]}" == AEAD-* ]]; then tls13_cipher_list+=":$fs_cipher" else ecdhe_cipher_list+=":$fs_cipher" @@ -9676,7 +9677,7 @@ run_fs() { if [[ "${ciph[i]}" == "DHE-"* ]] || ( "$using_sockets" && [[ "${rfc_ciph[i]}" == "TLS_DHE_"* ]] ); then ffdhe_offered=true ffdhe_cipher_list_hex+=", ${hexcode[i]}" - elif [[ "${ciph[i]}" == TLS13* ]] || [[ "${ciph[i]}" == TLS_* ]]; then + elif [[ "${ciph[i]}" == TLS13* ]] || [[ "${ciph[i]}" == TLS_* ]] || [[ "${ciph[i]}" == AEAD-* ]]; then ffdhe_cipher_list_hex+=", ${hexcode[i]}" fi fi