Project

General

Profile

Bug #9644 » CVE-2015-1855.patch

terceiro (Antonio Terceiro), 04/28/2015 12:35 AM

View differences:

ext/openssl/lib/openssl/ssl-internal.rb
94 94
          case san.tag
95 95
          when 2 # dNSName in GeneralName (RFC5280)
96 96
            should_verify_common_name = false
97
            reg = Regexp.escape(san.value).gsub(/\\\*/, "[^.]+")
98
            return true if /\A#{reg}\z/i =~ hostname
97
            return true if verify_hostname(hostname, san.value)
99 98
          when 7 # iPAddress in GeneralName (RFC5280)
100 99
            should_verify_common_name = false
101 100
            # follows GENERAL_NAME_print() in x509v3/v3_alt.c
......
110 109
      if should_verify_common_name
111 110
        cert.subject.to_a.each{|oid, value|
112 111
          if oid == "CN"
113
            reg = Regexp.escape(value).gsub(/\\\*/, "[^.]+")
114
            return true if /\A#{reg}\z/i =~ hostname
112
            return true if verify_hostname(hostname, value)
115 113
          end
116 114
        }
117 115
      end
......
119 117
    end
120 118
    module_function :verify_certificate_identity
121 119

  
120
    def verify_hostname(hostname, san) # :nodoc:
121
      # RFC 5280, IA5String is limited to the set of ASCII characters
122
      return false unless san.ascii_only?
123
      return false unless hostname.ascii_only?
124

  
125
      # See RFC 6125, section 6.4.1
126
      # Matching is case-insensitive.
127
      san_parts = san.downcase.split(".")
128

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

  
132
      # Matching is case-insensitive.
133
      host_parts = hostname.downcase.split(".")
134

  
135
      # RFC 6125, section 6.4.3, subitem 2.
136
      # If the wildcard character is the only character of the left-most
137
      # label in the presented identifier, the client SHOULD NOT compare
138
      # against anything but the left-most label of the reference
139
      # identifier (e.g., *.example.com would match foo.example.com but
140
      # not bar.foo.example.com or example.com).
141
      return false unless san_parts.size == host_parts.size
142

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

  
149
      san_parts.join(".") == host_parts.join(".")
150
    end
151
    module_function :verify_hostname
152

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

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

  
159
      # RFC 6125, section 6.4.3, subitem 3.
160
      # The client SHOULD NOT attempt to match a presented identifier
161
      # where the wildcard character is embedded within an A-label or
162
      # U-label of an internationalized domain name.
163
      return false if domain_component.start_with?("xn--") && san_component != "*"
164

  
165
      parts[0].length + parts[1].length < domain_component.length &&
166
      domain_component.start_with?(parts[0]) &&
167
      domain_component.end_with?(parts[1])
168
    end
169
    module_function :verify_wildcard
170

  
122 171
    class SSLSocket
123 172
      include Buffering
124 173
      include SocketForwarder
125 174
      include Nonblock
126 175

  
176
      ##
177
      # Perform hostname verification after an SSL connection is established
178
      #
179
      # This method MUST be called after calling #connect to ensure that the
180
      # hostname of a remote peer has been verified.
127 181
      def post_connection_check(hostname)
128 182
        unless OpenSSL::SSL.verify_certificate_identity(peer_cert, hostname)
129 183
          raise SSLError, "hostname does not match the server certificate"
130
-- a/test/openssl/test_ssl.rb
184
++ b/test/openssl/test_ssl.rb
......
1
# encoding: UTF-8
2

  
1 3
require_relative "utils"
2 4

  
3 5
if defined?(OpenSSL)
......
363 365
    end
364 366
  end
365 367

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

  
373
  def test_verify_wildcard
374
    assert_equal(false, OpenSSL::SSL.verify_wildcard("foo", "x*"))
375
    assert_equal(true,  OpenSSL::SSL.verify_wildcard("foo", "foo"))
376
    assert_equal(true,  OpenSSL::SSL.verify_wildcard("foo", "f*"))
377
    assert_equal(true,  OpenSSL::SSL.verify_wildcard("foo", "*"))
378
    assert_equal(false, OpenSSL::SSL.verify_wildcard("abc*bcd", "abcd"))
