Merge branch 'master' into version_negotiation

This commit is contained in:
David Cooper 2016-07-05 10:24:53 -04:00
commit b6accbe737
2 changed files with 170 additions and 47 deletions

View File

@ -1,6 +1,7 @@
## Intro ## Intro
[![Build Status](https://travis-ci.org/drwetter/testssl.sh.svg?branch=master)](https://travis-ci.org/drwetter/testssl.sh)
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/drwetter/testssl.sh?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/drwetter/testssl.sh?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
`testssl.sh` is a free command line tool which checks a server's service on `testssl.sh` is a free command line tool which checks a server's service on

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.7dev" readonly VERSION="2.8rc1"
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,10 +170,12 @@ 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)
@ -1533,9 +1535,13 @@ neat_list(){
kx="${3//Kx=/}" kx="${3//Kx=/}"
enc="${4//Enc=/}" enc="${4//Enc=/}"
strength=$(sed -e 's/.*(//' -e 's/)//' <<< "$enc") # strength = encryption bits strength="${enc//\)/}" # retrieve (). first remove traling ")"
strength="${strength//ChaCha20-Poly1305/ly1305}" strength="${strength#*\(}" # exfiltrate (VAL
enc=$(sed -e 's/(.*)//g' -e 's/ChaCha20-Poly1305/ChaCha20-Po/g' <<< "$enc") # workaround for empty bits ChaCha20-Poly1305 enc="${enc%%\(*}"
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
@ -2009,7 +2015,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" ]] || ! $EXPERIMENTAL; then if $SSL_NATIVE || [[ -n "$STARTTLS" ]]; then
using_sockets=false using_sockets=false
fi fi
@ -3819,6 +3825,7 @@ sclient_connect_successful() {
} }
# arg1 is "-cipher <OpenSSL cipher>" or empty # arg1 is "-cipher <OpenSSL cipher>" or empty
# arg2 is a list of protocols to try (tls1_2, tls1_1, tls1, ssl3) or empty (if all should be tried)
determine_tls_extensions() { determine_tls_extensions() {
local proto local proto
local success local success
@ -3828,9 +3835,15 @@ determine_tls_extensions() {
"$HAS_ALPN" && alpn="h2-14,h2-15,h2" "$HAS_ALPN" && alpn="h2-14,h2-15,h2"
if [[ -n "$2" ]]; then
protocols_to_try="$2"
else
protocols_to_try="tls1_2 tls1_1 tls1 ssl3"
fi
# throwing 1st every cipher/protocol at the server to know what works # throwing 1st every cipher/protocol at the server to know what works
success=7 success=7
for proto in tls1_2 tls1_1 tls1 ssl3; do for proto in $protocols_to_try; do
# alpn: echo | openssl s_client -connect google.com:443 -tlsextdebug -alpn h2-14 -servername google.com <-- suport needs to be checked b4 -- see also: ssl/t1_trce.c # alpn: echo | openssl s_client -connect google.com:443 -tlsextdebug -alpn h2-14 -servername google.com <-- suport needs to be checked b4 -- see also: ssl/t1_trce.c
$OPENSSL s_client $STARTTLS $BUGS $1 -showcerts -connect $NODEIP:$PORT $PROXY $SNI -$proto -tlsextdebug -nextprotoneg $alpn -status </dev/null 2>$ERRFILE >$TMPFILE $OPENSSL s_client $STARTTLS $BUGS $1 -showcerts -connect $NODEIP:$PORT $PROXY $SNI -$proto -tlsextdebug -nextprotoneg $alpn -status </dev/null 2>$ERRFILE >$TMPFILE
sclient_connect_successful $? $TMPFILE && success=0 && break sclient_connect_successful $? $TMPFILE && success=0 && break
@ -3898,6 +3911,43 @@ get_cn_from_cert() {
return $? return $?
} }
# Return 0 if the server name provided in arg1 matches the CN or SAN in arg2, otherwise return 1.
compare_server_name_to_cert()
{
local servername=$1
local cert=$2
local cn dns_sans ip_sans san basename
cn="$(get_cn_from_cert $cert)"
if [[ -n "$cn" ]]; then
[[ "$cn" == "$servername" ]] && return 0
# If the CN contains a wildcard name, then do a wildcard match
if echo -n "$cn" | grep -q '^*.'; then
basename="$(echo -n "$cn" | sed 's/^\*.//')"
[[ "$cn" == "*.$basename" ]] && [[ "$servername" == *".$basename" ]] && return 0
fi
fi
# Check whether any of the DNS names in the certificate match the servername
dns_sans=$($OPENSSL x509 -in $cert -noout -text 2>>$ERRFILE | grep -A2 "Subject Alternative Name" | \
sed -e 's/,/\n/g' | grep "DNS:" | sed -e 's/DNS://g' -e 's/ //g')
for san in $dns_sans; do
[[ "$san" == "$servername" ]] && return 0
# If $san is a wildcard name, then do a wildcard match
if echo -n "$san" | grep -q '^*.'; then
basename="$(echo -n "$san" | sed 's/^\*.//')"
[[ "$san" == "*.$basename" ]] && [[ "$servername" == *".$basename" ]] && return 0
fi
done
# Check whether any of the IP addresses in the certificate match the serername
ip_sans=$($OPENSSL x509 -in $cert -noout -text 2>>$ERRFILE | grep -A2 "Subject Alternative Name" | \
sed -e 's/,/\n/g' | grep "IP Address:" | sed -e 's/IP Address://g' -e 's/ //g')
for san in $ip_sans; do
[[ "$san" == "$servername" ]] && return 0
done
return 1
}
certificate_info() { certificate_info() {
local proto local proto
@ -4033,7 +4083,7 @@ certificate_info() {
;; ;;
*) *)
out "$cert_sig_algo (" out "$cert_sig_algo ("
pr_warning "FIXME: is unknown" pr_warning "FIXME: can't tell whether this is good or not"
outln ")" outln ")"
fileout "${json_prefix}algorithm" "DEBUG" "Signature Algorithm: $sign_algo" fileout "${json_prefix}algorithm" "DEBUG" "Signature Algorithm: $sign_algo"
;; ;;
@ -4103,7 +4153,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 here or not" pr_warning "FIXME: can't tell whether this is good 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
@ -4141,7 +4191,7 @@ certificate_info() {
fi fi
else else
cn="no CN field in subject" cn="no CN field in subject"
pr_warning "($cn)" out "($cn)"
cnfinding="$cn" cnfinding="$cn"
cnok="INFO" cnok="INFO"
fi fi
@ -4191,7 +4241,7 @@ certificate_info() {
fi fi
fileout "${json_prefix}cn" "$cnok" "$cnfinding" fileout "${json_prefix}cn" "$cnok" "$cnfinding"
sans=$($OPENSSL x509 -in $HOSTCERT -noout -text 2>>$ERRFILE | grep -A3 "Subject Alternative Name" | \ sans=$($OPENSSL x509 -in $HOSTCERT -noout -text 2>>$ERRFILE | grep -A2 "Subject Alternative Name" | \
egrep "DNS:|IP Address:|email:|URI:|DirName:|Registered ID:" | \ egrep "DNS:|IP Address:|email:|URI:|DirName:|Registered ID:" | \
sed -e 's/ *DNS://g' -e 's/ *IP Address://g' -e 's/ *email://g' -e 's/ *URI://g' -e 's/ *DirName://g' \ sed -e 's/ *DNS://g' -e 's/ *IP Address://g' -e 's/ *email://g' -e 's/ *URI://g' -e 's/ *DirName://g' \
-e 's/ *Registered ID://g' -e 's/,/\n/g' \ -e 's/ *Registered ID://g' -e 's/,/\n/g' \
@ -4366,7 +4416,7 @@ certificate_info() {
run_server_defaults() { run_server_defaults() {
local ciph match_found newhostcert local ciph match_found newhostcert sni
local sessticket_str="" local sessticket_str=""
local lifetime unit local lifetime unit
local line local line
@ -4374,7 +4424,8 @@ run_server_defaults() {
local all_tls_extensions="" local all_tls_extensions=""
local -i certs_found=0 local -i certs_found=0
local -a previous_hostcert previous_intermediates keysize cipher ocsp_response ocsp_response_status local -a previous_hostcert previous_intermediates keysize cipher ocsp_response ocsp_response_status
local -a ciphers_to_test local -a ciphers_to_test success
local cn_nosni cn_sni sans_nosni sans_sni san
# Try each public key type once: # Try each public key type once:
# ciphers_to_test[1]: cipher suites using certificates with RSA signature public keys # ciphers_to_test[1]: cipher suites using certificates with RSA signature public keys
@ -4401,10 +4452,28 @@ run_server_defaults() {
ciphers_to_test[6]="aECDSA" ciphers_to_test[6]="aECDSA"
ciphers_to_test[7]="aGOST" ciphers_to_test[7]="aGOST"
for n in 1 2 3 4 5 6 7 ; do for (( n=1; n <= 14 ; n++ )); do
# Some servers use a different certificate if the ClientHello
# specifies TLSv1.1 and doesn't include a server name extension.
# So, for each public key type for which a certificate was found,
# try again, but only with TLSv1.1 and without SNI.
if [[ $n -ge 8 ]]; then
ciphers_to_test[n]=""
[[ ${success[n-7]} -eq 0 ]] && ciphers_to_test[n]="${ciphers_to_test[n-7]}"
fi
if [[ -n "${ciphers_to_test[n]}" ]] && [[ $(count_ciphers $($OPENSSL ciphers "${ciphers_to_test[n]}" 2>>$ERRFILE)) -ge 1 ]]; then if [[ -n "${ciphers_to_test[n]}" ]] && [[ $(count_ciphers $($OPENSSL ciphers "${ciphers_to_test[n]}" 2>>$ERRFILE)) -ge 1 ]]; then
if [[ $n -ge 8 ]]; then
sni="$SNI"
SNI=""
determine_tls_extensions "-cipher ${ciphers_to_test[n]}" "tls1_1"
success[n]=$?
SNI="$sni"
else
determine_tls_extensions "-cipher ${ciphers_to_test[n]}" determine_tls_extensions "-cipher ${ciphers_to_test[n]}"
if [[ $? -eq 0 ]]; then success[n]=$?
fi
if [[ ${success[n]} -eq 0 ]]; then
# check to see if any new TLS extensions were returned and add any new ones to all_tls_extensions # check to see if any new TLS extensions were returned and add any new ones to all_tls_extensions
while read -d "\"" -r line; do while read -d "\"" -r line; do
if [[ $line != "" ]] && ! grep -q "$line" <<< "$all_tls_extensions"; then if [[ $line != "" ]] && ! grep -q "$line" <<< "$all_tls_extensions"; then
@ -4429,7 +4498,45 @@ run_server_defaults() {
fi fi
i=$((i + 1)) i=$((i + 1))
done done
if ! $match_found ; then if ! "$match_found" && [[ $n -ge 8 ]] && [[ $certs_found -ne 0 ]]; then
# A new certificate was found using TLSv1.1 without SNI.
# Check to see if the new certificate should be displayed.
# It should be displayed if it is either a match for the
# $NODE being tested or if it has the same subject
# (CN and SAN) as other certificates for this host.
compare_server_name_to_cert "$NODE" "$HOSTCERT"
success[n]=$?
if [[ ${success[n]} -ne 0 ]]; then
cn_nosni="$(get_cn_from_cert $HOSTCERT)"
sans_nosni=$($OPENSSL x509 -in $HOSTCERT -noout -text 2>>$ERRFILE | grep -A2 "Subject Alternative Name" | grep "DNS:" | \
sed -e 's/DNS://g' -e 's/ //g' -e 's/,/ /g' -e 's/othername:<unsupported>//g')
echo "${previous_hostcert[1]}" > $HOSTCERT
cn_sni="$(get_cn_from_cert $HOSTCERT)"
# FIXME: Not sure what the matching rule should be. At
# the moment, the no SNI certificate is considered a
# match if the CNs are the same and the SANs (if
# present) contain at least one DNS name in common.
if [[ "$cn_nosni" == "$cn_sni" ]]; then
sans_sni=$($OPENSSL x509 -in $HOSTCERT -noout -text 2>>$ERRFILE | grep -A2 "Subject Alternative Name" | grep "DNS:" | \
sed -e 's/DNS://g' -e 's/ //g' -e 's/,/ /g' -e 's/othername:<unsupported>//g')
if [[ "$sans_nosni" == "$sans_sni" ]]; then
success[n]=0
else
for san in $sans_nosni; do
[[ " $sans_sni " =~ " $san " ]] && success[n]=0 && break
done
fi
fi
fi
# If the certificate found for TLSv1.1 w/o SNI appears to
# be for a different host, then set match_found to true so
# that the new certificate will not be included in the output.
[[ ${success[n]} -ne 0 ]] && match_found=true
fi
if ! "$match_found"; then
certs_found=$(($certs_found + 1)) certs_found=$(($certs_found + 1))
cipher[certs_found]=${ciphers_to_test[n]} cipher[certs_found]=${ciphers_to_test[n]}
keysize[certs_found]=$(grep -aw "^Server public key is" $TMPFILE | sed -e 's/^Server public key is //' -e 's/bit//' -e 's/ //') keysize[certs_found]=$(grep -aw "^Server public key is" $TMPFILE | sed -e 's/^Server public key is //' -e 's/bit//' -e 's/ //')
@ -4499,18 +4606,13 @@ 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
# https://community.qualys.com/blogs/securitylabs/2013/08/05/configuring-apache-nginx-and-openssl-for-forward-secrecy -- but with RC4: local pfs_cipher_list="$ROBUST_PFS_CIPHERS"
#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
@ -4530,7 +4632,7 @@ run_pfs() {
return 1 return 1
fi fi
$OPENSSL s_client -cipher 'ECDH:DH' $STARTTLS $BUGS -connect $NODEIP:$PORT $PROXY $SNI >$TMPFILE 2>$ERRFILE </dev/null $OPENSSL s_client -cipher $pfs_cipher_list $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
@ -6622,7 +6724,7 @@ 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" "ERROR" "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 ." fileout "old_fart" "ERROR" "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." -5
} }
# try very hard to determine the install path to get ahold of the mapping file # try very hard to determine the install path to get ahold of the mapping file
@ -6705,7 +6807,7 @@ find_openssl_binary() {
# no ERRFILE initialized yet, thus we use /dev/null for stderr directly # no ERRFILE initialized yet, thus we use /dev/null for stderr directly
$OPENSSL version -a 2>/dev/null >/dev/null $OPENSSL version -a 2>/dev/null >/dev/null
if [[ $? -ne 0 ]] || [[ ! -x "$OPENSSL" ]]; then if [[ $? -ne 0 ]] || [[ ! -x "$OPENSSL" ]]; then
fatal "\ncannot exec or find any openssl binary" -1 fatal "\ncannot exec or find any openssl binary" -5
fi fi
# http://www.openssl.org/news/openssl-notes.html # http://www.openssl.org/news/openssl-notes.html
@ -6745,7 +6847,7 @@ find_openssl_binary() {
return 0 return 0
} }
openssl_age() { check4openssl_oldfarts() {
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
@ -6770,6 +6872,16 @@ openssl_age() {
} }
# FreeBSD needs to have /dev/fd mounted. This is a friendly hint, see #258
check_bsd_mount() {
if [[ "$(uname)" == FreeBSD ]]; then
if ! mount | grep '/dev/fd' | grep -q fdescfs; then
fatal "You need to mount fdescfs on FreeBSD: \"mount -t fdescfs fdesc /dev/fd\"" -3
fi
fi
}
help() { help() {
cat << EOF cat << EOF
@ -7012,6 +7124,13 @@ cleanup () {
fatal() { fatal() {
pr_magentaln "Fatal error: $1" >&2 pr_magentaln "Fatal error: $1" >&2
exit $2 exit $2
# 1: cmd line error
# 2: secondary/other cmd line error
# -1: other user error
# -2: network problem
# -3: s.th. fatal is not supported in the client
# -4: s.th. is not supported yet
# -5: openssl problem
} }
@ -7060,9 +7179,9 @@ EOF
ignore_no_or_lame() { ignore_no_or_lame() {
local a local a
[[ "$WARNINGS" == "off" ]] && return 0 [[ "$WARNINGS" == off ]] && return 0
[[ "$WARNINGS" == "false" ]] && return 0 [[ "$WARNINGS" == false ]] && return 0
[[ "$WARNINGS" == "batch" ]] && return 1 [[ "$WARNINGS" == batch ]] && return 1
pr_magenta "$1 " pr_magenta "$1 "
read a read a
case $a in case $a in
@ -7131,7 +7250,9 @@ prepare_logging() {
fi fi
>$LOGFILE >$LOGFILE
outln "## Scan started as: \"$PROG_NAME $CMDLINE\"" >>${LOGFILE} outln "## Scan started as: \"$PROG_NAME $CMDLINE\"" >>${LOGFILE}
outln "## ($VERSION ${GIT_REL_SHORT:-$CVS_REL_SHORT} from $REL_DATE, at $HNAME:$OPENSSL_LOCATION)\n" >>${LOGFILE} outln "## at $HNAME:$OPENSSL_LOCATION" >>${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)
@ -7235,7 +7356,7 @@ get_a_record() {
elif which dig &>/dev/null; then elif which dig &>/dev/null; then
ip4=$(filter_ip4_address $(dig @224.0.0.251 -p 5353 +short -t a +notcp "$1" 2>/dev/null | sed '/^;;/d')) ip4=$(filter_ip4_address $(dig @224.0.0.251 -p 5353 +short -t a +notcp "$1" 2>/dev/null | sed '/^;;/d'))
else else
fatal "Local hostname given but no 'avahi-resolve' or 'dig' avaliable." fatal "Local hostname given but no 'avahi-resolve' or 'dig' avaliable." -3
fi fi
fi fi
if [[ -z "$ip4" ]]; then if [[ -z "$ip4" ]]; then
@ -7272,7 +7393,7 @@ get_aaaa_record() {
elif which dig &>/dev/null; then elif which dig &>/dev/null; then
ip6=$(filter_ip6_address $(dig @ff02::fb -p 5353 -t aaaa +short +notcp "$NODE")) ip6=$(filter_ip6_address $(dig @ff02::fb -p 5353 -t aaaa +short +notcp "$NODE"))
else else
fatal "Local hostname given but no 'avahi-resolve' or 'dig' avaliable." fatal "Local hostname given but no 'avahi-resolve' or 'dig' avaliable." -3
fi fi
elif which host &> /dev/null ; then elif which host &> /dev/null ; then
ip6=$(filter_ip6_address $(host -t aaaa "$NODE" | grep -v alias | grep -v "no AAAA record" | sed 's/^.*address //')) ip6=$(filter_ip6_address $(host -t aaaa "$NODE" | grep -v alias | grep -v "no AAAA record" | sed 's/^.*address //'))
@ -7386,11 +7507,11 @@ get_mx_record() {
check_proxy() { check_proxy() {
if [[ -n "$PROXY" ]]; then if [[ -n "$PROXY" ]]; then
if ! $OPENSSL s_client -help 2>&1 | grep -qw proxy; then if ! $OPENSSL s_client -help 2>&1 | grep -qw proxy; then
fatal "Your $OPENSSL is too old to support the \"--proxy\" option" -1 fatal "Your $OPENSSL is too old to support the \"--proxy\" option" -5
fi fi
PROXYNODE=${PROXY%:*} PROXYNODE=${PROXY%:*}
PROXYPORT=${PROXY#*:} PROXYPORT=${PROXY#*:}
is_number "$PROXYPORT" || fatal "Proxy port cannot be determined from \"$PROXY\"" "-3" is_number "$PROXYPORT" || fatal "Proxy port cannot be determined from \"$PROXY\"" "2"
#if is_ipv4addr "$PROXYNODE" || is_ipv6addr "$PROXYNODE" ; then #if is_ipv4addr "$PROXYNODE" || is_ipv6addr "$PROXYNODE" ; then
# IPv6 via openssl -proxy: that doesn't work. Sockets does # IPv6 via openssl -proxy: that doesn't work. Sockets does
@ -7400,7 +7521,7 @@ check_proxy() {
else else
check_resolver_bins check_resolver_bins
PROXYIP=$(get_a_record $PROXYNODE 2>/dev/null | grep -v alias | sed 's/^.*address //') PROXYIP=$(get_a_record $PROXYNODE 2>/dev/null | grep -v alias | sed 's/^.*address //')
[[ -z "$PROXYIP" ]] && fatal "Proxy IP cannot be determined from \"$PROXYNODE\"" "-3" [[ -z "$PROXYIP" ]] && fatal "Proxy IP cannot be determined from \"$PROXYNODE\"" "2"
fi fi
PROXY="-proxy $PROXYIP:$PROXYPORT" PROXY="-proxy $PROXYIP:$PROXYPORT"
fi fi
@ -7517,12 +7638,12 @@ determine_service() {
ftp|smtp|pop3|imap|xmpp|telnet|ldap) ftp|smtp|pop3|imap|xmpp|telnet|ldap)
STARTTLS="-starttls $protocol" STARTTLS="-starttls $protocol"
SNI="" SNI=""
if [[ $protocol == "xmpp" ]]; then if [[ "$protocol" == xmpp ]]; then
# for XMPP, openssl has a problem using -connect $NODEIP:$PORT. thus we use -connect $NODE:$PORT instead! # for XMPP, openssl has a problem using -connect $NODEIP:$PORT. thus we use -connect $NODE:$PORT instead!
NODEIP="$NODE" NODEIP="$NODE"
if [[ -n "$XMPP_HOST" ]]; then if [[ -n "$XMPP_HOST" ]]; then
if ! $OPENSSL s_client --help 2>&1 | grep -q xmpphost; then if ! $OPENSSL s_client --help 2>&1 | grep -q xmpphost; then
fatal "Your $OPENSSL does not support the \"-xmpphost\" option" -3 fatal "Your $OPENSSL does not support the \"-xmpphost\" option" -5
fi fi
STARTTLS="$STARTTLS -xmpphost $XMPP_HOST" # it's a hack -- instead of changing calls all over the place STARTTLS="$STARTTLS -xmpphost $XMPP_HOST" # it's a hack -- instead of changing calls all over the place
# see http://xmpp.org/rfcs/rfc3920.html # see http://xmpp.org/rfcs/rfc3920.html
@ -7541,7 +7662,7 @@ determine_service() {
outln outln
;; ;;
*) outln *) outln
fatal "momentarily only ftp, smtp, pop3, imap, xmpp, telnet and ldap allowed" -1 fatal "momentarily only ftp, smtp, pop3, imap, xmpp, telnet and ldap allowed" -4
;; ;;
esac esac
fi fi
@ -7645,7 +7766,7 @@ run_mass_testing_parallel() {
local global_cmdline=${CMDLINE%%--file*} 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\"" "2"
fi fi
pr_reverse "====== Running in parallel file batch mode with file=\"$FNAME\" ======"; outln pr_reverse "====== Running in parallel file batch mode with file=\"$FNAME\" ======"; outln
outln "(output is in ....\n)" outln "(output is in ....\n)"
@ -7671,7 +7792,7 @@ run_mass_testing() {
local global_cmdline=${CMDLINE%%--file*} 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\"" "2"
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"
@ -8140,7 +8261,7 @@ reset_hostdepended_vars() {
lets_roll() { lets_roll() {
local ret local ret
[[ -z "$NODEIP" ]] && fatal "$NODE doesn't resolve to an IP address" -1 [[ -z "$NODEIP" ]] && fatal "$NODE doesn't resolve to an IP address" 2
nodeip_to_proper_ip6 nodeip_to_proper_ip6
reset_hostdepended_vars reset_hostdepended_vars
determine_rdns determine_rdns
@ -8216,7 +8337,8 @@ find_openssl_binary
maketempf maketempf
mybanner mybanner
check_proxy check_proxy
openssl_age check4openssl_oldfarts
check_bsd_mount
# TODO: it is ugly to have those two vars here --> main() # TODO: it is ugly to have those two vars here --> main()
ret=0 ret=0
@ -8242,7 +8364,7 @@ else
parse_hn_port "${URI}" # NODE, URL_PATH, PORT, IPADDR and IP46ADDR is set now parse_hn_port "${URI}" # NODE, URL_PATH, PORT, IPADDR and IP46ADDR is set now
prepare_logging prepare_logging
if ! determine_ip_addresses && [[ -z "$CMDLINE_IP" ]]; then if ! determine_ip_addresses && [[ -z "$CMDLINE_IP" ]]; then
fatal "No IP address could be determined" fatal "No IP address could be determined" 2
fi fi
if [[ -n "$CMDLINE_IP" ]]; then if [[ -n "$CMDLINE_IP" ]]; then
[[ "$CMDLINE_IP" == "one" ]] && \ [[ "$CMDLINE_IP" == "one" ]] && \
@ -8274,4 +8396,4 @@ fi
exit $? exit $?
# $Id: testssl.sh,v 1.509 2016/06/28 10:21:48 dirkw Exp $ # $Id: testssl.sh,v 1.519 2016/07/04 22:08:50 dirkw Exp $