scashin133-net-ldap 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/net/ber.rb ADDED
@@ -0,0 +1,339 @@
1
+ # NET::BER
2
+ # Mixes ASN.1/BER convenience methods into several standard classes. Also
3
+ # provides BER parsing functionality.
4
+ #
5
+ #--
6
+ # Copyright (C) 2006 by Francis Cianfrocca. All Rights Reserved.
7
+ #
8
+ # Gmail: garbagecat10
9
+ #
10
+ # This program is free software; you can redistribute it and/or modify it
11
+ # under the terms of the GNU General Public License as published by the Free
12
+ # Software Foundation; either version 2 of the License, or (at your option)
13
+ # any later version.
14
+ #
15
+ # This program is distributed in the hope that it will be useful, but
16
+ # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
17
+ # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
18
+ # for more details.
19
+ #
20
+ # You should have received a copy of the GNU General Public License along
21
+ # with this program; if not, write to the Free Software Foundation, Inc., 51
22
+ # Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23
+ #++
24
+
25
+ module Net
26
+ ##
27
+ # == Basic Encoding Rules (BER) Support Module
28
+ #
29
+ # Much of the text below is cribbed from Wikipedia:
30
+ # http://en.wikipedia.org/wiki/Basic_Encoding_Rules
31
+ #
32
+ # The ITU Specification is also worthwhile reading:
33
+ # http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf
34
+ #
35
+ # The Basic Encoding Rules were the original rules laid out by the ASN.1
36
+ # standard for encoding abstract information into a concrete data stream.
37
+ # The rules, collectively referred to as a transfer syntax in ASN.1
38
+ # parlance, specify the exact octet sequences which are used to encode a
39
+ # given data item. The syntax defines such elements as: the
40
+ # representations for basic data types, the structure of length
41
+ # information, and the means for defining complex or compound types based
42
+ # on more primitive types. The BER syntax, along with two subsets of BER
43
+ # (the Canonical Encoding Rules and the Distinguished Encoding Rules), are
44
+ # defined by the ITU-T's X.690 standards document, which is part of the
45
+ # ASN.1 document series.
46
+ #
47
+ # == Encoding
48
+ # The BER format specifies a self-describing and self-delimiting format
49
+ # for encoding ASN.1 data structures. Each data element is encoded as a
50
+ # type identifier, a length description, the actual data elements, and
51
+ # where necessary, an end-of-content marker. This format allows a receiver
52
+ # to decode the ASN.1 information from an incomplete stream, without
53
+ # requiring any pre-knowledge of the size, content, or semantic meaning of
54
+ # the data.
55
+ #
56
+ # <Type | Length | Value [| End-of-Content]>
57
+ #
58
+ # == Protocol Data Units (PDU)
59
+ # Protocols are defined with schema represented in BER, such that a PDU
60
+ # consists of cascaded type-length-value encodings.
61
+ #
62
+ # === Type Tags
63
+ # BER type tags are represented as single octets (bytes). The lower five
64
+ # bits of the octet are tag identifier numbers and the upper three bits of
65
+ # the octet are used to distinguish the type as native to ASN.1,
66
+ # application-specific, context-specific, or private. See
67
+ # Net::BER::TAG_CLASS and Net::BER::ENCODING_TYPE for more information.
68
+ #
69
+ # If Class is set to Universal (0b00______), the value is of a type native
70
+ # to ASN.1 (e.g. INTEGER). The Application class (0b01______) is only
71
+ # valid for one specific application. Context_specific (0b10______)
72
+ # depends on the context and private (0b11_______) can be defined in
73
+ # private specifications
74
+ #
75
+ # If the primitive/constructed bit is zero (0b__0_____), it specifies that
76
+ # the value is primitive like an INTEGER. If it is one (0b__1_____), the
77
+ # value is a constructed value that contains type-length-value encoded
78
+ # types like a SET or a SEQUENCE.
79
+ #
80
+ # === Defined Universal (ASN.1 Native) Types
81
+ # There are a number of pre-defined universal (native) types.
82
+ #
83
+ # <table>
84
+ # <tr><th>Name</th><th>Primitive<br />Constructed</th><th>Number</th></tr>
85
+ # <tr><th>EOC (End-of-Content)</th><th>P</th><td>0: 0 (0x0, 0b00000000)</td></tr>
86
+ # <tr><th>BOOLEAN</th><th>P</th><td>1: 1 (0x01, 0b00000001)</td></tr>
87
+ # <tr><th>INTEGER</th><th>P</th><td>2: 2 (0x02, 0b00000010)</td></tr>
88
+ # <tr><th>BIT STRING</th><th>P</th><td>3: 3 (0x03, 0b00000011)</td></tr>
89
+ # <tr><th>BIT STRING</th><th>C</th><td>3: 35 (0x23, 0b00100011)</td></tr>
90
+ # <tr><th>OCTET STRING</th><th>P</th><td>4: 4 (0x04, 0b00000100)</td></tr>
91
+ # <tr><th>OCTET STRING</th><th>C</th><td>4: 36 (0x24, 0b00100100)</td></tr>
92
+ # <tr><th>NULL</th><th>P</th><td>5: 5 (0x05, 0b00000101)</td></tr>
93
+ # <tr><th>OBJECT IDENTIFIER</th><th>P</th><td>6: 6 (0x06, 0b00000110)</td></tr>
94
+ # <tr><th>Object Descriptor</th><th>P</th><td>7: 7 (0x07, 0b00000111)</td></tr>
95
+ # <tr><th>EXTERNAL</th><th>C</th><td>8: 40 (0x28, 0b00101000)</td></tr>
96
+ # <tr><th>REAL (float)</th><th>P</th><td>9: 9 (0x09, 0b00001001)</td></tr>
97
+ # <tr><th>ENUMERATED</th><th>P</th><td>10: 10 (0x0a, 0b00001010)</td></tr>
98
+ # <tr><th>EMBEDDED PDV</th><th>C</th><td>11: 43 (0x2b, 0b00101011)</td></tr>
99
+ # <tr><th>UTF8String</th><th>P</th><td>12: 12 (0x0c, 0b00001100)</td></tr>
100
+ # <tr><th>UTF8String</th><th>C</th><td>12: 44 (0x2c, 0b00101100)</td></tr>
101
+ # <tr><th>RELATIVE-OID</th><th>P</th><td>13: 13 (0x0d, 0b00001101)</td></tr>
102
+ # <tr><th>SEQUENCE and SEQUENCE OF</th><th>C</th><td>16: 48 (0x30, 0b00110000)</td></tr>
103
+ # <tr><th>SET and SET OF</th><th>C</th><td>17: 49 (0x31, 0b00110001)</td></tr>
104
+ # <tr><th>NumericString</th><th>P</th><td>18: 18 (0x12, 0b00010010)</td></tr>
105
+ # <tr><th>NumericString</th><th>C</th><td>18: 50 (0x32, 0b00110010)</td></tr>
106
+ # <tr><th>PrintableString</th><th>P</th><td>19: 19 (0x13, 0b00010011)</td></tr>
107
+ # <tr><th>PrintableString</th><th>C</th><td>19: 51 (0x33, 0b00110011)</td></tr>
108
+ # <tr><th>T61String</th><th>P</th><td>20: 20 (0x14, 0b00010100)</td></tr>
109
+ # <tr><th>T61String</th><th>C</th><td>20: 52 (0x34, 0b00110100)</td></tr>
110
+ # <tr><th>VideotexString</th><th>P</th><td>21: 21 (0x15, 0b00010101)</td></tr>
111
+ # <tr><th>VideotexString</th><th>C</th><td>21: 53 (0x35, 0b00110101)</td></tr>
112
+ # <tr><th>IA5String</th><th>P</th><td>22: 22 (0x16, 0b00010110)</td></tr>
113
+ # <tr><th>IA5String</th><th>C</th><td>22: 54 (0x36, 0b00110110)</td></tr>
114
+ # <tr><th>UTCTime</th><th>P</th><td>23: 23 (0x17, 0b00010111)</td></tr>
115
+ # <tr><th>UTCTime</th><th>C</th><td>23: 55 (0x37, 0b00110111)</td></tr>
116
+ # <tr><th>GeneralizedTime</th><th>P</th><td>24: 24 (0x18, 0b00011000)</td></tr>
117
+ # <tr><th>GeneralizedTime</th><th>C</th><td>24: 56 (0x38, 0b00111000)</td></tr>
118
+ # <tr><th>GraphicString</th><th>P</th><td>25: 25 (0x19, 0b00011001)</td></tr>
119
+ # <tr><th>GraphicString</th><th>C</th><td>25: 57 (0x39, 0b00111001)</td></tr>
120
+ # <tr><th>VisibleString</th><th>P</th><td>26: 26 (0x1a, 0b00011010)</td></tr>
121
+ # <tr><th>VisibleString</th><th>C</th><td>26: 58 (0x3a, 0b00111010)</td></tr>
122
+ # <tr><th>GeneralString</th><th>P</th><td>27: 27 (0x1b, 0b00011011)</td></tr>
123
+ # <tr><th>GeneralString</th><th>C</th><td>27: 59 (0x3b, 0b00111011)</td></tr>
124
+ # <tr><th>UniversalString</th><th>P</th><td>28: 28 (0x1c, 0b00011100)</td></tr>
125
+ # <tr><th>UniversalString</th><th>C</th><td>28: 60 (0x3c, 0b00111100)</td></tr>
126
+ # <tr><th>CHARACTER STRING</th><th>P</th><td>29: 29 (0x1d, 0b00011101)</td></tr>
127
+ # <tr><th>CHARACTER STRING</th><th>C</th><td>29: 61 (0x3d, 0b00111101)</td></tr>
128
+ # <tr><th>BMPString</th><th>P</th><td>30: 30 (0x1e, 0b00011110)</td></tr>
129
+ # <tr><th>BMPString</th><th>C</th><td>30: 62 (0x3e, 0b00111110)</td></tr>
130
+ # </table>
131
+ module BER
132
+ VERSION = '0.1.0'
133
+
134
+ ##
135
+ # Used for BER-encoding the length and content bytes of a Fixnum integer
136
+ # values.
137
+ MAX_FIXNUM_SIZE = 0.size
138
+
139
+ ##
140
+ # BER tag classes are kept in bits seven and eight of the tag type
141
+ # octet.
142
+ #
143
+ # <table>
144
+ # <tr><th>Bitmask</th><th>Definition</th></tr>
145
+ # <tr><th><tt>0b00______</tt></th><td>Universal (ASN.1 Native) Types</td></tr>
146
+ # <tr><th><tt>0b01______</tt></th><td>Application Types</td></tr>
147
+ # <tr><th><tt>0b10______</tt></th><td>Context-Specific Types</td></tr>
148
+ # <tr><th><tt>0b11______</tt></th><td>Private Types</td></tr>
149
+ # </table>
150
+ TAG_CLASS = {
151
+ :universal => 0b00000000, # 0
152
+ :application => 0b01000000, # 64
153
+ :context_specific => 0b10000000, # 128
154
+ :private => 0b11000000, # 192
155
+ }
156
+
157
+ ##
158
+ # BER encoding type is kept in bit 6 of the tag type octet.
159
+ #
160
+ # <table>
161
+ # <tr><th>Bitmask</th><th>Definition</th></tr>
162
+ # <tr><th><tt>0b__0_____</tt></th><td>Primitive</td></tr>
163
+ # <tr><th><tt>0b__1_____</tt></th><td>Constructed</td></tr>
164
+ # </table>
165
+ ENCODING_TYPE = {
166
+ :primitive => 0b00000000, # 0
167
+ :constructed => 0b00100000, # 32
168
+ }
169
+
170
+ ##
171
+ # Accepts a hash of hashes describing a BER syntax and converts it into
172
+ # a byte-keyed object for fast BER conversion lookup. The resulting
173
+ # "compiled" syntax is used by Net::BER::BERParser.
174
+ #
175
+ # This method should be called only by client classes of Net::BER (e.g.,
176
+ # Net::LDAP and Net::SNMP) and not by clients of those classes.
177
+ #
178
+ # The hash-based syntax uses TAG_CLASS keys that contain hashes of
179
+ # ENCODING_TYPE keys that contain tag numbers with object type markers.
180
+ #
181
+ # :<TAG_CLASS> => {
182
+ # :<ENCODING_TYPE> => {
183
+ # <number> => <object-type>
184
+ # },
185
+ # },
186
+ #
187
+ # === Permitted Object Types
188
+ # <tt>:string</tt>:: A string value, represented as BerIdentifiedString.
189
+ # <tt>:integer</tt>:: An integer value, represented with Fixnum.
190
+ # <tt>:oid</tt>:: An Object Identifier value; see X.690 section
191
+ # 8.19. Currently represented with a standard array,
192
+ # but may be better represented as a
193
+ # BerIdentifiedOID object.
194
+ # <tt>:array</tt>:: A sequence, represented as BerIdentifiedArray.
195
+ # <tt>:boolean</tt>:: A boolean value, represented as +true+ or +false+.
196
+ # <tt>:null</tt>:: A null value, represented as BerIdentifiedNull.
197
+ #
198
+ # === Example
199
+ # Net::LDAP defines its ASN.1 BER syntax something like this:
200
+ #
201
+ # class Net::LDAP
202
+ # AsnSyntax = Net::BER.compile_syntax({
203
+ # :application => {
204
+ # :primitive => {
205
+ # 2 => :null,
206
+ # },
207
+ # :constructed => {
208
+ # 0 => :array,
209
+ # # ...
210
+ # },
211
+ # },
212
+ # :context_specific => {
213
+ # :primitive => {
214
+ # 0 => :string,
215
+ # # ...
216
+ # },
217
+ # :constructed => {
218
+ # 0 => :array,
219
+ # # ...
220
+ # },
221
+ # }
222
+ # })
223
+ # end
224
+ #
225
+ # NOTE:: For readability and formatting purposes, Net::LDAP and its
226
+ # siblings actually construct their syntaxes more deliberately,
227
+ # as shown below. Since a hash is passed in the end in any case,
228
+ # the format does not matter.
229
+ #
230
+ # primitive = { 2 => :null }
231
+ # constructed = {
232
+ # 0 => :array,
233
+ # # ...
234
+ # }
235
+ # application = {
236
+ # :primitive => primitive,
237
+ # :constructed => constructed
238
+ # }
239
+ #
240
+ # primitive = {
241
+ # 0 => :string,
242
+ # # ...
243
+ # }
244
+ # constructed = {
245
+ # 0 => :array,
246
+ # # ...
247
+ # }
248
+ # context_specific = {
249
+ # :primitive => primitive,
250
+ # :constructed => constructed
251
+ # }
252
+ # AsnSyntax = Net::BER.compile_syntax(:application => application,
253
+ # :context_specific => context_specific)
254
+ def self.compile_syntax(syntax)
255
+ # TODO 20100327 AZ: Should we be allocating an array of 256 values
256
+ # that will either be +nil+ or an object type symbol, or should we
257
+ # allocate an empty Hash since unknown values return +nil+ anyway?
258
+ out = [ nil ] * 256
259
+ syntax.each do |tag_class_id, encodings|
260
+ tag_class = TAG_CLASS[tag_class_id]
261
+ encodings.each do |encoding_id, classes|
262
+ encoding = ENCODING_TYPE[encoding_id]
263
+ object_class = tag_class + encoding
264
+ classes.each do |number, object_type|
265
+ out[object_class + number] = object_type
266
+ end
267
+ end
268
+ end
269
+ out
270
+ end
271
+ end
272
+ end
273
+
274
+ class Net::BER::BerError < RuntimeError; end
275
+
276
+ ##
277
+ # An Array object with a BER identifier attached.
278
+ class Net::BER::BerIdentifiedArray < Array
279
+ attr_accessor :ber_identifier
280
+
281
+ def initialize(*args)
282
+ super
283
+ end
284
+ end
285
+
286
+ ##
287
+ # A BER object identifier.
288
+ class Net::BER::BerIdentifiedOid
289
+ attr_accessor :ber_identifier
290
+
291
+ def initialize(oid)
292
+ if oid.is_a?(String)
293
+ oid = oid.split(/\./).map {|s| s.to_i }
294
+ end
295
+ @value = oid
296
+ end
297
+
298
+ def to_ber
299
+ to_ber_oid
300
+ end
301
+
302
+ def to_ber_oid
303
+ @value.to_ber_oid
304
+ end
305
+
306
+ def to_s
307
+ @value.join(".")
308
+ end
309
+
310
+ def to_arr
311
+ @value.dup
312
+ end
313
+ end
314
+
315
+ ##
316
+ # A String object with a BER identifier attached.
317
+ class Net::BER::BerIdentifiedString < String
318
+ attr_accessor :ber_identifier
319
+ def initialize args
320
+ super args
321
+ end
322
+ end
323
+
324
+ module Net::BER
325
+ ##
326
+ # A BER null object.
327
+ class BerIdentifiedNull
328
+ attr_accessor :ber_identifier
329
+ def to_ber
330
+ "\005\000"
331
+ end
332
+ end
333
+
334
+ ##
335
+ # The default BerIdentifiedNull object.
336
+ Null = Net::BER::BerIdentifiedNull.new
337
+ end
338
+
339
+ require 'net/ber/core_ext'
@@ -0,0 +1,174 @@
1
+ #----------------------------------------------------------------------------
2
+ #
3
+ # Copyright (C) 2006 by Francis Cianfrocca. All Rights Reserved.
4
+ #
5
+ # Gmail: garbagecat10
6
+ #
7
+ # This program is free software; you can redistribute it and/or modify
8
+ # it under the terms of the GNU General Public License as published by
9
+ # the Free Software Foundation; either version 2 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # This program is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU General Public License
18
+ # along with this program; if not, write to the Free Software
19
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20
+ #
21
+ #---------------------------------------------------------------------------
22
+
23
+ ##
24
+ # An LDAP Dataset. Used primarily as an intermediate format for converting
25
+ # to and from LDIF strings and Net::LDAP::Entry objects.
26
+ class Net::LDAP::Dataset < Hash
27
+ ##
28
+ # Dataset object comments.
29
+ attr_reader :comments
30
+
31
+ class << self
32
+ class ChompedIO #:nodoc:
33
+ def initialize(io)
34
+ @io = io
35
+ end
36
+ def gets
37
+ s = @io.gets
38
+ s.chomp if s
39
+ end
40
+ end
41
+
42
+ ##
43
+ # Reads an object that returns data line-wise (using #gets) and parses
44
+ # LDIF data into a Dataset object.
45
+ def read_ldif(io) #:yields: entry-type, value Used mostly for debugging.
46
+ ds = Net::LDAP::Dataset.new
47
+ io = ChompedIO.new(io)
48
+
49
+ line = io.gets
50
+ dn = nil
51
+
52
+ while line
53
+ new_line = io.gets
54
+
55
+ if new_line =~ /^[\s]+/
56
+ line << " " << $'
57
+ else
58
+ nextline = new_line
59
+
60
+ if line =~ /^#/
61
+ ds.comments << line
62
+ yield :comment, line if block_given?
63
+ elsif line =~ /^dn:[\s]*/i
64
+ dn = $'
65
+ ds[dn] = Hash.new { |k,v| k[v] = [] }
66
+ yield :dn, dn if block_given?
67
+ elsif line.empty?
68
+ dn = nil
69
+ yield :end, nil if block_given?
70
+ elsif line =~ /^([^:]+):([\:]?)[\s]*/
71
+ # $1 is the attribute name
72
+ # $2 is a colon iff the attr-value is base-64 encoded
73
+ # $' is the attr-value
74
+ # Avoid the Base64 class because not all Ruby versions have it.
75
+ attrvalue = ($2 == ":") ? $'.unpack('m').shift : $'
76
+ ds[dn][$1.downcase.to_sym] << attrvalue
77
+ yield :attr, [$1.downcase.to_sym, attrvalue] if block_given?
78
+ end
79
+
80
+ line = nextline
81
+ end
82
+ end
83
+
84
+ ds
85
+ end
86
+
87
+ ##
88
+ # Creates a Dataset object from an Entry object. Used mostly to assist
89
+ # with the conversion of
90
+ def from_entry(entry)
91
+ dataset = Net::LDAP::Dataset.new
92
+ hash = { }
93
+ entry.each_attribute do |attribute, value|
94
+ next if attribute == :dn
95
+ hash[attribute] = value
96
+ end
97
+ dataset[entry.dn] = hash
98
+ dataset
99
+ end
100
+ end
101
+
102
+ def initialize(*args, &block) #:nodoc:
103
+ super
104
+ @comments = []
105
+ end
106
+
107
+ ##
108
+ # Outputs an LDAP Dataset as an array of strings representing LDIF
109
+ # entries.
110
+ def to_ldif
111
+ ary = []
112
+ ary += @comments unless @comments.empty?
113
+ keys.sort.each do |dn|
114
+ ary << "dn: #{dn}"
115
+
116
+ attributes = self[dn].keys.map { |attr| attr.to_s }.sort
117
+ attributes.each do |attr|
118
+ self[dn][attr.to_sym].each do |value|
119
+ if attr == "userpassword" or value_is_binary?(value)
120
+ value = [value].pack("m").chomp.gsub(/\n/m, "\n ")
121
+ ary << "#{attr}:: #{value}"
122
+ else
123
+ ary << "#{attr}: #{value}"
124
+ end
125
+ end
126
+ end
127
+
128
+ ary << ""
129
+ end
130
+ block_given? and ary.each { |line| yield line}
131
+
132
+ ary
133
+ end
134
+
135
+ ##
136
+ # Outputs an LDAP Dataset as an LDIF string.
137
+ def to_ldif_string
138
+ to_ldif.join("\n")
139
+ end
140
+
141
+ ##
142
+ # Convert the parsed LDIF objects to Net::LDAP::Entry objects.
143
+ def to_entries
144
+ ary = []
145
+ keys.each do |dn|
146
+ entry = Net::LDAP::Entry.new(dn)
147
+ self[dn].each do |attr, value|
148
+ entry[attr] = value
149
+ end
150
+ ary << entry
151
+ end
152
+ ary
153
+ end
154
+
155
+ # This is an internal convenience method to determine if a value requires
156
+ # base64-encoding before conversion to LDIF output. The standard approach
157
+ # in most LDAP tools is to check whether the value is a password, or if
158
+ # the first or last bytes are non-printable. Microsoft Active Directory,
159
+ # on the other hand, sometimes sends values that are binary in the middle.
160
+ #
161
+ # In the worst cases, this could be a nasty performance killer, which is
162
+ # why we handle the simplest cases first. Ideally, we would also test the
163
+ # first/last byte, but it's a bit harder to do this in a way that's
164
+ # compatible with both 1.8.6 and 1.8.7.
165
+ def value_is_binary?(value)
166
+ value = value.to_s
167
+ return true if value[0] == ?: or value[0] == ?<
168
+ value.each_byte { |byte| return true if (byte < 32) || (byte > 126) }
169
+ false
170
+ end
171
+ private :value_is_binary?
172
+ end
173
+
174
+ require 'net/ldap/entry' unless defined? Net::LDAP::Entry