net-ldap 0.0.5

Sign up to get free protection for your applications and to get access to all the features.

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
+