From 1d4622ebab69e499f532d6217f699b7a37970068 Mon Sep 17 00:00:00 2001 From: David Cooper Date: Fri, 6 May 2016 15:12:53 -0400 Subject: [PATCH] Additional checks in run_protocols() One server I am testing responds to an SSLv3 ClientHello with TLSv1.2. If tls_sockets is being used, then testssl.sh responds with "#FIXME: downgraded. still missing a test case here." This PR fixes that, and in general checks the responses in run_protocols() more closely. If tls_sockets is being used and the connection fails even though the server supports an earlier version of SSL/TLS, then it flags an error. If tls_sockets returns 2, then it verifies that $DETECTED_TLS_VERSION is equal to the highest version number supported by the server (that is also less than the version number in the ClientHello). In addition, in order to test servers' support for version negotiation, it adds a new test that sends a TLSv1.4 ClientHello and verifies that the server responds with the highest version number that it supports. (This test only runs if both $using_sockets and $EXPERIMENTAL are true and server actually supports some version of SSL/TLS other than SSLv2.) --- testssl.sh | 195 +++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 161 insertions(+), 34 deletions(-) diff --git a/testssl.sh b/testssl.sh index 8d133d4..1869d06 100755 --- a/testssl.sh +++ b/testssl.sh @@ -2224,31 +2224,34 @@ run_protocols() { local using_sockets=true local supported_no_ciph1="supported but couldn't detect a cipher (may need debugging)" local supported_no_ciph2="supported but couldn't detect a cipher" - local via="" + local latest_supported="" # version.major and version.minor of highest version supported by the server. + local detected_version_string latest_supported_string + local extra_spaces="" outln; pr_headline " Testing protocols " - via="Protocol tested " #FIXME: use PROTOS_OFFERED here if $SSL_NATIVE; then using_sockets=false pr_headlineln "(via native openssl)" - via+="via native openssl" else if [[ -n "$STARTTLS" ]]; then pr_headlineln "(via openssl, SSLv2 via sockets) " - via+="via openssl, SSLv2 via sockets" using_sockets=false else using_sockets=true - pr_headlineln "(via sockets except TLS 1.2, SPDY+HTTP2) " - via+="via sockets except for TLS1.2, SPDY+HTTP2" + if $EXPERIMENTAL; then + pr_headlineln "(via sockets except SPDY+HTTP2) " + extra_spaces=" " + else + pr_headlineln "(via sockets except TLS 1.2, SPDY+HTTP2) " + fi fi fi outln - pr_bold " SSLv2 "; + pr_bold " SSLv2 $extra_spaces"; if ! $SSL_NATIVE; then sslv2_sockets #FIXME: messages need to be moved to this higher level else @@ -2272,7 +2275,7 @@ run_protocols() { esac fi - pr_bold " SSLv3 "; + pr_bold " SSLv3 $extra_spaces"; if $using_sockets; then tls_sockets "00" "$TLS_CIPHER" else @@ -2282,14 +2285,22 @@ run_protocols() { 0) pr_svrty_highln "offered (NOT ok)" fileout "sslv3" "NOT ok" "SSLv3 is offered (NOT ok)" + latest_supported="0300" + latest_supported_string="SSLv3" ;; 1) pr_done_bestln "not offered (OK)" fileout "sslv3" "OK" "SSLv3 is not offered (OK)" ;; 2) - pr_warningln "#FIXME: downgraded. still missing a test case here" - fileout "sslv3" "WARN" "SSLv3: #FIXME: downgraded. still missing a test case here" + if [[ "$DETECTED_TLS_VERSION" == 03* ]]; then + detected_version_string="TLSv1.$((0x$DETECTED_TLS_VERSION-0x0301))" + pr_svrty_criticalln "server responded with higher version number ($detected_version_string) than requested by client (NOT ok)" + fileout "sslv3" "NOT ok" "SSLv3: server responded with higher version number ($detected_version_string) than requested by client (NOT ok)" + else + pr_svrty_criticalln "server responded with version number ${DETECTED_TLS_VERSION:0:2}.${DETECTED_TLS_VERSION:2:2} (NOT ok)" + fileout "sslv3" "NOT ok" "SSLv3: server responded with version number ${DETECTED_TLS_VERSION:0:2}.${DETECTED_TLS_VERSION:2:2} (NOT ok)" + fi ;; 5) fileout "sslv3" "WARN" "SSLv3 is $supported_no_ciph1" @@ -2301,7 +2312,7 @@ run_protocols() { ;; # no local support esac - pr_bold " TLS 1 "; + pr_bold " TLS 1 $extra_spaces"; if $using_sockets; then tls_sockets "01" "$TLS_CIPHER" else @@ -2311,16 +2322,33 @@ run_protocols() { 0) outln "offered" fileout "tls1" "INFO" "TLSv1.0 is offered" + latest_supported="0301" + latest_supported_string="TLSv1.0" ;; # nothing wrong with it -- per se 1) - outln "not offered" - fileout "tls1" "INFO" "TLSv1.0 is not offered" - ;; # neither good or bad + out "not offered" + if ! $using_sockets || [[ -z $latest_supported ]]; then + outln + fileout "tls1" "INFO" "TLSv1.0 is not offered" # neither good or bad + else + pr_svrty_criticalln " -- connection failed rather than downgrading to $latest_supported_string (NOT ok)" + fileout "tls1" "NOT ok" "TLSv1.0: connection failed rather than downgrading to $latest_supported_string (NOT ok)" + fi + ;; 2) pr_svrty_medium "not offered (NOT ok)" - [[ $DEBUG -eq 1 ]] && out " -- downgraded" - outln - fileout "tls1" "NOT ok" "TLSv1.0 is not offered, and downgraded to SSL (NOT ok)" + if [[ "$DETECTED_TLS_VERSION" == "0300" ]]; then + [[ $DEBUG -eq 1 ]] && out " -- downgraded" + outln + fileout "tls1" "NOT ok" "TLSv1.0 is not offered, and downgraded to SSL (NOT ok)" + elif [[ "$DETECTED_TLS_VERSION" == 03* ]]; then + detected_version_string="TLSv1.$((0x$DETECTED_TLS_VERSION-0x0301))" + pr_svrty_criticalln " -- server responded with higher version number ($detected_version_string) than requested by client" + fileout "tls1" "NOT ok" "TLSv1.0: server responded with higher version number ($detected_version_string) than requested by client (NOT ok)" + else + pr_svrty_criticalln " -- server responded with version number ${DETECTED_TLS_VERSION:0:2}.${DETECTED_TLS_VERSION:2:2}" + fileout "tls1" "NOT ok" "TLSv1.0: server responded with version number ${DETECTED_TLS_VERSION:0:2}.${DETECTED_TLS_VERSION:2:2} (NOT ok)" + fi ;; 5) outln "$supported_no_ciph1" # protocol ok, but no cipher @@ -2331,7 +2359,7 @@ run_protocols() { ;; # no local support esac - pr_bold " TLS 1.1 "; + pr_bold " TLS 1.1 $extra_spaces"; if $using_sockets; then tls_sockets "02" "$TLS_CIPHER" else @@ -2341,16 +2369,36 @@ run_protocols() { 0) outln "offered" fileout "tls1_1" "INFO" "TLSv1.1 is offered" + latest_supported="0302" + latest_supported_string="TLSv1.1" ;; # nothing wrong with it 1) - outln "not offered" - fileout "tls1_1" "INFO" "TLSv1.1 is not offered" - ;; # neither good or bad + out "not offered" + if ! $using_sockets || [[ -z $latest_supported ]]; then + outln + fileout "tls1_1" "INFO" "TLSv1.1 is not offered" # neither good or bad + else + pr_svrty_criticalln " -- connection failed rather than downgrading to $latest_supported_string" + fileout "tls1_1" "NOT ok" "TLSv1.1: connection failed rather than downgrading to $latest_supported_string (NOT ok)" + fi + ;; 2) out "not offered" - [[ $DEBUG -eq 1 ]] && out " -- downgraded" - outln - fileout "tls1_1" "NOT ok" "TLSv1.1 is not offered, and downgraded to a weaker protocol (NOT ok)" + if [[ "$DETECTED_TLS_VERSION" == "$latest_supported" ]]; then + [[ $DEBUG -eq 1 ]] && out " -- downgraded" + outln + fileout "tls1_1" "NOT ok" "TLSv1.1 is not offered, and downgraded to a weaker protocol (NOT ok)" + elif [[ "$DETECTED_TLS_VERSION" == "0300" ]] && [[ "$latest_supported" == "0301" ]]; then + pr_svrty_criticalln " -- server supports TLSv1.0, but downgraded to SSLv3 (NOT ok)" + fileout "tls1_1" "NOT ok" "TLSv1.1 is not offered, and downgraded to SSLv3 rather than TLSv1.0 (NOT ok)" + elif [[ "$DETECTED_TLS_VERSION" == 03* ]] && [[ 0x$DETECTED_TLS_VERSION -gt 0x0302 ]]; then + detected_version_string="TLSv1.$((0x$DETECTED_TLS_VERSION-0x0301))" + pr_svrty_criticalln " -- server responded with higher version number ($detected_version_string) than requested by client (NOT ok)" + fileout "tls1_1" "NOT ok" "TLSv1.1 is not offered, server responded with higher version number ($detected_version_string) than requested by client (NOT ok)" + else + pr_svrty_criticalln " -- server responded with version number ${DETECTED_TLS_VERSION:0:2}.${DETECTED_TLS_VERSION:2:2} (NOT ok)" + fileout "tls1" "NOT ok" "TLSv1.1: server responded with version number ${DETECTED_TLS_VERSION:0:2}.${DETECTED_TLS_VERSION:2:2} (NOT ok)" + fi ;; 5) outln "$supported_no_ciph1" @@ -2361,7 +2409,7 @@ run_protocols() { ;; # no local support esac - pr_bold " TLS 1.2 "; + pr_bold " TLS 1.2 $extra_spaces"; if $using_sockets && $EXPERIMENTAL; then #TODO: IIS servers do have a problem here with our handshake tls_sockets "03" "$TLS12_CIPHER" else @@ -2371,16 +2419,40 @@ run_protocols() { 0) pr_done_bestln "offered (OK)" fileout "tls1_2" "OK" "TLSv1.2 is offered (OK)" + latest_supported="0303" + latest_supported_string="TLSv1.2" ;; # GCM cipher in TLS 1.2: very good! 1) - pr_svrty_mediumln "not offered (NOT ok)" - fileout "tls1_2" "NOT ok" "TLSv1.2 is not offered (NOT ok)" - ;; # no GCM, penalty + pr_svrty_medium "not offered (NOT ok)" + if ! $using_sockets || ! $EXPERIMENTAL || [[ -z $latest_supported ]]; then + outln + fileout "tls1_2" "NOT ok" "TLSv1.2 is not offered (NOT ok)" # no GCM, penalty + else + pr_svrty_criticalln " -- connection failed rather than downgrading to $latest_supported_string" + fileout "tls1_1" "NOT ok" "TLSv1.2: connection failed rather than downgrading to $latest_supported_string" + fi + ;; 2) - pr_svrty_medium "not offered (NOT ok)" - [[ $DEBUG -eq 1 ]] && out " -- downgraded" - outln - fileout "tls1_2" "NOT ok" "TLSv1.2 is not offered and downgraded to a weaker protocol (NOT ok)" + pr_svrty_medium "not offered (NOT ok)" + if [[ "$DETECTED_TLS_VERSION" == "0300" ]]; then + detected_version_string="SSLv3" + elif [[ "$DETECTED_TLS_VERSION" == 03* ]]; then + detected_version_string="TLSv1.$((0x$DETECTED_TLS_VERSION-0x0301))" + fi + if [[ "$DETECTED_TLS_VERSION" == "$latest_supported" ]]; then + [[ $DEBUG -eq 1 ]] && out " -- downgraded" + outln + fileout "tls1_2" "NOT ok" "TLSv1.2 is not offered and downgraded to a weaker protocol (NOT ok)" + elif [[ "$DETECTED_TLS_VERSION" == 03* ]] && [[ 0x$DETECTED_TLS_VERSION -lt 0x$latest_supported ]]; then + pr_svrty_criticalln " -- server supports $latest_supported_string, but downgraded to $detected_version_string" + fileout "tls1_2" "NOT ok" "TLSv1.2 is not offered, and downgraded to $detected_version_string rather than $latest_supported_string (NOT ok)" + elif [[ "$DETECTED_TLS_VERSION" == 03* ]] && [[ 0x$DETECTED_TLS_VERSION -gt 0x0303 ]]; then + pr_svrty_criticalln " -- server responded with higher version number ($detected_version_string) than requested by client" + fileout "tls1_2" "NOT ok" "TLSv1.2 is not offered, server responded with higher version number ($detected_version_string) than requested by client (NOT ok)" + else + pr_svrty_criticalln " -- server responded with version number ${DETECTED_TLS_VERSION:0:2}.${DETECTED_TLS_VERSION:2:2}" + fileout "tls1" "NOT ok" "TLSv1.2: server responded with version number ${DETECTED_TLS_VERSION:0:2}.${DETECTED_TLS_VERSION:2:2} (NOT ok)" + fi ;; 5) outln "$supported_no_ciph1" @@ -2390,6 +2462,55 @@ run_protocols() { fileout "tls1_2" "INFO" "TLSv1.2 is not tested due to lack of local support" ;; # no local support esac + + # Testing version negotiation. RFC 5246, Appendix E.1, states: + # + # If a TLS server receives a ClientHello containing a version number + # greater than the highest version supported by the server, it MUST + # reply according to the highest version supported by the server. + if [[ -n $latest_supported ]] && $using_sockets && $EXPERIMENTAL; then + pr_bold " Version Negotiation "; + tls_sockets "05" "$TLS12_CIPHER" + case $? in + 0) + pr_svrty_criticalln "server claims support for non-existent TLSv1.4" + fileout "TLS Version Negotiation" "NOT ok" "Server claims support for non-existent TLSv1.4 (NOT ok)" + ;; + 1) + pr_svrty_criticalln "version negotiation did not work -- connection failed rather than downgrading to $latest_supported_string (NOT ok)" + fileout "TLS Version Negotiation" "NOT ok" "Version negotiation did not work -- connection failed rather than downgrading to $latest_supported_string (NOT ok)" + ;; + 2) + case $DETECTED_TLS_VERSION in + 0304) + pr_svrty_criticalln "server claims support for TLSv1.3, which is still a working draft (NOT ok)" + fileout "TLS Version Negotiation" "NOT ok" "Server claims support for TLSv1.3, which is still a working draft (NOT ok)" + ;; + 0303|0302|0301|0300) + if [[ "$DETECTED_TLS_VERSION" == "0300" ]]; then + detected_version_string="SSLv3" + else + detected_version_string="TLSv1.$((0x$DETECTED_TLS_VERSION-0x0301))" + fi + if [[ 0x$DETECTED_TLS_VERSION -lt 0x$latest_supported ]]; then + pr_svrty_criticalln "server supports $latest_supported_string, but downgraded to $detected_version_string (NOT ok)" + fileout "TLS Version Negotiation" "NOT ok" "Downgraded to $detected_version_string rather than $latest_supported_string (NOT ok)" + else + pr_done_bestln "downgraded to $detected_version_string (OK)" + fileout "TLS Version Negotiation" "OK" "Downgraded to $detected_version_string" + fi + ;; + *) + pr_svrty_criticalln "server responded with version number ${DETECTED_TLS_VERSION:0:2}.${DETECTED_TLS_VERSION:2:2} (NOT ok)" + fileout "TLS Version Negotiation" "NOT ok" "TLSv1.4: server responded with version number ${DETECTED_TLS_VERSION:0:2}.${DETECTED_TLS_VERSION:2:2} (NOT ok)" + ;; + esac ;; + 5) + pr_svrty_criticalln "server claims support for non-existent TLSv1.4 (NOT ok)" + fileout "TLS Version Negotiation" "NOT ok" "Server claims support for non-existent TLSv1.4 (NOT ok)" + ;; + esac + fi return 0 } @@ -3689,8 +3810,11 @@ http2_pre(){ run_spdy() { local tmpstr local -i ret=0 + extra_spaces="" - pr_bold " SPDY/NPN " + ! $SSL_NATIVE && [[ -z "$STARTTLS" ]] && $EXPERIMENTAL && extra_spaces=" " + + pr_bold " SPDY/NPN $extra_spaces" if ! spdy_pre ; then outln return 0 @@ -3727,8 +3851,11 @@ run_http2() { local -i ret=0 local had_alpn_proto=false local alpn_finding="" + extra_spaces="" - pr_bold " HTTP2/ALPN " + ! $SSL_NATIVE && [[ -z "$STARTTLS" ]] && $EXPERIMENTAL && extra_spaces=" " + + pr_bold " HTTP2/ALPN $extra_spaces" if ! http2_pre ; then outln return 0