##// END OF EJS Templates
Fix applied to ruby-net-ldap (#608)....
Jean-Philippe Lang -
r1134:96b78fdae579
parent child
Show More
@@ -1,294 +1,294
1 # $Id: ber.rb 142 2006-07-26 12:20:33Z blackhedd $
1 # $Id: ber.rb 142 2006-07-26 12:20:33Z blackhedd $
2 #
2 #
3 # NET::BER
3 # NET::BER
4 # Mixes ASN.1/BER convenience methods into several standard classes.
4 # Mixes ASN.1/BER convenience methods into several standard classes.
5 # Also provides BER parsing functionality.
5 # Also provides BER parsing functionality.
6 #
6 #
7 #----------------------------------------------------------------------------
7 #----------------------------------------------------------------------------
8 #
8 #
9 # Copyright (C) 2006 by Francis Cianfrocca. All Rights Reserved.
9 # Copyright (C) 2006 by Francis Cianfrocca. All Rights Reserved.
10 #
10 #
11 # Gmail: garbagecat10
11 # Gmail: garbagecat10
12 #
12 #
13 # This program is free software; you can redistribute it and/or modify
13 # This program is free software; you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation; either version 2 of the License, or
15 # the Free Software Foundation; either version 2 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
26 #
26 #
27 #---------------------------------------------------------------------------
27 #---------------------------------------------------------------------------
28 #
28 #
29 #
29 #
30
30
31
31
32
32
33
33
34 module Net
34 module Net
35
35
36 module BER
36 module BER
37
37
38 class BerError < Exception; end
38 class BerError < Exception; end
39
39
40
40
41 # This module is for mixing into IO and IO-like objects.
41 # This module is for mixing into IO and IO-like objects.
42 module BERParser
42 module BERParser
43
43
44 # The order of these follows the class-codes in BER.
44 # The order of these follows the class-codes in BER.
45 # Maybe this should have been a hash.
45 # Maybe this should have been a hash.
46 TagClasses = [:universal, :application, :context_specific, :private]
46 TagClasses = [:universal, :application, :context_specific, :private]
47
47
48 BuiltinSyntax = {
48 BuiltinSyntax = {
49 :universal => {
49 :universal => {
50 :primitive => {
50 :primitive => {
51 1 => :boolean,
51 1 => :boolean,
52 2 => :integer,
52 2 => :integer,
53 4 => :string,
53 4 => :string,
54 10 => :integer,
54 10 => :integer,
55 },
55 },
56 :constructed => {
56 :constructed => {
57 16 => :array,
57 16 => :array,
58 17 => :array
58 17 => :array
59 }
59 }
60 }
60 }
61 }
61 }
62
62
63 #
63 #
64 # read_ber
64 # read_ber
65 # TODO: clean this up so it works properly with partial
65 # TODO: clean this up so it works properly with partial
66 # packets coming from streams that don't block when
66 # packets coming from streams that don't block when
67 # we ask for more data (like StringIOs). At it is,
67 # we ask for more data (like StringIOs). At it is,
68 # this can throw TypeErrors and other nasties.
68 # this can throw TypeErrors and other nasties.
69 #
69 #
70 def read_ber syntax=nil
70 def read_ber syntax=nil
71 return nil if eof?
71 return nil if (StringIO == self.class) and eof?
72
72
73 id = getc # don't trash this value, we'll use it later
73 id = getc # don't trash this value, we'll use it later
74 tag = id & 31
74 tag = id & 31
75 tag < 31 or raise BerError.new( "unsupported tag encoding: #{id}" )
75 tag < 31 or raise BerError.new( "unsupported tag encoding: #{id}" )
76 tagclass = TagClasses[ id >> 6 ]
76 tagclass = TagClasses[ id >> 6 ]
77 encoding = (id & 0x20 != 0) ? :constructed : :primitive
77 encoding = (id & 0x20 != 0) ? :constructed : :primitive
78
78
79 n = getc
79 n = getc
80 lengthlength,contentlength = if n <= 127
80 lengthlength,contentlength = if n <= 127
81 [1,n]
81 [1,n]
82 else
82 else
83 j = (0...(n & 127)).inject(0) {|mem,x| mem = (mem << 8) + getc}
83 j = (0...(n & 127)).inject(0) {|mem,x| mem = (mem << 8) + getc}
84 [1 + (n & 127), j]
84 [1 + (n & 127), j]
85 end
85 end
86
86
87 newobj = read contentlength
87 newobj = read contentlength
88
88
89 objtype = nil
89 objtype = nil
90 [syntax, BuiltinSyntax].each {|syn|
90 [syntax, BuiltinSyntax].each {|syn|
91 if syn && (ot = syn[tagclass]) && (ot = ot[encoding]) && ot[tag]
91 if syn && (ot = syn[tagclass]) && (ot = ot[encoding]) && ot[tag]
92 objtype = ot[tag]
92 objtype = ot[tag]
93 break
93 break
94 end
94 end
95 }
95 }
96
96
97 obj = case objtype
97 obj = case objtype
98 when :boolean
98 when :boolean
99 newobj != "\000"
99 newobj != "\000"
100 when :string
100 when :string
101 (newobj || "").dup
101 (newobj || "").dup
102 when :integer
102 when :integer
103 j = 0
103 j = 0
104 newobj.each_byte {|b| j = (j << 8) + b}
104 newobj.each_byte {|b| j = (j << 8) + b}
105 j
105 j
106 when :array
106 when :array
107 seq = []
107 seq = []
108 sio = StringIO.new( newobj || "" )
108 sio = StringIO.new( newobj || "" )
109 # Interpret the subobject, but note how the loop
109 # Interpret the subobject, but note how the loop
110 # is built: nil ends the loop, but false (a valid
110 # is built: nil ends the loop, but false (a valid
111 # BER value) does not!
111 # BER value) does not!
112 while (e = sio.read_ber(syntax)) != nil
112 while (e = sio.read_ber(syntax)) != nil
113 seq << e
113 seq << e
114 end
114 end
115 seq
115 seq
116 else
116 else
117 raise BerError.new( "unsupported object type: class=#{tagclass}, encoding=#{encoding}, tag=#{tag}" )
117 raise BerError.new( "unsupported object type: class=#{tagclass}, encoding=#{encoding}, tag=#{tag}" )
118 end
118 end
119
119
120 # Add the identifier bits into the object if it's a String or an Array.
120 # Add the identifier bits into the object if it's a String or an Array.
121 # We can't add extra stuff to Fixnums and booleans, not that it makes much sense anyway.
121 # We can't add extra stuff to Fixnums and booleans, not that it makes much sense anyway.
122 obj and ([String,Array].include? obj.class) and obj.instance_eval "def ber_identifier; #{id}; end"
122 obj and ([String,Array].include? obj.class) and obj.instance_eval "def ber_identifier; #{id}; end"
123 obj
123 obj
124
124
125 end
125 end
126
126
127 end # module BERParser
127 end # module BERParser
128 end # module BER
128 end # module BER
129
129
130 end # module Net
130 end # module Net
131
131
132
132
133 class IO
133 class IO
134 include Net::BER::BERParser
134 include Net::BER::BERParser
135 end
135 end
136
136
137 require "stringio"
137 require "stringio"
138 class StringIO
138 class StringIO
139 include Net::BER::BERParser
139 include Net::BER::BERParser
140 end
140 end
141
141
142 begin
142 begin
143 require 'openssl'
143 require 'openssl'
144 class OpenSSL::SSL::SSLSocket
144 class OpenSSL::SSL::SSLSocket
145 include Net::BER::BERParser
145 include Net::BER::BERParser
146 end
146 end
147 rescue LoadError
147 rescue LoadError
148 # Ignore LoadError.
148 # Ignore LoadError.
149 # DON'T ignore NameError, which means the SSLSocket class
149 # DON'T ignore NameError, which means the SSLSocket class
150 # is somehow unavailable on this implementation of Ruby's openssl.
150 # is somehow unavailable on this implementation of Ruby's openssl.
151 # This may be WRONG, however, because we don't yet know how Ruby's
151 # This may be WRONG, however, because we don't yet know how Ruby's
152 # openssl behaves on machines with no OpenSSL library. I suppose
152 # openssl behaves on machines with no OpenSSL library. I suppose
153 # it's possible they do not fail to require 'openssl' but do not
153 # it's possible they do not fail to require 'openssl' but do not
154 # create the classes. So this code is provisional.
154 # create the classes. So this code is provisional.
155 # Also, you might think that OpenSSL::SSL::SSLSocket inherits from
155 # Also, you might think that OpenSSL::SSL::SSLSocket inherits from
156 # IO so we'd pick it up above. But you'd be wrong.
156 # IO so we'd pick it up above. But you'd be wrong.
157 end
157 end
158
158
159 class String
159 class String
160 def read_ber syntax=nil
160 def read_ber syntax=nil
161 StringIO.new(self).read_ber(syntax)
161 StringIO.new(self).read_ber(syntax)
162 end
162 end
163 end
163 end
164
164
165
165
166
166
167 #----------------------------------------------
167 #----------------------------------------------
168
168
169
169
170 class FalseClass
170 class FalseClass
171 #
171 #
172 # to_ber
172 # to_ber
173 #
173 #
174 def to_ber
174 def to_ber
175 "\001\001\000"
175 "\001\001\000"
176 end
176 end
177 end
177 end
178
178
179
179
180 class TrueClass
180 class TrueClass
181 #
181 #
182 # to_ber
182 # to_ber
183 #
183 #
184 def to_ber
184 def to_ber
185 "\001\001\001"
185 "\001\001\001"
186 end
186 end
187 end
187 end
188
188
189
189
190
190
191 class Fixnum
191 class Fixnum
192 #
192 #
193 # to_ber
193 # to_ber
194 #
194 #
195 def to_ber
195 def to_ber
196 i = [self].pack('w')
196 i = [self].pack('w')
197 [2, i.length].pack("CC") + i
197 [2, i.length].pack("CC") + i
198 end
198 end
199
199
200 #
200 #
201 # to_ber_enumerated
201 # to_ber_enumerated
202 #
202 #
203 def to_ber_enumerated
203 def to_ber_enumerated
204 i = [self].pack('w')
204 i = [self].pack('w')
205 [10, i.length].pack("CC") + i
205 [10, i.length].pack("CC") + i
206 end
206 end
207
207
208 #
208 #
209 # to_ber_length_encoding
209 # to_ber_length_encoding
210 #
210 #
211 def to_ber_length_encoding
211 def to_ber_length_encoding
212 if self <= 127
212 if self <= 127
213 [self].pack('C')
213 [self].pack('C')
214 else
214 else
215 i = [self].pack('N').sub(/^[\0]+/,"")
215 i = [self].pack('N').sub(/^[\0]+/,"")
216 [0x80 + i.length].pack('C') + i
216 [0x80 + i.length].pack('C') + i
217 end
217 end
218 end
218 end
219
219
220 end # class Fixnum
220 end # class Fixnum
221
221
222
222
223 class Bignum
223 class Bignum
224
224
225 def to_ber
225 def to_ber
226 i = [self].pack('w')
226 i = [self].pack('w')
227 i.length > 126 and raise Net::BER::BerError.new( "range error in bignum" )
227 i.length > 126 and raise Net::BER::BerError.new( "range error in bignum" )
228 [2, i.length].pack("CC") + i
228 [2, i.length].pack("CC") + i
229 end
229 end
230
230
231 end
231 end
232
232
233
233
234
234
235 class String
235 class String
236 #
236 #
237 # to_ber
237 # to_ber
238 # A universal octet-string is tag number 4,
238 # A universal octet-string is tag number 4,
239 # but others are possible depending on the context, so we
239 # but others are possible depending on the context, so we
240 # let the caller give us one.
240 # let the caller give us one.
241 # The preferred way to do this in user code is via to_ber_application_sring
241 # The preferred way to do this in user code is via to_ber_application_sring
242 # and to_ber_contextspecific.
242 # and to_ber_contextspecific.
243 #
243 #
244 def to_ber code = 4
244 def to_ber code = 4
245 [code].pack('C') + length.to_ber_length_encoding + self
245 [code].pack('C') + length.to_ber_length_encoding + self
246 end
246 end
247
247
248 #
248 #
249 # to_ber_application_string
249 # to_ber_application_string
250 #
250 #
251 def to_ber_application_string code
251 def to_ber_application_string code
252 to_ber( 0x40 + code )
252 to_ber( 0x40 + code )
253 end
253 end
254
254
255 #
255 #
256 # to_ber_contextspecific
256 # to_ber_contextspecific
257 #
257 #
258 def to_ber_contextspecific code
258 def to_ber_contextspecific code
259 to_ber( 0x80 + code )
259 to_ber( 0x80 + code )
260 end
260 end
261
261
262 end # class String
262 end # class String
263
263
264
264
265
265
266 class Array
266 class Array
267 #
267 #
268 # to_ber_appsequence
268 # to_ber_appsequence
269 # An application-specific sequence usually gets assigned
269 # An application-specific sequence usually gets assigned
270 # a tag that is meaningful to the particular protocol being used.
270 # a tag that is meaningful to the particular protocol being used.
271 # This is different from the universal sequence, which usually
271 # This is different from the universal sequence, which usually
272 # gets a tag value of 16.
272 # gets a tag value of 16.
273 # Now here's an interesting thing: We're adding the X.690
273 # Now here's an interesting thing: We're adding the X.690
274 # "application constructed" code at the top of the tag byte (0x60),
274 # "application constructed" code at the top of the tag byte (0x60),
275 # but some clients, notably ldapsearch, send "context-specific
275 # but some clients, notably ldapsearch, send "context-specific
276 # constructed" (0xA0). The latter would appear to violate RFC-1777,
276 # constructed" (0xA0). The latter would appear to violate RFC-1777,
277 # but what do I know? We may need to change this.
277 # but what do I know? We may need to change this.
278 #
278 #
279
279
280 def to_ber id = 0; to_ber_seq_internal( 0x30 + id ); end
280 def to_ber id = 0; to_ber_seq_internal( 0x30 + id ); end
281 def to_ber_set id = 0; to_ber_seq_internal( 0x31 + id ); end
281 def to_ber_set id = 0; to_ber_seq_internal( 0x31 + id ); end
282 def to_ber_sequence id = 0; to_ber_seq_internal( 0x30 + id ); end
282 def to_ber_sequence id = 0; to_ber_seq_internal( 0x30 + id ); end
283 def to_ber_appsequence id = 0; to_ber_seq_internal( 0x60 + id ); end
283 def to_ber_appsequence id = 0; to_ber_seq_internal( 0x60 + id ); end
284 def to_ber_contextspecific id = 0; to_ber_seq_internal( 0xA0 + id ); end
284 def to_ber_contextspecific id = 0; to_ber_seq_internal( 0xA0 + id ); end
285
285
286 private
286 private
287 def to_ber_seq_internal code
287 def to_ber_seq_internal code
288 s = self.to_s
288 s = self.to_s
289 [code].pack('C') + s.length.to_ber_length_encoding + s
289 [code].pack('C') + s.length.to_ber_length_encoding + s
290 end
290 end
291
291
292 end # class Array
292 end # class Array
293
293
294
294
General Comments 0
You need to be logged in to leave comments. Login now