Fix and enhance CN matching

PR #1373 changed get_cn_from_cert() to handle certificate subject names that include more than one CN attribute. It did this by converting newline characters to spaces. It seems that this resulted in a space character being added to the end of the string returned by get_cn_from_cert() even in the case that the subject name only included one CN attribute. The presence of the space character in returned value caused compare_server_name_to_cert() to determine that the CN attribute did not contain a DNS name (since DNS names cannot include spaces), and so compare_server_name_to_cert() reports that the server name does not match against the CN in the subject. This may be the reason for the problem noted in #1555.

This commit fixes the above problem and also enhances the matching of the CN in the subject name against the server's name. Currently, compare_server_name_to_cert() assumes that the subject field contains at most one CN attribute. However, as noted in #1373, some certificates include subject names with more than one CN attribute, and RFC 6125 (Section 6.2.2) indicates that the certificate subject name include more than one CN, with each specifying a different DNS name.

So, in addition to fixing the problem with the space character, this commit also enhances the CN matching to work even if the certificate includes more than one CN attribute in the subject name.
This commit is contained in:
David Cooper 2020-06-08 13:57:00 -04:00
parent 6a91dadb31
commit a6c2168cd9

View File

@ -7804,9 +7804,9 @@ wildcard_match()
compare_server_name_to_cert() {
local cert="$1"
local servername cn dns_sans ip_sans san dercert tag
local servername cns cn dns_sans ip_sans san dercert tag
local srv_id="" xmppaddr=""
local -i i len len1
local -i i len len1 cn_match=0
local -i subret=0 # no error condition, passing results
HAS_DNS_SANS=false
@ -7960,19 +7960,23 @@ compare_server_name_to_cert() {
done <<< "$dns_sans"
fi
cn="$(get_cn_from_cert "$cert")"
# Get every CN from the subject field and compare against the server name.
cns="$($OPENSSL x509 -in $1 -noout -subject -nameopt multiline,-align,sname,-esc_msb,utf8,-space_eq 2>>$ERRFILE | awk -F'=' '/CN=/ { print $2 }')"
while read cn; do
# If the CN contains any characters that are not valid for a DNS name,
# then assume it does not contain a DNS name.
[[ -n $(sed 's/^[_\.a-zA-Z0-9*\-]*//' <<< "$cn") ]] && continue
# If the CN contains any characters that are not valid for a DNS name,
# then assume it does not contain a DNS name.
[[ -n $(sed 's/^[_\.a-zA-Z0-9*\-]*//' <<< "$cn") ]] && return $subret
# Check whether the CN matches the servername
[[ $(toupper "$cn") == "$servername" ]] && cn_match=4 && break
# Check whether the CN in the certificate matches the servername
[[ $(toupper "$cn") == "$servername" ]] && subret+=4 && return $subret
# Check whether the CN in the certificate is a wildcard name that matches
# the servername
wildcard_match "$servername" "$cn"
[[ $? -eq 0 ]] && subret+=8
# Check whether the CN is a wildcard name that matches the servername
# NOTE: Don't stop loop on a wildcard match in case there is another CN
# that is an exact match.
wildcard_match "$servername" "$cn"
[[ $? -eq 0 ]] && cn_match=8
done <<< "$cns"
subret+=$cn_match
return $subret
}