Back to our original commit

This commit is contained in:
Frank Breedijk 2016-07-04 17:35:19 +02:00
parent 4efb6531c2
commit b24fdc5e21

View File

@ -83,7 +83,7 @@ readonly PS4='${LINENO}> ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'
# make sure that temporary files are cleaned up after use in ANY case # make sure that temporary files are cleaned up after use in ANY case
trap "cleanup" QUIT EXIT trap "cleanup" QUIT EXIT
readonly VERSION="2.8rc1" readonly VERSION="2.7dev"
readonly SWCONTACT="dirk aet testssl dot sh" readonly SWCONTACT="dirk aet testssl dot sh"
egrep -q "dev|rc" <<< "$VERSION" && \ egrep -q "dev|rc" <<< "$VERSION" && \
SWURL="https://testssl.sh/dev/" || SWURL="https://testssl.sh/dev/" ||
@ -170,12 +170,10 @@ USLEEP_SND=${USLEEP_SND:-0.1} # sleep time for general socket send
USLEEP_REC=${USLEEP_REC:-0.2} # sleep time for general socket receive USLEEP_REC=${USLEEP_REC:-0.2} # sleep time for general socket receive
HSTS_MIN=${HSTS_MIN:-179} # >179 days is ok for HSTS HSTS_MIN=${HSTS_MIN:-179} # >179 days is ok for HSTS
HPKP_MIN=${HPKP_MIN:-30} # >=30 days should be ok for HPKP_MIN, practical hints? HPKP_MIN=${HPKP_MIN:-30} # >=30 days should be ok for HPKP_MIN, practical hints?
readonly CLIENT_MIN_PFS=5 # number of ciphers needed to run a test for PFS
DAYS2WARN1=${DAYS2WARN1:-60} # days to warn before cert expires, threshold 1 DAYS2WARN1=${DAYS2WARN1:-60} # days to warn before cert expires, threshold 1
DAYS2WARN2=${DAYS2WARN2:-30} # days to warn before cert expires, threshold 2 DAYS2WARN2=${DAYS2WARN2:-30} # days to warn before cert expires, threshold 2
VULN_THRESHLD=${VULN_THRESHLD:-1} # if vulnerabilities to check >$VULN_THRESHLD we DON'T show a separate header line in the output each vuln. check VULN_THRESHLD=${VULN_THRESHLD:-1} # if vulnerabilities to check >$VULN_THRESHLD we DON'T show a separate header line in the output each vuln. check
readonly CLIENT_MIN_PFS=5 # number of ciphers needed to run a test for PFS
# generated from 'kEECDH:kEDH:!aNULL:!eNULL:!DES:!3DES:!RC4' with openssl 1.0.2i and openssl 1.1.0
readonly ROBUST_PFS_CIPHERS="DHE-DSS-AES128-GCM-SHA256:DHE-DSS-AES128-SHA256:DHE-DSS-AES128-SHA:DHE-DSS-AES256-GCM-SHA384:DHE-DSS-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-DSS-CAMELLIA128-SHA256:DHE-DSS-CAMELLIA128-SHA:DHE-DSS-CAMELLIA256-SHA256:DHE-DSS-CAMELLIA256-SHA:DHE-DSS-SEED-SHA:DHE-RSA-AES128-CCM8:DHE-RSA-AES128-CCM:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-CCM8:DHE-RSA-AES256-CCM:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-CAMELLIA128-SHA256:DHE-RSA-CAMELLIA128-SHA:DHE-RSA-CAMELLIA256-SHA256:DHE-RSA-CAMELLIA256-SHA:DHE-RSA-CHACHA20-POLY1305-OLD:DHE-RSA-CHACHA20-POLY1305:DHE-RSA-SEED-SHA:ECDHE-ECDSA-AES128-CCM8:ECDHE-ECDSA-AES128-CCM:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-ECDSA-AES256-CCM8:ECDHE-ECDSA-AES256-CCM:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-ECDSA-CAMELLIA128-SHA256:ECDHE-ECDSA-CAMELLIA256-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305-OLD:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-RSA-CAMELLIA128-SHA256:ECDHE-RSA-CAMELLIA256-SHA384:ECDHE-RSA-CHACHA20-POLY1305-OLD:ECDHE-RSA-CHACHA20-POLY1305"
HAD_SLEPT=0 HAD_SLEPT=0
CAPATH="${CAPATH:-/etc/ssl/certs/}" # Does nothing yet (FC has only a CA bundle per default, ==> openssl version -d) CAPATH="${CAPATH:-/etc/ssl/certs/}" # Does nothing yet (FC has only a CA bundle per default, ==> openssl version -d)
@ -962,7 +960,6 @@ run_hpkp() {
local i local i
local hpkp_headers local hpkp_headers
local first_hpkp_header local first_hpkp_header
local ca_bundles="$INSTALL_DIR/etc/*.pem"
if [[ ! -s $HEADERFILE ]]; then if [[ ! -s $HEADERFILE ]]; then
run_http_header "$1" || return 3 run_http_header "$1" || return 3
@ -1032,98 +1029,21 @@ run_hpkp() {
if [[ ! -s "$HOSTCERT" ]]; then if [[ ! -s "$HOSTCERT" ]]; then
get_host_cert || return 1 get_host_cert || return 1
fi fi
# Get the pins first
pins=$(tr ';' '\n' < $TMPFILE | tr -d ' ' | tr -d '\"' | awk -F'=' '/pin.*=/ { print $2 }')
# get the key fingerprint from the host certificate # get the key fingerprint from the host certificate
hpkp_key_hostcert="$($OPENSSL x509 -in $HOSTCERT -pubkey -noout | grep -v PUBLIC | \ hpkp_key_hostcert="$($OPENSSL x509 -in $HOSTCERT -pubkey -noout | grep -v PUBLIC | \
$OPENSSL base64 -d | $OPENSSL dgst -sha256 -binary | $OPENSSL base64)" $OPENSSL base64 -d | $OPENSSL dgst -sha256 -binary | $OPENSSL base64)"
# compare it with the ones provided in the header # compare it with the ones provided in the header
for hpkp_key in $(echo "$pins"); do while read hpkp_key; do
if [[ "$hpkp_key_hostcert" == "$hpkp_key" ]] || [[ "$hpkp_key_hostcert" == "$hpkp_key=" ]]; then if [[ "$hpkp_key_hostcert" == "$hpkp_key" ]] || [[ "$hpkp_key_hostcert" == "$hpkp_key=" ]]; then
out "\n$spaces matching host key: " out "\n$spaces matching host key: "
pr_done_good "$hpkp_key" pr_done_good "$hpkp_key"
fileout "hpkp_keymatch" "OK" "Host certificate key matches a key pinned in the HPKP header" fileout "hpkp_keymatch" "OK" "Key matches a key pinned in the HPKP header"
key_found=true key_found=true
fi fi
debugme out "\n $hpkp_key | $hpkp_key_hostcert" debugme out "\n $hpkp_key | $hpkp_key_hostcert"
done done < <(tr ';' '\n' < $TMPFILE | tr -d ' ' | tr -d '\"' | awk -F'=' '/pin.*=/ { print $2 }')
if ! $key_found ; then
# Get keys from intermediate certificates
$OPENSSL s_client -showcerts $STARTTLS $BUGS $PROXY -showcerts -connect $NODEIP:$PORT ${sni[i]} </dev/null >$TMPFILE 2>$ERRFILE
# Place the server's certificate in $HOSTCERT and any intermediate
# certificates that were provided in $TEMPDIR/intermediatecerts.pem
# http://backreference.org/2010/05/09/ocsp-verification-with-openssl/
awk -v n=-1 "/Certificate chain/ {start=1}
/-----BEGIN CERTIFICATE-----/{ if (start) {inc=1; n++} }
inc { print > (\"$TEMPDIR/level\" n \".crt\") }
/---END CERTIFICATE-----/{ inc=0 }" $TMPFILE
nrsaved=$(count_words "$(echo $TEMPDIR/level?.crt 2>/dev/null)")
rm $TEMPDIR/level0.crt
# Compare them against the hashes found
if [[ nrsaved -ge 2 ]]; then
echo -n "" > "$TEMPDIR/hashes.intermediate"
for cert_fname in $TEMPDIR/level?.crt; do
hpkp_key_ca="$($OPENSSL x509 -in "$cert_fname" -pubkey -noout | grep -v PUBLIC|$OPENSSL dgst -sha256 -binary | $OPENSSL enc -base64)"
issuer="$(get_cn_from_cert $cert_fname)"
[[ -n $hpkp_name ]] || hpkp_name=$($OPENSSL x509 -in "$cert_fname" -subject -noout| sed "s/^subject= //")
echo "$hpkp_key_ca $bundle_name $issuer" >> "$TEMPDIR/hashes.intermediate"
done
for hpkp_key in $(echo $pins); do
hpkp_matches=$(grep "$hpkp_key" $TEMPDIR/hashes.intermediate)
if [[ -n $hpkp_matches ]]; then
# We have a winner!
key_found=true
out "\n$spaces Intermediate CA match : "
pr_done_good "$hpkp_matches"
fileout "hpkp_keymatch" "OK" "Intermediate CA key matches a key pinned in the HPKP header\nKey/CA: $hpkp_matches"
fi
done
fi
fi
if ! $key_found ; then
# Get keys from Root CAs
for bundle_fname in $ca_bundles; do
local bundle_name=$(basename ${bundle_fname//.pem})
if [[ ! -r $bundle_fname ]]; then
pr_warningln "\"$bundle_fname\" cannot be found / not readable"
return 7
fi
debugme printf -- " %-12s" "${certificate_file[i]}"
# Split up the certificate bundle
awk -v n=-1 "BEGIN {start=1}
/-----BEGIN CERTIFICATE-----/{ if (start) {inc=1; n++} }
inc { print >> (\"$TEMPDIR/$bundle_name.\" n \".crt\") ; close (\"$TEMPDIR/$bundle_name.\" n \".crt\") }
/---END CERTIFICATE-----/{ inc=0 }" $bundle_fname
# Clear temp file
echo -n "" > "$TEMPDIR/hashes.$bundle_name"
for cert_fname in $TEMPDIR/$bundle_name.*.crt; do
hpkp_key_ca="$($OPENSSL x509 -in "$cert_fname" -pubkey -noout | grep -v PUBLIC|$OPENSSL dgst -sha256 -binary | $OPENSSL enc -base64)"
issuer="$(get_cn_from_cert $cert_fname)"
[[ -n $hpkp_name ]] || hpkp_name=$($OPENSSL x509 -in "$cert_fname" -subject -noout| sed "s/^subject= //")
echo "$hpkp_key_ca $bundle_name : $issuer" >> "$TEMPDIR/hashes.$bundle_name"
done
done
for hpkp_key in $(echo $pins); do
hpkp_matches=$(grep -h "$hpkp_key" $TEMPDIR/hashes.*)
if [[ -n $hpkp_matches ]]; then
# We have a winner!
key_found=true
out "\n$spaces Root CA match : "
pr_done_goodln "$hpkp_key"
#out "$hpkp_matches"
fileout "hpkp_keymatch" "OK" "Root CA key matches a key pinned in the HPKP header\nKey/OS/CA: $hpkp_matches"
fi
done
fi
# If all else fails...
if ! $key_found ; then if ! $key_found ; then
out "\n$spaces"
pr_svrty_high " No matching key for pins found " pr_svrty_high " No matching key for pins found "
out "(CAs pinned? -- not checked for yet)" out "(CAs pinned? -- not checked for yet)"
fileout "hpkp_keymatch" "DEBUG" "The TLS key does not match any key pinned in the HPKP header. If you pinned a CA key you can ignore this" fileout "hpkp_keymatch" "DEBUG" "The TLS key does not match any key pinned in the HPKP header. If you pinned a CA key you can ignore this"
@ -1613,13 +1533,9 @@ neat_list(){
kx="${3//Kx=/}" kx="${3//Kx=/}"
enc="${4//Enc=/}" enc="${4//Enc=/}"
strength="${enc//\)/}" # retrieve (). first remove traling ")" strength=$(sed -e 's/.*(//' -e 's/)//' <<< "$enc") # strength = encryption bits
strength="${strength#*\(}" # exfiltrate (VAL strength="${strength//ChaCha20-Poly1305/ly1305}"
enc="${enc%%\(*}" enc=$(sed -e 's/(.*)//g' -e 's/ChaCha20-Poly1305/ChaCha20-Po/g' <<< "$enc") # workaround for empty bits ChaCha20-Poly1305
enc="${enc//POLY1305/}" # remove POLY1305
enc="${enc//\//}" # remove "/"
echo "$export" | grep -iq export && strength="$strength,exp" echo "$export" | grep -iq export && strength="$strength,exp"
#printf -- "%q" "$kx" | xxd | head -1 #printf -- "%q" "$kx" | xxd | head -1
@ -2093,7 +2009,7 @@ run_client_simulation() {
local name tls proto cipher local name tls proto cipher
local using_sockets=true local using_sockets=true
if $SSL_NATIVE || [[ -n "$STARTTLS" ]]; then if $SSL_NATIVE || [[ -n "$STARTTLS" ]] || ! $EXPERIMENTAL; then
using_sockets=false using_sockets=false
fi fi
@ -3997,7 +3913,7 @@ certificate_info() {
;; ;;
*) *)
out "$cert_sig_algo (" out "$cert_sig_algo ("
pr_warning "FIXME: can't tell whether this is good or not" pr_warning "FIXME: is unknown"
outln ")" outln ")"
fileout "${json_prefix}algorithm" "DEBUG" "Signature Algorithm: $sign_algo" fileout "${json_prefix}algorithm" "DEBUG" "Signature Algorithm: $sign_algo"
;; ;;
@ -4067,7 +3983,7 @@ certificate_info() {
fi fi
else else
out "$cert_keysize bits (" out "$cert_keysize bits ("
pr_warning "FIXME: can't tell whether this is good or not" pr_warning "FIXME: can't tell whether this is good here or not"
outln ")" outln ")"
fileout "${json_prefix}key_size" "WARN" "Server keys $cert_keysize bits (unknown signature algorithm)" fileout "${json_prefix}key_size" "WARN" "Server keys $cert_keysize bits (unknown signature algorithm)"
fi fi
@ -4463,13 +4379,18 @@ run_server_defaults() {
done done
} }
# http://www.heise.de/security/artikel/Forward-Secrecy-testen-und-einrichten-1932806.html
run_pfs() { run_pfs() {
local -i sclient_success local -i sclient_success
local pfs_offered=false local pfs_offered=false
local tmpfile local tmpfile
local dhlen local dhlen
local hexcode dash pfs_cipher sslvers kx auth enc mac local hexcode dash pfs_cipher sslvers kx auth enc mac
local pfs_cipher_list="$ROBUST_PFS_CIPHERS" # https://community.qualys.com/blogs/securitylabs/2013/08/05/configuring-apache-nginx-and-openssl-for-forward-secrecy -- but with RC4:
#local pfs_ciphers='EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA256 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EDH+aRSA EECDH RC4 !RC4-SHA !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS:@STRENGTH'
#w/o RC4:
#local pfs_ciphers='EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA256 EECDH+aRSA+SHA256 EDH+aRSA EECDH !RC4-SHA !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS:@STRENGTH'
local pfs_cipher_list="ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-CAMELLIA256-SHA256:DHE-RSA-CAMELLIA256-SHA:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-CAMELLIA256-SHA384:ECDHE-ECDSA-CAMELLIA256-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-ECDSA-CAMELLIA128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-CAMELLIA128-SHA256:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-CAMELLIA128-SHA256:DHE-RSA-SEED-SHA:DHE-RSA-CAMELLIA128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA"
local -i nr_supported_ciphers=0 local -i nr_supported_ciphers=0
local pfs_ciphers local pfs_ciphers
@ -4489,7 +4410,7 @@ run_pfs() {
return 1 return 1
fi fi
$OPENSSL s_client -cipher $pfs_cipher_list $STARTTLS $BUGS -connect $NODEIP:$PORT $PROXY $SNI >$TMPFILE 2>$ERRFILE </dev/null $OPENSSL s_client -cipher 'ECDH:DH' $STARTTLS $BUGS -connect $NODEIP:$PORT $PROXY $SNI >$TMPFILE 2>$ERRFILE </dev/null
sclient_connect_successful $? $TMPFILE sclient_connect_successful $? $TMPFILE
if [[ $? -ne 0 ]] || [[ $(grep -ac "BEGIN CERTIFICATE" $TMPFILE) -eq 0 ]]; then if [[ $? -ne 0 ]] || [[ $(grep -ac "BEGIN CERTIFICATE" $TMPFILE) -eq 0 ]]; then
outln outln
@ -6698,7 +6619,7 @@ find_openssl_binary() {
return 0 return 0
} }
check4openssl_oldfarts() { openssl_age() {
case "$OSSL_VER" in case "$OSSL_VER" in
0.9.7*|0.9.6*|0.9.5*) 0.9.7*|0.9.6*|0.9.5*)
# 0.9.5a was latest in 0.9.5 an released 2000/4/1, that'll NOT suffice for this test # 0.9.5a was latest in 0.9.5 an released 2000/4/1, that'll NOT suffice for this test
@ -7091,9 +7012,7 @@ prepare_logging() {
fi fi
>$LOGFILE >$LOGFILE
outln "## Scan started as: \"$PROG_NAME $CMDLINE\"" >>${LOGFILE} outln "## Scan started as: \"$PROG_NAME $CMDLINE\"" >>${LOGFILE}
outln "## at $HNAME:$OPENSSL_LOCATION" >>${LOGFILE} outln "## ($VERSION ${GIT_REL_SHORT:-$CVS_REL_SHORT} from $REL_DATE, at $HNAME:$OPENSSL_LOCATION)\n" >>${LOGFILE}
outln "## version testssl: $VERSION ${GIT_REL_SHORT:-$CVS_REL_SHORT} from $REL_DATE" >>${LOGFILE}
outln "## version openssl: \"$OSSL_VER\" from \"$OSSL_BUILD_DATE\")\n" >>${LOGFILE}
exec > >(tee -a ${LOGFILE}) exec > >(tee -a ${LOGFILE})
# not decided yet. Maybe good to have a separate file or none at all # not decided yet. Maybe good to have a separate file or none at all
#exec 2> >(tee -a ${LOGFILE} >&2) #exec 2> >(tee -a ${LOGFILE} >&2)
@ -8178,7 +8097,7 @@ find_openssl_binary
maketempf maketempf
mybanner mybanner
check_proxy check_proxy
check4openssl_oldfarts openssl_age
bsd-bash bsd-bash
# TODO: it is ugly to have those two vars here --> main() # TODO: it is ugly to have those two vars here --> main()
@ -8237,4 +8156,4 @@ fi
exit $? exit $?
# $Id: testssl.sh,v 1.514 2016/07/04 11:59:38 dirkw Exp $ # $Id: testssl.sh,v 1.509 2016/06/28 10:21:48 dirkw Exp $