From 9ad1492236b34268a818eba7d55043be591ddadc Mon Sep 17 00:00:00 2001 From: David Cooper Date: Wed, 22 Mar 2017 15:18:38 -0400 Subject: [PATCH 1/2] Cleanup extraction of TLS extensions Currently there is code to extract TLS extensions in three places, in `get_server_certificate()` and two places in `determine_tls_extensions()`. This PR replaces them with one new function, `extract_new_tls_extensions()`. In order for the new function to work correctly whether OpenSSL or `tls_sockets()` is being used, this PR also changes `parse_tls_serverhello()` so that extensions are formatted in the file it creates in the same way as they are formatted by OpenSSL. --- testssl.sh | 148 ++++++++++++++++++++++++++--------------------------- 1 file changed, 72 insertions(+), 76 deletions(-) diff --git a/testssl.sh b/testssl.sh index 798c89d..92ec191 100755 --- a/testssl.sh +++ b/testssl.sh @@ -5075,6 +5075,30 @@ sclient_connect_successful() { return 1 } +extract_new_tls_extensions() { + local tls_extensions + + # this is not beautiful (grep+sed) + # but maybe we should just get the ids and do a private matching, according to + # https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml + tls_extensions=$(grep -a 'TLS server extension ' "$1" | \ + sed -e 's/TLS server extension //g' -e 's/\" (id=/\/#/g' \ + -e 's/,.*$/,/g' -e 's/),$/\"/g' \ + -e 's/elliptic curves\/#10/supported_groups\/#10/g') + tls_extensions=$(echo $tls_extensions) # into one line + + if [[ -n "$tls_extensions" ]]; then + # check to see if any new TLS extensions were returned and add any new ones to TLS_EXTENSIONS + while read -d "\"" -r line; do + if [[ $line != "" ]] && [[ ! "$TLS_EXTENSIONS" =~ "$line" ]]; then + #FIXME: This is a string of quoted strings, so this seems to deterime the output format already. Better e.g. would be an array + TLS_EXTENSIONS+=" \"${line}\"" + fi + done <<<$tls_extensions + [[ "${TLS_EXTENSIONS:0:1}" == " " ]] && TLS_EXTENSIONS="${TLS_EXTENSIONS:1}" + fi +} + # Note that since, at the moment, this function is only called by run_server_defaults() # and run_heartbleed(), this function does not look for the status request or NPN # extensions. For run_heartbleed(), only the heartbeat extension needs to be detected. @@ -5117,7 +5141,7 @@ determine_tls_extensions() { success=$? fi [[ $success -eq 2 ]] && success=0 - [[ $success -eq 0 ]] && tls_extensions="$(grep -a 'TLS Extensions: ' "$TEMPDIR/$NODEIP.parse_tls_serverhello.txt" | sed 's/TLS Extensions: //' )" + [[ $success -eq 0 ]] && extract_new_tls_extensions "$TEMPDIR/$NODEIP.parse_tls_serverhello.txt" if [[ -r "$TEMPDIR/$NODEIP.parse_tls_serverhello.txt" ]]; then cp "$TEMPDIR/$NODEIP.parse_tls_serverhello.txt" $TMPFILE tmpfile_handle $FUNCNAME.txt @@ -5144,24 +5168,9 @@ determine_tls_extensions() { sclient_connect_successful $? $TMPFILE success=$? fi - if [[ $success -eq 0 ]]; then - tls_extensions=$(grep -a 'TLS server extension ' $TMPFILE | \ - sed -e 's/TLS server extension //g' -e 's/\" (id=/\/#/g' \ - -e 's/,.*$/,/g' -e 's/),$/\"/g' \ - -e 's/elliptic curves\/#10/supported_groups\/#10/g') - tls_extensions=$(echo $tls_extensions) # into one line - fi + [[ $success -eq 0 ]] && extract_new_tls_extensions $TMPFILE tmpfile_handle $FUNCNAME.txt fi - if [[ -n "$tls_extensions" ]]; then - # check to see if any new TLS extensions were returned and add any new ones to TLS_EXTENSIONS - while read -d "\"" -r line; do - if [[ $line != "" ]] && [[ ! "$TLS_EXTENSIONS" =~ "$line" ]]; then - TLS_EXTENSIONS+=" \"${line}\"" - fi - done <<<$tls_extensions - [[ "${TLS_EXTENSIONS:0:1}" == " " ]] && TLS_EXTENSIONS="${TLS_EXTENSIONS:1}" - fi return $success } @@ -5170,7 +5179,7 @@ determine_tls_extensions() { get_server_certificate() { local protocols_to_try proto addcmd local success - local npn_params="" tls_extensions line + local npn_params="" line local savedir local nrsaved @@ -5246,25 +5255,7 @@ get_server_certificate() { GOST_STATUS_PROBLEM=true fi fi - #tls_extensions=$(awk -F'"' '/TLS server extension / { printf "\""$2"\" " }' $TMPFILE) - # - # this is not beautiful (grep+sed) - # but maybe we should just get the ids and do a private matching, according to - # https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml - tls_extensions=$(grep -a 'TLS server extension ' $TEMPDIR/tlsext.txt | \ - sed -e 's/TLS server extension //g' -e 's/\" (id=/\/#/g' \ - -e 's/,.*$/,/g' -e 's/),$/\"/g' \ - -e 's/elliptic curves\/#10/supported_groups\/#10/g') - tls_extensions=$(echo $tls_extensions) # into one line - - # check to see if any new TLS extensions were returned and add any new ones to TLS_EXTENSIONS - while read -d "\"" -r line; do - if [[ $line != "" ]] && [[ ! "$TLS_EXTENSIONS" =~ "$line" ]]; then -#FIXME: This is a string of quoted strings, so this seems to deterime the output format already. Better e.g. would be an array - TLS_EXTENSIONS+=" \"${line}\"" - fi - done <<<$tls_extensions - [[ "${TLS_EXTENSIONS:0:1}" == " " ]] && TLS_EXTENSIONS="${TLS_EXTENSIONS:1}" + extract_new_tls_extensions $TMPFILE # Place the server's certificate in $HOSTCERT and any intermediate # certificates that were provided in $TEMPDIR/intermediatecerts.pem @@ -7812,23 +7803,23 @@ parse_tls_serverhello() { return 1 fi case $extension_type in - 0000) tls_extensions+=" \"server name/#0\"" ;; - 0001) tls_extensions+=" \"max fragment length/#1\"" ;; - 0002) tls_extensions+=" \"client certificate URL/#2\"" ;; - 0003) tls_extensions+=" \"trusted CA keys/#3\"" ;; - 0004) tls_extensions+=" \"truncated HMAC/#4\"" ;; - 0005) tls_extensions+=" \"status request/#5\"" ;; - 0006) tls_extensions+=" \"user mapping/#6\"" ;; - 0007) tls_extensions+=" \"client authz/#7\"" ;; - 0008) tls_extensions+=" \"server authz/#8\"" ;; - 0009) tls_extensions+=" \"cert type/#9\"" ;; - 000A) tls_extensions+=" \"supported_groups/#10\"" ;; - 000B) tls_extensions+=" \"EC point formats/#11\"" ;; - 000C) tls_extensions+=" \"SRP/#12\"" ;; - 000D) tls_extensions+=" \"signature algorithms/#13\"" ;; - 000E) tls_extensions+=" \"use SRTP/#14\"" ;; - 000F) tls_extensions+=" \"heartbeat/#15\"" ;; - 0010) tls_extensions+=" \"application layer protocol negotiation/#16\"" + 0000) tls_extensions+="TLS server extension \"server name\" (id=0), len=$extension_len\n" ;; + 0001) tls_extensions+="TLS server extension \"max fragment length\" (id=1), len=$extension_len\n" ;; + 0002) tls_extensions+="TLS server extension \"client certificate URL\" (id=2), len=$extension_len\n" ;; + 0003) tls_extensions+="TLS server extension \"trusted CA keys\" (id=3, len=$extension_len\n)" ;; + 0004) tls_extensions+="TLS server extension \"truncated HMAC\" (id=4), len=$extension_len\n" ;; + 0005) tls_extensions+="TLS server extension \"status request\" (id=5), len=$extension_len\n" ;; + 0006) tls_extensions+="TLS server extension \"user mapping\" (id=6), len=$extension_len\n" ;; + 0007) tls_extensions+="TLS server extension \"client authz\" (id=7), len=$extension_len\n" ;; + 0008) tls_extensions+="TLS server extension \"server authz\" (id=8), len=$extension_len\n" ;; + 0009) tls_extensions+="TLS server extension \"cert type\" (id=9), len=$extension_len\n" ;; + 000A) tls_extensions+="TLS server extension \"supported_groups\" (id=10), len=$extension_len\n" ;; + 000B) tls_extensions+="TLS server extension \"EC point formats\" (id=11), len=$extension_len\n" ;; + 000C) tls_extensions+="TLS server extension \"SRP\" (id=12), len=$extension_len\n" ;; + 000D) tls_extensions+="TLS server extension \"signature algorithms\" (id=13), len=$extension_len\n" ;; + 000E) tls_extensions+="TLS server extension \"use SRTP\" (id=14), len=$extension_len\n" ;; + 000F) tls_extensions+="TLS server extension \"heartbeat\" (id=15), len=$extension_len\n" ;; + 0010) tls_extensions+="TLS server extension \"application layer protocol negotiation\" (id=16), len=$extension_len\n" if [[ $extension_len -lt 4 ]]; then debugme echo "Malformed application layer protocol negotiation extension." return 1 @@ -7851,24 +7842,24 @@ parse_tls_serverhello() { echo "" >> $TMPFILE echo "===============================================================================" >> $TMPFILE ;; - 0011) tls_extensions+=" \"certificate status version 2/#17\"" ;; - 0012) tls_extensions+=" \"signed certificate timestamps/#18\"" ;; - 0013) tls_extensions+=" \"client certificate type/#19\"" ;; - 0014) tls_extensions+=" \"server certificate type/#20\"" ;; - 0015) tls_extensions+=" \"TLS padding/#21\"" ;; - 0016) tls_extensions+=" \"encrypt-then-mac/#22\"" ;; - 0017) tls_extensions+=" \"extended master secret/#23\"" ;; - 0018) tls_extensions+=" \"token binding/#24\"" ;; - 0019) tls_extensions+=" \"cached info/#25\"" ;; - 0023) tls_extensions+=" \"session ticket/#35\"" ;; - 0028) tls_extensions+=" \"key share/#40\"" ;; - 0029) tls_extensions+=" \"pre-shared key/#41\"" ;; - 002A) tls_extensions+=" \"early data/#42\"" ;; - 002B) tls_extensions+=" \"supported versions/#43\"" ;; - 002C) tls_extensions+=" \"cookie/#44\"" ;; - 002D) tls_extensions+=" \"psk key exchange modes/#45\"" ;; - 002E) tls_extensions+=" \"ticket early data info/#46\"" ;; - 3374) tls_extensions+=" \"next protocol/#13172\"" + 0011) tls_extensions+="TLS server extension \"certificate status version 2\" (id=17), len=$extension_len\n" ;; + 0012) tls_extensions+="TLS server extension \"signed certificate timestamps\" (id=18), len=$extension_len\n" ;; + 0013) tls_extensions+="TLS server extension \"client certificate type\" (id=19), len=$extension_len\n" ;; + 0014) tls_extensions+="TLS server extension \"server certificate type\" (id=20), len=$extension_len\n" ;; + 0015) tls_extensions+="TLS server extension \"TLS padding\" (id=21), len=$extension_len\n" ;; + 0016) tls_extensions+="TLS server extension \"encrypt-then-mac\" (id=22), len=$extension_len\n" ;; + 0017) tls_extensions+="TLS server extension \"extended master secret\" (id=23), len=$extension_len\n" ;; + 0018) tls_extensions+="TLS server extension \"token binding\" (id=24), len=$extension_len\n" ;; + 0019) tls_extensions+="TLS server extension \"cached info\" (id=25), len=$extension_len\n" ;; + 0023) tls_extensions+="TLS server extension \"session ticket\" (id=35), len=$extension_len\n" ;; + 0028) tls_extensions+="TLS server extension \"key share\" (id=40), len=$extension_len\n" ;; + 0029) tls_extensions+="TLS server extension \"pre-shared key\" (id=41), len=$extension_len\n" ;; + 002A) tls_extensions+="TLS server extension \"early data\" (id=42), len=$extension_len\n" ;; + 002B) tls_extensions+="TLS server extension \"supported versions\" (id=43), len=$extension_len\n" ;; + 002C) tls_extensions+="TLS server extension \"cookie\" (id=44), len=$extension_len\n" ;; + 002D) tls_extensions+="TLS server extension \"psk key exchange modes\" (id=45), len=$extension_len\n" ;; + 002E) tls_extensions+="TLS server extension \"ticket early data info\" (id=46), len=$extension_len\n" ;; + 3374) tls_extensions+="TLS server extension \"next protocol\" (id=13172), len=$extension_len\n" local -i protocol_len echo -n "Protocols advertised by server: " >> $TMPFILE let offset=$extns_offset+12+$i @@ -7890,8 +7881,8 @@ parse_tls_serverhello() { echo "" >> $TMPFILE echo "===============================================================================" >> $TMPFILE ;; - FF01) tls_extensions+=" \"renegotiation info/#65281\"" ;; - *) tls_extensions+=" \"unrecognized extension/#$(printf "%d\n\n" "0x$extension_type")\"" ;; + FF01) tls_extensions+="TLS server extension \"renegotiation info\" (id=65281), len=$extension_len\n" ;; + *) tls_extensions+="TLS server extension \"unrecognized extension\" (id=$(printf "%d\n\n" "0x$extension_type")), len=$extension_len\n" ;; esac done fi @@ -7921,7 +7912,7 @@ parse_tls_serverhello() { esac echo "===============================================================================" >> $TMPFILE fi - [[ -n "$tls_extensions" ]] && echo "TLS Extensions: ${tls_extensions:1}" >> $TMPFILE + [[ -n "$tls_extensions" ]] && echo -e "$tls_extensions" >> $TMPFILE if [[ $DEBUG -ge 2 ]]; then echo "TLS server hello message:" @@ -7944,7 +7935,12 @@ parse_tls_serverhello() { esac fi if [[ -n "$tls_extensions" ]]; then - echo " tls_extensions: ${tls_extensions:1}" + echo -n " tls_extensions: " + newline_to_spaces "$(grep -a 'TLS server extension ' $TMPFILE | \ + sed -e 's/TLS server extension //g' -e 's/\" (id=/\/#/g' \ + -e 's/,.*$/,/g' -e 's/),$/\"/g' \ + -e 's/elliptic curves\/#10/supported_groups\/#10/g')" + echo "" if [[ "$tls_extensions" =~ "application layer protocol negotiation" ]]; then echo " ALPN protocol: $(grep "ALPN protocol:" "$TMPFILE" | sed 's/ALPN protocol: //')" fi From 63d02688bc668619232961c9de8d8d96801be8f3 Mon Sep 17 00:00:00 2001 From: David Cooper Date: Wed, 22 Mar 2017 15:21:22 -0400 Subject: [PATCH 2/2] Fix typo --- testssl.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testssl.sh b/testssl.sh index 92ec191..eba3a95 100755 --- a/testssl.sh +++ b/testssl.sh @@ -5091,7 +5091,7 @@ extract_new_tls_extensions() { # check to see if any new TLS extensions were returned and add any new ones to TLS_EXTENSIONS while read -d "\"" -r line; do if [[ $line != "" ]] && [[ ! "$TLS_EXTENSIONS" =~ "$line" ]]; then - #FIXME: This is a string of quoted strings, so this seems to deterime the output format already. Better e.g. would be an array +#FIXME: This is a string of quoted strings, so this seems to determine the output format already. Better e.g. would be an array TLS_EXTENSIONS+=" \"${line}\"" fi done <<<$tls_extensions