Merge branch 'master' into run_allciphers(),run_cipher_per_proto(),-and-SSLv2

This commit is contained in:
David Cooper 2016-05-25 16:38:23 -04:00
commit a503d883c7
1 changed files with 369 additions and 120 deletions

View File

@ -20,11 +20,11 @@
# license permitted. # license permitted.
# If you enclose this script or parts of it in your software, it has to # If you enclose this script or parts of it in your software, it has to
# be accompanied by the same license (see link) and the place where to get # be accompanied by the same license (see link) and the place where to get
# the recent version of this program. Do not violate the license andif # the recent version of this program. Do not violate the license and if
# you do not agree to all of these terms, do not use it in the first place. # you do not agree to all of these terms, do not use it in the first place.
# #
# OpenSSL which is being used and maybe distributed via one of this projects' # OpenSSL, which is being used and maybe distributed via one of this projects'
# web site is subject to their licensing: https://www.openssl.org/source/license.txt # web sites, is subject to their licensing: https://www.openssl.org/source/license.txt
# #
# The client simulation data comes from SSLlabs and is licensed to the 'Qualys SSL Labs # The client simulation data comes from SSLlabs and is licensed to the 'Qualys SSL Labs
# Terms of Use' (v2.2), see https://www.ssllabs.com/downloads/Qualys_SSL_Labs_Terms_of_Use.pdf, # Terms of Use' (v2.2), see https://www.ssllabs.com/downloads/Qualys_SSL_Labs_Terms_of_Use.pdf,
@ -42,7 +42,7 @@
# wiki.openssl.org/index.php/Command_Line_Utilities) that it was difficult to resist # wiki.openssl.org/index.php/Command_Line_Utilities) that it was difficult to resist
# wrapping some shell commands around it, which I used for my pen tests. This is how # wrapping some shell commands around it, which I used for my pen tests. This is how
# everything started. # everything started.
# Now it has grown up, it has bash socket support for some features which basically replacing # Now it has grown up, it has bash socket support for some features, which is basically replacing
# more and more functions of OpenSSL and will serve as some kind of library in the future. # more and more functions of OpenSSL and will serve as some kind of library in the future.
# The socket checks in bash may sound cool and unique -- they are -- but probably you # The socket checks in bash may sound cool and unique -- they are -- but probably you
# can achieve e.g. the same result with my favorite interactive shell: zsh (zmodload zsh/net/socket # can achieve e.g. the same result with my favorite interactive shell: zsh (zmodload zsh/net/socket
@ -204,7 +204,6 @@ HAS_SSL2=false
HAS_SSL3=false HAS_SSL3=false
HAS_ALPN=false HAS_ALPN=false
HAS_SPDY=false HAS_SPDY=false
HAS_SSL2=false
ADD_RFC_STR="rfc" # display RFC ciphernames ADD_RFC_STR="rfc" # display RFC ciphernames
PORT=443 # unless otherwise auto-determined, see below PORT=443 # unless otherwise auto-determined, see below
NODE="" NODE=""
@ -225,7 +224,7 @@ URI=""
CERT_FINGERPRINT_SHA2="" CERT_FINGERPRINT_SHA2=""
SHOW_CENSYS_LINK=${SHOW_CENSYS_LINK:-true} SHOW_CENSYS_LINK=${SHOW_CENSYS_LINK:-true}
STARTTLS_PROTOCOL="" STARTTLS_PROTOCOL=""
OPTIMAL_PROTO="" # we need this for IIS6 (sigh) and OpenSSL 1.02, otherwise some handshakes OPTIMAL_PROTO="" # we need this for IIS6 (sigh) and OpenSSL 1.0.2, otherwise some handshakes
# will fail, see https://github.com/PeterMosmans/openssl/issues/19#issuecomment-100897892 # will fail, see https://github.com/PeterMosmans/openssl/issues/19#issuecomment-100897892
STARTTLS_OPTIMAL_PROTO="" # same for STARTTLS, see https://github.com/drwetter/testssl.sh/issues/188 STARTTLS_OPTIMAL_PROTO="" # same for STARTTLS, see https://github.com/drwetter/testssl.sh/issues/188
TLS_TIME="" TLS_TIME=""
@ -489,11 +488,6 @@ hex2dec() {
echo $((16#$1)) echo $((16#$1))
} }
dec2hex() {
#/usr/bin/printf -- "%x" "$1"
echo $((0x$1))
}
# trim spaces for BSD and old sed # trim spaces for BSD and old sed
count_lines() { count_lines() {
wc -l <<<"$1" | sed 's/ //g' wc -l <<<"$1" | sed 's/ //g'
@ -966,7 +960,7 @@ run_hpkp() {
out "# of keys: " out "# of keys: "
if [[ $hpkp_nr_keys -eq 1 ]]; then if [[ $hpkp_nr_keys -eq 1 ]]; then
pr_svrty_high "1 (NOT ok), " pr_svrty_high "1 (NOT ok), "
fileout "hpkp_keys" "NOT ok" "Only one key pinned in HPKP header, this means the site may become unavaiable if the key is revoked" fileout "hpkp_keys" "NOT ok" "Only one key pinned in HPKP header, this means the site may become unavailable if the key is revoked"
else else
out "$hpkp_nr_keys, " out "$hpkp_nr_keys, "
fileout "hpkp_keys" "OK" "$hpkp_nr_keys keys pinned in HPKP header, additional keys are available if the current key is revoked" fileout "hpkp_keys" "OK" "$hpkp_nr_keys keys pinned in HPKP header, additional keys are available if the current key is revoked"
@ -1077,9 +1071,9 @@ run_server_banner() {
emphasize_stuff_in_headers "$serverbanner" emphasize_stuff_in_headers "$serverbanner"
fileout "serverbanner" "INFO" "Server banner identified: $serverbanner" fileout "serverbanner" "INFO" "Server banner identified: $serverbanner"
if [[ "$serverbanner" = *Microsoft-IIS/6.* ]] && [[ $OSSL_VER == 1.0.2* ]]; then if [[ "$serverbanner" = *Microsoft-IIS/6.* ]] && [[ $OSSL_VER == 1.0.2* ]]; then
pr_warningln " It's recommended to run another test w/ OpenSSL 1.01 !" pr_warningln " It's recommended to run another test w/ OpenSSL 1.0.1 !"
# see https://github.com/PeterMosmans/openssl/issues/19#issuecomment-100897892 # see https://github.com/PeterMosmans/openssl/issues/19#issuecomment-100897892
fileout "IIS6_openssl_mismatch" "WARN" "It is recommended to rerun this test w/ OpenSSL 1.01\nSee https://github.com/PeterMosmans/openssl/issues/19#issuecomment-100897892" fileout "IIS6_openssl_mismatch" "WARN" "It is recommended to rerun this test w/ OpenSSL 1.0.1. See https://github.com/PeterMosmans/openssl/issues/19#issuecomment-100897892"
fi fi
fi fi
# mozilla.github.io/server-side-tls/ssl-config-generator/ # mozilla.github.io/server-side-tls/ssl-config-generator/
@ -1393,8 +1387,8 @@ std_cipherlists() {
;; ;;
3) # not totally bad 3) # not totally bad
if [[ $sclient_success -eq 0 ]]; then if [[ $sclient_success -eq 0 ]]; then
pr_svrty_mediumln "offered (NOT ok)" pr_svrty_mediumln "offered"
fileout "std_$4" "NOT ok" "$2 offered (NOT ok) - not too bad" fileout "std_$4" "NOT ok" "$2 offered - not too bad"
else else
outln "not offered (OK)" outln "not offered (OK)"
fileout "std_$4" "OK" "$2 not offered (OK)" fileout "std_$4" "OK" "$2 not offered (OK)"
@ -1492,7 +1486,7 @@ neat_list(){
#printf -- "%q" "$kx" | xxd | head -1 #printf -- "%q" "$kx" | xxd | head -1
# length correction for color escape codes (printf counts the escape color codes!!) # length correction for color escape codes (printf counts the escape color codes!!)
if printf -- "%q" "$kx" | egrep -aq '.;3.m|E\[1m' ; then # here's a color code which screws up the formatting with prinf below if printf -- "%q" "$kx" | egrep -aq '.;3.m|E\[1m' ; then # here's a color code which screws up the formatting with printf below
while [[ ${#kx} -lt 20 ]]; do while [[ ${#kx} -lt 20 ]]; do
kx="$kx " kx="$kx "
done done
@ -1567,7 +1561,7 @@ test_just_one(){
} }
# test for all ciphers locally configured (w/o distinguishing whether they are good or bad # test for all ciphers locally configured (w/o distinguishing whether they are good or bad)
run_allciphers() { run_allciphers() {
local tmpfile local tmpfile
local -i nr_ciphers=0 local -i nr_ciphers=0
@ -2063,7 +2057,7 @@ run_client_simulation() {
ciphers+=("ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DH-DSS-AES256-GCM-SHA384:DHE-DSS-AES256-GCM-SHA384:DH-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA256:DH-RSA-AES256-SHA256:DH-DSS-AES256-SHA256:DHE-RSA-AES256-SHA:DHE-DSS-AES256-SHA:DH-RSA-AES256-SHA:DH-DSS-AES256-SHA:DHE-RSA-CAMELLIA256-SHA:DHE-DSS-CAMELLIA256-SHA:DH-RSA-CAMELLIA256-SHA:DH-DSS-CAMELLIA256-SHA:ECDH-RSA-AES256-GCM-SHA384:ECDH-ECDSA-AES256-GCM-SHA384:ECDH-RSA-AES256-SHA384:ECDH-ECDSA-AES256-SHA384:ECDH-RSA-AES256-SHA:ECDH-ECDSA-AES256-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:CAMELLIA256-SHA:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:DH-DSS-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:DH-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256:DHE-DSS-AES128-SHA256:DH-RSA-AES128-SHA256:DH-DSS-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA:DH-RSA-AES128-SHA:DH-DSS-AES128-SHA:DHE-RSA-SEED-SHA:DHE-DSS-SEED-SHA:DH-RSA-SEED-SHA:DH-DSS-SEED-SHA:DHE-RSA-CAMELLIA128-SHA:DHE-DSS-CAMELLIA128-SHA:DH-RSA-CAMELLIA128-SHA:DH-DSS-CAMELLIA128-SHA:ECDH-RSA-AES128-GCM-SHA256:ECDH-ECDSA-AES128-GCM-SHA256:ECDH-RSA-AES128-SHA256:ECDH-ECDSA-AES128-SHA256:ECDH-RSA-AES128-SHA:ECDH-ECDSA-AES128-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:SEED-SHA:CAMELLIA128-SHA:IDEA-CBC-SHA:ECDHE-RSA-RC4-SHA:ECDHE-ECDSA-RC4-SHA:ECDH-RSA-RC4-SHA:ECDH-ECDSA-RC4-SHA:RC4-SHA:RC4-MD5:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:EDH-DSS-DES-CBC3-SHA:DH-RSA-DES-CBC3-SHA:DH-DSS-DES-CBC3-SHA:ECDH-RSA-DES-CBC3-SHA:ECDH-ECDSA-DES-CBC3-SHA:DES-CBC3-SHA:EDH-RSA-DES-CBC-SHA:EDH-DSS-DES-CBC-SHA:DH-RSA-DES-CBC-SHA:DH-DSS-DES-CBC-SHA:DES-CBC-SHA:EXP-EDH-RSA-DES-CBC-SHA:EXP-EDH-DSS-DES-CBC-SHA:EXP-DES-CBC-SHA:EXP-RC2-CBC-MD5:EXP-RC4-MD5") ciphers+=("ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DH-DSS-AES256-GCM-SHA384:DHE-DSS-AES256-GCM-SHA384:DH-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA256:DH-RSA-AES256-SHA256:DH-DSS-AES256-SHA256:DHE-RSA-AES256-SHA:DHE-DSS-AES256-SHA:DH-RSA-AES256-SHA:DH-DSS-AES256-SHA:DHE-RSA-CAMELLIA256-SHA:DHE-DSS-CAMELLIA256-SHA:DH-RSA-CAMELLIA256-SHA:DH-DSS-CAMELLIA256-SHA:ECDH-RSA-AES256-GCM-SHA384:ECDH-ECDSA-AES256-GCM-SHA384:ECDH-RSA-AES256-SHA384:ECDH-ECDSA-AES256-SHA384:ECDH-RSA-AES256-SHA:ECDH-ECDSA-AES256-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:CAMELLIA256-SHA:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:DH-DSS-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:DH-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256:DHE-DSS-AES128-SHA256:DH-RSA-AES128-SHA256:DH-DSS-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA:DH-RSA-AES128-SHA:DH-DSS-AES128-SHA:DHE-RSA-SEED-SHA:DHE-DSS-SEED-SHA:DH-RSA-SEED-SHA:DH-DSS-SEED-SHA:DHE-RSA-CAMELLIA128-SHA:DHE-DSS-CAMELLIA128-SHA:DH-RSA-CAMELLIA128-SHA:DH-DSS-CAMELLIA128-SHA:ECDH-RSA-AES128-GCM-SHA256:ECDH-ECDSA-AES128-GCM-SHA256:ECDH-RSA-AES128-SHA256:ECDH-ECDSA-AES128-SHA256:ECDH-RSA-AES128-SHA:ECDH-ECDSA-AES128-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:SEED-SHA:CAMELLIA128-SHA:IDEA-CBC-SHA:ECDHE-RSA-RC4-SHA:ECDHE-ECDSA-RC4-SHA:ECDH-RSA-RC4-SHA:ECDH-ECDSA-RC4-SHA:RC4-SHA:RC4-MD5:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:EDH-DSS-DES-CBC3-SHA:DH-RSA-DES-CBC3-SHA:DH-DSS-DES-CBC3-SHA:ECDH-RSA-DES-CBC3-SHA:ECDH-ECDSA-DES-CBC3-SHA:DES-CBC3-SHA:EDH-RSA-DES-CBC-SHA:EDH-DSS-DES-CBC-SHA:DH-RSA-DES-CBC-SHA:DH-DSS-DES-CBC-SHA:DES-CBC-SHA:EXP-EDH-RSA-DES-CBC-SHA:EXP-EDH-DSS-DES-CBC-SHA:EXP-DES-CBC-SHA:EXP-RC2-CBC-MD5:EXP-RC4-MD5")
tlsvers+=("-tls1_2 -tls1_1 -tls1") tlsvers+=("-tls1_2 -tls1_1 -tls1")
sni+=("$SNI") sni+=("$SNI")
#warning+=("Tests are based on OpenSSL 1.01, therefore ciphers 0xe and 0xb are missing") #warning+=("Tests are based on OpenSSL 1.0.1, therefore ciphers 0xe and 0xb are missing")
warning+=("") warning+=("")
names+=("Safari 5.1.9/ OSX 10.6.8 ") names+=("Safari 5.1.9/ OSX 10.6.8 ")
@ -2157,7 +2151,7 @@ run_client_simulation() {
#FIXME: awk #FIXME: awk
proto=$(grep -aw "Protocol" $TMPFILE | sed -e 's/^.*Protocol.*://' -e 's/ //g') proto=$(grep -aw "Protocol" $TMPFILE | sed -e 's/^.*Protocol.*://' -e 's/ //g')
if [[ "$proto" == TLSv1.2 ]]; then if [[ "$proto" == TLSv1.2 ]]; then
# OpenSSL reports TLS1.2 even if the connection is TLS1.1 or TLS1.0 Need to figure out which one it is... # OpenSSL reports TLS1.2 even if the connection is TLS1.1 or TLS1.0. Need to figure out which one it is...
for tls in ${tlsvers[i]}; do for tls in ${tlsvers[i]}; do
$OPENSSL s_client $tls -cipher ${ciphers[i]} ${protos[i]} $STARTTLS $BUGS $PROXY -connect $NODEIP:$PORT ${sni[i]} </dev/null >$TMPFILE 2>$ERRFILE $OPENSSL s_client $tls -cipher ${ciphers[i]} ${protos[i]} $STARTTLS $BUGS $PROXY -connect $NODEIP:$PORT ${sni[i]} </dev/null >$TMPFILE 2>$ERRFILE
debugme echo "$OPENSSL s_client $tls -cipher ${ciphers[i]} ${protos[i]} $STARTTLS $BUGS $PROXY -connect $NODEIP:$PORT ${sni[i]} </dev/null" debugme echo "$OPENSSL s_client $tls -cipher ${ciphers[i]} ${protos[i]} $STARTTLS $BUGS $PROXY -connect $NODEIP:$PORT ${sni[i]} </dev/null"
@ -2212,7 +2206,7 @@ locally_supported() {
# 1) we need to have a variable where the results are being stored so that every other test doesn't have to do this again. # 1) we need to have a variable where the results are being stored so that every other test doesn't have to do this again.
# 2) the code is too old and one can do that way better # 2) the code is too old and one can do that way better
# 3) HAS_SSL3/2 does already exist # 3) HAS_SSL3/2 does already exist
# we should do what's available and faster (openssl vs. sockets) . Keep in mind that the socket reply for SSLv2 returns the number # of ciphers! # we should do what's available and faster (openssl vs. sockets). Keep in mind that the socket reply for SSLv2 returns the number # of ciphers!
# #
# arg1: -ssl2|-ssl3|-tls1 # arg1: -ssl2|-ssl3|-tls1
# arg2: doesn't seem to be used in calling, seems to be a textstring with the protocol though # arg2: doesn't seem to be used in calling, seems to be a textstring with the protocol though
@ -2403,10 +2397,10 @@ run_protocols() {
fileout "tls1_2" "NOT ok" "TLSv1.2 is not offered (NOT ok)" fileout "tls1_2" "NOT ok" "TLSv1.2 is not offered (NOT ok)"
;; # no GCM, penalty ;; # no GCM, penalty
2) 2)
pr_svrty_medium "not offered (NOT ok)" pr_svrty_medium "not offered"
[[ $DEBUG -eq 1 ]] && out " -- downgraded" [[ $DEBUG -eq 1 ]] && out " -- downgraded"
outln outln
fileout "tls1_2" "NOT ok" "TLSv1.2 is not offered and downgraded to a weaker protocol (NOT ok)" fileout "tls1_2" "INFO" "TLSv1.2 is not offered and downgraded to a weaker protocol (medium)"
;; ;;
5) 5)
outln "$supported_no_ciph1" outln "$supported_no_ciph1"
@ -2587,8 +2581,8 @@ run_server_preference() {
"") "")
pr_warning "default proto empty" pr_warning "default proto empty"
if [[ $OSSL_VER == 1.0.2* ]]; then if [[ $OSSL_VER == 1.0.2* ]]; then
outln " (Hint: if IIS6 give OpenSSL 1.01 a try)" outln " (Hint: if IIS6 give OpenSSL 1.0.1 a try)"
fileout "order_proto" "WARN" "Default protocol empty (Hint: if IIS6 give OpenSSL 1.01 a try)" fileout "order_proto" "WARN" "Default protocol empty (Hint: if IIS6 give OpenSSL 1.0.1 a try)"
else else
fileout "order_proto" "WARN" "Default protocol empty" fileout "order_proto" "WARN" "Default protocol empty"
fi fi
@ -2626,8 +2620,8 @@ run_server_preference() {
"") "")
pr_warning "default cipher empty" ; pr_warning "default cipher empty" ;
if [[ $OSSL_VER == 1.0.2* ]]; then if [[ $OSSL_VER == 1.0.2* ]]; then
out " (Hint: if IIS6 give OpenSSL 1.01 a try)" out " (Hint: if IIS6 give OpenSSL 1.0.1 a try)"
fileout "order_cipher" "WARN" "Default cipher empty (Hint: if IIS6 give OpenSSL 1.01 a try) $remark4default_cipher" fileout "order_cipher" "WARN" "Default cipher empty (Hint: if IIS6 give OpenSSL 1.0.1 a try) $remark4default_cipher"
else else
fileout "order_cipher" "WARN" "Default cipher empty $remark4default_cipher" fileout "order_cipher" "WARN" "Default cipher empty $remark4default_cipher"
fi fi
@ -2646,11 +2640,11 @@ run_server_preference() {
i=1 i=1
for p in ssl2 ssl3 tls1 tls1_1 tls1_2; do for p in ssl2 ssl3 tls1 tls1_1 tls1_2; do
if [[ $p == ssl2 ]] && ! "$HAS_SSL2"; then if [[ $p == ssl2 ]] && ! "$HAS_SSL2"; then
out " (SSLv2: "; local_problem "/usr/bin/openssl doesn't support \"s_client -ssl2\""; outln ")"; out " (SSLv2: "; local_problem "$OPENSSL doesn't support \"s_client -ssl2\""; outln ")";
continue continue
fi fi
if [[ $p == ssl3 ]] && ! "$HAS_SSL3"; then if [[ $p == ssl3 ]] && ! "$HAS_SSL3"; then
out " (SSLv3: "; local_problem "/usr/bin/openssl doesn't support \"s_client -ssl3\"" ; outln ")"; out " (SSLv3: "; local_problem "$OPENSSL doesn't support \"s_client -ssl3\"" ; outln ")";
continue continue
fi fi
$OPENSSL s_client $STARTTLS -"$p" $BUGS -connect $NODEIP:$PORT $PROXY $SNI </dev/null 2>>$ERRFILE >$TMPFILE $OPENSSL s_client $STARTTLS -"$p" $BUGS -connect $NODEIP:$PORT $PROXY $SNI </dev/null 2>>$ERRFILE >$TMPFILE
@ -2721,11 +2715,11 @@ cipher_pref_check() {
for p in ssl2 ssl3 tls1 tls1_1 tls1_2; do for p in ssl2 ssl3 tls1 tls1_1 tls1_2; do
order="" order=""
if [[ $p == ssl2 ]] && ! "$HAS_SSL2"; then if [[ $p == ssl2 ]] && ! "$HAS_SSL2"; then
out "\n SSLv2: "; local_problem "/usr/bin/openssl doesn't support \"s_client -ssl2\""; out "\n SSLv2: "; local_problem "$OPENSSL doesn't support \"s_client -ssl2\"";
continue continue
fi fi
if [[ $p == ssl3 ]] && ! "$HAS_SSL3"; then if [[ $p == ssl3 ]] && ! "$HAS_SSL3"; then
out "\n SSLv3: "; local_problem "/usr/bin/openssl doesn't support \"s_client -ssl3\""; out "\n SSLv3: "; local_problem "$OPENSSL doesn't support \"s_client -ssl3\"";
continue continue
fi fi
$OPENSSL s_client $STARTTLS -"$p" $BUGS -connect $NODEIP:$PORT $PROXY $SNI </dev/null 2>$ERRFILE >$TMPFILE $OPENSSL s_client $STARTTLS -"$p" $BUGS -connect $NODEIP:$PORT $PROXY $SNI </dev/null 2>$ERRFILE >$TMPFILE
@ -2924,7 +2918,7 @@ tls_time() {
local now difftime local now difftime
local spaces=" " local spaces=" "
tls_sockets "01" "$TLS_CIPHER" # try first TLS 1.0 (mostfrequently used protocol) tls_sockets "01" "$TLS_CIPHER" # try first TLS 1.0 (most frequently used protocol)
[[ -z "$TLS_TIME" ]] && tls_sockets "03" "$TLS12_CIPHER" # TLS 1.2 [[ -z "$TLS_TIME" ]] && tls_sockets "03" "$TLS12_CIPHER" # TLS 1.2
[[ -z "$TLS_TIME" ]] && tls_sockets "02" "$TLS_CIPHER" # TLS 1.1 [[ -z "$TLS_TIME" ]] && tls_sockets "02" "$TLS_CIPHER" # TLS 1.1
[[ -z "$TLS_TIME" ]] && tls_sockets "00" "$TLS_CIPHER" # SSL 3 [[ -z "$TLS_TIME" ]] && tls_sockets "00" "$TLS_CIPHER" # SSL 3
@ -3049,7 +3043,7 @@ certificate_info() {
local cert_keysize=$4 local cert_keysize=$4
local ocsp_response=$5 local ocsp_response=$5
local ocsp_response_status=$6 local ocsp_response_status=$6
local cert_sig_algo cert_key_algo local cert_sig_algo cert_sig_hash_algo cert_key_algo
local expire days2expire secs2warn ocsp_uri crl startdate enddate issuer_C issuer_O issuer sans san cn cn_nosni local expire days2expire secs2warn ocsp_uri crl startdate enddate issuer_C issuer_O issuer sans san cn cn_nosni
local cert_fingerprint_sha1 cert_fingerprint_sha2 cert_fingerprint_serial local cert_fingerprint_sha1 cert_fingerprint_sha2 cert_fingerprint_serial
local policy_oid local policy_oid
@ -3082,6 +3076,10 @@ certificate_info() {
pr_svrty_mediumln "SHA1 with RSA" pr_svrty_mediumln "SHA1 with RSA"
fileout "${json_prefix}algorithm" "WARN" "Signature Algorithm: SHA1 with RSA (warning)" fileout "${json_prefix}algorithm" "WARN" "Signature Algorithm: SHA1 with RSA (warning)"
;; ;;
sha224WithRSAEncryption)
outln "SHA224 with RSA"
fileout "${json_prefix}algorithm" "INFO" "Signature Algorithm: SHA224 with RSA"
;;
sha256WithRSAEncryption) sha256WithRSAEncryption)
pr_done_goodln "SHA256 with RSA" pr_done_goodln "SHA256 with RSA"
fileout "${json_prefix}algorithm" "OK" "Signature Algorithm: SHA256 with RSA (OK)" fileout "${json_prefix}algorithm" "OK" "Signature Algorithm: SHA256 with RSA (OK)"
@ -3094,10 +3092,75 @@ certificate_info() {
pr_done_goodln "SHA512 with RSA" pr_done_goodln "SHA512 with RSA"
fileout "${json_prefix}algorithm" "OK" "Signature Algorithm: SHA512 with RSA (OK)" fileout "${json_prefix}algorithm" "OK" "Signature Algorithm: SHA512 with RSA (OK)"
;; ;;
ecdsa-with-SHA1)
pr_svrty_mediumln "ECDSA with SHA1"
fileout "${json_prefix}algorithm" "WARN" "Signature Algorithm: ECDSA with SHA1 (warning)"
;;
ecdsa-with-SHA224)
outln "ECDSA with SHA224"
fileout "${json_prefix}algorithm" "INFO" "Signature Algorithm: ECDSA with SHA224"
;;
ecdsa-with-SHA256) ecdsa-with-SHA256)
pr_done_goodln "ECDSA with SHA256" pr_done_goodln "ECDSA with SHA256"
fileout "${json_prefix}algorithm" "OK" "Signature Algorithm: ECDSA with SHA256 (OK)" fileout "${json_prefix}algorithm" "OK" "Signature Algorithm: ECDSA with SHA256 (OK)"
;; ;;
ecdsa-with-SHA384)
pr_done_goodln "ECDSA with SHA384"
fileout "${json_prefix}algorithm" "OK" "Signature Algorithm: ECDSA with SHA384 (OK)"
;;
ecdsa-with-SHA512)
pr_done_goodln "ECDSA with SHA512"
fileout "${json_prefix}algorithm" "OK" "Signature Algorithm: ECDSA with SHA512 (OK)"
;;
dsaWithSHA1)
pr_svrty_mediumln "DSA with SHA1"
fileout "${json_prefix}algorithm" "WARN" "Signature Algorithm: DSA with SHA1 (warning)"
;;
dsa_with_SHA224)
outln "DSA with SHA224"
fileout "${json_prefix}algorithm" "INFO" "Signature Algorithm: DSA with SHA224"
;;
dsa_with_SHA256)
pr_done_goodln "DSA with SHA256"
fileout "${json_prefix}algorithm" "OK" "Signature Algorithm: DSA with SHA256 (OK)"
;;
rsassaPss)
cert_sig_hash_algo="$($OPENSSL x509 -in $HOSTCERT -noout -text 2>>$ERRFILE | grep -A 1 "Signature Algorithm" | head -2 | tail -1 | sed 's/^.*Hash Algorithm: //')"
case $cert_sig_hash_algo in
sha1)
pr_svrty_mediumln "RSASSA-PSS with SHA1"
fileout "${json_prefix}algorithm" "WARN" "Signature Algorithm: RSASSA-PSS with SHA1 (warning)"
;;
sha224)
outln "RSASSA-PSS with SHA224"
fileout "${json_prefix}algorithm" "INFO" "Signature Algorithm: RSASSA-PSS with SHA224"
;;
sha256)
pr_done_goodln "RSASSA-PSS with SHA256"
fileout "${json_prefix}algorithm" "OK" "Signature Algorithm: RSASSA-PSS with SHA256 (OK)"
;;
sha384)
pr_done_goodln "RSASSA-PSS with SHA384"
fileout "${json_prefix}algorithm" "OK" "Signature Algorithm: RSASSA-PSS with SHA384 (OK)"
;;
sha512)
pr_done_goodln "RSASSA-PSS with SHA512"
fileout "${json_prefix}algorithm" "OK" "Signature Algorithm: RSASSA-PSS with SHA512 (OK)"
;;
*)
out "RSASSA-PSS with $cert_sig_hash_algo"
pr_warningln " (Unknown hash algorithm)"
fileout "${json_prefix}algorithm" "WARN" "Signature Algorithm: RSASSA-PSS with $cert_sig_hash_algo"
esac
;;
md2*)
pr_svrty_criticalln "MD2"
fileout "${json_prefix}algorithm" "NOT ok" "Signature Algorithm: MD2 (NOT ok)"
;;
md4*)
pr_svrty_criticalln "MD4"
fileout "${json_prefix}algorithm" "NOT ok" "Signature Algorithm: MD4 (NOT ok)"
;;
md5*) md5*)
pr_svrty_criticalln "MD5" pr_svrty_criticalln "MD5"
fileout "${json_prefix}algorithm" "NOT ok" "Signature Algorithm: MD5 (NOT ok)" fileout "${json_prefix}algorithm" "NOT ok" "Signature Algorithm: MD5 (NOT ok)"
@ -3120,7 +3183,7 @@ certificate_info() {
# http://infoscience.epfl.ch/record/164526/files/NPDF-22.pdf # http://infoscience.epfl.ch/record/164526/files/NPDF-22.pdf
# see http://csrc.nist.gov/publications/nistpubs/800-57/sp800-57_part1_rev3_general.pdf # see http://csrc.nist.gov/publications/nistpubs/800-57/sp800-57_part1_rev3_general.pdf
# Table 2 @ chapter 5.6.1 (~ p64) # Table 2 @ chapter 5.6.1 (~ p64)
if [[ $cert_sig_algo =~ ecdsa ]] || [[ $cert_key_algo =~ ecPublicKey ]]; then if [[ $cert_key_algo =~ ecdsa ]] || [[ $cert_key_algo =~ ecPublicKey ]]; then
if [[ "$cert_keysize" -le 110 ]]; then # a guess if [[ "$cert_keysize" -le 110 ]]; then # a guess
pr_svrty_critical "$cert_keysize" pr_svrty_critical "$cert_keysize"
fileout "${json_prefix}key_size" "NOT ok" "Server keys $cert_keysize EC bits (NOT ok)" fileout "${json_prefix}key_size" "NOT ok" "Server keys $cert_keysize EC bits (NOT ok)"
@ -3140,8 +3203,8 @@ certificate_info() {
out "keysize: $cert_keysize (not expected, FIXME)" out "keysize: $cert_keysize (not expected, FIXME)"
fileout "${json_prefix}key_size" "WARN" "Server keys $cert_keysize bits (not expected)" fileout "${json_prefix}key_size" "WARN" "Server keys $cert_keysize bits (not expected)"
fi fi
outln " bit" outln " bits"
elif [[ $cert_sig_algo = *RSA* ]]; then elif [[ $cert_key_algo = *RSA* ]] || [[ $cert_key_algo = *rsa* ]] || [[ $cert_key_algo = *dsa* ]]; then
if [[ "$cert_keysize" -le 512 ]]; then if [[ "$cert_keysize" -le 512 ]]; then
pr_svrty_critical "$cert_keysize" pr_svrty_critical "$cert_keysize"
outln " bits" outln " bits"
@ -3296,7 +3359,8 @@ certificate_info() {
# http://events.ccc.de/congress/2010/Fahrplan/attachments/1777_is-the-SSLiverse-a-safe-place.pdf, see page 40pp # http://events.ccc.de/congress/2010/Fahrplan/attachments/1777_is-the-SSLiverse-a-safe-place.pdf, see page 40pp
out "$indent"; pr_bold " EV cert"; out " (experimental) " out "$indent"; pr_bold " EV cert"; out " (experimental) "
policy_oid=$($OPENSSL x509 -in $HOSTCERT -text 2>>$ERRFILE | awk '/ .Policy: / { print $2 }') # only the first one, seldom we have two
policy_oid=$($OPENSSL x509 -in $HOSTCERT -text 2>>$ERRFILE | awk '/ .Policy: / { print $2 }' | awk 'NR < 2')
if echo "$issuer" | egrep -q 'Extended Validation|Extended Validated|EV SSL|EV CA' || \ if echo "$issuer" | egrep -q 'Extended Validation|Extended Validated|EV SSL|EV CA' || \
[[ 2.16.840.1.114028.10.1.2 == "$policy_oid" ]] || \ [[ 2.16.840.1.114028.10.1.2 == "$policy_oid" ]] || \
[[ 2.16.840.1.114412.1.3.0.2 == "$policy_oid" ]] || \ [[ 2.16.840.1.114412.1.3.0.2 == "$policy_oid" ]] || \
@ -3410,7 +3474,7 @@ certificate_info() {
ret=0 ret=0
else else
out "(response status unknown)" out "(response status unknown)"
fileout "${json_prefix}ocsp_stapling" "OK" "OCSP stapling : not sure what's going on here, debug: grep -aA 20 "OCSP response" <<<"$ocsp_response"" fileout "${json_prefix}ocsp_stapling" "OK" "OCSP stapling : not sure what's going on here, debug: $ocsp_response"
debugme grep -a -A20 -B2 "OCSP response" <<<"$ocsp_response" debugme grep -a -A20 -B2 "OCSP response" <<<"$ocsp_response"
ret=2 ret=2
fi fi
@ -3576,7 +3640,7 @@ run_pfs() {
local pfs_ciphers local pfs_ciphers
outln outln
pr_headlineln " Testing (perfect) forward secrecy, (P)FS -- omitting 3DES, RC4 and Null Encryption here " pr_headlineln " Testing robust (perfect) forward secrecy, (P)FS -- omitting Null Authentication/Encryption as well as 3DES and RC4 here "
if ! "$HAS_DH_BITS" && "$WIDE"; then if ! "$HAS_DH_BITS" && "$WIDE"; then
pr_warningln " (Your $OPENSSL cannot show DH/ECDH bits)" pr_warningln " (Your $OPENSSL cannot show DH/ECDH bits)"
fi fi
@ -3640,7 +3704,7 @@ run_pfs() {
out "$pfs_cipher " out "$pfs_cipher "
fi fi
fi fi
pfs_ciphers+="$pfs_cipher " [[ $sclient_success -eq 0 ]] && pfs_ciphers+="$pfs_cipher "
debugme rm $tmpfile debugme rm $tmpfile
done < <($OPENSSL ciphers -V "$pfs_cipher_list" 2>$ERRFILE) # -V doesn't work with openssl < 1.0 done < <($OPENSSL ciphers -V "$pfs_cipher_list" 2>$ERRFILE) # -V doesn't work with openssl < 1.0
debugme echo $pfs_offered debugme echo $pfs_offered
@ -3734,8 +3798,8 @@ run_spdy() {
fileout "spdy_npn" "INFO" "SPDY/NPN : $tmpstr (advertised)" fileout "spdy_npn" "INFO" "SPDY/NPN : $tmpstr (advertised)"
ret=0 ret=0
else else
pr_cyanln "please check manually, server response was ambigious ..." pr_cyanln "please check manually, server response was ambiguous ..."
fileout "spdy_npn" "INFO" "SPDY/NPN : please check manually, server response was ambigious ..." fileout "spdy_npn" "INFO" "SPDY/NPN : please check manually, server response was ambiguous ..."
ret=10 ret=10
fi fi
fi fi
@ -4036,92 +4100,268 @@ parse_sslv2_serverhello() {
# arg1: name of file with socket reply # arg1: name of file with socket reply
parse_tls_serverhello() { parse_tls_serverhello() {
local tls_hello_ascii=$(hexdump -v -e '16/1 "%02X"' "$1") local tls_hello_ascii=$(hexdump -v -e '16/1 "%02X"' "$1")
local tls_content_type tls_protocol tls_len_all local tls_handshake_ascii="" tls_alert_ascii=""
#TODO: all vars here local -i tls_hello_ascii_len tls_handshake_ascii_len tls_alert_ascii_len msg_len
local tls_serverhello_ascii=""
local -i tls_serverhello_ascii_len=0
local tls_alert_descrip tls_sid_len_hex
local -i tls_sid_len offset
local tls_msg_type tls_content_type tls_protocol tls_protocol2 tls_hello_time
local tls_err_level tls_err_descr tls_cipher_suite tls_compression_method
local -i i
TLS_TIME="" TLS_TIME=""
DETECTED_TLS_VERSION="" DETECTED_TLS_VERSION=""
# server hello, handshake details see http://en.wikipedia.org/wiki/Transport_Layer_Security-SSL#TLS_record # $tls_hello_ascii may contain trailing whitespace. Remove it:
tls_hello_ascii="${tls_hello_ascii%%[!0-9A-F]*}"
[[ "$DEBUG" -eq 5 ]] && echo $tls_hello_ascii # one line without any blanks
# Client messages, including handshake messages, are carried by the record layer.
# First, extract the handshake and alert messages.
# see http://en.wikipedia.org/wiki/Transport_Layer_Security-SSL#TLS_record
# byte 0: content type: 0x14=CCS, 0x15=TLS alert x16=Handshake, 0x17 Aplication, 0x18=HB # byte 0: content type: 0x14=CCS, 0x15=TLS alert x16=Handshake, 0x17 Aplication, 0x18=HB
# byte 1+2: TLS version word, major is 03, minor 00=SSL3, 01=TLS1 02=TLS1.1 03=TLS 1.2 # byte 1+2: TLS version word, major is 03, minor 00=SSL3, 01=TLS1 02=TLS1.1 03=TLS 1.2
# byte 3+4: length all # byte 3+4: fragment length
# byte 5: handshake type (2=hello) TLS alert: level (2=fatal), descr (0x28=handshake failure) # bytes 5...: message fragment
# byte 6+7+8: length server hello tls_hello_ascii_len=${#tls_hello_ascii}
# byte 9+10: 03, TLS version word see byte 1+2 if [[ $DEBUG -ge 2 ]] && [[ $tls_hello_ascii_len -gt 0 ]]; then
# byte 11-14: TLS timestamp for OpenSSL <1.01f echo "TLS message fragments:"
# byte 15-42: random, 28 bytes
# byte 43: session id length
# byte 44+45+sid-len: cipher suite!
# byte 46+sid-len: compression method: 00: none, 01: deflate
# byte 47+48+sid-len: extension length
[[ "$DEBUG" -eq 5 ]] && echo $tls_hello_ascii # one line without any blanks
if [[ -z "$tls_hello_ascii" ]]; then
debugme echo "server hello empty, TCP connection closed"
return 1 # no server hello received
fi fi
for (( i=0; i<tls_hello_ascii_len; i=i+msg_len )); do
if [[ $tls_hello_ascii_len-$i -lt 10 ]]; then
# This could just be a result of the server's response being
# split across two or more packets.
continue
fi
tls_content_type="${tls_hello_ascii:i:2}"
i=$i+2
tls_protocol="${tls_hello_ascii:i:4}"
i=$i+4
msg_len=2*$(hex2dec "${tls_hello_ascii:i:4}")
i=$i+4
# now scrape two bytes out of the reply per byte
tls_content_type="${tls_hello_ascii:0:2}" # normally this is x16 (Handshake) here
tls_protocol="${tls_hello_ascii:2:4}"
DETECTED_TLS_VERSION=$tls_protocol
tls_len_all=${tls_hello_ascii:6:4}
sid_len_offset=86
tls_hello="${tls_hello_ascii:10:2}" # normally this is x02
tls_protocol2="${tls_hello_ascii:18:4}"
tls_hello_time="${tls_hello_ascii:22:8}"
if [[ $tls_content_type == "15" ]]; then # TLS ALERT
tls_err_level=${tls_hello_ascii:10:2} # 1: warning, 2: fatal
tls_err_descr=${tls_hello_ascii:12:2} # 112/0x70: Unrecognized name, 111/0x6F: certificate_unobtainable,
# 113/0x71: bad_certificate_status_response, #114/0x72: bad_certificate_hash_value
if [[ $DEBUG -ge 2 ]]; then if [[ $DEBUG -ge 2 ]]; then
echo "tls_protocol (reclyr): 0x$tls_protocol" echo " tls_protocol (reclyr): 0x$tls_protocol"
echo "tls_content_type: 0x$tls_content_type" out " tls_content_type: 0x$tls_content_type"
echo "tls_len_all: $tls_len_all" case $tls_content_type in
echo "tls_err_descr: 0x${tls_err_descr} / = $(hex2dec ${tls_err_descr})" 15) outln " (alert)" ;;
echo "tls_err_level: ${tls_err_level} (warning:1, fatal:2)" 16) outln " (handshake)" ;;
*) outln ;;
esac
echo " msg_len: $((msg_len/2))"
outln
fi fi
# now, here comes a strange thing... -- on the first glance if [[ $tls_content_type != "15" ]] && [[ $tls_content_type != "16" ]]; then
# IF an apache 2.2/2.4 server e.g. has a default servername configured but we send SNI <myhostname> debugme pr_warningln "Content type other than alert or handshake detected."
# we get a TLS ALERT saying "unrecognized_name" (0x70) and a warning (0x1), see RFC https://tools.ietf.org/html/rfc6066#page-17 return 1
# note that RFC recommended to fail instead: https://tools.ietf.org/html/rfc6066#section-3 elif [[ "${tls_protocol:0:2}" != "03" ]]; then
# we need to handle this properly -- otherwise we always return that the protocol or cipher is not available! debugme pr_warningln "Protocol record_version.major is not 03."
if [[ "$tls_err_descr" == 70 ]] && [[ "${tls_err_level}" == "01" ]]; then
sid_len_offset=100 # we are 2x7 bytes off (formerly: 86 instead of 100)
tls_hello="${tls_hello_ascii:24:2}" # here, too (normally this is (02)
tls_protocol2="${tls_hello_ascii:32:4}" # here, too
tls_hello_time="${tls_hello_ascii:36:8}" # and here, too
else
return 1 return 1
fi fi
DETECTED_TLS_VERSION=$tls_protocol
if [[ $msg_len -gt $tls_hello_ascii_len-$i ]]; then
# This could just be a result of the server's response being
# split across two or more packets. Just grab the part that
# is available.
msg_len=$tls_hello_ascii_len-$i
fi fi
TLS_TIME=$(hex2dec "$tls_hello_time") if [[ $tls_content_type == "16" ]]; then
tls_sid_len=$(hex2dec "${tls_hello_ascii:$sid_len_offset:2}") tls_handshake_ascii="$tls_handshake_ascii${tls_hello_ascii:i:msg_len}"
let sid_offset=$sid_len_offset+2+$tls_sid_len*2 elif [[ $tls_content_type == "15" ]]; then # TLS ALERT
tls_cipher_suite="${tls_hello_ascii:$sid_offset:4}" tls_alert_ascii="$tls_alert_ascii${tls_hello_ascii:i:msg_len}"
let sid_offset=$sid_len_offset+6++$tls_sid_len*2 fi
tls_compression_method="${tls_hello_ascii:$sid_offset:2}" done
# Now check the alert messages.
tls_alert_ascii_len=${#tls_alert_ascii}
if [[ $tls_alert_ascii_len -gt 0 ]]; then
debugme echo "TLS alert messages:"
for (( i=0; i+3 < tls_alert_ascii_len; i=i+4 )); do
tls_err_level=${tls_alert_ascii:i:2} # 1: warning, 2: fatal
j=$i+2
tls_err_descr=${tls_alert_ascii:j:2} # 112/0x70: Unrecognized name, 111/0x6F: certificate_unobtainable,
# 113/0x71: bad_certificate_status_response, #114/0x72: bad_certificate_hash_value
debugme out " tls_err_descr: 0x${tls_err_descr} / = $(hex2dec ${tls_err_descr})"
case $tls_err_descr in
00) tls_alert_descrip="close notify" ;;
0A) tls_alert_descrip="unexpected message" ;;
14) tls_alert_descrip="bad record mac" ;;
15) tls_alert_descrip="decryption failed" ;;
16) tls_alert_descrip="record overflow" ;;
1E) tls_alert_descrip="decompression failure" ;;
28) tls_alert_descrip="handshake failure" ;;
29) tls_alert_descrip="no certificate RESERVED" ;;
2A) tls_alert_descrip="bad certificate" ;;
2B) tls_alert_descrip="unsupported certificate" ;;
2C) tls_alert_descrip="certificate revoked" ;;
2D) tls_alert_descrip="certificate expired" ;;
2E) tls_alert_descrip="certificate unknown" ;;
2F) tls_alert_descrip="illegal parameter" ;;
30) tls_alert_descrip="unknown ca" ;;
31) tls_alert_descrip="access denied" ;;
32) tls_alert_descrip="decode error" ;;
33) tls_alert_descrip="decrypt error" ;;
3C) tls_alert_descrip="export restriction RESERVED" ;;
46) tls_alert_descrip="protocol version" ;;
47) tls_alert_descrip="insufficient security" ;;
50) tls_alert_descrip="internal error" ;;
56) tls_alert_descrip="inappropriate fallback" ;;
5A) tls_alert_descrip="user canceled" ;;
64) tls_alert_descrip="no renegotiation" ;;
6E) tls_alert_descrip="unsupported extension" ;;
6F) tls_alert_descrip="certificate unobtainable" ;;
70) tls_alert_descrip="unrecognized name" ;;
71) tls_alert_descrip="bad certificate status response" ;;
72) tls_alert_descrip="bad certificate hash value" ;;
73) tls_alert_descrip="unknown psk identity" ;;
78) tls_alert_descrip="no application protocol" ;;
*) tls_alert_descrip="$(hex2dec "$tls_err_descr")";;
esac
if [[ $DEBUG -ge 2 ]]; then if [[ $DEBUG -ge 2 ]]; then
echo "tls_protocol (reclyr): 0x$tls_protocol" outln " ($tls_alert_descrip)"
echo "tls_hello: 0x$tls_hello" out " tls_err_level: ${tls_err_level}"
if [[ $DEBUG -ge 4 ]]; then case $tls_err_level in
echo "tls_protocol: 0x$tls_protocol2" 01) outln " (warning)" ;;
echo "tls_sid_len: 0x$(dec2hex $tls_sid_len) / = $tls_sid_len" 02) outln " (fatal)" ;;
*) outln ;;
esac
outln
fi fi
echo -n "tls_hello_time: 0x$tls_hello_time " if [[ "$tls_err_level" != "01" ]] && [[ "$tls_err_level" != "02" ]]; then
debugme pr_warningln "Unexpected AlertLevel (0x$tls_err_level)."
return 1
elif [[ "$tls_err_level" == "02" ]]; then
# Fatal alert
return 1
fi
done
fi
# Now extract just the server hello handshake message.
tls_handshake_ascii_len=${#tls_handshake_ascii}
if [[ $DEBUG -ge 2 ]] && [[ $tls_handshake_ascii_len -gt 0 ]]; then
echo "TLS handshake messages:"
fi
for (( i=0; i<tls_handshake_ascii_len; i=i+msg_len )); do
if [[ $tls_handshake_ascii_len-$i -lt 8 ]]; then
# This could just be a result of the server's response being
# split across two or more packets.
continue
fi
tls_msg_type="${tls_handshake_ascii:i:2}"
i=$i+2
msg_len=2*$(hex2dec "${tls_handshake_ascii:i:6}")
i=$i+6
if [[ $DEBUG -ge 2 ]]; then
out " handshake type: 0x${tls_msg_type}"
case $tls_msg_type in
00) outln " (hello_request)" ;;
01) outln " (client_hello)" ;;
02) outln " (server_hello)" ;;
03) outln " (hello_verify_request)" ;;
04) outln " (NewSessionTicket)" ;;
0B) outln " (certificate)" ;;
0C) outln " (server_key_exchange)" ;;
0D) outln " (certificate_request)" ;;
0E) outln " (server_hello_done)" ;;
0F) outln " (certificate_verify)" ;;
10) outln " (client_key_exchange)" ;;
14) outln " (finished)" ;;
15) outln " (certificate_url)" ;;
16) outln " (certificate_status)" ;;
17) outln " (supplemental_data)" ;;
*) outln ;;
esac
echo " msg_len: $((msg_len/2))"
outln
fi
if [[ $msg_len -gt $tls_handshake_ascii_len-$i ]]; then
# This could just be a result of the server's response being
# split across two or more packets. Just grab the part that
# is available.
msg_len=$tls_handshake_ascii_len-$i
fi
if [[ "$tls_msg_type" == "02" ]]; then
if [[ -n "$tls_serverhello_ascii" ]]; then
debugme pr_warningln "Response contained more than one ServerHello handshake message."
return 1
fi
tls_serverhello_ascii="${tls_handshake_ascii:i:msg_len}"
tls_serverhello_ascii_len=$msg_len
fi
done
if [[ $tls_serverhello_ascii_len -eq 0 ]]; then
debugme echo "server hello empty, TCP connection closed"
return 1 # no server hello received
elif [[ $tls_serverhello_ascii_len -lt 76 ]]; then
debugme echo "Malformed response"
return 1
elif [[ "${tls_handshake_ascii:0:2}" != "02" ]]; then
# the ServerHello MUST be the first handshake message
debugme pr_warningln "The first handshake protocol message is not a ServerHello."
return 1
fi
# Parse the server hello handshake message
# byte 0+1: 03, TLS version word see byte 1+2
# byte 2-5: TLS timestamp for OpenSSL <1.01f
# byte 6-33: random, 28 bytes
# byte 34: session id length
# byte 35+36+sid-len: cipher suite!
# byte 37+sid-len: compression method: 00: none, 01: deflate, 64: LZS
# byte 38+39+sid-len: extension length
tls_protocol2="${tls_serverhello_ascii:0:4}"
if [[ "${tls_protocol2:0:2}" != "03" ]]; then
debugme pr_warningln "server_version.major in ServerHello is not 03."
return 1
fi
DETECTED_TLS_VERSION="$tls_protocol2"
tls_hello_time="${tls_serverhello_ascii:4:8}"
TLS_TIME=$(hex2dec "$tls_hello_time")
tls_sid_len_hex="${tls_serverhello_ascii:68:2}"
tls_sid_len=2*$(hex2dec "$tls_sid_len_hex")
let offset=70+$tls_sid_len
if [[ $tls_serverhello_ascii_len -lt 76+$tls_sid_len ]]; then
debugme echo "Malformed response"
return 1
fi
tls_cipher_suite="${tls_serverhello_ascii:$offset:4}"
let offset=74+$tls_sid_len
tls_compression_method="${tls_serverhello_ascii:$offset:2}"
if [[ $DEBUG -ge 2 ]]; then
echo "TLS server hello message:"
if [[ $DEBUG -ge 4 ]]; then
echo " tls_protocol: 0x$tls_protocol2"
echo " tls_sid_len: 0x$tls_sid_len_hex / = $((tls_sid_len/2))"
fi
echo -n " tls_hello_time: 0x$tls_hello_time "
if "$HAS_GNUDATE"; then if "$HAS_GNUDATE"; then
date --date="@$TLS_TIME" "+%Y-%m-%d %r" date --date="@$TLS_TIME" "+%Y-%m-%d %r"
else else
LC_ALL=C date -j -f %s "$TLS_TIME" "+%Y-%m-%d %r" LC_ALL=C date -j -f %s "$TLS_TIME" "+%Y-%m-%d %r"
fi fi
echo "tls_cipher_suite: 0x$tls_cipher_suite" echo " tls_cipher_suite: 0x$tls_cipher_suite"
echo "tls_compression_method: 0x$tls_compression_method" echo -n " tls_compression_method: 0x$tls_compression_method "
case $tls_compression_method in
00) echo "(NONE)" ;;
01) echo "(zlib compression)" ;;
40) echo "(LZS compression)" ;;
*) echo "(unrecognized compression method)" ;;
esac
outln outln
fi fi
return 0 return 0
@ -4149,7 +4389,7 @@ sslv2_sockets() {
outln " (rerun with DEBUG >=2)" outln " (rerun with DEBUG >=2)"
[[ $DEBUG -ge 3 ]] && hexdump -C "$SOCK_REPLY_FILE" | head -1 [[ $DEBUG -ge 3 ]] && hexdump -C "$SOCK_REPLY_FILE" | head -1
ret=7 ret=7
fileout "sslv2" "WARN" "SSLv2: received a strange SSLv2 replay (rerun with DEBUG>=2)" fileout "sslv2" "WARN" "SSLv2: received a strange SSLv2 reply (rerun with DEBUG>=2)"
;; ;;
1) # no sslv2 server hello returned, like in openlitespeed which returns HTTP! 1) # no sslv2 server hello returned, like in openlitespeed which returns HTTP!
pr_done_bestln "not offered (OK)" pr_done_bestln "not offered (OK)"
@ -5115,7 +5355,7 @@ run_drown() {
outln " (rerun with DEBUG >=2)" outln " (rerun with DEBUG >=2)"
[[ $DEBUG -ge 3 ]] && hexdump -C "$SOCK_REPLY_FILE" | head -1 [[ $DEBUG -ge 3 ]] && hexdump -C "$SOCK_REPLY_FILE" | head -1
ret=7 ret=7
fileout "DROWN" "MINOR_ERROR" "SSLv2: received a strange SSLv2 replay (rerun with DEBUG>=2)" fileout "DROWN" "MINOR_ERROR" "SSLv2: received a strange SSLv2 reply (rerun with DEBUG>=2)"
;; ;;
3) # vulnerable 3) # vulnerable
lines=$(count_lines "$(hexdump -C "$SOCK_REPLY_FILE" 2>/dev/null)") lines=$(count_lines "$(hexdump -C "$SOCK_REPLY_FILE" 2>/dev/null)")
@ -5371,9 +5611,9 @@ run_rc4() {
fi fi
outln outln
else else
pr_svrty_high "$rc4_cipher " [[ $sclient_success -eq 0 ]] && pr_svrty_high "$rc4_cipher "
fi fi
rc4_detected+="$rc4_cipher " [[ $sclient_success -eq 0 ]] && rc4_detected+="$rc4_cipher "
done < <($OPENSSL ciphers -V $rc4_ciphers_list:@STRENGTH) done < <($OPENSSL ciphers -V $rc4_ciphers_list:@STRENGTH)
outln outln
"$WIDE" && pr_svrty_high "VULNERABLE (NOT ok)" "$WIDE" && pr_svrty_high "VULNERABLE (NOT ok)"
@ -5408,11 +5648,11 @@ run_tls_truncation() {
old_fart() { old_fart() {
outln "Get precompiled bins or compile https://github.com/PeterMosmans/openssl ." outln "Get precompiled bins or compile https://github.com/PeterMosmans/openssl ."
fileout "old_fart" "WARN" "Your $OPENSSL $OSSL_VER version is an old fart... . It doesn\'t make much sense to proceed.\nGet precompiled bins or compile https://github.com/PeterMosmans/openssl ." fileout "old_fart" "WARN" "Your $OPENSSL $OSSL_VER version is an old fart... . It doesn\'t make much sense to proceed. Get precompiled bins or compile https://github.com/PeterMosmans/openssl ."
fatal "Your $OPENSSL $OSSL_VER version is an old fart... . It doesn\'t make much sense to proceed." -2 fatal "Your $OPENSSL $OSSL_VER version is an old fart... . It doesn\'t make much sense to proceed." -2
} }
# try very hard to determine th install path to get ahold of the mapping file # try very hard to determine the install path to get ahold of the mapping file
# it provides "keycode/ RFC style name", see RFCs, cipher(1), www.carbonwind.net/TLS_Cipher_Suites_Project/tls_ssl_cipher_suites_simple_table_all.htm # it provides "keycode/ RFC style name", see RFCs, cipher(1), www.carbonwind.net/TLS_Cipher_Suites_Project/tls_ssl_cipher_suites_simple_table_all.htm
get_install_dir() { get_install_dir() {
#INSTALL_DIR=$(cd "$(dirname "$0")" && pwd)/$(basename "$0") #INSTALL_DIR=$(cd "$(dirname "$0")" && pwd)/$(basename "$0")
@ -5609,7 +5849,8 @@ special invocations:
partly mandatory parameters: partly mandatory parameters:
URI host|host:port|URL|URL:port (port 443 is assumed unless otherwise specified) URI host|host:port|URL|URL:port (port 443 is assumed unless otherwise specified)
pattern an ignore case word pattern of cipher hexcode or any other string in the name, kx or bits pattern an ignore case word pattern of cipher hexcode or any other string in the name, kx or bits
protocol is one of ftp,smtp,pop3,imap,xmpp,telnet,ldap (for the latter two you need e.g. the supplied openssl) protocol is one of the STARTTLS protocols ftp,smtp,pop3,imap,xmpp,telnet,ldap
(for the latter two you need e.g. the supplied openssl)
tuning options (can also be preset via environment variables): tuning options (can also be preset via environment variables):
--bugs enables the "-bugs" option of s_client, needed e.g. for some buggy F5s --bugs enables the "-bugs" option of s_client, needed e.g. for some buggy F5s
@ -5867,6 +6108,11 @@ parse_hn_port() {
# strip trailing urlpath # strip trailing urlpath
NODE=$(echo "$NODE" | sed -e 's/\/.*$//') NODE=$(echo "$NODE" | sed -e 's/\/.*$//')
# if there's a trailing ':' probably a starttls/application protocol was specified
if grep -q ':$' <<< $NODE ; then
fatal "\"$1\" is not a valid URI" 1
fi
# was the address supplied like [AA:BB:CC::]:port ? # was the address supplied like [AA:BB:CC::]:port ?
if echo "$NODE" | grep -q ']' ; then if echo "$NODE" | grep -q ']' ; then
tmp_port=$(printf "$NODE" | sed 's/\[.*\]//' | sed 's/://') tmp_port=$(printf "$NODE" | sed 's/\[.*\]//' | sed 's/://')
@ -6190,7 +6436,7 @@ sclient_auth() {
if [[ -z $(awk '/Session-ID: / { print $2 }' "$2") ]]; then # probably no SSL session if [[ -z $(awk '/Session-ID: / { print $2 }' "$2") ]]; then # probably no SSL session
if [[ 2 -eq $(grep -c CERTIFICATE "$2") ]]; then # do another sanity check to be sure if [[ 2 -eq $(grep -c CERTIFICATE "$2") ]]; then # do another sanity check to be sure
CLIENT_AUTH=false CLIENT_AUTH=false
NO_SSL_SESSIONID=true # NO_SSL_SESSIONI is preset globally to false for all other cases NO_SSL_SESSIONID=true # NO_SSL_SESSIONID is preset globally to false for all other cases
return 0 return 0
fi fi
fi fi
@ -6408,6 +6654,7 @@ mx_all_ips() {
run_mass_testing_parallel() { run_mass_testing_parallel() {
local cmdline="" local cmdline=""
local global_cmdline=${CMDLINE%%--file*}
if [[ ! -r "$FNAME" ]] && $IKNOW_FNAME; then if [[ ! -r "$FNAME" ]] && $IKNOW_FNAME; then
fatal "Can't read file \"$FNAME\"" "-1" fatal "Can't read file \"$FNAME\"" "-1"
@ -6418,7 +6665,7 @@ run_mass_testing_parallel() {
cmdline=$(filter_input "$cmdline") cmdline=$(filter_input "$cmdline")
[[ -z "$cmdline" ]] && continue [[ -z "$cmdline" ]] && continue
[[ "$cmdline" == "EOF" ]] && break [[ "$cmdline" == "EOF" ]] && break
cmdline="$0 --warnings=batch -q $cmdline" cmdline="$0 $global_cmdline --warnings=batch -q $cmdline"
draw_line "=" $((TERM_DWITH / 2)); outln; draw_line "=" $((TERM_DWITH / 2)); outln;
determine_logfile determine_logfile
outln "$cmdline" outln "$cmdline"
@ -6431,16 +6678,18 @@ run_mass_testing_parallel() {
run_mass_testing() { run_mass_testing() {
local cmdline="" local cmdline=""
local global_cmdline=${CMDLINE%%--file*}
if [[ ! -r "$FNAME" ]] && "$IKNOW_FNAME"; then if [[ ! -r "$FNAME" ]] && "$IKNOW_FNAME"; then
fatal "Can't read file \"$FNAME\"" "-1" fatal "Can't read file \"$FNAME\"" "-1"
fi fi
pr_reverse "====== Running in file batch mode with file=\"$FNAME\" ======"; outln "\n" pr_reverse "====== Running in file batch mode with file=\"$FNAME\" ======"; outln "\n"
while read cmdline; do while read cmdline; do
cmdline=$(filter_input "$cmdline") cmdline=$(filter_input "$cmdline")
[[ -z "$cmdline" ]] && continue [[ -z "$cmdline" ]] && continue
[[ "$cmdline" == "EOF" ]] && break [[ "$cmdline" == "EOF" ]] && break
cmdline="$0 --warnings=batch -q $cmdline" cmdline="$0 $global_cmdline --warnings=batch -q $cmdline"
draw_line "=" $((TERM_DWITH / 2)); outln; draw_line "=" $((TERM_DWITH / 2)); outln;
outln "$cmdline" outln "$cmdline"
$cmdline $cmdline
@ -6794,7 +7043,7 @@ parse_cmd_line() {
do_logging=true do_logging=true
;; # DEFINITION of LOGFILE if no arg specified: automagically in parse_hn_port() ;; # DEFINITION of LOGFILE if no arg specified: automagically in parse_hn_port()
# following does the same but we can specify a log location additionally # following does the same but we can specify a log location additionally
--logfile=*) --logfile|--logfile=*)
LOGFILE=$(parse_opt_equal_sign "$1" "$2") LOGFILE=$(parse_opt_equal_sign "$1" "$2")
[[ $? -eq 0 ]] && shift [[ $? -eq 0 ]] && shift
do_logging=true do_logging=true
@ -6803,7 +7052,7 @@ parse_cmd_line() {
do_json=true do_json=true
;; # DEFINITION of JSONFILE is not arg specified: automagically in parse_hn_port() ;; # DEFINITION of JSONFILE is not arg specified: automagically in parse_hn_port()
# following does the same but we can specify a log location additionally # following does the same but we can specify a log location additionally
--jsonfile=*) --jsonfile|--jsonfile=*)
JSONFILE=$(parse_opt_equal_sign "$1" "$2") JSONFILE=$(parse_opt_equal_sign "$1" "$2")
[[ $? -eq 0 ]] && shift [[ $? -eq 0 ]] && shift
do_json=true do_json=true
@ -6812,7 +7061,7 @@ parse_cmd_line() {
do_csv=true do_csv=true
;; # DEFINITION of CSVFILE is not arg specified: automagically in parse_hn_port() ;; # DEFINITION of CSVFILE is not arg specified: automagically in parse_hn_port()
# following does the same but we can specify a log location additionally # following does the same but we can specify a log location additionally
--csvfile=*) --csvfile|--csvfile=*)
CSVFILE=$(parse_opt_equal_sign "$1" "$2") CSVFILE=$(parse_opt_equal_sign "$1" "$2")
[[ $? -eq 0 ]] && shift [[ $? -eq 0 ]] && shift
do_csv=true do_csv=true
@ -7028,4 +7277,4 @@ fi
exit $? exit $?
# $Id: testssl.sh,v 1.478 2016/03/30 21:28:30 dirkw Exp $ # $Id: testssl.sh,v 1.487 2016/05/23 20:42:39 dirkw Exp $