Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 39 additions & 4 deletions lib/openssl/ssl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,7 @@ def verify_certificate_identity(cert, hostname)
case san.tag
when 2 # dNSName in GeneralName (RFC5280)
should_verify_common_name = false
reg = Regexp.escape(san.value).gsub(/\\\*/, "[^.]+")
return true if /\A#{reg}\z/i =~ hostname
return true if verify_hostname(hostname, san.value)
when 7 # iPAddress in GeneralName (RFC5280)
should_verify_common_name = false
# follows GENERAL_NAME_print() in x509v3/v3_alt.c
Expand All @@ -159,15 +158,51 @@ def verify_certificate_identity(cert, hostname)
if should_verify_common_name
cert.subject.to_a.each{|oid, value|
if oid == "CN"
reg = Regexp.escape(value).gsub(/\\\*/, "[^.]+")
return true if /\A#{reg}\z/i =~ hostname
return true if verify_hostname(hostname, value)
end
}
end
return false
end
module_function :verify_certificate_identity

def verify_hostname(hostname, san) # :nodoc:
san_parts = san.split(".")

# TODO: this behavior should probably be more strict
return san == hostname if san_parts.size < 2

host_parts = hostname.split(".")
return false unless san_parts.size == host_parts.size

# RFC 6125, section 6.4.3, subitem 1.
# The client SHOULD NOT attempt to match a presented identifier in
# which the wildcard character comprises a label other than the
# left-most label (e.g., do not match bar.*.example.net).
return false unless verify_wildcard(host_parts.shift, san_parts.shift)

san_parts.join(".") == host_parts.join(".")
end
module_function :verify_hostname

def verify_wildcard(domain_component, san_component) # :nodoc:
parts = san_component.split("*", -1)

return false if parts.size > 2
return san_component == domain_component if parts.size == 1

# RFC 6125, section 6.4.3, subitem 3.
# The client SHOULD NOT attempt to match a presented identifier
# where the wildcard character is embedded within an A-label or
# U-label of an internationalized domain name.
return false if domain_component.start_with?("xn--") && (!parts[0].empty? || !parts[1].empty?)

parts[0].length + parts[1].length < domain_component.length &&
domain_component.start_with?(parts[0]) &&
domain_component.end_with?(parts[1])
end
module_function :verify_wildcard

class SSLSocket
include Buffering
include SocketForwarder
Expand Down
16 changes: 16 additions & 0 deletions test/test_ssl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,22 @@ def test_verify_certificate_identity
end
end

def test_verify_hostname
assert_equal(true, OpenSSL::SSL.verify_hostname("www.example.com", "*.example.com"))
assert_equal(false, OpenSSL::SSL.verify_hostname("www.subdomain.example.com", "*.example.com"))
end

def test_verify_wildcard
assert_equal(false, OpenSSL::SSL.verify_wildcard("foo", "x*"))
assert_equal(true, OpenSSL::SSL.verify_wildcard("foo", "foo"))
assert_equal(true, OpenSSL::SSL.verify_wildcard("foo", "f*"))
assert_equal(true, OpenSSL::SSL.verify_wildcard("foo", "*"))
assert_equal(false, OpenSSL::SSL.verify_wildcard("abc*bcd", "abcd"))
assert_equal(false, OpenSSL::SSL.verify_wildcard("xn--qdk4b9b", "x*"))
assert_equal(false, OpenSSL::SSL.verify_wildcard("xn--qdk4b9b", "*--qdk4b9b")
assert_equal(true, OpenSSL::SSL.verify_wildcard("xn--qdk4b9b", "xn--qdk4b9b"))
end

# Create NULL byte SAN certificate
def create_null_byte_SAN_certificate(critical = false)
ef = OpenSSL::X509::ExtensionFactory.new
Expand Down