379
    assert_equal(false, OpenSSL::SSL.verify_wildcard("xn--qdk4b9b", "x*"))
380
    assert_equal(false, OpenSSL::SSL.verify_wildcard("xn--qdk4b9b", "*--qdk4b9b"))
381
    assert_equal(true,  OpenSSL::SSL.verify_wildcard("xn--qdk4b9b", "xn--qdk4b9b"))
382
  end
383

  
384
  # Comments in this test is excerpted from http://tools.ietf.org/html/rfc6125#page-27
385
  def test_post_connection_check_wildcard_san
386
    # case-insensitive ASCII comparison
387
    # RFC 6125, section 6.4.1
388
    #
389
    # "..matching of the reference identifier against the presented identifier
390
    # is performed by comparing the set of domain name labels using a
391
    # case-insensitive ASCII comparison, as clarified by [DNS-CASE] (e.g.,
392
    # "WWW.Example.Com" would be lower-cased to "www.example.com" for
393
    # comparison purposes)
394
    assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
395
      create_cert_with_san('DNS:*.example.com'), 'www.example.com'))
396
    assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
397
      create_cert_with_san('DNS:*.Example.COM'), 'www.example.com'))
398
    assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
399
      create_cert_with_san('DNS:*.example.com'), 'WWW.Example.COM'))
400
    # 1.  The client SHOULD NOT attempt to match a presented identifier in
401
    #     which the wildcard character comprises a label other than the
402
    #     left-most label (e.g., do not match bar.*.example.net).
403
    assert_equal(false, OpenSSL::SSL.verify_certificate_identity(
404
      create_cert_with_san('DNS:www.*.com'), 'www.example.com'))
405
    # 2.  If the wildcard character is the only character of the left-most
406
    #     label in the presented identifier, the client SHOULD NOT compare
407
    #     against anything but the left-most label of the reference
408
    #     identifier (e.g., *.example.com would match foo.example.com but
409
    #     not bar.foo.example.com or example.com).
410
    assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
411
      create_cert_with_san('DNS:*.example.com'), 'foo.example.com'))
412
    assert_equal(false, OpenSSL::SSL.verify_certificate_identity(
413
      create_cert_with_san('DNS:*.example.com'), 'bar.foo.example.com'))
414
    # 3.  The client MAY match a presented identifier in which the wildcard
415
    #     character is not the only character of the label (e.g.,
416
    #     baz*.example.net and *baz.example.net and b*z.example.net would
417
    #     be taken to match baz1.example.net and foobaz.example.net and
418
    #     buzz.example.net, respectively).  ...
419
    assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
420
      create_cert_with_san('DNS:baz*.example.com'), 'baz1.example.com'))
421
    assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
422
      create_cert_with_san('DNS:*baz.example.com'), 'foobaz.example.com'))
423
    assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
424
      create_cert_with_san('DNS:b*z.example.com'), 'buzz.example.com'))
425
    # Section 6.4.3 of RFC6125 states that client should NOT match identifier
426
    # where wildcard is other than left-most label.
427
    #
428
    # Also implicitly mentions the wildcard character only in singular form,
429
    # and discourages matching against more than one wildcard.
430
    #
431
    # See RFC 6125, section 7.2, subitem 2.
432
    assert_equal(false, OpenSSL::SSL.verify_certificate_identity(
433
      create_cert_with_san('DNS:*b*.example.com'), 'abc.example.com'))
434
    assert_equal(false, OpenSSL::SSL.verify_certificate_identity(
435
      create_cert_with_san('DNS:*b*.example.com'), 'ab.example.com'))
436
    assert_equal(false, OpenSSL::SSL.verify_certificate_identity(
437
      create_cert_with_san('DNS:*b*.example.com'), 'bc.example.com'))
438
    #                                ...  However, the client SHOULD NOT
439
    #   attempt to match a presented identifier where the wildcard
440
    #   character is embedded within an A-label or U-label [IDNA-DEFS] of
441
    #   an internationalized domain name [IDNA-PROTO].
442
    assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
443
      create_cert_with_san('DNS:xn*.example.com'), 'xn1ca.example.com'))
