From ad5590a444b0dfcf9fbca6f19165d6e3bed8ab75 Mon Sep 17 00:00:00 2001
From: David Cooper <dcooper16@gmail.com>
Date: Wed, 4 Jan 2017 10:19:11 -0500
Subject: [PATCH 1/4] run_server_defaults() bugfix

If `determine_tls_extensions()` does not create a temporary file (`$TEMPDIR/$NODEIP.determine_tls_extensions.txt`) then `run_server_defaults()` will display error messages when an attempt is made to copy this file or to search (grep) it. This may happen if `$OPTIMAL_PROTO` is `-ssl2` or if `determine_tls_extensions()` uses sockets and `parse_tls_serverhello()` encountered an error and did not create a temporary file (`$TEMPDIR/$NODEIP.parse_tls_serverhello.txt`). This PR fixes this by only trying to copy and search `$TEMPDIR/$NODEIP.determine_tls_extensions.txt` is `$OPTIMAL_PROTO` is not `-ssl2` and `determine_tls_extensions()` was successful (return value 0).
---
 testssl.sh | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/testssl.sh b/testssl.sh
index 80a8ac8..fc6287e 100755
--- a/testssl.sh
+++ b/testssl.sh
@@ -6097,11 +6097,12 @@ run_server_defaults() {
      done
 
      determine_tls_extensions
+     if [[ $? -eq 0 ]] && [[ "$OPTIMAL_PROTO" != "-ssl2" ]]; then
+          cp "$TEMPDIR/$NODEIP.determine_tls_extensions.txt" $TMPFILE
+          >$ERRFILE
 
-     cp "$TEMPDIR/$NODEIP.determine_tls_extensions.txt" $TMPFILE
-     >$ERRFILE
-
-     [[ -z "$sessticket_str" ]] && sessticket_str=$(grep -aw "session ticket" $TMPFILE | grep -a lifetime)
+          [[ -z "$sessticket_str" ]] && sessticket_str=$(grep -aw "session ticket" $TMPFILE | grep -a lifetime)
+     fi
 
      outln
      pr_headlineln " Testing server defaults (Server Hello) "

From d66e5ec0d7b0b307cbb94405866336288697f3ef Mon Sep 17 00:00:00 2001
From: David Cooper <dcooper16@gmail.com>
Date: Wed, 4 Jan 2017 10:47:36 -0500
Subject: [PATCH 2/4] sslv2_sockets() bug fixes

This PR fixes a few bugs in `sslv2_sockets()`. The main issue is that a server may not send the entire ServerHello in a single packet. If it doesn't and the full response is being parsed (i.e., certificate and list of ciphers), then `parse_sslv2_serverhello()` will encounter errors, since it assumes that it has the entire ServerHello. This PR compares the length of the response to the length of the ServerHello as specified in the first two bytes of the response and requests more data from the server if the response appears incomplete.

This PR also modifies `parse_sslv2_serverhello()` to check for more errors. It compares the length of the response it has been provided to the specified length (`$v2_hello_length`) and returns an error if the response is shorter than `$v2_hello_length` and the full response is supposed to be parsed. It will also check whether there was an error in converting the certificate from DER to PEM format and will return an error if there was (and it will suppress the error message).
---
 testssl.sh | 40 +++++++++++++++++++++++++++++++++++++---
 1 file changed, 37 insertions(+), 3 deletions(-)

diff --git a/testssl.sh b/testssl.sh
index 80a8ac8..5c133d1 100755
--- a/testssl.sh
+++ b/testssl.sh
@@ -7063,6 +7063,7 @@ parse_sslv2_serverhello() {
      "$parse_complete" && echo "======================================" > $TMPFILE
 
      v2_hello_ascii=$(hexdump -v -e '16/1 "%02X"' $1)
+     v2_hello_ascii="${v2_hello_ascii%%[!0-9A-F]*}"
      [[ "$DEBUG" -ge 5 ]] && echo "$v2_hello_ascii"
      if [[ -z "$v2_hello_ascii" ]]; then
           ret=0                                      # 1 line without any blanks: no server hello received
@@ -7094,6 +7095,10 @@ parse_sslv2_serverhello() {
                echo "SSLv2 certificate length:  0x$v2_hello_cert_length"
                echo "SSLv2 cipher spec length:  0x$v2_hello_cipherspec_length"
           fi
+
+          if "$parse_complete" && [[ 2*$(hex2dec "$v2_hello_length") -ne ${#v2_hello_ascii}-4 ]]; then
+               ret=7
+          fi
      fi
 
      "$parse_complete" || return $ret
@@ -7101,11 +7106,15 @@ parse_sslv2_serverhello() {
      rm -f $HOSTCERT $TEMPDIR/intermediatecerts.pem
      if [[ $ret -eq 3 ]]; then
           certificate_len=2*$(hex2dec "$v2_hello_cert_length")
-     
+
           if [[ "$v2_cert_type" == "01" ]] && [[ "$v2_hello_cert_length" != "00" ]]; then
                tmp_der_certfile=$(mktemp $TEMPDIR/der_cert.XXXXXX) || return $ret
                asciihex_to_binary_file "${v2_hello_ascii:26:certificate_len}" "$tmp_der_certfile"
-               $OPENSSL x509 -inform DER -in $tmp_der_certfile -outform PEM -out $HOSTCERT
+               $OPENSSL x509 -inform DER -in $tmp_der_certfile -outform PEM -out $HOSTCERT 2>$ERRFILE
+               if [[ $? -ne 0 ]]; then
+                    debugme echo "Malformed certificate in ServerHello."
+                    return 1
+               fi
                rm $tmp_der_certfile
                get_pub_key_size
                echo "======================================" >> $TMPFILE
@@ -8100,6 +8109,13 @@ sslv2_sockets() {
      local ret
      local client_hello cipher_suites len_client_hello
      local len_ciph_suites_byte len_ciph_suites
+     local server_hello sock_reply_file2
+     local -i response_len server_hello_len
+     local parse_complete=false
+
+     if [[ "$2" == "true" ]]; then
+          parse_complete=true
+     fi
 
      if [[ -n "$1" ]]; then
           cipher_suites="$1"
@@ -8140,13 +8156,31 @@ sslv2_sockets() {
      socksend_sslv2_clienthello "$client_hello"
 
      sockread_serverhello 32768
+     if "$parse_complete"; then
+          server_hello=$(hexdump -v -e '16/1 "%02X"' "$SOCK_REPLY_FILE")
+          server_hello_len=2+$(hex2dec "${server_hello:1:3}")
+          response_len=$(wc -c "$SOCK_REPLY_FILE" | awk '{ print $1 }')
+          for (( 1; response_len < server_hello_len; 1 )); do
+               sock_reply_file2=$(mktemp $TEMPDIR/ddreply.XXXXXX) || return 7
+               mv "$SOCK_REPLY_FILE" "$sock_reply_file2"
+
+               debugme echo "requesting more server hello data..."
+               socksend "" $USLEEP_SND
+               sockread_serverhello 32768
+
+               [[ ! -s "$SOCK_REPLY_FILE" ]] && break
+               cat "$SOCK_REPLY_FILE" >> "$sock_reply_file2"
+               mv "$sock_reply_file2" "$SOCK_REPLY_FILE"
+               response_len=$(wc -c "$SOCK_REPLY_FILE" | awk '{ print $1 }')
+          done
+     fi
      debugme outln "reading server hello... "
      if [[ "$DEBUG" -ge 4 ]]; then
           hexdump -C "$SOCK_REPLY_FILE" | head -6
           outln
      fi
 
-     parse_sslv2_serverhello "$SOCK_REPLY_FILE" "$2"
+     parse_sslv2_serverhello "$SOCK_REPLY_FILE" "$parse_complete"
      ret=$?
 
      close_socket

From 70e6e289e1572770d52572350dc4e7ff85d6e86e Mon Sep 17 00:00:00 2001
From: Todd Swatling <todd.swatling@gmail.com>
Date: Thu, 5 Jan 2017 14:20:19 -0500
Subject: [PATCH 3/4] removed trailing spaces

---
 testssl.sh | 41 ++++++++++++++++++++---------------------
 1 file changed, 20 insertions(+), 21 deletions(-)

diff --git a/testssl.sh b/testssl.sh
index 80a8ac8..fc010c3 100755
--- a/testssl.sh
+++ b/testssl.sh
@@ -148,7 +148,7 @@ BUGS=${BUGS:-""}                        # -bugs option from openssl, needed for
 DEBUG=${DEBUG:-0}                       # 1: normal putput the files in /tmp/ are kept for further debugging purposes
                                         # 2: list more what's going on , also lists some errors of connections
                                         # 3: slight hexdumps + other info,
-                                        # 4: display bytes sent via sockets 
+                                        # 4: display bytes sent via sockets
                                         # 5: display bytes received via sockets
                                         # 6: whole 9 yards
 FAST=${FAST:-false}                     # preference: show only first cipher, run_allciphers with openssl instead of sockets
@@ -159,7 +159,7 @@ CSVFILE=${CSVFILE:-""}                  # csvfile if used
 APPEND=${APPEND:-false}                 # append to csv/json file instead of overwriting it
 GIVE_HINTS=false                   # give an addtional info to findings
 HAS_IPv6=${HAS_IPv6:-false}             # if you have OpenSSL with IPv6 support AND IPv6 networking set it to yes
-UNBRACKTD_IPV6=${UNBRACKTD_IPV6:-false} # some versions of OpenSSL (like Gentoo) don't support [bracketed] IPv6 addresses 
+UNBRACKTD_IPV6=${UNBRACKTD_IPV6:-false} # some versions of OpenSSL (like Gentoo) don't support [bracketed] IPv6 addresses
 SERVER_SIZE_LIMIT_BUG=false             # Some servers have either a ClientHello total size limit or cipher limit of ~128 ciphers (e.g. old ASAs)
 
 # tuning vars, can not be set by a cmd line switch
@@ -556,7 +556,7 @@ declare TLS_CIPHER_OSSL_SUPPORTED=()
 
 ###### output functions ######
 # a little bit of sanitzing with bash internal search&replace -- otherwise printf will hiccup at '%' and '--' does the rest.
-out(){ 
+out(){
 #     if [[ "$BASH_VERSINFO" -eq 4 ]]; then
           printf -- "%b" "${1//%/%%}"
 #     else
@@ -590,10 +590,10 @@ pr_greyln()     { pr_grey "$1"; outln; }
 
 pr_done_good()       { [[ "$COLOR" -eq 2 ]] && ( "$COLORBLIND" && out "\033[0;34m$1" || out "\033[0;32m$1" ) || out "$1"; pr_off; }   # litegreen (liteblue), This is good
 pr_done_goodln()     { pr_done_good "$1"; outln; }
-pr_done_best()       { [[ "$COLOR" -eq 2 ]] && ( "$COLORBLIND" && out "\033[1;34m$1" || out "\033[1;32m$1" ) ||  out "$1"; pr_off; }  # green (blue), This is the best 
+pr_done_best()       { [[ "$COLOR" -eq 2 ]] && ( "$COLORBLIND" && out "\033[1;34m$1" || out "\033[1;32m$1" ) ||  out "$1"; pr_off; }  # green (blue), This is the best
 pr_done_bestln()     { pr_done_best "$1"; outln; }
 
-pr_svrty_minor()     { [[ "$COLOR" -eq 2 ]] && out "\033[1;33m$1" || out "$1"; pr_off; }                   # yellow brown | academic or minor problem 
+pr_svrty_minor()     { [[ "$COLOR" -eq 2 ]] && out "\033[1;33m$1" || out "$1"; pr_off; }                   # yellow brown | academic or minor problem
 pr_svrty_minorln()   { pr_svrty_minor "$1"; outln; }
 pr_svrty_medium()    { [[ "$COLOR" -eq 2 ]] && out "\033[0;33m$1" || out "$1"; pr_off; }                   # brown | it is not a bad problem but you shouldn't do this
 pr_svrty_mediumln()  { pr_svrty_medium "$1"; outln; }
@@ -1082,7 +1082,7 @@ string_to_asciihex() {
      [[ -n "$string" ]] && output+="$(printf "%02x" "'${string:eos:1}")"
      out "$output"
      return 0
-     
+
 }
 
 ###### check code starts here ######
@@ -6221,7 +6221,7 @@ run_pfs() {
           done < <($OPENSSL ciphers -V "$pfs_cipher_list" 2>$ERRFILE)
      fi
      export=""
-     
+
      if "$using_sockets"; then
           tls_sockets "03" "${pfs_hex_cipher_list:2}"
           sclient_success=$?
@@ -7101,7 +7101,7 @@ parse_sslv2_serverhello() {
      rm -f $HOSTCERT $TEMPDIR/intermediatecerts.pem
      if [[ $ret -eq 3 ]]; then
           certificate_len=2*$(hex2dec "$v2_hello_cert_length")
-     
+
           if [[ "$v2_cert_type" == "01" ]] && [[ "$v2_hello_cert_length" != "00" ]]; then
                tmp_der_certfile=$(mktemp $TEMPDIR/der_cert.XXXXXX) || return $ret
                asciihex_to_binary_file "${v2_hello_ascii:26:certificate_len}" "$tmp_der_certfile"
@@ -7222,7 +7222,7 @@ parse_tls_serverhello() {
      local process_full="$2"
      local tls_handshake_ascii="" tls_alert_ascii=""
      local -i tls_hello_ascii_len tls_handshake_ascii_len tls_alert_ascii_len msg_len
-     local tls_serverhello_ascii="" tls_certificate_ascii="" 
+     local tls_serverhello_ascii="" tls_certificate_ascii=""
      local tls_serverkeyexchange_ascii="" tls_certificate_status_ascii=""
      local -i tls_serverhello_ascii_len=0 tls_certificate_ascii_len=0
      local -i tls_serverkeyexchange_ascii_len=0 tls_certificate_status_ascii_len=0
@@ -8029,7 +8029,7 @@ parse_tls_serverhello() {
                     len1="82$(printf "%04x" $((dh_param_len/2)))"
                fi
                dh_param="30${len1}${dh_p}${dh_g}"
-               
+
                # Make a SEQUENCE of the paramters SEQUENCE and the OID
                dh_param_len=22+${#dh_param}
                if [[ $dh_param_len -lt 256 ]]; then
@@ -8069,7 +8069,7 @@ parse_tls_serverhello() {
                [[ -n "$key_bitstring" ]] && echo "$key_bitstring" >> $TMPFILE
 
                # Check to see whether the ephemeral public key uses one of the groups from
-               # RFC 7919 for parameters               
+               # RFC 7919 for parameters
                case $dh_bits in
                     2048) named_curve=256; named_curve_str=" ffdhe2048," ;;
                     3072) named_curve=257; named_curve_str=" ffdhe3072," ;;
@@ -8263,7 +8263,7 @@ socksend_tls_clienthello() {
                00, 01, 00, 02, 00, 03, 00, 0f, 00, 10, 00, 11"
                # Supported Point Formats Extension
                extension_supported_point_formats="
-               00, 0b,                    # Type: Supported Point Formats , see RFC 4492 
+               00, 0b,                    # Type: Supported Point Formats , see RFC 4492
                00, 02,                    # len
                01, 00"
           fi
@@ -9641,7 +9641,7 @@ run_rc4() {
                     ciphers_found[nr_ciphers]=false
                     sigalg[nr_ciphers]=""
                     ossl_supported[nr_ciphers]="${TLS_CIPHER_OSSL_SUPPORTED[i]}"
-                    if "$using_sockets" && "$WIDE" && ! "$HAS_DH_BITS" && 
+                    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
@@ -9897,7 +9897,7 @@ get_install_dir() {
      fi
 
      if [[ ! -r "$CIPHERS_BY_STRENGTH_FILE" ]] ; then
-          unset ADD_RFC_STR 
+          unset ADD_RFC_STR
           debugme echo "$CIPHERS_BY_STRENGTH_FILE"
           pr_warningln "\nATTENTION: No cipher mapping file found!"
           outln "Please note from 2.9dev on $PROG_NAME needs files in \"\$TESTSSL_INSTALL_DIR/etc/\" to function correctly."
@@ -10045,9 +10045,9 @@ check4openssl_oldfarts() {
           pr_warningln " Your \"$OPENSSL\" is way too old (<version 1.0) !"
           case $SYSTEM in
                *BSD|Darwin)
-                    outln " Please use binary provided in \$INSTALLDIR/bin/ or from ports/brew or compile from github.com/PeterMosmans/openssl" 
+                    outln " Please use binary provided in \$INSTALLDIR/bin/ or from ports/brew or compile from github.com/PeterMosmans/openssl"
                     fileout "too_old_openssl" "WARN" "Your $OPENSSL $OSSL_VER version is way too old. Please use binary provided in \$INSTALLDIR/bin/ or from ports/brew or compile from github.com/PeterMosmans/openssl ." ;;
-               *)   outln " Update openssl binaries or compile from github.com/PeterMosmans/openssl" 
+               *)   outln " Update openssl binaries or compile from github.com/PeterMosmans/openssl"
                     fileout "too_old_openssl" "WARN" "Update openssl binaries or compile from github.com/PeterMosmans/openssl .";;
           esac
           ignore_no_or_lame " Type \"yes\" to accept false negatives or positives" "yes"
@@ -10076,8 +10076,8 @@ help() {
 
 
 "$PROG_NAME URI", where URI is:
-     
-     URI                           host|host:port|URL|URL:port   port 443 is default, URL can only contain HTTPS protocol) 
+
+     URI                           host|host:port|URL|URL:port   port 443 is default, URL can only contain HTTPS protocol)
 
 "$PROG_NAME <options>", where <options> is:
 
@@ -10092,7 +10092,7 @@ help() {
 
 "$PROG_NAME <options> URI", where <options> is:
 
-     -t, --starttls <protocol>     does a default run against a STARTTLS enabled <protocol, 
+     -t, --starttls <protocol>     does a default run against a STARTTLS enabled <protocol,
                                    protocol is <ftp|smtp|pop3|imap|xmpp|telnet|ldap|postgres> (latter three require supplied openssl)
      --xmpphost <to_domain>        for STARTTLS enabled XMPP it supplies the XML stream to-'' domain -- sometimes needed
      --mx <domain/host>            tests MX records from high to low priority (STARTTLS, port 25)
@@ -10281,7 +10281,7 @@ EOF
                          ossl_ciph="$(grep -w "$hexc" <<< "$ossl_supported_tls" | awk '{ print $3 }')"
                          if [[ -n "$ossl_ciph" ]]; then
                               TLS_CIPHER_OSSL_SUPPORTED[TLS_NR_CIPHERS]=true
-                              [[ "$ossl_ciph" != "${TLS_CIPHER_OSSL_NAME[TLS_NR_CIPHERS]}" ]] && TLS_CIPHER_OSSL_NAME[TLS_NR_CIPHERS]="$ossl_ciph" 
+                              [[ "$ossl_ciph" != "${TLS_CIPHER_OSSL_NAME[TLS_NR_CIPHERS]}" ]] && TLS_CIPHER_OSSL_NAME[TLS_NR_CIPHERS]="$ossl_ciph"
                          fi
                     fi
                elif [[ $OSSL_VER_MAJOR -lt 1 ]]; then
@@ -11708,4 +11708,3 @@ else
 fi
 
 exit $?
-

From 557c15607a7b82dac59b6330dff9f158660f8665 Mon Sep 17 00:00:00 2001
From: Todd Swatling <todd.swatling@gmail.com>
Date: Thu, 5 Jan 2017 14:45:39 -0500
Subject: [PATCH 4/4] detects install dir when symlinked and realpath not
 present $ ls -l /usr/local/bin/testssl lrwxrwxrwx /usr/local/bin/testssl ->
 /home/user/testssl.sh/testssl.sh

---
 testssl.sh | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/testssl.sh b/testssl.sh
index fc010c3..dee99e4 100755
--- a/testssl.sh
+++ b/testssl.sh
@@ -9896,6 +9896,16 @@ get_install_dir() {
           [[ -r "$TESTSSL_INSTALL_DIR/cipher-mapping.txt" ]] && CIPHERS_BY_STRENGTH_FILE="$TESTSSL_INSTALL_DIR/cipher-mapping.txt"
      fi
 
+     # still no cipher mapping file (and realpath is not present):
+     if [[ ! -r "$CIPHERS_BY_STRENGTH_FILE" ]] && which readlink &>/dev/null ; then
+         readlink -f ls &>/dev/null && \
+              TESTSSL_INSTALL_DIR=$(dirname $(readlink -f ${BASH_SOURCE[0]})) || \
+              TESTSSL_INSTALL_DIR=$(dirname $(readlink ${BASH_SOURCE[0]}))
+              # not sure whether Darwin has -f
+          CIPHERS_BY_STRENGTH_FILE="$TESTSSL_INSTALL_DIR/etc/cipher-mapping.txt"
+          [[ -r "$TESTSSL_INSTALL_DIR/cipher-mapping.txt" ]] && CIPHERS_BY_STRENGTH_FILE="$TESTSSL_INSTALL_DIR/cipher-mapping.txt"
+     fi
+
      if [[ ! -r "$CIPHERS_BY_STRENGTH_FILE" ]] ; then
           unset ADD_RFC_STR
           debugme echo "$CIPHERS_BY_STRENGTH_FILE"