[#35027] [Ruby 1.9-Bug#4352][Open] [patch] Fix eval(s, b) backtrace; make eval(s, b) consistent with eval(s) — "James M. Lawrence" <redmine@...>

Bug #4352: [patch] Fix eval(s, b) backtrace; make eval(s, b) consistent with eval(s)

16 messages 2011/02/01

[#35114] [Ruby 1.9-Bug#4373][Open] http.rb:677: [BUG] Segmentation fault — Christian Fazzini <redmine@...>

Bug #4373: http.rb:677: [BUG] Segmentation fault

59 messages 2011/02/06

[#35171] [Ruby 1.9-Bug#4386][Open] encoding: directive does not affect regex expressions — mathew murphy <redmine@...>

Bug #4386: encoding: directive does not affect regex expressions

9 messages 2011/02/09

[#35237] [Ruby 1.9-Bug#4400][Open] nested at_exit hooks run in strange order — Suraj Kurapati <redmine@...>

Bug #4400: nested at_exit hooks run in strange order

12 messages 2011/02/15

[ruby-core:35123] [Ruby 1.9-Bug#4374][Open] [ext/openssl] ASN1.decode wrong for infinite length values

From: Martin Bosslet <redmine@...>
Date: 2011-02-06 17:52:52 UTC
List: ruby-core #35123
Bug #4374: [ext/openssl] ASN1.decode wrong for infinite length values
http://redmine.ruby-lang.org/issues/show/4374

Author: Martin Bosslet
Status: Open, Priority: Normal
Category: ext, Target version: 1.9.3
ruby -v: ruby 1.9.2p136 (2010-12-25 revision 30365) [i686-linux]

Hi all,

ASN.1 decoding behaves incorrectly for DER encodings with infinite length values. Two examples:


require 'openssl'
require 'pp'

eoc = OpenSSL::ASN1::EndOfContent.new
int = OpenSSL::ASN1::Integer.new (1)

inner = OpenSSL::ASN1::Sequence.new([int, eoc])
inner.infinite_length = true

outer = OpenSSL::ASN1::Sequence.new([inner, eoc])
outer.infinite_length = true

asn1 = OpenSSL::ASN1.decode(outer.to_der)

pp asn1

=> #<OpenSSL::ASN1::Sequence:0x9b4bd70
 @infinite_length=true,
 @tag=16,
 @tag_class=:UNIVERSAL,
 @tagging=nil,
 @value=
  [#<OpenSSL::ASN1::Sequence:0x9b4bd84
    @infinite_length=true,
    @tag=16,
    @tag_class=:UNIVERSAL,
    @tagging=nil,
    @value=
     [#<OpenSSL::ASN1::Integer:0x9b4be24
       @infinite_length=false,
       @tag=2,
       @tag_class=:UNIVERSAL,
       @tagging=nil,
       @value=1>,
      #<OpenSSL::ASN1::EndOfContent:0x9b4bde8
       @infinite_length=false,
       @tag=0,
       @tag_class=:UNIVERSAL,
       @tagging=nil,
       @value="">,
      #<OpenSSL::ASN1::EndOfContent:0x9b4bdac
       @infinite_length=false,
       @tag=0,
       @tag_class=:UNIVERSAL,
       @tagging=nil,
       @value="">]>]>

The end of content DER for the outer Sequence is incorrectly stored with the values 
of the inner sequence. Although after encoding the resulting DER will be correct, the
structure should rather look like this:

#<OpenSSL::ASN1::Sequence:0x9f58ee0
 @infinite_length=true,
 @tag=16,
 @tag_class=:UNIVERSAL,
 @tagging=nil,
 @value=
  [#<OpenSSL::ASN1::Sequence:0x9f58f30
    @infinite_length=true,
    @tag=16,
    @tag_class=:UNIVERSAL,
    @tagging=nil,
    @value=
     [#<OpenSSL::ASN1::Integer:0x9f58f94
       @infinite_length=false,
       @tag=2,
       @tag_class=:UNIVERSAL,
       @tagging=nil,
       @value=1>,
      #<OpenSSL::ASN1::EndOfContent:0x9f58f6c
       @infinite_length=false,
       @tag=0,
       @tag_class=:UNIVERSAL,
       @tagging=nil,
       @value="">]>,
   #<OpenSSL::ASN1::EndOfContent:0x9f58f08
    @infinite_length=false,
    @tag=0,
    @tag_class=:UNIVERSAL,
    @tagging=nil,
    @value="">]>

Another example:

require 'openssl'
require 'pp'

eoc = OpenSSL::ASN1::EndOfContent.new
oct = OpenSSL::ASN1::OctetString.new ("\x01")

inner = OpenSSL::ASN1::Constructive.new([oct, eoc], OpenSSL::ASN1::OCTET_STRING)
inner.infinite_length = true

outer = OpenSSL::ASN1::Constructive.new([inner, eoc], OpenSSL::ASN1::OCTET_STRING)
outer.infinite_length = true

asn1 = OpenSSL::ASN1.decode(outer.to_der)

pp asn1

=> <OpenSSL::ASN1::ASN1Data:0xa0fcdf0
 @infinite_length=true,
 @tag=4,
 @tag_class=:CONTEXT_SPECIFIC,
 @value=
  [#<OpenSSL::ASN1::Constructive:0xa0fce04
    @infinite_length=true,
    @tag=4,
    @tag_class=:UNIVERSAL,
    @tagging=:EXPLICIT,
    @value=
     [#<OpenSSL::ASN1::ASN1Data:0xa0fce2c
       @infinite_length=true,
       @tag=4,
       @tag_class=:CONTEXT_SPECIFIC,
       @value=
        [#<OpenSSL::ASN1::Constructive:0xa0fce40
          @infinite_length=true,
          @tag=4,
          @tag_class=:UNIVERSAL,
          @tagging=:EXPLICIT,
          @value=
           [#<OpenSSL::ASN1::OctetString:0xa0fcee0
             @infinite_length=false,
             @tag=4,
             @tag_class=:UNIVERSAL,
             @tagging=nil,
             @value="\x01">,
            #<OpenSSL::ASN1::EndOfContent:0xa0fceb8
             @infinite_length=false,
             @tag=0,
             @tag_class=:UNIVERSAL,
             @tagging=nil,
             @value="">,
            #<OpenSSL::ASN1::EndOfContent:0xa0fce68
             @infinite_length=false,
             @tag=0,
             @tag_class=:UNIVERSAL,
             @tagging=nil,
             @value="">]>]>]>]>

Here it's worse, because when calling asn1.to_der it will even result in an error:

test.rb:17:in `to_der': invalid constructed encoding (OpenSSL::ASN1::ASN1Error)
	from test.rb:17:in `each'
	from test.rb:17:in `to_der'
	from test.rb:17:in `<main>'

The problem are the defaults for tagging and tag_class in ossl_asn1_initialize that are not 
intuitive and are defaults for tagged DER values instead of "normal" values.

The correct structure for the above would look like this:

#<OpenSSL::ASN1::Constructive:0x93ed128
 @infinite_length=true,
 @tag=4,
 @tag_class=:UNIVERSAL,
 @tagging=nil,
 @value=
  [#<OpenSSL::ASN1::Constructive:0x93ed178
    @infinite_length=true,
    @tag=4,
    @tag_class=:UNIVERSAL,
    @tagging=nil,
    @value=
     [#<OpenSSL::ASN1::OctetString:0x93ed1c8
       @infinite_length=false,
       @tag=4,
       @tag_class=:UNIVERSAL,
       @tagging=nil,
       @value="\x01">,
      #<OpenSSL::ASN1::EndOfContent:0x93ed1a0
       @infinite_length=false,
       @tag=0,
       @tag_class=:UNIVERSAL,
       @tagging=nil,
       @value="">]>,
   #<OpenSSL::ASN1::EndOfContent:0x93ed150
    @infinite_length=false,
    @tag=0,
    @tag_class=:UNIVERSAL,
    @tagging=nil,
    @value="">]>

The attached patch fixes the problems and has also "more natural" defaults for
ossl_asn1_initialize.

Regards,
Martin


----------------------------------------
http://redmine.ruby-lang.org

Attachments (1)

fix_asn1.diff (3.62 KB, text/x-diff)
Index: ruby/ext/openssl/ossl_asn1.c
===================================================================
--- ruby/ext/openssl/ossl_asn1.c	(Revision 30810)
+++ ruby/ext/openssl/ossl_asn1.c	(Arbeitskopie)
@@ -734,8 +734,9 @@
     ary = rb_ary_new();
     p = *pp;
     while(length > 0){
 	p0 = p;
+       infinite = 0;
 	j = ASN1_get_object(&p0, &len, &tag, &tc, length);
 	p = (unsigned char *)p0;
 	if(j & 0x80) ossl_raise(eASN1Error, NULL);
@@ -826,6 +827,7 @@
                     asn1data = rb_funcall(cASN1EndOfContent,
                                           rb_intern("new"),
                                           0);
+                    once = 1;
                 }
                 else{
                     asn1data = rb_funcall(klass, rb_intern("new"), 1, value);
@@ -912,12 +914,14 @@
     if(argc > 1){
 	if(NIL_P(tag))
 	    ossl_raise(eASN1Error, "must specify tag number");
-        if(NIL_P(tagging))
-	    tagging = ID2SYM(sEXPLICIT);
-	if(!SYMBOL_P(tagging))
-	    ossl_raise(eASN1Error, "invalid tag default");
-	if(NIL_P(tag_class))
-	    tag_class = ID2SYM(sCONTEXT_SPECIFIC);
+        if(!NIL_P(tagging) && !SYMBOL_P(tagging))
+	    ossl_raise(eASN1Error, "invalid tagging method");
+	if(NIL_P(tag_class)) {
+            if (NIL_P(tagging))
+                tag_class = ID2SYM(sUNIVERSAL);
+            else
+                tag_class = ID2SYM(sCONTEXT_SPECIFIC);
+        }
 	if(!SYMBOL_P(tag_class))
 	    ossl_raise(eASN1Error, "invalid tag class");
 	if(SYM2ID(tagging) == sIMPLICIT && NUM2INT(tag) > 31)
Index: ruby/test/openssl/test_asn1.rb
===================================================================
--- ruby/test/openssl/test_asn1.rb	(Revision 30810)
+++ ruby/test/openssl/test_asn1.rb	(Arbeitskopie)
@@ -438,6 +438,51 @@
       OpenSSL::ASN1.decode_all(raw)
     end
   end
+
+  def test_recursive_octet_string_parse
+    test = %w{ 24 80 24 80 04 01 01 00 00 24 80 04 01 02 00 00 04 01 03 00 00 }
+    raw = [test.join('')].pack('H*')
+    asn1 = OpenSSL::ASN1.decode(raw)
+    assert_equal(OpenSSL::ASN1::Constructive, asn1.class)
+    assert_universal(OpenSSL::ASN1::OCTET_STRING, asn1)
+    assert_equal(true, asn1.infinite_length)
+    assert_equal(4, asn1.value.size)
+    nested1 = asn1.value[0]
+    assert_equal(OpenSSL::ASN1::Constructive, nested1.class)
+    assert_universal(OpenSSL::ASN1::OCTET_STRING, nested1)
+    assert_equal(true, nested1.infinite_length)
+    assert_equal(2, nested1.value.size)
+    oct1 = nested1.value[0]
+    assert_universal(OpenSSL::ASN1::OCTET_STRING, oct1)
+    assert_equal(false, oct1.infinite_length)
+    assert_universal(OpenSSL::ASN1::EOC, nested1.value[1])
+    assert_equal(false, nested1.value[1].infinite_length)
+    nested2 = asn1.value[1]
+    assert_equal(OpenSSL::ASN1::Constructive, nested2.class)
+    assert_universal(OpenSSL::ASN1::OCTET_STRING, nested2)
+    assert_equal(true, nested2.infinite_length)
+    assert_equal(2, nested2.value.size)
+    oct2 = nested2.value[0]
+    assert_universal(OpenSSL::ASN1::OCTET_STRING, oct2)
+    assert_equal(false, oct2.infinite_length)
+    assert_universal(OpenSSL::ASN1::EOC, nested2.value[1])
+    assert_equal(false, nested2.value[1].infinite_length)
+    oct3 = asn1.value[2]
+    assert_universal(OpenSSL::ASN1::OCTET_STRING, oct3)
+    assert_equal(false, oct3.infinite_length)
+    assert_universal(OpenSSL::ASN1::EOC, asn1.value[3])
+    assert_equal(false, asn1.value[3].infinite_length)
+  end
+
+  private
   
+  def assert_universal(tag, asn1)
+    assert_equal(tag, asn1.tag)
+    if asn1.respond_to?(:tagging)
+      assert_nil(asn1.tagging)
+    end
+    assert_equal(:UNIVERSAL, asn1.tag_class)
+  end
+  
 end if defined?(OpenSSL)
 

In This Thread

Prev Next