444
    # part of A-label
445
    assert_equal(false, OpenSSL::SSL.verify_certificate_identity(
446
      create_cert_with_san('DNS:xn--*.example.com'), 'xn--1ca.example.com'))
447
    # part of U-label
448
    # dNSName in RFC5280 is an IA5String so U-label should NOT be allowed
449
    # regardless of wildcard.
450
    #
451
    # See Section 7.2 of RFC 5280:
452
    #   IA5String is limited to the set of ASCII characters.
453
    assert_equal(false, OpenSSL::SSL.verify_certificate_identity(
454
      create_cert_with_san('DNS:á*.example.com'), 'á1.example.com'))
455
  end
456

  
457
  def test_post_connection_check_wildcard_cn
458
    assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
459
      create_cert_with_name('*.example.com'), 'www.example.com'))
460
    assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
461
      create_cert_with_name('*.Example.COM'), 'www.example.com'))
462
    assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
463
      create_cert_with_name('*.example.com'), 'WWW.Example.COM'))
464
    assert_equal(false, OpenSSL::SSL.verify_certificate_identity(
465
      create_cert_with_name('www.*.com'), 'www.example.com'))
466
    assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
467
      create_cert_with_name('*.example.com'), 'foo.example.com'))
468
    assert_equal(false, OpenSSL::SSL.verify_certificate_identity(
469
      create_cert_with_name('*.example.com'), 'bar.foo.example.com'))
470
    assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
471
      create_cert_with_name('baz*.example.com'), 'baz1.example.com'))
472
    assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
473
      create_cert_with_name('*baz.example.com'), 'foobaz.example.com'))
474
    assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
475
      create_cert_with_name('b*z.example.com'), 'buzz.example.com'))
476
    # Section 6.4.3 of RFC6125 states that client should NOT match identifier
477
    # where wildcard is other than left-most label.
478
    #
479
    # Also implicitly mentions the wildcard character only in singular form,
480
    # and discourages matching against more than one wildcard.
481
    #
482
    # See RFC 6125, section 7.2, subitem 2.
483
    assert_equal(false, OpenSSL::SSL.verify_certificate_identity(
484
      create_cert_with_name('*b*.example.com'), 'abc.example.com'))
485
    assert_equal(false, OpenSSL::SSL.verify_certificate_identity(
486
      create_cert_with_name('*b*.example.com'), 'ab.example.com'))
487
    assert_equal(false, OpenSSL::SSL.verify_certificate_identity(
488
      create_cert_with_name('*b*.example.com'), 'bc.example.com'))
489
    assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
490
      create_cert_with_name('xn*.example.com'), 'xn1ca.example.com'))
491
    assert_equal(false, OpenSSL::SSL.verify_certificate_identity(
492
      create_cert_with_name('xn--*.example.com'), 'xn--1ca.example.com'))
493
    # part of U-label
494
    # Subject in RFC5280 states case-insensitive ASCII comparison.
495
    #
496
    # See Section 7.2 of RFC 5280:
497
    #   IA5String is limited to the set of ASCII characters.
498
    assert_equal(false, OpenSSL::SSL.verify_certificate_identity(
499
      create_cert_with_name('á*.example.com'), 'á1.example.com'))
500
  end
501

  
502
  def create_cert_with_san(san)
503
    ef = OpenSSL::X509::ExtensionFactory.new
504
    cert = OpenSSL::X509::Certificate.new
505
    cert.subject = OpenSSL::X509::Name.parse("/DC=some/DC=site/CN=Some Site")
506
    ext = ef.create_ext('subjectAltName', san)
507
    cert.add_extension(ext)
508
    cert
509
  end
510

  
511
  def create_cert_with_name(name)
512
    cert = OpenSSL::X509::Certificate.new
513
    cert.subject = OpenSSL::X509::Name.new([['DC', 'some'], ['DC', 'site'], ['CN', name]])
514
    cert
515
  end
516

  
517

  
366 518
  # Create NULL byte SAN certificate
367 519
  def create_null_byte_SAN_certificate(critical = false)
368 520
    ef = OpenSSL::X509::ExtensionFactory.new