socialcast-net-ldap 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/.gemtest +0 -0
  2. data/COPYING +272 -0
  3. data/Gemfile +10 -0
  4. data/Gemfile.lock +29 -0
  5. data/Hacking.rdoc +16 -0
  6. data/History.txt +137 -0
  7. data/LICENSE +56 -0
  8. data/Manifest.txt +45 -0
  9. data/README.txt +70 -0
  10. data/Rakefile +124 -0
  11. data/lib/net-ldap.rb +1 -0
  12. data/lib/net/ber.rb +341 -0
  13. data/lib/net/ber/ber_parser.rb +168 -0
  14. data/lib/net/ber/core_ext.rb +72 -0
  15. data/lib/net/ber/core_ext/array.rb +79 -0
  16. data/lib/net/ber/core_ext/bignum.rb +19 -0
  17. data/lib/net/ber/core_ext/false_class.rb +7 -0
  18. data/lib/net/ber/core_ext/fixnum.rb +63 -0
  19. data/lib/net/ber/core_ext/string.rb +57 -0
  20. data/lib/net/ber/core_ext/true_class.rb +9 -0
  21. data/lib/net/ldap.rb +1539 -0
  22. data/lib/net/ldap/dataset.rb +174 -0
  23. data/lib/net/ldap/entry.rb +208 -0
  24. data/lib/net/ldap/filter.rb +781 -0
  25. data/lib/net/ldap/password.rb +52 -0
  26. data/lib/net/ldap/pdu.rb +279 -0
  27. data/lib/net/ldif.rb +34 -0
  28. data/lib/net/snmp.rb +295 -0
  29. data/spec/integration/ssl_ber_spec.rb +33 -0
  30. data/spec/spec.opts +2 -0
  31. data/spec/spec_helper.rb +5 -0
  32. data/spec/unit/ber/ber_spec.rb +109 -0
  33. data/spec/unit/ber/core_ext/string_spec.rb +51 -0
  34. data/spec/unit/ldap/entry_spec.rb +51 -0
  35. data/spec/unit/ldap/filter_spec.rb +83 -0
  36. data/spec/unit/ldap_spec.rb +48 -0
  37. data/test/common.rb +3 -0
  38. data/test/test_entry.rb +59 -0
  39. data/test/test_filter.rb +115 -0
  40. data/test/test_ldif.rb +68 -0
  41. data/test/test_password.rb +17 -0
  42. data/test/test_rename.rb +79 -0
  43. data/test/test_snmp.rb +114 -0
  44. data/test/testdata.ldif +101 -0
  45. data/testserver/ldapserver.rb +210 -0
  46. data/testserver/testdata.ldif +101 -0
  47. metadata +178 -0
