net-ldap 0.0.5

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.

Potentially problematic release.


This version of net-ldap might be problematic. Click here for more details.

@@ -0,0 +1,258 @@
1
+ # $Id$
2
+ #
3
+ # LDAP PDU support classes
4
+ #
5
+ #----------------------------------------------------------------------------
6
+ #
7
+ # Copyright (C) 2006 by Francis Cianfrocca. All 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
+
27
+ module Net
28
+
29
+ class LdapPduError < StandardError; end
30
+
31
+ class LdapPdu
32
+
33
+ BindRequest = 0
34
+ BindResult = 1
35
+ UnbindRequest = 2
36
+ SearchRequest = 3
37
+ SearchReturnedData = 4
38
+ SearchResult = 5
39
+ ModifyResponse = 7
40
+ AddResponse = 9
41
+ DeleteResponse = 11
42
+ ModifyRDNResponse = 13
43
+ SearchResultReferral = 19
44
+ ExtendedRequest = 23
45
+ ExtendedResponse = 24
46
+
47
+ attr_reader :msg_id, :app_tag
48
+ attr_reader :search_dn, :search_attributes, :search_entry
49
+ attr_reader :search_referrals
50
+ attr_reader :search_parameters, :bind_parameters
51
+
52
+ # An LDAP PDU always looks like a BerSequence with
53
+ # at least two elements: an integer (message-id number), and
54
+ # an application-specific sequence.
55
+ # Some LDAPv3 packets also include an optional
56
+ # third element, which is a sequence of "controls"
57
+ # (See RFC 2251, section 4.1.12).
58
+ # The application-specific tag in the sequence tells
59
+ # us what kind of packet it is, and each kind has its
60
+ # own format, defined in RFC-1777.
61
+ # Observe that many clients (such as ldapsearch)
62
+ # do not necessarily enforce the expected application
63
+ # tags on received protocol packets. This implementation
64
+ # does interpret the RFC strictly in this regard, and
65
+ # it remains to be seen whether there are servers out
66
+ # there that will not work well with our approach.
67
+ #
68
+ # Added a controls-processor to SearchResult.
69
+ # Didn't add it everywhere because it just _feels_
70
+ # like it will need to be refactored.
71
+ #
72
+ def initialize ber_object
73
+ begin
74
+ @msg_id = ber_object[0].to_i
75
+ # Modified 25Nov06. We want to "un-decorate" the ber-identifier
76
+ # of the incoming packet. Originally we did this by subtracting 0x60,
77
+ # which ASSUMES the identifier is a constructed app-specific value.
78
+ # But at least one value (UnbindRequest) is app-specific primitive.
79
+ # So it makes more sense just to grab the bottom five bits.
80
+ #@app_tag = ber_object[1].ber_identifier - 0x60
81
+ @app_tag = ber_object[1].ber_identifier & 31
82
+ @ldap_controls = []
83
+ rescue
84
+ # any error becomes a data-format error
85
+ raise LdapPduError.new( "ldap-pdu format error" )
86
+ end
87
+
88
+ case @app_tag
89
+ when BindResult
90
+ parse_bind_response ber_object[1]
91
+ when SearchReturnedData
92
+ parse_search_return ber_object[1]
93
+ when SearchResultReferral
94
+ parse_search_referral ber_object[1]
95
+ when SearchResult
96
+ parse_ldap_result ber_object[1]
97
+ parse_controls(ber_object[2]) if ber_object[2]
98
+ when ModifyResponse
99
+ parse_ldap_result ber_object[1]
100
+ when AddResponse
101
+ parse_ldap_result ber_object[1]
102
+ when DeleteResponse
103
+ parse_ldap_result ber_object[1]
104
+ when ModifyRDNResponse
105
+ parse_ldap_result ber_object[1]
106
+ when SearchRequest
107
+ parse_ldap_search_request ber_object[1]
108
+ when BindRequest
109
+ parse_bind_request ber_object[1]
110
+ when UnbindRequest
111
+ parse_unbind_request ber_object[1]
112
+ when ExtendedResponse
113
+ parse_ldap_result ber_object[1]
114
+ else
115
+ raise LdapPduError.new( "unknown pdu-type: #{@app_tag}" )
116
+ end
117
+ end
118
+
119
+ # Returns a hash which (usually) defines the members :resultCode, :errorMessage, and :matchedDN.
120
+ # These values come directly from an LDAP response packet returned by the remote peer.
121
+ # See #result_code for a sugaring.
122
+ #
123
+ def result
124
+ @ldap_result || {}
125
+ end
126
+
127
+ # This returns an LDAP result code taken from the PDU,
128
+ # but it will be nil if there wasn't a result code.
129
+ # That can easily happen depending on the type of packet.
130
+ #
131
+ def result_code code = :resultCode
132
+ @ldap_result and @ldap_result[code]
133
+ end
134
+
135
+ # Return RFC-2251 Controls if any.
136
+ # Messy. Does this functionality belong somewhere else?
137
+ def result_controls
138
+ @ldap_controls
139
+ end
140
+
141
+ # Return serverSaslCreds, which are only present in BindResponse packets.
142
+ # Messy. Does this functionality belong somewhere else?
143
+ # We ought to refactor the accessors of this class before they get any kludgier.
144
+ def result_server_sasl_creds
145
+ @ldap_result && @ldap_result[:serverSaslCreds]
146
+ end
147
+
148
+ # parse_ldap_result
149
+ #
150
+ def parse_ldap_result sequence
151
+ sequence.length >= 3 or raise LdapPduError
152
+ @ldap_result = {:resultCode => sequence[0], :matchedDN => sequence[1], :errorMessage => sequence[2]}
153
+ end
154
+
155
+ private :parse_ldap_result
156
+
157
+ # A Bind Response may have an additional field, ID [7], serverSaslCreds, per RFC 2251 pgh 4.2.3.
158
+ #
159
+ def parse_bind_response sequence
160
+ sequence.length >= 3 or raise LdapPduError
161
+ @ldap_result = {:resultCode => sequence[0], :matchedDN => sequence[1], :errorMessage => sequence[2]}
162
+ @ldap_result[:serverSaslCreds] = sequence[3] if sequence.length >= 4
163
+ @ldap_result
164
+ end
165
+
166
+ private :parse_bind_response
167
+
168
+ # Definition from RFC 1777 (we're handling application-4 here)
169
+ #
170
+ # Search Response ::=
171
+ # CHOICE {
172
+ # entry [APPLICATION 4] SEQUENCE {
173
+ # objectName LDAPDN,
174
+ # attributes SEQUENCE OF SEQUENCE {
175
+ # AttributeType,
176
+ # SET OF AttributeValue
177
+ # }
178
+ # },
179
+ # resultCode [APPLICATION 5] LDAPResult
180
+ # }
181
+ #
182
+ # We concoct a search response that is a hash of the returned attribute values.
183
+ # NOW OBSERVE CAREFULLY: WE ARE DOWNCASING THE RETURNED ATTRIBUTE NAMES.
184
+ # This is to make them more predictable for user programs, but it
185
+ # may not be a good idea. Maybe this should be configurable.
186
+ # ALTERNATE IMPLEMENTATION: In addition to @search_dn and @search_attributes,
187
+ # we also return @search_entry, which is an LDAP::Entry object.
188
+ # If that works out well, then we'll remove the first two.
189
+ #
190
+ # Provisionally removed obsolete search_attributes and search_dn, 04May06.
191
+ #
192
+ def parse_search_return sequence
193
+ sequence.length >= 2 or raise LdapPduError
194
+ @search_entry = LDAP::Entry.new( sequence[0] )
195
+ sequence[1].each {|seq|
196
+ @search_entry[seq[0]] = seq[1]
197
+ }
198
+ end
199
+
200
+ # A search referral is a sequence of one or more LDAP URIs.
201
+ # Any number of search-referral replies can be returned by the server, interspersed
202
+ # with normal replies in any order.
203
+ # Until I can think of a better way to do this, we'll return the referrals as an array.
204
+ # It'll be up to higher-level handlers to expose something reasonable to the client.
205
+ def parse_search_referral uris
206
+ @search_referrals = uris
207
+ end
208
+
209
+ # Per RFC 2251, an LDAP "control" is a sequence of tuples, each consisting
210
+ # of an OID, a boolean criticality flag defaulting FALSE, and an OPTIONAL
211
+ # Octet String. If only two fields are given, the second one may be
212
+ # either criticality or data, since criticality has a default value.
213
+ # Someday we may want to come back here and add support for some of
214
+ # more-widely used controls. RFC-2696 is a good example.
215
+ #
216
+ def parse_controls sequence
217
+ @ldap_controls = sequence.map do |control|
218
+ o = OpenStruct.new
219
+ o.oid,o.criticality,o.value = control[0],control[1],control[2]
220
+ if o.criticality and o.criticality.is_a?(String)
221
+ o.value = o.criticality
222
+ o.criticality = false
223
+ end
224
+ o
225
+ end
226
+ end
227
+
228
+ private :parse_controls
229
+
230
+ # (provisional, must document)
231
+ def parse_ldap_search_request sequence
232
+ s = OpenStruct.new
233
+ s.base_object,
234
+ s.scope,
235
+ s.deref_aliases,
236
+ s.size_limit,
237
+ s.time_limit,
238
+ s.types_only,
239
+ s.filter,
240
+ s.attributes = sequence
241
+ @search_parameters = s
242
+ end
243
+
244
+ # (provisional, must document)
245
+ def parse_bind_request sequence
246
+ s = OpenStruct.new
247
+ s.version,
248
+ s.name,
249
+ s.authentication = sequence
250
+ @bind_parameters = s
251
+ end
252
+
253
+ # (provisional, must document)
254
+ # UnbindRequest has no content so this is a no-op.
255
+ def parse_unbind_request sequence
256
+ end
257
+ end
258
+ end # module Net
@@ -0,0 +1,64 @@
1
+ # $Id$
2
+ #
3
+ #
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
11
+ # it under the terms of the GNU General Public License as published by
12
+ # the Free Software Foundation; either version 2 of the License, or
13
+ # (at your option) any later version.
14
+ #
15
+ # This program is distributed in the hope that it will be useful,
16
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
17
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
+ # GNU General Public License for more details.
19
+ #
20
+ # You should have received a copy of the GNU General Public License
21
+ # along with this program; if not, write to the Free Software
22
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23
+ #
24
+ #---------------------------------------------------------------------------
25
+ #
26
+ #
27
+
28
+
29
+ module Net
30
+ class LDAP
31
+
32
+
33
+ class Password
34
+ class << self
35
+
36
+ # Generate a password-hash suitable for inclusion in an LDAP attribute.
37
+ # Pass a hash type (currently supported: :md5 and :sha) and a plaintext
38
+ # password. This function will return a hashed representation.
39
+ # STUB: This is here to fulfill the requirements of an RFC, which one?
40
+ # TODO, gotta do salted-sha and (maybe) salted-md5.
41
+ # Should we provide sha1 as a synonym for sha1? I vote no because then
42
+ # should you also provide ssha1 for symmetry?
43
+ def generate( type, str )
44
+ case type
45
+ when :md5
46
+ require 'md5'
47
+ "{MD5}#{ [MD5.new( str.to_s ).digest].pack("m").chomp }"
48
+ when :sha
49
+ require 'sha1'
50
+ "{SHA}#{ [SHA1.new( str.to_s ).digest].pack("m").chomp }"
51
+ # when ssha
52
+ else
53
+ raise Net::LDAP::LdapError.new( "unsupported password-hash type (#{type})" )
54
+ end
55
+ end
56
+
57
+ end
58
+ end
59
+
60
+
61
+ end # class LDAP
62
+ end # module Net
63
+
64
+
@@ -0,0 +1,39 @@
1
+ # $Id$
2
+ #
3
+ # Net::LDIF for Ruby
4
+ #
5
+ #
6
+ #
7
+ # Copyright (C) 2006 by Francis Cianfrocca. All 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
+
27
+ # THIS FILE IS A STUB.
28
+
29
+ module Net
30
+
31
+ class LDIF
32
+
33
+
34
+ end # class LDIF
35
+
36
+
37
+ end # module Net
38
+
39
+
@@ -0,0 +1,297 @@
1
+ # $Id$
2
+ #
3
+ # NET::SNMP
4
+ #
5
+ #----------------------------------------------------------------------------
6
+ #
7
+ # Copyright (C) 2006-07 by Francis Cianfrocca. All 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
+ #
27
+ #
28
+
29
+ require 'net/ber'
30
+
31
+
32
+ module Net
33
+
34
+ class SNMP
35
+
36
+ AsnSyntax = BER.compile_syntax({
37
+ :application => {
38
+ :primitive => {
39
+ 1 => :integer, # Counter32, (RFC2578 sec 2)
40
+ 2 => :integer, # Gauge32 or Unsigned32, (RFC2578 sec 2)
41
+ 3 => :integer # TimeTicks32, (RFC2578 sec 2)
42
+ },
43
+ :constructed => {
44
+ }
45
+ },
46
+ :context_specific => {
47
+ :primitive => {
48
+ },
49
+ :constructed => {
50
+ 0 => :array, # GetRequest PDU (RFC1157 pgh 4.1.2)
51
+ 1 => :array, # GetNextRequest PDU (RFC1157 pgh 4.1.3)
52
+ 2 => :array # GetResponse PDU (RFC1157 pgh 4.1.4)
53
+ }
54
+ }
55
+ })
56
+
57
+ # SNMP 32-bit counter.
58
+ # Defined in RFC1155 (Structure of Mangement Information), section 6.
59
+ # A 32-bit counter is an ASN.1 application [1] implicit unsigned integer
60
+ # with a range from 0 to 2^^32 - 1.
61
+ class Counter32
62
+ def initialize value
63
+ @value = value
64
+ end
65
+ def to_ber
66
+ @value.to_ber_application(1)
67
+ end
68
+ end
69
+
70
+ # SNMP 32-bit gauge.
71
+ # Defined in RFC1155 (Structure of Mangement Information), section 6.
72
+ # A 32-bit counter is an ASN.1 application [2] implicit unsigned integer.
73
+ # This is also indistinguishable from Unsigned32. (Need to alias them.)
74
+ class Gauge32
75
+ def initialize value
76
+ @value = value
77
+ end
78
+ def to_ber
79
+ @value.to_ber_application(2)
80
+ end
81
+ end
82
+
83
+ # SNMP 32-bit timer-ticks.
84
+ # Defined in RFC1155 (Structure of Mangement Information), section 6.
85
+ # A 32-bit counter is an ASN.1 application [3] implicit unsigned integer.
86
+ class TimeTicks32
87
+ def initialize value
88
+ @value = value
89
+ end
90
+ def to_ber
91
+ @value.to_ber_application(3)
92
+ end
93
+ end
94
+ end
95
+
96
+ class SnmpPdu
97
+ class Error < StandardError; end
98
+
99
+ PduTypes = [
100
+ :get_request,
101
+ :get_next_request,
102
+ :get_response,
103
+ :set_request,
104
+ :trap
105
+ ]
106
+ ErrorStatusCodes = { # Per RFC1157, pgh 4.1.1
107
+ 0 => "noError",
108
+ 1 => "tooBig",
109
+ 2 => "noSuchName",
110
+ 3 => "badValue",
111
+ 4 => "readOnly",
112
+ 5 => "genErr"
113
+ }
114
+
115
+ class << self
116
+ def parse ber_object
117
+ n = new
118
+ n.send :parse, ber_object
119
+ n
120
+ end
121
+ end
122
+
123
+ attr_reader :version, :community, :pdu_type, :variables, :error_status
124
+ attr_accessor :request_id, :error_index
125
+
126
+
127
+ def initialize args={}
128
+ @version = args[:version] || 0
129
+ @community = args[:community] || "public"
130
+ @pdu_type = args[:pdu_type] # leave nil unless specified; there's no reasonable default value.
131
+ @error_status = args[:error_status] || 0
132
+ @error_index = args[:error_index] || 0
133
+ @variables = args[:variables] || []
134
+ end
135
+
136
+ #--
137
+ def parse ber_object
138
+ begin
139
+ parse_ber_object ber_object
140
+ rescue Error
141
+ # Pass through any SnmpPdu::Error instances
142
+ raise $!
143
+ rescue
144
+ # Wrap any basic parsing error so it becomes a PDU-format error
145
+ raise Error.new( "snmp-pdu format error" )
146
+ end
147
+ end
148
+ private :parse
149
+
150
+ def parse_ber_object ber_object
151
+ send :version=, ber_object[0].to_i
152
+ send :community=, ber_object[1].to_s
153
+
154
+ data = ber_object[2]
155
+ case (app_tag = data.ber_identifier & 31)
156
+ when 0
157
+ send :pdu_type=, :get_request
158
+ parse_get_request data
159
+ when 1
160
+ send :pdu_type=, :get_next_request
161
+ # This PDU is identical to get-request except for the type.
162
+ parse_get_request data
163
+ when 2
164
+ send :pdu_type=, :get_response
165
+ # This PDU is identical to get-request except for the type,
166
+ # the error_status and error_index values are meaningful,
167
+ # and the fact that the variable bindings will be non-null.
168
+ parse_get_response data
169
+ else
170
+ raise Error.new( "unknown snmp-pdu type: #{app_tag}" )
171
+ end
172
+ end
173
+ private :parse_ber_object
174
+
175
+ #--
176
+ # Defined in RFC1157, pgh 4.1.2.
177
+ def parse_get_request data
178
+ send :request_id=, data[0].to_i
179
+ # data[1] is error_status, always zero.
180
+ # data[2] is error_index, always zero.
181
+ send :error_status=, 0
182
+ send :error_index=, 0
183
+ data[3].each {|n,v|
184
+ # A variable-binding, of which there may be several,
185
+ # consists of an OID and a BER null.
186
+ # We're ignoring the null, we might want to verify it instead.
187
+ unless v.is_a?(Net::BER::BerIdentifiedNull)
188
+ raise Error.new(" invalid variable-binding in get-request" )
189
+ end
190
+ add_variable_binding n, nil
191
+ }
192
+ end
193
+ private :parse_get_request
194
+
195
+ #--
196
+ # Defined in RFC1157, pgh 4.1.4
197
+ def parse_get_response data
198
+ send :request_id=, data[0].to_i
199
+ send :error_status=, data[1].to_i
200
+ send :error_index=, data[2].to_i
201
+ data[3].each {|n,v|
202
+ # A variable-binding, of which there may be several,
203
+ # consists of an OID and a BER null.
204
+ # We're ignoring the null, we might want to verify it instead.
205
+ add_variable_binding n, v
206
+ }
207
+ end
208
+ private :parse_get_response
209
+
210
+
211
+ def version= ver
212
+ unless [0,2].include?(ver)
213
+ raise Error.new("unknown snmp-version: #{ver}")
214
+ end
215
+ @version = ver
216
+ end
217
+
218
+ def pdu_type= t
219
+ unless PduTypes.include?(t)
220
+ raise Error.new("unknown pdu-type: #{t}")
221
+ end
222
+ @pdu_type = t
223
+ end
224
+
225
+ def error_status= es
226
+ unless ErrorStatusCodes.has_key?(es)
227
+ raise Error.new("unknown error-status: #{es}")
228
+ end
229
+ @error_status = es
230
+ end
231
+
232
+ def community= c
233
+ @community = c.to_s
234
+ end
235
+
236
+ #--
237
+ # Syntactic sugar
238
+ def add_variable_binding name, value=nil
239
+ @variables ||= []
240
+ @variables << [name, value]
241
+ end
242
+
243
+ def to_ber_string
244
+ [
245
+ version.to_ber,
246
+ community.to_ber,
247
+ pdu_to_ber_string
248
+ ].to_ber_sequence
249
+ end
250
+
251
+ #--
252
+ # Helper method that returns a PDU payload in BER form,
253
+ # depending on the PDU type.
254
+ def pdu_to_ber_string
255
+ case pdu_type
256
+ when :get_request
257
+ [
258
+ request_id.to_ber,
259
+ error_status.to_ber,
260
+ error_index.to_ber,
261
+ [
262
+ @variables.map {|n,v|
263
+ [n.to_ber_oid, Net::BER::BerIdentifiedNull.new.to_ber].to_ber_sequence
264
+ }
265
+ ].to_ber_sequence
266
+ ].to_ber_contextspecific(0)
267
+ when :get_next_request
268
+ [
269
+ request_id.to_ber,
270
+ error_status.to_ber,
271
+ error_index.to_ber,
272
+ [
273
+ @variables.map {|n,v|
274
+ [n.to_ber_oid, Net::BER::BerIdentifiedNull.new.to_ber].to_ber_sequence
275
+ }
276
+ ].to_ber_sequence
277
+ ].to_ber_contextspecific(1)
278
+ when :get_response
279
+ [
280
+ request_id.to_ber,
281
+ error_status.to_ber,
282
+ error_index.to_ber,
283
+ [
284
+ @variables.map {|n,v|
285
+ [n.to_ber_oid, v.to_ber].to_ber_sequence
286
+ }
287
+ ].to_ber_sequence
288
+ ].to_ber_contextspecific(2)
289
+ else
290
+ raise Error.new( "unknown pdu-type: #{pdu_type}" )
291
+ end
292
+ end
293
+ private :pdu_to_ber_string
294
+
295
+ end
296
+ end
297
+