On Fri, 29 Nov 2013, Thijs Kinkhorst wrote: > > PS: I didn't took care of oldstable. Someone should handle that. > > Obviously we prefer to release updates for all suites at the same time. > Are the versions in squeeze so much different that it would be a lot of > work to also apply the patches there?
Probably not, in fact Moritz already provided a patch for ruby1.8/squeeze. I just took care to prepare the corresponding updates too (see debdiff attached for 1.8.7.302-2squeeze2 and 1.9.2.0-2+deb6u2). Again they do build but they are untested. Dear maintainers, please test and upload. Cheers, -- Raphaël Hertzog ◈ Debian Developer Discover the Debian Administrator's Handbook: → http://debian-handbook.info/get/
diff --git a/debian/changelog b/debian/changelog index a001b23..7d20446 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,15 @@ +ruby1.8 (1.8.7.302-2squeeze2) stable-security; urgency=high + + * Add multiple patches to fix security issues: + - CVE-2013-4164: Fix heap overflow in floating point parsing + (Closes: #730189) + - CVE-2013-1821: Fix entity expansion DoS vulnerability in REXML + (Closes: #702526) + - CVE-2013-4073: Fix incorrect ssl hostname verification (Closes: #714541) + Thanks to Moritz Muehlenhoff for the patches. + + -- Raphaël Hertzog <hert...@debian.org> Fri, 29 Nov 2013 13:51:39 +0100 + ruby1.8 (1.8.7.302-2squeeze1) stable; urgency=low * Add Conflicts and Replaces to libruby1.8 for irb1.8 and rdoc1.8. diff --git a/debian/patches/904-CVE-2013-4164.patch b/debian/patches/904-CVE-2013-4164.patch new file mode 100644 index 0000000..071002c --- /dev/null +++ b/debian/patches/904-CVE-2013-4164.patch @@ -0,0 +1,63 @@ +Description: Fix Heap Overflow in Floating Point Parsing + This vulnerability is tracked with CVE-2013-4164. + . + https://www.ruby-lang.org/en/news/2013/11/22/ruby-1-9-3-p484-is-released/ +Origin: backport, https://bugs.ruby-lang.org/projects/ruby-193/repository/revisions/43776/diff + https://bugs.ruby-lang.org/projects/ruby-193/repository/revisions/43782/diff +Bug-Debian: http://bugs.debian.org/730189 +Forwarded: not-needed +Applied-Upstream: 1.9.3-p484, 2.0.0-p353 +Last-Update: 2013-11-29 + +diff -aur ruby1.8-1.8.7.302.orig//util.c ruby1.8-1.8.7.302/util.c +--- ruby1.8-1.8.7.302.orig//util.c 2009-11-16 11:53:12.000000000 +0100 ++++ ruby1.8-1.8.7.302/util.c 2013-11-25 13:22:29.977023805 +0100 +@@ -888,6 +888,11 @@ + #else + #define MALLOC malloc + #endif ++#ifdef FREE ++extern void FREE(void*); ++#else ++#define FREE free ++#endif + + #ifndef Omit_Private_Memory + #ifndef PRIVATE_MEM +@@ -1172,7 +1177,7 @@ + #endif + + ACQUIRE_DTOA_LOCK(0); +- if ((rv = freelist[k]) != 0) { ++ if (k <= Kmax && (rv = freelist[k]) != 0) { + freelist[k] = rv->next; + } + else { +@@ -1182,7 +1187,7 @@ + #else + len = (sizeof(Bigint) + (x-1)*sizeof(ULong) + sizeof(double) - 1) + /sizeof(double); +- if (pmem_next - private_mem + len <= PRIVATE_mem) { ++ if (k <= Kmax && pmem_next - private_mem + len <= PRIVATE_mem) { + rv = (Bigint*)pmem_next; + pmem_next += len; + } +@@ -1201,6 +1206,10 @@ + Bfree(Bigint *v) + { + if (v) { ++ if (v->k > Kmax) { ++ FREE(v); ++ return; ++ } + ACQUIRE_DTOA_LOCK(0); + v->next = freelist[v->k]; + freelist[v->k] = v; +@@ -2196,6 +2205,7 @@ + for (; c >= '0' && c <= '9'; c = *++s) { + have_dig: + nz++; ++ if (nf > DBL_DIG * 4) continue; + if (c -= '0') { + nf += nz; + for (i = 1; i < nz; i++) diff --git a/debian/patches/905-CVE-2013-1821.patch b/debian/patches/905-CVE-2013-1821.patch new file mode 100644 index 0000000..7d87556 --- /dev/null +++ b/debian/patches/905-CVE-2013-1821.patch @@ -0,0 +1,120 @@ +Description: Fix entity expansion DoS vulnerability in REXML + CVE-2013-1821 +Origin: upstream, http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=39384&view=patch +Bug-Debian: http://bugs.debian.org/702526 +Forwarded: not-needed +Author: Salvatore Bonaccorso <car...@debian.org> +Last-Update: 2013-03-09 + +--- a/lib/rexml/document.rb ++++ b/lib/rexml/document.rb +@@ -214,6 +214,18 @@ + return @@entity_expansion_limit + end + ++ @@entity_expansion_text_limit = 10_240 ++ ++ # Set the entity expansion limit. By default the limit is set to 10240. ++ def Document::entity_expansion_text_limit=( val ) ++ @@entity_expansion_text_limit = val ++ end ++ ++ # Get the entity expansion limit. By default the limit is set to 10000. ++ def Document::entity_expansion_text_limit ++ return @@entity_expansion_text_limit ++ end ++ + attr_reader :entity_expansion_count + + def record_entity_expansion +--- a/test/rexml/test_document.rb ++++ b/test/rexml/test_document.rb +@@ -63,4 +63,23 @@ + ensure + REXML::Document.entity_expansion_limit = 10000 + end ++ ++ def test_entity_string_limit ++ template = '<!DOCTYPE bomb [ <!ENTITY a "^" > ]> <bomb>$</bomb>' ++ len = 5120 # 5k per entity ++ template.sub!(/\^/, "B" * len) ++ ++ # 10k is OK ++ entities = '&a;' * 2 # 5k entity * 2 = 10k ++ xmldoc = REXML::Document.new(template.sub(/\$/, entities)) ++ assert_equal(len * 2, xmldoc.root.text.bytesize) ++ ++ # above 10k explodes ++ entities = '&a;' * 3 # 5k entity * 2 = 15k ++ xmldoc = REXML::Document.new(template.sub(/\$/, entities)) ++ assert_raises(RuntimeError) do ++ xmldoc.root.text ++ end ++ end ++ + end +--- a/lib/rexml/text.rb ++++ b/lib/rexml/text.rb +@@ -308,37 +308,35 @@ + + # Unescapes all possible entities + def Text::unnormalize( string, doctype=nil, filter=nil, illegal=nil ) +- rv = string.clone +- rv.gsub!( /\r\n?/, "\n" ) +- matches = rv.scan( REFERENCE ) +- return rv if matches.size == 0 +- rv.gsub!( NUMERICENTITY ) {|m| +- m=$1 +- m = "0#{m}" if m[0] == ?x +- [Integer(m)].pack('U*') ++ sum = 0 ++ string.gsub( /\r\n?/, "\n" ).gsub( REFERENCE ) { ++ s = Text.expand($&, doctype, filter) ++ if sum + s.bytesize > Document.entity_expansion_text_limit ++ raise "entity expansion has grown too large" ++ else ++ sum += s.bytesize ++ end ++ s + } +- matches.collect!{|x|x[0]}.compact! +- if matches.size > 0 +- if doctype +- matches.each do |entity_reference| +- unless filter and filter.include?(entity_reference) +- entity_value = doctype.entity( entity_reference ) +- re = /&#{entity_reference};/ +- rv.gsub!( re, entity_value ) if entity_value +- end +- end ++ end ++ ++ def Text.expand(ref, doctype, filter) ++ if ref[1] == ?# ++ if ref[2] == ?x ++ [ref[3...-1].to_i(16)].pack('U*') + else +- matches.each do |entity_reference| +- unless filter and filter.include?(entity_reference) +- entity_value = DocType::DEFAULT_ENTITIES[ entity_reference ] +- re = /&#{entity_reference};/ +- rv.gsub!( re, entity_value.value ) if entity_value +- end +- end ++ [ref[2...-1].to_i].pack('U*') + end +- rv.gsub!( /&/, '&' ) ++ elsif ref == '&' ++ '&' ++ elsif filter and filter.include?( ref[1...-1] ) ++ ref ++ elsif doctype ++ doctype.entity( ref[1...-1] ) or ref ++ else ++ entity_value = DocType::DEFAULT_ENTITIES[ ref[1...-1] ] ++ entity_value ? entity_value.value : ref + end +- rv + end + end + end diff --git a/debian/patches/906-CVE-2013-4073.patch b/debian/patches/906-CVE-2013-4073.patch new file mode 100644 index 0000000..871aab9 --- /dev/null +++ b/debian/patches/906-CVE-2013-4073.patch @@ -0,0 +1,79 @@ +Description: fix incorrect ssl hostname verification +Origin: upstream, https://github.com/ruby/ruby/commit/961bf7496ded3acfe847cf56fa90bbdcfd6e614f +Origin: backport, https://github.com/ruby/ruby/commit/a3a62f87e144be31b9ca8ad6415b207f43f4e126 +Bug-Debian: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=714541 +Bug: https://bugs.ruby-lang.org/issues/8575 + +Index: ruby1.8-1.8.7.352/ext/openssl/lib/openssl/ssl-internal.rb +=================================================================== +--- ruby1.8-1.8.7.352.orig/ext/openssl/lib/openssl/ssl-internal.rb 2013-07-08 10:16:49.926709322 -0400 ++++ ruby1.8-1.8.7.352/ext/openssl/lib/openssl/ssl-internal.rb 2013-07-08 10:16:49.918709322 -0400 +@@ -90,14 +90,22 @@ + should_verify_common_name = true + cert.extensions.each{|ext| + next if ext.oid != "subjectAltName" +- ext.value.split(/,\s+/).each{|general_name| +- if /\ADNS:(.*)/ =~ general_name ++ ostr = OpenSSL::ASN1.decode(ext.to_der).value.last ++ sequence = OpenSSL::ASN1.decode(ostr.value) ++ sequence.value.each{|san| ++ case san.tag ++ when 2 # dNSName in GeneralName (RFC5280) + should_verify_common_name = false +- reg = Regexp.escape($1).gsub(/\\\*/, "[^.]+") ++ reg = Regexp.escape(san.value).gsub(/\\\*/, "[^.]+") + return true if /\A#{reg}\z/i =~ hostname +- elsif /\AIP Address:(.*)/ =~ general_name ++ when 7 # iPAddress in GeneralName (RFC5280) + should_verify_common_name = false +- return true if $1 == hostname ++ # follows GENERAL_NAME_print() in x509v3/v3_alt.c ++ if san.value.size == 4 ++ return true if san.value.unpack('C*').join('.') == hostname ++ elsif san.value.size == 16 ++ return true if san.value.unpack('n*').map { |e| sprintf("%X", e) }.join(':') == hostname ++ end + end + } + } +Index: ruby1.8-1.8.7.352/test/openssl/test_ssl.rb +=================================================================== +--- ruby1.8-1.8.7.352.orig/test/openssl/test_ssl.rb 2013-07-08 10:16:49.926709322 -0400 ++++ ruby1.8-1.8.7.352/test/openssl/test_ssl.rb 2013-07-08 10:16:49.922709322 -0400 +@@ -532,6 +532,36 @@ + end + end + end ++ ++ def test_verify_certificate_identity ++ [true, false].each do |criticality| ++ cert = create_null_byte_SAN_certificate(criticality) ++ assert_equal(false, OpenSSL::SSL.verify_certificate_identity(cert, 'www.example.com')) ++ assert_equal(true, OpenSSL::SSL.verify_certificate_identity(cert, 'www.example.com\0.evil.com')) ++ assert_equal(false, OpenSSL::SSL.verify_certificate_identity(cert, '192.168.7.255')) ++ assert_equal(true, OpenSSL::SSL.verify_certificate_identity(cert, '192.168.7.1')) ++ assert_equal(false, OpenSSL::SSL.verify_certificate_identity(cert, '13::17')) ++ assert_equal(true, OpenSSL::SSL.verify_certificate_identity(cert, '13:0:0:0:0:0:0:17')) ++ end ++ end ++ ++ # Create NULL byte SAN certificate ++ def create_null_byte_SAN_certificate(critical = false) ++ ef = OpenSSL::X509::ExtensionFactory.new ++ cert = OpenSSL::X509::Certificate.new ++ cert.subject = OpenSSL::X509::Name.parse "/DC=some/DC=site/CN=Some Site" ++ ext = ef.create_ext('subjectAltName', 'DNS:placeholder,IP:192.168.7.1,IP:13::17', critical) ++ ext_asn1 = OpenSSL::ASN1.decode(ext.to_der) ++ san_list_der = ext_asn1.value.reduce(nil) { |memo,val| val.tag == 4 ? val.value : memo } ++ san_list_asn1 = OpenSSL::ASN1.decode(san_list_der) ++ san_list_asn1.value[0].value = 'www.example.com\0.evil.com' ++ pos = critical ? 2 : 1 ++ ext_asn1.value[pos].value = san_list_asn1.to_der ++ real_ext = OpenSSL::X509::Extension.new ext_asn1 ++ cert.add_extension(real_ext) ++ cert ++ end ++ + end + + end diff --git a/debian/patches/series b/debian/patches/series index 9dd9322..6c35224 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -10,3 +10,6 @@ 100730_disable_getsetcontext_on_nptl.patch 100730_verbose-tests.patch 100901_threading_fixes.patch +904-CVE-2013-4164.patch +905-CVE-2013-1821.patch +906-CVE-2013-4073.patch
diff --git a/debian/changelog b/debian/changelog index 8542e71..8313465 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,10 @@ +ruby1.9.1 (1.9.2.0-2+deb6u2) oldstable-security; urgency=high + + * debian/patches/CVE-2013-4164.patch: add upstream patch to fix heap + overflow in floating point parsing. Closes: #730178 + + -- Raphaël Hertzog <b...@kali.org> Fri, 29 Nov 2013 07:36:01 +0000 + ruby1.9.1 (1.9.2.0-2+deb6u1) oldstable-security; urgency=high * Team upload diff --git a/debian/patches/CVE-2013-4164.patch b/debian/patches/CVE-2013-4164.patch new file mode 100644 index 0000000..6bb222f --- /dev/null +++ b/debian/patches/CVE-2013-4164.patch @@ -0,0 +1,80 @@ +Description: Fix Heap Overflow in Floating Point Parsing + This vulnerability is tracked with CVE-2013-4164. + . + https://www.ruby-lang.org/en/news/2013/11/22/ruby-1-9-3-p484-is-released/ +Origin: upstream, https://bugs.ruby-lang.org/projects/ruby-193/repository/revisions/43776/diff +Bug-Debian: http://bugs.debian.org/730178 +Forwarded: not-needed +Applied-Upstream: 1.9.3-p484, 2.0.0-p353 +Last-Update: 2013-11-29 + +--- a/util.c ++++ b/util.c +@@ -852,6 +852,11 @@ extern void *MALLOC(size_t); + #else + #define MALLOC malloc + #endif ++#ifdef FREE ++extern void FREE(void*); ++#else ++#define FREE free ++#endif + + #ifndef Omit_Private_Memory + #ifndef PRIVATE_MEM +@@ -1142,7 +1147,7 @@ Balloc(int k) + #endif + + ACQUIRE_DTOA_LOCK(0); +- if ((rv = freelist[k]) != 0) { ++ if (k <= Kmax && (rv = freelist[k]) != 0) { + freelist[k] = rv->next; + } + else { +@@ -1152,7 +1157,7 @@ Balloc(int k) + #else + len = (sizeof(Bigint) + (x-1)*sizeof(ULong) + sizeof(double) - 1) + /sizeof(double); +- if (pmem_next - private_mem + len <= PRIVATE_mem) { ++ if (k <= Kmax && pmem_next - private_mem + len <= PRIVATE_mem) { + rv = (Bigint*)pmem_next; + pmem_next += len; + } +@@ -1171,6 +1176,10 @@ static void + Bfree(Bigint *v) + { + if (v) { ++ if (v->k > Kmax) { ++ FREE(v); ++ return; ++ } + ACQUIRE_DTOA_LOCK(0); + v->next = freelist[v->k]; + freelist[v->k] = v; +@@ -2201,6 +2210,7 @@ break2: + for (; c >= '0' && c <= '9'; c = *++s) { + have_dig: + nz++; ++ if (nf > DBL_DIG * 4) continue; + if (c -= '0') { + nf += nz; + for (i = 1; i < nz; i++) +--- a/test/ruby/test_float.rb ++++ b/test/ruby/test_float.rb +@@ -450,4 +450,16 @@ class TestFloat < Test::Unit::TestCase + sleep(0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1) + end + end ++ ++ def test_long_string ++ assert_normal_exit(<<-'end;') ++ assert_in_epsilon(10.0, ("1."+"1"*300000).to_f*9) ++ end; ++ end ++ ++ def test_long_string ++ assert_normal_exit(<<-'end;') ++ assert_in_epsilon(10.0, ("1."+"1"*300000).to_f*9) ++ end; ++ end + end diff --git a/debian/patches/series b/debian/patches/series index d2ff813..8097101 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -27,3 +27,4 @@ 20100829-rubygems_default_dir.diff CVE-2013-1821.patch CVE-2013-4073.patch +CVE-2013-4164.patch