@@ -0,0 +1,168 @@
1
+ require 'stringio'
2
+
3
+ ##
4
+ # Implements Basic Encoding Rules parsing to be mixed into types as needed.
5
+ module Net::BER::BERParser
6
+ primitive = {
7
+ 1 => :boolean,
8
+ 2 => :integer,
9
+ 4 => :string,
10
+ 5 => :null,
11
+ 6 => :oid,
12
+ 10 => :integer,
13
+ 13 => :string # (relative OID)
14
+ }
15
+ constructed = {
16
+ 16 => :array,
17
+ 17 => :array
18
+ }
19
+ universal = { :primitive => primitive, :constructed => constructed }
20
+
21
+ primitive = { 10 => :integer }
22
+ context = { :primitive => primitive }
23
+
24
+ # The universal, built-in ASN.1 BER syntax.
25
+ BuiltinSyntax = Net::BER.compile_syntax(:universal => universal,
26
+ :context_specific => context)
27
+
28
+ ##
29
+ # This is an extract of our BER object parsing to simplify our
30
+ # understanding of how we parse basic BER object types.
31
+ def parse_ber_object(syntax, id, data)
32
+ # Find the object type from either the provided syntax lookup table or
33
+ # the built-in syntax lookup table.
34
+ #
35
+ # This exceptionally clever bit of code is verrrry slow.
36
+ object_type = (syntax && syntax[id]) || BuiltinSyntax[id]
37
+
38
+ # == is expensive so sort this so the common cases are at the top.
39
+ if object_type == :string
40
+ s = Net::BER::BerIdentifiedString.new(data || "")
41
+ s.ber_identifier = id
42
+ s
43
+ elsif object_type == :integer
44
+ j = 0
45
+ data.each_byte { |b| j = (j << 8) + b }
46
+ j
47
+ elsif object_type == :oid
48
+ # See X.690 pgh 8.19 for an explanation of this algorithm.
49
+ # This is potentially not good enough. We may need a
50
+ # BerIdentifiedOid as a subclass of BerIdentifiedArray, to
51
+ # get the ber identifier and also a to_s method that produces
52
+ # the familiar dotted notation.
53
+ oid = data.unpack("w*")
54
+ f = oid.shift
55
+ g = if f < 40
56
+ [0, f]
57
+ elsif f < 80
58
+ [1, f - 40]
59
+ else
60
+ # f - 80 can easily be > 80. What a weird optimization.
61
+ [2, f - 80]
62
+ end
63
+ oid.unshift g.last
64
+ oid.unshift g.first
65
+ # Net::BER::BerIdentifiedOid.new(oid)
66
+ oid
67
+ elsif object_type == :array
68
+ seq = Net::BER::BerIdentifiedArray.new
69
+ seq.ber_identifier = id
70
+ sio = StringIO.new(data || "")
71
+ # Interpret the subobject, but note how the loop is built:
72
+ # nil ends the loop, but false (a valid BER value) does not!
73
+ while (e = sio.read_ber(syntax)) != nil
74
+ seq << e
75
+ end
76
+ seq
77
+ elsif object_type == :boolean
78
+ data != "\000"
79
+ elsif object_type == :null
80
+ n = Net::BER::BerIdentifiedNull.new
81
+ n.ber_identifier = id
82
+ n
83
+ else
84
+ raise Net::BER::BerError, "Unsupported object type: id=#{id}"
85
+ end
86
+ end
87
+ private :parse_ber_object
88
+
89
+ ##
90
+ # This is an extract of how our BER object length parsing is done to
91
+ # simplify the primary call. This is defined in X.690 section 8.1.3.
92
+ #
93
+ # The BER length will either be a single byte or up to 126 bytes in
94
+ # length. There is a special case of a BER length indicating that the
95
+ # content-length is undefined and will be identified by the presence of
96
+ # two null values (0x00 0x00).
97
+ #
98
+ # <table>
99
+ # <tr>
100
+ # <th>Range</th>
101
+ # <th>Length</th>
102
+ # </tr>
103
+ # <tr>
104
+ # <th>0x00 -- 0x7f<br />0b00000000 -- 0b01111111</th>
105
+ # <td>0 - 127 bytes</td>
106
+ # </tr>
107
+ # <tr>
108
+ # <th>0x80<br />0b10000000</th>
109
+ # <td>Indeterminate (end-of-content marker required)</td>
110
+ # </tr>
111
+ # <tr>
112
+ # <th>0x81 -- 0xfe<br />0b10000001 -- 0b11111110</th>
113
+ # <td>1 - 126 bytes of length as an integer value</td>
114
+ # </tr>
115
+ # <tr>
116
+ # <th>0xff<br />0b11111111</th>
117
+ # <td>Illegal (reserved for future expansion)</td>
118
+ # </tr>
119
+ # </table>
120
+ #
121
+ #--
122
+ # This has been modified from the version that was previously inside
123
+ # #read_ber to handle both the indeterminate terminator case and the
124
+ # invalid BER length case. Because the "lengthlength" value was not used
125
+ # inside of #read_ber, we no longer return it.
126
+ def read_ber_length
127
+ n = getbyte
128
+
129
+ if n <= 0x7f
130
+ n
131
+ elsif n == 0x80
132
+ -1
133
+ elsif n == 0xff
134
+ raise Net::BER::BerError, "Invalid BER length 0xFF detected."
135
+ else
136
+ v = 0
137
+ read(n & 0x7f).each_byte do |b|
138
+ v = (v << 8) + b
139
+ end
140
+
141
+ v
142
+ end
143
+ end
144
+ private :read_ber_length
145
+
146
+ ##
147
+ # Reads a BER object from the including object. Requires that #getbyte is
148
+ # implemented on the including object and that it returns a Fixnum value.
149
+ # Also requires #read(bytes) to work.
150
+ #
151
+ # This does not work with non-blocking I/O.
152
+ def read_ber(syntax = nil)
153
+ # TODO: clean this up so it works properly with partial packets coming
154
+ # from streams that don't block when we ask for more data (like
155
+ # StringIOs). At it is, this can throw TypeErrors and other nasties.
156
+
157
+ id = getbyte or return nil # don't trash this value, we'll use it later
158
+ content_length = read_ber_length
159
+
160
+ if -1 == content_length
161
+ raise Net::BER::BerError, "Indeterminite BER content length not implemented."
162
+ else
163
+ data = read(content_length)
164
+ end
165
+
166
+ parse_ber_object(syntax, id, data)
167
+ end
168
+ end
@@ -0,0 +1,72 @@
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 and other contributors. All
7
+ # Rights Reserved.
8
+ #
9
+ # Gmail: garbagecat10
10
+ #
11
+ # This program is free software; you can redistribute it and/or modify
12
+ # it under the terms of the GNU General Public License as published by
13
+ # the Free Software Foundation; either version 2 of the License, or
14
+ # (at your option) any later version.
15
+ #
16
+ # This program is distributed in the hope that it will be useful,
17
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
18
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
+ # GNU General Public License for more details.
20
+ #
21
+ # You should have received a copy of the GNU General Public License
22
+ # along with this program; if not, write to the Free Software
23
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24
+ #++
25
+
26
+ require 'net/ber/ber_parser'
27
+ class IO
28
+ include Net::BER::BERParser
29
+ end
30
+
31
+ class StringIO
32
+ include Net::BER::BERParser
33
+ end
34
+
35
+ if defined? ::OpenSSL
36
+ class OpenSSL::SSL::SSLSocket
37
+ include Net::BER::BERParser
38
+ end
39
+ end
40
+
41
+ module Net::BER::Extensions; end
42
+
43
+ require 'net/ber/core_ext/string'
44
+ class String
45
+ include Net::BER::BERParser
46
+ include Net::BER::Extensions::String
47
+ end
48
+
49
+ require 'net/ber/core_ext/array'
50
+ class Array
51
+ include Net::BER::Extensions::Array
52
+ end
53
+
54
+ require 'net/ber/core_ext/bignum'
55
+ class Bignum
56
+ include Net::BER::Extensions::Bignum
57
+ end
58
+
59
+ require 'net/ber/core_ext/fixnum'
60
+ class Fixnum
61
+ include Net::BER::Extensions::Fixnum
62
+ end
63
+
64
+ require 'net/ber/core_ext/true_class'
65
+ class TrueClass
66
+ include Net::BER::Extensions::TrueClass
67
+ end
68
+
69
+ require 'net/ber/core_ext/false_class'
70
+ class FalseClass
71
+ include Net::BER::Extensions::FalseClass
72
+ end
@@ -0,0 +1,79 @@
1
+ module Net::BER::Extensions::Array
2
+ ##
3
+ # Converts an Array to a BER sequence. All values in the Array are
4
+ # expected to be in BER format prior to calling this method.
5
+ def to_ber(id = 0)
6
+ # The universal sequence tag 0x30 is composed of the base tag value
7
+ # (0x10) and the constructed flag (0x20).
8
+ to_ber_seq_internal(0x30 + id)
9
+ end
10
+ alias_method :to_ber_sequence, :to_ber
11
+
12
+ ##
13
+ # Converts an Array to a BER set. All values in the Array are expected to
14
+ # be in BER format prior to calling this method.
15
+ def to_ber_set(id = 0)
16
+ # The universal set tag 0x31 is composed of the base tag value (0x11)
17
+ # and the constructed flag (0x20).
18
+ to_ber_seq_internal(0x31 + id)
19
+ end
20
+
21
+ ##
22
+ # Converts an Array to an application-specific sequence, assigned a tag
23
+ # value that is meaningful to the particular protocol being used. All
24
+ # values in the Array are expected to be in BER format pr prior to calling
25
+ # this method.
26
+ #--
27
+ # Implementor's note 20100320(AZ): RFC 4511 (the LDAPv3 protocol) as well
28
+ # as earlier RFCs 1777 and 2559 seem to indicate that LDAP only has
29
+ # application constructed sequences (0x60). However, ldapsearch sends some
30
+ # context-specific constructed sequences (0xA0); other clients may do the
31
+ # same. This behaviour appears to violate the RFCs. In real-world
32
+ # practice, we may need to change calls of #to_ber_appsequence to
33
+ # #to_ber_contextspecific for full LDAP server compatibility.
34
+ #
35
+ # This note probably belongs elsewhere.
36
+ #++
37
+ def to_ber_appsequence(id = 0)
38
+ # The application sequence tag always starts from the application flag
39
+ # (0x40) and the constructed flag (0x20).
40
+ to_ber_seq_internal(0x60 + id)
41
+ end
42
+
43
+ ##
44
+ # Converts an Array to a context-specific sequence, assigned a tag value
45
+ # that is meaningful to the particular context of the particular protocol
46
+ # being used. All values in the Array are expected to be in BER format
47
+ # prior to calling this method.
48
+ def to_ber_contextspecific(id = 0)
49
+ # The application sequence tag always starts from the context flag
50
+ # (0x80) and the constructed flag (0x20).
51
+ to_ber_seq_internal(0xa0 + id)
52
+ end
53
+
54
+ ##
55
+ # The internal sequence packing routine. All values in the Array are
56
+ # expected to be in BER format prior to calling this method.
57
+ def to_ber_seq_internal(code)
58
+ s = self.join
59
+ [code].pack('C') + s.length.to_ber_length_encoding + s
60
+ end
61
+ private :to_ber_seq_internal
62
+
63
+ ##
64
+ # SNMP Object Identifiers (OID) are special arrays
65
+ #--
66
+ # 20100320 AZ: I do not think that this method should be in BER, since
67
+ # this appears to be SNMP-specific. This should probably be subsumed by a
68
+ # proper SNMP OID object.
69
+ #++
70
+ def to_ber_oid
71
+ ary = self.dup
72
+ first = ary.shift
73
+ raise Net::BER::BerError, "Invalid OID" unless [0, 1, 2].include?(first)
74
+ first = first * 40 + ary.shift
75
+ ary.unshift first
76
+ oid = ary.pack("w*")
77
+ [6, oid.length].pack("CC") + oid
78
+ end
79
+ end
@@ -0,0 +1,19 @@
1
+ module Net::BER::Extensions::Bignum
2
+ ##
3
+ # Converts a Bignum to an uncompressed BER integer.
4
+ def to_ber
5
+ result = []
6
+
7
+ # NOTE: Array#pack's 'w' is a BER _compressed_ integer. We need
8
+ # uncompressed BER integers, so we're not using that. See also:
9
+ # http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/228864
10
+ n = self
11
+ while n > 0
12
+ b = n & 0xff
13
+ result << b
14
+ n = n >> 8
15
+ end
16
+
17
+ "\002" + ([result.size] + result.reverse).pack('C*')
18
+ end
19
+ end
@@ -0,0 +1,7 @@
1
+ module Net::BER::Extensions::FalseClass
2
+ ##
3
+ # Converts +false+ to the BER wireline representation of +false+.
4
+ def to_ber
5
+ "\001\001\000"
6
+ end
7
+ end
@@ -0,0 +1,63 @@
1
+ module Net::BER::Extensions::Fixnum
2
+ ##
3
+ # Converts the fixnum to BER format.
4
+ def to_ber
5
+ "\002#{to_ber_internal}"
6
+ end
7
+
8
+ ##
9
+ # Converts the fixnum to BER enumerated format.
10
+ def to_ber_enumerated
11
+ "\012#{to_ber_internal}"
12
+ end
13
+
14
+ ##
15
+ # Converts the fixnum to BER length encodining format.
16
+ def to_ber_length_encoding
17
+ if self <= 127
18
+ [self].pack('C')
19
+ else
20
+ i = [self].pack('N').sub(/^[\0]+/,"")
21
+ [0x80 + i.length].pack('C') + i
22
+ end
23
+ end
24
+
25
+ ##
26
+ # Generate a BER-encoding for an application-defined INTEGER. Examples of
27
+ # such integers are SNMP's Counter, Gauge, and TimeTick types.
28
+ def to_ber_application(tag)
29
+ [0x40 + tag].pack("C") + to_ber_internal
30
+ end
31
+
32
+ ##
33
+ # Used to BER-encode the length and content bytes of a Fixnum. Callers
34
+ # must prepend the tag byte for the contained value.
35
+ def to_ber_internal
36
+ # CAUTION: Bit twiddling ahead. You might want to shield your eyes or
37
+ # something.
38
+
39
+ # Looks for the first byte in the fixnum that is not all zeroes. It does
40
+ # this by masking one byte after another, checking the result for bits
41
+ # that are left on.
42
+ size = Net::BER::MAX_FIXNUM_SIZE
43
+ while size > 1
44
+ break if (self & (0xff << (size - 1) * 8)) > 0
45
+ size -= 1
46
+ end
47
+
48
+ # Store the size of the fixnum in the result
49
+ result = [size]
50
+
51
+ # Appends bytes to result, starting with higher orders first. Extraction
52
+ # of bytes is done by right shifting the original fixnum by an amount
53
+ # and then masking that with 0xff.
54
+ while size > 0
55
+ # right shift size - 1 bytes, mask with 0xff
56
+ result << ((self >> ((size - 1) * 8)) & 0xff)
57
+ size -= 1
58
+ end
59
+
60
+ result.pack('C*')
61
+ end
62
+ private :to_ber_internal
63
+ end
@@ -0,0 +1,57 @@
1
+ require 'stringio'
2
+
3
+ module Net::BER::Extensions::String
4
+ ##
5
+ # Converts a string to a BER string. Universal octet-strings are tagged
6
+ # with 0x04, but other values are possible depending on the context, so we
7
+ # let the caller give us one.
8
+ #
9
+ # User code should call either #to_ber_application_string or
10
+ # #to_ber_contextspecific.
11
+ def to_ber(code = 0x04)
12
+ raw_string = raw_utf8_encoded
13
+ [code].pack('C') + raw_string.length.to_ber_length_encoding + raw_string
14
+ end
15
+
16
+ def raw_utf8_encoded
17
+ if self.respond_to?(:encode)
18
+ # Strings should be UTF-8 encoded according to LDAP.
19
+ # However, the BER code is not necessarily valid UTF-8
20
+ self.encode('UTF-8').force_encoding('ASCII-8BIT')
21
+ else
22
+ self
23
+ end
24
+ end
25
+ private :raw_utf8_encoded
26
+
27
+ ##
28
+ # Creates an application-specific BER string encoded value with the
29
+ # provided syntax code value.
30
+ def to_ber_application_string(code)
31
+ to_ber(0x40 + code)
32
+ end
33
+
34
+ ##
35
+ # Creates a context-specific BER string encoded value with the provided
36
+ # syntax code value.
37
+ def to_ber_contextspecific(code)
38
+ to_ber(0x80 + code)
39
+ end
40
+
41
+ ##
42
+ # Nondestructively reads a BER object from this string.
43
+ def read_ber(syntax = nil)
44
+ StringIO.new(self).read_ber(syntax)
45
+ end
46
+
47
+ ##
48
+ # Destructively reads a BER object from the string.
49
+ def read_ber!(syntax = nil)
50
+ io = StringIO.new(self)
51
+
52
+ result = io.read_ber(syntax)
53
+ self.slice!(0...io.pos)
54
+
55
+ return result
56
+ end
57
+ end