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.
# 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
# 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.
#
# 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
# OpenSSL, which is being used and maybe distributed via one of this projects'
# 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
# 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
# wrapping some shell commands around it, which I used for my pen tests. This is how
# 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.
# 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
@ -204,7 +204,6 @@ HAS_SSL2=false
HAS_SSL3=false
HAS_ALPN=false
HAS_SPDY=false
HAS_SSL2=false
ADD_RFC_STR="rfc" # display RFC ciphernames
PORT=443 # unless otherwise auto-determined, see below
NODE=""
@ -225,7 +224,7 @@ URI=""
CERT_FINGERPRINT_SHA2=""
SHOW_CENSYS_LINK=${SHOW_CENSYS_LINK:-true}
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
STARTTLS_OPTIMAL_PROTO="" # same for STARTTLS, see https://github.com/drwetter/testssl.sh/issues/188
TLS_TIME=""
@ -489,11 +488,6 @@ hex2dec() {
echo $((16#$1))
}
dec2hex() {
#/usr/bin/printf -- "%x" "$1"
echo $((0x$1))
}
# trim spaces for BSD and old sed
count_lines() {
wc -l <<<"$1" | sed 's/ //g'
@ -966,7 +960,7 @@ run_hpkp() {
out "# of keys: "
if [[ $hpkp_nr_keys -eq 1 ]]; then
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
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"
@ -1077,9 +1071,9 @@ run_server_banner() {
emphasize_stuff_in_headers "$serverbanner"
fileout "serverbanner" "INFO" "Server banner identified: $serverbanner"
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
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
# mozilla.github.io/server-side-tls/ssl-config-generator/
@ -1393,8 +1387,8 @@ std_cipherlists() {
;;
3) # not totally bad
if [[ $sclient_success -eq 0 ]]; then
pr_svrty_mediumln "offered (NOT ok)"
fileout "std_$4" "NOT ok" "$2 offered (NOT ok) - not too bad"
pr_svrty_mediumln "offered"
fileout "std_$4" "NOT ok" "$2 offered - not too bad"
else
outln "not offered (OK)"
fileout "std_$4" "OK" "$2 not offered (OK)"
@ -1492,7 +1486,7 @@ neat_list(){
#printf -- "%q" "$kx" | xxd | head -1
# 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
kx="$kx "
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() {
local tmpfile
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")
tlsvers+=("-tls1_2 -tls1_1 -tls1")
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+=("")
names+=("Safari 5.1.9/ OSX 10.6.8 ")
@ -2157,7 +2151,7 @@ run_client_simulation() {
#FIXME: awk
proto=$(grep -aw "Protocol" $TMPFILE | sed -e 's/^.*Protocol.*://' -e 's/ //g')
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
$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"
@ -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.
# 2) the code is too old and one can do that way better
# 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
# 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)"
;; # no GCM, penalty
2)
pr_svrty_medium "not offered (NOT ok)"
pr_svrty_medium "not offered"
[[ $DEBUG -eq 1 ]] && out " -- downgraded"
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)
outln "$supported_no_ciph1"
@ -2587,8 +2581,8 @@ run_server_preference() {
"")
pr_warning "default proto empty"
if [[ $OSSL_VER == 1.0.2* ]]; then
outln " (Hint: if IIS6 give OpenSSL 1.01 a try)"
fileout "order_proto" "WARN" "Default protocol empty (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.0.1 a try)"
else
fileout "order_proto" "WARN" "Default protocol empty"
fi
@ -2626,8 +2620,8 @@ run_server_preference() {
"")
pr_warning "default cipher empty" ;
if [[ $OSSL_VER == 1.0.2* ]]; then
out " (Hint: if IIS6 give OpenSSL 1.01 a try)"
fileout "order_cipher" "WARN" "Default cipher empty (Hint: if IIS6 give OpenSSL 1.01 a try) $remark4default_cipher"
out " (Hint: if IIS6 give OpenSSL 1.0.1 a try)"
fileout "order_cipher" "WARN" "Default cipher empty (Hint: if IIS6 give OpenSSL 1.0.1 a try) $remark4default_cipher"
else
fileout "order_cipher" "WARN" "Default cipher empty $remark4default_cipher"
fi
@ -2646,11 +2640,11 @@ run_server_preference() {
i=1
for p in ssl2 ssl3 tls1 tls1_1 tls1_2; do
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
fi
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
fi
$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
order=""
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
fi
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
fi
$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 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 "02" "$TLS_CIPHER" # TLS 1.1
[[ -z "$TLS_TIME" ]] && tls_sockets "00" "$TLS_CIPHER" # SSL 3
@ -3049,7 +3043,7 @@ certificate_info() {
local cert_keysize=$4
local ocsp_response=$5
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 cert_fingerprint_sha1 cert_fingerprint_sha2 cert_fingerprint_serial
local policy_oid
@ -3082,6 +3076,10 @@ certificate_info() {
pr_svrty_mediumln "SHA1 with RSA"
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)
pr_done_goodln "SHA256 with RSA"
fileout "${json_prefix}algorithm" "OK" "Signature Algorithm: SHA256 with RSA (OK)"
@ -3094,10 +3092,75 @@ certificate_info() {
pr_done_goodln "SHA512 with RSA"
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)
pr_done_goodln "ECDSA with SHA256"
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*)
pr_svrty_criticalln "MD5"
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
# see http://csrc.nist.gov/publications/nistpubs/800-57/sp800-57_part1_rev3_general.pdf
# 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
pr_svrty_critical "$cert_keysize"
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)"
fileout "${json_prefix}key_size" "WARN" "Server keys $cert_keysize bits (not expected)"
fi
outln " bit"
elif [[ $cert_sig_algo = *RSA* ]]; then
outln " bits"
elif [[ $cert_key_algo = *RSA* ]] || [[ $cert_key_algo = *rsa* ]] || [[ $cert_key_algo = *dsa* ]]; then
if [[ "$cert_keysize" -le 512 ]]; then
pr_svrty_critical "$cert_keysize"
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
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' || \
[[ 2.16.840.1.114028.10.1.2 == "$policy_oid" ]] || \
[[ 2.16.840.1.114412.1.3.0.2 == "$policy_oid" ]] || \
@ -3410,7 +3474,7 @@ certificate_info() {
ret=0
else
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"
ret=2
fi
@ -3576,7 +3640,7 @@ run_pfs() {
local pfs_ciphers
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
pr_warningln " (Your $OPENSSL cannot show DH/ECDH bits)"
fi
@ -3640,7 +3704,7 @@ run_pfs() {
out "$pfs_cipher "
fi
fi
pfs_ciphers+="$pfs_cipher "
[[ $sclient_success -eq 0 ]] && pfs_ciphers+="$pfs_cipher "
debugme rm $tmpfile
done < <($OPENSSL ciphers -V "$pfs_cipher_list" 2>$ERRFILE) # -V doesn't work with openssl < 1.0
debugme echo $pfs_offered
@ -3734,8 +3798,8 @@ run_spdy() {
fileout "spdy_npn" "INFO" "SPDY/NPN : $tmpstr (advertised)"
ret=0
else
pr_cyanln "please check manually, server response was ambigious ..."
fileout "spdy_npn" "INFO" "SPDY/NPN : 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 ambiguous ..."
ret=10
fi
fi
@ -4036,92 +4100,268 @@ parse_sslv2_serverhello() {
# arg1: name of file with socket reply
parse_tls_serverhello() {
local tls_hello_ascii=$(hexdump -v -e '16/1 "%02X"' "$1")
local tls_content_type tls_protocol tls_len_all
#TODO: all vars here
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=""
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=""
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 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 5: handshake type (2=hello) TLS alert: level (2=fatal), descr (0x28=handshake failure)
# byte 6+7+8: length server hello
# byte 9+10: 03, TLS version word see byte 1+2
# byte 11-14: TLS timestamp for OpenSSL <1.01f
# 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
# byte 3+4: fragment length
# bytes 5...: message fragment
tls_hello_ascii_len=${#tls_hello_ascii}
if [[ $DEBUG -ge 2 ]] && [[ $tls_hello_ascii_len -gt 0 ]]; then
echo "TLS message fragments:"
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
echo "tls_protocol (reclyr): 0x$tls_protocol"
echo "tls_content_type: 0x$tls_content_type"
echo "tls_len_all: $tls_len_all"
echo "tls_err_descr: 0x${tls_err_descr} / = $(hex2dec ${tls_err_descr})"
echo "tls_err_level: ${tls_err_level} (warning:1, fatal:2)"
echo " tls_protocol (reclyr): 0x$tls_protocol"
out " tls_content_type: 0x$tls_content_type"
case $tls_content_type in
15) outln " (alert)" ;;
16) outln " (handshake)" ;;
*) outln ;;
esac
echo " msg_len: $((msg_len/2))"
outln
fi
# now, here comes a strange thing... -- on the first glance
# IF an apache 2.2/2.4 server e.g. has a default servername configured but we send SNI <myhostname>
# we get a TLS ALERT saying "unrecognized_name" (0x70) and a warning (0x1), see RFC https://tools.ietf.org/html/rfc6066#page-17
# note that RFC recommended to fail instead: https://tools.ietf.org/html/rfc6066#section-3
# we need to handle this properly -- otherwise we always return that the protocol or cipher is not available!
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
if [[ $tls_content_type != "15" ]] && [[ $tls_content_type != "16" ]]; then
debugme pr_warningln "Content type other than alert or handshake detected."
return 1
elif [[ "${tls_protocol:0:2}" != "03" ]]; then
debugme pr_warningln "Protocol record_version.major is not 03."
return 1
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
TLS_TIME=$(hex2dec "$tls_hello_time")
tls_sid_len=$(hex2dec "${tls_hello_ascii:$sid_len_offset:2}")
let sid_offset=$sid_len_offset+2+$tls_sid_len*2
tls_cipher_suite="${tls_hello_ascii:$sid_offset:4}"
let sid_offset=$sid_len_offset+6++$tls_sid_len*2
tls_compression_method="${tls_hello_ascii:$sid_offset:2}"
if [[ $tls_content_type == "16" ]]; then
tls_handshake_ascii="$tls_handshake_ascii${tls_hello_ascii:i:msg_len}"
elif [[ $tls_content_type == "15" ]]; then # TLS ALERT
tls_alert_ascii="$tls_alert_ascii${tls_hello_ascii:i:msg_len}"
fi
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
echo "tls_protocol (reclyr): 0x$tls_protocol"
echo "tls_hello: 0x$tls_hello"
if [[ $DEBUG -ge 4 ]]; then
echo "tls_protocol: 0x$tls_protocol2"
echo "tls_sid_len: 0x$(dec2hex $tls_sid_len) / = $tls_sid_len"
outln " ($tls_alert_descrip)"
out " tls_err_level: ${tls_err_level}"
case $tls_err_level in
01) outln " (warning)" ;;
02) outln " (fatal)" ;;
*) outln ;;
esac
outln
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
date --date="@$TLS_TIME" "+%Y-%m-%d %r"
else
LC_ALL=C date -j -f %s "$TLS_TIME" "+%Y-%m-%d %r"
fi
echo "tls_cipher_suite: 0x$tls_cipher_suite"
echo "tls_compression_method: 0x$tls_compression_method"
echo " tls_cipher_suite: 0x$tls_cipher_suite"
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
fi
return 0
@ -4149,7 +4389,7 @@ sslv2_sockets() {
outln " (rerun with DEBUG >=2)"
[[ $DEBUG -ge 3 ]] && hexdump -C "$SOCK_REPLY_FILE" | head -1
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!
pr_done_bestln "not offered (OK)"
@ -5115,7 +5355,7 @@ run_drown() {
outln " (rerun with DEBUG >=2)"
[[ $DEBUG -ge 3 ]] && hexdump -C "$SOCK_REPLY_FILE" | head -1
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
lines=$(count_lines "$(hexdump -C "$SOCK_REPLY_FILE" 2>/dev/null)")
@ -5371,9 +5611,9 @@ run_rc4() {
fi
outln
else
pr_svrty_high "$rc4_cipher "
[[ $sclient_success -eq 0 ]] && pr_svrty_high "$rc4_cipher "
fi
rc4_detected+="$rc4_cipher "
[[ $sclient_success -eq 0 ]] && rc4_detected+="$rc4_cipher "
done < <($OPENSSL ciphers -V $rc4_ciphers_list:@STRENGTH)
outln
"$WIDE" && pr_svrty_high "VULNERABLE (NOT ok)"
@ -5408,11 +5648,11 @@ run_tls_truncation() {
old_fart() {
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
}
# 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
get_install_dir() {
#INSTALL_DIR=$(cd "$(dirname "$0")" && pwd)/$(basename "$0")
@ -5609,7 +5849,8 @@ special invocations:
partly mandatory parameters:
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
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):
--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
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 ?
if echo "$NODE" | grep -q ']' ; then
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 [[ 2 -eq $(grep -c CERTIFICATE "$2") ]]; then # do another sanity check to be sure
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
fi
fi
@ -6408,6 +6654,7 @@ mx_all_ips() {
run_mass_testing_parallel() {
local cmdline=""
local global_cmdline=${CMDLINE%%--file*}
if [[ ! -r "$FNAME" ]] && $IKNOW_FNAME; then
fatal "Can't read file \"$FNAME\"" "-1"
@ -6418,7 +6665,7 @@ run_mass_testing_parallel() {
cmdline=$(filter_input "$cmdline")
[[ -z "$cmdline" ]] && continue
[[ "$cmdline" == "EOF" ]] && break
cmdline="$0 --warnings=batch -q $cmdline"
cmdline="$0 $global_cmdline --warnings=batch -q $cmdline"
draw_line "=" $((TERM_DWITH / 2)); outln;
determine_logfile
outln "$cmdline"
@ -6431,16 +6678,18 @@ run_mass_testing_parallel() {
run_mass_testing() {
local cmdline=""
local global_cmdline=${CMDLINE%%--file*}
if [[ ! -r "$FNAME" ]] && "$IKNOW_FNAME"; then
fatal "Can't read file \"$FNAME\"" "-1"
fi
pr_reverse "====== Running in file batch mode with file=\"$FNAME\" ======"; outln "\n"
while read cmdline; do
cmdline=$(filter_input "$cmdline")
[[ -z "$cmdline" ]] && continue
[[ "$cmdline" == "EOF" ]] && break
cmdline="$0 --warnings=batch -q $cmdline"
cmdline="$0 $global_cmdline --warnings=batch -q $cmdline"
draw_line "=" $((TERM_DWITH / 2)); outln;
outln "$cmdline"
$cmdline
@ -6794,7 +7043,7 @@ parse_cmd_line() {
do_logging=true
;; # DEFINITION of LOGFILE if no arg specified: automagically in parse_hn_port()
# following does the same but we can specify a log location additionally
--logfile=*)
--logfile|--logfile=*)
LOGFILE=$(parse_opt_equal_sign "$1" "$2")
[[ $? -eq 0 ]] && shift
do_logging=true
@ -6803,7 +7052,7 @@ parse_cmd_line() {
do_json=true
;; # DEFINITION of JSONFILE is not arg specified: automagically in parse_hn_port()
# following does the same but we can specify a log location additionally
--jsonfile=*)
--jsonfile|--jsonfile=*)
JSONFILE=$(parse_opt_equal_sign "$1" "$2")
[[ $? -eq 0 ]] && shift
do_json=true
@ -6812,7 +7061,7 @@ parse_cmd_line() {
do_csv=true
;; # DEFINITION of CSVFILE is not arg specified: automagically in parse_hn_port()
# following does the same but we can specify a log location additionally
--csvfile=*)
--csvfile|--csvfile=*)
CSVFILE=$(parse_opt_equal_sign "$1" "$2")
[[ $? -eq 0 ]] && shift
do_csv=true
@ -7028,4 +7277,4 @@ fi
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 $