Merge pull request #371 from dcooper16/fix_issue_276

Fix issue #276
This commit is contained in:
Dirk Wetter 2016-07-04 23:25:13 +02:00 committed by GitHub
commit 251e3f9a3b

View File

@ -3705,6 +3705,7 @@ sclient_connect_successful() {
}
# 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() {
local proto
local success
@ -3714,9 +3715,15 @@ determine_tls_extensions() {
"$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
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
$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
@ -3784,6 +3791,43 @@ get_cn_from_cert() {
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 -A3 "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 -A3 "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() {
local proto
@ -4252,7 +4296,7 @@ certificate_info() {
run_server_defaults() {
local ciph match_found newhostcert
local ciph match_found newhostcert sni
local sessticket_str=""
local lifetime unit
local line
@ -4260,8 +4304,9 @@ run_server_defaults() {
local all_tls_extensions=""
local -i certs_found=0
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:
# ciphers_to_test[1]: cipher suites using certificates with RSA signature public keys
# ciphers_to_test[2]: cipher suites using certificates with RSA key encipherment public keys
@ -4286,11 +4331,29 @@ run_server_defaults() {
ciphers_to_test[5]="aECDH"
ciphers_to_test[6]="aECDSA"
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
determine_tls_extensions "-cipher ${ciphers_to_test[n]}"
if [[ $? -eq 0 ]]; 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]}"
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
while read -d "\"" -r line; do
if [[ $line != "" ]] && ! grep -q "$line" <<< "$all_tls_extensions"; then
@ -4315,6 +4378,44 @@ run_server_defaults() {
fi
i=$((i + 1))
done
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 -A3 "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 -A3 "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))
cipher[certs_found]=${ciphers_to_test[n]}